From: Richard Smith Date: Thu, 9 Feb 2012 03:29:58 +0000 (+0000) Subject: CWG issue 1405: mutable members are allowed in literal types, but can't undergo X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b4e5e286a5cd156247720b1eb204abaa8e09568d;p=clang CWG issue 1405: mutable members are allowed in literal types, but can't undergo lvalue-to-rvalue conversions in constant expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150145 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 978df36f9f..80d9329172 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -85,6 +85,8 @@ def note_constexpr_ltor_volatile_type : Note< def note_constexpr_ltor_volatile_obj : Note< "read of volatile %select{temporary|object %1|member %1}0 is not allowed in " "a constant expression">; +def note_constexpr_ltor_mutable : Note< + "read of mutable member %0 is not allowed in a constant expression">; def note_constexpr_ltor_non_const_int : Note< "read of non-const variable %0 is not allowed in a constant expression">; def note_constexpr_ltor_non_constexpr : Note< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c5155c6e13..cbb4d177ef 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1425,8 +1425,6 @@ def note_non_literal_user_provided_dtor : Note< "%0 is not literal because it has a user-provided destructor">; def note_non_literal_nontrivial_dtor : Note< "%0 is not literal because it has a non-trivial destructor">; -def note_non_literal_mutable_field : Note< - "%0 is not literal because it has a mutable data member">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index d5221031a5..aa24e9b625 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -800,11 +800,7 @@ NotASpecialMember:; } // Record if this field is the first non-literal field or base. - // As a slight variation on the standard, we regard mutable members as being - // non-literal, since mutating a constexpr variable would break C++11 - // constant expression semantics. - if ((!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType()) || - Field->isMutable()) + if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType()) data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer()) { diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 48e0c6f7da..e43884e376 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1453,6 +1453,13 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, O = &O->getArrayFiller(); ObjType = CAT->getElementType(); } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { + if (Field->isMutable()) { + Info.Diag(E->getExprLoc(), diag::note_constexpr_ltor_mutable, 1) + << Field; + Info.Note(Field->getLocation(), diag::note_declared_at); + return false; + } + // Next subobject is a class, struct or union field. RecordDecl *RD = ObjType->castAs()->getDecl(); if (RD->isUnion()) { diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 82f7fed7ba..3d32091a4b 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -773,11 +773,15 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // emit it as a global instead. if (CGM.getCodeGenOpts().MergeAllConstants && Ty.isConstQualified() && !NRVO && !isByRef && Ty->isLiteralType()) { - EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage); - - emission.Address = 0; // signal this condition to later callbacks - assert(emission.wasEmittedAsGlobal()); - return emission; + CXXRecordDecl *RD = + Ty->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || !RD->hasMutableFields()) { + EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage); + + emission.Address = 0; // signal this condition to later callbacks + assert(emission.wasEmittedAsGlobal()); + return emission; + } } // Otherwise, tell the initialization code that we're in this case. diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index daf0b683b6..9fd611b5fe 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -4302,9 +4302,6 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, Diag((*I)->getLocation(), diag::note_non_literal_field) << RD << (*I) << (*I)->getType(); return true; - } else if ((*I)->isMutable()) { - Diag((*I)->getLocation(), diag::note_non_literal_mutable_field) << RD; - return true; } } } else if (!RD->hasTrivialDestructor()) { diff --git a/test/CXX/basic/basic.types/p10.cpp b/test/CXX/basic/basic.types/p10.cpp index 11c2f5fc49..def4dc95bb 100644 --- a/test/CXX/basic/basic.types/p10.cpp +++ b/test/CXX/basic/basic.types/p10.cpp @@ -107,21 +107,3 @@ struct ArrBad { S s[3]; // expected-note {{data member 's' of non-literal type 'S [3]'}} }; constexpr int f(ArrBad); // expected-error {{1st parameter type 'ArrBad' is not a literal type}} - - -// As a non-conforming tweak to the standard, we do not allow a literal type to -// have any mutable data members. -namespace MutableMembers { - struct MM { - mutable int n; // expected-note {{'MM' is not literal because it has a mutable data member}} - }; - constexpr int f(MM); // expected-error {{not a literal type}} - - // Here's one reason why allowing this would be a disaster... - template struct Id { int k = n; }; - int f() { - constexpr MM m = { 0 }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-literal type 'const MutableMembers::MM' cannot be used in a constant expression}} expected-note {{here}} - ++m.n; - return Id().k; // expected-error {{not a constant expression}} expected-note {{initializer of 'm' is not a constant expression}} - } -} diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index ccbc8c1abd..58f36fdf9c 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1121,6 +1121,30 @@ namespace IndirectField { static_assert(s2.f == 7, ""); } +// DR1405: don't allow reading mutable members in constant expressions. +namespace MutableMembers { + struct MM { + mutable int n; // expected-note 3{{declared here}} + } constexpr mm = { 4 }; + constexpr int mmn = mm.n; // expected-error {{constant expression}} expected-note {{read of mutable member 'n' is not allowed in a constant expression}} + int x = (mm.n = 1, 3); + constexpr int mmn2 = mm.n; // expected-error {{constant expression}} expected-note {{read of mutable member 'n' is not allowed in a constant expression}} + + // Here's one reason why allowing this would be a disaster... + template struct Id { int k = n; }; + int f() { + constexpr MM m = { 0 }; + ++m.n; + return Id().k; // expected-error {{not a constant expression}} expected-note {{read of mutable member 'n' is not allowed in a constant expression}} + } + + struct A { int n; }; + struct B { mutable A a; }; // expected-note {{here}} + struct C { B b; }; + constexpr C c[3] = {}; + constexpr int k = c[1].b.a.n; // expected-error {{constant expression}} expected-note {{mutable}} +} + namespace Fold { // This macro forces its argument to be constant-folded, even if it's not