Skip to content

Commit c21e178

Browse files
committed
[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. (cherry picked from commit 713562f)
1 parent 0df1362 commit c21e178

File tree

10 files changed

+124
-62
lines changed

10 files changed

+124
-62
lines changed

clang/include/clang/AST/ASTConcept.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ class ConceptSpecializationExpr;
2929
class ConstraintSatisfaction : public llvm::FoldingSetNode {
3030
// The template-like entity that 'owns' the constraint checked here (can be a
3131
// constrained entity or a concept).
32-
NamedDecl *ConstraintOwner = nullptr;
32+
const NamedDecl *ConstraintOwner = nullptr;
3333
llvm::SmallVector<TemplateArgument, 4> TemplateArgs;
3434

3535
public:
3636

3737
ConstraintSatisfaction() = default;
3838

39-
ConstraintSatisfaction(NamedDecl *ConstraintOwner,
39+
ConstraintSatisfaction(const NamedDecl *ConstraintOwner,
4040
ArrayRef<TemplateArgument> TemplateArgs) :
4141
ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(),
4242
TemplateArgs.end()) { }
@@ -57,7 +57,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
5757
}
5858

5959
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C,
60-
NamedDecl *ConstraintOwner,
60+
const NamedDecl *ConstraintOwner,
6161
ArrayRef<TemplateArgument> TemplateArgs);
6262
};
6363

clang/include/clang/Basic/DiagnosticSemaKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -4683,6 +4683,8 @@ def note_checking_constraints_for_var_spec_id_here : Note<
46834683
def note_checking_constraints_for_class_spec_id_here : Note<
46844684
"while checking constraint satisfaction for class template partial "
46854685
"specialization '%0' required here">;
4686+
def note_checking_constraints_for_function_here : Note<
4687+
"while checking constraint satisfaction for function '%0' required here">;
46864688
def note_constraint_substitution_here : Note<
46874689
"while substituting template arguments into constraint expression here">;
46884690
def note_constraint_normalization_here : Note<

clang/include/clang/Sema/Sema.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -6275,7 +6275,7 @@ class Sema final {
62756275
/// \returns true if an error occurred and satisfaction could not be checked,
62766276
/// false otherwise.
62776277
bool CheckConstraintSatisfaction(
6278-
NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
6278+
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
62796279
ArrayRef<TemplateArgument> TemplateArgs,
62806280
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
62816281

@@ -6288,6 +6288,17 @@ class Sema final {
62886288
bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
62896289
ConstraintSatisfaction &Satisfaction);
62906290

6291+
/// Check whether the given function decl's trailing requires clause is
6292+
/// satisfied, if any. Returns false and updates Satisfaction with the
6293+
/// satisfaction verdict if successful, emits a diagnostic and returns true if
6294+
/// an error occured and satisfaction could not be determined.
6295+
///
6296+
/// \returns true if an error occurred, false otherwise.
6297+
bool CheckFunctionConstraints(const FunctionDecl *FD,
6298+
ConstraintSatisfaction &Satisfaction,
6299+
SourceLocation UsageLoc = SourceLocation());
6300+
6301+
62916302
/// \brief Ensure that the given template arguments satisfy the constraints
62926303
/// associated with the given template, emitting a diagnostic if they do not.
62936304
///

clang/lib/AST/ASTConcept.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ ASTConstraintSatisfaction::Create(const ASTContext &C,
5959
}
6060

6161
void ConstraintSatisfaction::Profile(
62-
llvm::FoldingSetNodeID &ID, const ASTContext &C, NamedDecl *ConstraintOwner,
63-
ArrayRef<TemplateArgument> TemplateArgs) {
62+
llvm::FoldingSetNodeID &ID, const ASTContext &C,
63+
const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
6464
ID.AddPointer(ConstraintOwner);
6565
ID.AddInteger(TemplateArgs.size());
6666
for (auto &Arg : TemplateArgs)

clang/lib/Sema/SemaConcept.cpp

+31-27
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
167167
return false;
168168
}
169169

170-
template <typename TemplateDeclT>
171170
static bool calculateConstraintSatisfaction(
172-
Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,
171+
Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
173172
SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
174173
const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
175174
return calculateConstraintSatisfaction(
@@ -182,8 +181,9 @@ static bool calculateConstraintSatisfaction(
182181
{
183182
TemplateDeductionInfo Info(TemplateNameLoc);
184183
Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
185-
Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,
186-
Info, AtomicExpr->getSourceRange());
184+
Sema::InstantiatingTemplate::ConstraintSubstitution{},
185+
const_cast<NamedDecl *>(Template), Info,
186+
AtomicExpr->getSourceRange());
187187
if (Inst.isInvalid())
188188
return ExprError();
189189
// We do not want error diagnostics escaping here.
@@ -230,8 +230,7 @@ static bool calculateConstraintSatisfaction(
230230
});
231231
}
232232

233-
template<typename TemplateDeclT>
234-
static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
233+
static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template,
235234
ArrayRef<const Expr *> ConstraintExprs,
236235
ArrayRef<TemplateArgument> TemplateArgs,
237236
SourceRange TemplateIDRange,
@@ -249,8 +248,8 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
249248
}
250249

251250
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
252-
Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
253-
TemplateIDRange);
251+
Sema::InstantiatingTemplate::ConstraintsCheck{},
252+
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
254253
if (Inst.isInvalid())
255254
return true;
256255

