Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -67,7 +67,8 @@ /// derived classes. class TemplateParameterList final : private llvm::TrailingObjects { + llvm::PointerUnion> { /// The location of the 'template' keyword. SourceLocation TemplateLoc; @@ -92,13 +93,15 @@ protected: TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef Params, - SourceLocation RAngleLoc, Expr *RequiresClause); + SourceLocation RAngleLoc, Expr *RequiresClause, + TemplateParameterList *InheritedConstraints); size_t numTrailingObjects(OverloadToken) const { return NumParams; } - size_t numTrailingObjects(OverloadToken) const { + size_t numTrailingObjects( + OverloadToken>) const{ return HasRequiresClause ? 1 : 0; } @@ -108,11 +111,10 @@ friend TrailingObjects; static TemplateParameterList *Create(const ASTContext &C, - SourceLocation TemplateLoc, - SourceLocation LAngleLoc, - ArrayRef Params, - SourceLocation RAngleLoc, - Expr *RequiresClause); + SourceLocation TemplateLoc, SourceLocation LAngleLoc, + ArrayRef Params, SourceLocation RAngleLoc, + Expr *RequiresClause, + TemplateParameterList *InheritedConstraints = nullptr); /// Iterates through the template parameters in this list. using iterator = NamedDecl **; @@ -171,14 +173,63 @@ return false; } + SourceLocation getTemplateLoc() const { return TemplateLoc; } + SourceLocation getLAngleLoc() const { return LAngleLoc; } + SourceLocation getRAngleLoc() const { return RAngleLoc; } + + SourceRange getSourceRange() const LLVM_READONLY { + return SourceRange(TemplateLoc, RAngleLoc); + } + /// The constraint-expression of the associated requires-clause. Expr *getRequiresClause() { - return HasRequiresClause ? getTrailingObjects()[0] : nullptr; + if (!HasRequiresClause) + return nullptr; + const auto &P = *getTrailingObjects>(); + if (auto *I = P.dyn_cast()) + return I->getRequiresClause(); + return P.get(); } /// The constraint-expression of the associated requires-clause. const Expr *getRequiresClause() const { - return HasRequiresClause ? getTrailingObjects()[0] : nullptr; + if (!HasRequiresClause) + return nullptr; + const auto &P = *getTrailingObjects>(); + if (auto *I = P.dyn_cast()) + return I->getRequiresClause(); + return P.get(); + } + + void setRequiresClause(Expr *RC) { + assert(HasRequiresClause && + "Can only set requires clause on TPLs that were originally created " + "with one."); + *getTrailingObjects>() = RC; + } + + void setInheritedConstraints(TemplateParameterList *InheritedConstraints) { + assert(HasRequiresClause && + "Can only setInheritedConstraints when there is already a requires " + "clause"); + auto &P = *getTrailingObjects>(); + P = InheritedConstraints; + } + + TemplateParameterList *getInheritedConstraints() { + return getTrailingObjects>() + ->get(); + } + + bool inheritsConstraints() const { + return getTrailingObjects>() + ->is(); } /// \brief All associated constraints derived from this template parameter @@ -189,16 +240,10 @@ /// conjunction ("and"). void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const; + /// \brief Whether there are associated constraints in this template parameter + /// list or in any of its parameters. bool hasAssociatedConstraints() const; - SourceLocation getTemplateLoc() const { return TemplateLoc; } - SourceLocation getLAngleLoc() const { return LAngleLoc; } - SourceLocation getRAngleLoc() const { return RAngleLoc; } - - SourceRange getSourceRange() const LLVM_READONLY { - return SourceRange(TemplateLoc, RAngleLoc); - } - public: // FIXME: workaround for MSVC 2013; remove when no longer needed using FixedSizeStorageOwner = TrailingObjects::FixedSizeStorageOwner; @@ -211,9 +256,8 @@ class FixedSizeTemplateParameterListStorage : public TemplateParameterList::FixedSizeStorageOwner { typename TemplateParameterList::FixedSizeStorage< - NamedDecl *, Expr *>::with_counts< - N, HasRequiresClause ? 1u : 0u - >::type storage; + NamedDecl *, llvm::PointerUnion>:: + with_counts::type storage; public: FixedSizeTemplateParameterListStorage(const ASTContext &C, @@ -221,12 +265,16 @@ SourceLocation LAngleLoc, ArrayRef Params, SourceLocation RAngleLoc, - Expr *RequiresClause) + Expr *RequiresClause, + TemplateParameterList *InheritedConstraints) : FixedSizeStorageOwner( (assert(N == Params.size()), - assert(HasRequiresClause == (RequiresClause != nullptr)), + assert(HasRequiresClause == (RequiresClause != nullptr + || (InheritedConstraints != nullptr + && InheritedConstraints->HasRequiresClause))), new (static_cast(&storage)) TemplateParameterList(C, - TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause))) {} + TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause, + InheritedConstraints))) {} }; /// A template argument list. @@ -1213,13 +1261,20 @@ /// If false, it was declared with the 'class' keyword. bool Typename : 1; - /// Whether this template type parameter has a type-constraint construct. - bool HasTypeConstraint : 1; - - /// Whether the type constraint has been initialized. This can be false if the - /// constraint was not initialized yet or if there was an error forming the - /// type constriant. - bool TypeConstraintInitialized : 1; + /// Whether this template type parameter owns a type-constraint construct. + bool OwnsTypeConstraint : 1; + + /// The int indicates whether the type constraint was initialized, and if the + /// pointer is not null, it means that the type constraint was inherited from + /// another declaration, and points to that declaration. + enum TypeConstraintStatus { + TCS_None, + TCS_Uninitialized, + TCS_Owned, + TCS_Inherited + }; + llvm::PointerIntPair + TypeConstraintStatus; /// The default template argument, if any. using DefArgStorage = @@ -1228,9 +1283,11 @@ TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc, SourceLocation IdLoc, IdentifierInfo *Id, - bool Typename, bool HasTypeConstraint) + bool Typename, bool OwnsTypeConstraint) : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename), - HasTypeConstraint(HasTypeConstraint), TypeConstraintInitialized(false) {} + OwnsTypeConstraint(OwnsTypeConstraint), + TypeConstraintStatus(nullptr, + OwnsTypeConstraint ? TCS_Uninitialized : TCS_None) {} public: static TemplateTypeParmDecl *Create(const ASTContext &C, DeclContext *DC, @@ -1239,12 +1296,12 @@ unsigned D, unsigned P, IdentifierInfo *Id, bool Typename, bool ParameterPack, - bool HasTypeConstraint); + bool OwnsTypeConstraint); static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, unsigned ID); static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, unsigned ID, - bool HasTypeConstraint); + bool OwnsTypeConstraint); /// Whether this template type parameter was declared with /// the 'typename' keyword. @@ -1252,7 +1309,7 @@ /// If not, it was either declared with the 'class' keyword or with a /// type-constraint (see hasTypeConstraint()). bool wasDeclaredWithTypename() const { - return Typename && !HasTypeConstraint; + return Typename && !hasTypeConstraint(); } const DefArgStorage &getDefaultArgStorage() const { return DefaultArgument; } @@ -1313,8 +1370,18 @@ /// Returns the type constraint associated with this template parameter (if /// any). const TypeConstraint *getTypeConstraint() const { - return TypeConstraintInitialized ? getTrailingObjects() : - nullptr; + switch (TypeConstraintStatus.getInt()) { + case TCS_None: + return nullptr; + case TCS_Inherited: + return getInheritedFromTypeConstraintDecl()->getTypeConstraint(); + case TCS_Owned: + return getTrailingObjects(); + case TCS_Uninitialized: + llvm_unreachable("Type constraint should be initialized before we can " + "access it"); + } + llvm_unreachable("Unhandled case"); } void setTypeConstraint(NestedNameSpecifierLoc NNS, @@ -1325,7 +1392,26 @@ /// Determine whether this template parameter has a type-constraint. bool hasTypeConstraint() const { - return HasTypeConstraint; + return TypeConstraintStatus.getInt() != TCS_None; + } + + /// \brief Sets the type constraint associated with this template + /// parameter (if any) to inherit the type constraint of a previously declared + /// template parameter. + void setInheritedTypeConstraint(TemplateTypeParmDecl *Prev) { + assert(Prev->hasTypeConstraint()); + TypeConstraintStatus.setPointer(Prev); + TypeConstraintStatus.setInt(TCS_Inherited); + } + + TemplateTypeParmDecl *getInheritedFromTypeConstraintDecl() const { + assert(typeConstraintWasInherited()); + return TypeConstraintStatus.getPointer(); + } + + bool typeConstraintWasInherited() const { + assert(hasTypeConstraint()); + return TypeConstraintStatus.getInt() == TCS_Inherited; } /// \brief Get the associated-constraints of this template parameter. @@ -1334,7 +1420,7 @@ /// Use this instead of getConstraintExpression for concepts APIs that /// accept an ArrayRef of constraint expressions. void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { - if (HasTypeConstraint) + if (hasTypeConstraint()) AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint()); } @@ -2146,9 +2232,9 @@ /// template<> template /// struct X::Inner { /* ... */ }; /// \endcode - bool isMemberSpecialization() { + bool isMemberSpecialization() const { const auto *First = - cast(getFirstDecl()); + cast(getFirstDecl()); return First->InstantiatedFromMember.getInt(); } @@ -2993,9 +3079,9 @@ /// template<> template /// U* X::Inner = (T*)(0) + 1; /// \endcode - bool isMemberSpecialization() { + bool isMemberSpecialization() const { const auto *First = - cast(getFirstDecl()); + cast(getFirstDecl()); return First->InstantiatedFromMember.getInt(); } Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2660,11 +2660,8 @@ else if (RetReq.isTypeConstraint()) TRY_TO(TraverseTemplateParameterListHelper( RetReq.getTypeConstraintTemplateParameterList())); - } else { - auto *NestedReq = cast(Req); - if (!NestedReq->isSubstitutionFailure()) - TRY_TO(TraverseStmt(NestedReq->getConstraintExpr())); - } + } else + TRY_TO(TraverseStmt(cast(Req)->getConstraintExpr())); }) // These literals (all of them) do not need any action. Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2470,8 +2470,8 @@ def note_atomic_constraint_evaluated_to_false_elaborated : Note< "%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">; def note_could_not_normalize_argument_substitution_failed : Note< - "when substituting into %0 %1. Make sure concept arguments are " - "valid for any substitution">; + "when substituting into %0. Make sure concept arguments are valid for any " + "substitution">; def err_constrained_virtual_method : Error< "virtual function cannot have a requires clause">; def err_reference_to_function_with_unsatisfied_constraints : Error< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5781,15 +5781,17 @@ /// \brief Returns whether the given declaration's associated constraints are /// more constrained than another declaration's according to the partial /// ordering of constraints. - /// \param NoCache If true, the subsumption cache will not be used or updated - /// with the result of the check. Use when the given decls may have different - /// associated constraints later (e.g. when D1 is a template template - /// parameter and AC1 are the associated constraints for the current - /// instantiation). bool IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef AC1, - NamedDecl *D2, ArrayRef AC2, - bool NoCache = false); + NamedDecl *D2, ArrayRef AC2); + + /// \brief Returns whether the given declaration's associated constraints are + /// more constrained than another declaration's according to the partial + /// ordering of constraints. + bool IsAtLeastAsConstrained(ArrayRef AC1, + const MultiLevelTemplateArgumentList &MLTAL1, + ArrayRef AC2, + const MultiLevelTemplateArgumentList &MLTAL2); /// \brief If D1 was not at least as constrained as D2, but would've been if /// a pair of atomic constraints involved had been declared in a concept and @@ -5797,6 +5799,10 @@ /// \returns true if such a diagnostic was emitted, false otherwise. bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, ArrayRef AC2); + bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, + ArrayRef AC1, const MultiLevelTemplateArgumentList &MLTAL1, + NamedDecl *D2, ArrayRef AC2, + const MultiLevelTemplateArgumentList &MLTAL2); /// \brief Check whether the given list of constraint expressions are /// satisfied (as if in a 'conjunction') given template arguments. @@ -5811,23 +5817,16 @@ /// expression. /// \returns true if an error occurred and satisfaction could not be checked, /// false otherwise. - bool CheckConstraintSatisfaction(TemplateDecl *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); + bool CheckConstraintSatisfaction(NamedDecl *Template, + ArrayRef ConstraintExprs, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); - bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); - - bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); + bool CheckConstraintSatisfaction(NestedRequirement *Req, + const Expr *ConstraintExpr, + const MultiLevelTemplateArgumentList &TemplateArgs, + ConstraintSatisfaction &Satisfaction, bool &IsDependent, + bool &ContainsUnexpandedParameterPack); /// \brief Check whether the given non-dependent constraint expression is /// satisfied. Returns false and updates Satisfaction with the satisfaction @@ -6691,9 +6690,11 @@ QualType InstantiatedParamType, Expr *Arg, TemplateArgument &Converted, CheckTemplateArgumentKind CTAK = CTAK_Specified); - bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, - TemplateParameterList *Params, - TemplateArgumentLoc &Arg); + bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, + TemplateParameterList *Params, + TemplateArgumentLoc &Arg, + unsigned ArgumentPackIndex, + ArrayRef Converted); ExprResult BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, @@ -7430,7 +7431,7 @@ // MultiLevelTemplateArgumentList - getTemplateInstantiationArgs(NamedDecl *D, + getTemplateInstantiationArgs(Decl *D, const TemplateArgumentList *Innermost = nullptr, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr); @@ -7808,6 +7809,8 @@ /// specialization in some surrounding active instantiation. bool isAlreadyInstantiating() const { return AlreadyInstantiating; } + InstantiatingTemplate(InstantiatingTemplate&&); + private: Sema &SemaRef; bool Invalid; @@ -8215,9 +8218,7 @@ void InstantiateExceptionSpec(SourceLocation PointOfInstantiation, FunctionDecl *Function); - bool CheckInstantiatedFunctionTemplateConstraints( - SourceLocation PointOfInstantiation, FunctionDecl *Decl, - ArrayRef TemplateArgs, + bool CheckFunctionConstraints(FunctionDecl *Decl, ConstraintSatisfaction &Satisfaction); FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, const TemplateArgumentList *Args, Index: include/clang/Sema/SemaConcept.h =================================================================== --- include/clang/Sema/SemaConcept.h +++ include/clang/Sema/SemaConcept.h @@ -24,6 +24,7 @@ #include namespace clang { class ConceptSpecializationExpr; +class MultiLevelTemplateArgumentList; class Sema; /// \brief The result of a constraint satisfaction check, containing the @@ -331,46 +332,29 @@ /// \brief A requires-expression requirement which is satisfied when a general /// constraint expression is satisfied ('nested' requirements). class NestedRequirement : public Requirement { - llvm::PointerUnion Value; + Expr *ConstraintExpr; ConstraintSatisfaction Satisfaction; public: friend class ASTStmtReader; friend class ASTStmtWriter; - NestedRequirement(SubstitutionDiagnostic *SubstDiag) : - Requirement(RK_Nested, /*Dependent=*/false, - /*ContainsUnexpandedParameterPack*/false, - /*Satisfied=*/false), Value(SubstDiag) {} - NestedRequirement(Expr *Constraint) : - Requirement(RK_Nested, /*Dependent=*/true, + Requirement(RK_Nested, /*IsDependent=*/true, Constraint->containsUnexpandedParameterPack()), - Value(Constraint) { - assert(Constraint->isInstantiationDependent() && - "Nested requirement with Non-dependent constraint must be " - "constructed with a Sema& or a ConstraintSatisfaction object"); + ConstraintExpr(Constraint) { + assert(Constraint->isInstantiationDependent() && "Non-dependent constraint " + "expressions must be provided along a Sema& or a precalculated " + "ConstraintSatisfaction"); } - NestedRequirement(Sema &S, Expr *Constraint); + NestedRequirement(Sema &S, Expr *Constraint, + const MultiLevelTemplateArgumentList &TemplateArgs); NestedRequirement(Expr *Constraint, ConstraintSatisfaction Satisfaction) : Requirement(RK_Nested, false, false, Satisfaction.IsSatisfied), - Value(Constraint), Satisfaction(std::move(Satisfaction)) {} - - bool isSubstitutionFailure() const { - return Value.is(); - } - SubstitutionDiagnostic *getSubstitutionDiagnostic() const { - assert(isSubstitutionFailure() && - "getSubstitutionDiagnostic() may not be called when there was no " - "substitution failure."); - return Value.get(); - } + ConstraintExpr(Constraint), Satisfaction(std::move(Satisfaction)) {} Expr *getConstraintExpr() const { - assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called " - "on nested requirements with " - "substitution failures."); - return Value.get(); + return ConstraintExpr; } void Diagnose(Sema &S, bool First) const override; Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -49,10 +49,12 @@ SourceLocation LAngleLoc, ArrayRef Params, SourceLocation RAngleLoc, - Expr *RequiresClause) + Expr *RequiresClause, + TemplateParameterList *InheritedConstraints) : TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), NumParams(Params.size()), ContainsUnexpandedParameterPack(false), - HasRequiresClause(RequiresClause != nullptr), + HasRequiresClause(RequiresClause != nullptr || + (InheritedConstraints && InheritedConstraints->HasRequiresClause)), HasConstrainedParameters(false) { for (unsigned Idx = 0; Idx < NumParams; ++Idx) { NamedDecl *P = Params[Idx]; @@ -80,9 +82,15 @@ } if (HasRequiresClause) { - if (RequiresClause->containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - *getTrailingObjects() = RequiresClause; + auto &P = *getTrailingObjects>(); + if (InheritedConstraints) + P = InheritedConstraints; + else { + if (RequiresClause->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + P = RequiresClause; + } } } @@ -90,12 +98,19 @@ TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef Params, - SourceLocation RAngleLoc, Expr *RequiresClause) { - void *Mem = C.Allocate(totalSizeToAlloc( - Params.size(), RequiresClause ? 1u : 0u), + SourceLocation RAngleLoc, Expr *RequiresClause, + TemplateParameterList *InheritedConstraints) { + assert(!RequiresClause || !InheritedConstraints); + void *Mem = C.Allocate(totalSizeToAlloc>( + Params.size(), RequiresClause || + (InheritedConstraints && + InheritedConstraints->HasRequiresClause) ? 1u : 0u), alignof(TemplateParameterList)); return new (Mem) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params, - RAngleLoc, RequiresClause); + RAngleLoc, RequiresClause, + InheritedConstraints); } unsigned TemplateParameterList::getMinRequiredArguments() const { @@ -534,12 +549,12 @@ SourceLocation KeyLoc, SourceLocation NameLoc, unsigned D, unsigned P, IdentifierInfo *Id, bool Typename, bool ParameterPack, - bool HasTypeConstraint) { + bool OwnsTypeConstraint) { auto *TTPDecl = new (C, DC, - additionalSizeToAlloc(HasTypeConstraint ? 1 : 0)) + additionalSizeToAlloc(OwnsTypeConstraint ? 1 : 0)) TemplateTypeParmDecl(DC, KeyLoc, NameLoc, Id, Typename, - HasTypeConstraint); + OwnsTypeConstraint); QualType TTPType = C.getTemplateTypeParmType(D, P, ParameterPack, TTPDecl); TTPDecl->setTypeForDecl(TTPType.getTypePtr()); return TTPDecl; @@ -554,9 +569,9 @@ TemplateTypeParmDecl * TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID, - bool HasTypeConstraint) { + bool OwnsTypeConstraint) { return new (C, ID, - additionalSizeToAlloc(HasTypeConstraint ? 1 : 0)) + additionalSizeToAlloc(OwnsTypeConstraint ? 1 : 0)) TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(), nullptr, false, false); } @@ -591,14 +606,14 @@ DeclarationNameInfo NameInfo, NamedDecl *FoundDecl, ConceptDecl *CD, const ASTTemplateArgumentListInfo *ArgsAsWritten, Expr *ImmediatelyDeclaredConstraint) { - assert(HasTypeConstraint && - "HasTypeConstraint=true must be passed at construction in order to " + assert(OwnsTypeConstraint && + "OwnsTypeConstraint=true must be passed at construction in order to " "call setTypeConstraint"); - assert(!TypeConstraintInitialized && + assert(TypeConstraintStatus.getInt() == TCS_Uninitialized && "TypeConstraint was already initialized!"); new (getTrailingObjects()) TypeConstraint(NNS, NameInfo, FoundDecl, CD, ArgsAsWritten, ImmediatelyDeclaredConstraint); - TypeConstraintInitialized = true; + TypeConstraintStatus.setInt(TCS_Owned); } //===----------------------------------------------------------------------===// @@ -1247,7 +1262,7 @@ auto *T = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false, - /*HasTypeConstraint=*/false); + /*OwnsTypeConstraint=*/false); T->setImplicit(true); // T ...Ints @@ -1273,7 +1288,7 @@ auto *TemplateTypeParm = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1, /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false, - /*HasTypeConstraint=*/false); + /*OwnsTypeConstraint=*/false); TemplateTypeParm->setImplicit(true); // T N @@ -1302,7 +1317,7 @@ auto *Ts = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1, /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true, - /*HasTypeConstraint=*/false); + /*OwnsTypeConstraint=*/false); Ts->setImplicit(true); // template Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2246,10 +2246,7 @@ } else { auto *NestedReq = cast(Req); OS << "requires "; - if (NestedReq->isSubstitutionFailure()) - OS << "<>"; - else - PrintExpr(NestedReq->getConstraintExpr()); + PrintExpr(NestedReq->getConstraintExpr()); } OS << "; "; } Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1341,9 +1341,7 @@ } else { ID.AddInteger(Requirement::RK_Nested); auto *NestedReq = cast(Req); - ID.AddBoolean(NestedReq->isSubstitutionFailure()); - if (!NestedReq->isSubstitutionFailure()) - Visit(NestedReq->getConstraintExpr()); + Visit(NestedReq->getConstraintExpr()); } } } Index: lib/Sema/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -21,6 +21,7 @@ #include "clang/Sema/Initialization.h" #include "clang/Sema/SemaInternal.h" #include "clang/AST/ExprCXX.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" using namespace clang; @@ -128,11 +129,14 @@ return false; } -template -static bool calculateConstraintSatisfaction( - Sema &S, TemplateDeclT *Template, ArrayRef TemplateArgs, - SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, - const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { +template +static bool calculateConstraintSatisfaction(Sema &S, + SubstitutionInstantiatingTemplateCreator Creator, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation TemplateNameLoc, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction, + bool *ContainsUnexpandedParameterPack = nullptr, + bool *IsDependent = nullptr) { return calculateConstraintSatisfaction( S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { EnterExpressionEvaluationContext ConstantEvaluated( @@ -142,15 +146,15 @@ ExprResult SubstitutedExpression; { TemplateDeductionInfo Info(TemplateNameLoc); - Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(), - Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template, - Info, AtomicExpr->getSourceRange()); + Sema::InstantiatingTemplate Inst( + Creator(AtomicExpr->getBeginLoc(), AtomicExpr->getSourceRange(), + Info)); if (Inst.isInvalid()) return ExprError(); // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); SubstitutedExpression = S.SubstExpr(const_cast(AtomicExpr), - MLTAL); + TemplateArgs); if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) { // C++2a [temp.constr.atomic]p1 // ...If substitution results in an invalid type or expression, the @@ -176,6 +180,34 @@ } } + if (ContainsUnexpandedParameterPack != nullptr) + *ContainsUnexpandedParameterPack |= + SubstitutedExpression.get()->containsUnexpandedParameterPack(); + + if (SubstitutedExpression.get()->isInstantiationDependent()) { + // This might happen when constraint expressions present somewhere in + // a member declaration of a template are instantiated: + // + // template + // struct S { + // template 1); } + // ) W> + // struct M { }; + // } + // + // Referencing S will trigger the instantiation of the + // nested-requirement, with only the argument, and no the + // argument. We will treat this as satisfied for now because the + // expression will be instantiated again anyway with both the and + // the arguments. + if (IsDependent != nullptr) + *IsDependent = true; + Satisfaction.IsSatisfied = true; + return ExprEmpty(); + } + if (!S.CheckConstraintExpression(SubstitutedExpression.get())) return ExprError(); @@ -183,37 +215,21 @@ }); } -template -static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - if (ConstraintExprs.empty()) { - Satisfaction.IsSatisfied = true; - return false; - } - - for (auto& Arg : TemplateArgs) - if (Arg.isInstantiationDependent()) { - // No need to check satisfaction for dependent constraint expressions. - Satisfaction.IsSatisfied = true; - return false; - } - - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs, - TemplateIDRange); - if (Inst.isInvalid()) - return true; - - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TemplateArgs); - +template +static bool +CheckConstraintSatisfaction(Sema &S, ArrayRef ConstraintExprs, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceRange TemplateIDRange, + SubstitutionInstantiatingTemplateCreator Creator, + ConstraintSatisfaction &Satisfaction, + bool *ContainsUnexpandedParameterPack = nullptr, + bool *IsDependent = nullptr) { for (const Expr *ConstraintExpr : ConstraintExprs) { - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateIDRange.getBegin(), MLTAL, - ConstraintExpr, Satisfaction)) + if (calculateConstraintSatisfaction(S, Creator, TemplateArgs, + TemplateIDRange.getBegin(), + ConstraintExpr, Satisfaction, + ContainsUnexpandedParameterPack, + IsDependent)) return true; if (!Satisfaction.IsSatisfied) // [temp.constr.op] p2 @@ -225,36 +241,53 @@ return false; } -bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); -} +bool Sema::CheckConstraintSatisfaction(NamedDecl *Template, + ArrayRef ConstraintExprs, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { + if (ConstraintExprs.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + InstantiatingTemplate Inst(*this, TemplateIDRange.getBegin(), + InstantiatingTemplate::ConstraintsCheck{}, Template, + TemplateArgs.getInnermost(), TemplateIDRange); + if (Inst.isInvalid()) + return true; -bool -Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); + return ::CheckConstraintSatisfaction(*this, ConstraintExprs, + TemplateArgs, TemplateIDRange, + [&] (SourceLocation PointOfInstantiation, + SourceRange InstantiationRange, + TemplateDeductionInfo &DeductionInfo) { + return std::move(InstantiatingTemplate(*this, PointOfInstantiation, + InstantiatingTemplate::ConstraintSubstitution{}, Template, + DeductionInfo, InstantiationRange)); + }, Satisfaction); } -bool -Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); +bool Sema::CheckConstraintSatisfaction(NestedRequirement *Req, + const Expr *ConstraintExpr, + const MultiLevelTemplateArgumentList &TemplateArgs, + ConstraintSatisfaction &Satisfaction, + bool &IsDependent, bool &ContainsUnexpandedParameterPack) { + IsDependent = false; + ContainsUnexpandedParameterPack = false; + + InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(), Req, + InstantiatingTemplate::ConstraintsCheck{}, + ConstraintExpr->getSourceRange()); + if (Inst.isInvalid()) + return true; + + return ::CheckConstraintSatisfaction(*this, {ConstraintExpr}, + TemplateArgs, ConstraintExpr->getSourceRange(), + [&] (SourceLocation PointOfInstantiation, + SourceRange InstantiationRange, + TemplateDeductionInfo &DeductionInfo) { + return std::move(InstantiatingTemplate(*this, PointOfInstantiation, + Req, DeductionInfo, InstantiationRange)); + }, Satisfaction, &ContainsUnexpandedParameterPack, &IsDependent); } bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, @@ -272,8 +305,11 @@ ConstraintSatisfaction Satisfaction; llvm::SmallVector AssociatedConstraints; TD->getAssociatedConstraints(AssociatedConstraints); - if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs, - TemplateIDRange, Satisfaction)) + TemplateArgumentList TAL(TemplateArgumentList::OnStack, TemplateArgs); + MultiLevelTemplateArgumentList MLTAL = + getTemplateInstantiationArgs(TD, /*Innermost=*/&TAL); + if (CheckConstraintSatisfaction(TD, AssociatedConstraints, + MLTAL, TemplateIDRange, Satisfaction)) return true; if (!Satisfaction.IsSatisfied) { @@ -408,26 +444,46 @@ } } namespace { +struct TemplateParameterFinder : RecursiveASTVisitor { + llvm::SmallSet OccurringParameters; + + bool VisitTemplateTypeParmType(TemplateTypeParmType *T) { + OccurringParameters.insert(T->getDecl()); + return true; + } +}; + struct AtomicConstraint { + const Expr *ConstraintExpr; + llvm::SmallVector ParameterMapping; + AtomicConstraint(const Expr *ConstraintExpr, - const ASTTemplateArgumentListInfo *ParameterMapping = nullptr) : - ConstraintExpr{ConstraintExpr}, ParameterMapping{ParameterMapping} {} + ArrayRef ParameterMapping) : + ConstraintExpr{ConstraintExpr} { + // C++2a [temp.constr.atomic]p1 + // An atomic constraint is formed from an expression E and a mapping from + // the template parameters that appear within E to template arguments + // involving the template parameters of the constrained entity, called + // the parameter mapping. + TemplateParameterFinder Finder; + Finder.TraverseStmt(const_cast(ConstraintExpr)); + for (TemplateTypeParmDecl *Param : Finder.OccurringParameters) + if (ParameterMapping.empty()) + // This signals the identity mapping + this->ParameterMapping.push_back(QualType(Param->getTypeForDecl(), 0)); + else + this->ParameterMapping.push_back(ParameterMapping[Param->getIndex()]); + } bool hasMatchingParameterMapping(ASTContext &C, const AtomicConstraint &Other) const { - if ((!ParameterMapping) != (!Other.ParameterMapping)) - return false; - if (!ParameterMapping) - return true; - if (ParameterMapping->NumTemplateArgs != - Other.ParameterMapping->NumTemplateArgs) + if (ParameterMapping.size() != Other.ParameterMapping.size()) return false; - for (unsigned I = 0, S = ParameterMapping->NumTemplateArgs; I < S; ++I) - if (!C.getCanonicalTemplateArgument( - ParameterMapping->arguments()[I].getArgument()) + for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) + if (!C.getCanonicalTemplateArgument(ParameterMapping[I]) .structurallyEquals(C.getCanonicalTemplateArgument( - Other.ParameterMapping->arguments()[I].getArgument()))) + Other.ParameterMapping[I]))) return false; return true; } @@ -451,9 +507,6 @@ // Check that the parameter lists are identical return hasMatchingParameterMapping(C, Other); } - - const Expr *ConstraintExpr; - const ASTTemplateArgumentListInfo *ParameterMapping; }; /// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is @@ -498,9 +551,9 @@ "getAtomicConstraint called on non-atomic constraint."); return Constraint.get(); } - static llvm::Optional fromConstraintExpr( - Sema &S, const Expr *E, TemplateDecl *TD = nullptr, - const ASTTemplateArgumentListInfo *ParameterMapping = nullptr) { + static llvm::Optional fromConstraintExpr(Sema &S, + const Expr *E, + const MultiLevelTemplateArgumentList &ParameterMapping) { assert(E != nullptr); // C++ [temp.constr.normal]p1.1 @@ -508,13 +561,13 @@ // - The normal form of an expression (E) is the normal form of E. // [...] if (auto *P = dyn_cast(E)) - return fromConstraintExpr(S, P->getSubExpr(), TD, ParameterMapping); + return fromConstraintExpr(S, P->getSubExpr(), ParameterMapping); if (auto *BO = dyn_cast(E)) { if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { - auto LHS = fromConstraintExpr(S, BO->getLHS(), TD, ParameterMapping); + auto LHS = fromConstraintExpr(S, BO->getLHS(), ParameterMapping); if (!LHS) return llvm::Optional{}; - auto RHS = fromConstraintExpr(S, BO->getRHS(), TD, ParameterMapping); + auto RHS = fromConstraintExpr(S, BO->getRHS(), ParameterMapping); if (!RHS) return llvm::Optional{}; @@ -534,7 +587,7 @@ // [...] const ASTTemplateArgumentListInfo *Mapping = CSE->getTemplateArgsAsWritten(); - if (!ParameterMapping) { + if (ParameterMapping.getNumLevels() == 0) { // This is a top level CSE. // // template @@ -582,11 +635,12 @@ // // Just treat C as an atomic constraint. return NormalizedConstraint{new (S.Context) - AtomicConstraint(E, Mapping)}; - + AtomicConstraint(E, TempList)}; + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TempList); return fromConstraintExpr(S, CSE->getNamedConcept()->getConstraintExpr(), - CSE->getNamedConcept(), Mapping); + MLTAL); } // This is not a top level CSE. @@ -597,7 +651,7 @@ // template // concept C2 = C1; -> We are here. // Mapping is {T1=U, T2=T} - // ParameterMapping is {T=X, U=Y}, TD is C2 + // ParameterMapping is {T=X, U=Y} // // template // void foo() requires C2 {} @@ -608,37 +662,14 @@ // Mapping, we instead substitute ParameterMapping into C1 and take // the substituted argument list as the ParameterMapping for the next // level down. - assert(TD && "ParameterMapping provided without TemplateDecl"); - - TemplateArgumentListInfo TALI(ParameterMapping->LAngleLoc, - ParameterMapping->RAngleLoc); - for (auto &Arg : ParameterMapping->arguments()) - TALI.addArgument(Arg); - llvm::SmallVector TempList; - bool InstantiationDependent = false; - bool Success = - !S.CheckTemplateArgumentList(TD, ParameterMapping->LAngleLoc, - TALI, /*PartialTemplateArgs=*/false, - TempList, - /*UpdateArgsWithConversions=*/false, - &InstantiationDependent) && - !InstantiationDependent; - assert(Success && "ParameterMapping should have already been cheked " - "against template argument list earlier."); auto DiagnoseSubstitutionError = [&](unsigned int Diag) { - std::string TemplateArgString = S.getTemplateArgumentBindingsText( - TD->getTemplateParameters(), TempList.data(), TempList.size()); S.Diag(CSE->getBeginLoc(), Diag) - << const_cast(CSE) - << TemplateArgString; + << const_cast(CSE); }; - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TempList); - ExprResult Result = S.SubstExpr( - const_cast(CSE), MLTAL); + const_cast(CSE), ParameterMapping); if (!Result.isUsable() || Result.isInvalid()) { // C++ [temp.constr.normal] // If any such substitution results in an invalid type or @@ -650,18 +681,22 @@ diag::note_could_not_normalize_argument_substitution_failed); return llvm::Optional{}; } - ParameterMapping = cast(Result.get()) + Mapping = cast(Result.get()) ->getTemplateArgsAsWritten(); - TemplateArgumentListInfo SubstTALI(ParameterMapping->LAngleLoc, - ParameterMapping->RAngleLoc); - for (auto &Arg : ParameterMapping->arguments()) + TemplateArgumentListInfo SubstTALI(Mapping->LAngleLoc, + Mapping->RAngleLoc); + for (auto &Arg : Mapping->arguments()) SubstTALI.addArgument(Arg); llvm::SmallVector Converted; + bool InstantiationDependent; bool Failure = S.CheckTemplateArgumentList( CSE->getNamedConcept(), CSE->getBeginLoc(), SubstTALI, /*PartialTemplateArgs=*/false, Converted, /*UpdateArgsWithConversions=*/true, &InstantiationDependent); + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); + // The case is this: // // template @@ -695,29 +730,32 @@ // // Treat the CSE as an atomic expression. return NormalizedConstraint{new (S.Context) - AtomicConstraint(E, ParameterMapping)}; + AtomicConstraint(E, Converted)}; return fromConstraintExpr(S, CSE->getNamedConcept()->getConstraintExpr(), - CSE->getNamedConcept(), ParameterMapping); + MLTAL); } - return NormalizedConstraint{new (S.Context) - AtomicConstraint(E, ParameterMapping)}; + return NormalizedConstraint{ + new (S.Context) AtomicConstraint(E, + ParameterMapping.getNumLevels() != 0 ? + ParameterMapping.getInnermost() : ArrayRef{})}; } static llvm::Optional fromConstraintExprs(Sema &S, - ArrayRef E) { + ArrayRef E, + const MultiLevelTemplateArgumentList &ParameterMapping) { assert(E.size() != 0); - auto First = fromConstraintExpr(S, E[0]); + auto First = fromConstraintExpr(S, E[0], ParameterMapping); if (E.size() == 1) return First; - auto Second = fromConstraintExpr(S, E[1]); + auto Second = fromConstraintExpr(S, E[1], ParameterMapping); if (!Second) return llvm::Optional{}; llvm::Optional Conjunction; Conjunction.emplace(S.Context, std::move(*First), std::move(*Second), CCK_Conjunction); for (unsigned I = 2; I < E.size(); ++I) { - auto Next = fromConstraintExpr(S, E[I]); + auto Next = fromConstraintExpr(S, E[I], ParameterMapping); if (!Next) return llvm::Optional{}; NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction), @@ -792,22 +830,15 @@ } template -static bool subsumes(Sema &S, ArrayRef P, - ArrayRef Q, bool &DoesSubsume, +static bool subsumes(Sema &S, NormalizedConstraint &PNormalized, + NormalizedConstraint &QNormalized, bool &DoesSubsume, AtomicSubsumptionEvaluator E) { // C++ [temp.constr.order] p2 // In order to determine if a constraint P subsumes a constraint Q, P is // transformed into disjunctive normal form, and Q is transformed into // conjunctive normal form. [...] - auto PNormalized = NormalizedConstraint::fromConstraintExprs(S, P); - if (!PNormalized) - return true; - const NormalForm PDNF = makeDNF(*PNormalized); - - auto QNormalized = NormalizedConstraint::fromConstraintExprs(S, Q); - if (!QNormalized) - return true; - const NormalForm QCNF = makeCNF(*QNormalized); + const NormalForm PDNF = makeDNF(PNormalized); + const NormalForm QCNF = makeCNF(QNormalized); // C++ [temp.constr.order] p2 // Then, P subsumes Q if and only if, for every disjunctive clause Pi in the @@ -842,8 +873,7 @@ } bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef AC1, - NamedDecl *D2, ArrayRef AC2, - bool NoCache) { + NamedDecl *D2, ArrayRef AC2) { if (AC1.empty()) return AC2.empty(); if (AC2.empty()) @@ -851,26 +881,56 @@ return true; std::pair Key{D1, D2}; - if (!NoCache) { - auto CacheEntry = SubsumptionCache.find(Key); - if (CacheEntry != SubsumptionCache.end()) - return CacheEntry->second; - } + auto CacheEntry = SubsumptionCache.find(Key); + if (CacheEntry != SubsumptionCache.end()) + return CacheEntry->second; + + MultiLevelTemplateArgumentList MLTAL1 = + getTemplateInstantiationArgs(cast(D1->getDeclContext())); + MultiLevelTemplateArgumentList MLTAL2 = + getTemplateInstantiationArgs(cast(D2->getDeclContext())); + bool Subsumes = IsAtLeastAsConstrained(AC1, MLTAL1, AC2, MLTAL2); + SubsumptionCache.try_emplace(Key, Subsumes); + return Subsumes; +} + +bool +Sema::IsAtLeastAsConstrained(ArrayRef AC1, + const MultiLevelTemplateArgumentList &MLTAL1, + ArrayRef AC2, + const MultiLevelTemplateArgumentList &MLTAL2) { + if (AC1.empty()) + return AC2.empty(); + if (AC2.empty()) + // TD1 has associated constraints and TD2 does not. + return true; + + auto Normalized1 = NormalizedConstraint::fromConstraintExprs(*this, AC1, + MLTAL1); + if (!Normalized1) + // Program is ill-formed at this point. + return false; + + auto Normalized2 = NormalizedConstraint::fromConstraintExprs(*this, AC2, + MLTAL2); + if (!Normalized2) + // Program is ill-formed at this point. + return false; bool Subsumes; - if (subsumes(*this, AC1, AC2, Subsumes, + if (subsumes(*this, *Normalized1, *Normalized2, Subsumes, [this] (const AtomicConstraint &A, const AtomicConstraint &B) { return A.subsumes(Context, B); })) // Program is ill-formed at this point. return false; - if (!NoCache) - SubsumptionCache.try_emplace(Key, Subsumes); return Subsumes; } bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, - ArrayRef AC1, NamedDecl *D2, ArrayRef AC2) { + ArrayRef AC1, const MultiLevelTemplateArgumentList &MLTAL1, + NamedDecl *D2, ArrayRef AC2, + const MultiLevelTemplateArgumentList &MLTAL2) { if (AC1.empty() || AC2.empty()) return false; @@ -904,15 +964,32 @@ { // The subsumption checks might cause diagnostics SFINAETrap Trap(*this); + + auto Normalized1 = NormalizedConstraint::fromConstraintExprs(*this, AC1, + MLTAL1); + if (!Normalized1) + // Program is ill-formed at this point. + return false; + + auto Normalized2 = NormalizedConstraint::fromConstraintExprs(*this, AC2, + MLTAL2); + if (!Normalized2) + // Program is ill-formed at this point. + return false; + bool Is1AtLeastAs2Normally, Is2AtLeastAs1Normally; - if (subsumes(*this, AC1, AC2, Is1AtLeastAs2Normally, NormalExprEvaluator)) + if (subsumes(*this, *Normalized1, *Normalized2, Is1AtLeastAs2Normally, + NormalExprEvaluator)) return false; - if (subsumes(*this, AC2, AC1, Is2AtLeastAs1Normally, NormalExprEvaluator)) + if (subsumes(*this, *Normalized2, *Normalized1, Is2AtLeastAs1Normally, + NormalExprEvaluator)) return false; bool Is1AtLeastAs2, Is2AtLeastAs1; - if (subsumes(*this, AC1, AC2, Is1AtLeastAs2, IdenticalExprEvaluator)) + if (subsumes(*this, *Normalized1, *Normalized2, Is1AtLeastAs2, + IdenticalExprEvaluator)) return false; - if (subsumes(*this, AC2, AC1, Is2AtLeastAs1, IdenticalExprEvaluator)) + if (subsumes(*this, *Normalized2, *Normalized1, Is2AtLeastAs1, + IdenticalExprEvaluator)) return false; if (Is1AtLeastAs2 == Is1AtLeastAs2Normally && Is2AtLeastAs1 == Is2AtLeastAs1Normally) @@ -932,6 +1009,17 @@ return true; } +bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, + ArrayRef AC1, NamedDecl *D2, ArrayRef AC2) { + if (AC1.empty() || AC2.empty()) + return false; + MultiLevelTemplateArgumentList MLTAL1 = + getTemplateInstantiationArgs(cast(D1->getDeclContext())); + MultiLevelTemplateArgumentList MLTAL2 = + getTemplateInstantiationArgs(cast(D2->getDeclContext())); + return MaybeEmitAmbiguousAtomicConstraintsDiagnostic(D1, AC1, MLTAL1, D2, AC2, + MLTAL2); +} ExprRequirement::ExprRequirement(Sema &S, Expr *E, bool IsSimple, @@ -1174,28 +1262,24 @@ } } -NestedRequirement::NestedRequirement(Sema &S, Expr *Constraint) : - Requirement(RK_Nested, Constraint->isInstantiationDependent(), - Constraint->containsUnexpandedParameterPack(), - /*Satisfied=*/false), Value(Constraint) { - if (isDependent()) - return; - S.CheckConstraintSatisfaction(Constraint, Satisfaction); +NestedRequirement::NestedRequirement(Sema &S, Expr *Constraint, + const MultiLevelTemplateArgumentList &TemplateArgs) : + Requirement(RK_Nested, + /*(set below)Dependent=*/false, + /*(set below)ContainsUnexpandedParameterPack=*/false, + /*(set below)Satisfied=*/false), ConstraintExpr(Constraint) { + if (TemplateArgs.getNumLevels() == 0) + S.CheckConstraintSatisfaction(Constraint, Satisfaction); + else { + bool IsDependent, ContainsUnexpandedParameterPack; + S.CheckConstraintSatisfaction(this, Constraint, TemplateArgs, + Satisfaction, IsDependent, ContainsUnexpandedParameterPack); + setDependent(IsDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + } setSatisfied(Satisfaction.IsSatisfied); } void NestedRequirement::Diagnose(Sema &S, bool First) const { - if (isSubstitutionFailure()) { - auto *SubstDiag = getSubstitutionDiagnostic(); - if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_nested_requirement_substitution_error) << (int)First - << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; - else - S.Diag(SubstDiag->DiagLoc, - diag::note_nested_requirement_unknown_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity; - return; - } S.DiagnoseUnsatisfiedConstraint(Satisfaction, First); } \ No newline at end of file Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -337,20 +337,17 @@ // // See if this is a function with constraints that need to be satisfied. if (FunctionDecl *FD = dyn_cast(D)) { - if (Expr *RC = FD->getTrailingRequiresClause()) { - ConstraintSatisfaction Satisfaction; - bool Failed = CheckConstraintSatisfaction(RC, Satisfaction); - if (Failed) - // A diagnostic will have already been generated (non-constant - // constraint expression, for example) - return true; - if (!Satisfaction.IsSatisfied) { - Diag(Loc, - diag::err_reference_to_function_with_unsatisfied_constraints) - << D; - DiagnoseUnsatisfiedConstraint(Satisfaction); - return true; - } + ConstraintSatisfaction Satisfaction; + bool Failed = CheckFunctionConstraints(FD, Satisfaction); + if (Failed) + // A diagnostic will have already been generated (non-constant + // constraint expression, for example) + return true; + if (!Satisfaction.IsSatisfied) { + Diag(Loc, + diag::err_reference_to_function_with_unsatisfied_constraints) << D; + DiagnoseUnsatisfiedConstraint(Satisfaction); + return true; } } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaLambda.h" +#include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/STLExtras.h" @@ -8081,9 +8082,9 @@ SourceLocation(), SourceLocation(), Depth, /*Index=*/0, &II, - /*Typename=*/true, + /*Typename=*/false, /*ParameterPack=*/false, - /*HasTypeConstraint=*/true); + /*OwnsTypeConstraint=*/true); if (ActOnTypeConstraint(TypeConstraint, TParam, /*EllpsisLoc=*/SourceLocation())) @@ -8102,7 +8103,10 @@ } Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { - return new (Context) NestedRequirement(*this, Constraint); + if (!Constraint->isInstantiationDependent()) + return new (Context) NestedRequirement(*this, Constraint, + MultiLevelTemplateArgumentList()); + return new (Context) NestedRequirement(Constraint); } RequiresExprBodyDecl * Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -6175,10 +6175,9 @@ return; } - Expr *RequiresClause = Function->getTrailingRequiresClause(); - if (LangOpts.ConceptsTS && RequiresClause) { + if (LangOpts.ConceptsTS) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Function, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -6687,10 +6686,9 @@ return; } - Expr *RequiresClause = Method->getTrailingRequiresClause(); - if (LangOpts.ConceptsTS && RequiresClause) { + if (LangOpts.ConceptsTS) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Method, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -7049,10 +7047,9 @@ return; } - Expr *RequiresClause = Conversion->getTrailingRequiresClause(); - if (LangOpts.ConceptsTS && RequiresClause) { + if (LangOpts.ConceptsTS) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Conversion, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -9675,22 +9672,20 @@ return false; } - if (const Expr *RC = FD->getTrailingRequiresClause()) { - ConstraintSatisfaction Satisfaction; - if (S.CheckConstraintSatisfaction(RC, Satisfaction)) - return false; - if (!Satisfaction.IsSatisfied) { - if (Complain) { - if (InOverloadResolution) - S.Diag(FD->getBeginLoc(), - diag::note_ovl_candidate_unsatisfied_constraints); - else - S.Diag(Loc, diag::err_addrof_function_constraints_not_satisfied) - << FD; - S.DiagnoseUnsatisfiedConstraint(Satisfaction); - } - return false; + ConstraintSatisfaction Satisfaction; + if (S.CheckFunctionConstraints(const_cast(FD), Satisfaction)) + return false; + if (!Satisfaction.IsSatisfied) { + if (Complain) { + if (InOverloadResolution) + S.Diag(FD->getBeginLoc(), + diag::note_ovl_candidate_unsatisfied_constraints); + else + S.Diag(Loc, diag::err_addrof_function_constraints_not_satisfied) + << FD; + S.DiagnoseUnsatisfiedConstraint(Satisfaction); } + return false; } auto I = llvm::find_if(FD->parameters(), [](const ParmVarDecl *P) { @@ -10625,8 +10620,7 @@ diag::note_ovl_candidate_constraints_not_satisfied) << (unsigned) FnKind; ConstraintSatisfaction Satisfaction; - if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(), - Satisfaction)) + if (S.CheckFunctionConstraints(Fn, Satisfaction)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); } Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -4273,15 +4273,17 @@ break; } } - if (!IsInstantiationDependent) + if (!IsInstantiationDependent) { + TemplateArgumentList TempList(TemplateArgumentList::OnStack, Converted); if (CheckConstraintSatisfaction(NamedConcept, {NamedConcept->getConstraintExpr()}, - Converted, + MultiLevelTemplateArgumentList(TempList), SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), TemplateArgs->getRAngleLoc()), Satisfaction)) return ExprError(); + } return ConceptSpecializationExpr::Create(Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, @@ -5124,7 +5126,8 @@ case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - if (CheckTemplateTemplateArgument(TempParm, Params, Arg)) + if (CheckTemplateArgument(TempParm, Params, Arg, ArgumentPackIndex, + Converted)) return true; Converted.push_back(Arg.getArgument()); @@ -6873,9 +6876,11 @@ /// /// 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) { +bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, + TemplateParameterList *Params, + TemplateArgumentLoc &Arg, + unsigned ArgumentPackIndex, + ArrayRef Converted) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); TemplateDecl *Template = Name.getAsTemplateDecl(); if (!Template) { @@ -6929,6 +6934,25 @@ if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template, Arg.getLocation())) { + TemplateArgumentList ParamsSoFar(TemplateArgumentList::OnStack, + Converted); + // We want the enclosing template arguments along with the template + // arguments provided thus far in this template parameter list: + // + // template + // struct A { + // template class TT> struct B { }; + // }; + // In the above example, for checking TT, we'll need: + // ParamMLTAL: + // Depth 0: + // Depth 1: + DeclContext *TemplatedEntityParent = Param->getDeclContext()->getParent(); + MultiLevelTemplateArgumentList ParamMLTAL = + getTemplateInstantiationArgs(cast(TemplatedEntityParent), + /*Innermost=*/&ParamsSoFar); + MultiLevelTemplateArgumentList ArgMLTAL = + getTemplateInstantiationArgs(Template); // C++2a[temp.func.order]p2 // [...] If both deductions succeed, the partial ordering selects the // more constrained template as described by the rules in @@ -6936,16 +6960,16 @@ SmallVector ParamsAC, TemplateAC; Params->getAssociatedConstraints(ParamsAC); Template->getAssociatedConstraints(TemplateAC); - if (!IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - /*NoCache=*/true)) { + if (!IsAtLeastAsConstrained(ParamsAC, ParamMLTAL, TemplateAC, ArgMLTAL)) { Diag(Arg.getLocation(), diag::err_template_template_parameter_not_at_least_as_constrained) << Template << Param << Arg.getSourceRange(); Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, + ParamMLTAL, Template, + TemplateAC, ArgMLTAL); return true; } return false; Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -2716,8 +2716,15 @@ TemplateDeductionInfo& Info) { llvm::SmallVector AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); + TemplateArgumentList TAL(TemplateArgumentList::OnStack, DeducedArgs); + DeclContext *DC = Template->getDeclContext(); + MultiLevelTemplateArgumentList MLTAL; + if (NamedDecl *ND = dyn_cast(DC)) + MLTAL = S.getTemplateInstantiationArgs(ND, /*Innermost=*/&TAL); + else + MLTAL.addOuterTemplateArguments(&TAL); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, - DeducedArgs, Info.getLocation(), + MLTAL, Info.getLocation(), Info.AssociatedConstraintsSatisfaction) || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); @@ -3409,8 +3416,8 @@ // ([temp.constr.decl]), those constraints are checked for satisfaction // ([temp.constr.constr]). If the constraints are not satisfied, type // deduction fails. - if (CheckInstantiatedFunctionTemplateConstraints(Info.getLocation(), - Specialization, Builder, Info.AssociatedConstraintsSatisfaction)) + if (CheckFunctionConstraints(Specialization, + Info.AssociatedConstraintsSatisfaction)) return TDK_MiscellaneousDeductionFailure; if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { @@ -4500,7 +4507,7 @@ QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0); NamedDecl *TemplParamPtr = TemplParam; FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt( - Context, Loc, Loc, TemplParamPtr, Loc, nullptr); + Context, Loc, Loc, TemplParamPtr, Loc, nullptr, nullptr); QualType FuncParam = SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false) Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -53,7 +53,7 @@ /// used to determine the proper set of template instantiation arguments for /// friend function template specializations. MultiLevelTemplateArgumentList -Sema::getTemplateInstantiationArgs(NamedDecl *D, +Sema::getTemplateInstantiationArgs(Decl *D, const TemplateArgumentList *Innermost, bool RelativeToPrimary, const FunctionDecl *Pattern) { @@ -451,6 +451,13 @@ } } +Sema:: +InstantiatingTemplate::InstantiatingTemplate(InstantiatingTemplate &&Other) : + SemaRef(Other.SemaRef), Invalid(Other.Invalid), + AlreadyInstantiating(Other.AlreadyInstantiating) { + Other.Invalid = true; +} + bool Sema::InstantiatingTemplate::CheckInstantiationDepth( SourceLocation PointOfInstantiation, SourceRange InstantiationRange) { @@ -1763,22 +1770,58 @@ else TransRetReq.emplace(SemaRef.Context, TransType); } else if (RetReq.isTypeConstraint()) { - TemplateParameterList *OrigTPL = - RetReq.getTypeConstraintTemplateParameterList(); - Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(), - Req, Info, OrigTPL->getSourceRange()); - if (TPLInst.isInvalid()) - return nullptr; - TemplateParameterList *TPL = - TransformTemplateParameterList(OrigTPL); - if (!TPL) - TransRetReq.emplace(createSubstDiag(SemaRef, Info, - [&] (llvm::raw_string_ostream& OS) { - RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint() - ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); - })); - else { - TPLInst.Clear(); + auto *OrigTParam = + cast( + RetReq.getTypeConstraintTemplateParameterList()->getParam(0)); + auto *TParam = + TemplateTypeParmDecl::Create( + SemaRef.Context, OrigTParam->getDeclContext(), SourceLocation(), + SourceLocation(), + OrigTParam->getDepth() - TemplateArgs.getNumSubstitutedLevels(), + /*Index=*/0, OrigTParam->getIdentifier(), /*Typename=*/false, + /*ParameterPack=*/false, /*OwnsTypeConstraint=*/true); + + auto *TC = RetReq.getTypeConstraint(); + auto *OrigArgs = TC->getTemplateArgsAsWritten(); + if (OrigArgs) { + Sema::InstantiatingTemplate TALInst( + SemaRef, OrigArgs->getLAngleLoc(), Req, Info, + SourceRange(OrigArgs->getLAngleLoc(), OrigArgs->getRAngleLoc())); + if (TALInst.isInvalid()) + return nullptr; + TemplateArgumentListInfo InstTemplateArgs(OrigArgs->LAngleLoc, + OrigArgs->RAngleLoc); + if (TransformTemplateArguments(OrigArgs->getTemplateArgs(), + OrigArgs->NumTemplateArgs, + InstTemplateArgs)) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_string_ostream& OS) { + TC->print(OS, SemaRef.getPrintingPolicy()); + })); + else { + TALInst.Clear(); + + if (SemaRef.AttachTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), + TC->getNamedConcept(), + &InstTemplateArgs, TParam, + /*EllipsisLoc=*/SourceLocation())) + return nullptr; + } + } else if (SemaRef.AttachTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), + TC->getNamedConcept(), + /*TemplateArgs=*/nullptr, TParam, + /*EllipsisLoc=*/SourceLocation())) + return nullptr; + + if (!TransRetReq) { + auto *TPL = TemplateParameterList::Create(SemaRef.Context, + SourceLocation(), + SourceLocation(), + ArrayRef(TParam), + SourceLocation(), + /*RequiresClause=*/nullptr); TransRetReq.emplace(SemaRef.Context, TPL); } } @@ -1794,39 +1837,7 @@ NestedRequirement * TemplateInstantiator::TransformNestedRequirement(NestedRequirement *Req) { - if (!Req->isDependent() && !AlwaysRebuild()) - return Req; - if (Req->isSubstitutionFailure()) { - if (AlwaysRebuild()) - return RebuildNestedRequirement( - Req->getSubstitutionDiagnostic()); - return Req; - } - Sema::InstantiatingTemplate ReqInst(SemaRef, - Req->getConstraintExpr()->getBeginLoc(), Req, - Sema::InstantiatingTemplate::ConstraintsCheck{}, - Req->getConstraintExpr()->getSourceRange()); - - ExprResult TransConstraint; - TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); - { - EnterExpressionEvaluationContext ContextRAII( - SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); - Sema::SFINAETrap Trap(SemaRef); - Sema::InstantiatingTemplate ConstrInst(SemaRef, - Req->getConstraintExpr()->getBeginLoc(), Req, Info, - Req->getConstraintExpr()->getSourceRange()); - if (ConstrInst.isInvalid()) - return nullptr; - TransConstraint = TransformExpr(Req->getConstraintExpr()); - if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) - return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, - [&] (llvm::raw_string_ostream& OS) { - Req->getConstraintExpr()->printPretty(OS, nullptr, - SemaRef.getPrintingPolicy()); - })); - } - return RebuildNestedRequirement(TransConstraint.get()); + return RebuildNestedRequirement(Req->getConstraintExpr(), TemplateArgs); } @@ -2899,6 +2910,15 @@ if (TSK == TSK_ExplicitInstantiationDefinition && !Pattern->isDefined()) continue; + // C++2a [temp.explicit]p10: + // [...] provided that the associated constraints, if any, of that + // member are satisfied by the template arguments of the explicit + // instantiation. [...] + ConstraintSatisfaction Satisfaction; + if (CheckFunctionConstraints(Function, Satisfaction) || + !Satisfaction.IsSatisfied) + continue; + Function->setTemplateSpecializationKind(TSK, PointOfInstantiation); if (Function->isDefined()) { Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1704,17 +1704,6 @@ return nullptr; } - Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); - if (TrailingRequiresClause) { - ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, - TemplateArgs); - if (!SubstRC.isUsable() || SubstRC.isInvalid()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) - return nullptr; - } - // If we're instantiating a local function declaration, put the result // in the enclosing namespace; otherwise we need to find the instantiated // context. @@ -1774,7 +1763,7 @@ Params[P]->setOwningFunction(Function); Function->setParams(Params); - if (TrailingRequiresClause) + if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) Function->setTrailingRequiresClause(TrailingRequiresClause); if (TemplateParams) { @@ -2025,15 +2014,6 @@ } Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); - if (TrailingRequiresClause) { - ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, - TemplateArgs); - if (!SubstRC.isUsable() || SubstRC.isInvalid()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) - return nullptr; - } DeclContext *DC = Owner; if (isFriend) { @@ -2306,34 +2286,8 @@ SemaRef.Context, Owner, D->getBeginLoc(), D->getLocation(), D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(), D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack(), - D->hasTypeConstraint()); + /*OwnsTypeConstraint=*/false); Inst->setAccess(AS_public); - if (auto *TC = D->getTypeConstraint()) { - // TODO: Concepts: do not instantiate the constraint (delayed constraint - // substitution) - const ASTTemplateArgumentListInfo *TemplArgInfo - = TC->getTemplateArgsAsWritten(); - TemplateArgumentListInfo InstArgs; - - if (TemplArgInfo) { - InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); - InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); - if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(), - TemplArgInfo->NumTemplateArgs, - InstArgs, TemplateArgs)) - return nullptr; - } - ExprResult Result = - SemaRef.SubstExpr(TC->getImmediatelyDeclaredConstraint(), TemplateArgs); - if (Result.isInvalid()) - return nullptr; - - Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), - TC->getConceptNameInfo(), TC->getFoundDecl(), TC->getNamedConcept(), - TemplArgInfo ? ASTTemplateArgumentListInfo::Create(SemaRef.Context, - InstArgs) : nullptr, - Result.get()); - } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { TypeSourceInfo *InstantiatedDefaultArg = SemaRef.SubstType(D->getDefaultArgumentInfo(), TemplateArgs, @@ -2342,6 +2296,13 @@ Inst->setDefaultArgument(InstantiatedDefaultArg); } + if (D->hasTypeConstraint()) { + if (D->typeConstraintWasInherited()) + Inst->setInheritedTypeConstraint(D->getInheritedFromTypeConstraintDecl()); + else + Inst->setInheritedTypeConstraint(D); + } + // Introduce this template parameter's instantiation into the instantiation // scope. SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Inst); @@ -2621,6 +2582,7 @@ D->getDefaultArgument().getTemplateQualifierLoc(), D->getDefaultArgument().getTemplateNameLoc())); } + Param->setAccess(AS_public); // Introduce this template parameter's instantiation into the instantiation @@ -3481,21 +3443,12 @@ if (Invalid) return nullptr; - // FIXME: Concepts: Substitution into requires clause should only happen when - // checking satisfaction. - Expr *InstRequiresClause = nullptr; - if (Expr *E = L->getRequiresClause()) { - ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs); - if (Res.isInvalid() || !Res.isUsable()) { - return nullptr; - } - InstRequiresClause = Res.get(); - } - TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), L->getLAngleLoc(), Params, - L->getRAngleLoc(), InstRequiresClause); + L->getRAngleLoc(), + /*RequiresClause=*/nullptr, + /*InheritedConstraints=*/L); return InstL; } @@ -3979,17 +3932,69 @@ TemplateArgs); } -bool Sema::CheckInstantiatedFunctionTemplateConstraints( - SourceLocation PointOfInstantiation, FunctionDecl *Decl, - ArrayRef TemplateArgs, +bool Sema::CheckFunctionConstraints(FunctionDecl *Decl, ConstraintSatisfaction &Satisfaction) { + NamedDecl *Template = Decl->getPrimaryTemplate(); + llvm::SmallVector AssociatedConstraints; + if (Template) { + cast(Template)->getAssociatedConstraints(AssociatedConstraints); + } else { + Decl->getAssociatedConstraints(AssociatedConstraints); + if (!AssociatedConstraints.empty()) { + DeclContext *DC = Decl->getParent(); + while (!DC->isFileContext()) { + if (auto *Spec = dyn_cast(DC)) { + if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(Spec)) + break; + if (!isa(Spec)) { + Template = Spec->getSpecializedTemplate(); + } else { + Template = Spec; + } + break; + } + if (auto *Record = dyn_cast(DC)) + if (auto *ClassTemplate = Record->getDescribedClassTemplate()) { + Template = ClassTemplate; + break; + } + if (auto *Function = dyn_cast(DC)) + if (auto *FunctionTemplate = Function->getDescribedTemplate()) { + Template = FunctionTemplate; + break; + } + DC = DC->getParent(); + } + } + } + + if (AssociatedConstraints.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + if (!Template) { + // This is a non-template function which is also not a method of a template + // class. + if (Expr *RC = Decl->getTrailingRequiresClause()) + return CheckConstraintSatisfaction(RC, Satisfaction); + + Satisfaction.IsSatisfied = true; + return false; + } + + // This is either an instantiated function template or a non-template method + // of a class template. + // Enter the scope of this instantiation. We don't use // PushDeclContext because we don't have a scope. Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); MultiLevelTemplateArgumentList MLTAL = - getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true); + getTemplateInstantiationArgs(Decl, /*Innermost=*/nullptr, + /*RelativeToPrimary*/true); // If this is not an explicit specialization - we need to get the instantiated // version of the template arguments and add them to scope for the @@ -4000,13 +4005,21 @@ Scope, MLTAL)) return true; - FunctionTemplateDecl *Template = Decl->getPrimaryTemplate(); + // Note - code synthesis context for the constraints check is created // inside CheckConstraintsSatisfaction. - SmallVector TemplateAC; - Template->getAssociatedConstraints(TemplateAC); - return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs, - PointOfInstantiation, Satisfaction); + if (auto *TD = dyn_cast(Template)) + return CheckConstraintSatisfaction(TD, AssociatedConstraints, MLTAL, + Decl->getPointOfInstantiation(), + Satisfaction); + if (auto *Var = dyn_cast(Template)) + return CheckConstraintSatisfaction(Var, AssociatedConstraints, MLTAL, + Decl->getPointOfInstantiation(), + Satisfaction); + return CheckConstraintSatisfaction( + cast(Template), + AssociatedConstraints, MLTAL, Decl->getPointOfInstantiation(), + Satisfaction); } /// Initializes the common fields of an instantiation function Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2953,7 +2953,7 @@ /*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(), TemplateParameterDepth, AutoParameterPosition, /*Identifier*/ nullptr, false, IsParameterPack, - /*HasTypeConstraint=*/false); + /*OwnsTypeConstraint=*/false); LSI->AutoTemplateParams.push_back(CorrespondingTemplateParam); // Replace the 'auto' in the function parameter with this invented // template type parameter. Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3041,12 +3041,15 @@ } NestedRequirement * - RebuildNestedRequirement(Requirement::SubstitutionDiagnostic *SubstDiag) { - return new (SemaRef.Context) NestedRequirement(SubstDiag); + RebuildNestedRequirement(Expr *Constraint, + const MultiLevelTemplateArgumentList &TemplateArgs) { + return new (SemaRef.Context) NestedRequirement(SemaRef, Constraint, + TemplateArgs); } - NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { - return new (SemaRef.Context) NestedRequirement(SemaRef, Constraint); + NestedRequirement * + RebuildNestedRequirement(Expr *Constraint) { + return new (SemaRef.Context) NestedRequirement(Constraint); } /// \brief Build a new Objective-C boxed expression. @@ -10988,8 +10991,7 @@ template bool TreeTransform::TransformRequiresExprRequirements( - ArrayRef Reqs, - SmallVectorImpl &Transformed) { + ArrayRef Reqs, SmallVectorImpl &Transformed) { for (Requirement *Req : Reqs) { Requirement *TransReq = nullptr; if (auto *TypeReq = dyn_cast(Req)) @@ -11069,12 +11071,6 @@ template NestedRequirement * TreeTransform::TransformNestedRequirement(NestedRequirement *Req) { - if (Req->isSubstitutionFailure()) { - if (getDerived().AlwaysRebuild()) - return getDerived().RebuildNestedRequirement( - Req->getSubstitutionDiagnostic()); - return Req; - } ExprResult TransConstraint = getDerived().TransformExpr(Req->getConstraintExpr()); if (TransConstraint.isInvalid()) Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -8840,6 +8840,7 @@ while (NumParams--) Params.push_back(ReadDeclAs(F, Record, Idx)); + // TODO: Concepts: We do not preserve constraint inheritance here. bool HasRequiresClause = Record[Idx++]; Expr *RequiresClause = HasRequiresClause ? ReadExpr(F) : nullptr; Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -2358,15 +2358,20 @@ D->setDeclaredWithTypename(Record.readInt()); if (Record.readInt()) { - NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); - DeclarationNameInfo DN; - Record.readDeclarationNameInfo(DN); - ConceptDecl *NamedConcept = cast(Record.readDecl()); - const ASTTemplateArgumentListInfo *ArgsAsWritten = - Record.readASTTemplateArgumentListInfo(); - Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); - D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, - ArgsAsWritten, ImmediatelyDeclaredConstraint); + if (Record.readInt()) { + NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); + DeclarationNameInfo DN; + Record.readDeclarationNameInfo(DN); + ConceptDecl *NamedConcept = cast(Record.readDecl()); + const ASTTemplateArgumentListInfo *ArgsAsWritten = + Record.readASTTemplateArgumentListInfo(); + Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); + D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, + ArgsAsWritten, ImmediatelyDeclaredConstraint); + } + else + D->setInheritedTypeConstraint( + Record.readDeclAs()); } if (Record.readInt()) @@ -3807,9 +3812,9 @@ D = FunctionTemplateDecl::CreateDeserialized(Context, ID); break; case DECL_TEMPLATE_TYPE_PARM: { - bool HasTypeConstraint = Record.readInt(); + bool OwnsTypeConstraint = Record.readInt(); D = TemplateTypeParmDecl::CreateDeserialized(Context, ID, - HasTypeConstraint); + OwnsTypeConstraint); break; } case DECL_NON_TYPE_TEMPLATE_PARM: Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -843,15 +843,10 @@ } } break; case Requirement::RK_Nested: { - if (bool IsSubstitutionDiagnostic = Record.readInt()) { - R = new (Record.getContext()) NestedRequirement( - readSubstitutionDiagnostic(Record)); - break; - } Expr *E = Record.readExpr(); - if (E->isInstantiationDependent()) + if (/*IsDependent=*/Record.readInt()) { R = new (Record.getContext()) NestedRequirement(E); - else + } else R = new (Record.getContext()) NestedRequirement(E, readConstraintSatisfaction(Record)); } break; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5918,6 +5918,7 @@ Record->push_back(TemplateParams->size()); for (const auto &P : *TemplateParams) AddDeclRef(P); + // TODO: Concepts: We do not preserve constraint inheritance here. if (const Expr *RequiresClause = TemplateParams->getRequiresClause()) { Record->push_back(true); AddStmt(const_cast(RequiresClause)); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1580,7 +1580,7 @@ } void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { - Record.push_back(D->hasTypeConstraint()); + Record.push_back(D->hasTypeConstraint() && !D->typeConstraintWasInherited()); VisitTypeDecl(D); Record.push_back(D->wasDeclaredWithTypename()); @@ -1588,11 +1588,16 @@ const TypeConstraint *TC = D->getTypeConstraint(); Record.push_back(TC != nullptr); if (TC) { - Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc()); - Record.AddDeclarationNameInfo(TC->getConceptNameInfo()); - Record.AddDeclRef(TC->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten()); - Record.AddStmt(TC->getImmediatelyDeclaredConstraint()); + bool OwnsTC = !D->typeConstraintWasInherited(); + Record.push_back(OwnsTC); + if (OwnsTC) { + Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc()); + Record.AddDeclarationNameInfo(TC->getConceptNameInfo()); + Record.AddDeclRef(TC->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten()); + Record.AddStmt(TC->getImmediatelyDeclaredConstraint()); + } else + Record.AddDeclRef(D->getInheritedFromTypeConstraintDecl()); } bool OwnsDefaultArg = D->hasDefaultArgument() && Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -483,14 +483,10 @@ auto *NestedReq = cast(R); Record.push_back(Requirement::RK_Nested); - if (auto *SubstDiag = - NestedReq->Value.dyn_cast()){ - addSubstitutionDiagnostic(Record, SubstDiag); - } else { - Record.AddStmt(NestedReq->Value.get()); - if (!NestedReq->isDependent()) - addConstraintSatisfaction(Record, NestedReq->Satisfaction); - } + Record.AddStmt(NestedReq->getConstraintExpr()); + Record.push_back(NestedReq->isDependent()); + if (!NestedReq->isDependent()) + addConstraintSatisfaction(Record, NestedReq->Satisfaction); } } Record.AddSourceLocation(E->getEndLoc()); Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p4.cpp =================================================================== --- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p4.cpp +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p4.cpp @@ -26,11 +26,19 @@ struct A { static void foo(int) requires (sizeof(T) == 1) {} // expected-note 3{{because 'sizeof(char [2]) == 1' (2 == 1) evaluated to false}} static void bar(int) requires (sizeof(T) == 2) {} // expected-note 3{{because 'sizeof(char) == 2' (1 == 2) evaluated to false}} + template + static void baz(int) requires (sizeof(T) == 2) { + struct { static void goo() requires (sizeof(T) > sizeof(U)) {} } a; + // expected-note@-1{{because 'sizeof(short) > sizeof(int)' (2 > 4) evaluated to false}} + a.goo(); + // expected-error@-1{{invalid reference to function 'goo': constraints not satisfied}} + } }; void baz() { A::foo(1); A::bar(1); // expected-error{{invalid reference to function 'bar': constraints not satisfied}} + A::baz(1); // expected-note{{in instantiation of function template specialization 'methods::A::baz' requested here}} A::foo(1); // expected-error{{invalid reference to function 'foo': constraints not satisfied}} A::bar(1); void (*p1)(int) = A::foo; Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/compound-requirement.cpp =================================================================== --- test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/compound-requirement.cpp +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/compound-requirement.cpp @@ -171,7 +171,7 @@ // Substitution failure in type constraint -template requires requires (T t) { { t } -> Same; } // expected-note{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +template requires requires (T t) { { t } -> Same; } // expected-note{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} struct r15 {}; using r15i1 = r15; Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/nested-requirement.cpp =================================================================== --- test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/nested-requirement.cpp +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -19,11 +19,12 @@ template struct X { - template requires requires (U u) { requires sizeof(u) == sizeof(T); } // expected-note{{because 'sizeof (u) == sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} + template requires requires (U u) { requires decltype(u)::hey == sizeof(T); } + // expected-note@-1{{because substituted constraint expression is ill-formed: type 'decltype(u)' (aka 'int') cannot be used prior to '::' because it has no members}} struct r4 {}; }; -using r4i = X::r4; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}} +using r4i = X::r4; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}} // C++ [expr.prim.req.nested] Examples namespace std_example { @@ -43,4 +44,4 @@ requires sizeof(a) == 4; // OK requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}} }; -} \ No newline at end of file +} Index: test/CXX/concepts-ts/over/over.match/over.match.best/p1.cpp =================================================================== --- test/CXX/concepts-ts/over/over.match/over.match.best/p1.cpp +++ test/CXX/concepts-ts/over/over.match/over.match.best/p1.cpp @@ -38,7 +38,7 @@ template void bar() requires (sizeof(T) == 1 && sizeof(T) >= 0) { } // expected-note@-1{{candidate function [with T = char]}} - // expected-note@-2{{'sizeof(char) == 1' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} + // expected-note@-2{{'sizeof(T) == 1' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} static_assert(is_same_v()), void>); // expected-error@-1{{call to 'bar' is ambiguous}} @@ -112,4 +112,3 @@ static_assert(goo(1) == 1); static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}} } - Index: test/CXX/concepts-ts/temp/temp.arg.template/p3.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.arg.template/p3.cpp +++ test/CXX/concepts-ts/temp/temp.arg.template/p3.cpp @@ -5,6 +5,8 @@ template concept D = C && T::g(); template concept F = T::f(); // expected-note@-1{{'T::f()' in the two declarations is not considered equivalent - move it to a concept and reference it from here:}} +template concept G = T::f(U{}); + template class P> struct S1 { }; // expected-note 2{{'P' declared here}} template struct X { }; // expected-note{{'X' declared here}} @@ -12,6 +14,8 @@ template struct Y { }; // expected-note 2{{'Y' declared here}} template struct Z { }; template struct W { }; // expected-note{{'W' declared here}} +template> struct U { }; // expected-note{{'U' declared here}} +template> struct V { }; // expected-note 2{{'V' declared here}} S1 s11; S1 s12; // expected-error{{template template argument 'Y' must not be more constrained than template template parameter 'P'}} @@ -32,3 +36,19 @@ using s31 = S3; using s32 = S3; + +template> class P> struct S4 { }; +// expected-note@-1{{'P' declared here}} + +S4 s41; +S4 s42; // expected-error{{template template argument 'U' must not be more constrained than template template parameter 'P'}} + +template +struct S5 { + template> class P> struct S { }; + // expected-note@-1 2{{'P' declared here}} +}; + +S5::S s51; +S5::S s52; // expected-error{{template template argument 'V' must not be more constrained than template template parameter 'P'}} +S5::S s53; // expected-error{{template template argument 'V' must not be more constrained than template template parameter 'P'}} Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s -#if 0 + template requires (sizeof(T) >= 2) // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}} struct A { static constexpr int value = sizeof(T); @@ -16,14 +16,14 @@ static_assert(SizeDiff == 3); static_assert(SizeDiff == 0); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = int, U = char [4]]}} static_assert(SizeDiff == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int]}} -#endif + template requires ((sizeof(Ts) == 4) || ...) // expected-note{{because 'sizeof(char) == 4' (1 == 4) evaluated to false}} expected-note{{'sizeof(long long) == 4' (8 == 4) evaluated to false}} expected-note{{'sizeof(int [20]) == 4' (80 == 4) evaluated to false}} constexpr auto SumSizes = (sizeof(Ts) + ...); static_assert(SumSizes == 13); static_assert(SumSizes == 89); // expected-error{{constraints not satisfied for variable template 'SumSizes' [with Ts = ]}} -#if 0 + template concept IsBig = sizeof(T) > 100; // expected-note{{because 'sizeof(int) > 100' (4 > 100) evaluated to false}} @@ -90,4 +90,11 @@ static_assert(C{}); // expected-note{{while checking constraint satisfaction for template 'C' required here}} static_assert(D{}); // expected-note{{while checking constraint satisfaction for template 'D' required here}} -#endif \ No newline at end of file + +template +struct dummy { + template + requires (sizeof(U) == 4) + class A; + using type = A; +}; Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/p3.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/p3.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/p3.cpp @@ -9,17 +9,18 @@ concept Invalid = X{}; template -concept False = false; // expected-note{{because 'false' evaluated to false}} +concept False = false; // expected-note 2{{because 'false' evaluated to false}} template concept True = true; -// TODO: Concepts: Uncomment trailing requires clauses here when we have correct substitution. -//template -// requires False -//void g1() requires Invalid; -// -//using g1i = decltype(g1()); +template + requires False // expected-note{{because 'int' does not satisfy 'False'}} +void g1() requires Invalid; +// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = int]}} + +using g1i = decltype(g1()); +// expected-error@-1{{no matching function for call to 'g1'}} template // expected-note{{because 'int' does not satisfy 'False'}} requires Invalid Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp @@ -27,7 +27,7 @@ template concept C2 = C1::foo>; // expected-error@-1 2{{implicit instantiation of undefined template 'ill_formed_subst::B'}} - // expected-note@-2 2{{when substituting into C1::foo> [with T = type-parameter-0-0, U = int]. Make sure concept arguments are valid for any substitution}} + // expected-note@-2 2{{when substituting into C1::foo>. Make sure concept arguments are valid for any substitution}} template requires C2 struct A {}; // expected-note {{template is declared here}} @@ -43,7 +43,7 @@ template concept C2 = C1; // expected-error@-1 2{{too many template arguments for concept 'C1'}} - // expected-note@-2 2{{when substituting into C1 [with Ts = ]. Make sure concept arguments are valid for any substitution}} + // expected-note@-2 2{{when substituting into C1. Make sure concept arguments are valid for any substitution}} template requires C2 struct A {}; // expected-note{{template is declared here}} @@ -68,3 +68,12 @@ template requires C2 && true struct A {}; } + +namespace unreferenced_parameters { + template constexpr bool B = true; + template concept C0 = B; + template concept C1 = C0 && C0; + + template requires C1 struct A; + template requires C1 struct A; +} \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -85,4 +85,4 @@ struct I { }; // expected-note {{template is declared here}} template requires C2 -struct I { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} \ No newline at end of file +struct I { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} Index: test/CXX/concepts-ts/temp/temp.explicit/p10.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.explicit/p10.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s +// expected-no-diagnostics + +template +concept Comparable = requires(T a) { {a == a} -> bool; }; + +template +struct W { + T val; + + bool operator==(W const& y) const + requires Comparable + { return this->val == y.val; } +}; + +struct X {}; +static_assert(!Comparable); +static_assert(!Comparable>); +template struct W; \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.inst/p17.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.inst/p17.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +// expected-no-diagnostics + +template +struct dont_instantiate { using T::value; }; + +template +struct S1 { + template requires dont_instantiate::value + struct S2 { }; + + void f() requires dont_instantiate::value; +}; + +S1 s; \ No newline at end of file Index: test/SemaTemplate/instantiate-requires-expr.cpp =================================================================== --- test/SemaTemplate/instantiate-requires-expr.cpp +++ test/SemaTemplate/instantiate-requires-expr.cpp @@ -35,8 +35,8 @@ template requires false_v -// expected-note@-1 {{because 'false_v'}} -// expected-note@-2 {{because 'false_v' evaluated to false}} +// expected-note@-1 {{because 'false_v'}} +// expected-note@-2 {{because 'false_v' evaluated to false}} struct r2 {}; using r2i1 = r2; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = ]}} @@ -207,25 +207,35 @@ namespace nested_requirement { // check that constraint expression is instantiated correctly - template requires false_v // expected-note{{because 'false_v' evaluated to false}} + // (substitution doesn't actually occur at the expression level because of [expr.prim.req.nested]p1 + template requires false_v // expected-note{{because 'false_v' evaluated to false}} struct r1 {}; using r1i = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} - // substitution error occurs in expr, then expr is instantiated again. + // substitution occurs in expr, then expr is instantiated again. template struct a { + template + struct r {}; + }; + + using ari = a::r; + + // substitution error occurs in expr, then expr is instantiated again. + template + struct b { template requires - (requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires <>; } , false' evaluated to false}} + (requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires sizeof (T::a) == 0; } , false' evaluated to false}} struct r {}; }; - using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + using bri = b::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} // Parameter pack inside expr template requires false_v<(requires { requires sizeof(Ts) == 0; } && ...)> - // expected-note@-1 {{because 'false_v' evaluated to false}} + // expected-note@-1 {{because 'false_v' evaluated to false}} struct r2 {}; using r2i = r2; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = ]}} @@ -234,7 +244,7 @@ // Parameter pack inside multiple requirements template requires false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)> -// expected-note@-1 {{because 'false_v' evaluated to false}} +// expected-note@-1 {{because 'false_v' evaluated to false}} struct r4 {}; using r4i = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}}