diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-09-16 01:24:02 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-09-16 01:24:02 +0000 |
commit | b01fe40c07747f8353584d3fb9a1e066565ae24c (patch) | |
tree | 5fb18b812a9a21e6948789d939df2de63f9b45be | |
parent | 74acb46d26a348d1867ff05293af3426da0d6a29 (diff) | |
download | bcm5719-llvm-b01fe40c07747f8353584d3fb9a1e066565ae24c.tar.gz bcm5719-llvm-b01fe40c07747f8353584d3fb9a1e066565ae24c.zip |
Reject a slightly-sneaky way to perform a read of mutable state from within a
constexpr function. Part of this fix is a tentative fix for an as-yet-unfiled
core issue (we're missing a prohibition against reading mutable members from
unions via a trivial constructor/assignment, since that doesn't perform an
lvalue-to-rvalue conversion on the members).
llvm-svn: 217852
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 66 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx11.cpp | 43 |
2 files changed, 109 insertions, 0 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 155d9be8f96..743e59f19a6 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2080,6 +2080,64 @@ static void expandArray(APValue &Array, unsigned Index) { Array.swap(NewValue); } +/// Determine whether a type would actually be read by an lvalue-to-rvalue +/// conversion. If it's of class type, we may assume that the copy operation +/// is trivial. Note that this is never true for a union type with fields +/// (because the copy always "reads" the active member) and always true for +/// a non-class type. +static bool isReadByLvalueToRvalueConversion(QualType T) { + CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || (RD->isUnion() && !RD->field_empty())) + return true; + if (RD->isEmpty()) + return false; + + for (auto *Field : RD->fields()) + if (isReadByLvalueToRvalueConversion(Field->getType())) + return true; + + for (auto &BaseSpec : RD->bases()) + if (isReadByLvalueToRvalueConversion(BaseSpec.getType())) + return true; + + return false; +} + +/// Diagnose an attempt to read from any unreadable field within the specified +/// type, which might be a class type. +static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, + QualType T) { + CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD) + return false; + + if (!RD->hasMutableFields()) + return false; + + for (auto *Field : RD->fields()) { + // If we're actually going to read this field in some way, then it can't + // be mutable. If we're in a union, then assigning to a mutable field + // (even an empty one) can change the active member, so that's not OK. + // FIXME: Add core issue number for the union case. + if (Field->isMutable() && + (RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) { + Info.Diag(E, diag::note_constexpr_ltor_mutable, 1) << Field; + Info.Note(Field->getLocation(), diag::note_declared_at); + return true; + } + + if (diagnoseUnreadableFields(Info, E, Field->getType())) + return true; + } + + for (auto &BaseSpec : RD->bases()) + if (diagnoseUnreadableFields(Info, E, BaseSpec.getType())) + return true; + + // All mutable fields were empty, and thus not actually read. + return false; +} + /// Kinds of access we can perform on an object, for diagnostics. enum AccessKinds { AK_Read, @@ -2135,6 +2193,14 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, } if (I == N) { + // If we are reading an object of class type, there may still be more + // things we need to check: if there are any mutable subobjects, we + // cannot perform this read. (This only happens when performing a trivial + // copy or assignment.) + if (ObjType->isRecordType() && handler.AccessKind == AK_Read && + diagnoseUnreadableFields(Info, E, ObjType)) + return handler.failed(); + if (!handler.found(*O, ObjType)) return false; diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 57ca725d910..336fea87a50 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1326,6 +1326,49 @@ namespace MutableMembers { struct C { B b; }; constexpr C c[3] = {}; constexpr int k = c[1].b.a.n; // expected-error {{constant expression}} expected-note {{mutable}} + + struct D { int x; mutable int y; }; // expected-note {{here}} + constexpr D d1 = { 1, 2 }; + int l = ++d1.y; + constexpr D d2 = d1; // expected-error {{constant}} expected-note {{mutable}} expected-note {{in call}} + + struct E { + union { + int a; + mutable int b; // expected-note {{here}} + }; + }; + constexpr E e1 = {{1}}; + constexpr E e2 = e1; // expected-error {{constant}} expected-note {{mutable}} expected-note {{in call}} + + struct F { + union U { }; + mutable U u; + struct X { }; + mutable X x; + struct Y : X { X x; U u; }; + mutable Y y; + int n; + }; + // This is OK; we don't actually read any mutable state here. + constexpr F f1 = {}; + constexpr F f2 = f1; + + struct G { + struct X {}; + union U { X a; }; + mutable U u; // expected-note {{here}} + }; + constexpr G g1 = {}; + constexpr G g2 = g1; // expected-error {{constant}} expected-note {{mutable}} expected-note {{in call}} + constexpr G::U gu1 = {}; + constexpr G::U gu2 = gu1; + + union H { + mutable G::X gx; // expected-note {{here}} + }; + constexpr H h1 = {}; + constexpr H h2 = h1; // expected-error {{constant}} expected-note {{mutable}} expected-note {{in call}} } namespace Fold { |