saar.raz created this revision. saar.raz added reviewers: nwilson, hubert.reinterpretcast, changyu, rsmith, faisalv, Quuxplusone. Herald added a subscriber: cfe-commits.
Added support for constrained template parameters, both simple and with partial template arguments. Depends on https://reviews.llvm.org/D43357. Repository: rC Clang https://reviews.llvm.org/D44352 Files: include/clang/AST/DeclTemplate.h include/clang/AST/RecursiveASTVisitor.h include/clang/AST/TemplateBase.h include/clang/Basic/DiagnosticParseKinds.td include/clang/Parse/Parser.h include/clang/Sema/Sema.h lib/AST/ASTContext.cpp lib/AST/ASTDumper.cpp lib/AST/ASTImporter.cpp lib/AST/DeclTemplate.cpp lib/AST/ODRHash.cpp lib/Parse/ParseExprCXX.cpp lib/Parse/ParseTemplate.cpp lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaConcept.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateDeduction.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp lib/Serialization/ASTReader.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp lib/Serialization/ASTWriterDecl.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp test/CXX/concepts-ts/temp/temp.param/p10.cpp test/Parser/cxx-constrained-template-param-with-partial-id.cpp test/Parser/cxx-constrained-template-param.cpp tools/libclang/CIndex.cpp
Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -750,6 +750,10 @@ } bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + if (Expr *CE = D->getConstraintExpression()) + if (Visit(MakeCXCursor(CE, StmtParent, TU, RegionOfInterest))) + return true; + // Visit the default argument. if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) if (TypeSourceInfo *DefArg = D->getDefaultArgumentInfo()) @@ -898,6 +902,10 @@ bool CursorVisitor::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { if (VisitDeclaratorDecl(D)) return true; + + if (Expr *CE = D->getConstraintExpression()) + if (Visit(MakeCXCursor(CE, StmtParent, TU, RegionOfInterest))) + return true; if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) if (Expr *DefArg = D->getDefaultArgument()) @@ -929,7 +937,11 @@ bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { if (VisitTemplateParameters(D->getTemplateParameters())) return true; - + + if (Expr *CE = D->getConstraintExpression()) + if (Visit(MakeCXCursor(CE, StmtParent, TU, RegionOfInterest))) + return true; + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited() && VisitTemplateArgumentLoc(D->getDefaultArgument())) return true; Index: test/Parser/cxx-constrained-template-param.cpp =================================================================== --- /dev/null +++ test/Parser/cxx-constrained-template-param.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify +// expected-no-diagnostics + +namespace type +{ + template<typename T> + concept C1 = true; + + template<C1 T, C1 U = int> + using A = T[10]; + + using a = A<int>; + + namespace ns { + template<typename T, int a = 0> + concept C2 = true; + } + + template<ns::C2 T1, ::type::ns::C2 T2> requires sizeof(T1) <= sizeof(T2) + struct B { }; + + using b = B<int, int>; + + template<ns::C2... T1> + struct C { }; + + using c1 = C<char, char, char>; + using c2 = C<char, char, char, char>; +} + +namespace non_type +{ + template<int v> + concept C1 = true; + + template<C1 v, C1 u = 0> + int A = v; + + int& a = A<1>; + + namespace ns { + template<bool x, typename T = int> + concept C2 = true; + } + + template<ns::C2 v1, ::non_type::ns::C2 v2> requires sizeof(v1) <= sizeof(v2) + struct B { }; + + using b = B<true, false>; + + template<ns::C2... T1> + struct C { }; + + using c1 = C<false, true, false>; + using c2 = C<false, true, false, false>; +} + +namespace temp +{ + template<typename> + struct test1 { }; + + template<typename> + struct test2 { }; + + template<template<typename> typename T> + concept C1 = true; + + template<C1 TT, C1 UU = test1> + using A = TT<int>; + + using a = A<test1>; + + namespace ns { + template<template<typename> typename... TT> + concept C2 = true; + } + + template<ns::C2 TT1, ::temp::ns::C2 TT2> + requires sizeof(TT1<int>) <= sizeof(TT2<int>) + struct B { }; + + using b = B<test1, test2>; + + template<ns::C2... T1> + struct C { }; + + using c1 = C<test1>; + using c2 = C<test1, test2, test2>; +} \ No newline at end of file Index: test/Parser/cxx-constrained-template-param-with-partial-id.cpp =================================================================== --- /dev/null +++ test/Parser/cxx-constrained-template-param-with-partial-id.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template<typename T, int a> +concept C1 = true; + +template<C1 T> // expected-error {{concept 'C1' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +using badA = T[10]; + +template<C1<0> T> +using A = T[10]; + +using a = A<int>; + +namespace ns { + template<typename T, typename U, typename... X> + concept C2 = true; +} + +template<ns::C2 T1, ::ns::C2 T2> // expected-error 2{{concept 'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +requires sizeof(T1) <= sizeof(T2) +struct badB { }; + +template<ns::C2<int> T1, ::ns::C2<char, T1> T2> + requires sizeof(T1) <= sizeof(T2) +struct B { }; + +using b = B<int, int>; + +template<ns::C2... T1> // expected-error {{concept 'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +struct badC { }; + +template<ns::C2<int>... T1> +struct C { }; + +using c1 = C<char, char, char>; +using c2 = C<char, char, char, char>; \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.param/p10.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.param/p10.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> +concept C1 = sizeof(T) == 1; // expected-note 2{{because 'sizeof(short) == 1' (2 == 1) evaluated to false}} expected-note {{because 'sizeof(int) == 1' (4 == 1) evaluated to false}} + +template<C1 T> // expected-note {{because 'int' does not satisfy 'C1'}} +using A = T; + +using a1 = A<int>; // expected-error {{constraints not satisfied for alias template 'A' [with T = int]}} +using a2 = A<char>; + +template<typename T> +concept C2 = sizeof(T) == 2; // expected-note 2{{because 'sizeof(char) == 2' (1 == 2) evaluated to false}} + +template<C1 T1, C2 T2> // expected-note 2{{because 'short' does not satisfy 'C1'}} expected-note {{because 'char' does not satisfy 'C2'}} expected-note {{and 'char' does not satisfy 'C2'}} +using B = T1; + +using b1 = B<char, short>; +using b2 = B<char, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = char, T2 = char]}} +using b3 = B<short, short>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = short]}} +using b4 = B<short, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = char]}} + +template<typename... T> +concept C3 = (sizeof(T) + ...) == 12; // expected-note {{because 'sizeof(char [11]) == 12' (11 == 12) evaluated to false}} expected-note {{because 'sizeof(char [10]) == 12' (10 == 12) evaluated to false}} expected-note {{because 'sizeof(char [12]) + sizeof(int [3]) + sizeof(short [6]) == 12' (36 == 12) evaluated to false}} + +template<C3 T1, C3 T2, C3 T3> // expected-note {{because 'char [11]' does not satisfy 'C3'}} expected-note {{and 'char [10]' does not satisfy 'C3'}} +using C = T2; + +using c1 = C<char[12], int[3], short[6]>; +using c2 = C<char[12], char[11], char[10]>; // expected-error {{constraints not satisfied for alias template 'C' [with T1 = char [12], T2 = char [11], T3 = char [10]]}} + +template<C3... Ts> // expected-note {{because 'C3<char [12], int [3], short [6]>' evaluated to false}} +using D = int; + +using d1 = D<char[12], int[3], short[6]>; // expected-error {{constraints not satisfied for alias template 'D' [with Ts = <char [12], int [3], short [6]>}} +using d2 = D<int, int, int>; +using d3 = D<short, short, short, short, short, short>; + +template<typename T> +concept C4 = sizeof(T) == 4; // expected-note 3{{because 'sizeof(char) == 4' (1 == 4) evaluated to false}} + +template<C4... Ts> // expected-note 2{{because 'char' does not satisfy 'C4'}} expected-note {{and 'char' does not satisfy 'C4'}} +using E = int; + +using e1 = E<int>; +using e2 = E<char, int>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, int>]}} +using e3 = E<char, char>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, char>]}} +using e4 = E<>; + +template<typename T, typename U> +constexpr bool is_same_v = false; + +template<typename T> +constexpr bool is_same_v<T, T> = true; + +template<typename T, typename U> +concept Same = is_same_v<T, U>; // expected-note {{because 'is_same_v<long, int>' evaluated to false}} + +template<Same<int> T> // expected-error {{because 'Same<long, int>' evaluated to false}} +using F = T; + +using f1 = F<int>; +using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}} + +template<typename T, typename... Ts> +concept OneOf = (is_same_v<T, Ts> || ...); // expected-note 2{{because 'is_same_v<char, char [1]>' evaluated to false}} expected-note 2{{and 'is_same_v<char, char [2]>' evaluated to false}} expected-note {{because 'is_same_v<short, int>' evaluated to false}} expected-note {{and 'is_same_v<short, long>' evaluated to false}} expected-note {{and 'is_same_v<short, char>' evaluated to false}} + +template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U> // expected-note 2{{because 'OneOf<char, char [1], char [2]' evaluated to false}} expected-note {{because 'OneOf<short, int, long, char>' evaluated to false}} expected-note {{and 'OneOf<short, int, long, char>' evaluated to false}} +using G = T; + +using g1 = G<char[1], int>; +using g2 = G<char, int>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = int]}} +using g3 = G<char[1], short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char [1], U = short]}} +using g4 = G<char, short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = short]}} + +template<OneOf<char[1], char[2]>... Ts> +using H = T; + +using h1 = H<char[1], int>; +using h2 = H<int, int>; Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp @@ -7,6 +7,14 @@ template <typename U> requires bool(U()) struct A; +template<typename T> +concept C1 = true; + +template <C1 T> requires bool(T()) +struct B; +template <C1 U> requires bool(U()) +struct B; + } // end namespace nodiag namespace diag { @@ -24,6 +32,14 @@ template <typename T> requires !0 // expected-error{{associated constraints differ in template redeclaration}} struct C; +template<typename T> +concept C1 = true; + +template <C1 T> // expected-note{{previous template declaration is here}} +struct D; +template <typename T> requires C1<T> // expected-error{{associated constraints differ in template redeclaration}} +struct D; + } // end namespace diag namespace nodiag { Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1567,6 +1567,11 @@ Record.push_back(D->wasDeclaredWithTypename()); + Expr *CE = D->getConstraintExpression(); + Record.push_back(CE != nullptr); + if (CE) + Record.AddStmt(CE); + bool OwnsDefaultArg = D->hasDefaultArgument() && !D->defaultArgumentWasInherited(); Record.push_back(OwnsDefaultArg); @@ -1598,6 +1603,10 @@ } else { // Rest of NonTypeTemplateParmDecl. Record.push_back(D->isParameterPack()); + Expr *CE = D->getConstraintExpression(); + Record.push_back(CE != nullptr); + if (CE) + Record.AddStmt(CE); bool OwnsDefaultArg = D->hasDefaultArgument() && !D->defaultArgumentWasInherited(); Record.push_back(OwnsDefaultArg); @@ -1627,6 +1636,10 @@ } else { // Rest of TemplateTemplateParmDecl. Record.push_back(D->isParameterPack()); + Expr *CE = D->getConstraintExpression(); + Record.push_back(CE != nullptr); + if (CE) + Record.AddStmt(CE); bool OwnsDefaultArg = D->hasDefaultArgument() && !D->defaultArgumentWasInherited(); Record.push_back(OwnsDefaultArg); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5853,7 +5853,7 @@ Record->push_back(TemplateParams->size()); for (const auto &P : *TemplateParams) - AddDeclRef(P); // TODO: Concepts - constrained parameters. + AddDeclRef(P); if (const Expr *RequiresClause = TemplateParams->getRequiresClause()) { Record->push_back(true); AddStmt(const_cast<Expr*>(RequiresClause)); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -2287,6 +2287,9 @@ D->setDeclaredWithTypename(Record.readInt()); + if (Record.readInt()) + D->setConstraintExpression(Record.readExpr()); + if (Record.readInt()) D->setDefaultArgument(GetTypeSourceInfo()); } @@ -2306,6 +2309,8 @@ } else { // Rest of NonTypeTemplateParmDecl. D->ParameterPack = Record.readInt(); + if (Record.readInt()) + D->setConstraintExpression(Record.readExpr()); if (Record.readInt()) D->setDefaultArgument(Record.readExpr()); } @@ -2325,6 +2330,8 @@ } else { // Rest of TemplateTemplateParmDecl. D->ParameterPack = Record.readInt(); + if (Record.readInt()) + D->setConstraintExpression(Record.readExpr()); if (Record.readInt()) D->setDefaultArgument(Reader.getContext(), Record.readTemplateArgumentLoc()); Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -8664,7 +8664,6 @@ Params.reserve(NumParams); while (NumParams--) Params.push_back(ReadDeclAs<NamedDecl>(F, Record, Idx)); - // TODO: Concepts: Constrained parameters bool HasRequiresClause = Record[Idx++]; Expr *RequiresClause = HasRequiresClause ? ReadExpr(F) : nullptr; Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2181,7 +2181,12 @@ D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(), D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack()); Inst->setAccess(AS_public); - + if (Expr *CE = D->getConstraintExpression()) { + ExprResult Result = SemaRef.SubstExpr(CE, TemplateArgs); + if (Result.isInvalid()) + return nullptr; + Inst->setConstraintExpression(Result.get()); + } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { TypeSourceInfo *InstantiatedDefaultArg = SemaRef.SubstType(D->getDefaultArgumentInfo(), TemplateArgs, @@ -2330,6 +2335,12 @@ if (Invalid) Param->setInvalidDecl(); + if (Expr *CE = D->getConstraintExpression()) { + ExprResult Result = SemaRef.SubstExpr(CE, TemplateArgs); + if (Result.isInvalid()) + return nullptr; + Param->setConstraintExpression(Result.get()); + } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { EnterExpressionEvaluationContext ConstantEvaluated( SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); @@ -2454,6 +2465,12 @@ SemaRef.Context, Owner, D->getLocation(), D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getPosition(), D->isParameterPack(), D->getIdentifier(), InstParams); + if (Expr *CE = D->getConstraintExpression()) { + ExprResult Result = SemaRef.SubstExpr(CE, TemplateArgs); + if (Result.isInvalid()) + return nullptr; + Param->setConstraintExpression(Result.get()); + } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { NestedNameSpecifierLoc QualifierLoc = D->getDefaultArgument().getTemplateQualifierLoc(); @@ -3262,10 +3279,7 @@ void *InsertPos = nullptr; ClassTemplateSpecializationDecl *PrevDecl = ClassTemplate->findPartialSpecialization(Converted, - // TODO: Concepts - change this - // to associated constraints once - // we have them. - InstParams->getRequiresClause(), + InstParams->getAssociatedConstraints(), InsertPos); // Build the canonical type that describes the converted template @@ -3398,10 +3412,7 @@ void *InsertPos = nullptr; VarTemplateSpecializationDecl *PrevDecl = VarTemplate->findPartialSpecialization(Converted, - // TODO: Concepts - change this - // to associated constraints once - // we have them. - InstParams->getRequiresClause(), + InstParams->getAssociatedConstraints(), InsertPos); // Build the canonical type that describes the converted template Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -4437,7 +4437,7 @@ QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0); NamedDecl *TemplParamPtr = TemplParam; FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt( - Loc, Loc, TemplParamPtr, Loc, nullptr); + Context, Loc, Loc, TemplParamPtr, Loc, nullptr, nullptr); QualType FuncParam = SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false) Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -904,27 +904,31 @@ return QualType(); } -Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, +Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, + const DeclSpec &DS, + SourceLocation StartLoc, + TypeSourceInfo *TInfo, + IdentifierInfo *ParamName, + SourceLocation ParamNameLoc, + bool IsParameterPack, unsigned Depth, unsigned Position, SourceLocation EqualLoc, Expr *Default) { - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); // Check that we have valid decl-specifiers specified. - auto CheckValidDeclSpecifiers = [this, &D] { + auto CheckValidDeclSpecifiers = [this, &DS] { // C++ [temp.param] // p1 - // template-parameter: - // ... - // parameter-declaration + // template-parameter: + // ... + // parameter-declaration // p2 // ... A storage class shall not be specified in a template-parameter // declaration. // [dcl.typedef]p1: // The typedef specifier [...] shall not be used in the decl-specifier-seq // of a parameter-declaration - const DeclSpec &DS = D.getDeclSpec(); auto EmitDiag = [this](SourceLocation Loc) { Diag(Loc, diag::err_invalid_decl_specifier_in_nontype_parm) << FixItHint::CreateRemoval(Loc); @@ -966,37 +970,34 @@ CheckValidDeclSpecifiers(); if (TInfo->getType()->isUndeducedType()) { - Diag(D.getIdentifierLoc(), + Diag(ParamNameLoc, diag::warn_cxx14_compat_template_nontype_parm_auto_type) << QualType(TInfo->getType()->getContainedAutoType(), 0); } assert(S->isTemplateParamScope() && "Non-type template parameter not in template parameter scope!"); bool Invalid = false; - QualType T = CheckNonTypeTemplateParameterType(TInfo, D.getIdentifierLoc()); + QualType T = CheckNonTypeTemplateParameterType(TInfo, ParamNameLoc); if (T.isNull()) { T = Context.IntTy; // Recover with an 'int' type. Invalid = true; } - IdentifierInfo *ParamName = D.getIdentifier(); - bool IsParameterPack = D.hasEllipsis(); NonTypeTemplateParmDecl *Param = NonTypeTemplateParmDecl::Create(Context, Context.getTranslationUnitDecl(), - D.getLocStart(), - D.getIdentifierLoc(), + StartLoc, + ParamNameLoc, Depth, Position, ParamName, T, IsParameterPack, TInfo); Param->setAccess(AS_public); if (Invalid) Param->setInvalidDecl(); if (ParamName) { - maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(), - ParamName); + maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName); // Add the template parameter into the current scope. S->AddDecl(Param); @@ -2004,12 +2005,11 @@ if (OldParams) OldParam = OldParams->begin(); - // TODO: Concepts: Replace getRequiresClause with getAssociatedConstraints - // when we have it. if (OldParams && - !CheckRedeclarationConstraintMatch(OldParams->getRequiresClause(), - NewParams->getRequiresClause())) { - DiagnoseRedeclarationConstraintMismatch(OldParams, NewParams); + !CheckRedeclarationConstraintMatch(OldParams->getAssociatedConstraints(), + NewParams->getAssociatedConstraints())){ + DiagnoseRedeclarationConstraintMismatch(OldParams->getTemplateLoc(), + NewParams->getTemplateLoc()); Invalid = true; } @@ -3595,9 +3595,7 @@ if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(), Converted) && (!Context.getLangOpts().ConceptsTS - // TODO: Concepts: change this to getAssociatedConstraints when we - // have them. - || TemplateParams->getRequiresClause() == nullptr)) { + || TemplateParams->getAssociatedConstraints() == nullptr)) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical @@ -3618,10 +3616,7 @@ if (IsPartialSpecialization) // FIXME: Template parameter list matters too PrevDecl = VarTemplate->findPartialSpecialization(Converted, - // TODO: Concepts - replace with - // AssociatedConstraints once we - // have them. - TemplateParams->getRequiresClause(), + TemplateParams->getAssociatedConstraints(), InsertPos); else PrevDecl = VarTemplate->findSpecialization(Converted, InsertPos); @@ -3931,7 +3926,6 @@ ExprResult Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, - const DeclarationNameInfo &NameInfo, ConceptDecl *Template, SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs) { @@ -3944,7 +3938,7 @@ Converted, /*UpdateArgsWithConversions=*/false)) return ExprError(); - return CreateConceptSpecializationExpr(NameInfo.getLoc(), Template, + return CreateConceptSpecializationExpr(TemplateLoc, Template, TemplateArgs); } @@ -3979,9 +3973,8 @@ } if (R.getAsSingle<ConceptDecl>()) { - return CheckConceptTemplateId(SS, R.getLookupNameInfo(), - R.getAsSingle<ConceptDecl>(), - TemplateKWLoc, TemplateArgs); + return CheckConceptTemplateId(SS, R.getAsSingle<ConceptDecl>(), + R.getNameLoc(), TemplateArgs); } // We don't want lookup warnings at this point. @@ -6768,7 +6761,6 @@ bool Complain, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { - // TODO: Concepts: Check constrained-parameter constraints here. // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -6813,55 +6805,67 @@ return false; } + Expr *OldCE, *NewCE; + // For non-type template parameters, check the type of the parameter. if (NonTypeTemplateParmDecl *OldNTTP = dyn_cast<NonTypeTemplateParmDecl>(Old)) { NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New); + OldCE = OldNTTP->getConstraintExpression(); + NewCE = NewNTTP->getConstraintExpression(); // 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_TemplateTemplateArgumentMatch && - (OldNTTP->getType()->isDependentType() || - NewNTTP->getType()->isDependentType())) - return true; - - if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { - 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 (Kind != Sema::TPL_TemplateTemplateArgumentMatch || + (!OldNTTP->getType()->isDependentType() && + !NewNTTP->getType()->isDependentType())) + if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { + 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 true; + return false; + } } - // For template template parameters, check the template parameter types. // The template parameter lists of template template // parameters must agree. - if (TemplateTemplateParmDecl *OldTTP + else if (TemplateTemplateParmDecl *OldTTP = dyn_cast<TemplateTemplateParmDecl>(Old)) { TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New); - return S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), - OldTTP->getTemplateParameters(), - Complain, + OldCE = OldTTP->getConstraintExpression(); + NewCE = NewTTP->getConstraintExpression(); + if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), + OldTTP->getTemplateParameters(), + Complain, (Kind == Sema::TPL_TemplateMatch ? Sema::TPL_TemplateTemplateParmMatch : Kind), - TemplateArgLoc); + TemplateArgLoc)) + return false; + } else { + OldCE = cast<TemplateTypeParmDecl>(Old)->getConstraintExpression(); + NewCE = cast<TemplateTypeParmDecl>(New)->getConstraintExpression(); + } + + if (!S.CheckRedeclarationConstraintMatch(OldCE, NewCE)) { + if (Complain) + S.DiagnoseRedeclarationConstraintMismatch(Old->getLocStart(), + New->getLocStart()); + return false; } return true; @@ -6978,10 +6982,14 @@ return false; } - if (!CheckRedeclarationConstraintMatch(Old->getRequiresClause(), - New->getRequiresClause())) { + Expr *OldRC = Old->getRequiresClause(), + *NewRC = New->getRequiresClause(); + if (!CheckRedeclarationConstraintMatch(OldRC, NewRC)) { if (Complain) - DiagnoseRedeclarationConstraintMismatch(Old, New); + DiagnoseRedeclarationConstraintMismatch(OldRC ? OldRC->getLocStart() + : Old->getTemplateLoc(), + NewRC ? NewRC->getLocStart() + : New->getTemplateLoc()); return false; } @@ -7535,10 +7543,7 @@ if (isPartialSpecialization) // FIXME: Template parameter list matters, too PrevDecl = ClassTemplate->findPartialSpecialization(Converted, - // TODO: Concepts: Replace with - // AssociatedConstraints once we - // have them. - TemplateParams->getRequiresClause(), + TemplateParams->getAssociatedConstraints(), InsertPos); else PrevDecl = ClassTemplate->findSpecialization(Converted, InsertPos); @@ -7565,9 +7570,7 @@ if (Context.hasSameType(CanonType, ClassTemplate->getInjectedClassNameSpecialization()) && (!Context.getLangOpts().ConceptsTS - // TODO: Concepts: change this to getAssociatedConstraints when we - // have them. - || TemplateParams->getRequiresClause() == nullptr)) { + || TemplateParams->getAssociatedConstraints() == nullptr)) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical Index: lib/Sema/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -61,13 +61,11 @@ } void -Sema::DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, - const TemplateParameterList *New){ - Diag(New->getTemplateLoc(), - diag::err_template_different_associated_constraints); +Sema::DiagnoseRedeclarationConstraintMismatch(SourceLocation Old, + SourceLocation New){ + Diag(New, diag::err_template_different_associated_constraints); - Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) - << /*declaration*/0; + Diag(Old, diag::note_template_prev_declaration) << /*declaration*/0; } template<typename TemplateDeclT> Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -462,6 +462,7 @@ /// 'true' if the identifier is treated as if it was followed by ':', /// not '::'. /// \param OnlyNamespace If true, only considers namespaces in lookup. +/// \param SuppressDiagnostics If true, will not emit diagnostics on an error. /// /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in /// that it contains an extra parameter \p ScopeLookupResult, which provides @@ -479,7 +480,8 @@ NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, bool *IsCorrectedToColon, - bool OnlyNamespace) { + bool OnlyNamespace, + bool SuppressDiagnostics) { if (IdInfo.Identifier->isEditorPlaceholder()) return true; LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, @@ -574,7 +576,7 @@ return false; } - if (Found.empty() && !ErrorRecoveryLookup) { + if (Found.empty() && !ErrorRecoveryLookup && !SuppressDiagnostics) { // If identifier is not found as class-name-or-namespace-name, but is found // as other entity, don't look for typos. LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName); @@ -608,7 +610,8 @@ } } - if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) { + if (Found.empty() && !ErrorRecoveryLookup && !SuppressDiagnostics + && !getLangOpts().MSVCCompat) { // We haven't found anything, and we're not recovering from a // different kind of error, so look for typos. DeclarationName Name = Found.getLookupName(); @@ -678,7 +681,7 @@ !Context.hasSameType( Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)), Context.getTypeDeclType(cast<TypeDecl>(SD))))) { - if (ErrorRecoveryLookup) + if (ErrorRecoveryLookup || SuppressDiagnostics) return true; Diag(IdInfo.IdentifierLoc, @@ -760,7 +763,7 @@ // Otherwise, we have an error case. If we don't want diagnostics, just // return an error now. - if (ErrorRecoveryLookup) + if (ErrorRecoveryLookup || SuppressDiagnostics) return true; // If we didn't find anything during our lookup, try again with @@ -828,13 +831,15 @@ bool EnteringContext, CXXScopeSpec &SS, bool ErrorRecoveryLookup, bool *IsCorrectedToColon, - bool OnlyNamespace) { + bool OnlyNamespace, + bool SuppressDiagnostic) { if (SS.isInvalid()) return true; return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, /*ScopeLookupResult=*/nullptr, false, - IsCorrectedToColon, OnlyNamespace); + IsCorrectedToColon, OnlyNamespace, + SuppressDiagnostic); } bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS, Index: lib/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" @@ -532,29 +533,121 @@ /// template-parameter: [C++ temp.param] /// type-parameter /// parameter-declaration +/// constrained-parameter +/// +/// type-parameter: (See below) +/// type-parameter-key ...[opt] identifier[opt] +/// type-parameter-key identifier[opt] = type-id +/// 'template' '<' template-parameter-list '>' type-parameter-key +/// ...[opt] identifier[opt] +/// 'template' '<' template-parameter-list '>' type-parameter-key +/// identifier[opt] '=' id-expression +/// +/// type-parameter-key: +/// class +/// typename +/// +/// constrained-parameter: +/// qualified-concept-name ... identifier[opt] +/// qualified-concept-name identifier[opt] +/// default-template-argument[opt] +/// +/// qualified-concept-name: +/// nested-name-specifier[opt] concept-name +/// nested-name-specifier[opt] partial-concept-id +/// +/// partial-concept-id: +/// concept-name '<' template-argument-list[opt] '>' +/// +/// default-template-argument: +/// = type-id +/// = id-expression +/// = initializer-clause /// -/// type-parameter: (see below) -/// 'class' ...[opt] identifier[opt] -/// 'class' identifier[opt] '=' type-id -/// 'typename' ...[opt] identifier[opt] -/// 'typename' identifier[opt] '=' type-id -/// 'template' '<' template-parameter-list '>' -/// 'class' ...[opt] identifier[opt] -/// 'template' '<' template-parameter-list '>' 'class' identifier[opt] -/// = id-expression Decl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { if (isStartOfTemplateTypeParameter()) return ParseTypeParameter(Depth, Position); if (Tok.is(tok::kw_template)) return ParseTemplateTemplateParameter(Depth, Position); + // At this point we're either facing a constrained-parameter or a typename for + // a non type template parameter. + DeclResult CP = TryParseConstrainedTemplateParameter(Depth, Position); + if (!CP.isUnset()) + return CP.isInvalid() ? nullptr : CP.get(); + // If it's none of the above, then it must be a parameter declaration. // NOTE: This will pick up errors in the closure of the template parameter // list (e.g., template < ; Check here to implement >> style closures. return ParseNonTypeTemplateParameter(Depth, Position); } + +DeclResult +Parser::TryParseConstrainedTemplateParameter(unsigned Depth, unsigned Position){ + TentativeParsingAction TPA(*this); + CXXScopeSpec SS; + SourceLocation ParamStartLoc = Tok.getLocation(); + + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, + /*LastII=*/nullptr, + /*OnlyNamespace=*/true, + /*SuppressDiagnostics=*/true)) { + TPA.Revert(); + return{}; + } + + if (!Tok.is(tok::identifier)) { + TPA.Revert(); + return{}; + } + + UnqualifiedId PossibleConceptName; + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + ConsumeToken(); + + TemplateTy PossibleConcept; + bool MemberOfUnknownSpecialization = false; + auto TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + PossibleConceptName, + /*ObjectType=*/ParsedType(), + /*EnteringContext=*/false, + PossibleConcept, + MemberOfUnknownSpecialization); + assert(!MemberOfUnknownSpecialization + && "Member when we only allowed namespace scope qualifiers??"); + if (!PossibleConcept || TNK != TNK_Concept_template) { + TPA.Revert(); + return{}; + } + + TPA.Commit(); + + // At this point we're sure we're dealing with a constrained parameter. It + // may or may not have a template parameter list following the concept name. + if (Tok.is(tok::less)) { + if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS, + /*TemplateKWLoc=*/SourceLocation(), + PossibleConceptName, + /*AllowTypeAnnotation=*/false)) + return DeclResult(/*Invalid=*/true); + PossibleConceptName + .setTemplateId((TemplateIdAnnotation*)Tok.getAnnotationValue()); + ConsumeAnnotationToken(); + } + + auto *CD = cast<ConceptDecl>(PossibleConcept.get().getAsTemplateDecl()); + Decl *CP = ParseConstrainedTemplateParameter(Depth, Position, ParamStartLoc, + PossibleConceptName, CD); + return CP ? DeclResult(CP) : DeclResult(/*Invalid=*/true); +} + /// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]). /// Other kinds of template parameters are parsed in /// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter. @@ -774,11 +867,240 @@ } // Create the parameter. - return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl, + return Actions.ActOnNonTypeTemplateParameter(getCurScope(), + ParamDecl.getDeclSpec(), + ParamDecl.getLocStart(), + Actions.GetTypeForDeclarator( + ParamDecl, getCurScope()), + ParamDecl.getIdentifier(), + ParamDecl.getIdentifierLoc(), + ParamDecl.hasEllipsis(), Depth, Position, EqualLoc, DefaultArg.get()); } +Decl * +Parser::ParseConstrainedTemplateParameter(unsigned Depth, unsigned Position, + SourceLocation ParamStartLoc, + UnqualifiedId &ConceptName, + ConceptDecl *CD) { + + TemplateArgumentListInfo TALI; + bool HasPartialConceptId = ConceptName.Kind == UnqualifiedId::IK_TemplateId; + + if (HasPartialConceptId) { + // partial-concept-id + TALI.setLAngleLoc(ConceptName.TemplateId->LAngleLoc); + TALI.setRAngleLoc(ConceptName.TemplateId->RAngleLoc); + // Translate the parser's template argument list into our AST format. + Actions.translateTemplateArguments( + MutableArrayRef<ParsedTemplateArgument>( + ConceptName.TemplateId->getTemplateArgs(), + ConceptName.TemplateId->NumArgs), TALI); + } + + // Grab the ellipsis (if given). + SourceLocation EllipsisLoc; + TryConsumeToken(tok::ellipsis, EllipsisLoc); + + // Grab the template parameter name (if given) + SourceLocation NameLoc; + IdentifierInfo *ParamName = nullptr; + if (Tok.is(tok::identifier)) { + ParamName = Tok.getIdentifierInfo(); + NameLoc = ConsumeToken(); + } else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater, + tok::greatergreater)) { + // Unnamed template parameter. Don't have to do anything here, just + // don't consume this token. + } else { + Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; + return nullptr; + } + + // Recover from misplaced ellipsis. + bool AlreadyHasEllipsis = EllipsisLoc.isValid(); + if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) + DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, + true); + + // Grab a default argument (if available). + // Per C++0x [basic.scope.pdecl]p9, we parse the default argument before + // we introduce the type parameter into the local scope. + SourceLocation EqualLoc; + + NamedDecl *DeclaredParm = nullptr; + NamedDecl *ConceptPrototypeParameter = *CD->getTemplateParameters()->begin(); + if (TemplateTypeParmDecl *TypeP = dyn_cast<TemplateTypeParmDecl>( + ConceptPrototypeParameter)) { + ParsedType DefaultArg; + if (TryConsumeToken(tok::equal, EqualLoc)) + DefaultArg = ParseTypeName(/*SourceRange=*/nullptr, + Declarator::TemplateTypeArgContext) + .get(); + DeclaredParm = cast_or_null<NamedDecl>( + Actions.ActOnTypeParameter(getCurScope(), + TypeP->wasDeclaredWithTypename(), + EllipsisLoc, ParamStartLoc, ParamName, + NameLoc, Depth, Position, EqualLoc, + DefaultArg)); + if (!DeclaredParm) + return nullptr; + + QualType Q(cast<TemplateTypeParmDecl>(DeclaredParm)->getTypeForDecl(), + 0); + if (!EllipsisLoc.isInvalid() + && CD->getTemplateParameters()->hasParameterPack()) + // C++ [temp.param]p11.1 + // If P declares a template parameter pack and C is a variadic concept, + // then A is the pack expansion P... . Otherwise, A is the + // id-expression P. + Q = Actions.Context.getPackExpansionType(Q, /*NumExpansions=*/None); + TALI.prependArgument( + TemplateArgumentLoc(TemplateArgument(Q), + TemplateArgumentLocInfo( + Actions.Context.getTrivialTypeSourceInfo(Q)))); + } else if (TemplateTemplateParmDecl *TemplateP = + dyn_cast<TemplateTemplateParmDecl>(ConceptPrototypeParameter)) { + ParsedTemplateArgument DefaultArg; + if (TryConsumeToken(tok::equal, EqualLoc)) { + DefaultArg = ParseTemplateTemplateArgument(); + if (DefaultArg.isInvalid()) { + Diag(Tok.getLocation(), + diag::err_default_template_template_parameter_not_template); + SkipUntil(tok::comma, tok::greater, tok::greatergreater, + StopAtSemi | StopBeforeMatch); + } + } + + + DeclaredParm = cast_or_null<NamedDecl>( + Actions.ActOnTemplateTemplateParameter(getCurScope(), + ParamStartLoc, + TemplateP->getTemplateParameters(), + EllipsisLoc, ParamName, + NameLoc, Depth, Position, + EqualLoc, DefaultArg)); + if (!DeclaredParm) + return nullptr; + TemplateName TemplName(cast_or_null<TemplateDecl>(DeclaredParm)); + + // C++ [temp.param]p11.1 + // If P declares a template parameter pack and C is a variadic concept, + // then A is the pack expansion P... . Otherwise, A is the + // id-expression P. + bool ShouldExpand = !EllipsisLoc.isInvalid() + && CD->getTemplateParameters()->hasParameterPack(); + + NestedNameSpecifierLocBuilder Builder; + TemplateArgumentLocInfo LocInf(Builder.getWithLocInContext(Actions.Context), + NameLoc, EllipsisLoc); + TemplateArgumentLoc TAL(ShouldExpand + ? TemplateArgument(TemplName, Optional<unsigned>()) + : TemplateArgument(TemplName), LocInf); + TALI.prependArgument(TAL); + } else if (NonTypeTemplateParmDecl *NonTypeP = + dyn_cast<NonTypeTemplateParmDecl>(ConceptPrototypeParameter)) { + ExprResult DefaultArg; + if (TryConsumeToken(tok::equal, EqualLoc)) { + // C++ [temp.param]p15: + // When parsing a default template-argument for a non-type + // template-parameter, the first non-nested > is taken as the + // end of the template-parameter-list rather than a greater-than + // operator. + GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + DefaultArg = Actions.CorrectDelayedTyposInExpr( + ParseAssignmentExpression()); + if (DefaultArg.isInvalid()) + SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); + } + + DeclaredParm = cast_or_null<NamedDecl>( + Actions.ActOnNonTypeTemplateParameter(getCurScope(), + DeclSpec(getAttrFactory()), + ParamStartLoc, + NonTypeP->getTypeSourceInfo(), + ParamName, + NameLoc, + EllipsisLoc.isValid(), + Depth, Position, EqualLoc, + DefaultArg.isInvalid() ? nullptr : + DefaultArg.get())); + if (!DeclaredParm) + return nullptr; + + ValueDecl *VD = cast<ValueDecl>(DeclaredParm); + Expr *DerivedArgument = + new (Actions.Context) DeclRefExpr(VD, + /*RefersToEnclosingVariableOrCapture=*/false, + VD->getType(), VK_LValue, NameLoc, + DeclarationNameLoc(VD->getDeclName())); + // C++ [temp.param]p11.1 + // If P declares a template parameter pack and C is a variadic concept, + // then A is the pack expansion P... . Otherwise, A is the + // id-expression P. + if (EllipsisLoc.isValid() + && CD->getTemplateParameters()->hasParameterPack()) + DerivedArgument = + new (Actions.Context) PackExpansionExpr(Actions.Context.DependentTy, + DerivedArgument, EllipsisLoc, + /*NumExpansions=*/None); + TemplateArgument DeclaredParmA(DerivedArgument); + TALI.prependArgument( + TemplateArgumentLoc(DeclaredParmA, + TemplateArgumentLocInfo(DerivedArgument))); + } else + llvm_unreachable("Unrecognized concept prototype parameter type."); + + // We now have an actual template parmeter declared - form the constraint + // expression and attach it to the declared parameter. + + // If the user did not use a partial concept id and the concept does not + // accept a single argument or parameter pack, fail now with a nicer error + // message + if (!HasPartialConceptId + && CD->getTemplateParameters()->getMinRequiredArguments() > 1) { + Diag(diag::err_constrained_parameter_missing_arguments) << CD; + return DeclaredParm; + } + + CXXScopeSpec SS; + ExprResult Result = Actions.CheckConceptTemplateId(SS, CD, ParamStartLoc, + &TALI); + if (Result.isInvalid() || !Result.isUsable()) + // Just ignore the constraint and attempt to continue. + return DeclaredParm; + + Expr *IntroducedConstraint = Result.get(); + if (EllipsisLoc.isValid() && !CD->getTemplateParameters()->hasParameterPack()) + // We have the following case: + // + // template<typename T> concept C1 = true; + // template<C1... T> struct s1; + // + // The constraint: (C1<T> && ...) + IntroducedConstraint = + Actions.ActOnCXXFoldExpr(/*LParenLoc=*/SourceLocation(), + IntroducedConstraint, tok::ampamp, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation()).get(); + + if (TemplateTypeParmDecl *TypeP = + dyn_cast<TemplateTypeParmDecl>(DeclaredParm)) + TypeP->setConstraintExpression(IntroducedConstraint); + else if (TemplateTemplateParmDecl *TemplateP = + dyn_cast<TemplateTemplateParmDecl>(DeclaredParm)) + TemplateP->setConstraintExpression(IntroducedConstraint); + else + cast<NonTypeTemplateParmDecl>(DeclaredParm) + ->setConstraintExpression(IntroducedConstraint); + + return DeclaredParm; +} + void Parser::DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc, SourceLocation CorrectLoc, bool AlreadyHasEllipsis, Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -143,14 +143,18 @@ /// /// \param OnlyNamespace If true, only considers namespaces in lookup. /// +/// \param SuppressDiagnostic If true, suppress diagnostic on incorrect scope +/// specifier. +/// /// \returns true if there was an error parsing a scope specifier bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ParsedType ObjectType, bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename, IdentifierInfo **LastII, - bool OnlyNamespace) { + bool OnlyNamespace, + bool SuppressDiagnostic) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); @@ -455,7 +459,7 @@ bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr; if (Actions.ActOnCXXNestedNameSpecifier( getCurScope(), IdInfo, EnteringContext, SS, false, - CorrectionFlagPtr, OnlyNamespace)) { + CorrectionFlagPtr, OnlyNamespace, SuppressDiagnostic)) { // Identifier is not recognized as a nested name, but we can have // mistyped '::' instead of ':'. if (CorrectionFlagPtr && IsCorrectedToColon) { @@ -476,6 +480,11 @@ // nested-name-specifier: // type-name '<' if (Next.is(tok::less)) { + if (OnlyNamespace) + // We can't have template-ids as part of a namespace scope specifier, + // the scope specifier must end here. + break; + TemplateTy Template; UnqualifiedId TemplateName; TemplateName.setIdentifier(&II, Tok.getLocation()); @@ -2440,14 +2449,17 @@ /// /// \param Result on a successful parse, contains the parsed unqualified-id. /// +/// \param SuppressDiag whether to suppress the diagnostic when an unqualified +/// id was not found at the current location. +/// /// \returns true if parsing fails, false otherwise. bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, bool AllowDestructorName, bool AllowConstructorName, bool AllowDeductionGuide, ParsedType ObjectType, SourceLocation& TemplateKWLoc, - UnqualifiedId &Result) { + UnqualifiedId &Result, bool SuppressDiag) { // Handle 'A::template B'. This is for template-ids which have not // already been annotated by ParseOptionalCXXScopeSpecifier(). @@ -2649,9 +2661,10 @@ Result.setDestructorName(TildeLoc, Ty, ClassNameLoc); return false; } - - Diag(Tok, diag::err_expected_unqualified_id) - << getLangOpts().CPlusPlus; + + if (!SuppressDiag) + Diag(Tok, diag::err_expected_unqualified_id) + << getLangOpts().CPlusPlus; return true; } Index: lib/AST/ODRHash.cpp =================================================================== --- lib/AST/ODRHash.cpp +++ lib/AST/ODRHash.cpp @@ -365,6 +365,11 @@ AddTemplateArgument(D->getDefaultArgument()); } + Expr *CE = D->getConstraintExpression(); + Hash.AddBoolean(CE != nullptr); + if (CE) + AddStmt(CE); + Inherited::VisitTemplateTypeParmDecl(D); } @@ -377,6 +382,11 @@ AddStmt(D->getDefaultArgument()); } + Expr *CE = D->getConstraintExpression(); + Hash.AddBoolean(CE != nullptr); + if (CE) + AddStmt(CE); + Inherited::VisitNonTypeTemplateParmDecl(D); } @@ -389,6 +399,11 @@ AddTemplateArgument(D->getDefaultArgument().getArgument()); } + Expr *CE = D->getConstraintExpression(); + Hash.AddBoolean(CE != nullptr); + if (CE) + AddStmt(CE); + Inherited::VisitTemplateTemplateParmDecl(D); } }; Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -43,14 +43,30 @@ // TemplateParameterList Implementation //===----------------------------------------------------------------------===// -TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc, +// Create a constraint expression as the conjunction (the "and") of two other +// constraint expressions. +static Expr *CreateConstraintConjunction(const ASTContext &C, Expr *A, Expr *B){ + if (!A) { + return B; + } + if (B) { + return new (C) BinaryOperator(A, B, BO_LAnd, C.BoolTy, VK_RValue, + OK_Ordinary, /*opLoc=*/SourceLocation(), + FPOptions()); + } + return A; +} + +TemplateParameterList::TemplateParameterList(const ASTContext& C, + SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, - Expr *RequiresClause) + Expr *RequiresClause, + Expr *ConstrainedParamsConstraints) : TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), NumParams(Params.size()), ContainsUnexpandedParameterPack(false), - HasRequiresClause(static_cast<bool>(RequiresClause)) { + HasAssociatedConstraints(ConstrainedParamsConstraints || RequiresClause) { for (unsigned Idx = 0; Idx < NumParams; ++Idx) { NamedDecl *P = Params[Idx]; begin()[Idx] = P; @@ -68,21 +84,37 @@ // template parameter list does too. } } - if (RequiresClause) { - *getTrailingObjects<Expr *>() = RequiresClause; + if (HasAssociatedConstraints) { + Expr **ACStorage = getTrailingObjects<Expr *>(); + ACStorage[0] = RequiresClause; + ACStorage[1] = CreateConstraintConjunction(C, RequiresClause, + ConstrainedParamsConstraints); } } TemplateParameterList * TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, Expr *RequiresClause) { + Expr *AC = nullptr; + for (NamedDecl *P : Params) + if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) { + if (Expr *CE = NTTP->getConstraintExpression()) + AC = CreateConstraintConjunction(C, AC, CE); + } else if (TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(P)) { + if (Expr *CE = TTP->getConstraintExpression()) + AC = CreateConstraintConjunction(C, AC, CE); + } else + if (Expr *CE = cast<TemplateTypeParmDecl>(P)->getConstraintExpression()) + AC = CreateConstraintConjunction(C, AC, CE); + void *Mem = C.Allocate(totalSizeToAlloc<NamedDecl *, Expr *>( - Params.size(), RequiresClause ? 1u : 0u), + Params.size(), RequiresClause || AC ? 2u : 0u), alignof(TemplateParameterList)); - return new (Mem) TemplateParameterList(TemplateLoc, LAngleLoc, Params, - RAngleLoc, RequiresClause); + return new (Mem) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params, + RAngleLoc, RequiresClause, AC); } unsigned TemplateParameterList::getMinRequiredArguments() const { @@ -146,27 +178,11 @@ } // namespace clang -// Create a constraint expression as the conjunction (the "and") of two other -// constraint expressions. -static Expr *CreateConstraintConjunction(ASTContext &C, Expr *A, Expr *B) { - if (!A) { - return B; - } - if (B) { - return new (C) BinaryOperator(A, B, BO_LAnd, C.BoolTy, VK_RValue, - OK_Ordinary, /*opLoc=*/SourceLocation(), - FPOptions()); - } - return A; -} - static ConstrainedTemplateDeclInfo * collectAssociatedConstraints(ASTContext &C, TemplateParameterList *Params, Expr *TrailingRequiresClause = nullptr) { - // TODO: Instead of calling getRequiresClause - write and call a - // TemplateParameterList member function calculateAssociatedConstraints, which - // will also fetch constraint-expressions from constrained-parameters. - Expr *TotalAC = CreateConstraintConjunction(C, Params->getRequiresClause(), + Expr *TotalAC = CreateConstraintConjunction(C, + Params->getAssociatedConstraints(), TrailingRequiresClause); if (TotalAC) { ConstrainedTemplateDeclInfo *CTDI = new (C) ConstrainedTemplateDeclInfo; Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -3696,15 +3696,17 @@ // is created. // FIXME: Import default argument. - return TemplateTypeParmDecl::Create(Importer.getToContext(), - Importer.getToContext().getTranslationUnitDecl(), - Importer.Import(D->getLocStart()), - Importer.Import(D->getLocation()), - D->getDepth(), - D->getIndex(), - Importer.Import(D->getIdentifier()), - D->wasDeclaredWithTypename(), - D->isParameterPack()); + auto *R = TemplateTypeParmDecl::Create(Importer.getToContext(), + Importer.getToContext().getTranslationUnitDecl(), + Importer.Import(D->getLocStart()), + Importer.Import(D->getLocation()), + D->getDepth(), D->getIndex(), + Importer.Import(D->getIdentifier()), + D->wasDeclaredWithTypename(), + D->isParameterPack()); + if (Expr *CE = D->getConstraintExpression()) + R->setConstraintExpression(VisitExpr(CE)); + return R; } Decl * @@ -3729,12 +3731,15 @@ // FIXME: Import default argument. - return NonTypeTemplateParmDecl::Create(Importer.getToContext(), + auto *R = NonTypeTemplateParmDecl::Create(Importer.getToContext(), Importer.getToContext().getTranslationUnitDecl(), - Importer.Import(D->getInnerLocStart()), - Loc, D->getDepth(), D->getPosition(), - Name.getAsIdentifierInfo(), - T, D->isParameterPack(), TInfo); + Importer.Import(D->getInnerLocStart()), + Loc, D->getDepth(), D->getPosition(), + Name.getAsIdentifierInfo(), T, + D->isParameterPack(), TInfo); + if (Expr *CE = D->getConstraintExpression()) + R->setConstraintExpression(VisitExpr(CE)); + return R; } Decl * @@ -3754,13 +3759,14 @@ return nullptr; // FIXME: Import default argument. - - return TemplateTemplateParmDecl::Create(Importer.getToContext(), - Importer.getToContext().getTranslationUnitDecl(), - Loc, D->getDepth(), D->getPosition(), - D->isParameterPack(), - Name.getAsIdentifierInfo(), - TemplateParams); + auto *R = TemplateTemplateParmDecl::Create(Importer.getToContext(), + Importer.getToContext().getTranslationUnitDecl(), + Loc, D->getDepth(), D->getPosition(), + D->isParameterPack(), Name.getAsIdentifierInfo(), + TemplateParams); + if (Expr *CE = D->getConstraintExpression()) + R->setConstraintExpression(VisitExpr(CE)); + return R; } Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -1649,6 +1649,10 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + if (Expr *CE = D->getConstraintExpression()) { + OS << " requires "; + dumpStmt(CE); + } if (D->hasDefaultArgument()) dumpTemplateArgument(D->getDefaultArgument()); } @@ -1659,6 +1663,10 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + if (Expr *CE = D->getConstraintExpression()) { + OS << " requires "; + dumpStmt(CE); + } if (D->hasDefaultArgument()) dumpTemplateArgument(D->getDefaultArgument()); } @@ -1670,6 +1678,10 @@ OS << " ..."; dumpName(D); dumpTemplateParameters(D->getTemplateParameters()); + if (Expr *CE = D->getConstraintExpression()) { + OS << " requires "; + dumpStmt(CE); + } if (D->hasDefaultArgument()) dumpTemplateArgumentLoc(D->getDefaultArgument()); } Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -699,8 +699,8 @@ cast<TemplateTemplateParmDecl>(*P))); } - assert(!TTP->getRequiresClause() && - "Unexpected requires-clause on template template-parameter"); + assert(!TTP->getAssociatedConstraints() && + "Unexpected constraints on template template-parameter"); Expr *const CanonRequiresClause = nullptr; TemplateTemplateParmDecl *CanonTTP Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5326,7 +5326,8 @@ NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, bool *IsCorrectedToColon = nullptr, - bool OnlyNamespace = false); + bool OnlyNamespace = false, + bool SuppressDiagnostics = false); /// \brief The parser has parsed a nested-name-specifier 'identifier::'. /// @@ -5352,14 +5353,17 @@ /// /// \param OnlyNamespace If true, only considers namespaces in lookup. /// + /// \param SuppressDiagnostic If true, suppress diagnostic on error. + /// /// \returns true if an error occurred, false otherwise. bool ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, bool EnteringContext, CXXScopeSpec &SS, bool ErrorRecoveryLookup = false, bool *IsCorrectedToColon = nullptr, - bool OnlyNamespace = false); + bool OnlyNamespace = false, + bool SuppressDiagnostic = false); ExprResult ActOnDecltypeExpression(Expr *E); @@ -5659,8 +5663,8 @@ StringRef Diagnostic); void DiagnoseUnsatisfiedIllFormedConstraint(PartialDiagnosticAt *Diagnostic); - void DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, - const TemplateParameterList *New); + void DiagnoseRedeclarationConstraintMismatch(SourceLocation Old, + SourceLocation New); // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, @@ -6167,11 +6171,17 @@ SourceLocation Loc); QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc); - Decl *ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, + Decl *ActOnNonTypeTemplateParameter(Scope *S, + const DeclSpec &DS, + SourceLocation StartLoc, + TypeSourceInfo *TInfo, + IdentifierInfo *ParamName, + SourceLocation ParamNameLoc, + bool IsParameterPack, unsigned Depth, unsigned Position, SourceLocation EqualLoc, - Expr *DefaultArg); + Expr *Default); Decl *ActOnTemplateTemplateParameter(Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params, @@ -6278,9 +6288,7 @@ const TemplateArgumentListInfo *TemplateArgs); ExprResult - CheckConceptTemplateId(const CXXScopeSpec &SS, - const DeclarationNameInfo &NameInfo, - ConceptDecl *Template, + CheckConceptTemplateId(const CXXScopeSpec &SS, ConceptDecl *Template, SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs); Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1584,7 +1584,8 @@ bool *MayBePseudoDestructor = nullptr, bool IsTypename = false, IdentifierInfo **LastII = nullptr, - bool OnlyNamespace = false); + bool OnlyNamespace = false, + bool SuppressDiagnostic = false); //===--------------------------------------------------------------------===// // C++0x 5.1.2: Lambda expressions @@ -2719,7 +2720,7 @@ bool AllowDeductionGuide, ParsedType ObjectType, SourceLocation& TemplateKWLoc, - UnqualifiedId &Result); + UnqualifiedId &Result, bool SuppressDiag = false); private: //===--------------------------------------------------------------------===// @@ -2752,6 +2753,12 @@ Decl *ParseTypeParameter(unsigned Depth, unsigned Position); Decl *ParseTemplateTemplateParameter(unsigned Depth, unsigned Position); Decl *ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position); + DeclResult TryParseConstrainedTemplateParameter(unsigned Depth, + unsigned Position); + Decl *ParseConstrainedTemplateParameter(unsigned Depth, unsigned Position, + SourceLocation ParamStartLoc, + UnqualifiedId &ConceptName, + ConceptDecl *CD); void DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc, SourceLocation CorrectLoc, bool AlreadyHasEllipsis, Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -666,6 +666,9 @@ def err_explicit_instantiation_enum : Error< "enumerations cannot be explicitly instantiated">; def err_expected_template_parameter : Error<"expected template parameter">; +def err_constrained_parameter_missing_arguments : Error< + "concept %0 requires more than 1 template argument; provide the remaining " + "arguments explicitly to use it here">; def err_missing_dependent_template_keyword : Error< "use 'template' keyword to treat '%0' as a dependent template name">; Index: include/clang/AST/TemplateBase.h =================================================================== --- include/clang/AST/TemplateBase.h +++ include/clang/AST/TemplateBase.h @@ -586,6 +586,10 @@ void addArgument(const TemplateArgumentLoc &Loc) { Arguments.push_back(Loc); } + + void prependArgument(const TemplateArgumentLoc &Loc) { + Arguments.insert(Arguments.begin(), Loc); + } }; /// \brief Represents an explicit template argument list in C++, e.g., Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1730,9 +1730,10 @@ // D is the "T" in something like // template <template <typename> class T> class container { }; TRY_TO(TraverseDecl(D->getTemplatedDecl())); - if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { + if (Expr *CE = D->getConstraintExpression()) + TRY_TO(TraverseStmt(CE)); + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument())); - } TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); }) @@ -1744,6 +1745,8 @@ // D is the "T" in something like "template<typename T> class vector;" if (D->getTypeForDecl()) TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); + if (Expr *CE = D->getConstraintExpression()) + TRY_TO(TraverseStmt(CE)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc())); }) @@ -2061,6 +2064,8 @@ DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, { // A non-type template parameter, e.g. "S" in template<int S> class Foo ... TRY_TO(TraverseDeclaratorHelper(D)); + if (Expr *CE = D->getConstraintExpression()) + TRY_TO(TraverseStmt(CE)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseStmt(D->getDefaultArgument())); }) Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -82,20 +82,22 @@ /// pack. unsigned ContainsUnexpandedParameterPack : 1; - /// Whether this template parameter list has an associated requires-clause - unsigned HasRequiresClause : 1; + /// Whether this template parameter list has associated constraints, be it a + /// requires clause or constrained parameters. + unsigned HasAssociatedConstraints : 1; protected: - TemplateParameterList(SourceLocation TemplateLoc, SourceLocation LAngleLoc, - ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, - Expr *RequiresClause); + TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc, + SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, + SourceLocation RAngleLoc, Expr *RequiresClause, + Expr *ConstrainedParamsConstraints); size_t numTrailingObjects(OverloadToken<NamedDecl *>) const { return NumParams; } size_t numTrailingObjects(OverloadToken<Expr *>) const { - return HasRequiresClause; + return HasAssociatedConstraints ? 2 : 0; } public: @@ -159,14 +161,29 @@ return ContainsUnexpandedParameterPack; } + /// \brief Determine whether this template parameter list contains a parameter + /// pack. + bool hasParameterPack() const { + for (const NamedDecl *P : asArray()) + if (P->isParameterPack()) + return true; + return false; + } + /// \brief The constraint-expression of the associated requires-clause. Expr *getRequiresClause() { - return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr; + return HasAssociatedConstraints ? getTrailingObjects<Expr *>()[0] : nullptr; } /// \brief The constraint-expression of the associated requires-clause. const Expr *getRequiresClause() const { - return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr; + return HasAssociatedConstraints ? getTrailingObjects<Expr *>()[0] : nullptr; + } + + /// \brief Gets the combined constraint-expression derived from the associated + /// requires-clause and constrained-parameters (if any). + Expr *getAssociatedConstraints() const { + return HasAssociatedConstraints ? getTrailingObjects<Expr *>()[1] : nullptr; } SourceLocation getTemplateLoc() const { return TemplateLoc; } @@ -185,25 +202,29 @@ /// \brief Stores a list of template parameters and the associated /// requires-clause (if any) for a TemplateDecl and its derived classes. /// Suitable for creating on the stack. -template <size_t N, bool HasRequiresClause> +template <size_t N, bool HasAssociatedConstraints> class FixedSizeTemplateParameterListStorage : public TemplateParameterList::FixedSizeStorageOwner { typename TemplateParameterList::FixedSizeStorage< NamedDecl *, Expr *>::with_counts< - N, HasRequiresClause ? 1u : 0u + N, HasAssociatedConstraints ? 2u : 0u >::type storage; public: - FixedSizeTemplateParameterListStorage(SourceLocation TemplateLoc, + FixedSizeTemplateParameterListStorage(const ASTContext &C, + SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, - Expr *RequiresClause) + Expr *RequiresClause, + Expr *ConstrainedParamsConstraints) : FixedSizeStorageOwner( (assert(N == Params.size()), - assert(HasRequiresClause == static_cast<bool>(RequiresClause)), - new (static_cast<void *>(&storage)) TemplateParameterList( - TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause))) {} + assert(HasAssociatedConstraints == + (RequiresClause || ConstrainedParamsConstraints)), + new (static_cast<void *>(&storage)) TemplateParameterList(C, + TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause, + ConstrainedParamsConstraints))) {} }; /// \brief A template argument list. @@ -467,7 +488,7 @@ /// template parameters. void init(NamedDecl *templatedDecl, TemplateParameterList* templateParams) { assert(!TemplatedDecl && "TemplatedDecl already set!"); - assert(!TemplateParams && "TemplateParams already set!"); + assert(!TemplateParams.getPointer() && "TemplateParams already set!"); TemplatedDecl = templatedDecl; TemplateParams.setPointer(templateParams); } @@ -1131,6 +1152,10 @@ DefaultArgStorage<TemplateTypeParmDecl, TypeSourceInfo *>; DefArgStorage DefaultArgument; + /// \brief The constraint expression introduced by this declaration (by means + /// of a 'constrained-parameter'. + Expr *ConstraintExpression = nullptr; + TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc, SourceLocation IdLoc, IdentifierInfo *Id, bool Typename) @@ -1207,6 +1232,18 @@ /// \brief Returns whether this is a parameter pack. bool isParameterPack() const; + /// \brief Returns the constraint expression associated with this template + /// parameter (if any). + Expr *getConstraintExpression() const { + return ConstraintExpression; + } + + /// \brief Sets the constraint expression associated with this template + /// parameter (if any). + void setConstraintExpression(Expr *E) { + ConstraintExpression = E; + } + SourceRange getSourceRange() const override LLVM_READONLY; // Implement isa/cast/dyncast/etc. @@ -1246,6 +1283,10 @@ /// \brief The number of types in an expanded parameter pack. unsigned NumExpandedTypes = 0; + /// \brief The constraint expression introduced by this declaration (by means + /// of a 'constrained-parameter'. + Expr *ConstraintExpression = nullptr; + size_t numTrailingObjects( OverloadToken<std::pair<QualType, TypeSourceInfo *>>) const { return NumExpandedTypes; @@ -1392,6 +1433,18 @@ return TypesAndInfos[I].second; } + /// \brief Returns the constraint expression associated with this template + /// parameter (if any). + Expr *getConstraintExpression() const { + return ConstraintExpression; + } + + /// \brief Sets the constraint expression associated with this template + /// parameter (if any). + void setConstraintExpression(Expr *E) { + ConstraintExpression = E; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == NonTypeTemplateParm; } @@ -1425,6 +1478,10 @@ /// \brief The number of parameters in an expanded parameter pack. unsigned NumExpandedParams = 0; + /// \brief The constraint expression introduced by this declaration (by means + /// of a 'constrained-parameter'. + Expr *ConstraintExpression = nullptr; + TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D, unsigned P, bool ParameterPack, IdentifierInfo *Id, TemplateParameterList *Params) @@ -1552,6 +1609,18 @@ /// \brief Removes the default argument of this template parameter. void removeDefaultArgument() { DefaultArgument.clear(); } + /// \brief Returns the constraint expression associated with this template + /// parameter (if any). + Expr *getConstraintExpression() const { + return ConstraintExpression; + } + + /// \brief Sets the constraint expression associated with this template + /// parameter (if any). + void setConstraintExpression(Expr *E) { + ConstraintExpression = E; + } + SourceRange getSourceRange() const override LLVM_READONLY { SourceLocation End = getLocation(); if (hasDefaultArgument() && !defaultArgumentWasInherited())
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits