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.

llvm-svn: 141768
This commit is contained in:
Richard Smith 2011-10-12 05:08:15 +00:00
parent 918cea2c27
commit fd53aaf7e7
4 changed files with 29 additions and 1 deletions

View File

@ -1266,6 +1266,8 @@ def note_non_literal_user_provided_dtor : Note<
"%0 is not literal because it has a user-provided destructor">; "%0 is not literal because it has a user-provided destructor">;
def note_non_literal_nontrivial_dtor : Note< def note_non_literal_nontrivial_dtor : Note<
"%0 is not literal because it has a non-trivial destructor">; "%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++ // Objective-C++
def err_objc_decls_may_only_appear_in_global_scope : Error< def err_objc_decls_may_only_appear_in_global_scope : Error<

View File

@ -719,7 +719,11 @@ NotASpecialMember:;
} }
// Record if this field is the first non-literal field or base. // 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; data().HasNonLiteralTypeFieldsOrBases = true;
if (Field->hasInClassInitializer()) { if (Field->hasInClassInitializer()) {

View File

@ -4127,6 +4127,9 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
Diag((*I)->getLocation(), diag::note_non_literal_field) Diag((*I)->getLocation(), diag::note_non_literal_field)
<< RD << (*I) << (*I)->getType(); << RD << (*I) << (*I)->getType();
return true; return true;
} else if ((*I)->isMutable()) {
Diag((*I)->getLocation(), diag::note_non_literal_mutable_field) << RD;
return true;
} }
} }
} else if (!RD->hasTrivialDestructor()) { } else if (!RD->hasTrivialDestructor()) {

View File

@ -106,3 +106,22 @@ struct ArrBad {
S s[3]; // expected-note {{data member 's' of non-literal type 'S [3]'}} 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}} 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<int n> 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<m.n>().k; // expected-error {{not an integral constant expression}}
}
}