Implement current CWG direction for support of arrays of unknown bounds in
constant expressions.
We permit array-to-pointer decay on such arrays, but disallow pointer
arithmetic (since we do not know whether it will have defined behavior).
This is based on r311970 and r301822 (the former by me and the latter by Robert
Haberlach). Between then and now, two things have changed: we have committee
feedback indicating that this is indeed the right direction, and the code
broken by this change has been fixed.
This is necessary in C++17 to continue accepting certain forms of non-type
template argument involving arrays of unknown bound.
llvm-svn: 316245
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ecce50e..c25b3b6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -62,7 +62,13 @@
static QualType getType(APValue::LValueBase B) {
if (!B) return QualType();
if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>())
- return D->getType();
+ // FIXME: It's unclear where we're supposed to take the type from, and
+ // this actually matters for arrays of unknown bound. Using the type of
+ // the most recent declaration isn't clearly correct in general. Eg:
+ //
+ // extern int arr[]; void f() { extern int arr[3]; };
+ // constexpr int *p = &arr[1]; // valid?
+ return cast<ValueDecl>(D->getMostRecentDecl())->getType();
const Expr *Base = B.get<const Expr*>();
@@ -141,6 +147,12 @@
return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E);
}
+ /// The bound to claim that an array of unknown bound has.
+ /// The value in MostDerivedArraySize is undefined in this case. So, set it
+ /// to an arbitrary value that's likely to loudly break things if it's used.
+ static const uint64_t AssumedSizeForUnsizedArray =
+ std::numeric_limits<uint64_t>::max() / 2;
+
/// Determines if an LValue with the given LValueBase will have an unsized
/// array in its designator.
/// Find the path length and type of the most-derived subobject in the given
@@ -148,7 +160,8 @@
static unsigned
findMostDerivedSubobject(ASTContext &Ctx, APValue::LValueBase Base,
ArrayRef<APValue::LValuePathEntry> Path,
- uint64_t &ArraySize, QualType &Type, bool &IsArray) {
+ uint64_t &ArraySize, QualType &Type, bool &IsArray,
+ bool &FirstEntryIsUnsizedArray) {
// This only accepts LValueBases from APValues, and APValues don't support
// arrays that lack size info.
assert(!isBaseAnAllocSizeCall(Base) &&
@@ -158,12 +171,18 @@
for (unsigned I = 0, N = Path.size(); I != N; ++I) {
if (Type->isArrayType()) {
- const ConstantArrayType *CAT =
- cast<ConstantArrayType>(Ctx.getAsArrayType(Type));
- Type = CAT->getElementType();
- ArraySize = CAT->getSize().getZExtValue();
+ const ArrayType *AT = Ctx.getAsArrayType(Type);
+ Type = AT->getElementType();
MostDerivedLength = I + 1;
IsArray = true;
+
+ if (auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ ArraySize = CAT->getSize().getZExtValue();
+ } else {
+ assert(I == 0 && "unexpected unsized array designator");
+ FirstEntryIsUnsizedArray = true;
+ ArraySize = AssumedSizeForUnsizedArray;
+ }
} else if (Type->isAnyComplexType()) {
const ComplexType *CT = Type->castAs<ComplexType>();
Type = CT->getElementType();
@@ -246,10 +265,12 @@
Entries.insert(Entries.end(), VEntries.begin(), VEntries.end());
if (V.getLValueBase()) {
bool IsArray = false;
+ bool FirstIsUnsizedArray = false;
MostDerivedPathLength = findMostDerivedSubobject(
Ctx, V.getLValueBase(), V.getLValuePath(), MostDerivedArraySize,
- MostDerivedType, IsArray);
+ MostDerivedType, IsArray, FirstIsUnsizedArray);
MostDerivedIsArrayElement = IsArray;
+ FirstEntryIsAnUnsizedArray = FirstIsUnsizedArray;
}
}
}
@@ -318,7 +339,7 @@
// The value in MostDerivedArraySize is undefined in this case. So, set it
// to an arbitrary value that's likely to loudly break things if it's
// used.
- MostDerivedArraySize = std::numeric_limits<uint64_t>::max() / 2;
+ MostDerivedArraySize = AssumedSizeForUnsizedArray;
MostDerivedPathLength = Entries.size();
}
/// Update this designator to refer to the given base or member of this
@@ -350,6 +371,7 @@
MostDerivedArraySize = 2;
MostDerivedPathLength = Entries.size();
}
+ void diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info, const Expr *E);
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E,
const APSInt &N);
/// Add N to the address of this subobject.
@@ -357,6 +379,7 @@
if (Invalid || !N) return;
uint64_t TruncatedN = N.extOrTrunc(64).getZExtValue();
if (isMostDerivedAnUnsizedArray()) {
+ diagnoseUnsizedArrayPointerArithmetic(Info, E);
// Can't verify -- trust that the user is doing the right thing (or if
// not, trust that the caller will catch the bad behavior).
// FIXME: Should we reject if this overflows, at least?
@@ -1094,9 +1117,19 @@
setInvalid();
return false;
}
+ // Note, we do not diagnose if isMostDerivedAnUnsizedArray(), because there
+ // must actually be at least one array element; even a VLA cannot have a
+ // bound of zero. And if our index is nonzero, we already had a CCEDiag.
return true;
}
+void SubobjectDesignator::diagnoseUnsizedArrayPointerArithmetic(EvalInfo &Info,
+ const Expr *E) {
+ Info.CCEDiag(E, diag::note_constexpr_unsized_array_indexed);
+ // Do not set the designator as invalid: we can represent this situation,
+ // and correct handling of __builtin_object_size requires us to do so.
+}
+
void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info,
const Expr *E,
const APSInt &N) {
@@ -1240,8 +1273,6 @@
IsNullPtr);
else {
assert(!InvalidBase && "APValues can't handle invalid LValue bases");
- assert(!Designator.FirstEntryIsAnUnsizedArray &&
- "Unsized array with a valid base?");
V = APValue(Base, Offset, Designator.Entries,
Designator.IsOnePastTheEnd, CallIndex, IsNullPtr);
}
@@ -1314,10 +1345,14 @@
if (checkSubobject(Info, E, isa<FieldDecl>(D) ? CSK_Field : CSK_Base))
Designator.addDeclUnchecked(D, Virtual);
}
- void addUnsizedArray(EvalInfo &Info, QualType ElemTy) {
- assert(Designator.Entries.empty() && getType(Base)->isPointerType());
- assert(isBaseAnAllocSizeCall(Base) &&
- "Only alloc_size bases can have unsized arrays");
+ void addUnsizedArray(EvalInfo &Info, const Expr *E, QualType ElemTy) {
+ if (!Designator.Entries.empty()) {
+ Info.CCEDiag(E, diag::note_constexpr_unsupported_unsized_array);
+ Designator.setInvalid();
+ return;
+ }
+
+ assert(getType(Base)->isPointerType() || getType(Base)->isArrayType());
Designator.FirstEntryIsAnUnsizedArray = true;
Designator.addUnsizedArrayUnchecked(ElemTy);
}
@@ -2624,10 +2659,12 @@
if (Sub.Invalid)
// A diagnostic will have already been produced.
return handler.failed();
- if (Sub.isOnePastTheEnd()) {
+ if (Sub.isOnePastTheEnd() || Sub.isMostDerivedAnUnsizedArray()) {
if (Info.getLangOpts().CPlusPlus11)
- Info.FFDiag(E, diag::note_constexpr_access_past_end)
- << handler.AccessKind;
+ Info.FFDiag(E, Sub.isOnePastTheEnd()
+ ? diag::note_constexpr_access_past_end
+ : diag::note_constexpr_access_unsized_array)
+ << handler.AccessKind;
else
Info.FFDiag(E);
return handler.failed();
@@ -5487,7 +5524,7 @@
Result.setInvalid(E);
QualType Pointee = E->getType()->castAs<PointerType>()->getPointeeType();
- Result.addUnsizedArray(Info, Pointee);
+ Result.addUnsizedArray(Info, E, Pointee);
return true;
}
@@ -5697,7 +5734,8 @@
return true;
}
}
- case CK_ArrayToPointerDecay:
+
+ case CK_ArrayToPointerDecay: {
if (SubExpr->isGLValue()) {
if (!evaluateLValue(SubExpr, Result))
return false;
@@ -5708,12 +5746,13 @@
return false;
}
// The result is a pointer to the first element of the array.
- if (const ConstantArrayType *CAT
- = Info.Ctx.getAsConstantArrayType(SubExpr->getType()))
+ auto *AT = Info.Ctx.getAsArrayType(SubExpr->getType());
+ if (auto *CAT = dyn_cast<ConstantArrayType>(AT))
Result.addArray(Info, E, CAT);
else
- Result.Designator.setInvalid();
+ Result.addUnsizedArray(Info, E, AT->getElementType());
return true;
+ }
case CK_FunctionToPointerDecay:
return evaluateLValue(SubExpr, Result);
@@ -5780,7 +5819,7 @@
Result.setInvalid(E);
QualType PointeeTy = E->getType()->castAs<PointerType>()->getPointeeType();
- Result.addUnsizedArray(Info, PointeeTy);
+ Result.addUnsizedArray(Info, E, PointeeTy);
return true;
}
@@ -7341,7 +7380,8 @@
/// Please note: this function is specialized for how __builtin_object_size
/// views "objects".
///
-/// If this encounters an invalid RecordDecl, it will always return true.
+/// If this encounters an invalid RecordDecl or otherwise cannot determine the
+/// correct result, it will always return true.
static bool isDesignatorAtObjectEnd(const ASTContext &Ctx, const LValue &LVal) {
assert(!LVal.Designator.Invalid);
@@ -7372,9 +7412,8 @@
unsigned I = 0;
QualType BaseType = getType(Base);
if (LVal.Designator.FirstEntryIsAnUnsizedArray) {
- assert(isBaseAnAllocSizeCall(Base) &&
- "Unsized array in non-alloc_size call?");
- // If this is an alloc_size base, we should ignore the initial array index
+ // If we don't know the array bound, conservatively assume we're looking at
+ // the final array element.
++I;
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
}