Skip to content

Commit ec17c76

Browse files
committed
Addressing feedback. Redesigning proposed solution.
1 parent 9f98e40 commit ec17c76

File tree

17 files changed

+113
-78
lines changed

17 files changed

+113
-78
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,10 @@ Bug Fixes to C++ Support
593593
- Fixed a use-after-free bug in parsing of type constraints with default arguments that involve lambdas. (#GH67235)
594594
- Fixed bug in which the body of a consteval lambda within a template was not parsed as within an
595595
immediate function context.
596-
- Fix a bug in which valid constexpr variable initializers containing immediate function calls were
597-
sometimes diagnosed as not constant expressions.
596+
- Fix a C++23 bug in implementation of P2564R3 which evaluates immediate invocations in place
597+
within initializers for variables that are usable in constant expressions or are constant
598+
initialized, rather than evaluating them as a part of the larger manifestly constant evaluated
599+
expression.
598600

599601
Bug Fixes to AST Handling
600602
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10201,7 +10201,9 @@ class Sema final : public SemaBase {
1020110201
S.ExprEvalContexts.back().InImmediateFunctionContext =
1020210202
FD->isImmediateFunction() ||
1020310203
S.ExprEvalContexts[S.ExprEvalContexts.size() - 2]
10204-
.isConstantEvaluated();
10204+
.isConstantEvaluated() ||
10205+
S.ExprEvalContexts[S.ExprEvalContexts.size() - 2]
10206+
.isImmediateFunctionContext();
1020510207
S.ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
1020610208
S.getLangOpts().CPlusPlus20 && FD->isImmediateEscalating();
1020710209
} else

clang/lib/Parse/ParseDecl.cpp

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2552,43 +2552,37 @@ Decl *Parser::ParseDeclarationAfterDeclarator(
25522552
return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo);
25532553
}
25542554

