Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c62284c

Browse files
[clang] Followup for constexpr-unknown potential constant expressions. (#151053)
6a60f18 fixed the primary issue of dereferences, but there are some expressions that depend on the identity of the pointed-to object without actually accessing it. Handle those cases. Also, while I'm here, fix a crash in interpreter mode comparing typeid to nullptr.
1 parent cf3a887 commit c62284c

File tree

3 files changed

+117
-65
lines changed

3 files changed

+117
-65
lines changed

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,6 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
627627
if (A.isTypeidPointer() && B.isTypeidPointer())
628628
return true;
629629

630-
if (A.isIntegralPointer() || B.isIntegralPointer())
631-
return A.getSource() == B.getSource();
632-
633630
if (A.StorageKind != B.StorageKind)
634631
return false;
635632

clang/lib/AST/ExprConstant.cpp

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -456,49 +456,7 @@ namespace {
456456
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
457457
const APSInt &N);
458458
/// Add N to the address of this subobject.
459-
void adjustIndex(EvalInfo &Info, const Expr *E, APSInt N) {
460-
if (Invalid || !N) return;
461-
uint64_t TruncatedN = N.extOrTrunc(64).getZExtValue();
462-
if (isMostDerivedAnUnsizedArray()) {
463-
diagnoseUnsizedArrayPointerArithmetic(Info, E);
464-
// Can't verify -- trust that the user is doing the right thing (or if
465-
// not, trust that the caller will catch the bad behavior).
466-
// FIXME: Should we reject if this overflows, at least?
467-
Entries.back() = PathEntry::ArrayIndex(
468-
Entries.back().getAsArrayIndex() + TruncatedN);
469-
return;
470-
}
471-
472-
// [expr.add]p4: For the purposes of these operators, a pointer to a
473-
// nonarray object behaves the same as a pointer to the first element of
474-
// an array of length one with the type of the object as its element type.
475-
bool IsArray = MostDerivedPathLength == Entries.size() &&
476-
MostDerivedIsArrayElement;
477-
uint64_t ArrayIndex = IsArray ? Entries.back().getAsArrayIndex()
478-
: (uint64_t)IsOnePastTheEnd;
479-
uint64_t ArraySize =
480-
IsArray ? getMostDerivedArraySize() : (uint64_t)1;
481-
482-
if (N < -(int64_t)ArrayIndex || N > ArraySize - ArrayIndex) {
483-
// Calculate the actual index in a wide enough type, so we can include
484-
// it in the note.
485-
N = N.extend(std::max<unsigned>(N.getBitWidth() + 1, 65));
486-
(llvm::APInt&)N += ArrayIndex;
487-
assert(N.ugt(ArraySize) && "bounds check failed for in-bounds index");
488-
diagnosePointerArithmetic(Info, E, N);
489-
setInvalid();
490-
return;
491-
}
492-
493-
ArrayIndex += TruncatedN;
494-
assert(ArrayIndex <= ArraySize &&
495-
"bounds check succeeded for out-of-bounds index");
496-
497-
if (IsArray)
498-
Entries.back() = PathEntry::ArrayIndex(ArrayIndex);
499-
else
500-
IsOnePastTheEnd = (ArrayIndex != 0);
501-
}
459+
void adjustIndex(EvalInfo &Info, const Expr *E, APSInt N, const LValue &LV);
502460
};
503461

504462
/// A scope at the end of which an object can need to be destroyed.
@@ -1795,7 +1753,7 @@ namespace {
17951753
Offset = CharUnits::fromQuantity(Offset64 + ElemSize64 * Index64);
17961754

17971755
if (checkNullPointer(Info, E, CSK_ArrayIndex))
1798-
Designator.adjustIndex(Info, E, Index);
1756+
Designator.adjustIndex(Info, E, Index, *this);
17991757
clearIsNullPointer();
18001758
}
18011759
void adjustOffset(CharUnits N) {
@@ -1903,6 +1861,54 @@ namespace {
19031861
}
19041862
}
19051863

