From fd53aaf7e7d09478d966fcf71a4cddd54df54165 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 12 Oct 2011 05:08:15 +0000 Subject: [PATCH] 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 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 ++ clang/lib/AST/DeclCXX.cpp | 6 +++++- clang/lib/Sema/SemaType.cpp | 3 +++ clang/test/CXX/basic/basic.types/p10.cpp | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e652ba1ecdc..5617f7e69121 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/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/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 8f61ea23cd44..f3da67c4ff81 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/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/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 154b2a83f991..cee4ed67a1fe 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/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/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp index 66189552350a..614272b91209 100644 --- a/clang/test/CXX/basic/basic.types/p10.cpp +++ b/clang/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}} + } +}