2555-
static bool isConstexprVariable(const Decl *D) {
2556-
if (const VarDecl *Var = dyn_cast_if_present<VarDecl>(D))
2557-
return Var->isConstexpr();
2558-
2559-
return false;
2560-
}
2561-
25622555
Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
25632556
Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) {
25642557
// RAII type used to track whether we're inside an initializer.
25652558
struct InitializerScopeRAII {
25662559
Parser &P;
25672560
Declarator &D;
25682561
Decl *ThisDecl;
2569-
EnterExpressionEvaluationContext EC;
2562+
bool Entered;
25702563

25712564
InitializerScopeRAII(Parser &P, Declarator &D, Decl *ThisDecl)
2572-
: P(P), D(D), ThisDecl(ThisDecl),
2573-
EC(P.Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
2574-
ThisDecl, Sema::ExpressionEvaluationContextRecord::EK_Other,
2575-
isConstexprVariable(ThisDecl)) {
2565+
: P(P), D(D), ThisDecl(ThisDecl), Entered(false) {
25762566
if (ThisDecl && P.getLangOpts().CPlusPlus) {
25772567
Scope *S = nullptr;
25782568
if (D.getCXXScopeSpec().isSet()) {
25792569
P.EnterScope(0);
25802570
S = P.getCurScope();
25812571
}
2582-
P.Actions.ActOnCXXEnterDeclInitializer(S, ThisDecl);
2572+
if (ThisDecl && !ThisDecl->isInvalidDecl()) {
2573+
P.Actions.ActOnCXXEnterDeclInitializer(S, ThisDecl);
2574+
Entered = true;
2575+
}
25832576
}
25842577
}
2585-
~InitializerScopeRAII() { pop(); }
2586-
void pop() {
2578+
~InitializerScopeRAII() {
25872579
if (ThisDecl && P.getLangOpts().CPlusPlus) {
25882580
Scope *S = nullptr;
25892581
if (D.getCXXScopeSpec().isSet())
25902582
S = P.getCurScope();
2591-
P.Actions.ActOnCXXExitDeclInitializer(S, ThisDecl);
2583+
2584+
if (Entered)
2585+
P.Actions.ActOnCXXExitDeclInitializer(S, ThisDecl);
25922586
if (S)
25932587
P.ExitScope();
25942588
}
@@ -2719,8 +2713,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
27192713
FRI->RangeExpr = Init;
27202714
}
27212715

2722-
InitScope.pop();
2723-
27242716
if (Init.isInvalid()) {
27252717
SmallVector<tok::TokenKind, 2> StopTokens;
27262718
StopTokens.push_back(tok::comma);
@@ -2768,8 +2760,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
27682760

27692761
bool SawError = ParseExpressionList(Exprs, ExpressionStarts);
27702762

2771-
InitScope.pop();
2772-
27732763
if (SawError) {
27742764
if (ThisVarDecl && PP.isCodeCompletionReached() && !CalledSignatureHelp) {
27752765
Actions.ProduceConstructorSignatureHelp(
@@ -2801,8 +2791,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
28012791
PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl);
28022792
ExprResult Init(ParseBraceInitializer());
28032793

2804-
InitScope.pop();
2805-
28062794
if (Init.isInvalid()) {
28072795
Actions.ActOnInitializerError(ThisDecl);
28082796
} else

clang/lib/Sema/SemaChecking.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16548,8 +16548,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1654816548
std::string PrettySourceValue = toString(Value, 10);
1654916549
std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
1655016550

16551-
S.DiagRuntimeBehavior(
16552-
E->getExprLoc(), E,
16551+
S.Diag(
16552+
E->getExprLoc(),
1655316553
S.PDiag(diag::warn_impcast_integer_precision_constant)
1655416554
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
1655516555
<< E->getSourceRange() << SourceRange(CC));

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18545,15 +18545,6 @@ void Sema::ActOnPureSpecifier(Decl *D, SourceLocation ZeroLoc) {
1854518545
Diag(D->getLocation(), diag::err_illegal_initializer);
1854618546
}
1854718547

18548-
/// Determine whether the given declaration is a global variable or
18549-
/// static data member.
18550-
static bool isNonlocalVariable(const Decl *D) {
18551-
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(D))
18552-
return Var->hasGlobalStorage();
18553-
18554-
return false;
18555-
}
18556-
1855718548
/// Invoked when we are about to parse an initializer for the declaration
1855818549
/// 'Dcl'.
1855918550
///
@@ -18562,9 +18553,7 @@ static bool isNonlocalVariable(const Decl *D) {
1856218553
/// class X. If the declaration had a scope specifier, a scope will have
1856318554
/// been created and passed in for this purpose. Otherwise, S will be null.
1856418555
void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) {
18565-
// If there is no declaration, there was an error parsing it.
18566-
if (!D || D->isInvalidDecl())
18567-
return;
18556+
assert(D && !D->isInvalidDecl());
1856818557

1856918558
// We will always have a nested name specifier here, but this declaration
1857018559
// might not be out of line if the specifier names the current namespace:
@@ -18573,25 +18562,35 @@ void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) {
1857318562
if (S && D->isOutOfLine())
1857418563
EnterDeclaratorContext(S, D->getDeclContext());
1857518564

18576-
// If we are parsing the initializer for a static data member, push a
18577-
// new expression evaluation context that is associated with this static
18578-
// data member.
18579-
if (isNonlocalVariable(D))
18580-
PushExpressionEvaluationContext(
18581-
ExpressionEvaluationContext::PotentiallyEvaluated, D);
18565+
PushExpressionEvaluationContext(
18566+
ExpressionEvaluationContext::PotentiallyEvaluated, D);
1858218567
}
1858318568

1858418569
/// Invoked after we are finished parsing an initializer for the declaration D.
1858518570
void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) {
18586-
// If there is no declaration, there was an error parsing it.
18587-
if (!D || D->isInvalidDecl())
18588-
return;
18589-
18590-
if (isNonlocalVariable(D))
18591-
PopExpressionEvaluationContext();
18571+
assert(D);
1859218572

1859318573
if (S && D->isOutOfLine())
1859418574
ExitDeclaratorContext(S);
18575+
18576+
if (getLangOpts().CPlusPlus23) {
18577+
// An expression or conversion is 'manifestly constant-evaluated' if it is:
18578+
// [...]
18579+
// - the initializer of a variable that is usable in constant expressions or
18580+
// has constant initialization.
18581+
if (auto *VD = dyn_cast<VarDecl>(D);
18582+
VD && (VD->isUsableInConstantExpressions(Context) ||
18583+
VD->hasConstantInitialization())) {
18584+
// An expression or conversion is in an 'immediate function context' if it
18585+
// is potentially evaluated and either:
18586+
// [...]
18587+
// - it is a subexpression of a manifestly constant-evaluated expression or
18588+
// conversion.
18589+
ExprEvalContexts.back().InImmediateFunctionContext = true;
18590+
}
18591+
}
18592+
18593+
PopExpressionEvaluationContext();
1859518594
}
1859618595

1859718596
/// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a

clang/lib/Sema/SemaExpr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18011,6 +18011,7 @@ HandleImmediateInvocations(Sema &SemaRef,
1801118011
Sema::ExpressionEvaluationContextRecord &Rec) {
1801218012
if ((Rec.ImmediateInvocationCandidates.size() == 0 &&
1801318013
Rec.ReferenceToConsteval.size() == 0) ||
18014+
Rec.isImmediateFunctionContext() ||
1801418015
SemaRef.RebuildingImmediateInvocation)
1801518016
return;
1801618017

clang/test/AST/Interp/intap.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ using MaxBitInt = _BitInt(128);
99

1010
constexpr _BitInt(2) A = 0;
1111
constexpr _BitInt(2) B = A + 1;
12-
constexpr _BitInt(2) C = B + 1;
12+
constexpr _BitInt(2) C = B + 1; // expected-warning {{from 2 to -2}} \
13+
// ref-warning {{from 2 to -2}}
1314
static_assert(C == -2, "");
1415
static_assert(C - B == A, ""); // expected-error {{not an integral constant expression}} \
1516
// expected-note {{value -3 is outside the range of representable values}} \

clang/test/CXX/expr/expr.const/p2-0x.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ namespace UndefinedBehavior {
244244
constexpr int n13 = n5 + n5; // expected-error {{constant expression}} expected-note {{value -4294967296 is outside the range of }}
245245
constexpr int n14 = n3 - n5; // expected-error {{constant expression}} expected-note {{value 4294967295 is outside the range of }}
246246
constexpr int n15 = n5 * n5; // expected-error {{constant expression}} expected-note {{value 4611686018427387904 is outside the range of }}
247-
constexpr signed char c1 = 100 * 2; // ok - no error from changing value because initializer is constexpr.
248-
constexpr signed char c2 = '\x64' * '\2'; // also ok - no error from changing value because initializer is constexpr.
247+
constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}}
248+
constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning{{changes value}}
249249
constexpr long long ll1 = 0x7fffffffffffffff; // ok
250250
constexpr long long ll2 = ll1 + 1; // expected-error {{constant}} expected-note {{ 9223372036854775808 }}
251251
constexpr long long ll3 = -ll1 - 1; // ok

clang/test/CXX/expr/expr.const/p6-2a.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ struct Temporary {
4343
constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}}
4444

4545
namespace P1073R3 {
46-
consteval int f() { return 42; } // expected-note {{declared here}}
46+
consteval int f() { return 42; } // expected-note 2 {{declared here}}
4747
consteval auto g() { return f; }
4848
consteval int h(int (*p)() = g()) { return p(); }
4949
constexpr int r = h();
50-
constexpr auto e = g(); // expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \
51-
expected-note {{pointer to a consteval declaration is not a constant expression}}
50+
constexpr auto e = g(); // expected-error {{call to consteval function 'P1073R3::g' is not a constant expression}} \
51+
expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \
52+
expected-note 2 {{pointer to a consteval declaration is not a constant expression}}
5253
static_assert(r == 42);
5354
} // namespace P1073R3

clang/test/Parser/pragma-fenv_access.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ int main(void) {
3232
CONST int not_too_big = 255;
3333
CONST float fnot_too_big = not_too_big;
3434
CONST int too_big = 0x7ffffff0;
35+
#if defined(CPP)
36+
//expected-warning@+2{{implicit conversion}}
37+
#endif
3538
CONST float fbig = too_big; // inexact
3639
#if !defined(CPP)
3740
#define static_assert _Static_assert

0 commit comments

Comments
 (0)