aboutsummaryrefslogtreecommitdiff
path: root/lib/AST
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2013-04-26 14:36:30 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2013-04-26 14:36:30 +0000
commitbebf5b1bcfbf591dd3cd80c4aebd6486bb34f41c (patch)
tree986b8fc5afeef78149df9a104df35b2be3cf8e67 /lib/AST
parent4f1d1551ed48e2ac52f5614ba8f94e2931546b51 (diff)
C++1y: support simple variable assignments in constexpr functions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@180603 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/AST')
-rw-r--r--lib/AST/ExprConstant.cpp472
1 files changed, 376 insertions, 96 deletions
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 2a7e8a7d3b..ccad975796 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -286,7 +286,7 @@ namespace {
/// ParmBindings - Parameter bindings for this function call, indexed by
/// parameters' function scope indices.
- const APValue *Arguments;
+ APValue *Arguments;
// Note that we intentionally use std::map here so that references to
// values are stable.
@@ -297,7 +297,7 @@ namespace {
CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
- const APValue *Arguments);
+ APValue *Arguments);
~CallStackFrame();
};
@@ -597,7 +597,7 @@ void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info,
CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
- const APValue *Arguments)
+ APValue *Arguments)
: Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee),
Index(Info.NextCallIndex++), This(This), Arguments(Arguments) {
Info.CurrentCall = this;
@@ -1471,6 +1471,12 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
// If this is a local variable, dig out its value.
if (VD->hasLocalStorage() && Frame && Frame->Index > 1) {
+ // In C++1y, we can't safely read anything which might have been mutated
+ // when checking a potential constant expression.
+ if (Info.getLangOpts().CPlusPlus1y &&
+ Info.CheckingPotentialConstantExpression)
+ return false;
+
Result = Frame->Temporaries[VD];
// If we've carried on past an unevaluatable local variable initializer,
// we can't go any further. This can happen during potential constant
@@ -1542,15 +1548,15 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived,
llvm_unreachable("base class missing from derived class's bases list");
}
-/// Extract the value of a character from a string literal. CharType is used to
-/// determine the expected signedness of the result -- a string literal used to
-/// initialize an array of 'signed char' or 'unsigned char' might contain chars
-/// of the wrong signedness.
-static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
- uint64_t Index, QualType CharType) {
+/// Extract the value of a character from a string literal.
+static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
+ uint64_t Index) {
// FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
- const StringLiteral *S = dyn_cast<StringLiteral>(Lit);
- assert(S && "unexpected string literal expression kind");
+ const StringLiteral *S = cast<StringLiteral>(Lit);
+ const ConstantArrayType *CAT =
+ Info.Ctx.getAsConstantArrayType(S->getType());
+ assert(CAT && "string literal isn't an array");
+ QualType CharType = CAT->getElementType();
assert(CharType->isIntegerType() && "unexpected character type");
APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
@@ -1560,24 +1566,77 @@ static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
return Value;
}
-/// Extract the designated sub-object of an rvalue.
-static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
- APValue &Obj, QualType ObjType,
- const SubobjectDesignator &Sub, QualType SubType) {
+// Expand a string literal into an array of characters.
+static void expandStringLiteral(EvalInfo &Info, const Expr *Lit,
+ APValue &Result) {
+ const StringLiteral *S = cast<StringLiteral>(Lit);
+ const ConstantArrayType *CAT =
+ Info.Ctx.getAsConstantArrayType(S->getType());
+ assert(CAT && "string literal isn't an array");
+ QualType CharType = CAT->getElementType();
+ assert(CharType->isIntegerType() && "unexpected character type");
+
+ unsigned Elts = CAT->getSize().getZExtValue();
+ Result = APValue(APValue::UninitArray(),
+ std::min(S->getLength(), Elts), Elts);
+ APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
+ CharType->isUnsignedIntegerType());
+ if (Result.hasArrayFiller())
+ Result.getArrayFiller() = APValue(Value);
+ for (unsigned I = 0, N = Result.getArrayInitializedElts(); I != N; ++I) {
+ Value = S->getCodeUnit(I);
+ Result.getArrayInitializedElt(I) = APValue(Value);
+ }
+}
+
+// Expand an array so that it has more than Index filled elements.
+static void expandArray(APValue &Array, unsigned Index) {
+ unsigned Size = Array.getArraySize();
+ assert(Index < Size);
+
+ // Always at least double the number of elements for which we store a value.
+ unsigned OldElts = Array.getArrayInitializedElts();
+ unsigned NewElts = std::max(Index+1, OldElts * 2);
+ NewElts = std::min(Size, std::max(NewElts, 8u));
+
+ // Copy the data across.
+ APValue NewValue(APValue::UninitArray(), NewElts, Size);
+ for (unsigned I = 0; I != OldElts; ++I)
+ NewValue.getArrayInitializedElt(I).swap(Array.getArrayInitializedElt(I));
+ for (unsigned I = OldElts; I != NewElts; ++I)
+ NewValue.getArrayInitializedElt(I) = Array.getArrayFiller();
+ if (NewValue.hasArrayFiller())
+ NewValue.getArrayFiller() = Array.getArrayFiller();
+ Array.swap(NewValue);
+}
+
+/// Kinds of access we can perform on an object.
+enum AccessKinds {
+ AK_Read,
+ AK_Assign
+};
+
+/// Find the designated sub-object of an rvalue.
+template<typename SubobjectHandler>
+typename SubobjectHandler::result_type
+findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType,
+ const SubobjectDesignator &Sub, SubobjectHandler &handler) {
if (Sub.Invalid)
// A diagnostic will have already been produced.
- return false;
+ return handler.failed();
if (Sub.isOnePastTheEnd()) {
- Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
- (unsigned)diag::note_constexpr_read_past_end :
- (unsigned)diag::note_invalid_subexpr_in_const_expr);
- return false;
+ if (Info.getLangOpts().CPlusPlus11)
+ Info.Diag(E, diag::note_constexpr_access_past_end)
+ << handler.AccessKind;
+ else
+ Info.Diag(E);
+ return handler.failed();
}
if (Sub.Entries.empty())
- return true;
+ return handler.found(Obj, ObjType);
if (Info.CheckingPotentialConstantExpression && Obj.isUninit())
// This object might be initialized later.
- return false;
+ return handler.failed();
APValue *O = &Obj;
// Walk the designator's path to find the subobject.
@@ -1590,49 +1649,67 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
if (CAT->getSize().ule(Index)) {
// Note, it should not be possible to form a pointer with a valid
// designator which points more than one past the end of the array.
- Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
- (unsigned)diag::note_constexpr_read_past_end :
- (unsigned)diag::note_invalid_subexpr_in_const_expr);
- return false;
+ if (Info.getLangOpts().CPlusPlus11)
+ Info.Diag(E, diag::note_constexpr_access_past_end)
+ << handler.AccessKind;
+ else
+ Info.Diag(E);
+ return handler.failed();
}
+
+ ObjType = CAT->getElementType();
+
// An array object is represented as either an Array APValue or as an
// LValue which refers to a string literal.
if (O->isLValue()) {
assert(I == N - 1 && "extracting subobject of character?");
assert(!O->hasLValuePath() || O->getLValuePath().empty());
- Obj = APValue(ExtractStringLiteralCharacter(
- Info, O->getLValueBase().get<const Expr*>(), Index, SubType));
- return true;
- } else if (O->getArrayInitializedElts() > Index)
+ if (handler.AccessKind != AK_Read)
+ expandStringLiteral(Info, O->getLValueBase().get<const Expr *>(),
+ *O);
+ else
+ return handler.foundString(*O, ObjType, Index);
+ }
+
+ if (O->getArrayInitializedElts() > Index)
O = &O->getArrayInitializedElt(Index);
- else
+ else if (handler.AccessKind != AK_Read) {
+ expandArray(*O, Index);
+ O = &O->getArrayInitializedElt(Index);
+ } else
O = &O->getArrayFiller();
- ObjType = CAT->getElementType();
} else if (ObjType->isAnyComplexType()) {
// Next subobject is a complex number.
uint64_t Index = Sub.Entries[I].ArrayIndex;
if (Index > 1) {
- Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
- (unsigned)diag::note_constexpr_read_past_end :
- (unsigned)diag::note_invalid_subexpr_in_const_expr);
- return false;
+ if (Info.getLangOpts().CPlusPlus11)
+ Info.Diag(E, diag::note_constexpr_access_past_end)
+ << handler.AccessKind;
+ else
+ Info.Diag(E);
+ return handler.failed();
}
+
+ bool WasConstQualified = ObjType.isConstQualified();
+ ObjType = ObjType->castAs<ComplexType>()->getElementType();
+ if (WasConstQualified)
+ ObjType.addConst();
+
assert(I == N - 1 && "extracting subobject of scalar?");
if (O->isComplexInt()) {
- Obj = APValue(Index ? O->getComplexIntImag()
- : O->getComplexIntReal());
+ return handler.found(Index ? O->getComplexIntImag()
+ : O->getComplexIntReal(), ObjType);
} else {
assert(O->isComplexFloat());
- Obj = APValue(Index ? O->getComplexFloatImag()
- : O->getComplexFloatReal());
+ return handler.found(Index ? O->getComplexFloatImag()
+ : O->getComplexFloatReal(), ObjType);
}
- return true;
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
- if (Field->isMutable()) {
+ if (Field->isMutable() && handler.AccessKind == AK_Read) {
Info.Diag(E, diag::note_constexpr_ltor_mutable, 1)
<< Field;
Info.Note(Field->getLocation(), diag::note_declared_at);
- return false;
+ return handler.failed();
}
// Next subobject is a class, struct or union field.
@@ -1641,49 +1718,151 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
const FieldDecl *UnionField = O->getUnionField();
if (!UnionField ||
UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
- Info.Diag(E, diag::note_constexpr_read_inactive_union_member)
- << Field << !UnionField << UnionField;
- return false;
+ Info.Diag(E, diag::note_constexpr_access_inactive_union_member)
+ << handler.AccessKind << Field << !UnionField << UnionField;
+ return handler.failed();
}
O = &O->getUnionValue();
} else
O = &O->getStructField(Field->getFieldIndex());
+
+ bool WasConstQualified = ObjType.isConstQualified();
ObjType = Field->getType();
+ if (WasConstQualified && !Field->isMutable())
+ ObjType.addConst();
if (ObjType.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus) {
// FIXME: Include a description of the path to the volatile subobject.
- Info.Diag(E, diag::note_constexpr_ltor_volatile_obj, 1)
- << 2 << Field;
+ Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << handler.AccessKind << 2 << Field;
Info.Note(Field->getLocation(), diag::note_declared_at);
} else {
Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
}
- return false;
+ return handler.failed();
}
} else {
// Next subobject is a base class.
const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl();
const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]);
O = &O->getStructBase(getBaseIndex(Derived, Base));
+
+ bool WasConstQualified = ObjType.isConstQualified();
ObjType = Info.Ctx.getRecordType(Base);
+ if (WasConstQualified)
+ ObjType.addConst();
}
if (O->isUninit()) {
if (!Info.CheckingPotentialConstantExpression)
- Info.Diag(E, diag::note_constexpr_read_uninit);
+ Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind;
+ return handler.failed();
+ }
+ }
+
+ return handler.found(*O, ObjType);
+}
+
+struct ExtractSubobjectHandler {
+ EvalInfo &Info;
+ APValue &Obj;
+
+ static const AccessKinds AccessKind = AK_Read;
+
+ typedef bool result_type;
+ bool failed() { return false; }
+ bool found(APValue &Subobj, QualType SubobjType) {
+ if (&Subobj != &Obj) {
+ // We can't just swap Obj and Subobj here, because we'd create an object
+ // that has itself as a subobject. To avoid the leak, we ensure that Tmp
+ // ends up owning the original complete object, which is destroyed by
+ // Tmp's destructor.
+ APValue Tmp;
+ Subobj.swap(Tmp);
+ Obj.swap(Tmp);
+ }
+ return true;
+ }
+ bool found(APSInt &Value, QualType SubobjType) {
+ Obj = APValue(Value);
+ return true;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ Obj = APValue(Value);
+ return true;
+ }
+ bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
+ Obj = APValue(extractStringLiteralCharacter(
+ Info, Subobj.getLValueBase().get<const Expr *>(), Character));
+ return true;
+ }
+};
+const AccessKinds ExtractSubobjectHandler::AccessKind;
+
+/// Extract the designated sub-object of an rvalue.
+static bool extractSubobject(EvalInfo &Info, const Expr *E,
+ APValue &Obj, QualType ObjType,
+ const SubobjectDesignator &Sub) {
+ ExtractSubobjectHandler Handler = { Info, Obj };
+ return findSubobject(Info, E, Obj, ObjType, Sub, Handler);
+}
+
+struct ModifySubobjectHandler {
+ EvalInfo &Info;
+ APValue &NewVal;
+ const Expr *E;
+
+ typedef bool result_type;
+ static const AccessKinds AccessKind = AK_Assign;
+
+ bool checkConst(QualType QT) {
+ // Assigning to a const object has undefined behavior.
+ if (QT.isConstQualified()) {
+ Info.Diag(E, diag::note_constexpr_modify_const_type) << QT;
return false;
}
+ return true;
}
- // This may look super-stupid, but it serves an important purpose: if we just
- // swapped Obj and *O, we'd create an object which had itself as a subobject.
- // To avoid the leak, we ensure that Tmp ends up owning the original complete
- // object, which is destroyed by Tmp's destructor.
- APValue Tmp;
- O->swap(Tmp);
- Obj.swap(Tmp);
- return true;
+ bool failed() { return false; }
+ bool found(APValue &Subobj, QualType SubobjType) {
+ if (!checkConst(SubobjType))
+ return false;
+ // We've been given ownership of NewVal, so just swap it in.
+ Subobj.swap(NewVal);
+ return true;
+ }
+ bool found(APSInt &Value, QualType SubobjType) {
+ if (!checkConst(SubobjType))
+ return false;
+ if (!NewVal.isInt()) {
+ // Maybe trying to write a cast pointer value into a complex?
+ Info.Diag(E);
+ return false;
+ }
+ Value = NewVal.getInt();
+ return true;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ if (!checkConst(SubobjType))
+ return false;
+ Value = NewVal.getFloat();
+ return true;
+ }
+ bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
+ llvm_unreachable("shouldn't encounter string elements with ExpandArrays");
+ }
+};
+const AccessKinds ModifySubobjectHandler::AccessKind;
+
+/// Update the designated sub-object of an rvalue to the given value.
+static bool modifySubobject(EvalInfo &Info, const Expr *E,
+ APValue &Obj, QualType ObjType,
+ const SubobjectDesignator &Sub,
+ APValue &NewVal) {
+ ModifySubobjectHandler Handler = { Info, NewVal, E };
+ return findSubobject(Info, E, Obj, ObjType, Sub, Handler);
}
/// Find the position where two subobject designators diverge, or equivalently
@@ -1744,14 +1923,14 @@ static bool AreElementsOfSameArray(QualType ObjType,
}
/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on
-/// the given lvalue. This can also be used for 'lvalue-to-lvalue' conversions
+/// the given glvalue. This can also be used for 'lvalue-to-lvalue' conversions
/// for looking up the glvalue referred to by an entity of reference type.
///
/// \param Info - Information about the ongoing evaluation.
/// \param Conv - The expression for which we are performing the conversion.
/// Used for diagnostics.
-/// \param Type - The type we expect this conversion to produce, before
-/// stripping cv-qualifiers in the case of a non-clas type.
+/// \param Type - The type of the glvalue (before stripping cv-qualifiers in the
+/// case of a non-class type).
/// \param LVal - The glvalue on which we are attempting to perform this action.
/// \param RVal - The produced value will be placed here.
static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
@@ -1764,8 +1943,7 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
if (!LVal.Base) {
- // FIXME: Indirection through a null pointer deserves a specific diagnostic.
- Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr);
+ Info.Diag(Conv, diag::note_constexpr_access_null) << AK_Read;
return false;
}
@@ -1773,7 +1951,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
if (LVal.CallIndex) {
Frame = Info.getCallFrame(LVal.CallIndex);
if (!Frame) {
- Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base;
+ Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1)
+ << AK_Read << !Base;
NoteLValueLocation(Info, LVal.Base);
return false;
}
@@ -1785,7 +1964,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
// semantics.
if (Type.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus)
- Info.Diag(Conv, diag::note_constexpr_ltor_volatile_type) << Type;
+ Info.Diag(Conv, diag::note_constexpr_access_volatile_type)
+ << AK_Read << Type;
else
Info.Diag(Conv);
return false;
@@ -1812,7 +1992,8 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
QualType VT = VD->getType();
if (VT.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus) {
- Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 1 << VD;
+ Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1)
+ << AK_Read << 1 << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
Info.Diag(Conv);
@@ -1857,37 +2038,15 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
}
}
- if (!EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal))
- return false;
-
- if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue())
- return ExtractSubobject(Info, Conv, RVal, VT, LVal.Designator, Type);
-
- // The declaration was initialized by an lvalue, with no lvalue-to-rvalue
- // conversion. This happens when the declaration and the lvalue should be
- // considered synonymous, for instance when initializing an array of char
- // from a string literal. Continue as if the initializer lvalue was the
- // value we were originally given.
- assert(RVal.getLValueOffset().isZero() &&
- "offset for lvalue init of non-reference");
- Base = RVal.getLValueBase().get<const Expr*>();
-
- if (unsigned CallIndex = RVal.getLValueCallIndex()) {
- Frame = Info.getCallFrame(CallIndex);
- if (!Frame) {
- Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base;
- NoteLValueLocation(Info, RVal.getLValueBase());
- return false;
- }
- } else {
- Frame = 0;
- }
+ return EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal) &&
+ extractSubobject(Info, Conv, RVal, VT, LVal.Designator);
}
// Volatile temporary objects cannot be read in constant expressions.
if (Base->getType().isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus) {
- Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 0;
+ Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1)
+ << AK_Read << 0;
Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
} else {
Info.Diag(Conv);
@@ -1896,6 +2055,12 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
}
if (Frame) {
+ // In C++1y, we can't safely read anything which might have been mutated
+ // when checking a potential constant expression.
+ if (Info.getLangOpts().CPlusPlus1y &&
+ Info.CheckingPotentialConstantExpression)
+ return false;
+
// If this is a temporary expression with a nontrivial initializer, grab the
// value from the relevant stack frame.
RVal = Frame->Temporaries[Base];
@@ -1908,6 +2073,11 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
if (!Evaluate(RVal, Info, CLE->getInitializer()))
return false;
} else if (isa<StringLiteral>(Base)) {
+ if (Info.getLangOpts().CPlusPlus1y &&
+ Info.CheckingPotentialConstantExpression &&
+ !Base->getType().isConstQualified())
+ return false;
+
// We represent a string literal array as an lvalue pointing at the
// corresponding expression, rather than building an array of chars.
// FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
@@ -1917,8 +2087,105 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
return false;
}
- return ExtractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator,
- Type);
+ return extractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator);
+}
+
+/// Perform an assignment of Val to LVal. Takes ownership of Val.
+// FIXME: Factor out duplication with HandleLValueToRValueConversion.
+static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
+ QualType LValType, APValue &Val) {
+ if (!Info.getLangOpts().CPlusPlus1y) {
+ Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ }
+
+ if (LVal.Designator.Invalid)
+ // A diagnostic will have already been produced.
+ return false;
+
+ if (Info.CheckingPotentialConstantExpression)
+ return false;
+
+ const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
+
+ if (!LVal.Base) {
+ Info.Diag(E, diag::note_constexpr_access_null) << AK_Assign;
+ return false;
+ }
+
+ if (!LVal.CallIndex) {
+ Info.Diag(E, diag::note_constexpr_modify_global);
+ return false;
+ }
+
+ CallStackFrame *Frame = Info.getCallFrame(LVal.CallIndex);
+ if (!Frame) {
+ Info.Diag(E, diag::note_constexpr_lifetime_ended, 1)
+ << AK_Assign << !Base;
+ NoteLValueLocation(Info, LVal.Base);
+ return false;
+ }
+
+ if (LValType.isVolatileQualified()) {
+ if (Info.getLangOpts().CPlusPlus)
+ Info.Diag(E, diag::note_constexpr_access_volatile_type)
+ << AK_Assign << LValType;
+ else
+ Info.Diag(E);
+ return false;
+ }
+
+ // Compute value storage location and type of base object.
+ APValue *BaseVal = 0;
+ QualType BaseType;
+
+ if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
+ const VarDecl *VD = dyn_cast<VarDecl>(D);
+ if (VD) {
+ if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
+ VD = VDef;
+ }
+ if (!VD || VD->isInvalidDecl()) {
+ Info.Diag(E);
+ return false;
+ }
+
+ // Modifications to volatile-qualified objects are not allowed.
+ BaseType = VD->getType();
+ if (BaseType.isVolatileQualified()) {
+ Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK_Assign << 1 << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ return false;
+ }
+
+ if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
+ if (!Frame || !Frame->Arguments) {
+ Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ }
+ BaseVal = &Frame->Arguments[PVD->getFunctionScopeIndex()];
+ } else if (VD->hasLocalStorage() && Frame && Frame->Index > 1) {
+ BaseVal = &Frame->Temporaries[VD];
+ } else {
+ // FIXME: Can this happen?
+ Info.Diag(E);
+ return false;
+ }
+ } else {
+ BaseType = Base->getType();
+ BaseVal = &Frame->Temporaries[Base];
+
+ // Volatile temporary objects cannot be modified in constant expressions.
+ if (BaseType.isVolatileQualified()) {
+ Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK_Assign << 0;
+ Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
+ return false;
+ }
+ }
+
+ return modifySubobject(Info, E, *BaseVal, BaseType, LVal.Designator, Val);
}
/// Build an lvalue for the object argument of a member function call.
@@ -2264,8 +2531,11 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
EvalStmtResult ESR = EvaluateStmt(Result, Info, Body);
- if (ESR == ESR_Succeeded)
+ if (ESR == ESR_Succeeded) {
+ if (Callee->getResultType()->isVoidType())
+ return true;
Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return);
+ }
return ESR == ESR_Returned;
}
@@ -2715,7 +2985,7 @@ public:
SubobjectDesignator Designator(BaseTy);
Designator.addDeclUnchecked(FD);
- return ExtractSubobject(Info, E, Val, BaseTy, Designator, E->getType()) &&
+ return extractSubobject(Info, E, Val, BaseTy, Designator) &&
DerivedSuccess(Val, E);
}
@@ -2829,6 +3099,16 @@ public:
case BO_PtrMemD:
case BO_PtrMemI:
return HandleMemberPointerAccess(this->Info, E, Result);
+
+ case BO_Assign: {
+ if (!this->Visit(E->getLHS()))
+ return false;
+ APValue NewVal;
+ if (!Evaluate(NewVal, this->Info, E->getRHS()))
+ return false;
+ return HandleAssignment(this->Info, E, Result, E->getLHS()->getType(),
+ NewVal);
+ }
}
}