From: Richard Smith Date: Wed, 12 Oct 2011 05:08:15 +0000 (+0000) Subject: constexpr: don't consider class types with mutable members to be literal types. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5fa6a0422f12216d549d0f2991a29d5690634065;p=clang constexpr: don't consider class types with mutable members to be literal types. The standard doesn't allow this, but mutable constexpr variables break the semantics so badly that we can't reasonably accept them. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@141768 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3e652ba1ec..5617f7e691 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 8f61ea23cd..f3da67c4ff 100644 --- a/lib/AST/DeclCXX.cpp +++ b/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/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 154b2a83f9..cee4ed67a1 100644 --- a/lib/Sema/SemaType.cpp +++ b/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/test/CXX/basic/basic.types/p10.cpp b/test/CXX/basic/basic.types/p10.cpp index 6618955235..614272b912 100644 --- a/test/CXX/basic/basic.types/p10.cpp +++ b/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}} + } +}