]> granicus.if.org Git - clang/commitdiff
constexpr: don't consider class types with mutable members to be literal types.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 12 Oct 2011 05:08:15 +0000 (05:08 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 12 Oct 2011 05:08:15 +0000 (05:08 +0000)
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

include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/DeclCXX.cpp
lib/Sema/SemaType.cpp
test/CXX/basic/basic.types/p10.cpp

index 3e652ba1ecdceaaf1f25fcf8ab117250bbab93db..5617f7e69121f2b382ef31a53f438246662312c2 100644 (file)
@@ -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<
index 8f61ea23cd44bdf3efee315fffe208d8ad3ac9bd..f3da67c4ff8175fa3897379424490ab73ced19f8 100644 (file)
@@ -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()) {
index 154b2a83f991f01dbfd693c45695cadbda667976..cee4ed67a1fed2381cc416807704d3baea5a438e 100644 (file)
@@ -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()) {
index 66189552350a2d630f1f7ebceb31dc71d6816a25..614272b91209c5e58cf92beb0032e317c4b458c6 100644 (file)
@@ -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}}
+  }
+}