Skip to content

Commit 0717657

Browse files
[clang] Improve constexpr-unknown diagnostics. (llvm#146288)
APValue::ConstexprUnknown() constructs a broken LValue that doesn't have an lvalue path, which confuses later error handling. It turns out we don't actually use the result of createConstexprUnknownAPValues for anything, so just stop using it. Just construct the LValue directly when we need it. Make findCompleteObject emit errors more aggressively; allowing it to succeed for constexpr-unknown objects leads to weird states where it succeeds, but doesn't return a well-formed object. Delete the check for constexpr-unknown in dynamic_cast handling: it's not necessary, and breaks with the other changes in this patch. These changes allow us to produce proper diagnostics when something fails to be evaluated, instead of just printing a generic top-level error without any notes.
1 parent 18991f4 commit 0717657

File tree

5 files changed

+118
-52
lines changed

5 files changed

+118
-52
lines changed

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ def note_constexpr_access_static_temporary : Note<
240240
"outside the expression that created the temporary">;
241241
def note_constexpr_access_unreadable_object : Note<
242242
"%sub{access_kind}0 object '%1' whose value is not known">;
243+
def note_constexpr_access_unknown_variable : Note<
244+
"%sub{access_kind}0 variable %1 whose value is not known">;
243245
def note_constexpr_access_deleted_object : Note<
244246
"%sub{access_kind}0 heap allocated object that has been deleted">;
245247
def note_constexpr_modify_global : Note<

clang/lib/AST/APValue.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
821821
else if (isLValueOnePastTheEnd())
822822
Out << "*(&";
823823

824-
QualType ElemTy = Base.getType();
824+
QualType ElemTy = Base.getType().getNonReferenceType();
825825
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
826826
Out << *VD;
827827
} else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {

clang/lib/AST/ExprConstant.cpp

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ namespace {
202202
assert(!isBaseAnAllocSizeCall(Base) &&
203203
"Unsized arrays shouldn't appear here");
204204
unsigned MostDerivedLength = 0;
205-
Type = getType(Base);
205+
// The type of Base is a reference type if the base is a constexpr-unknown
206+
// variable. In that case, look through the reference type.
207+
Type = getType(Base).getNonReferenceType();
206208

207209
for (unsigned I = 0, N = Path.size(); I != N; ++I) {
208210
if (Type->isArrayType()) {
@@ -289,7 +291,7 @@ namespace {
289291
: Invalid(false), IsOnePastTheEnd(false),
290292
FirstEntryIsAnUnsizedArray(false), MostDerivedIsArrayElement(false),
291293
MostDerivedPathLength(0), MostDerivedArraySize(0),
292-
MostDerivedType(T) {}
294+
MostDerivedType(T.isNull() ? QualType() : T.getNonReferenceType()) {}
293295

294296
SubobjectDesignator(ASTContext &Ctx, const APValue &V)
295297
: Invalid(!V.isLValue() || !V.hasLValuePath()), IsOnePastTheEnd(false),
@@ -571,7 +573,6 @@ namespace {
571573
typedef std::map<MapKeyTy, APValue> MapTy;
572574
/// Temporaries - Temporary lvalues materialized within this stack frame.
573575
MapTy Temporaries;
574-
MapTy ConstexprUnknownAPValues;
575576

576577
/// CallRange - The source range of the call expression for this call.
577578
SourceRange CallRange;
@@ -646,9 +647,6 @@ namespace {
646647
APValue &createTemporary(const KeyT *Key, QualType T,
647648
ScopeKind Scope, LValue &LV);
648649

649-
APValue &createConstexprUnknownAPValues(const VarDecl *Key,
650-
APValue::LValueBase Base);
651-
652650
/// Allocate storage for a parameter of a function call made in this frame.
653651
APValue &createParam(CallRef Args, const ParmVarDecl *PVD, LValue &LV);
654652

@@ -1756,7 +1754,8 @@ namespace {
17561754
return;
17571755
}
17581756
if (checkSubobject(Info, E, CSK_ArrayToPointer)) {
1759-
assert(getType(Base)->isPointerType() || getType(Base)->isArrayType());
1757+
assert(getType(Base).getNonReferenceType()->isPointerType() ||
1758+
getType(Base).getNonReferenceType()->isArrayType());
17601759
Designator.FirstEntryIsAnUnsizedArray = true;
17611760
Designator.addUnsizedArrayUnchecked(ElemTy);
17621761
}
@@ -1955,15 +1954,6 @@ APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
19551954
return createLocal(Base, Key, T, Scope);
19561955
}
19571956

1958-
APValue &
1959-
CallStackFrame::createConstexprUnknownAPValues(const VarDecl *Key,
1960-
APValue::LValueBase Base) {
1961-
APValue &Result = ConstexprUnknownAPValues[MapKeyTy(Key, Base.getVersion())];
1962-
Result = APValue(Base, CharUnits::Zero(), APValue::ConstexprUnknown{});
1963-
1964-
return Result;
1965-
}
1966-
19671957
/// Allocate storage for a parameter of a function call made in this frame.
19681958
APValue &CallStackFrame::createParam(CallRef Args, const ParmVarDecl *PVD,
19691959
LValue &LV) {
@@ -3493,7 +3483,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
34933483
APValue::LValueBase Base(VD, Frame ? Frame->Index : 0, Version);
34943484

34953485
auto CheckUninitReference = [&](bool IsLocalVariable) {
3496-
if (!Result->hasValue() && VD->getType()->isReferenceType()) {
3486+
if (!Result || (!Result->hasValue() && VD->getType()->isReferenceType())) {
34973487
// C++23 [expr.const]p8
34983488
// ... For such an object that is not usable in constant expressions, the
34993489
// dynamic type of the object is constexpr-unknown. For such a reference
@@ -3509,7 +3499,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
35093499
Info.FFDiag(E, diag::note_constexpr_use_uninit_reference);
35103500
return false;
35113501
}
3512-
Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
3502+
Result = nullptr;
35133503
}
35143504
return true;
35153505
};
@@ -3552,7 +3542,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
35523542
// ... its lifetime began within the evaluation of E;
35533543
if (isa<ParmVarDecl>(VD)) {
35543544
if (AllowConstexprUnknown) {
3555-
Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
3545+
Result = nullptr;
35563546
return true;
35573547
}
35583548

@@ -3659,12 +3649,8 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
36593649

36603650
Result = VD->getEvaluatedValue();
36613651

3662-
if (!Result) {
3663-
if (AllowConstexprUnknown)
3664-
Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
3665-
else
3666-
return false;
3667-
}
3652+
if (!Result && !AllowConstexprUnknown)
3653+
return false;
36683654

36693655
return CheckUninitReference(/*IsLocalVariable=*/false);
36703656
}
@@ -3947,11 +3933,6 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
39473933
const FieldDecl *LastField = nullptr;
39483934
const FieldDecl *VolatileField = nullptr;
39493935

3950-
// C++23 [expr.const]p8 If we have an unknown reference or pointers and it
3951-
// does not have a value then bail out.
3952-
if (O->allowConstexprUnknown() && !O->hasValue())
3953-
return false;
3954-
39553936
// Walk the designator's path to find the subobject.
39563937
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
39573938
// Reading an indeterminate value is undefined, but assigning over one is OK.
@@ -4491,6 +4472,15 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
44914472

44924473
if (!evaluateVarDeclInit(Info, E, VD, Frame, LVal.getLValueVersion(), BaseVal))
44934474
return CompleteObject();
4475+
// If evaluateVarDeclInit sees a constexpr-unknown variable, it returns
4476+
// a null BaseVal. Any constexpr-unknown variable seen here is an error:
4477+
// we can't access a constexpr-unknown object.
4478+
if (!BaseVal) {
4479+
Info.FFDiag(E, diag::note_constexpr_access_unknown_variable, 1)
4480+
<< AK << VD;
4481+
Info.Note(VD->getLocation(), diag::note_declared_at);
4482+
return CompleteObject();
4483+
}
44944484
} else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) {
44954485
std::optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA);
44964486
if (!Alloc) {
@@ -6057,15 +6047,6 @@ struct CheckDynamicTypeHandler {
60576047
/// dynamic type.
60586048
static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
60596049
AccessKinds AK, bool Polymorphic) {
6060-
// We are not allowed to invoke a virtual function whose dynamic type
6061-
// is constexpr-unknown, so stop early and let this fail later on if we
6062-
// attempt to do so.
6063-
// C++23 [expr.const]p5.6
6064-
// an invocation of a virtual function ([class.virtual]) for an object whose
6065-
// dynamic type is constexpr-unknown;
6066-
if (This.allowConstexprUnknown())
6067-
return true;
6068-
60696050
if (This.Designator.Invalid)
60706051
return false;
60716052

@@ -6139,9 +6120,7 @@ static std::optional<DynamicType> ComputeDynamicType(EvalInfo &Info,
61396120
// meaningful dynamic type. (We consider objects of non-class type to have no
61406121
// dynamic type.)
61416122
if (!checkDynamicType(Info, E, This, AK,
6142-
(AK == AK_TypeId
6143-
? (E->getType()->isReferenceType() ? true : false)
6144-
: true)))
6123+
AK != AK_TypeId || This.AllowConstexprUnknown))
61456124
return std::nullopt;
61466125

61476126
if (This.Designator.Invalid)
@@ -9063,6 +9042,12 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
90639042
if (!evaluateVarDeclInit(Info, E, VD, Frame, Version, V))
90649043
return false;
90659044

9045+
if (!V) {
9046+
Result.set(VD);
9047+
Result.AllowConstexprUnknown = true;
9048+
return true;
9049+
}
9050+
90669051
return Success(*V, E);
90679052
}
90689053

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,7 @@ namespace InstantiateCaseStmt {
14661466

14671467
namespace ConvertedConstantExpr {
14681468
extern int &m;
1469-
extern int &n; // pre-cxx23-note 2{{declared here}}
1469+
extern int &n; // expected-note 2{{declared here}}
14701470

14711471
constexpr int k = 4;
14721472
int &m = const_cast<int&>(k);
@@ -1475,9 +1475,9 @@ namespace ConvertedConstantExpr {
14751475
// useless note and instead just point to the non-constant subexpression.
14761476
enum class E {
14771477
em = m,
1478-
en = n, // expected-error {{enumerator value is not a constant expression}} cxx11_20-note {{initializer of 'n' is unknown}}
1478+
en = n, // expected-error {{enumerator value is not a constant expression}} cxx11_20-note {{initializer of 'n' is unknown}} cxx23-note {{read of non-constexpr variable 'n'}}
14791479
eo = (m + // expected-error {{not a constant expression}}
1480-
n // cxx11_20-note {{initializer of 'n' is unknown}}
1480+
n // cxx11_20-note {{initializer of 'n' is unknown}} cxx23-note {{read of non-constexpr variable 'n'}}
14811481
),
14821482
eq = reinterpret_cast<long>((int*)0) // expected-error {{not a constant expression}} expected-note {{reinterpret_cast}}
14831483
};

clang/test/SemaCXX/constant-expression-p2280r4.cpp

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,26 @@ constexpr int how_many(Swim& swam) {
3535
return (p + 1 - 1)->phelps();
3636
}
3737

38-
void splash(Swim& swam) {
38+
void splash(Swim& swam) { // nointerpreter-note {{declared here}}
3939
static_assert(swam.phelps() == 28); // ok
4040
static_assert((&swam)->phelps() == 28); // ok
4141
Swim* pswam = &swam; // expected-note {{declared here}}
4242
static_assert(pswam->phelps() == 28); // expected-error {{static assertion expression is not an integral constant expression}} \
4343
// expected-note {{read of non-constexpr variable 'pswam' is not allowed in a constant expression}}
4444
static_assert(how_many(swam) == 28); // ok
4545
static_assert(Swim().lochte() == 12); // ok
46-
static_assert(swam.lochte() == 12); // expected-error {{static assertion expression is not an integral constant expression}}
47-
static_assert(swam.coughlin == 12); // expected-error {{static assertion expression is not an integral constant expression}}
46+
static_assert(swam.lochte() == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
47+
// nointerpreter-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
48+
static_assert(swam.coughlin == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
49+
// nointerpreter-note {{read of variable 'swam' whose value is not known}}
4850
}
4951

5052
extern Swim dc;
5153
extern Swim& trident; // interpreter-note {{declared here}}
5254

5355
constexpr auto& sandeno = typeid(dc); // ok: can only be typeid(Swim)
5456
constexpr auto& gallagher = typeid(trident); // expected-error {{constexpr variable 'gallagher' must be initialized by a constant expression}} \
57+
// nointerpreter-note {{typeid applied to object 'trident' whose dynamic type is not constant}} \
5558
// interpreter-note {{initializer of 'trident' is unknown}}
5659

5760
namespace explicitThis {
@@ -253,12 +256,88 @@ namespace uninit_reference_used {
253256

254257
namespace param_reference {
255258
constexpr int arbitrary = -12345;
256-
constexpr void f(const int &x = arbitrary) { // expected-note {{declared here}}
259+
constexpr void f(const int &x = arbitrary) { // nointerpreter-note 3 {{declared here}} interpreter-note {{declared here}}
257260
constexpr const int &v1 = x; // expected-error {{must be initialized by a constant expression}} \
258261
// expected-note {{reference to 'x' is not a constant expression}}
259262
constexpr const int &v2 = (x, arbitrary); // expected-warning {{left operand of comma operator has no effect}}
260-
constexpr int v3 = x; // expected-error {{must be initialized by a constant expression}}
261-
static_assert(x==arbitrary); // expected-error {{static assertion expression is not an integral constant expression}}
263+
constexpr int v3 = x; // expected-error {{must be initialized by a constant expression}} \
264+
// nointerpreter-note {{read of variable 'x' whose value is not known}}
265+
static_assert(x==arbitrary); // expected-error {{static assertion expression is not an integral constant expression}} \
266+
// nointerpreter-note {{read of variable 'x' whose value is not known}}
262267
static_assert(&x - &x == 0);
263268
}
264269
}
270+
271+
namespace dropped_note {
272+
extern int &x; // expected-note {{declared here}}
273+
constexpr int f() { return x; } // nointerpreter-note {{read of non-constexpr variable 'x'}} \
274+
// interpreter-note {{initializer of 'x' is unknown}}
275+
constexpr int y = f(); // expected-error {{constexpr variable 'y' must be initialized by a constant expression}} expected-note {{in call to 'f()'}}
276+
}
277+
278+
namespace dynamic {
279+
struct A {virtual ~A();};
280+
struct B : A {};
281+
void f(A& a) {
282+
constexpr B* b = dynamic_cast<B*>(&a); // expected-error {{must be initialized by a constant expression}} \
283+
// nointerpreter-note {{dynamic_cast applied to object 'a' whose dynamic type is not constant}}
284+
constexpr void* b2 = dynamic_cast<void*>(&a); // expected-error {{must be initialized by a constant expression}} \
285+
// nointerpreter-note {{dynamic_cast applied to object 'a' whose dynamic type is not constant}}
286+
}
287+
}
288+
289+
namespace unsized_array {
290+
void f(int (&a)[], int (&b)[], int (&c)[4]) {
291+
constexpr int t1 = a - a;
292+
constexpr int t2 = a - b; // expected-error {{constexpr variable 't2' must be initialized by a constant expression}} \
293+
// nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&b[0]' has unspecified value}} \
294+
// interpreter-note {{arithmetic involving unrelated objects 'a' and 'b' has unspecified value}}
295+
constexpr int t3 = a - &c[2]; // expected-error {{constexpr variable 't3' must be initialized by a constant expression}} \
296+
// nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&c[2]' has unspecified value}} \
297+
// interpreter-note {{arithmetic involving unrelated objects 'a' and '*((char*)&c + 8)' has unspecified value}}
298+
}
299+
}
300+
301+
namespace casting {
302+
struct A {};
303+
struct B : A {};
304+
struct C : A {};
305+
extern A &a; // interpreter-note {{declared here}}
306+
extern B &b; // expected-note {{declared here}} interpreter-note 2 {{declared here}}
307+
constexpr B &t1 = (B&)a; // expected-error {{must be initialized by a constant expression}} \
308+
// nointerpreter-note {{cannot cast object of dynamic type 'A' to type 'B'}} \
309+
// interpreter-note {{initializer of 'a' is unknown}}
310+
constexpr B &t2 = (B&)(A&)b; // expected-error {{must be initialized by a constant expression}} \
311+
// nointerpreter-note {{initializer of 'b' is not a constant expression}} \
312+
// interpreter-note {{initializer of 'b' is unknown}}
313+
// FIXME: interpreter incorrectly rejects.
314+
constexpr bool t3 = &b + 1 == &(B&)(A&)b; // interpreter-error {{must be initialized by a constant expression}} \
315+
// interpreter-note {{initializer of 'b' is unknown}}
316+
constexpr C &t4 = (C&)(A&)b; // expected-error {{must be initialized by a constant expression}} \
317+
// nointerpreter-note {{cannot cast object of dynamic type 'B' to type 'C'}} \
318+
// interpreter-note {{initializer of 'b' is unknown}}
319+
}
320+
321+
namespace pointer_comparisons {
322+
extern int &extern_n; // interpreter-note 2 {{declared here}}
323+
extern int &extern_n2;
324+
constexpr int f1(bool b, int& n) {
325+
if (b) {
326+
return &extern_n == &n;
327+
}
328+
return f1(true, n);
329+
}
330+
// FIXME: interpreter incorrectly rejects; both sides are the same constexpr-unknown value.
331+
static_assert(f1(false, extern_n)); // interpreter-error {{static assertion expression is not an integral constant expression}} \
332+
// interpreter-note {{initializer of 'extern_n' is unknown}}
333+
// FIXME: We should diagnose this: we don't know if the references bind
334+
// to the same object.
335+
static_assert(&extern_n != &extern_n2); // interpreter-error {{static assertion expression is not an integral constant expression}} \
336+
// interpreter-note {{initializer of 'extern_n' is unknown}}
337+
void f2(const int &n) {
338+
// FIXME: We should not diagnose this: the two objects provably have
339+
// different addresses because the lifetime of "n" extends across
340+
// the initialization.
341+
constexpr int x = &x == &n; // nointerpreter-error {{must be initialized by a constant expression}}
342+
}
343+
}

0 commit comments

Comments
 (0)