@@ -273,7 +272,7 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
273272
}
274273

275274
bool Sema::CheckConstraintSatisfaction(
276-
NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
275+
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
277276
ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange,
278277
ConstraintSatisfaction &OutSatisfaction) {
279278
if (ConstraintExprs.empty()) {
@@ -284,7 +283,8 @@ bool Sema::CheckConstraintSatisfaction(
284283
llvm::FoldingSetNodeID ID;
285284
void *InsertPos;
286285
ConstraintSatisfaction *Satisfaction = nullptr;
287-
if (LangOpts.ConceptSatisfactionCaching) {
286+
bool ShouldCache = LangOpts.ConceptSatisfactionCaching && Template;
287+
if (ShouldCache) {
288288
ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs);
289289
Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos);
290290
if (Satisfaction) {
@@ -295,27 +295,15 @@ bool Sema::CheckConstraintSatisfaction(
295295
} else {
296296
Satisfaction = &OutSatisfaction;
297297
}
298-
bool Failed;
299-
if (auto *T = dyn_cast<TemplateDecl>(Template))
300-
Failed = ::CheckConstraintSatisfaction(*this, T, ConstraintExprs,
301-
TemplateArgs, TemplateIDRange,
302-
*Satisfaction);
303-
else if (auto *P =
304-
dyn_cast<ClassTemplatePartialSpecializationDecl>(Template))
305-
Failed = ::CheckConstraintSatisfaction(*this, P, ConstraintExprs,
306-
TemplateArgs, TemplateIDRange,
307-
*Satisfaction);
308-
else
309-
Failed = ::CheckConstraintSatisfaction(
310-
*this, cast<VarTemplatePartialSpecializationDecl>(Template),
311-
ConstraintExprs, TemplateArgs, TemplateIDRange, *Satisfaction);
312-
if (Failed) {
313-
if (LangOpts.ConceptSatisfactionCaching)
298+
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
299+
TemplateArgs, TemplateIDRange,
300+
*Satisfaction)) {
301+
if (ShouldCache)
314302
delete Satisfaction;
315303
return true;
316304
}
317305

318-
if (LangOpts.ConceptSatisfactionCaching) {
306+
if (ShouldCache) {
319307
// We cannot use InsertNode here because CheckConstraintSatisfaction might
320308
// have invalidated it.
321309
SatisfactionCache.InsertNode(Satisfaction);
@@ -333,6 +321,22 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
333321
});
334322
}
335323

324+
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
325+
ConstraintSatisfaction &Satisfaction,
326+
SourceLocation UsageLoc) {
327+
const Expr *RC = FD->getTrailingRequiresClause();
328+
assert(!RC->isInstantiationDependent() &&
329+
"CheckFunctionConstraints can only be used with functions with "
330+
"non-dependent constraints");
331+
// We substitute with empty arguments in order to rebuild the atomic
332+
// constraint in a constant-evaluated context.
333+
// FIXME: Should this be a dedicated TreeTransform?
334+
return CheckConstraintSatisfaction(
335+
FD, {RC}, /*TemplateArgs=*/{},
336+
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
337+
Satisfaction);
338+
}
339+
336340
bool Sema::EnsureTemplateArgumentListConstraints(
337341
TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
338342
SourceRange TemplateIDRange) {

clang/lib/Sema/SemaExpr.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
335335
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
336336
if (Expr *RC = FD->getTrailingRequiresClause()) {
337337
ConstraintSatisfaction Satisfaction;
338-
bool Failed = CheckConstraintSatisfaction(RC, Satisfaction);
338+
bool Failed = CheckConstraintSatisfaction(FD, {RC}, /*TemplateArgs=*/{},
339+
SourceRange(Loc), Satisfaction);
339340
if (Failed)
340341
// A diagnostic will have already been generated (non-constant
341342
// constraint expression, for example)

clang/lib/Sema/SemaExprCXX.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -8487,7 +8487,8 @@ concepts::NestedRequirement *
84878487
Sema::BuildNestedRequirement(Expr *Constraint) {
84888488
ConstraintSatisfaction Satisfaction;
84898489
if (!Constraint->isInstantiationDependent() &&
8490-
CheckConstraintSatisfaction(Constraint, Satisfaction))
8490+
CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{},
8491+
Constraint->getSourceRange(), Satisfaction))
84918492
return nullptr;
84928493
return new (Context) concepts::NestedRequirement(Context, Constraint,
84938494
Satisfaction);

clang/lib/Sema/SemaOverload.cpp

+9-11
Original file line numberDiff line numberDiff line change
@@ -6291,9 +6291,9 @@ void Sema::AddOverloadCandidate(
62916291
return;
62926292
}
62936293

6294-
if (Expr *RequiresClause = Function->getTrailingRequiresClause()) {
6294+
if (Function->getTrailingRequiresClause()) {
62956295
ConstraintSatisfaction Satisfaction;
6296-
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
6296+
if (CheckFunctionConstraints(Function, Satisfaction) ||
62976297
!Satisfaction.IsSatisfied) {
62986298
Candidate.Viable = false;
62996299
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
@@ -6808,9 +6808,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
68086808
return;
68096809
}
68106810

6811-
if (Expr *RequiresClause = Method->getTrailingRequiresClause()) {
6811+
if (Method->getTrailingRequiresClause()) {
68126812
ConstraintSatisfaction Satisfaction;
6813-
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
6813+
if (CheckFunctionConstraints(Method, Satisfaction) ||
68146814
!Satisfaction.IsSatisfied) {
68156815
Candidate.Viable = false;
68166816
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
@@ -7204,10 +7204,9 @@ void Sema::AddConversionCandidate(
72047204
return;
72057205
}
72067206

7207-
Expr *RequiresClause = Conversion->getTrailingRequiresClause();
7208-
if (RequiresClause) {
7207+
if (Conversion->getTrailingRequiresClause()) {
72097208
ConstraintSatisfaction Satisfaction;
7210-
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
7209+
if (CheckFunctionConstraints(Conversion, Satisfaction) ||
72117210
!Satisfaction.IsSatisfied) {
72127211
Candidate.Viable = false;
72137212
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
@@ -9947,9 +9946,9 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD,
99479946
return false;
99489947
}
99499948

9950-
if (const Expr *RC = FD->getTrailingRequiresClause()) {
9949+
if (FD->getTrailingRequiresClause()) {
99519950
ConstraintSatisfaction Satisfaction;
9952-
if (S.CheckConstraintSatisfaction(RC, Satisfaction))
9951+
if (S.CheckFunctionConstraints(FD, Satisfaction, Loc))
99539952
return false;
99549953
if (!Satisfaction.IsSatisfied) {
99559954
if (Complain) {
@@ -10974,8 +10973,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
1097410973
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
1097510974
<< FnDesc /* Ignored */;
1097610975
ConstraintSatisfaction Satisfaction;
10977-
if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(),
10978-
Satisfaction))
10976+
if (S.CheckFunctionConstraints(Fn, Satisfaction))
1097910977
break;
1098010978
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
1098110979
}

clang/lib/Sema/SemaTemplateInstantiate.cpp

+13-4
Original file line numberDiff line numberDiff line change
@@ -763,21 +763,30 @@ void Sema::PrintInstantiationStack() {
763763

764764
case CodeSynthesisContext::ConstraintsCheck: {
765765
unsigned DiagID = 0;
766+
if (!Active->Entity) {
767+
Diags.Report(Active->PointOfInstantiation,
768+
diag::note_nested_requirement_here)
769+
<< Active->InstantiationRange;
770+
break;
771+
}
766772
if (isa<ConceptDecl>(Active->Entity))
767773
DiagID = diag::note_concept_specialization_here;
768774
else if (isa<TemplateDecl>(Active->Entity))
769775
DiagID = diag::note_checking_constraints_for_template_id_here;
770776
else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity))
771777
DiagID = diag::note_checking_constraints_for_var_spec_id_here;
772-
else {
773-
assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity));
778+
else if (isa<ClassTemplatePartialSpecializationDecl>(Active->Entity))
774779
DiagID = diag::note_checking_constraints_for_class_spec_id_here;
780+
else {
781+
assert(isa<FunctionDecl>(Active->Entity));
782+
DiagID = diag::note_checking_constraints_for_function_here;
775783
}
776784
SmallVector<char, 128> TemplateArgsStr;
777785
llvm::raw_svector_ostream OS(TemplateArgsStr);
778786
cast<NamedDecl>(Active->Entity)->printName(OS);
779-
printTemplateArgumentList(OS, Active->template_arguments(),
780-
getPrintingPolicy());
787+
if (!isa<FunctionDecl>(Active->Entity))
788+
printTemplateArgumentList(OS, Active->template_arguments(),
789+
getPrintingPolicy());
781790
Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str()
782791
<< Active->InstantiationRange;
783792
break;

clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp

+48-12
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,51 @@
33
// Make sure constraint expressions are unevaluated before being substituted
44
// into during satisfaction checking.
55

6-
template<typename T> constexpr int f() { return T::value; }
7-
template<typename T> concept Foo = false && (f<int>(), true);
8-
bool k = Foo<int>;
9-
template<typename T> requires false && (f<int>(), true) struct S {};
10-
// expected-note@-1{{because}}
11-
using s = S<int>; // expected-error {{constraints not satisfied}}
12-
template<typename T> void foo() requires false && (f<int>(), true) { };
13-
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
14-
int a = (foo<int>(), 0); // expected-error{{no matching function}}
15-
template<typename T> void bar() requires requires { requires false && (f<int>(), true); } { };
16-
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
17-
int b = (bar<int>(), 0); // expected-error{{no matching function}}
6+
template<typename T> constexpr bool f = T::value;
7+
// expected-error@-1 4{{type}}
8+
9+
namespace unevaluated {
10+
template<typename T> concept Foo = false && f<int>;
11+
bool k = Foo<int>;
12+
template<typename T> requires false && f<int> struct S {};
13+
// expected-note@-1{{because}}
14+
using s = S<int>; // expected-error {{constraints not satisfied}}
15+
template<typename T> void foo() requires false && f<int> { };
16+
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
17+
int a = (foo<int>(), 0); // expected-error{{no matching function}}
18+
template<typename T> void bar() requires requires { requires false && f<int>; } { };
19+
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
20+
int b = (bar<int>(), 0); // expected-error{{no matching function}}
21+
template<typename T> struct M { static void foo() requires false && f<int> { }; };
22+
// expected-note@-1{{because}}
23+
int c = (M<int>::foo(), 0);
24+
// expected-error@-1{{invalid reference to function 'foo': constraints not satisfied}}
25+
}
26+
27+
namespace constant_evaluated {
28+
template<typename T> requires f<int[0]> struct S {};
29+
// expected-note@-1{{in instantiation of}} expected-note@-1{{while substituting}} \
30+
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \
31+
expected-note@-1{{subexpression not valid}}
32+
using s = S<int>;
33+
// expected-note@-1 2{{while checking}}
34+
template<typename T> void foo() requires f<int[1]> { };
35+
// expected-note@-1{{in instantiation}} expected-note@-1{{while substituting}} \
36+
expected-note@-1{{candidate template ignored}} expected-note@-1{{subexpression not valid}} \
37+
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
38+
int a = (foo<int>(), 0);
39+
// expected-note@-1 2{{while checking}} expected-error@-1{{no matching function}} \
40+
expected-note@-1 2{{in instantiation}}
41+
template<typename T> void bar() requires requires { requires f<int[2]>; } { };
42+
// expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \
43+
expected-note@-1{{while substituting}} \
44+
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \
45+
expected-note@-1 2{{while checking the satisfaction of nested requirement}}
46+
int b = (bar<int>(), 0);
47+
template<typename T> struct M { static void foo() requires f<int[3]> { }; };
48+
// expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \
49+
expected-note@-1{{while substituting}} \
50+
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
51+
int c = (M<int>::foo(), 0);
52+
// expected-note@-1 2{{while checking}}
53+
}

0 commit comments

Comments
 (0)