diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1392,6 +1392,9 @@ /// A stack of expression evaluation contexts. SmallVector ExprEvalContexts; + // Set of failed immediate invocations to avoid double diagnosing. + llvm::SmallPtrSet FailedImmediateInvocations; + /// Emit a warning for all pending noderef expressions that we recorded. void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17843,6 +17843,7 @@ bool Result = CE->EvaluateAsConstantExpr( Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); if (!Result || !Notes.empty()) { + SemaRef.FailedImmediateInvocations.insert(CE); Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); if (auto *FunctionalCast = dyn_cast(InnerExpr)) InnerExpr = FunctionalCast->getSubExpr(); @@ -17887,10 +17888,16 @@ [E](Sema::ImmediateInvocationCandidate Elem) { return Elem.getPointer() == E; }); - assert(It != IISet.rend() && - "ConstantExpr marked IsImmediateInvocation should " - "be present"); - It->setInt(1); // Mark as deleted + // It is possible that some subexpression of current immediate invocation + // was handled from another expression evaluation context. Do not handle + // current immediate invocation if some of its subexpressions failed + // before. + if (It == IISet.rend()) { + if (SemaRef.FailedImmediateInvocations.contains(E)) + CurrentII->setInt(1); + } else { + It->setInt(1); // Mark as deleted + } } ExprResult TransformConstantExpr(ConstantExpr *E) { if (!E->isImmediateInvocation()) @@ -17963,10 +17970,13 @@ SemaRef.RebuildingImmediateInvocation) return; - /// When we have more then 1 ImmediateInvocationCandidates we need to check - /// for nested ImmediateInvocationCandidates. when we have only 1 we only - /// need to remove ReferenceToConsteval in the immediate invocation. - if (Rec.ImmediateInvocationCandidates.size() > 1) { + /// When we have more then 1 ImmediateInvocationCandidates or previously + /// failed immediate invocations, we need to check for nested + /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. + /// Otherwise we only need to remove ReferenceToConsteval in the immediate + /// invocation. + if (Rec.ImmediateInvocationCandidates.size() > 1 || + SemaRef.FailedImmediateInvocations.size()) { /// Prevent sema calls during the tree transform from adding pointers that /// are already in the sets. diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -1050,3 +1050,41 @@ } } + +namespace GH58207 { +struct tester { + consteval tester(const char* name) noexcept { } +}; +consteval const char* make_name(const char* name) { return name;} +consteval const char* pad(int P) { return "thestring"; } + +int bad = 10; // expected-note 6{{declared here}} + +tester glob1(make_name("glob1")); +tester glob2(make_name("glob2")); +constexpr tester cglob(make_name("cglob")); +tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} + +constexpr tester glob3 = { make_name("glob3") }; +constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ + // expected-note 2{{read of non-const variable 'bad' is not allowed in a constant expression}} + +auto V = make_name(pad(3)); +auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} + + +void foo() { + static tester loc1(make_name("loc1")); + static constexpr tester loc2(make_name("loc2")); + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} +} + +void bar() { + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} +} +}