Skip to content

Commit

Permalink
[ExprConstant] Allow constexpr ctor to modify non static data members
Browse files Browse the repository at this point in the history
Fixes PR19741.

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

llvm-svn: 314865
  • Loading branch information
epilk committed Oct 4, 2017
1 parent af65856 commit 4292549
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 3 deletions.
33 changes: 30 additions & 3 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(); }
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions clang/test/SemaCXX/constant-expression-cxx1y.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 4292549

Please sign in to comment.