Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5205,6 +5205,8 @@ def err_template_recursion_depth_exceeded : Error< "recursive template instantiation exceeded maximum depth of %0">, DefaultFatal, NoSFINAE; +def err_constraint_depends_on_self : Error< + "satisfaction of constraints on %0 depends on itself">, NoSFINAE; def note_template_recursion_depth : Note< "use -ftemplate-depth=N to increase recursive template instantiation depth">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7219,7 +7219,28 @@ FunctionDecl *FD, llvm::Optional> TemplateArgs, LocalInstantiationScope &Scope); +private: + // The current stack of constraint satisfactions, so we can exit-early. + llvm::SmallVector + SatisfactionStack; public: + void PushSatisfactionStackEntry(const llvm::FoldingSetNodeID &ID) { + SatisfactionStack.push_back(ID); + } + + void PopSatisfactionStackEntry() { + SatisfactionStack.pop_back(); + } + + bool SatisfactionStackContains(const llvm::FoldingSetNodeID &ID) { + return llvm::find(SatisfactionStack, ID) != SatisfactionStack.end(); + } + + void + SwapSatisfactionStack(llvm::SmallVectorImpl &NewSS) { + SatisfactionStack.swap(NewSS); + } + const NormalizedConstraint * getNormalizedAssociatedConstraints( NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -146,6 +146,18 @@ return true; } +namespace { + struct SatisfactionStackRAII { + Sema &SemaRef; + SatisfactionStackRAII(Sema &SemaRef, llvm::FoldingSetNodeID FSNID) :SemaRef(SemaRef) { + SemaRef.PushSatisfactionStackEntry(FSNID); + } + ~SatisfactionStackRAII() { + SemaRef.PopSatisfactionStackEntry(); + } + }; +} + template static ExprResult calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, @@ -279,6 +291,31 @@ AtomicExpr->getSourceRange()); if (Inst.isInvalid()) return ExprError(); + +// TODO: ERICH: Cleanup +llvm::FoldingSetNodeID ID; +AtomicExpr->Profile(ID, S.Context, /*Canonical=*/true); +for (const auto &List : MLTAL) + for (const auto &TemplateArg : List.Args) + TemplateArg.Profile(ID, S.Context); + +if (S.SatisfactionStackContains(ID)) { + // TODO: ERICH: Figure out how to better print the constraint? + // TODO: ERICH: make sure we add tests from + // : recurse_crash.cpp + // : https://github.com/llvm/llvm-project/issues/44304 + // : https://github.com/llvm/llvm-project/issues/50891 + // : https://reviews.llvm.org/D133052 + S.Diag(AtomicExpr->getExprLoc(), diag::err_constraint_depends_on_self) + << "The Bad thing" << AtomicExpr->getSourceRange(); + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = true; + return ExprError(); +} + +SatisfactionStackRAII StackRAII(S, ID); + + // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); SubstitutedExpression = Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -13136,14 +13136,19 @@ namespace { class BuildRecoveryCallExprRAII { Sema &SemaRef; + llvm::SmallVector SatisfactionStack; public: BuildRecoveryCallExprRAII(Sema &S) : SemaRef(S) { assert(SemaRef.IsBuildingRecoveryCallExpr == false); SemaRef.IsBuildingRecoveryCallExpr = true; + // Ensure we clear the satisfaction stack here, so that we don't fail + // incorrectly for recursive lookups. + SemaRef.SwapSatisfactionStack(SatisfactionStack); } ~BuildRecoveryCallExprRAII() { SemaRef.IsBuildingRecoveryCallExpr = false; + SemaRef.SwapSatisfactionStack(SatisfactionStack); } }; Index: clang/test/SemaTemplate/concepts-GH53213.cpp =================================================================== --- clang/test/SemaTemplate/concepts-GH53213.cpp +++ clang/test/SemaTemplate/concepts-GH53213.cpp @@ -1,18 +1,18 @@ // RUN: %clang_cc1 -std=c++20 -verify %s -namespace GH53213 { -template -concept c = requires(T t) { f(t); }; // #CDEF - -auto f(c auto); // #FDEF - -void g() { - f(0); - // expected-error@-1{{no matching function for call to 'f'}} - // expected-note@#FDEF{{constraints not satisfied}} - // expected-note@#FDEF{{because 'int' does not satisfy 'c'}} - // expected-note@#CDEF{{because 'f(t)' would be invalid: no matching function for call to 'f'}} -} -} // namespace GH53213 +//namespace GH53213 { +//template +//concept c = requires(T t) { f(t); }; // #CDEF +// +//auto f(c auto); // #FDEF +// +//void g() { +// f(0); +// // expected-error@-1{{no matching function for call to 'f'}} +// // expected-note@#FDEF{{constraints not satisfied}} +// // expected-note@#FDEF{{because 'int' does not satisfy 'c'}} +// // expected-note@#CDEF{{because 'f(t)' would be invalid: no matching function for call to 'f'}} +//} +//} // namespace GH53213 namespace GH45736 { struct constrained;