summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Basic/DiagnosticASTKinds.td44
-rw-r--r--clang/lib/AST/ExprConstant.cpp107
-rw-r--r--clang/lib/AST/Interp/State.h1
-rw-r--r--clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp83
4 files changed, 201 insertions, 34 deletions
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 69d30b4eb3f..63207a0e225 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -38,8 +38,8 @@ def note_constexpr_pure_virtual_call : Note<
"pure virtual function %q0 called">;
def note_constexpr_polymorphic_unknown_dynamic_type : Note<
"%select{|||||virtual function called on|dynamic_cast applied to|"
- "typeid applied to|destruction of}0 object '%1' whose dynamic type "
- "is not constant">;
+ "typeid applied to|construction of|destruction of}0 object '%1' "
+ "whose dynamic type is not constant">;
def note_constexpr_dynamic_cast_to_reference_failed : Note<
"reference dynamic_cast failed: %select{"
"static type %1 of operand is a non-public base class of dynamic type %2|"
@@ -121,11 +121,12 @@ def note_constexpr_this : Note<
"evaluation of a call to a 'constexpr' member function">;
def note_constexpr_lifetime_ended : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
- "%select{temporary|variable}1 whose lifetime has ended">;
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 %select{temporary|variable}1 whose "
+ "%plural{8:storage duration|:lifetime}0 has ended">;
def note_constexpr_access_uninit : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|<ERRPR>|destruction of}0 "
"%select{object outside its lifetime|uninitialized object}1 "
"is not allowed in a constant expression">;
def note_constexpr_use_uninit_reference : Note<
@@ -136,18 +137,19 @@ def note_constexpr_modify_const_type : Note<
"in a constant expression">;
def note_constexpr_access_volatile_type : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "<ERROR>|<ERROR>|<ERROR>}0 "
+ "<ERROR>|<ERROR>|<ERROR>|<ERROR>}0 "
"volatile-qualified type %1 is not allowed in a constant expression">;
def note_constexpr_access_volatile_obj : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "<ERROR>|<ERROR>|<ERROR>}0 "
+ "<ERROR>|<ERROR>|<ERROR>|<ERROR>}0 "
"volatile %select{temporary|object %2|member %2}1 is not allowed in "
"a constant expression">;
def note_constexpr_volatile_here : Note<
"volatile %select{temporary created|object declared|member declared}0 here">;
def note_constexpr_access_mutable : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"mutable member %1 is not allowed in a constant expression">;
def note_constexpr_ltor_non_const_int : Note<
"read of non-const variable %0 is not allowed in a constant expression">;
@@ -157,35 +159,42 @@ def note_constexpr_ltor_incomplete_type : Note<
"read of incomplete type %0 is not allowed in a constant expression">;
def note_constexpr_access_null : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"dereferenced null pointer is not allowed in a constant expression">;
def note_constexpr_access_past_end : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"dereferenced one-past-the-end pointer is not allowed "
"in a constant expression">;
def note_constexpr_access_unsized_array : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"element of array without known bound "
"is not allowed in a constant expression">;
def note_constexpr_access_inactive_union_member : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|"
+ "construction of subobject of|destruction of}0 "
"member %1 of union with %select{active member %3|no active member}2 "
"is not allowed in a constant expression">;
def note_constexpr_access_static_temporary : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 temporary "
+ "member call on|dynamic_cast of|typeid applied to|reconstruction of|"
+ "destruction of}0 temporary "
"is not allowed in a constant expression outside the expression that "
"created the temporary">;
def note_constexpr_access_unreadable_object : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"object '%1' whose value is not known">;
def note_constexpr_access_deleted_object : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+ "member call on|dynamic_cast of|typeid applied to|construction of|"
+ "destruction of}0 "
"heap allocated object that has been deleted">;
def note_constexpr_modify_global : Note<
"a constant expression cannot modify an object that is visible outside "
@@ -255,6 +264,9 @@ def note_constexpr_bit_cast_indet_dest : Note<
def note_constexpr_pseudo_destructor : Note<
"pseudo-destructor call is not permitted in constant expressions "
"until C++20">;
+def note_constexpr_construct_complex_elem : Note<
+ "construction of individual component of complex number is not yet supported "
+ "in constant expressions">;
def note_constexpr_destroy_complex_elem : Note<
"destruction of individual component of complex number is not yet supported "
"in constant expressions">;
@@ -265,6 +277,8 @@ def note_constexpr_new_non_replaceable : Note<
"call to %select{placement|class-specific}0 %1">;
def note_constexpr_new_placement : Note<
"this placement new expression is not yet supported in constant expressions">;
+def note_constexpr_placement_new_wrong_type : Note<
+ "placement new would change type of storage from %0 to %1">;
def note_constexpr_new_negative : Note<
"cannot allocate array; evaluated array bound %0 is negative">;
def note_constexpr_new_too_large : Note<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 669acd3a666..33b0380fd60 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -594,6 +594,13 @@ namespace {
Frame *getCaller() const override { return Caller; }
SourceLocation getCallLocation() const override { return CallLoc; }
const FunctionDecl *getCallee() const override { return Callee; }
+
+ bool isStdFunction() const {
+ for (const DeclContext *DC = Callee; DC; DC = DC->getParent())
+ if (DC->isStdNamespace())
+ return true;
+ return false;
+ }
};
/// Temporarily override 'this'.
@@ -1395,6 +1402,7 @@ static bool isModification(AccessKinds AK) {
case AK_Assign:
case AK_Increment:
case AK_Decrement:
+ case AK_Construct:
case AK_Destroy:
return true;
}
@@ -1407,7 +1415,7 @@ static bool isAnyAccess(AccessKinds AK) {
/// Is this an access per the C++ definition?
static bool isFormalAccess(AccessKinds AK) {
- return isAnyAccess(AK) && AK != AK_Destroy;
+ return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy;
}
namespace {
@@ -3170,8 +3178,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
// Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
// Reading an indeterminate value is undefined, but assigning over one is OK.
- if (O->isAbsent() ||
- (O->isIndeterminate() && handler.AccessKind != AK_Assign &&
+ if ((O->isAbsent() && handler.AccessKind != AK_Construct) ||
+ (O->isIndeterminate() && handler.AccessKind != AK_Construct &&
+ handler.AccessKind != AK_Assign &&
handler.AccessKind != AK_ReadObjectRepresentation)) {
if (!Info.checkingPotentialConstantExpression())
Info.FFDiag(E, diag::note_constexpr_access_uninit)
@@ -3311,13 +3320,18 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
const FieldDecl *UnionField = O->getUnionField();
if (!UnionField ||
UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
- // FIXME: If O->getUnionValue() is absent, report that there's no
- // active union member rather than reporting the prior active union
- // member. We'll need to fix nullptr_t to not use APValue() as its
- // representation first.
- Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
- << handler.AccessKind << Field << !UnionField << UnionField;
- return handler.failed();
+ if (I == N - 1 && handler.AccessKind == AK_Construct) {
+ // Placement new onto an inactive union member makes it active.
+ O->setUnion(Field, APValue());
+ } else {
+ // FIXME: If O->getUnionValue() is absent, report that there's no
+ // active union member rather than reporting the prior active union
+ // member. We'll need to fix nullptr_t to not use APValue() as its
+ // representation first.
+ Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
+ << handler.AccessKind << Field << !UnionField << UnionField;
+ return handler.failed();
+ }
}
O = &O->getUnionValue();
} else
@@ -8438,14 +8452,23 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
return false;
FunctionDecl *OperatorNew = E->getOperatorNew();
- if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+
+ bool IsNothrow = false;
+ bool IsPlacement = false;
+ if (OperatorNew->isReservedGlobalPlacementOperator() &&
+ Info.CurrentCall->isStdFunction() && !E->isArray()) {
+ // FIXME Support array placement new.
+ assert(E->getNumPlacementArgs() == 1);
+ if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
+ return false;
+ if (Result.Designator.Invalid)
+ return false;
+ IsPlacement = true;
+ } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
return false;
- }
-
- bool IsNothrow = false;
- if (E->getNumPlacementArgs()) {
+ } else if (E->getNumPlacementArgs()) {
// The only new-placement list we support is of the form (std::nothrow).
//
// FIXME: There is no restriction on this, but it's not clear that any
@@ -8543,10 +8566,56 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
"array allocation with non-array new");
}
- // Perform the allocation and obtain a pointer to the resulting object.
- APValue *Val = Info.createHeapAlloc(E, AllocType, Result);
- if (!Val)
- return false;
+ APValue *Val;
+ if (IsPlacement) {
+ AccessKinds AK = AK_Construct;
+ struct FindObjectHandler {
+ EvalInfo &Info;
+ const Expr *E;
+ QualType AllocType;
+ const AccessKinds AccessKind;
+ APValue *Value;
+
+ typedef bool result_type;
+ bool failed() { return false; }
+ bool found(APValue &Subobj, QualType SubobjType) {
+ // FIXME: Reject the cases where [basic.life]p8 would not permit the
+ // old name of the object to be used to name the new object.
+ if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) {
+ Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) <<
+ SubobjType << AllocType;
+ return false;
+ }
+ Value = &Subobj;
+ return true;
+ }
+ bool found(APSInt &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+ return false;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+ return false;
+ }
+ } Handler = {Info, E, AllocType, AK, nullptr};
+
+ CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType);
+ if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler))
+ return false;
+
+ Val = Handler.Value;
+
+ // [basic.life]p1:
+ // The lifetime of an object o of type T ends when [...] the storage
+ // which the object occupies is [...] reused by an object that is not
+ // nested within o (6.6.2).
+ *Val = APValue();
+ } else {
+ // Perform the allocation and obtain a pointer to the resulting object.
+ Val = Info.createHeapAlloc(E, AllocType, Result);
+ if (!Val)
+ return false;
+ }
if (ResizedArrayILE) {
if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE,
diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h
index d02751693f6..d9a645a3eb3 100644
--- a/clang/lib/AST/Interp/State.h
+++ b/clang/lib/AST/Interp/State.h
@@ -32,6 +32,7 @@ enum AccessKinds {
AK_MemberCall,
AK_DynamicCast,
AK_TypeId,
+ AK_Construct,
AK_Destroy,
};
diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
index 5a39b33a87c..23582f2e302 100644
--- a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
+++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -83,3 +83,86 @@ static_assert(mismatched(2, 2));
constexpr int *escape = std::allocator<int>().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}}
constexpr int leak = (std::allocator<int>().allocate(3), 0); // expected-error {{constant expression}}
constexpr int no_lifetime_start = (*std::allocator<int>().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}}
+
+void *operator new(std::size_t, void *p) { return p; }
+constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
+ int a;
+ new (&a) int(42); // expected-note {{call to placement 'operator new'}}
+ return a == 42;
+}
+
+namespace std {
+ constexpr bool placement_new_in_stdlib() {
+ int a;
+ new (&a) int(42);
+ return a == 42;
+ }
+}
+static_assert(std::placement_new_in_stdlib());
+
+namespace std {
+ template<typename T, typename ...Args>
+ constexpr void construct_at(void *p, Args &&...args) {
+ new (p) T((Args&&)args...); // #new
+ }
+}
+
+constexpr bool call_std_construct_at() {
+ int *p = std::allocator<int>().allocate(3);
+ std::construct_at<int>(p, 1);
+ std::construct_at<int>(p + 1, 2);
+ std::construct_at<int>(p + 2, 3);
+ bool good = p[0] + p[1] + p[2] == 6;
+ std::allocator<int>().deallocate(p);
+ return good;
+}
+static_assert(call_std_construct_at());
+
+constexpr bool bad_construct_at_type() {
+ int a;
+ // expected-note@#new {{placement new would change type of storage from 'int' to 'float'}}
+ std::construct_at<float>(&a, 1.0f); // expected-note {{in call}}
+ return true;
+}
+static_assert(bad_construct_at_type()); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool bad_construct_at_subobject() {
+ struct X { int a, b; };
+ union A {
+ int a;
+ X x;
+ };
+ A a = {1};
+ // expected-note@#new {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}}
+ std::construct_at<int>(&a.x.a, 1); // expected-note {{in call}}
+ return true;
+}
+static_assert(bad_construct_at_subobject()); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool change_union_member() {
+ union U {
+ int a;
+ int b;
+ };
+ U u = {.a = 1};
+ std::construct_at<int>(&u.b, 2);
+ return u.b == 2;
+}
+static_assert(change_union_member());
+
+int external;
+// expected-note@#new {{visible outside}}
+static_assert((std::construct_at<int>(&external, 1), true)); // expected-error{{}} expected-note {{in call}}
+
+constexpr int &&temporary = 0; // expected-note {{created here}}
+// expected-note@#new {{construction of temporary is not allowed in a constant expression outside the expression that created the temporary}}
+static_assert((std::construct_at<int>(&temporary, 1), true)); // expected-error{{}} expected-note {{in call}}
+
+constexpr bool construct_after_lifetime() {
+ int *p = new int;
+ delete p;
+ // expected-note@#new {{construction of heap allocated object that has been deleted}}
+ std::construct_at<int>(p); // expected-note {{in call}}
+ return true;
+}
+static_assert(construct_after_lifetime()); // expected-error {{}} expected-note {{in call}}
OpenPOWER on IntegriCloud