forked from OSchip/llvm-project
Constant expression evaluation: track the manner in which an lvalue was written,
to allow us to implement the C++11 rule that a non-active union member can't be read, and use it to implement subobject access for string literals. llvm-svn: 143677
This commit is contained in:
parent
744756e389
commit
96e0c101fe
|
@ -46,6 +46,78 @@ namespace {
|
||||||
struct CallStackFrame;
|
struct CallStackFrame;
|
||||||
struct EvalInfo;
|
struct EvalInfo;
|
||||||
|
|
||||||
|
/// A path from a glvalue to a subobject of that glvalue.
|
||||||
|
struct SubobjectDesignator {
|
||||||
|
/// True if the subobject was named in a manner not supported by C++11. Such
|
||||||
|
/// lvalues can still be folded, but they are not core constant expressions
|
||||||
|
/// and we cannot perform lvalue-to-rvalue conversions on them.
|
||||||
|
bool Invalid : 1;
|
||||||
|
|
||||||
|
/// Whether this designates an array element.
|
||||||
|
bool ArrayElement : 1;
|
||||||
|
|
||||||
|
/// Whether this designates 'one past the end' of the current subobject.
|
||||||
|
bool OnePastTheEnd : 1;
|
||||||
|
|
||||||
|
union PathEntry {
|
||||||
|
/// If the current subobject is of class type, this indicates which
|
||||||
|
/// subobject of that type is accessed next.
|
||||||
|
const Decl *BaseOrMember;
|
||||||
|
/// If the current subobject is of array type, this indicates which index
|
||||||
|
/// within that array is accessed next.
|
||||||
|
uint64_t Index;
|
||||||
|
};
|
||||||
|
/// The entries on the path from the glvalue to the designated subobject.
|
||||||
|
SmallVector<PathEntry, 8> Entries;
|
||||||
|
|
||||||
|
SubobjectDesignator() :
|
||||||
|
Invalid(false), ArrayElement(false), OnePastTheEnd(false) {}
|
||||||
|
|
||||||
|
void setInvalid() {
|
||||||
|
Invalid = true;
|
||||||
|
Entries.clear();
|
||||||
|
}
|
||||||
|
/// Update this designator to refer to the given element within this array.
|
||||||
|
void addIndex(uint64_t N) {
|
||||||
|
if (Invalid) return;
|
||||||
|
if (OnePastTheEnd) {
|
||||||
|
setInvalid();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PathEntry Entry;
|
||||||
|
Entry.Index = N;
|
||||||
|
Entries.push_back(Entry);
|
||||||
|
ArrayElement = true;
|
||||||
|
}
|
||||||
|
/// Update this designator to refer to the given base or member of this
|
||||||
|
/// object.
|
||||||
|
void addDecl(const Decl *D) {
|
||||||
|
if (Invalid) return;
|
||||||
|
if (OnePastTheEnd) {
|
||||||
|
setInvalid();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PathEntry Entry;
|
||||||
|
Entry.BaseOrMember = D;
|
||||||
|
Entries.push_back(Entry);
|
||||||
|
ArrayElement = false;
|
||||||
|
}
|
||||||
|
/// Add N to the address of this subobject.
|
||||||
|
void adjustIndex(uint64_t N) {
|
||||||
|
if (Invalid) return;
|
||||||
|
if (ArrayElement) {
|
||||||
|
Entries.back().Index += N;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (OnePastTheEnd && N == (uint64_t)-1)
|
||||||
|
OnePastTheEnd = false;
|
||||||
|
else if (!OnePastTheEnd && N == 1)
|
||||||
|
OnePastTheEnd = true;
|
||||||
|
else if (N != 0)
|
||||||
|
setInvalid();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// A core constant value. This can be the value of any constant expression,
|
/// A core constant value. This can be the value of any constant expression,
|
||||||
/// or a pointer or reference to a non-static object or function parameter.
|
/// or a pointer or reference to a non-static object or function parameter.
|
||||||
class CCValue : public APValue {
|
class CCValue : public APValue {
|
||||||
|
@ -54,6 +126,9 @@ namespace {
|
||||||
/// If the value is a reference or pointer into a parameter or temporary,
|
/// If the value is a reference or pointer into a parameter or temporary,
|
||||||
/// this is the corresponding call stack frame.
|
/// this is the corresponding call stack frame.
|
||||||
CallStackFrame *CallFrame;
|
CallStackFrame *CallFrame;
|
||||||
|
/// If the value is a reference or pointer, this is a description of how the
|
||||||
|
/// subobject was specified.
|
||||||
|
SubobjectDesignator Designator;
|
||||||
public:
|
public:
|
||||||
struct GlobalValue {};
|
struct GlobalValue {};
|
||||||
|
|
||||||
|
@ -64,15 +139,23 @@ namespace {
|
||||||
CCValue(const APSInt &R, const APSInt &I) : APValue(R, I) {}
|
CCValue(const APSInt &R, const APSInt &I) : APValue(R, I) {}
|
||||||
CCValue(const APFloat &R, const APFloat &I) : APValue(R, I) {}
|
CCValue(const APFloat &R, const APFloat &I) : APValue(R, I) {}
|
||||||
CCValue(const CCValue &V) : APValue(V), CallFrame(V.CallFrame) {}
|
CCValue(const CCValue &V) : APValue(V), CallFrame(V.CallFrame) {}
|
||||||
CCValue(const Expr *B, const CharUnits &O, CallStackFrame *F) :
|
CCValue(const Expr *B, const CharUnits &O, CallStackFrame *F,
|
||||||
APValue(B, O), CallFrame(F) {}
|
const SubobjectDesignator &D) :
|
||||||
|
APValue(B, O), CallFrame(F), Designator(D) {}
|
||||||
CCValue(const APValue &V, GlobalValue) :
|
CCValue(const APValue &V, GlobalValue) :
|
||||||
APValue(V), CallFrame(0) {}
|
APValue(V), CallFrame(0), Designator() {}
|
||||||
|
|
||||||
CallStackFrame *getLValueFrame() const {
|
CallStackFrame *getLValueFrame() const {
|
||||||
assert(getKind() == LValue);
|
assert(getKind() == LValue);
|
||||||
return CallFrame;
|
return CallFrame;
|
||||||
}
|
}
|
||||||
|
SubobjectDesignator &getLValueDesignator() {
|
||||||
|
assert(getKind() == LValue);
|
||||||
|
return Designator;
|
||||||
|
}
|
||||||
|
const SubobjectDesignator &getLValueDesignator() const {
|
||||||
|
return const_cast<CCValue*>(this)->getLValueDesignator();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A stack frame in the constexpr call stack.
|
/// A stack frame in the constexpr call stack.
|
||||||
|
@ -189,20 +272,31 @@ namespace {
|
||||||
const Expr *Base;
|
const Expr *Base;
|
||||||
CharUnits Offset;
|
CharUnits Offset;
|
||||||
CallStackFrame *Frame;
|
CallStackFrame *Frame;
|
||||||
|
SubobjectDesignator Designator;
|
||||||
|
|
||||||
const Expr *getLValueBase() const { return Base; }
|
const Expr *getLValueBase() const { return Base; }
|
||||||
CharUnits &getLValueOffset() { return Offset; }
|
CharUnits &getLValueOffset() { return Offset; }
|
||||||
const CharUnits &getLValueOffset() const { return Offset; }
|
const CharUnits &getLValueOffset() const { return Offset; }
|
||||||
CallStackFrame *getLValueFrame() const { return Frame; }
|
CallStackFrame *getLValueFrame() const { return Frame; }
|
||||||
|
SubobjectDesignator &getLValueDesignator() { return Designator; }
|
||||||
|
const SubobjectDesignator &getLValueDesignator() const { return Designator;}
|
||||||
|
|
||||||
void moveInto(CCValue &V) const {
|
void moveInto(CCValue &V) const {
|
||||||
V = CCValue(Base, Offset, Frame);
|
V = CCValue(Base, Offset, Frame, Designator);
|
||||||
}
|
}
|
||||||
void setFrom(const CCValue &V) {
|
void setFrom(const CCValue &V) {
|
||||||
assert(V.isLValue());
|
assert(V.isLValue());
|
||||||
Base = V.getLValueBase();
|
Base = V.getLValueBase();
|
||||||
Offset = V.getLValueOffset();
|
Offset = V.getLValueOffset();
|
||||||
Frame = V.getLValueFrame();
|
Frame = V.getLValueFrame();
|
||||||
|
Designator = V.getLValueDesignator();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExpr(const Expr *E, CallStackFrame *F = 0) {
|
||||||
|
Base = E;
|
||||||
|
Offset = CharUnits::Zero();
|
||||||
|
Frame = F;
|
||||||
|
Designator = SubobjectDesignator();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -441,19 +535,7 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
|
||||||
if (!Base)
|
if (!Base)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// FIXME: Support accessing subobjects of objects of literal types. A simple
|
|
||||||
// byte offset is insufficient for C++11 semantics: we need to know how the
|
|
||||||
// reference was formed (which union member was named, for instance).
|
|
||||||
// FIXME: Support subobjects of StringLiteral and PredefinedExpr.
|
|
||||||
if (!LVal.Offset.isZero())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (const ValueDecl *D = GetLValueBaseDecl(LVal)) {
|
if (const ValueDecl *D = GetLValueBaseDecl(LVal)) {
|
||||||
// If the lvalue has been cast to some other type, don't try to read it.
|
|
||||||
// FIXME: Could simulate a bitcast here.
|
|
||||||
if (!Info.Ctx.hasSameUnqualifiedType(Type, D->getType()))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
|
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
|
||||||
// In C++11, constexpr, non-volatile variables initialized with constant
|
// In C++11, constexpr, non-volatile variables initialized with constant
|
||||||
// expressions are constant expressions too. Inside constexpr functions,
|
// expressions are constant expressions too. Inside constexpr functions,
|
||||||
|
@ -466,25 +548,62 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
|
||||||
// objects in constant expressions), but lvalue-to-rvalue conversions on
|
// objects in constant expressions), but lvalue-to-rvalue conversions on
|
||||||
// them are not permitted.
|
// them are not permitted.
|
||||||
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||||
if (!VD || !(IsConstNonVolatile(VD->getType()) || isa<ParmVarDecl>(VD)) ||
|
QualType VT = VD->getType();
|
||||||
!(Type->isIntegralOrEnumerationType() || Type->isRealFloatingType()) ||
|
if (!VD)
|
||||||
!EvaluateVarDeclInit(Info, VD, Frame, RVal))
|
return false;
|
||||||
|
if (!isa<ParmVarDecl>(VD)) {
|
||||||
|
if (!IsConstNonVolatile(VT))
|
||||||
|
return false;
|
||||||
|
if (!VT->isIntegralOrEnumerationType() && !VT->isRealFloatingType())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!EvaluateVarDeclInit(Info, VD, Frame, RVal))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue())
|
if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue())
|
||||||
return true;
|
// If the lvalue refers to a subobject or has been cast to some other
|
||||||
|
// type, don't use it.
|
||||||
|
return LVal.Offset.isZero() &&
|
||||||
|
Info.Ctx.hasSameUnqualifiedType(Type, VT);
|
||||||
|
|
||||||
// The declaration was initialized by an lvalue, with no lvalue-to-rvalue
|
// The declaration was initialized by an lvalue, with no lvalue-to-rvalue
|
||||||
// conversion. This happens when the declaration and the lvalue should be
|
// conversion. This happens when the declaration and the lvalue should be
|
||||||
// considered synonymous, for instance when initializing an array of char
|
// considered synonymous, for instance when initializing an array of char
|
||||||
// from a string literal. Continue as if the initializer lvalue was the
|
// from a string literal. Continue as if the initializer lvalue was the
|
||||||
// value we were originally given.
|
// value we were originally given.
|
||||||
if (!RVal.getLValueOffset().isZero())
|
assert(RVal.getLValueOffset().isZero() &&
|
||||||
return false;
|
"offset for lvalue init of non-reference");
|
||||||
Base = RVal.getLValueBase();
|
Base = RVal.getLValueBase();
|
||||||
Frame = RVal.getLValueFrame();
|
Frame = RVal.getLValueFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
|
||||||
|
if (const StringLiteral *S = dyn_cast<StringLiteral>(Base)) {
|
||||||
|
const SubobjectDesignator &Designator = LVal.Designator;
|
||||||
|
if (Designator.Invalid || Designator.Entries.size() != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
assert(Type->isIntegerType() && "string element not integer type");
|
||||||
|
uint64_t Index = Designator.Entries[0].Index;
|
||||||
|
if (Index > S->getLength())
|
||||||
|
return false;
|
||||||
|
APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
|
||||||
|
Type->isUnsignedIntegerType());
|
||||||
|
if (Index < S->getLength())
|
||||||
|
Value = S->getCodeUnit(Index);
|
||||||
|
RVal = CCValue(Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Support accessing subobjects of objects of literal types. A simple
|
||||||
|
// byte offset is insufficient for C++11 semantics: we need to know how the
|
||||||
|
// reference was formed (which union member was named, for instance).
|
||||||
|
|
||||||
|
// Beyond this point, we don't support accessing subobjects.
|
||||||
|
if (!LVal.Offset.isZero() ||
|
||||||
|
!Info.Ctx.hasSameUnqualifiedType(Type, Base->getType()))
|
||||||
|
return false;
|
||||||
|
|
||||||
// If this is a temporary expression with a nontrivial initializer, grab the
|
// If this is a temporary expression with a nontrivial initializer, grab the
|
||||||
// value from the relevant stack frame.
|
// value from the relevant stack frame.
|
||||||
if (Frame) {
|
if (Frame) {
|
||||||
|
@ -695,9 +814,7 @@ protected:
|
||||||
bool MakeTemporary(const Expr *Key, const Expr *Value, LValue &Result) {
|
bool MakeTemporary(const Expr *Key, const Expr *Value, LValue &Result) {
|
||||||
if (!Evaluate(Info.CurrentCall->Temporaries[Key], Info, Value))
|
if (!Evaluate(Info.CurrentCall->Temporaries[Key], Info, Value))
|
||||||
return false;
|
return false;
|
||||||
Result.Base = Key;
|
Result.setExpr(Key, Info.CurrentCall);
|
||||||
Result.Offset = CharUnits::Zero();
|
|
||||||
Result.Frame = Info.CurrentCall;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
@ -874,9 +991,7 @@ class LValueExprEvaluator
|
||||||
const Decl *PrevDecl;
|
const Decl *PrevDecl;
|
||||||
|
|
||||||
bool Success(const Expr *E) {
|
bool Success(const Expr *E) {
|
||||||
Result.Base = E;
|
Result.setExpr(E);
|
||||||
Result.Offset = CharUnits::Zero();
|
|
||||||
Result.Frame = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
@ -910,7 +1025,10 @@ public:
|
||||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||||
|
|
||||||
case CK_LValueBitCast:
|
case CK_LValueBitCast:
|
||||||
return Visit(E->getSubExpr());
|
if (!Visit(E->getSubExpr()))
|
||||||
|
return false;
|
||||||
|
Result.Designator.setInvalid();
|
||||||
|
return true;
|
||||||
|
|
||||||
// FIXME: Support CK_DerivedToBase and CK_UncheckedDerivedToBase.
|
// FIXME: Support CK_DerivedToBase and CK_UncheckedDerivedToBase.
|
||||||
// Reuse PointerExprEvaluator::VisitCastExpr for these.
|
// Reuse PointerExprEvaluator::VisitCastExpr for these.
|
||||||
|
@ -945,9 +1063,7 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
|
||||||
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
|
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
|
||||||
if (!VD->getType()->isReferenceType()) {
|
if (!VD->getType()->isReferenceType()) {
|
||||||
if (isa<ParmVarDecl>(VD)) {
|
if (isa<ParmVarDecl>(VD)) {
|
||||||
Result.Base = E;
|
Result.setExpr(E, Info.CurrentCall);
|
||||||
Result.Offset = CharUnits::Zero();
|
|
||||||
Result.Frame = Info.CurrentCall;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return Success(E);
|
return Success(E);
|
||||||
|
@ -1011,6 +1127,7 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
|
||||||
|
|
||||||
unsigned i = FD->getFieldIndex();
|
unsigned i = FD->getFieldIndex();
|
||||||
Result.Offset += Info.Ctx.toCharUnitsFromBits(RL.getFieldOffset(i));
|
Result.Offset += Info.Ctx.toCharUnitsFromBits(RL.getFieldOffset(i));
|
||||||
|
Result.Designator.addDecl(FD);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1025,9 +1142,13 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
|
||||||
APSInt Index;
|
APSInt Index;
|
||||||
if (!EvaluateInteger(E->getIdx(), Index, Info))
|
if (!EvaluateInteger(E->getIdx(), Index, Info))
|
||||||
return false;
|
return false;
|
||||||
|
uint64_t IndexValue
|
||||||
|
= Index.isSigned() ? static_cast<uint64_t>(Index.getSExtValue())
|
||||||
|
: Index.getZExtValue();
|
||||||
|
|
||||||
CharUnits ElementSize = Info.Ctx.getTypeSizeInChars(E->getType());
|
CharUnits ElementSize = Info.Ctx.getTypeSizeInChars(E->getType());
|
||||||
Result.Offset += Index.getSExtValue() * ElementSize;
|
Result.Offset += IndexValue * ElementSize;
|
||||||
|
Result.Designator.adjustIndex(IndexValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1045,9 +1166,7 @@ class PointerExprEvaluator
|
||||||
LValue &Result;
|
LValue &Result;
|
||||||
|
|
||||||
bool Success(const Expr *E) {
|
bool Success(const Expr *E) {
|
||||||
Result.Base = E;
|
Result.setExpr(E);
|
||||||
Result.Offset = CharUnits::Zero();
|
|
||||||
Result.Frame = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
@ -1110,9 +1229,10 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||||
int64_t AdditionalOffset
|
int64_t AdditionalOffset
|
||||||
= Offset.isSigned() ? Offset.getSExtValue()
|
= Offset.isSigned() ? Offset.getSExtValue()
|
||||||
: static_cast<int64_t>(Offset.getZExtValue());
|
: static_cast<int64_t>(Offset.getZExtValue());
|
||||||
|
if (E->getOpcode() == BO_Sub)
|
||||||
|
AdditionalOffset = -AdditionalOffset;
|
||||||
|
|
||||||
// Compute the new offset in the appropriate width.
|
// Compute the new offset in the appropriate width.
|
||||||
|
|
||||||
QualType PointeeType =
|
QualType PointeeType =
|
||||||
PExp->getType()->getAs<PointerType>()->getPointeeType();
|
PExp->getType()->getAs<PointerType>()->getPointeeType();
|
||||||
CharUnits SizeOfPointee;
|
CharUnits SizeOfPointee;
|
||||||
|
@ -1123,11 +1243,8 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||||
else
|
else
|
||||||
SizeOfPointee = Info.Ctx.getTypeSizeInChars(PointeeType);
|
SizeOfPointee = Info.Ctx.getTypeSizeInChars(PointeeType);
|
||||||
|
|
||||||
if (E->getOpcode() == BO_Add)
|
Result.Offset += AdditionalOffset * SizeOfPointee;
|
||||||
Result.Offset += AdditionalOffset * SizeOfPointee;
|
Result.Designator.adjustIndex(AdditionalOffset);
|
||||||
else
|
|
||||||
Result.Offset -= AdditionalOffset * SizeOfPointee;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1147,7 +1264,10 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
||||||
case CK_CPointerToObjCPointerCast:
|
case CK_CPointerToObjCPointerCast:
|
||||||
case CK_BlockPointerToObjCPointerCast:
|
case CK_BlockPointerToObjCPointerCast:
|
||||||
case CK_AnyPointerToBlockPointerCast:
|
case CK_AnyPointerToBlockPointerCast:
|
||||||
return Visit(SubExpr);
|
if (!Visit(SubExpr))
|
||||||
|
return false;
|
||||||
|
Result.Designator.setInvalid();
|
||||||
|
return true;
|
||||||
|
|
||||||
case CK_DerivedToBase:
|
case CK_DerivedToBase:
|
||||||
case CK_UncheckedDerivedToBase: {
|
case CK_UncheckedDerivedToBase: {
|
||||||
|
@ -1178,6 +1298,9 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
||||||
DerivedDecl = BaseDecl;
|
DerivedDecl = BaseDecl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
Result.Designator.setInvalid();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,6 +1318,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
||||||
Result.Base = 0;
|
Result.Base = 0;
|
||||||
Result.Offset = CharUnits::fromQuantity(N);
|
Result.Offset = CharUnits::fromQuantity(N);
|
||||||
Result.Frame = 0;
|
Result.Frame = 0;
|
||||||
|
Result.Designator.setInvalid();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// Cast is of an lvalue, no need to change value.
|
// Cast is of an lvalue, no need to change value.
|
||||||
|
@ -1206,7 +1330,11 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
||||||
// FIXME: Support array-to-pointer decay on array rvalues.
|
// FIXME: Support array-to-pointer decay on array rvalues.
|
||||||
if (!SubExpr->isGLValue())
|
if (!SubExpr->isGLValue())
|
||||||
return Error(E);
|
return Error(E);
|
||||||
return EvaluateLValue(SubExpr, Result, Info);
|
if (!EvaluateLValue(SubExpr, Result, Info))
|
||||||
|
return false;
|
||||||
|
// The result is a pointer to the first element of the array.
|
||||||
|
Result.Designator.addIndex(0);
|
||||||
|
return true;
|
||||||
|
|
||||||
case CK_FunctionToPointerDecay:
|
case CK_FunctionToPointerDecay:
|
||||||
return EvaluateLValue(SubExpr, Result, Info);
|
return EvaluateLValue(SubExpr, Result, Info);
|
||||||
|
|
|
@ -237,3 +237,29 @@ using check_value = int[same(n, n)];
|
||||||
using check_value = int[sameTemporary(9)];
|
using check_value = int[sameTemporary(9)];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace StringLiteral {
|
||||||
|
|
||||||
|
// FIXME: Refactor this once we support constexpr templates.
|
||||||
|
constexpr int MangleChars(const char *p) {
|
||||||
|
return *p + 3 * (*p ? MangleChars(p+1) : 0);
|
||||||
|
}
|
||||||
|
constexpr int MangleChars(const char16_t *p) {
|
||||||
|
return *p + 3 * (*p ? MangleChars(p+1) : 0);
|
||||||
|
}
|
||||||
|
constexpr int MangleChars(const char32_t *p) {
|
||||||
|
return *p + 3 * (*p ? MangleChars(p+1) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
using check_value = int[1768383];
|
||||||
|
using check_value = int[MangleChars("constexpr!")];
|
||||||
|
using check_value = int[MangleChars(u"constexpr!")];
|
||||||
|
using check_value = int[MangleChars(U"constexpr!")];
|
||||||
|
|
||||||
|
constexpr char c0 = "nought index"[0];
|
||||||
|
constexpr char c1 = "nice index"[10];
|
||||||
|
constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{indexes past the end}}
|
||||||
|
constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{indexes before the beginning}}
|
||||||
|
constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue