forked from OSchip/llvm-project
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:
parent
918cea2c27
commit
fd53aaf7e7
|
@ -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<
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue