Skip to content

Commit 68ca79f

Browse files
cor3ntinaokblast
authored andcommitted
[Clang] Avoid building unnecessary expressions when checking satisfaction (llvm#164611)
When establishing constraint satisfaction, we were building expressions even for compound constraint, This is unnecessary extra work that accounts for ~20% of the performance regression observed here llvm#161671 (comment)
1 parent 48ca60e commit 68ca79f

File tree

4 files changed

+34
-43
lines changed

4 files changed

+34
-43
lines changed

clang/lib/AST/ASTConcept.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID,
8686
ID.AddPointer(ConstraintOwner);
8787
ID.AddInteger(TemplateArgs.size());
8888
for (auto &Arg : TemplateArgs)
89-
C.getCanonicalTemplateArgument(Arg).Profile(ID, C);
89+
Arg.Profile(ID, C);
9090
}
9191

9292
ConceptReference *

clang/lib/Sema/SemaConcept.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ class ConstraintSatisfactionChecker {
417417
const NamedDecl *Template;
418418
SourceLocation TemplateNameLoc;
419419
UnsignedOrNone PackSubstitutionIndex;
420-
421420
ConstraintSatisfaction &Satisfaction;
421+
bool BuildExpression;
422422

423423
private:
424424
ExprResult
@@ -461,10 +461,11 @@ class ConstraintSatisfactionChecker {
461461
ConstraintSatisfactionChecker(Sema &SemaRef, const NamedDecl *Template,
462462
SourceLocation TemplateNameLoc,
463463
UnsignedOrNone PackSubstitutionIndex,
464-
ConstraintSatisfaction &Satisfaction)
464+
ConstraintSatisfaction &Satisfaction,
465+
bool BuildExpression)
465466
: S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc),
466467
PackSubstitutionIndex(PackSubstitutionIndex),
467-
Satisfaction(Satisfaction) {}
468+
Satisfaction(Satisfaction), BuildExpression(BuildExpression) {}
468469

469470
ExprResult Evaluate(const NormalizedConstraint &Constraint,
470471
const MultiLevelTemplateArgumentList &MLTAL);
@@ -821,9 +822,10 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
821822
Satisfaction.ContainsErrors = false;
822823
ExprResult Expr =
823824
ConstraintSatisfactionChecker(S, Template, TemplateNameLoc,
824-
UnsignedOrNone(I), Satisfaction)
825+
UnsignedOrNone(I), Satisfaction,
826+
/*BuildExpression=*/false)
825827
.Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs);
826-
if (Expr.isUsable()) {
828+
if (BuildExpression && Expr.isUsable()) {
827829
if (Out.isUnset())
828830
Out = Expr;
829831
else
@@ -834,7 +836,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
834836
Constraint.getBeginLoc(),
835837
FPOptionsOverride{});
836838
} else {
837-
assert(!Satisfaction.IsSatisfied);
839+
assert(!BuildExpression || !Satisfaction.IsSatisfied);
838840
}
839841
if (!Conjunction && Satisfaction.IsSatisfied) {
840842
Satisfaction.Details.erase(Satisfaction.Details.begin() +
@@ -985,7 +987,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
985987

986988
ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
987989

988-
if (!E.isUsable()) {
990+
if (E.isInvalid()) {
989991
Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, ConceptId);
990992
return E;
991993
}
@@ -1041,7 +1043,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
10411043
if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
10421044
return LHS;
10431045

1044-
if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
1046+
if (!Conjunction && !LHS.isInvalid() && Satisfaction.IsSatisfied &&
10451047
!Satisfaction.ContainsErrors)
10461048
return LHS;
10471049

@@ -1050,12 +1052,15 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
10501052

10511053
ExprResult RHS = Evaluate(Constraint.getRHS(), MLTAL);
10521054

1053-
if (RHS.isUsable() && Satisfaction.IsSatisfied &&
1055+
if (!Conjunction && !RHS.isInvalid() && Satisfaction.IsSatisfied &&
10541056
!Satisfaction.ContainsErrors)
10551057
Satisfaction.Details.erase(Satisfaction.Details.begin() +
10561058
EffectiveDetailEndIndex,
10571059
Satisfaction.Details.end());
10581060

1061+
if (!BuildExpression)
1062+
return Satisfaction.ContainsErrors ? ExprError() : ExprEmpty();
1063+
10591064
if (!LHS.isUsable())
10601065
return RHS;
10611066

@@ -1136,10 +1141,11 @@ static bool CheckConstraintSatisfaction(
11361141
Template, /*CSE=*/nullptr,
11371142
S.ArgPackSubstIndex);
11381143

1139-
ExprResult Res =
1140-
ConstraintSatisfactionChecker(S, Template, TemplateIDRange.getBegin(),
1141-
S.ArgPackSubstIndex, Satisfaction)
1142-
.Evaluate(*C, TemplateArgsLists);
1144+
ExprResult Res = ConstraintSatisfactionChecker(
1145+
S, Template, TemplateIDRange.getBegin(),
1146+
S.ArgPackSubstIndex, Satisfaction,
1147+
/*BuildExpression=*/ConvertedExpr != nullptr)
1148+
.Evaluate(*C, TemplateArgsLists);
11431149

11441150
if (Res.isInvalid())
11451151
return true;

clang/test/SemaCXX/cxx2c-fold-exprs.cpp

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -157,66 +157,55 @@ static_assert(And1<S, S>() == 1);
157157
// FIXME: The diagnostics are not so great
158158
static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}}
159159
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int>]}}
160-
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
161-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
160+
// expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
162161

