8000 [clang] Fix crash when handling nested immediate invocations · inside-compiler/llvm-project@cf85b3e · GitHub
[go: up one dir, main page]

Skip to content

Commit cf85b3e

Browse files
Fznamznontstellar
authored andcommitted
[clang] Fix crash when handling nested immediate invocations
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 llvm#58207 Reviewed By: aaron.ballman, shafik Differential Revision: https://reviews.llvm.org/D146234 (cherry picked from commit a5e1a93)
1 parent e32cbe9 commit cf85b3e

File tree

4 files changed

+62
-8
lines changed

4 files changed

+62
-8
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,9 @@ Bug Fixes in This Version
718718
- Fix crash when attempting to perform parenthesized initialization of an
719719
aggregate with a base class with only non-public constructors.
720720
(`#62296 <https://github.com/llvm/llvm-project/issues/62296>`_)
721+
- Fix crash when handling nested immediate invocations in initializers of global
722+
variables.
723+
(`#58207 <https://github.com/llvm/llvm-project/issues/58207>`_)
721724

722725
Bug Fixes to Compiler Builtins
723726
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,9 @@ class Sema final {
13941394
/// A stack of expression evaluation contexts.
13951395
SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
13961396

1397+
// Set of failed immediate invocations to avoid double diagnosing.
1398+
llvm::SmallPtrSet<ConstantExpr *, 4> FailedImmediateInvocations;
1399+
13971400
/// Emit a warning for all pending noderef expressions that we recorded.
13981401
void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec);
13991402

clang/lib/Sema/SemaExpr.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17805,6 +17805,7 @@ static void EvaluateAndDiagnoseImmediateInvocation(
1780517805
bool Result = CE->EvaluateAsConstantExpr(
1780617806
Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation);
1780717807
if (!Result || !Notes.empty()) {
17808+
SemaRef.FailedImmediateInvocations.insert(CE);
1780817809
Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit();
1780917810
if (auto *FunctionalCast = dyn_cast<CXXFunctionalCastExpr>(InnerExpr))
1781017811
InnerExpr = FunctionalCast->getSubExpr();
@@ -17849,10 +17850,16 @@ static void RemoveNestedImmediateInvocation(
1784917850
[E](Sema::ImmediateInvocationCandidate Elem) {
1785017851
return Elem.getPointer() == E;
1785117852
});
17852-
assert(It != IISet.rend() &&
17853-
"ConstantExpr marked IsImmediateInvocation should "
17854-
"be present");
17855-
It->setInt(1); // Mark as deleted
17853+
// It is possible that some subexpression of the current immediate
17854+
// invocation was handled from another expression evaluation context. Do
17855+
// not handle the current immediate invocation if some of its
17856+
// subexpressions failed before.
17857+
if (It == IISet.rend()) {
17858+
if (SemaRef.FailedImmediateInvocations.contains(E))
17859+
CurrentII->setInt(1);
17860+
} else {
17861+
It->setInt(1); // Mark as deleted
17862+
}
1785617863
}
1785717864
ExprResult TransformConstantExpr(ConstantExpr *E) {
1785817865
if (!E->isImmediateInvocation())
@@ -17925,10 +17932,13 @@ HandleImmediateInvocations(Sema &SemaRef,
1792517932
SemaRef.RebuildingImmediateInvocation)
1792617933
return;
1792717934

17928-
/// When we have more then 1 ImmediateInvocationCandidates we need to check
17929-
/// for nested ImmediateInvocationCandidates. when we have only 1 we only
17930-
/// need to remove ReferenceToConsteval in the immediate invocation.
17931-
if (Rec.ImmediateInvocationCandidates.size() > 1) {
17935+
/// When we have more than 1 ImmediateInvocationCandidates or previously
17936+
/// failed immediate invocations, we need to check for nested
17937+
/// ImmediateInvocationCandidates in order to avoid duplicate diagnostics.
17938+
/// Otherwise we only need to remove ReferenceToConsteval in the immediate
17939+
/// invocation.
17940+
if (Rec.ImmediateInvocationCandidates.size() > 1 ||
17941+
!SemaRef.FailedImmediateInvocations.empty()) {
1793217942

1793317943
/// Prevent sema calls during the tree transform from adding pointers that
1793417944
/// are already in the sets.

clang/test/SemaCXX/cxx2a-consteval.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,3 +1029,41 @@ int f() {
10291029
int x = A{};
10301030
}
10311031
}
1032+
1033+
namespace GH58207 {
1034+
struct tester {
1035+
consteval tester(const char* name) noexcept { }
1036+
};
1037+
consteval const char* make_name(const char* name) { return name;}
1038+
consteval const char* pad(int P) { return "thestring"; }
1039+
1040+
int bad = 10; // expected-note 6{{declared here}}
1041+
1042+
tester glob1(make_name("glob1"));
1043+
tester glob2(make_name("glob2"));
1044+
constexpr tester cglob(make_name("cglob"));
1045+
tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
1046+
// expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}}
1047+
1048+
constexpr tester glob3 = { make_name("glob3") };
1049+
constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
1050+
// expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \
1051+
// expected-note 2{{read of non-const variable 'bad' is not allowed in a constant expression}}
1052+
1053+
auto V = make_name(pad(3));
1054+
auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
1055+
// expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}}
1056+
1057+
1058+
void foo() {
1059+
static tester loc1(make_name("loc1"));
1060+
static constexpr tester loc2(make_name("loc2"));
1061+
static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
1062+
// expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}}
1063+
}
1064+
1065+
void bar() {
1066+
static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
1067+
// expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}}
1068+
}
1069+
}

0 commit comments

Comments
 (0)
0