1864+
void SubobjectDesignator::adjustIndex(EvalInfo &Info, const Expr *E, APSInt N,
1865+
const LValue &LV) {
1866+
if (Invalid || !N)
1867+
return;
1868+
uint64_t TruncatedN = N.extOrTrunc(64).getZExtValue();
1869+
if (isMostDerivedAnUnsizedArray()) {
1870+
diagnoseUnsizedArrayPointerArithmetic(Info, E);
1871+
// Can't verify -- trust that the user is doing the right thing (or if
1872+
// not, trust that the caller will catch the bad behavior).
1873+
// FIXME: Should we reject if this overflows, at least?
1874+
Entries.back() =
1875+
PathEntry::ArrayIndex(Entries.back().getAsArrayIndex() + TruncatedN);
1876+
return;
1877+
}
1878+
1879+
// [expr.add]p4: For the purposes of these operators, a pointer to a
1880+
// nonarray object behaves the same as a pointer to the first element of
1881+
// an array of length one with the type of the object as its element type.
1882+
bool IsArray =
1883+
MostDerivedPathLength == Entries.size() && MostDerivedIsArrayElement;
1884+
uint64_t ArrayIndex =
1885+
IsArray ? Entries.back().getAsArrayIndex() : (uint64_t)IsOnePastTheEnd;
1886+
uint64_t ArraySize = IsArray ? getMostDerivedArraySize() : (uint64_t)1;
1887+
1888+
if (N < -(int64_t)ArrayIndex || N > ArraySize - ArrayIndex) {
1889+
if (!Info.checkingPotentialConstantExpression() ||
1890+
!LV.AllowConstexprUnknown) {
1891+
// Calculate the actual index in a wide enough type, so we can include
1892+
// it in the note.
1893+
N = N.extend(std::max<unsigned>(N.getBitWidth() + 1, 65));
1894+
(llvm::APInt &)N += ArrayIndex;
1895+
assert(N.ugt(ArraySize) && "bounds check failed for in-bounds index");
1896+
diagnosePointerArithmetic(Info, E, N);
1897+
}
1898+
setInvalid();
1899+
return;
1900+
}
1901+
1902+
ArrayIndex += TruncatedN;
1903+
assert(ArrayIndex <= ArraySize &&
1904+
"bounds check succeeded for out-of-bounds index");
1905+
1906+
if (IsArray)
1907+
Entries.back() = PathEntry::ArrayIndex(ArrayIndex);
1908+
else
1909+
IsOnePastTheEnd = (ArrayIndex != 0);
1910+
}
1911+
19061912
static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E);
19071913
static bool EvaluateInPlace(APValue &Result, EvalInfo &Info,
19081914
const LValue &This, const Expr *E,
@@ -5144,12 +5150,18 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
51445150
if (const PointerType *PT = TargetQT->getAs<PointerType>())
51455151
TargetQT = PT->getPointeeType();
51465152

5147-
// Check this cast lands within the final derived-to-base subobject path.
5148-
if (D.MostDerivedPathLength + E->path_size() > D.Entries.size()) {
5149-
Info.CCEDiag(E, diag::note_constexpr_invalid_downcast)
5150-
<< D.MostDerivedType << TargetQT;
5153+
auto InvalidCast = [&]() {
5154+
if (!Info.checkingPotentialConstantExpression() ||
5155+
!Result.AllowConstexprUnknown) {
5156+
Info.CCEDiag(E, diag::note_constexpr_invalid_downcast)
5157+
<< D.MostDerivedType << TargetQT;
5158+
}
51515159
return false;
5152-
}
5160+
};
5161+
5162+
// Check this cast lands within the final derived-to-base subobject path.
5163+
if (D.MostDerivedPathLength + E->path_size() > D.Entries.size())
5164+
return InvalidCast();
51535165

51545166
// Check the type of the final cast. We don't need to check the path,
51555167
// since a cast can only be formed if the path is unique.
@@ -5160,11 +5172,8 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
51605172
FinalType = D.MostDerivedType->getAsCXXRecordDecl();
51615173
else
51625174
FinalType = getAsBaseClass(D.Entries[NewEntriesSize - 1]);
5163-
if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl()) {
5164-
Info.CCEDiag(E, diag::note_constexpr_invalid_downcast)
5165-
<< D.MostDerivedType << TargetQT;
5166-
return false;
5167-
}
5175+
if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl())
5176+
return InvalidCast();
51685177

