diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 30c4706d2a1569..3ebaad4eafdd8c 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -29,14 +29,14 @@ class ConceptSpecializationExpr; class ConstraintSatisfaction : public llvm::FoldingSetNode { // The template-like entity that 'owns' the constraint checked here (can be a // constrained entity or a concept). - NamedDecl *ConstraintOwner = nullptr; + const NamedDecl *ConstraintOwner = nullptr; llvm::SmallVector TemplateArgs; public: ConstraintSatisfaction() = default; - ConstraintSatisfaction(NamedDecl *ConstraintOwner, + ConstraintSatisfaction(const NamedDecl *ConstraintOwner, ArrayRef TemplateArgs) : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(), TemplateArgs.end()) { } @@ -57,7 +57,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { } static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, - NamedDecl *ConstraintOwner, + const NamedDecl *ConstraintOwner, ArrayRef TemplateArgs); }; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b49c222c8fb756..5e88c6ee86abef 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4686,6 +4686,8 @@ def note_checking_constraints_for_var_spec_id_here : Note< def note_checking_constraints_for_class_spec_id_here : Note< "while checking constraint satisfaction for class template partial " "specialization '%0' required here">; +def note_checking_constraints_for_function_here : Note< + "while checking constraint satisfaction for function '%0' required here">; def note_constraint_substitution_here : Note< "while substituting template arguments into constraint expression here">; def note_constraint_normalization_here : Note< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5ab74e4cd662b8..64a6793aa411c2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6288,7 +6288,7 @@ class Sema final { /// \returns true if an error occurred and satisfaction could not be checked, /// false otherwise. bool CheckConstraintSatisfaction( - NamedDecl *Template, ArrayRef ConstraintExprs, + const NamedDecl *Template, ArrayRef ConstraintExprs, ArrayRef TemplateArgs, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); @@ -6301,6 +6301,17 @@ class Sema final { bool CheckConstraintSatisfaction(const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction); + /// Check whether the given function decl's trailing requires clause is + /// satisfied, if any. Returns false and updates Satisfaction with the + /// satisfaction verdict if successful, emits a diagnostic and returns true if + /// an error occured and satisfaction could not be determined. + /// + /// \returns true if an error occurred, false otherwise. + bool CheckFunctionConstraints(const FunctionDecl *FD, + ConstraintSatisfaction &Satisfaction, + SourceLocation UsageLoc = SourceLocation()); + + /// \brief Ensure that the given template arguments satisfy the constraints /// associated with the given template, emitting a diagnostic if they do not. /// diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index c28a06bdf0b24a..549088ad4a8a87 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -59,8 +59,8 @@ ASTConstraintSatisfaction::Create(const ASTContext &C, } void ConstraintSatisfaction::Profile( - llvm::FoldingSetNodeID &ID, const ASTContext &C, NamedDecl *ConstraintOwner, - ArrayRef TemplateArgs) { + llvm::FoldingSetNodeID &ID, const ASTContext &C, + const NamedDecl *ConstraintOwner, ArrayRef TemplateArgs) { ID.AddPointer(ConstraintOwner); ID.AddInteger(TemplateArgs.size()); for (auto &Arg : TemplateArgs) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 81601b09ce0d64..e5c0fa28c11f20 100755 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -167,9 +167,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, return false; } -template static bool calculateConstraintSatisfaction( - Sema &S, TemplateDeclT *Template, ArrayRef TemplateArgs, + Sema &S, const NamedDecl *Template, ArrayRef TemplateArgs, SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( @@ -182,8 +181,9 @@ static bool calculateConstraintSatisfaction( { TemplateDeductionInfo Info(TemplateNameLoc); Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(), - Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template, - Info, AtomicExpr->getSourceRange()); + Sema::InstantiatingTemplate::ConstraintSubstitution{}, + const_cast(Template), Info, + AtomicExpr->getSourceRange()); if (Inst.isInvalid()) return ExprError(); // We do not want error diagnostics escaping here. @@ -230,8 +230,7 @@ static bool calculateConstraintSatisfaction( }); } -template -static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, +static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template, ArrayRef ConstraintExprs, ArrayRef TemplateArgs, SourceRange TemplateIDRange, @@ -249,8 +248,8 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, } Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs, - TemplateIDRange); + Sema::InstantiatingTemplate::ConstraintsCheck{}, + const_cast(Template), TemplateArgs, TemplateIDRange); if (Inst.isInvalid()) return true; @@ -273,7 +272,7 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, } bool Sema::CheckConstraintSatisfaction( - NamedDecl *Template, ArrayRef ConstraintExprs, + const NamedDecl *Template, ArrayRef ConstraintExprs, ArrayRef TemplateArgs, SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (ConstraintExprs.empty()) { @@ -284,7 +283,8 @@ bool Sema::CheckConstraintSatisfaction( llvm::FoldingSetNodeID ID; void *InsertPos; ConstraintSatisfaction *Satisfaction = nullptr; - if (LangOpts.ConceptSatisfactionCaching) { + bool ShouldCache = LangOpts.ConceptSatisfactionCaching && Template; + if (ShouldCache) { ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos); if (Satisfaction) { @@ -295,27 +295,15 @@ bool Sema::CheckConstraintSatisfaction( } else { Satisfaction = &OutSatisfaction; } - bool Failed; - if (auto *T = dyn_cast(Template)) - Failed = ::CheckConstraintSatisfaction(*this, T, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction); - else if (auto *P = - dyn_cast(Template)) - Failed = ::CheckConstraintSatisfaction(*this, P, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction); - else - Failed = ::CheckConstraintSatisfaction( - *this, cast(Template), - ConstraintExprs, TemplateArgs, TemplateIDRange, *Satisfaction); - if (Failed) { - if (LangOpts.ConceptSatisfactionCaching) + if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, + TemplateArgs, TemplateIDRange, + *Satisfaction)) { + if (ShouldCache) delete Satisfaction; return true; } - if (LangOpts.ConceptSatisfactionCaching) { + if (ShouldCache) { // We cannot use InsertNode here because CheckConstraintSatisfaction might // have invalidated it. SatisfactionCache.InsertNode(Satisfaction); @@ -333,6 +321,22 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, }); } +bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, + ConstraintSatisfaction &Satisfaction, + SourceLocation UsageLoc) { + const Expr *RC = FD->getTrailingRequiresClause(); + assert(!RC->isInstantiationDependent() && + "CheckFunctionConstraints can only be used with functions with " + "non-dependent constraints"); + // We substitute with empty arguments in order to rebuild the atomic + // constraint in a constant-evaluated context. + // FIXME: Should this be a dedicated TreeTransform? + return CheckConstraintSatisfaction( + FD, {RC}, /*TemplateArgs=*/{}, + SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), + Satisfaction); +} + bool Sema::EnsureTemplateArgumentListConstraints( TemplateDecl *TD, ArrayRef TemplateArgs, SourceRange TemplateIDRange) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 67b68ffb5edae3..5b00e55238fdd0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -335,7 +335,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, if (FunctionDecl *FD = dyn_cast(D)) { if (Expr *RC = FD->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - bool Failed = CheckConstraintSatisfaction(RC, Satisfaction); + bool Failed = CheckConstraintSatisfaction(FD, {RC}, /*TemplateArgs=*/{}, + SourceRange(Loc), Satisfaction); if (Failed) // A diagnostic will have already been generated (non-constant // constraint expression, for example) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b609bd904cee29..e53281d1175537 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8497,7 +8497,8 @@ concepts::NestedRequirement * Sema::BuildNestedRequirement(Expr *Constraint) { ConstraintSatisfaction Satisfaction; if (!Constraint->isInstantiationDependent() && - CheckConstraintSatisfaction(Constraint, Satisfaction)) + CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{}, + Constraint->getSourceRange(), Satisfaction)) return nullptr; return new (Context) concepts::NestedRequirement(Context, Constraint, Satisfaction); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8bb4c5312c17aa..751869c6c9d770 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6295,9 +6295,9 @@ void Sema::AddOverloadCandidate( return; } - if (Expr *RequiresClause = Function->getTrailingRequiresClause()) { + if (Function->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Function, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -6812,9 +6812,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, return; } - if (Expr *RequiresClause = Method->getTrailingRequiresClause()) { + if (Method->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Method, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -7208,10 +7208,9 @@ void Sema::AddConversionCandidate( return; } - Expr *RequiresClause = Conversion->getTrailingRequiresClause(); - if (RequiresClause) { + if (Conversion->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) || + if (CheckFunctionConstraints(Conversion, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -9951,9 +9950,9 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD, return false; } - if (const Expr *RC = FD->getTrailingRequiresClause()) { + if (FD->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (S.CheckConstraintSatisfaction(RC, Satisfaction)) + if (S.CheckFunctionConstraints(FD, Satisfaction, Loc)) return false; if (!Satisfaction.IsSatisfied) { if (Complain) { @@ -10978,8 +10977,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, << (unsigned)FnKindPair.first << (unsigned)ocs_non_template << FnDesc /* Ignored */; ConstraintSatisfaction Satisfaction; - if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(), - Satisfaction)) + if (S.CheckFunctionConstraints(Fn, Satisfaction)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 39bc28d62305b1..26dc5d92f231ab 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -763,21 +763,30 @@ void Sema::PrintInstantiationStack() { case CodeSynthesisContext::ConstraintsCheck: { unsigned DiagID = 0; + if (!Active->Entity) { + Diags.Report(Active->PointOfInstantiation, + diag::note_nested_requirement_here) + << Active->InstantiationRange; + break; + } if (isa(Active->Entity)) DiagID = diag::note_concept_specialization_here; else if (isa(Active->Entity)) DiagID = diag::note_checking_constraints_for_template_id_here; else if (isa(Active->Entity)) DiagID = diag::note_checking_constraints_for_var_spec_id_here; - else { - assert(isa(Active->Entity)); + else if (isa(Active->Entity)) DiagID = diag::note_checking_constraints_for_class_spec_id_here; + else { + assert(isa(Active->Entity)); + DiagID = diag::note_checking_constraints_for_function_here; } SmallVector TemplateArgsStr; llvm::raw_svector_ostream OS(TemplateArgsStr); cast(Active->Entity)->printName(OS); - printTemplateArgumentList(OS, Active->template_arguments(), - getPrintingPolicy()); + if (!isa(Active->Entity)) + printTemplateArgumentList(OS, Active->template_arguments(), + getPrintingPolicy()); Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str() << Active->InstantiationRange; break; diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp index a2a7232b4b88d2..5a1c9196e657e0 100644 --- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp +++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp @@ -3,15 +3,51 @@ // Make sure constraint expressions are unevaluated before being substituted // into during satisfaction checking. -template constexpr int f() { return T::value; } -template concept Foo = false && (f(), true); -bool k = Foo; -template requires false && (f(), true) struct S {}; -// expected-note@-1{{because}} -using s = S; // expected-error {{constraints not satisfied}} -template void foo() requires false && (f(), true) { }; -// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} -int a = (foo(), 0); // expected-error{{no matching function}} -template void bar() requires requires { requires false && (f(), true); } { }; -// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} -int b = (bar(), 0); // expected-error{{no matching function}} \ No newline at end of file +template constexpr bool f = T::value; +// expected-error@-1 4{{type}} + +namespace unevaluated { + template concept Foo = false && f; + bool k = Foo; + template requires false && f struct S {}; + // expected-note@-1{{because}} + using s = S; // expected-error {{constraints not satisfied}} + template void foo() requires false && f { }; + // expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} + int a = (foo(), 0); // expected-error{{no matching function}} + template void bar() requires requires { requires false && f; } { }; + // expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} + int b = (bar(), 0); // expected-error{{no matching function}} + template struct M { static void foo() requires false && f { }; }; + // expected-note@-1{{because}} + int c = (M::foo(), 0); + // expected-error@-1{{invalid reference to function 'foo': constraints not satisfied}} +} + +namespace constant_evaluated { + template requires f struct S {}; + // expected-note@-1{{in instantiation of}} expected-note@-1{{while substituting}} \ + expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \ + expected-note@-1{{subexpression not valid}} + using s = S; + // expected-note@-1 2{{while checking}} + template void foo() requires f { }; + // expected-note@-1{{in instantiation}} expected-note@-1{{while substituting}} \ + expected-note@-1{{candidate template ignored}} expected-note@-1{{subexpression not valid}} \ + expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} + int a = (foo(), 0); + // expected-note@-1 2{{while checking}} expected-error@-1{{no matching function}} \ + expected-note@-1 2{{in instantiation}} + template void bar() requires requires { requires f; } { }; + // expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \ + expected-note@-1{{while substituting}} \ + expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \ + expected-note@-1 2{{while checking the satisfaction of nested requirement}} + int b = (bar(), 0); + template struct M { static void foo() requires f { }; }; + // expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \ + expected-note@-1{{while substituting}} \ + expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} + int c = (M::foo(), 0); + // expected-note@-1 2{{while checking}} +}