[ExprConstant] Allow constexpr ctor to modify non static data members

Fixes PR19741.

Differential revision: https://reviews.llvm.org/D38483

llvm-svn: 314865
This commit is contained in:
Erik Pilkington 2017-10-04 00:18:55 +00:00
parent af65856eec
commit 4292549fb4
2 changed files with 63 additions and 3 deletions

View File

@ -573,6 +573,31 @@ namespace {
/// declaration whose initializer is being evaluated, if any.
APValue *EvaluatingDeclValue;
/// EvaluatingObject - Pair of the AST node that an lvalue represents and
/// the call index that that lvalue was allocated in.
typedef std::pair<APValue::LValueBase, unsigned> EvaluatingObject;
/// EvaluatingConstructors - Set of objects that are currently being
/// constructed.
llvm::DenseSet<EvaluatingObject> EvaluatingConstructors;
struct EvaluatingConstructorRAII {
EvalInfo &EI;
EvaluatingObject Object;
bool DidInsert;
EvaluatingConstructorRAII(EvalInfo &EI, EvaluatingObject Object)
: EI(EI), Object(Object) {
DidInsert = EI.EvaluatingConstructors.insert(Object).second;
}
~EvaluatingConstructorRAII() {
if (DidInsert) EI.EvaluatingConstructors.erase(Object);
}
};
bool isEvaluatingConstructor(APValue::LValueBase Decl, unsigned CallIndex) {
return EvaluatingConstructors.count(EvaluatingObject(Decl, CallIndex));
}
/// The current array initialization index, if we're performing array
/// initialization.
uint64_t ArrayInitIndex = -1;
@ -666,6 +691,7 @@ namespace {
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
EvaluatingDecl = Base;
EvaluatingDeclValue = &Value;
EvaluatingConstructors.insert({Base, 0});
}
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
@ -3098,10 +3124,9 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}
// During the construction of an object, it is not yet 'const'.
// FIXME: We don't set up EvaluatingDecl for local variables or temporaries,
// and this doesn't do quite the right thing for const subobjects of the
// FIXME: This doesn't do quite the right thing for const subobjects of the
// object under construction.
if (LVal.getLValueBase() == Info.EvaluatingDecl) {
if (Info.isEvaluatingConstructor(LVal.getLValueBase(), LVal.CallIndex)) {
BaseType = Info.Ctx.getCanonicalType(BaseType);
BaseType.removeLocalConst();
}
@ -4254,6 +4279,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
return false;
}
EvalInfo::EvaluatingConstructorRAII EvalObj(
Info, {This.getLValueBase(), This.CallIndex});
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues);
// FIXME: Creating an APValue just to hold a nonexistent return value is

View File

@ -988,3 +988,36 @@ constexpr void Void(int n) {
void();
}
constexpr int void_test = (Void(0), 1);
namespace PR19741 {
constexpr void addone(int &m) { m++; }
struct S {
int m = 0;
constexpr S() { addone(m); }
};
constexpr bool evalS() {
constexpr S s;
return s.m == 1;
}
static_assert(evalS(), "");
struct Nested {
struct First { int x = 42; };
union {
First first;
int second;
};
int x;
constexpr Nested(int x) : first(), x(x) { x = 4; }
constexpr Nested() : Nested(42) {
addone(first.x);
x = 3;
}
};
constexpr bool evalNested() {
constexpr Nested N;
return N.first.x == 43;
}
static_assert(evalNested(), "");
} // namespace PR19741