Skip to content

Commit 4266756

Browse files
author
Erich Keane
committed
Fix recursive error for constraints depending on itself incorrectly
Fixes: #60323 #60323 The problem is that we are profiling the 'Expr' components directly, however when they contain an unresolved lookup, those canonicalize identically. The result was the two versions of calls to 'go' were canonicalized identically. This patch fixes this by ensuring we consider the declaration the constraint is attached to, when possible. When not, we skip the diagnostic. The result is that we are relaxing our diagnostic in some cases (Of which I couldn't come up with a reproducer), such that we might see overflows when evaluating constraints that depend on themselves in a way that they are not attached to a declaration directly, such as if they are nested requirements, though the hope is this won't be a problem, since the 'parent' named constraint would catch this. I'm hopeful that the 'worst case' is that we catch recursion 'later' in the process, instead of immediately.
1 parent 9f307a0 commit 4266756

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7279,24 +7279,34 @@ class Sema final {
72797279

72807280
private:
72817281
// The current stack of constraint satisfactions, so we can exit-early.
7282-
llvm::SmallVector<llvm::FoldingSetNodeID, 10> SatisfactionStack;
7282+
using SatisfactionStackEntryTy =
7283+
std::pair<const NamedDecl *, llvm::FoldingSetNodeID>;
7284+
llvm::SmallVector<SatisfactionStackEntryTy, 10>
7285+
SatisfactionStack;
72837286

72847287
public:
7285-
void PushSatisfactionStackEntry(const llvm::FoldingSetNodeID &ID) {
7286-
SatisfactionStack.push_back(ID);
7288+
void PushSatisfactionStackEntry(const NamedDecl *D,
7289+
const llvm::FoldingSetNodeID &ID) {
7290+
const NamedDecl *Can = cast<NamedDecl>(D->getCanonicalDecl());
7291+
SatisfactionStack.emplace_back(Can, ID);
72877292
}
72887293

72897294
void PopSatisfactionStackEntry() { SatisfactionStack.pop_back(); }
72907295

7291-
bool SatisfactionStackContains(const llvm::FoldingSetNodeID &ID) const {
7292-
return llvm::find(SatisfactionStack, ID) != SatisfactionStack.end();
7296+
bool SatisfactionStackContains(const NamedDecl *D,
7297+
const llvm::FoldingSetNodeID &ID) const {
7298+
const NamedDecl *Can = cast<NamedDecl>(D->getCanonicalDecl());
7299+
return llvm::find(SatisfactionStack,
7300+
SatisfactionStackEntryTy{Can, ID}) !=
7301+
SatisfactionStack.end();
72937302
}
72947303

72957304
// Resets the current SatisfactionStack for cases where we are instantiating
72967305
// constraints as a 'side effect' of normal instantiation in a way that is not
72977306
// indicative of recursive definition.
72987307
class SatisfactionStackResetRAII {
7299-
llvm::SmallVector<llvm::FoldingSetNodeID, 10> BackupSatisfactionStack;
7308+
llvm::SmallVector<SatisfactionStackEntryTy, 10>
7309+
BackupSatisfactionStack;
73007310
Sema &SemaRef;
73017311

73027312
public:
@@ -7309,8 +7319,8 @@ class Sema final {
73097319
}
73107320
};
73117321

7312-
void
7313-
SwapSatisfactionStack(llvm::SmallVectorImpl<llvm::FoldingSetNodeID> &NewSS) {
7322+
void SwapSatisfactionStack(
7323+
llvm::SmallVectorImpl<SatisfactionStackEntryTy> &NewSS) {
73147324
SatisfactionStack.swap(NewSS);
73157325
}
73167326

clang/lib/Sema/SemaConcept.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,19 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
150150
namespace {
151151
struct SatisfactionStackRAII {
152152
Sema &SemaRef;
153-
SatisfactionStackRAII(Sema &SemaRef, llvm::FoldingSetNodeID FSNID)
153+
bool Inserted = false;
154+
SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND,
155+
llvm::FoldingSetNodeID FSNID)
154156
: SemaRef(SemaRef) {
155-
SemaRef.PushSatisfactionStackEntry(FSNID);
157+
if (ND) {
158+
SemaRef.PushSatisfactionStackEntry(ND, FSNID);
159+
Inserted = true;
160+
}
161+
}
162+
~SatisfactionStackRAII() {
163+
if (Inserted)
164+
SemaRef.PopSatisfactionStackEntry();
156165
}
157-
~SatisfactionStackRAII() { SemaRef.PopSatisfactionStackEntry(); }
158166
};
159167
} // namespace
160168

@@ -273,7 +281,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
273281
}
274282

275283
static bool
276-
DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, const Expr *E,
284+
DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
285+
const NamedDecl *Templ, const Expr *E,
277286
const MultiLevelTemplateArgumentList &MLTAL) {
278287
E->Profile(ID, S.Context, /*Canonical=*/true);
279288
for (const auto &List : MLTAL)
@@ -286,7 +295,7 @@ DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, const Expr *E,
286295
// expression, or when trying to determine the constexpr-ness of special
287296
// members. Otherwise we could just use the
288297
// Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function.
289-
if (S.SatisfactionStackContains(ID)) {
298+
if (S.SatisfactionStackContains(Templ, ID)) {
290299
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
291300
<< const_cast<Expr *>(E) << E->getSourceRange();
292301
return true;
@@ -317,13 +326,14 @@ static ExprResult calculateConstraintSatisfaction(
317326
return ExprError();
318327

319328
llvm::FoldingSetNodeID ID;
320-
if (DiagRecursiveConstraintEval(S, ID, AtomicExpr, MLTAL)) {
329+
if (Template &&
330+
DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
321331
Satisfaction.IsSatisfied = false;
322332
Satisfaction.ContainsErrors = true;
323333
return ExprEmpty();
324334
}
325335

326-
SatisfactionStackRAII StackRAII(S, ID);
336+
SatisfactionStackRAII StackRAII(S, Template, ID);
327337

328338
// We do not want error diagnostics escaping here.
329339
Sema::SFINAETrap Trap(S);

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,33 @@ namespace GH50891 {
113113

114114
} // namespace GH50891
115115

116+
117+
namespace GH60323 {
118+
// This should not diagnose, as it does not depend on itself.
119+
struct End {
120+
template<class T>
121+
void go(T t) { }
122+
123+
template<class T>
124+
auto endparens(T t)
125+
requires requires { go(t); }
126+
{ return go(t); }
127+
};
128+
129+
struct Size {
130+
template<class T>
131+
auto go(T t)
132+
{ return End().endparens(t); }
133+
134+
template<class T>
135+
auto sizeparens(T t)
136+
requires requires { go(t); }
137+
{ return go(t); }
138+
};
139+
140+
int f()
141+
{
142+
int i = 42;
143+
Size().sizeparens(i);
144+
}
145+
}

0 commit comments

Comments
 (0)