Fznamznon created this revision. Herald added a project: All. Fznamznon requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Before this patch it was expected that if there was several immediate invocations they all belong to the same expression evaluation context. During parsing of non local variable initializer a new evaluation context is pushed, so code like this namespace scope { struct channel { consteval channel(const char* name) noexcept { } }; consteval const char* make_channel_name(const char* name) { return name;} channel rsx_log(make_channel_name("rsx_log")); } produced a nested immediate invocation whose subexpressions are attached to different expression evaluation contexts. The constructor call belongs to TU context and `make_channel_name` call to context of variable initializer. This patch removes this assumption and adds tracking of previously failed immediate invocations, so it is possible when handling an immediate invocation th check that its subexpressions from possibly another evaluation context contains errors and not produce duplicate diagnostics. Fixes https://github.com/llvm/llvm-project/issues/58207 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D146234 Files: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaExpr.cpp clang/test/SemaCXX/cxx2a-consteval.cpp
Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ 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}} +} +} Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ 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<CXXFunctionalCastExpr>(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. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1392,6 +1392,9 @@ /// A stack of expression evaluation contexts. SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts; + // Set of failed immediate invocations to avoid double diagnosing. + llvm::SmallPtrSet<ConstantExpr*, 4> FailedImmediateInvocations; + /// Emit a warning for all pending noderef expressions that we recorded. void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits