Skip to content

Commit 7583d32

Browse files
committed
[Clang] Implement P2280R4 Using unknown pointers and references in constant expressions
P2280R4 allows the use of references in pointers of unknown origins in a constant expression context but only in specific cases that could be constant expressions. We track whether a variable is a constexpr unknown in a constant expression by setting a flag in either APValue or LValue and using this flag to prevent using unknown values in places where it is not allowed. Fixes: #63139 #63117
1 parent d4a0154 commit 7583d32

File tree

6 files changed

+183
-35
lines changed

6 files changed

+183
-35
lines changed

clang/include/clang/AST/APValue.h

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,15 @@ class APValue {
249249
struct NoLValuePath {};
250250
struct UninitArray {};
251251
struct UninitStruct {};
252+
struct ConstexprUnknown {};
252253

253254
template <typename Impl> friend class clang::serialization::BasicReaderBase;
254255
friend class ASTImporter;
255256
friend class ASTNodeImporter;
256257

257258
private:
258259
ValueKind Kind;
260+
bool AllowConstexprUnknown = false;
259261

260262
struct ComplexAPSInt {
261263
APSInt Real, Imag;
@@ -314,53 +316,69 @@ class APValue {
314316
DataType Data;
315317

316318
public:
317-
APValue() : Kind(None) {}
318-
explicit APValue(APSInt I) : Kind(None) {
319+
bool allowConstexprUnknown() const { return AllowConstexprUnknown; }
320+
321+
void setConstexprUnknown() { AllowConstexprUnknown = true; }
322+
323+
APValue() : Kind(None), AllowConstexprUnknown(false) {}
324+
explicit APValue(APSInt I) : Kind(None), AllowConstexprUnknown(false) {
319325
MakeInt(); setInt(std::move(I));
320326
}
321-
explicit APValue(APFloat F) : Kind(None) {
327+
explicit APValue(APFloat F) : Kind(None), AllowConstexprUnknown(false) {
322328
MakeFloat(); setFloat(std::move(F));
323329
}
324-
explicit APValue(APFixedPoint FX) : Kind(None) {
330+
explicit APValue(APFixedPoint FX) : Kind(None), AllowConstexprUnknown(false) {
325331
MakeFixedPoint(std::move(FX));
326332
}
327-
explicit APValue(const APValue *E, unsigned N) : Kind(None) {
333+
explicit APValue(const APValue *E, unsigned N)
334+
: Kind(None), AllowConstexprUnknown(false) {
328335
MakeVector(); setVector(E, N);
329336
}
330-
APValue(APSInt R, APSInt I) : Kind(None) {
337+
APValue(APSInt R, APSInt I) : Kind(None), AllowConstexprUnknown(false) {
331338
MakeComplexInt(); setComplexInt(std::move(R), std::move(I));
332339
}
333-
APValue(APFloat R, APFloat I) : Kind(None) {
340+
APValue(APFloat R, APFloat I) : Kind(None), AllowConstexprUnknown(false) {
334341
MakeComplexFloat(); setComplexFloat(std::move(R), std::move(I));
335342
}
336343
APValue(const APValue &RHS);
337344
APValue(APValue &&RHS);
338345
APValue(LValueBase B, const CharUnits &O, NoLValuePath N,
339346
bool IsNullPtr = false)
340-
: Kind(None) {
347+
: Kind(None), AllowConstexprUnknown(false) {
341348
MakeLValue(); setLValue(B, O, N, IsNullPtr);
342349
}
343350
APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
344351
bool OnePastTheEnd, bool IsNullPtr = false)
345-
: Kind(None) {
352+
: Kind(None), AllowConstexprUnknown(false) {
346353
MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
347354
}
348-
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) {
355+
356+
APValue(LValueBase B, ConstexprUnknown, const CharUnits &O,
357+
bool IsNullPtr = false)
358+
: Kind(None), AllowConstexprUnknown(true) {
359+
MakeLValue();
360+
setLValue(B, O, NoLValuePath{}, IsNullPtr);
361+
}
362+
363+
APValue(UninitArray, unsigned InitElts, unsigned Size)
364+
: Kind(None), AllowConstexprUnknown(false) {
349365
MakeArray(InitElts, Size);
350366
}
351-
APValue(UninitStruct, unsigned B, unsigned M) : Kind(None) {
367+
APValue(UninitStruct, unsigned B, unsigned M)
368+
: Kind(None), AllowConstexprUnknown(false) {
352369
MakeStruct(B, M);
353370
}
354371
explicit APValue(const FieldDecl *D, const APValue &V = APValue())
355-
: Kind(None) {
372+
: Kind(None), AllowConstexprUnknown(false) {
356373
MakeUnion(); setUnion(D, V);
357374
}
358375
APValue(const ValueDecl *Member, bool IsDerivedMember,
359-
ArrayRef<const CXXRecordDecl*> Path) : Kind(None) {
376+
ArrayRef<const CXXRecordDecl *> Path)
377+
: Kind(None), AllowConstexprUnknown(false) {
360378
MakeMemberPointer(Member, IsDerivedMember, Path);
361379
}
362-
APValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr)
363-
: Kind(None) {
380+
APValue(const AddrLabelExpr *LHSExpr, const AddrLabelExpr *RHSExpr)
381+
: Kind(None), AllowConstexprUnknown(false) {
364382
MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr);
365383
}
366384
static APValue IndeterminateValue() {

clang/lib/AST/APValue.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ APValue::UnionData::~UnionData () {
308308
delete Value;
309309
}
310310

311-
APValue::APValue(const APValue &RHS) : Kind(None) {
311+
APValue::APValue(const APValue &RHS)
312+
: Kind(None), AllowConstexprUnknown(RHS.AllowConstexprUnknown) {
312313
switch (RHS.getKind()) {
313314
case None:
314315
case Indeterminate:
@@ -379,13 +380,17 @@ APValue::APValue(const APValue &RHS) : Kind(None) {
379380
}
380381
}
381382

382-
APValue::APValue(APValue &&RHS) : Kind(RHS.Kind), Data(RHS.Data) {
383+
APValue::APValue(APValue &&RHS)
384+
: Kind(RHS.Kind), AllowConstexprUnknown(RHS.AllowConstexprUnknown),
385+
Data(RHS.Data) {
383386
RHS.Kind = None;
384387
}
385388

386389
APValue &APValue::operator=(const APValue &RHS) {
387390
if (this != &RHS)
388391
*this = APValue(RHS);
392+
393+
AllowConstexprUnknown = RHS.AllowConstexprUnknown;
389394
return *this;
390395
}
391396

@@ -395,6 +400,7 @@ APValue &APValue::operator=(APValue &&RHS) {
395400
DestroyDataAndMakeUninit();
396401
Kind = RHS.Kind;
397402
Data = RHS.Data;
403+
AllowConstexprUnknown = RHS.AllowConstexprUnknown;
398404
RHS.Kind = None;
399405
}
400406
return *this;
@@ -426,6 +432,7 @@ void APValue::DestroyDataAndMakeUninit() {
426432
else if (Kind == AddrLabelDiff)
427433
((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData();
428434
Kind = None;
435+
AllowConstexprUnknown = false;
429436
}
430437

431438
bool APValue::needsCleanup() const {
@@ -468,6 +475,7 @@ bool APValue::needsCleanup() const {
468475
void APValue::swap(APValue &RHS) {
469476
std::swap(Kind, RHS.Kind);
470477
std::swap(Data, RHS.Data);
478+
std::swap(AllowConstexprUnknown, RHS.AllowConstexprUnknown);
471479
}
472480

473481
/// Profile the value of an APInt, excluding its bit-width.

clang/lib/AST/ExprConstant.cpp

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,8 +1609,11 @@ namespace {
16091609
SubobjectDesignator Designator;
16101610
bool IsNullPtr : 1;
16111611
bool InvalidBase : 1;
1612+
// P2280R4 track if we have an unknown reference or pointer.
1613+
bool AllowConstexprUnknown = false;
16121614

16131615
const APValue::LValueBase getLValueBase() const { return Base; }
1616+
bool allowConstexprUnknown() const { return AllowConstexprUnknown; }
16141617
CharUnits &getLValueOffset() { return Offset; }
16151618
const CharUnits &getLValueOffset() const { return Offset; }
16161619
SubobjectDesignator &getLValueDesignator() { return Designator; }
@@ -1628,6 +1631,8 @@ namespace {
16281631
V = APValue(Base, Offset, Designator.Entries,
16291632
Designator.IsOnePastTheEnd, IsNullPtr);
16301633
}
1634+
if (AllowConstexprUnknown)
1635+
V.setConstexprUnknown();
16311636
}
16321637
void setFrom(ASTContext &Ctx, const APValue &V) {
16331638
assert(V.isLValue() && "Setting LValue from a non-LValue?");
@@ -1636,6 +1641,7 @@ namespace {
16361641
InvalidBase = false;
16371642
Designator = SubobjectDesignator(Ctx, V);
16381643
IsNullPtr = V.isNullPointer();
1644+
AllowConstexprUnknown = V.allowConstexprUnknown();
16391645
}
16401646

16411647
void set(APValue::LValueBase B, bool BInvalid = false) {
@@ -1653,6 +1659,7 @@ namespace {
16531659
InvalidBase = BInvalid;
16541660
Designator = SubobjectDesignator(getType(B));
16551661
IsNullPtr = false;
1662+
AllowConstexprUnknown = false;
16561663
}
16571664

16581665
void setNull(ASTContext &Ctx, QualType PointerTy) {
@@ -1662,6 +1669,7 @@ namespace {
16621669
InvalidBase = false;
16631670
Designator = SubobjectDesignator(PointerTy->getPointeeType());
16641671
IsNullPtr = true;
1672+
AllowConstexprUnknown = false;
16651673
}
16661674

16671675
void setInvalid(APValue::LValueBase B, unsigned I = 0) {
@@ -3300,6 +3308,11 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
33003308
static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
33013309
const VarDecl *VD, CallStackFrame *Frame,
33023310
unsigned Version, APValue *&Result) {
3311+
// P2280R4 If we have a reference type and we are in C++23 allow unknown
3312+
// references and pointers.
3313+
bool AllowConstexprUnknown =
3314+
Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
3315+
33033316
APValue::LValueBase Base(VD, Frame ? Frame->Index : 0, Version);
33043317

33053318
// If this is a local variable, dig out its value.
@@ -3334,7 +3347,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
33343347
return true;
33353348
}
33363349

3337-
if (isa<ParmVarDecl>(VD)) {
3350+
// P2280R4 struck the restriction that variable of referene type lifetime
3351+
// should begin within the evaluation of E
3352+
if (isa<ParmVarDecl>(VD) && !AllowConstexprUnknown) {
33383353
// Assume parameters of a potential constant expression are usable in
33393354
// constant expressions.
33403355
if (!Info.checkingPotentialConstantExpression() ||
@@ -3358,7 +3373,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
33583373
// FIXME: We should eventually check whether the variable has a reachable
33593374
// initializing declaration.
33603375
const Expr *Init = VD->getAnyInitializer(VD);
3361-
if (!Init) {
3376+
// P2280R4 struck the restriction that variable of referene type should have
3377+
// a preceding initialization.
3378+
if (!Init && !AllowConstexprUnknown) {
33623379
// Don't diagnose during potential constant expression checking; an
33633380
// initializer might be added later.
33643381
if (!Info.checkingPotentialConstantExpression()) {
@@ -3369,7 +3386,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
33693386
return false;
33703387
}
33713388

3372-
if (Init->isValueDependent()) {
3389+
// P2280R4 struck the initialization requirement for variables of reference
3390+
// type so we can no longer assume we have an Init.
3391+
if (Init && Init->isValueDependent()) {
33733392
// The DeclRefExpr is not value-dependent, but the variable it refers to
33743393
// has a value-dependent initializer. This should only happen in
33753394
// constant-folding cases, where the variable is not actually of a suitable
@@ -3388,7 +3407,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
33883407

33893408
// Check that we can fold the initializer. In C++, we will have already done
33903409
// this in the cases where it matters for conformance.
3391-
if (!VD->evaluateValue()) {
3410+
// P2280R4 struck the initialization requirement for variables of reference
3411+
// type so we can no longer assume we have an Init.
3412+
if (Init && !VD->evaluateValue()) {
33923413
Info.FFDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD;
33933414
NoteLValueLocation(Info, Base);
33943415
return false;
@@ -3420,6 +3441,15 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
34203441
}
34213442

34223443
Result = VD->getEvaluatedValue();
3444+
3445+
// P2280R4 If we don't have a value because this is a reference that was not
3446+
// initialized or whose lifetime began within E then create a value with as
3447+
// a ConstexprUnknown status.
3448+
if (AllowConstexprUnknown) {
3449+
if (!Result) {
3450+
Result = new APValue(Base, APValue::ConstexprUnknown{}, CharUnits::One());
3451+
}
3452+
}
34233453
return true;
34243454
}
34253455

@@ -3700,6 +3730,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
37003730
const FieldDecl *LastField = nullptr;
37013731
const FieldDecl *VolatileField = nullptr;
37023732

3733+
// P2280R4 If we have an unknown referene or pointer and we don't have a
3734+
// value then bail out.
3735+
if (O->allowConstexprUnknown() && !O->hasValue())
3736+
return false;
3737+
37033738
// Walk the designator's path to find the subobject.
37043739
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
37053740
// Reading an indeterminate value is undefined, but assigning over one is OK.
@@ -5732,6 +5767,12 @@ struct CheckDynamicTypeHandler {
57325767
/// dynamic type.
57335768
static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
57345769
AccessKinds AK, bool Polymorphic) {
5770+
// P2280R4 We are not allowed to invoke a virtual function whose dynamic type
5771+
// us constexpr-unknown, so stop early and let this fail later on if we
5772+
// attempt to do so.
5773+
if (This.allowConstexprUnknown())
5774+
return true;
5775+
57355776
if (This.Designator.Invalid)
57365777
return false;
57375778

@@ -5804,7 +5845,13 @@ static std::optional<DynamicType> ComputeDynamicType(EvalInfo &Info,
58045845
// If we don't have an lvalue denoting an object of class type, there is no
58055846
// meaningful dynamic type. (We consider objects of non-class type to have no
58065847
// dynamic type.)
5807-
if (!checkDynamicType(Info, E, This, AK, true))
5848+
if (!checkDynamicType(Info, E, This, AK,
5849+
(AK == AK_TypeId
5850+
? (E->getType()->isReferenceType() ? true : false)
5851+
: true)))
5852+
return std::nullopt;
5853+
5854+
if (This.Designator.Invalid)
58085855
return std::nullopt;
58095856

58105857
// Refuse to compute a dynamic type in the presence of virtual bases. This
@@ -8539,7 +8586,8 @@ static bool HandleLambdaCapture(EvalInfo &Info, const Expr *E, LValue &Result,
85398586
const ParmVarDecl *Self = MD->getParamDecl(0);
85408587
if (Self->getType()->isReferenceType()) {
85418588
APValue *RefValue = Info.getParamSlot(Info.CurrentCall->Arguments, Self);
8542-
Result.setFrom(Info.Ctx, *RefValue);
8589+
if (!RefValue->allowConstexprUnknown() || RefValue->hasValue())
8590+
Result.setFrom(Info.Ctx, *RefValue);
85438591
} else {
85448592
const ParmVarDecl *VD = Info.CurrentCall->Arguments.getOrigParam(Self);
85458593
CallStackFrame *Frame =
@@ -8595,7 +8643,10 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
85958643

85968644

85978645
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
8598-
8646+
// P2280R4 if we are in C++23 track if we have an unknown reference or
8647+
// pointer.
8648+
bool AllowConstexprUnknown =
8649+
Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
85998650
// If we are within a lambda's call operator, check whether the 'VD' referred
86008651
// to within 'E' actually represents a lambda-capture that maps to a
86018652
// data-member/field within the closure object, and if so, evaluate to the
@@ -8665,10 +8716,23 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
86658716
if (!V->hasValue()) {
86668717
// FIXME: Is it possible for V to be indeterminate here? If so, we should
86678718
// adjust the diagnostic to say that.
8668-
if (!Info.checkingPotentialConstantExpression())
8719+
// P2280R4 If we are have a variable that is unknown reference or pointer
8720+
// it may not have a value but still be usable later on so do not diagnose.
8721+
if (!Info.checkingPotentialConstantExpression() && !AllowConstexprUnknown)
86698722
Info.FFDiag(E, diag::note_constexpr_use_uninit_reference);
8723+
8724+
// P2280R4 If we are have a variable that is unknown reference or pointer
8725+
// try to recover it from the frame and set the result accordingly.
8726+
if (VD->getType()->isReferenceType() && AllowConstexprUnknown) {
8727+
if (Frame) {
8728+
Result.set({VD, Frame->Index, Version});
8729+
return true;
8730+
}
8731+
return Success(VD);
8732+
}
86708733
return false;
86718734
}
8735+
86728736
return Success(*V, E);
86738737
}
86748738

@@ -11486,7 +11550,10 @@ class IntExprEvaluator
1148611550
}
1148711551

1148811552
bool Success(const APValue &V, const Expr *E) {
11489-
if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate()) {
11553+
// P2280R4 if we have an unknown reference or pointer allow further
11554+
// evaluation of the value.
11555+
if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate() ||
11556+
V.allowConstexprUnknown()) {
1149011557
Result = V;
1149111558
return true;
1149211559
}

0 commit comments

Comments
 (0)