aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2013-05-05 21:17:10 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2013-05-05 21:17:10 +0000
commitb476a14e9b35ef2448b42b033e1f0cceaa3f2778 (patch)
tree0a6004bb4d1ab08ea9f63f829ed3c4f25f37e70c
parent62ed889272d7e9da8e367d8682fdcdeeec0d83b5 (diff)
Factor out duplication between lvalue-to-rvalue conversions and variable
assignments in constant expressions. No significant functionality changes (slight improvement to potential constant expression checking). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181170 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/AST/ExprConstant.cpp482
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp10
-rw-r--r--test/SemaCXX/constant-expression-cxx1y.cpp5
3 files changed, 255 insertions, 242 deletions
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index d1500d490c..13fd9df266 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -1451,9 +1451,16 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
}
/// Try to evaluate the initializer for a variable declaration.
-static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
- const VarDecl *VD,
- CallStackFrame *Frame, APValue &Result) {
+///
+/// \param Info Information about the ongoing evaluation.
+/// \param E An expression to be used when printing diagnostics.
+/// \param VD The variable whose initializer should be obtained.
+/// \param Frame The frame in which the variable was created. Must be null
+/// if this variable is not local to the evaluation.
+/// \param Result Filled in with a pointer to the value of the variable.
+static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
+ const VarDecl *VD, CallStackFrame *Frame,
+ APValue *&Result) {
// If this is a parameter to an active constexpr function call, perform
// argument substitution.
if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
@@ -1465,23 +1472,17 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
return false;
}
- Result = Frame->Arguments[PVD->getFunctionScopeIndex()];
+ Result = &Frame->Arguments[PVD->getFunctionScopeIndex()];
return true;
}
// 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 (Frame) {
+ 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
// expression checking.
- return !Result.isUninit();
+ return !Result->isUninit();
}
// Dig out the initializer, and use the declaration which it's attached to.
@@ -1497,8 +1498,8 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
// If we're currently evaluating the initializer of this declaration, use that
// in-flight value.
if (Info.EvaluatingDecl == VD) {
- Result = *Info.EvaluatingDeclValue;
- return !Result.isUninit();
+ Result = Info.EvaluatingDeclValue;
+ return !Result->isUninit();
}
// Never evaluate the initializer of a weak variable. We can't be sure that
@@ -1524,7 +1525,7 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
Info.addNotes(Notes);
}
- Result = *VD->getEvaluatedValue();
+ Result = VD->getEvaluatedValue();
return true;
}
@@ -1616,10 +1617,27 @@ enum AccessKinds {
AK_Assign
};
+/// A handle to a complete object (an object that is not a subobject of
+/// another object).
+struct CompleteObject {
+ /// The value of the complete object.
+ APValue *Value;
+ /// The type of the complete object.
+ QualType Type;
+
+ CompleteObject() : Value(0) {}
+ CompleteObject(APValue *Value, QualType Type)
+ : Value(Value), Type(Type) {
+ assert(Value && "missing value for complete object");
+ }
+
+ operator bool() const { return Value; }
+};
+
/// 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,
+findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
const SubobjectDesignator &Sub, SubobjectHandler &handler) {
if (Sub.Invalid)
// A diagnostic will have already been produced.
@@ -1633,12 +1651,13 @@ findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType,
return handler.failed();
}
if (Sub.Entries.empty())
- return handler.found(Obj, ObjType);
- if (Info.CheckingPotentialConstantExpression && Obj.isUninit())
+ return handler.found(*Obj.Value, Obj.Type);
+ if (Info.CheckingPotentialConstantExpression && Obj.Value->isUninit())
// This object might be initialized later.
return handler.failed();
- APValue *O = &Obj;
+ APValue *O = Obj.Value;
+ QualType ObjType = Obj.Type;
// Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) {
if (ObjType->isArrayType()) {
@@ -1767,48 +1786,44 @@ findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType,
namespace {
struct ExtractSubobjectHandler {
EvalInfo &Info;
- APValue &Obj;
+ APValue &Result;
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);
- }
+ Result = Subobj;
return true;
}
bool found(APSInt &Value, QualType SubobjType) {
- Obj = APValue(Value);
+ Result = APValue(Value);
return true;
}
bool found(APFloat &Value, QualType SubobjType) {
- Obj = APValue(Value);
+ Result = APValue(Value);
return true;
}
bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
- Obj = APValue(extractStringLiteralCharacter(
+ Result = APValue(extractStringLiteralCharacter(
Info, Subobj.getLValueBase().get<const Expr *>(), Character));
return true;
}
};
+} // end anonymous namespace
+
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);
+ const CompleteObject &Obj,
+ const SubobjectDesignator &Sub,
+ APValue &Result) {
+ ExtractSubobjectHandler Handler = { Info, Result };
+ return findSubobject(Info, E, Obj, Sub, Handler);
}
+namespace {
struct ModifySubobjectHandler {
EvalInfo &Info;
APValue &NewVal;
@@ -1855,16 +1870,17 @@ struct ModifySubobjectHandler {
llvm_unreachable("shouldn't encounter string elements with ExpandArrays");
}
};
-const AccessKinds ModifySubobjectHandler::AccessKind;
} // end anonymous namespace
+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 CompleteObject &Obj,
const SubobjectDesignator &Sub,
APValue &NewVal) {
ModifySubobjectHandler Handler = { Info, NewVal, E };
- return findSubobject(Info, E, Obj, ObjType, Sub, Handler);
+ return findSubobject(Info, E, Obj, Sub, Handler);
}
/// Find the position where two subobject designators diverge, or equivalently
@@ -1924,60 +1940,52 @@ static bool AreElementsOfSameArray(QualType ObjType,
return CommonLength >= A.Entries.size() - IsArray;
}
-/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on
-/// 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 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,
- QualType Type,
- const LValue &LVal, APValue &RVal) {
- if (LVal.Designator.Invalid)
- // A diagnostic will have already been produced.
- return false;
-
- const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
-
+/// Find the complete object to which an LValue refers.
+CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK,
+ const LValue &LVal, QualType LValType) {
if (!LVal.Base) {
- Info.Diag(Conv, diag::note_constexpr_access_null) << AK_Read;
- return false;
+ Info.Diag(E, diag::note_constexpr_access_null) << AK;
+ return CompleteObject();
}
CallStackFrame *Frame = 0;
if (LVal.CallIndex) {
Frame = Info.getCallFrame(LVal.CallIndex);
if (!Frame) {
- Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1)
- << AK_Read << !Base;
+ Info.Diag(E, diag::note_constexpr_lifetime_ended, 1)
+ << AK << LVal.Base.is<const ValueDecl*>();
NoteLValueLocation(Info, LVal.Base);
- return false;
+ return CompleteObject();
}
+ } else if (AK != AK_Read) {
+ Info.Diag(E, diag::note_constexpr_modify_global);
+ return CompleteObject();
}
// C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type
// is not a constant expression (even if the object is non-volatile). We also
// apply this rule to C++98, in order to conform to the expected 'volatile'
// semantics.
- if (Type.isVolatileQualified()) {
+ if (LValType.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus)
- Info.Diag(Conv, diag::note_constexpr_access_volatile_type)
- << AK_Read << Type;
+ Info.Diag(E, diag::note_constexpr_access_volatile_type)
+ << AK << LValType;
else
- Info.Diag(Conv);
- return false;
+ Info.Diag(E);
+ return CompleteObject();
}
+ // Compute value storage location and type of base object.
+ APValue *BaseVal = 0;
+ QualType BaseType;
+
if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
// In C++11, constexpr, non-volatile variables initialized with constant
// expressions are constant expressions too. Inside constexpr functions,
// parameters are constant expressions even if they're non-const.
+ // In C++1y, objects local to a constant expression (those with a Frame) are
+ // both readable and writable inside constant expressions.
// In C, such things can also be folded, although they are not ICEs.
const VarDecl *VD = dyn_cast<VarDecl>(D);
if (VD) {
@@ -1985,209 +1993,158 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
VD = VDef;
}
if (!VD || VD->isInvalidDecl()) {
- Info.Diag(Conv);
- return false;
+ Info.Diag(E);
+ return CompleteObject();
}
- // DR1313: If the object is volatile-qualified but the glvalue was not,
- // behavior is undefined so the result is not a constant expression.
- QualType VT = VD->getType();
- if (VT.isVolatileQualified()) {
+ // Accesses of volatile-qualified objects are not allowed.
+ BaseType = VD->getType();
+ if (BaseType.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus) {
- Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1)
- << AK_Read << 1 << VD;
+ Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK << 1 << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
- Info.Diag(Conv);
+ Info.Diag(E);
}
- return false;
+ return CompleteObject();
}
// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const.
- if (LVal.CallIndex <= 1 || !VD->hasLocalStorage()) {
+ if (!Frame) {
+ assert(AK == AK_Read && "can't modify non-local");
if (VD->isConstexpr()) {
// OK, we can read this variable.
- } else if (VT->isIntegralOrEnumerationType()) {
- if (!VT.isConstQualified()) {
+ } else if (BaseType->isIntegralOrEnumerationType()) {
+ if (!BaseType.isConstQualified()) {
if (Info.getLangOpts().CPlusPlus) {
- Info.Diag(Conv, diag::note_constexpr_ltor_non_const_int, 1) << VD;
+ Info.Diag(E, diag::note_constexpr_ltor_non_const_int, 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
- Info.Diag(Conv);
+ Info.Diag(E);
}
- return false;
+ return CompleteObject();
}
- } else if (VT->isFloatingType() && VT.isConstQualified()) {
+ } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) {
// We support folding of const floating-point types, in order to make
// static const data members of such types (supported as an extension)
// more useful.
if (Info.getLangOpts().CPlusPlus11) {
- Info.CCEDiag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
+ Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
- Info.CCEDiag(Conv);
+ Info.CCEDiag(E);
}
} else {
// FIXME: Allow folding of values of any literal type in all languages.
if (Info.getLangOpts().CPlusPlus11) {
- Info.Diag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
+ Info.Diag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
- Info.Diag(Conv);
+ Info.Diag(E);
}
- return false;
+ return CompleteObject();
}
}
- return EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal) &&
- extractSubobject(Info, Conv, RVal, VT, LVal.Designator);
- }
+ if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal))
+ return CompleteObject();
+ } else {
+ const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
- // Volatile temporary objects cannot be read in constant expressions.
- if (Base->getType().isVolatileQualified()) {
- if (Info.getLangOpts().CPlusPlus) {
- 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);
+ if (!Frame) {
+ Info.Diag(E);
+ return CompleteObject();
}
- return false;
- }
- 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];
- } else if (const CompoundLiteralExpr *CLE
- = dyn_cast<CompoundLiteralExpr>(Base)) {
- // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
- // initializer until now for such expressions. Such an expression can't be
- // an ICE in C, so this only matters for fold.
- assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?");
- if (!Evaluate(RVal, Info, CLE->getInitializer()))
- return false;
- } else if (isa<StringLiteral>(Base)) {
- if (Info.getLangOpts().CPlusPlus1y &&
- Info.CheckingPotentialConstantExpression &&
- !Base->getType().isConstQualified())
- return false;
+ BaseType = Base->getType();
+ BaseVal = &Frame->Temporaries[Base];
- // 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
- RVal = APValue(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0);
- } else {
- Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr);
- return false;
+ // Volatile temporary objects cannot be accessed in constant expressions.
+ if (BaseType.isVolatileQualified()) {
+ if (Info.getLangOpts().CPlusPlus) {
+ Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK << 0;
+ Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
+ } else {
+ Info.Diag(E);
+ }
+ return CompleteObject();
+ }
}
- return extractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator);
-}
+ // In C++1y, we can't safely access any mutable state when checking a
+ // potential constant expression.
+ if (Frame && Info.getLangOpts().CPlusPlus1y &&
+ Info.CheckingPotentialConstantExpression)
+ return CompleteObject();
-/// 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;
- }
+ return CompleteObject(BaseVal, BaseType);
+}
+/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on
+/// 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 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,
+ QualType Type,
+ const LValue &LVal, APValue &RVal) {
if (LVal.Designator.Invalid)
- // A diagnostic will have already been produced.
- return false;
-
- if (Info.CheckingPotentialConstantExpression)
return false;
+ // Check for special cases where there is no existing APValue to look at.
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.Designator.Invalid && Base && !LVal.CallIndex &&
+ !Type.isVolatileQualified()) {
+ if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
+ // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
+ // initializer until now for such expressions. Such an expression can't be
+ // an ICE in C, so this only matters for fold.
+ assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?");
+ if (Type.isVolatileQualified()) {
+ Info.Diag(Conv);
+ return false;
+ }
+ APValue Lit;
+ if (!Evaluate(Lit, Info, CLE->getInitializer()))
+ return false;
+ CompleteObject LitObj(&Lit, Base->getType());
+ return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
+ } else if (isa<StringLiteral>(Base)) {
+ // 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
+ APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0);
+ CompleteObject StrObj(&Str, Base->getType());
+ return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal);
+ }
}
- if (!LVal.CallIndex) {
- Info.Diag(E, diag::note_constexpr_modify_global);
- return false;
- }
+ CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type);
+ return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal);
+}
- CallStackFrame *Frame = Info.getCallFrame(LVal.CallIndex);
- if (!Frame) {
- Info.Diag(E, diag::note_constexpr_lifetime_ended, 1)
- << AK_Assign << !Base;
- NoteLValueLocation(Info, LVal.Base);
+/// Perform an assignment of Val to LVal. Takes ownership of Val.
+static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
+ QualType LValType, APValue &Val) {
+ if (LVal.Designator.Invalid)
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);
+ if (!Info.getLangOpts().CPlusPlus1y) {
+ 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);
+ CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType);
+ return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val);
}
/// Build an lvalue for the object argument of a member function call.
@@ -2984,11 +2941,13 @@ public:
assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
+ CompleteObject Obj(&Val, BaseTy);
SubobjectDesignator Designator(BaseTy);
Designator.addDeclUnchecked(FD);
- return extractSubobject(Info, E, Val, BaseTy, Designator) &&
- DerivedSuccess(Val, E);
+ APValue Result;
+ return extractSubobject(Info, E, Obj, Designator, Result) &&
+ DerivedSuccess(Result, E);
}
RetTy VisitCastExpr(const CastExpr *E) {
@@ -3101,16 +3060,6 @@ 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);
- }
}
}
@@ -3178,6 +3127,7 @@ public:
LValueExprEvaluatorBaseTy(Info, Result) {}
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
+ bool VisitIncDec(const UnaryOperator *UO);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); }
@@ -3192,6 +3142,10 @@ public:
bool VisitUnaryDeref(const UnaryOperator *E);
bool VisitUnaryReal(const UnaryOperator *E);
bool VisitUnaryImag(const UnaryOperator *E);
+ bool VisitUnaryPreInc(const UnaryOperator *UO) { return VisitIncDec(UO); }
+ bool VisitUnaryPreDec(const UnaryOperator *UO) { return VisitIncDec(UO); }
+ bool VisitBinAssign(const BinaryOperator *BO);
+ bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO);
bool VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
@@ -3233,18 +3187,22 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
}
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
+ CallStackFrame *Frame = 0;
+ if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1)
+ Frame = Info.CurrentCall;
+
if (!VD->getType()->isReferenceType()) {
- if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) {
- Result.set(VD, Info.CurrentCall->Index);
+ if (Frame) {
+ Result.set(VD, Frame->Index);
return true;
}
return Success(VD);
}
- APValue V;
- if (!EvaluateVarDeclInit(Info, E, VD, Info.CurrentCall, V))
+ APValue *V;
+ if (!evaluateVarDeclInit(Info, E, VD, Frame, V))
return false;
- return Success(V, E);
+ return Success(*V, E);
}
bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
@@ -3277,7 +3235,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
bool LValueExprEvaluator::VisitCXXUuidofExpr(const CXXUuidofExpr *E) {
return Success(E);
-}
+}
bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
// Handle static data members.
@@ -3338,6 +3296,54 @@ bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) {
return true;
}
+bool LValueExprEvaluator::VisitIncDec(const UnaryOperator *UO) {
+ if (!Info.getLangOpts().CPlusPlus1y)
+ return Error(UO);
+
+ if (!this->Visit(UO->getSubExpr()))
+ return false;
+
+ // FIXME:
+ //return handleIncDec(
+ // this->Info, CAO, Result, UO->getSubExpr()->getType(),
+ // UO->isIncrementOp());
+ // (Watch out for promotions: ++short can't overflow, ++bool is always true).
+ return Error(UO);
+}
+
+bool LValueExprEvaluator::VisitCompoundAssignOperator(
+ const CompoundAssignOperator *CAO) {
+ if (!Info.getLangOpts().CPlusPlus1y)
+ return Error(CAO);
+
+ // The overall lvalue result is the result of evaluating the LHS.
+ if (!this->Visit(CAO->getLHS()))
+ return false;
+
+ APValue RHS;
+ if (!Evaluate(RHS, this->Info, CAO->getRHS()))
+ return false;
+
+ // FIXME:
+ //return handleCompoundAssignment(
+ // this->Info, CAO,
+ // Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(),
+ // RHS, CAO->getRHS()->getType(),
+ // CAO->getOpForCompoundAssignment(CAO->getOpcode()),
+ // CAO->getComputationResultType());
+ return Error(CAO);
+}
+
+bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) {
+ 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);
+}
+
//===----------------------------------------------------------------------===//
// Pointer Evaluation
//===----------------------------------------------------------------------===//
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
index 9361642326..e12011d19c 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
@@ -274,11 +274,13 @@ namespace std_example {
int a; // expected-error {{must be initialized}}
return a;
}
- // FIXME: Once we support variable mutation, this can produce a
- // constant expression.
- constexpr int prev(int x) { // expected-error {{never produces a constant expression}}
- return --x; // expected-note {{subexpression}}
+ constexpr int prev(int x) {
+ return --x;
}
+#if 1 // FIXME: !defined CXX1Y
+ // expected-error@-4 {{never produces a constant expression}}
+ // expected-note@-4 {{subexpression}}
+#endif
constexpr int g(int x, int n) {
int r = 1;
while (--n > 0) r *= x;
diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp
index 543c7cffdb..62739ee8c1 100644
--- a/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -232,6 +232,11 @@ namespace potential_const_expr {
int z = 0;
return 100 / (set(z), 0); // expected-note {{division by zero}}
}
+ int n; // expected-note {{declared here}}
+ constexpr int ref() { // expected-error {{never produces a constant expression}}
+ int &r = n;
+ return r; // expected-note {{read of non-const variable 'n'}}
+ }
}
namespace subobject {