From: Richard Smith Date: Fri, 2 Dec 2011 00:30:33 +0000 (+0000) Subject: Fix wrong-code bug when a const automatic variable of struct type has both a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4bb6686274c292669bac9e658b3c210a317ee61a;p=clang Fix wrong-code bug when a const automatic variable of struct type has both a mutable member and a constant initializer. We'd previously promoted such variables to global constants, resulting in nasal demons if the mutable member was modified. This is only a temporary fix. The subtle interplay between isConstantInitializer and CGExprConstant is very bug-prone; there are some other issues in this area which I will be addressing in subsequent, more major reworking of this code. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145654 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 562cc5bb90..59efea0dae 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -739,11 +739,13 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { D.isNRVOVariable(); // If this value is a POD array or struct with a statically - // determinable constant initializer, there are optimizations we - // can do. - // TODO: we can potentially constant-evaluate non-POD structs and - // arrays as long as the initialization is trivial (e.g. if they - // have a non-trivial destructor, but not a non-trivial constructor). + // determinable constant initializer, there are optimizations we can do. + // + // TODO: we should constant-evaluate any variable of literal type + // as long as it is initialized by a constant expression. Currently, + // isConstantInitializer produces wrong answers for structs with + // reference or bitfield members, and a few other cases, and checking + // for POD-ness protects us from some of these. if (D.getInit() && (Ty->isArrayType() || Ty->isRecordType()) && (Ty.isPODType(getContext()) || @@ -751,9 +753,10 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { D.getInit()->isConstantInitializer(getContext(), false)) { // If the variable's a const type, and it's neither an NRVO - // candidate nor a __block variable, emit it as a global instead. + // candidate nor a __block variable and has no mutable members, + // emit it as a global instead. if (CGM.getCodeGenOpts().MergeAllConstants && Ty.isConstQualified() && - !NRVO && !isByRef) { + !NRVO && !isByRef && Ty->isLiteralType()) { EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage); emission.Address = 0; // signal this condition to later callbacks diff --git a/test/CodeGenCXX/const-init.cpp b/test/CodeGenCXX/const-init.cpp index 1fc3deba41..aa77d5c6c0 100644 --- a/test/CodeGenCXX/const-init.cpp +++ b/test/CodeGenCXX/const-init.cpp @@ -45,3 +45,14 @@ namespace test2 { // We don't expect to fold this in the frontend, but make sure it doesn't crash. // CHECK: @PR9558 = global float 0.000000e+0 float PR9558 = reinterpret_cast("asd"); + +// An initialized const automatic variable cannot be promoted to a constant +// global if it has a mutable member. +struct MutableMember { + mutable int n; +}; +int writeToMutable() { + // CHECK-NOT: {{.*}}MM{{.*}} = {{.*}}constant + const MutableMember MM = { 0 }; + return ++MM.n; +}