Author: Saar Raz Date: 2020-01-25T23:00:24+02:00 New Revision: 713562f54858f10bf8998ee21ff2c7e7bad0d177
URL: https://github.com/llvm/llvm-project/commit/713562f54858f10bf8998ee21ff2c7e7bad0d177 DIFF: https://github.com/llvm/llvm-project/commit/713562f54858f10bf8998ee21ff2c7e7bad0d177.diff LOG: [Concepts] Transform constraints of non-template functions to ConstantEvaluated We would previously try to evaluate atomic constraints of non-template functions as-is, and since they are now unevaluated at first, this would cause incorrect evaluation (bugs #44657, #44656). Substitute into atomic constraints of non-template functions as we would atomic constraints of template functions, in order to rebuild the expressions in a constant-evaluated context. Added: Modified: clang/include/clang/AST/ASTConcept.h clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/ASTConcept.cpp clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 30c4706d2a15..3ebaad4eafdd 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<TemplateArgument, 4> TemplateArgs; public: ConstraintSatisfaction() = default; - ConstraintSatisfaction(NamedDecl *ConstraintOwner, + ConstraintSatisfaction(const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> 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<TemplateArgument> TemplateArgs); }; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b49c222c8fb7..5e88c6ee86ab 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 5ab74e4cd662..64a6793aa411 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<const Expr *> ConstraintExprs, + const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, ArrayRef<TemplateArgument> 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 c28a06bdf0b2..549088ad4a8a 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<TemplateArgument> TemplateArgs) { + llvm::FoldingSetNodeID &ID, const ASTContext &C, + const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> 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 81601b09ce0d..e5c0fa28c11f 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 <typename TemplateDeclT> static bool calculateConstraintSatisfaction( - Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs, + Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> 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<NamedDecl *>(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<typename TemplateDeclT> -static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, +static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, ArrayRef<TemplateArgument> 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<NamedDecl *>(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<const Expr *> ConstraintExprs, + const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, ArrayRef<TemplateArgument> 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<TemplateDecl>(Template)) - Failed = ::CheckConstraintSatisfaction(*this, T, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction); - else if (auto *P = - dyn_cast<ClassTemplatePartialSpecializationDecl>(Template)) - Failed = ::CheckConstraintSatisfaction(*this, P, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction); - else - Failed = ::CheckConstraintSatisfaction( - *this, cast<VarTemplatePartialSpecializationDecl>(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<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 67b68ffb5eda..5b00e55238fd 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -335,7 +335,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, if (FunctionDecl *FD = dyn_cast<FunctionDecl>(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 b609bd904cee..e53281d11755 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 8bb4c5312c17..751869c6c9d7 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 39bc28d62305..26dc5d92f231 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<ConceptDecl>(Active->Entity)) DiagID = diag::note_concept_specialization_here; else if (isa<TemplateDecl>(Active->Entity)) DiagID = diag::note_checking_constraints_for_template_id_here; else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity)) DiagID = diag::note_checking_constraints_for_var_spec_id_here; - else { - assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity)); + else if (isa<ClassTemplatePartialSpecializationDecl>(Active->Entity)) DiagID = diag::note_checking_constraints_for_class_spec_id_here; + else { + assert(isa<FunctionDecl>(Active->Entity)); + DiagID = diag::note_checking_constraints_for_function_here; } SmallVector<char, 128> TemplateArgsStr; llvm::raw_svector_ostream OS(TemplateArgsStr); cast<NamedDecl>(Active->Entity)->printName(OS); - printTemplateArgumentList(OS, Active->template_arguments(), - getPrintingPolicy()); + if (!isa<FunctionDecl>(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 a2a7232b4b88..5a1c9196e657 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<typename T> constexpr int f() { return T::value; } -template<typename T> concept Foo = false && (f<int>(), true); -bool k = Foo<int>; -template<typename T> requires false && (f<int>(), true) struct S {}; -// expected-note@-1{{because}} -using s = S<int>; // expected-error {{constraints not satisfied}} -template<typename T> void foo() requires false && (f<int>(), true) { }; -// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} -int a = (foo<int>(), 0); // expected-error{{no matching function}} -template<typename T> void bar() requires requires { requires false && (f<int>(), true); } { }; -// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} -int b = (bar<int>(), 0); // expected-error{{no matching function}} \ No newline at end of file +template<typename T> constexpr bool f = T::value; +// expected-error@-1 4{{type}} + +namespace unevaluated { + template<typename T> concept Foo = false && f<int>; + bool k = Foo<int>; + template<typename T> requires false && f<int> struct S {}; + // expected-note@-1{{because}} + using s = S<int>; // expected-error {{constraints not satisfied}} + template<typename T> void foo() requires false && f<int> { }; + // expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} + int a = (foo<int>(), 0); // expected-error{{no matching function}} + template<typename T> void bar() requires requires { requires false && f<int>; } { }; + // expected-note@-1{{because}} expected-note@-1{{candidate template ignored}} + int b = (bar<int>(), 0); // expected-error{{no matching function}} + template<typename T> struct M { static void foo() requires false && f<int> { }; }; + // expected-note@-1{{because}} + int c = (M<int>::foo(), 0); + // expected-error@-1{{invalid reference to function 'foo': constraints not satisfied}} +} + +namespace constant_evaluated { + template<typename T> requires f<int[0]> 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<int>; + // expected-note@-1 2{{while checking}} + template<typename T> void foo() requires f<int[1]> { }; + // 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<int>(), 0); + // expected-note@-1 2{{while checking}} expected-error@-1{{no matching function}} \ + expected-note@-1 2{{in instantiation}} + template<typename T> void bar() requires requires { requires f<int[2]>; } { }; + // 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<int>(), 0); + template<typename T> struct M { static void foo() requires f<int[3]> { }; }; + // 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<int>::foo(), 0); + // expected-note@-1 2{{while checking}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits