diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e652ba1ecdc..5617f7e69121 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1266,6 +1266,8 @@ 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">; // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 8f61ea23cd44..f3da67c4ff81 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -719,7 +719,11 @@ NotASpecialMember:; } // Record if this field is the first non-literal field or base. - if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType()) + // 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()) data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer()) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 154b2a83f991..cee4ed67a1fe 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -4127,6 +4127,9 @@ 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/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp index 66189552350a..614272b91209 100644 --- a/clang/test/CXX/basic/basic.types/p10.cpp +++ b/clang/test/CXX/basic/basic.types/p10.cpp @@ -106,3 +106,22 @@ 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() { + // FIXME: correctly check whether the initializer is a constant expression. + constexpr MM m = { 0 }; // desired-error {{must be a constant expression}} + ++m.n; + return Id().k; // expected-error {{not an integral constant expression}} + } +}