diff options
| -rw-r--r-- | clang/include/clang/AST/ASTConcept.h | 6 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 13 | ||||
| -rw-r--r-- | clang/lib/AST/ASTConcept.cpp | 4 | ||||
| -rwxr-xr-x | clang/lib/Sema/SemaConcept.cpp | 58 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 20 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 17 | ||||
| -rw-r--r-- | clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp | 60 | 
10 files changed, 124 insertions, 62 deletions
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 30c4706d2a1..3ebaad4eafd 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 @@ public:    }    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 7636d04a34c..b7b8c5f17c4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4683,6 +4683,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 a88dd281448..72d49c119e8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6275,7 +6275,7 @@ public:    /// \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); @@ -6288,6 +6288,17 @@ public:    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 c28a06bdf0b..549088ad4a8 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 81601b09ce0..e5c0fa28c11 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 ea4b93ee6a5..4f777e7b981 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 192c237b6c1..98af7fb73ec 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8487,7 +8487,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 0fd932fac97..9f4996b5463 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6291,9 +6291,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; @@ -6808,9 +6808,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; @@ -7204,10 +7204,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; @@ -9947,9 +9946,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) { @@ -10974,8 +10973,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 39bc28d6230..26dc5d92f23 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 a2a7232b4b8..5a1c9196e65 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}} +}  | 

