aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaTemplate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaTemplate.cpp')
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp391
1 files changed, 293 insertions, 98 deletions
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 21fed2e..b6b8932 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
@@ -38,6 +39,7 @@
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/SaveAndRestore.h"
#include <optional>
@@ -306,9 +308,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD) ||
isa<BuiltinTemplateDecl>(TD) || isa<ConceptDecl>(TD));
TemplateKind =
- isa<VarTemplateDecl>(TD) ? TNK_Var_template :
- isa<ConceptDecl>(TD) ? TNK_Concept_template :
- TNK_Type_template;
+ isa<TemplateTemplateParmDecl>(TD)
+ ? dyn_cast<TemplateTemplateParmDecl>(TD)->templateParameterKind()
+ : isa<VarTemplateDecl>(TD) ? TNK_Var_template
+ : isa<ConceptDecl>(TD) ? TNK_Concept_template
+ : TNK_Type_template;
}
}
@@ -1071,12 +1075,26 @@ makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) {
bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
TemplateName TN = TypeConstr->Template.get();
- ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+ NamedDecl *CD = nullptr;
+ bool IsTypeConcept = false;
+ bool RequiresArguments = false;
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
+ IsTypeConcept = TTP->isTypeConceptTemplateParam();
+ RequiresArguments =
+ TTP->getTemplateParameters()->getMinRequiredArguments() > 1;
+ CD = TTP;
+ } else {
+ CD = TN.getAsTemplateDecl();
+ IsTypeConcept = cast<ConceptDecl>(CD)->isTypeConcept();
+ RequiresArguments = cast<ConceptDecl>(CD)
+ ->getTemplateParameters()
+ ->getMinRequiredArguments() > 1;
+ }
// C++2a [temp.param]p4:
// [...] The concept designated by a type-constraint shall be a type
// concept ([temp.concept]).
- if (!CD->isTypeConcept()) {
+ if (!IsTypeConcept) {
Diag(TypeConstr->TemplateNameLoc,
diag::err_type_constraint_non_type_concept);
return true;
@@ -1087,8 +1105,7 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
- if (!WereArgsSpecified &&
- CD->getTemplateParameters()->getMinRequiredArguments() > 1) {
+ if (!WereArgsSpecified && RequiresArguments) {
Diag(TypeConstr->TemplateNameLoc,
diag::err_type_constraint_missing_arguments)
<< CD;
@@ -1115,7 +1132,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
return true;
TemplateName TN = TypeConstr->Template.get();
- ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+ TemplateDecl *CD = cast<TemplateDecl>(TN.getAsTemplateDecl());
UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
@@ -1143,7 +1160,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
template <typename ArgumentLocAppender>
static ExprResult formImmediatelyDeclaredConstraint(
Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo,
- ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
+ NamedDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
SourceLocation RAngleLoc, QualType ConstrainedType,
SourceLocation ParamNameLoc, ArgumentLocAppender Appender,
SourceLocation EllipsisLoc) {
@@ -1162,10 +1179,19 @@ static ExprResult formImmediatelyDeclaredConstraint(
// constraint of T. [...]
CXXScopeSpec SS;
SS.Adopt(NS);
- ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
- SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
- /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
- &ConstraintArgs);
+ ExprResult ImmediatelyDeclaredConstraint;
+ if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
+ ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
+ SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+ /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
+ &ConstraintArgs);
+ }
+ // We have a template template parameter
+ else {
+ auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
+ ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
+ SS, NameInfo, CDT, SourceLocation(), &ConstraintArgs);
+ }
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
@@ -1191,7 +1217,8 @@ static ExprResult formImmediatelyDeclaredConstraint(
bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
- ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
+ TemplateDecl *NamedConcept,
+ NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc) {
@@ -1588,11 +1615,15 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
return Param;
}
+/// ActOnTemplateTemplateParameter - Called when a C++ template template
+/// parameter (e.g. T in template <template \<typename> class T> class array)
+/// has been parsed. S is the current scope.
NamedDecl *Sema::ActOnTemplateTemplateParameter(
- Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params,
- bool Typename, SourceLocation EllipsisLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, unsigned Depth, unsigned Position,
- SourceLocation EqualLoc, ParsedTemplateArgument Default) {
+ Scope *S, SourceLocation TmpLoc, TemplateNameKind Kind, bool Typename,
+ TemplateParameterList *Params, SourceLocation EllipsisLoc,
+ IdentifierInfo *Name, SourceLocation NameLoc, unsigned Depth,
+ unsigned Position, SourceLocation EqualLoc,
+ ParsedTemplateArgument Default) {
assert(S->isTemplateParamScope() &&
"Template template parameter not in template parameter scope!");
@@ -1609,7 +1640,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
Context, Context.getTranslationUnitDecl(),
NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
- Name, Typename, Params);
+ Name, Kind, Typename, Params);
Param->setAccess(AS_public);
if (Param->isParameterPack())
@@ -1658,6 +1689,14 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
return Param;
}
+ TemplateName Name =
+ DefaultArg.getArgument().getAsTemplateOrTemplatePattern();
+ TemplateDecl *Template = Name.getAsTemplateDecl();
+ if (Template &&
+ !CheckDeclCompatibleWithTemplateTemplate(Template, Param, DefaultArg)) {
+ return Param;
+ }
+
// Check for unexpanded parameter packs.
if (DiagnoseUnexpandedParameterPack(DefaultArg.getLocation(),
DefaultArg.getArgument().getAsTemplate(),
@@ -2667,6 +2706,18 @@ struct DependencyChecker : DynamicRecursiveASTVisitor {
return DynamicRecursiveASTVisitor::VisitDeclRefExpr(E);
}
+ bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) override {
+ if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
+ if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+ if (Matches(TTP->getDepth(), ULE->getExprLoc()))
+ return false;
+ }
+ for (auto &TLoc : ULE->template_arguments())
+ DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
+ }
+ return DynamicRecursiveASTVisitor::VisitUnresolvedLookupExpr(ULE);
+ }
+
bool VisitSubstTemplateTypeParmType(SubstTemplateTypeParmType *T) override {
return TraverseType(T->getReplacementType());
}
@@ -4047,8 +4098,10 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized,
static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
-static bool isTemplateArgumentTemplateParameter(
- const TemplateArgument &Arg, unsigned Depth, unsigned Index) {
+static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
+ NamedDecl *Param,
+ unsigned Depth,
+ unsigned Index) {
switch (Arg.getKind()) {
case TemplateArgument::Null:
case TemplateArgument::NullPtr:
@@ -4104,7 +4157,8 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
Arg = Arg.pack_begin()->getPackExpansionPattern();
}
- if (!isTemplateArgumentTemplateParameter(Arg, Depth, I))
+ if (!isTemplateArgumentTemplateParameter(Arg, Params->getParam(I), Depth,
+ I))
return false;
}
@@ -4694,6 +4748,43 @@ ExprResult Sema::CheckVarTemplateId(
return BuildDeclarationNameExpr(SS, NameInfo, Var, FoundD, TemplateArgs);
}
+ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
+ const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
+ TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+ const TemplateArgumentListInfo *TemplateArgs) {
+ assert(Template && "A variable template id without template?");
+
+ if (Template->templateParameterKind() != TemplateNameKind::TNK_Var_template &&
+ Template->templateParameterKind() !=
+ TemplateNameKind::TNK_Concept_template)
+ return ExprResult();
+
+ // Check that the template argument list is well-formed for this template.
+ CheckTemplateArgumentInfo CTAI;
+ if (CheckTemplateArgumentList(
+ Template, TemplateLoc,
+ // FIXME: TemplateArgs will not be modified because
+ // UpdateArgsWithConversions is false, however, we should
+ // CheckTemplateArgumentList to be const-correct.
+ const_cast<TemplateArgumentListInfo &>(*TemplateArgs),
+ /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI,
+ /*UpdateArgsWithConversions=*/false))
+ return true;
+
+ UnresolvedSet<1> R;
+ R.addDecl(Template);
+
+ // FIXME: We model references to variable template and concept parameters
+ // as an UnresolvedLookupExpr. This is because they encapsulate the same
+ // data, can generally be used in the same places and work the same way.
+ // However, it might be cleaner to use a dedicated AST node in the long run.
+ return UnresolvedLookupExpr::Create(
+ getASTContext(), nullptr, SS.getWithLocInContext(getASTContext()),
+ SourceLocation(), NameInfo, false, TemplateArgs, R.begin(), R.end(),
+ /*KnownDependent=*/false,
+ /*KnownInstantiationDependent=*/false);
+}
+
void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
SourceLocation Loc) {
Diag(Loc, diag::err_template_missing_args)
@@ -4804,21 +4895,29 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
KnownDependent = true;
}
+ // We don't want lookup warnings at this point.
+ R.suppressDiagnostics();
+
if (R.getAsSingle<ConceptDecl>()) {
return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
R.getRepresentativeDecl(),
R.getAsSingle<ConceptDecl>(), TemplateArgs);
}
- // We don't want lookup warnings at this point.
- R.suppressDiagnostics();
+ // Check variable template ids (C++17) and concept template parameters
+ // (C++26).
+ UnresolvedLookupExpr *ULE;
+ if (R.getAsSingle<TemplateTemplateParmDecl>())
+ return CheckVarOrConceptTemplateTemplateId(
+ SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
+ TemplateKWLoc, TemplateArgs);
- UnresolvedLookupExpr *ULE = UnresolvedLookupExpr::Create(
+ // Function templates
+ ULE = UnresolvedLookupExpr::Create(
Context, R.getNamingClass(), SS.getWithLocInContext(Context),
TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
R.begin(), R.end(), KnownDependent,
/*KnownInstantiationDependent=*/false);
-
// Model the templates with UnresolvedTemplateTy. The expression should then
// either be transformed in an instantiation or be diagnosed in
// CheckPlaceholderExpr.
@@ -5609,12 +5708,25 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
break;
case TemplateArgument::Expression:
- case TemplateArgument::Type:
+ case TemplateArgument::Type: {
+ auto Kind = 0;
+ switch (TempParm->templateParameterKind()) {
+ case TemplateNameKind::TNK_Var_template:
+ Kind = 1;
+ break;
+ case TemplateNameKind::TNK_Concept_template:
+ Kind = 2;
+ break;
+ default:
+ break;
+ }
+
// We have a template template parameter but the template
// argument does not refer to a template.
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_template)
- << getLangOpts().CPlusPlus11;
+ << Kind << getLangOpts().CPlusPlus11;
return true;
+ }
case TemplateArgument::Declaration:
case TemplateArgument::Integral:
@@ -6359,7 +6471,7 @@ enum NullPointerValueKind {
/// Determine whether the given template argument is a null pointer
/// value of the appropriate type.
static NullPointerValueKind
-isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
+isNullPointerValueTemplateArgument(Sema &S, NamedDecl *Param,
QualType ParamType, Expr *Arg,
Decl *Entity = nullptr) {
if (Arg->isValueDependent() || Arg->isTypeDependent())
@@ -6464,9 +6576,10 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
/// Checks whether the given template argument is compatible with its
/// template parameter.
-static bool CheckTemplateArgumentIsCompatibleWithParameter(
- Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
- Expr *Arg, QualType ArgType) {
+static bool
+CheckTemplateArgumentIsCompatibleWithParameter(Sema &S, NamedDecl *Param,
+ QualType ParamType, Expr *ArgIn,
+ Expr *Arg, QualType ArgType) {
bool ObjCLifetimeConversion;
if (ParamType->isPointerType() &&
!ParamType->castAs<PointerType>()->getPointeeType()->isFunctionType() &&
@@ -6522,7 +6635,7 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter(
/// Checks whether the given template argument is the address
/// of an object or function according to C++ [temp.arg.nontype]p1.
static bool CheckTemplateArgumentAddressOfObjectOrFunction(
- Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
+ Sema &S, NamedDecl *Param, QualType ParamType, Expr *ArgIn,
TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
bool Invalid = false;
Expr *Arg = ArgIn;
@@ -6784,11 +6897,9 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
/// Checks whether the given template argument is a pointer to
/// member constant according to C++ [temp.arg.nontype]p1.
-static bool
-CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
- QualType ParamType, Expr *&ResultArg,
- TemplateArgument &SugaredConverted,
- TemplateArgument &CanonicalConverted) {
+static bool CheckTemplateArgumentPointerToMember(
+ Sema &S, NamedDecl *Param, QualType ParamType, Expr *&ResultArg,
+ TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
bool Invalid = false;
Expr *Arg = ResultArg;
@@ -6919,8 +7030,15 @@ CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
return true;
}
-ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
- QualType ParamType, Expr *Arg,
+/// Check a template argument against its corresponding
+/// non-type template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.nontype].
+/// If an error occurred, it returns ExprError(); otherwise, it
+/// returns the converted template argument. \p ParamType is the
+/// type of the non-type template parameter after it has been instantiated.
+ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
+ Expr *Arg,
TemplateArgument &SugaredConverted,
TemplateArgument &CanonicalConverted,
bool StrictCheck,
@@ -6974,7 +7092,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ExprError();
} else {
TemplateDeductionInfo Info(DeductionArg->getExprLoc(),
- Param->getDepth() + 1);
+ Param->getTemplateDepth() + 1);
ParamType = QualType();
TemplateDeductionResult Result =
DeduceAutoType(TSI->getTypeLoc(), DeductionArg, ParamType, Info,
@@ -6986,13 +7104,14 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// checking the template argument list.
/*IgnoreConstraints=*/true);
if (Result == TemplateDeductionResult::AlreadyDiagnosed) {
- if (ParamType.isNull())
- return ExprError();
+ return ExprError();
} else if (Result != TemplateDeductionResult::Success) {
- Diag(Arg->getExprLoc(),
- diag::err_non_type_template_parm_type_deduction_failure)
- << Param->getDeclName() << Param->getType() << Arg->getType()
- << Arg->getSourceRange();
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ Diag(Arg->getExprLoc(),
+ diag::err_non_type_template_parm_type_deduction_failure)
+ << Param->getDeclName() << NTTP->getType() << Arg->getType()
+ << Arg->getSourceRange();
+ }
NoteTemplateParameterLocation(*Param);
return ExprError();
}
@@ -7387,7 +7506,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (IntegerType->isUnsignedIntegerOrEnumerationType() &&
(OldValue.isSigned() && OldValue.isNegative())) {
Diag(Arg->getBeginLoc(), diag::warn_template_arg_negative)
- << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+ << toString(OldValue, 10) << toString(Value, 10) << ParamType
<< Arg->getSourceRange();
NoteTemplateParameterLocation(*Param);
}
@@ -7402,7 +7521,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
RequiredBits = OldValue.getSignificantBits();
if (RequiredBits > AllowedBits) {
Diag(Arg->getBeginLoc(), diag::warn_template_arg_too_large)
- << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+ << toString(OldValue, 10) << toString(Value, 10) << ParamType
<< Arg->getSourceRange();
NoteTemplateParameterLocation(*Param);
}
@@ -7581,6 +7700,81 @@ static void DiagnoseTemplateParameterListArityMismatch(
Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
+bool Sema::CheckDeclCompatibleWithTemplateTemplate(
+ TemplateDecl *Template, TemplateTemplateParmDecl *Param,
+ const TemplateArgumentLoc &Arg) {
+ // C++0x [temp.arg.template]p1:
+ // A template-argument for a template template-parameter shall be
+ // the name of a class template or an alias template, expressed as an
+ // id-expression. When the template-argument names a class template, only
+ // primary class templates are considered when matching the
+ // template template argument with the corresponding parameter;
+ // partial specializations are not considered even if their
+ // parameter lists match that of the template template parameter.
+ //
+
+ TemplateNameKind Kind = TNK_Non_template;
+ unsigned DiagFoundKind = 0;
+
+ if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(Template)) {
+ switch (TTP->templateParameterKind()) {
+ case TemplateNameKind::TNK_Concept_template:
+ DiagFoundKind = 3;
+ break;
+ case TemplateNameKind::TNK_Var_template:
+ DiagFoundKind = 2;
+ break;
+ default:
+ DiagFoundKind = 1;
+ break;
+ }
+ Kind = TTP->templateParameterKind();
+ } else if (isa<ConceptDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Concept_template;
+ DiagFoundKind = 3;
+ } else if (isa<FunctionTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Function_template;
+ DiagFoundKind = 0;
+ } else if (isa<VarTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Var_template;
+ DiagFoundKind = 2;
+ } else if (isa<ClassTemplateDecl>(Template) ||
+ isa<TypeAliasTemplateDecl>(Template) ||
+ isa<BuiltinTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Type_template;
+ DiagFoundKind = 1;
+ } else {
+ assert(false && "Unexpected Decl");
+ }
+
+ if (Kind == Param->templateParameterKind()) {
+ return true;
+ }
+
+ unsigned DiagKind = 0;
+ switch (Param->templateParameterKind()) {
+ case TemplateNameKind::TNK_Concept_template:
+ DiagKind = 2;
+ break;
+ case TemplateNameKind::TNK_Var_template:
+ DiagKind = 1;
+ break;
+ default:
+ DiagKind = 0;
+ break;
+ }
+ Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template)
+ << DiagKind;
+ Diag(Template->getLocation(), diag::note_template_arg_refers_to_template_here)
+ << DiagFoundKind << Template;
+ return false;
+}
+
+/// Check a template argument against its corresponding
+/// template template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.template].
+/// It returns true if an error occurred, and false otherwise.
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
TemplateArgumentLoc &Arg,
@@ -7597,27 +7791,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
if (Template->isInvalidDecl())
return true;
- // C++0x [temp.arg.template]p1:
- // A template-argument for a template template-parameter shall be
- // the name of a class template or an alias template, expressed as an
- // id-expression. When the template-argument names a class template, only
- // primary class templates are considered when matching the
- // template template argument with the corresponding parameter;
- // partial specializations are not considered even if their
- // parameter lists match that of the template template parameter.
- //
- // Note that we also allow template template parameters here, which
- // will happen when we are dealing with, e.g., class template
- // partial specializations.
- if (!isa<ClassTemplateDecl>(Template) &&
- !isa<TemplateTemplateParmDecl>(Template) &&
- !isa<TypeAliasTemplateDecl>(Template) &&
- !isa<BuiltinTemplateDecl>(Template)) {
- assert(isa<FunctionTemplateDecl>(Template) &&
- "Only function templates are possible here");
- Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template);
- Diag(Template->getLocation(), diag::note_template_arg_refers_here_func)
- << Template;
+ if (!CheckDeclCompatibleWithTemplateTemplate(Template, Param, Arg)) {
+ return true;
}
// C++1z [temp.arg.template]p3: (DR 150)
@@ -7689,6 +7864,10 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) {
diag::note_template_param_external);
}
+/// Given a non-type template argument that refers to a
+/// declaration and the type of its corresponding non-type template
+/// parameter, produce an expression that properly refers to that
+/// declaration.
ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
NamedDecl *TemplateParam) {
@@ -8009,34 +8188,40 @@ static bool MatchTemplateParameterKind(
return false;
}
-
// For non-type template parameters, check the type of the parameter.
- if (NonTypeTemplateParmDecl *OldNTTP
- = dyn_cast<NonTypeTemplateParmDecl>(Old)) {
+ if (NonTypeTemplateParmDecl *OldNTTP =
+ dyn_cast<NonTypeTemplateParmDecl>(Old)) {
NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New);
- // C++20 [temp.over.link]p6:
- // Two [non-type] template-parameters are equivalent [if] they have
- // equivalent types ignoring the use of type-constraints for
- // placeholder types
- QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
- QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
- if (!S.Context.hasSameType(OldType, NewType)) {
- if (Complain) {
- unsigned NextDiag = diag::err_template_nontype_parm_different_type;
- if (TemplateArgLoc.isValid()) {
- S.Diag(TemplateArgLoc,
- diag::err_template_arg_template_params_mismatch);
- NextDiag = diag::note_template_nontype_parm_different_type;
+ // If we are matching a template template argument to a template
+ // template parameter and one of the non-type template parameter types
+ // is dependent, then we must wait until template instantiation time
+ // to actually compare the arguments.
+ if (Kind != Sema::TPL_TemplateTemplateParmMatch ||
+ (!OldNTTP->getType()->isDependentType() &&
+ !NewNTTP->getType()->isDependentType())) {
+ // C++20 [temp.over.link]p6:
+ // Two [non-type] template-parameters are equivalent [if] they have
+ // equivalent types ignoring the use of type-constraints for
+ // placeholder types
+ QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
+ QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
+ if (!S.Context.hasSameType(OldType, NewType)) {
+ if (Complain) {
+ unsigned NextDiag = diag::err_template_nontype_parm_different_type;
+ if (TemplateArgLoc.isValid()) {
+ S.Diag(TemplateArgLoc,
+ diag::err_template_arg_template_params_mismatch);
+ NextDiag = diag::note_template_nontype_parm_different_type;
+ }
+ S.Diag(NewNTTP->getLocation(), NextDiag)
+ << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
+ S.Diag(OldNTTP->getLocation(),
+ diag::note_template_nontype_parm_prev_declaration)
+ << OldNTTP->getType();
}
- S.Diag(NewNTTP->getLocation(), NextDiag)
- << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
- S.Diag(OldNTTP->getLocation(),
- diag::note_template_nontype_parm_prev_declaration)
- << OldNTTP->getType();
+ return false;
}
-
- return false;
}
}
// For template template parameters, check the template parameter types.
@@ -8045,6 +8230,8 @@ static bool MatchTemplateParameterKind(
else if (TemplateTemplateParmDecl *OldTTP =
dyn_cast<TemplateTemplateParmDecl>(Old)) {
TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
+ if (OldTTP->templateParameterKind() != NewTTP->templateParameterKind())
+ return false;
if (!S.TemplateParameterListsAreEqual(
NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
OldTTP->getTemplateParameters(), Complain,
@@ -8056,6 +8243,7 @@ static bool MatchTemplateParameterKind(
}
if (Kind != Sema::TPL_TemplateParamsEquivalent &&
+ Kind != Sema::TPL_TemplateTemplateParmMatch &&
!isa<TemplateTemplateParmDecl>(Old)) {
const Expr *NewC = nullptr, *OldC = nullptr;
@@ -8421,6 +8609,11 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
if (isa<NonTypeTemplateParmDecl>(DRE->getDecl()))
continue;
+ if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(ArgExpr);
+ ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+ continue;
+ }
+
// C++ [temp.class.spec]p9:
// Within the argument list of a class template partial
// specialization, the following restrictions apply:
@@ -9032,13 +9225,15 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl());
}
-bool Sema::CheckConceptUseInDefinition(ConceptDecl *Concept,
- SourceLocation Loc) {
- if (!Concept->isInvalidDecl() && !Concept->hasDefinition()) {
- Diag(Loc, diag::err_recursive_concept) << Concept;
- Diag(Concept->getLocation(), diag::note_declared_at);
+bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
+ if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
+ CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
+ Diag(Loc, diag::err_recursive_concept) << CE;
+ Diag(CE->getLocation(), diag::note_declared_at);
return true;
}
+ // Concept template parameters don't have a definition and can't
+ // be defined recursively.
return false;
}