163162
static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}}
164163
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <S, int>]}}
165-
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
166-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
164+
// expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
167165

168166
static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}}
169167
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int, S>]}}
170-
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
171-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
168+
// expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
172169

173170
static_assert(And2<S>() == 2);
174171
static_assert(And2<S, S>() == 2);
175172
static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}}
176173
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
177-
// expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
178-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
174+
// expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
179175

180176

181177
static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
182178
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} \
183-
// expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
184-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
179+
// expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
185180

186181
static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
187182
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
188-
// expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
189-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
183+
// expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
190184

191185
static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}}
192186
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
193-
// expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
194-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
187+
// expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
195188

196189
static_assert(And3<S>() == 3);
197190
static_assert(And3<S, S>() == 3);
198191
static_assert(And3<int>() == 3); // expected-error {{no matching function for call to 'And3'}}
199192
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
200-
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
201-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
193+
// expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
202194

203195

204196
static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
205197
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
206-
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
207-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
198+
// expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
208199

209200

210201
static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
211202
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}}
212-
// expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}}
213-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
203+
// expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
214204

215205

216206
static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}}
217207
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
218-
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
219-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
208+
// expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
220209

221210

222211
static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}}
@@ -227,25 +216,22 @@ static_assert(Or1<S, int>() == 1);
227216
static_assert(Or1<S, S>() == 1);
228217
static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}}
229218
// expected-note@#or1 {{candidate template ignored: constraints not satisfied}}
230-
// expected-note@#or1 {{because 'typename T::type' does not satisfy 'C'}}
231-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
219+
// expected-note@#or1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
232220

233221
static_assert(Or2<S>() == 2);
234222
static_assert(Or2<int, S>() == 2);
235223
static_assert(Or2<S, int>() == 2);
236224
static_assert(Or2<S, S>() == 2);
237225
static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}}
238226
// expected-note@#or2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
239-
// expected-note@#or2 {{because 'typename T::type' does not satisfy 'C'}}
240-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
227+
// expected-note@#or2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
241228
static_assert(Or3<S>() == 3);
242229
static_assert(Or3<int, S>() == 3);
243230
static_assert(Or3<S, int>() == 3);
244231
static_assert(Or3<S, S>() == 3);
245232
static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}}
246233
// expected-note@#or3 {{candidate template ignored: constraints not satisfied}}
247-
// expected-note@#or3 {{because 'typename T::type' does not satisfy 'C'}}
248-
// expected-note@#C {{because 'T' does not satisfy 'A'}}
234+
// expected-note@#or3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
249235
}
250236

251237
namespace bool_conversion_break {

clang/test/SemaTemplate/concepts-recursive-inst.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ auto it = begin(rng); // #BEGIN_CALL
8282
// expected-error@#BEGIN_CALL {{no matching function for call to 'begin'}}
8383
// expected-note@#NOTINF_BEGIN {{candidate function}}
8484
// expected-note@#INF_BEGIN{{candidate template ignored: constraints not satisfied}}
85-
// expected-note@#INF_BEGIN{{because 'Inf auto' does not satisfy 'Inf}}
8685
}
8786
} // namespace DirectRecursiveCheck
8887

0 commit comments

Comments
 (0)