Skip to content

Commit 39b9a09

Browse files
committed
[Clang] Diagnose forming references to nullptr
Per [decl.ref], > Because a null pointer value or a pointer past the end of an object does not point to an object, a reference in a well-defined program cannot refer to such things. Note this does not fixes the new bytecode interpreter. Fixes #48665
1 parent d223832 commit 39b9a09

File tree

4 files changed

+48
-11
lines changed

4 files changed

+48
-11
lines changed

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,11 @@ def note_constexpr_heap_alloc_limit_exceeded : Note<
174174
def note_constexpr_this : Note<
175175
"%select{|implicit }0use of 'this' pointer is only allowed within the "
176176
"evaluation of a call to a 'constexpr' member function">;
177-
def access_kind : TextSubstitution<
178-
"%select{read of|read of|assignment to|increment of|decrement of|"
179-
"member call on|dynamic_cast of|typeid applied to|construction of|"
180-
"destruction of|read of}0">;
177+
def access_kind
178+
: TextSubstitution<
179+
"%select{read of|read of|assignment to|increment of|decrement of|"
180+
"member call on|dynamic_cast of|typeid applied to|construction of|"
181+
"destruction of|read of|read of}0">;
181182
def access_kind_subobject : TextSubstitution<
182183
"%select{read of|read of|assignment to|increment of|decrement of|"
183184
"member call on|dynamic_cast of|typeid applied to|"

clang/lib/AST/ByteCode/State.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum AccessKinds {
3535
AK_Construct,
3636
AK_Destroy,
3737
AK_IsWithinLifetime,
38+
AK_CheckReferenceInitialization
3839
};
3940

4041
/// The order of this enum is important for diagnostics.

clang/lib/AST/ExprConstant.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,7 @@ CallStackFrame::~CallStackFrame() {
15291529

15301530
static bool isRead(AccessKinds AK) {
15311531
return AK == AK_Read || AK == AK_ReadObjectRepresentation ||
1532-
AK == AK_IsWithinLifetime;
1532+
AK == AK_IsWithinLifetime || AK == AK_CheckReferenceInitialization;
15331533
}
15341534

15351535
static bool isModification(AccessKinds AK) {
@@ -1540,6 +1540,7 @@ static bool isModification(AccessKinds AK) {
15401540
case AK_DynamicCast:
15411541
case AK_TypeId:
15421542
case AK_IsWithinLifetime:
1543+
case AK_CheckReferenceInitialization:
15431544
return false;
15441545
case AK_Assign:
15451546
case AK_Increment:
@@ -1558,7 +1559,7 @@ static bool isAnyAccess(AccessKinds AK) {
15581559
/// Is this an access per the C++ definition?
15591560
static bool isFormalAccess(AccessKinds AK) {
15601561
return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy &&
1561-
AK != AK_IsWithinLifetime;
1562+
AK != AK_IsWithinLifetime && AK != AK_CheckReferenceInitialization;
15621563
}
15631564

15641565
/// Is this kind of axcess valid on an indeterminate object value?
@@ -1571,6 +1572,7 @@ static bool isValidIndeterminateAccess(AccessKinds AK) {
15711572
return false;
15721573

15731574
case AK_IsWithinLifetime:
1575+
case AK_CheckReferenceInitialization:
15741576
case AK_ReadObjectRepresentation:
15751577
case AK_Assign:
15761578
case AK_Construct:
@@ -4425,7 +4427,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
44254427

44264428
// Unless we're looking at a local variable or argument in a constexpr call,
44274429
// the variable we're reading must be const.
4428-
if (!Frame) {
4430+
if (!Frame && AK != clang::AK_CheckReferenceInitialization) {
44294431
if (IsAccess && isa<ParmVarDecl>(VD)) {
44304432
// Access of a parameter that's not associated with a frame isn't going
44314433
// to work out, but we can leave it to evaluateVarDeclInit to provide a
@@ -4502,7 +4504,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
45024504
} else {
45034505
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
45044506

4505-
if (!Frame) {
4507+
if (!Frame && AK != clang::AK_CheckReferenceInitialization) {
45064508
if (const MaterializeTemporaryExpr *MTE =
45074509
dyn_cast_or_null<MaterializeTemporaryExpr>(Base)) {
45084510
assert(MTE->getStorageDuration() == SD_Static &&
@@ -4556,7 +4558,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
45564558
NoteLValueLocation(Info, LVal.Base);
45574559
return CompleteObject();
45584560
}
4559-
} else {
4561+
} else if (AK != clang::AK_CheckReferenceInitialization) {
45604562
BaseVal = Frame->getTemporary(Base, LVal.Base.getVersion());
45614563
assert(BaseVal && "missing value for temporary");
45624564
}
@@ -5242,7 +5244,19 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
52425244
if (InitE->isValueDependent())
52435245
return false;
52445246

5245-
if (!EvaluateInPlace(Val, Info, Result, InitE)) {
5247+
if (VD->getType()->isReferenceType() && InitE->isGLValue()) {
5248+
if (!EvaluateLValue(InitE, Result, Info))
5249+
return false;
5250+
CompleteObject Obj = findCompleteObject(
5251+
Info, InitE, AK_CheckReferenceInitialization, Result, InitE->getType());
5252+
if (Result.Designator.isOnePastTheEnd()) {
5253+
Info.FFDiag(InitE, diag::note_constexpr_access_past_end)
5254+
<< AK_CheckReferenceInitialization;
5255+
return false;
5256+
}
5257+
Result.moveInto(Val);
5258+
return !!Obj;
5259+
} else if (!EvaluateInPlace(Val, Info, Result, InitE)) {
52465260
// Wipe out any partially-computed value, to allow tracking that this
52475261
// evaluation failed.
52485262
Val = APValue();

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ namespace subobject {
250250
namespace lifetime {
251251
constexpr int &&id(int &&n) { return static_cast<int&&>(n); }
252252
constexpr int &&dead() { return id(0); } // expected-note {{temporary created here}}
253-
constexpr int bad() { int &&n = dead(); n = 1; return n; } // expected-note {{assignment to temporary whose lifetime has ended}}
253+
constexpr int bad() { int &&n = dead(); n = 1; return n; } // expected-note {{read of temporary whose lifetime has ended}}
254254
static_assert(bad(), ""); // expected-error {{constant expression}} expected-note {{in call}}
255255
}
256256

@@ -1321,3 +1321,24 @@ constexpr bool check = different_in_loop();
13211321
// expected-error@-1 {{}} expected-note@-1 {{in call}}
13221322

13231323
}
1324+
1325+
namespace GH48665 {
1326+
constexpr bool foo(int *i) {
1327+
int &j = *i;
1328+
// expected-note@-1 {{read of dereferenced null pointer is not allowed in a constant expression}}
1329+
return true;
1330+
}
1331+
1332+
static_assert(foo(nullptr), ""); // expected-note {{in call to 'foo(nullptr)'}}
1333+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
1334+
1335+
int arr[3]; // expected-note 2{{declared here}}
1336+
constexpr bool f() { // cxx14_20-error {{constexpr function never produces a constant expression}}
1337+
int &r = arr[3]; // cxx14_20-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}} \
1338+
// expected-warning {{array index 3 is past the end of the array}}\
1339+
// expected-note {{initializer of 'arr' is unknown}}
1340+
return true;
1341+
}
1342+
static_assert(f(), ""); // expected-note {{in call to 'f()'}}
1343+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
1344+
}

0 commit comments

Comments
 (0)