"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|"
"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<
"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">;
"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 "
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">;
"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<
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'.
case AK_Assign:
case AK_Increment:
case AK_Decrement:
+ case AK_Construct:
case AK_Destroy:
return true;
}
/// 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 {
// 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)
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
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
"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,
AK_MemberCall,
AK_DynamicCast,
AK_TypeId,
+ AK_Construct,
AK_Destroy,
};
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}}