aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td2
-rw-r--r--lib/AST/DeclCXX.cpp6
-rw-r--r--lib/Sema/SemaType.cpp3
-rw-r--r--test/CXX/basic/basic.types/p10.cpp19
4 files changed, 29 insertions, 1 deletions
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<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}}
+ }
+}