PR9551: Implement DR1004 (http://wg21.link/cwg1004).
This rule permits the injected-class-name of a class template to be used as
both a template type argument and a template template argument, with no extra
syntax required to disambiguate.
llvm-svn: 292426
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 2920ede..90de88a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3657,6 +3657,39 @@
TempTempParm->getDefaultArgument().getTemplateNameLoc());
}
+/// Convert a template-argument that we parsed as a type into a template, if
+/// possible. C++ permits injected-class-names to perform dual service as
+/// template template arguments and as template type arguments.
+static TemplateArgumentLoc convertTypeTemplateArgumentToTemplate(TypeLoc TLoc) {
+ // Extract and step over any surrounding nested-name-specifier.
+ NestedNameSpecifierLoc QualLoc;
+ if (auto ETLoc = TLoc.getAs<ElaboratedTypeLoc>()) {
+ if (ETLoc.getTypePtr()->getKeyword() != ETK_None)
+ return TemplateArgumentLoc();
+
+ QualLoc = ETLoc.getQualifierLoc();
+ TLoc = ETLoc.getNamedTypeLoc();
+ }
+
+ // If this type was written as an injected-class-name, it can be used as a
+ // template template argument.
+ if (auto InjLoc = TLoc.getAs<InjectedClassNameTypeLoc>())
+ return TemplateArgumentLoc(InjLoc.getTypePtr()->getTemplateName(),
+ QualLoc, InjLoc.getNameLoc());
+
+ // If this type was written as an injected-class-name, it may have been
+ // converted to a RecordType during instantiation. If the RecordType is
+ // *not* wrapped in a TemplateSpecializationType and denotes a class
+ // template specialization, it must have come from an injected-class-name.
+ if (auto RecLoc = TLoc.getAs<RecordTypeLoc>())
+ if (auto *CTSD =
+ dyn_cast<ClassTemplateSpecializationDecl>(RecLoc.getDecl()))
+ return TemplateArgumentLoc(TemplateName(CTSD->getSpecializedTemplate()),
+ QualLoc, RecLoc.getNameLoc());
+
+ return TemplateArgumentLoc();
+}
+
/// \brief Check that the given template argument corresponds to the given
/// template parameter.
///
@@ -3863,6 +3896,17 @@
return true;
}
+ // C++1z [temp.local]p1: (DR1004)
+ // When [the injected-class-name] is used [...] as a template-argument for
+ // a template template-parameter [...] it refers to the class template
+ // itself.
+ if (Arg.getArgument().getKind() == TemplateArgument::Type) {
+ TemplateArgumentLoc ConvertedArg = convertTypeTemplateArgumentToTemplate(
+ Arg.getTypeSourceInfo()->getTypeLoc());
+ if (!ConvertedArg.getArgument().isNull())
+ Arg = ConvertedArg;
+ }
+
switch (Arg.getArgument().getKind()) {
case TemplateArgument::Null:
llvm_unreachable("Should never see a NULL template argument here");
@@ -3976,11 +4020,11 @@
/// \brief Check that the given template argument list is well-formed
/// for specializing the given template.
-bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
- SourceLocation TemplateLoc,
- TemplateArgumentListInfo &TemplateArgs,
- bool PartialTemplateArgs,
- SmallVectorImpl<TemplateArgument> &Converted) {
+bool Sema::CheckTemplateArgumentList(
+ TemplateDecl *Template, SourceLocation TemplateLoc,
+ TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,
+ SmallVectorImpl<TemplateArgument> &Converted,
+ bool UpdateArgsWithConversions) {
// Make a copy of the template arguments for processing. Only make the
// changes at the end when successful in matching the arguments to the
// template.
@@ -4218,7 +4262,8 @@
// No problems found with the new argument list, propagate changes back
// to caller.
- TemplateArgs = std::move(NewArgs);
+ if (UpdateArgsWithConversions)
+ TemplateArgs = std::move(NewArgs);
return false;
}