51695178
// Truncate the lvalue to the appropriate derived class.
51705179
return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize);
@@ -6165,12 +6174,15 @@ static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
61656174
} else if (Polymorphic) {
61666175
// Conservatively refuse to perform a polymorphic operation if we would
61676176
// not be able to read a notional 'vptr' value.
6168-
APValue Val;
6169-
This.moveInto(Val);
6170-
QualType StarThisType =
6171-
Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx));
6172-
Info.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
6173-
<< AK << Val.getAsString(Info.Ctx, StarThisType);
6177+
if (!Info.checkingPotentialConstantExpression() ||
6178+
!This.AllowConstexprUnknown) {
6179+
APValue Val;
6180+
This.moveInto(Val);
6181+
QualType StarThisType =
6182+
Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx));
6183+
Info.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
6184+
<< AK << Val.getAsString(Info.Ctx, StarThisType);
6185+
}
61746186
return false;
61756187
}
61766188
return true;
@@ -15075,6 +15087,11 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
1507515087
// Reject differing bases from the normal codepath; we special-case
1507615088
// comparisons to null.
1507715089
if (!HasSameBase(LHSValue, RHSValue)) {
15090+
// Bail out early if we're checking potential constant expression.
15091+
// Otherwise, prefer to diagnose other issues.
15092+
if (Info.checkingPotentialConstantExpression() &&
15093+
(LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown))
15094+
return false;
1507815095
auto DiagComparison = [&] (unsigned DiagID, bool Reversed = false) {
1507915096
std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType());
1508015097
std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType());
@@ -15395,6 +15412,10 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
1539515412
// Reject differing bases from the normal codepath; we special-case
1539615413
// comparisons to null.
1539715414
if (!HasSameBase(LHSValue, RHSValue)) {
15415+
if (Info.checkingPotentialConstantExpression() &&
15416+
(LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown))
15417+
return false;
15418+
1539815419
const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr *>();
1539915420
const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>();
1540015421

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -std=c++23 -verify=expected,nointerpreter %s
2-
// RUN: %clang_cc1 -std=c++23 -verify=expected,interpreter %s -fexperimental-new-constant-interpreter
1+
// RUN: %clang_cc1 -std=c++23 -verify=expected,nointerpreter -Winvalid-constexpr %s
2+
// RUN: %clang_cc1 -std=c++23 -verify=expected,interpreter %s -fexperimental-new-constant-interpreter -Winvalid-constexpr
33

44
using size_t = decltype(sizeof(0));
55

@@ -397,3 +397,37 @@ namespace GH150015 {
397397
// nointerpreter-note {{comparison of addresses of subobjects of different base classes has unspecified value}} \
398398
// interpreter-note {{initializer of 'z' is unknown}}
399399
}
400+
401+
namespace InvalidConstexprFn {
402+
// Make sure we don't trigger -Winvalid-constexpr incorrectly.
403+
constexpr bool same_address(const int &a, const int &b) { return &a == &b; }
404+
constexpr int next_element(const int &p) { return (&p)[2]; }
405+
406+
struct Base {};
407+
struct Derived : Base { int n; };
408+
constexpr int get_derived_member(const Base& b) { return static_cast<const Derived&>(b).n; }
409+
410+
struct PolyBase {
411+
constexpr virtual int get() const { return 0; }
412+
};
413+
struct PolyDerived : PolyBase {
414+
constexpr int get() const override { return 1; }
415+
};
416+
constexpr int virtual_call(const PolyBase& b) { return b.get(); }
417+
constexpr auto* type(const PolyBase& b) { return &typeid(b); }
418+
// FIXME: Intepreter doesn't support constexpr dynamic_cast yet.
419+
constexpr const void* dyncast(const PolyBase& b) { return dynamic_cast<const void*>(&b); } // interpreter-error {{constexpr function never produces a constant expression}} \
420+
// interpreter-note 2 {{subexpression not valid in a constant expression}}
421+
constexpr int sub(const int (&a)[], const int (&b)[]) { return a-b; }
422+
constexpr const int* add(const int &a) { return &a+3; }
423+
424+
constexpr int arr[3]{0, 1, 2};
425+
static_assert(same_address(arr[1], arr[1]));
426+
static_assert(next_element(arr[0]) == 2);
427+
static_assert(get_derived_member(Derived{}) == 0);
428+
static_assert(virtual_call(PolyDerived{}) == 1);
429+
static_assert(type(PolyDerived{}) != nullptr);
430+
static_assert(dyncast(PolyDerived{}) != nullptr); // interpreter-error {{static assertion expression is not an integral constant expression}} interpreter-note {{in call}}
431+
static_assert(sub(arr, arr) == 0);
432+
static_assert(add(arr[0]) == &arr[3]);
433+
}

0 commit comments

Comments
 (0)