[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #34 from Toni Neubert --- Thank you both! Now everything works. :) I'll keep that in mind.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Status|REOPENED|RESOLVED Resolution|--- |FIXED --- Comment #33 from Jakub Jelinek --- Should be fixed now. If you have further issues, can you please file new bugreports instead of reopening the same? Thanks.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #32 from Jason Merrill --- Author: jason Date: Wed Jan 8 20:31:20 2020 New Revision: 280018 URL: https://gcc.gnu.org/viewcvs?rev=280018=gcc=rev Log: PR c++/91369 - constexpr destructor and member initializer. Previously it didn't matter whether we looked through a TARGET_EXPR in constexpr evaluation, but now that we have constexpr destructors it does. On IRC I mentioned the idea of clearing TARGET_EXPR_CLEANUP in digest_nsdmi_init, but since this initialization is expressed by an INIT_EXPR, it's better to handle all INIT_EXPR, not just those for a member initializer. * constexpr.c (cxx_eval_store_expression): Look through TARGET_EXPR when not preevaluating. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new10.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Assignee|unassigned at gcc dot gnu.org |jason at gcc dot gnu.org --- Comment #31 from Jakub Jelinek --- Jason said this could be fixed by clearing TARGET_EXPR_CLEANUPS in digest_nsdmi_init.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #30 from Jakub Jelinek --- (In reply to Toni Neubert from comment #28) > I have another test case which fails. (Maybe more..., I am sorry). No need to be sorry, your input is very valuable. Anyway, for the #c28 testcase (using struct S { constexpr S (int* i) : s{i} {} constexpr ~S () { delete s; } int *s; }; struct T { S t { new int }; }; constexpr auto foo () { T b; return true; } static_assert (foo ()); variant of it), not really sure if it is a compiler bug or testcase bug, the S class has defaulted copy ctor, so if it is ever copy constructed, there will be UB because you try to delete the same pointer multiple times. What I see happening is that in the T::T synthetized ctor, we have:> (int *) operator new (4) ) >; and the TARGET_EXPR has as cleanup S::~S (). So, when the constexpr evaluation evaluates this, it constructs the D.2161 temporary and queues a cleanup S::~S () until the CLEANUP_POINT_EXPR cleanup processing, then copies the value to the t member and when T::~T () is invoked during constexpr processing, S::~S (this->t) is invoked and that is the reason for the error, delete is called on the same pointer twice. Now, the reason why this doesn't fail when the foo function is invoked at runtime rather than at compile time is that cp_gimplify_init_expr has code to look through TARGET_EXPRs on the rhs of INIT_EXPR if their TARGET_EXPR_INITIAL is AGGR_INIT_EXPR/VEC_INIT_EXPR or COMPOUND_EXPR with those on the rightmost operand. The question is if what cp_gimplify_init_expr is just an optimization (then the testcase would be invalid), or if it is a mandatory C++ behavior, in that case I think cxx_eval_store_expression (if INIT_EXPR rather than MODIFY_EXPR) needs to detect this case and not sure if it can just look through the TARGET_EXPR (i.e. essentially ignore the cleanup), or if it needs to turn the whole INIT_EXPR into AGGR_INIT_EXPR or VEC_INIT_EXPR with different slot.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #29 from Jakub Jelinek --- Author: jakub Date: Tue Jan 7 07:13:50 2020 New Revision: 279943 URL: https://gcc.gnu.org/viewcvs?rev=279943=gcc=rev Log: PR c++/91369 * constexpr.c (struct constexpr_global_ctx): Add heap_alloc_count member, initialize it to zero in ctor. (cxx_eval_call_expression): Bump heap_dealloc_count when deleting a heap object. Don't cache calls to functions which allocate some heap objects and don't deallocate them or deallocate some heap objects they didn't allocate. * g++.dg/cpp1y/constexpr-new.C: Expect an error explaining why static_assert failed for C++2a. * g++.dg/cpp2a/constexpr-new9.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/testsuite/ChangeLog trunk/gcc/testsuite/g++.dg/cpp1y/constexpr-new.C
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #28 from Toni Neubert --- Thank you very much again for your fast help. Your patch works for this test case. I have another test case which fails. (Maybe more..., I am sorry). //main.cpp: in ‘constexpr’ expansion of ‘test3()’ //main.cpp: in ‘constexpr’ expansion of ‘(& b)->bar::~bar()’ //main.cpp: in ‘constexpr’ expansion of ‘(&((bar*)this)->bar::f)->foo::~foo()’ //main.cpp error: deallocation of already deallocated storage struct foo { constexpr foo(int* i) : ptr{i} {} constexpr ~foo() { delete ptr; } int* ptr; }; struct bar { foo f{new int}; }; constexpr auto test3() { bar b; return true; } static_assert(test3());
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #27 from Jakub Jelinek --- Created attachment 47590 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=47590=edit gcc10-pr91369-2.patch Untested patch.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Status|RESOLVED|REOPENED Resolution|FIXED |--- --- Comment #26 from Jakub Jelinek --- The problem is the constexpr call cache we have, disabling it altogether like: --- gcc/cp/constexpr.c 2020-01-04 11:19:50.585697766 +0100 +++ gcc/cp/constexpr.c 2020-01-04 11:18:42.0 +0100 @@ -2426,7 +2426,7 @@ cxx_eval_call_expression (const constexp else if (!result) result = void_node; if (entry) - entry->result = result; + entry->result = /*result*/error_mark_node; } /* The result of a constexpr function must be completely initialized. fixes it. Before the introduction of constexpr new/delete it was just fine to cache calls that way, a constexpr call of the same constexpr function with the same constant arguments with the same state of manifestly constant evaluation could be reused. I'm afraid that is no longer the case with constexpr new/delete, so I think we need to avoid the caching if the call allocates something and doesn't deallocate it before returning, or if it deallocates something it hasn't allocated, because the call in that case isn't really stateless for the constexpr evaluation. Checking for the former could be easy, remember before the call how the length of ctx->global->heap_vars and after the call, for any newly added elts verify if they were all deallocated. To detect the deallocation without allocation inside of the call we could perhaps add a counter for the deallocations and compare that counter to the number of the allocations.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #25 from Toni Neubert --- I get: "deallocation of already deallocated storage" for test2() but compiling just test1() or test2() is just fine. struct a { constexpr a(int* i) : i{i} { } constexpr ~a() { delete[] i; } int* i; }; constexpr a make_a(int size) { return {new int[size]()}; } constexpr bool test1() { make_a(1); return true; } constexpr bool test2() { make_a(1); return true; } static_assert(test1()); static_assert(test2());
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #24 from Toni Neubert --- Great thank you.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Status|REOPENED|RESOLVED Resolution|--- |FIXED --- Comment #23 from Jakub Jelinek --- Should be fixed now.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #22 from Jakub Jelinek --- Author: jakub Date: Tue Dec 3 19:27:47 2019 New Revision: 278945 URL: https://gcc.gnu.org/viewcvs?rev=278945=gcc=rev Log: PR c++/91369 * constexpr.c (struct constexpr_global_ctx): Add cleanups member, initialize it in the ctor. (cxx_eval_constant_expression) : If TARGET_EXPR_SLOT is already in the values hash_map, don't evaluate it again. Put TARGET_EXPR_SLOT into hash_map even if not lval, and push it into save_exprs too. If there is TARGET_EXPR_CLEANUP and not CLEANUP_EH_ONLY, push the cleanup to cleanups vector. : Save outer cleanups, set cleanups to local auto_vec, after evaluating the body evaluate cleanups and restore previous cleanups. : Don't crash if the first operand is NULL_TREE. (cxx_eval_outermost_constant_expr): Set cleanups to local auto_vec, after evaluating the expression evaluate cleanups. * g++.dg/cpp2a/constexpr-new8.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new8.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/testsuite/ChangeLog
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Toni Neubert changed: What|Removed |Added CC||lutztonineubert at gmail dot com --- Comment #21 from Toni Neubert --- I get: 'allocated storage has not been deallocated' when using prvalues/xvalues: struct A { constexpr A() : p{new int} {} constexpr ~A() { delete p; } int* p; }; constexpr bool test() { A{}; return true; } constexpr auto res = test(); static_assert(res);
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #20 from Jakub Jelinek --- Author: jakub Date: Fri Nov 1 23:26:17 2019 New Revision: 277732 URL: https://gcc.gnu.org/viewcvs?rev=277732=gcc=rev Log: PR c++/91369 - Implement P0784R7: constexpr new * cp-tree.h (CALL_FROM_NEW_OR_DELETE_P): Define. * init.c (build_new_1, build_vec_delete_1, build_delete): Set CALL_FROM_NEW_OR_DELETE_P on the CALL_EXPR to allocator functions. * constexpr.c (is_std_allocator_allocate): Only allow global replaceable allocator functions if CALL_FROM_NEW_OR_DELETE_P or in std::allocate::{,de}allocate. (potential_constant_expression_1): Likewise. * g++.dg/cpp2a/constexpr-new6.C: New test. * g++.dg/cpp2a/constexpr-new7.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/cp/cp-tree.h trunk/gcc/cp/init.c trunk/gcc/testsuite/ChangeLog
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #19 from Jakub Jelinek --- Author: jakub Date: Wed Oct 30 21:55:12 2019 New Revision: 277649 URL: https://gcc.gnu.org/viewcvs?rev=277649=gcc=rev Log: PR c++/91369 - Implement P0784R7: constexpr new * constexpr.c (cxx_replaceable_global_alloc_fn): Don't return true for placement new. (cxx_placement_new_fn, is_std_construct_at): New functions. (cxx_eval_call_expression): Allow placement new in std::construct_at. (potential_constant_expression_1): Likewise. * g++.dg/cpp2a/constexpr-new5.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-new5.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/testsuite/ChangeLog
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #18 from Jonathan Wakely --- Author: redi Date: Wed Oct 23 17:42:11 2019 New Revision: 277342 URL: https://gcc.gnu.org/viewcvs?rev=277342=gcc=rev Log: PR c++/91369 Implement P0784R7 changes to allocation and construction This patch is the first part of library support for constexpr std::vector and std::string. This only includes the changes to std::allocator, std::allocator_traits, std::construct_at, std::destroy_at, std::destroy and std::destroy_n. std::allocator::allocate and std::allocator::deallocate need to be added so that they can be intercepted by the compiler during constant evaluation. Outside of constant evaluation those new member functions just forward to the existing implementation in the base class. PR c++/91369 Implement P0784R7 changes to allocation and construction * include/bits/alloc_traits.h: Include . (allocator_traits::_S_allocate, allocator_traits::_S_construct) (allocator_traits::_S_destroy, allocator_traits::_S_max_size) (allocator_traits::_S_select, allocator_traits::allocate) (allocator_traits::deallocate, allocator_traits::construct) (allocator_traits::destroy, allocator_traits::max_size) (allocator_traits::select_on_container_copy_construction) (allocator_traits>): Add constexpr specifier for C++20. (allocator_traits>::construct): Use construct_at. (allocator_traits>::destroy): Use destroy_at. (__alloc_on_copy, __alloc_on_move, __alloc_on_swap): Add constexpr specifier. (_Destroy(ForwardIterator, ForwardIterator, Alloc&)) (_Destroy(ForwardIterator, ForwardIterator, allocator&)): Move here from . * include/bits/allocator.h (allocator::~allocator): Remove for C++20. (allocator::allocate, allocate::deallocate): Define for C++20 and up. (operator==, operator!=): Add constexpr specifier for C++20. * include/bits/stl_construct.h: Don't include . (destroy_at): For C++20 add constexpr specifier and support for destroying arrays. (construct_at): Define new function for C++20. (_Construct): Return result of placement new-expression. For C++11 and up add constexpr. For C++20 dispatch to std::construct_at during constant evaluation. (_Destroy(pointer)): Add constexpr specifier. For C++20 dispatch to std::destroy_at during constant evaluation. (_Destroy_aux::__destroy, _Destroy_n_aux::__destroy_n): Add constexpr specifier for C++20. (_Destroy(ForwardIterator, ForwardIterator)) (_Destroy(ForwardIterator, Size)): Likewise. Do not elide trivial destructors during constant evaluation. (destroy, destroy_n): Add constexpr specifier for C++20. (_Destroy(ForwardIterator, ForwardIterator, Alloc&)) (_Destroy(ForwardIterator, ForwardIterator, allocator&)): Move to , to remove dependency on allocators. * include/bits/stl_uninitialized.h: Include . Include instead of . * include/ext/alloc_traits.h: Always include . (__alloc_traits::construct, __alloc_traits::destroy) (__alloc_traits::_S_select_on_copy, __alloc_traits::_S_on_swap): Add constexpr specifier. * include/ext/malloc_allocator.h (operator==, operator!=): Add constexpr specifier for C++20. * include/ext/new_allocator.h (operator==, operator!=): Likewise. * testsuite/20_util/headers/memory/synopsis.cc: Add constexpr. * testsuite/20_util/scoped_allocator/69293_neg.cc: Ignore additional errors due to constexpr function called after failed static_assert. * testsuite/20_util/specialized_algorithms/construct_at/1.cc: New test. * testsuite/23_containers/vector/cons/destructible_debug_neg.cc: Ignore additional errors due to constexpr function called after failed static_assert. * testsuite/23_containers/vector/cons/destructible_neg.cc: Likewise. Added: trunk/libstdc++-v3/testsuite/20_util/specialized_algorithms/construct_at/ trunk/libstdc++-v3/testsuite/20_util/specialized_algorithms/construct_at/1.cc Modified: trunk/libstdc++-v3/ChangeLog trunk/libstdc++-v3/include/bits/alloc_traits.h trunk/libstdc++-v3/include/bits/allocator.h trunk/libstdc++-v3/include/bits/stl_construct.h trunk/libstdc++-v3/include/bits/stl_uninitialized.h trunk/libstdc++-v3/include/ext/alloc_traits.h trunk/libstdc++-v3/include/ext/malloc_allocator.h trunk/libstdc++-v3/include/ext/new_allocator.h trunk/libstdc++-v3/testsuite/20_util/headers/memory/synopsis.cc trunk/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc trunk/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc trunk/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #17 from Jakub Jelinek --- Ok, so do I need to somehow mark the CALL_EXPR created from new/delete lowering and only treat calls to global replaceable allocator/deallocator functions specially if they either have this flag or are in allocate//deallocate method of std::allocator template? And similarly perhaps do something for the placement new in std::construct_at etc.?
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #16 from Jonathan Wakely --- I'll commit a patch to add std::construct_at today or tomorrow.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #15 from Jonathan Wakely --- That's not valid, because operator new is not a constexpr function. You have to use a new-expression (that resolves to one of the standard operator new allocation functions), or std::allocator::allocate, or std::allocator_traits>::allocate. The result of std::allocator::allocate can't be dereferenced until you've constructed an object there (and attempting to do so in a constexpr function should be ill-formed). This should work: constexpr int bar () { auto a = new int; // obtain storage for an int and begin its lifetime *a = 1; *a = *a + 2; int r = *a; delete a; // end lifetime and release storage return r; } constexpr auto p = bar (); And this: constexpr int baz () { auto a = std::allocator::allocate(1); // obtain storage for an int std::construct_at(a); // begin lifetime of an int *a = 1; *a = *a + 2; int r = *a; std::destroy_at(a);// end lifetime std::allocator::deallocate(a, 1); // release storage return r; } constexpr auto q = baz (); And the equivalent using std::allocator_traits: constexpr int baz2 () { std::allocator alloc; using A = std::allocator_traits>; auto a = A::allocate(alloc, 1);// obtain storage for an int A::construct(a); // calls std::construct_at *a = 1; *a = *a + 2; int r = *a; A::destroy(alloc, a); // calls std::destroy_at A::deallocate(alloc, a, 1);// release storage return r; } constexpr auto q = baz2 ();
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #14 from Jakub Jelinek --- Another question is whether: constexpr int bar () { auto a = static_cast (::operator new (sizeof (int))); *a = 1; *a = *a + 2; int r = *a; ::operator delete (a); return r; } constexpr auto p = bar (); is valid or not (i.e. when there is no placement new at all, just static_cast and dereferencing of the pointer. Or if it is required to use std::allocator::allocate for that, and in that case, does one need to use placement new or can it be dereferenced without it?
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #13 from Jonathan Wakely --- (In reply to Jakub Jelinek from comment #12) > Well, not fully. The paper has additions of constexpr keywords on the > library side, but more importantly, we'll probably need some hacks in the > compiler for the library side, but waiting for Jonathan with that. Yes, I have the library parts coded up but not committed. > My understanding is that right now (perhaps that might change) placement new > should not be treated as constexpr function, but it needs to be in certain > STL templates (at least std::construct_at, something else?) The way EDG does it is not to allow placement new in constant expressions, but to intercept calls to std::construct_at and std::destroy_at and replace them with equivalent code that does the construction/destruction. If allowing the body of std::construct_at to be valid is easier, that's fine too. > Also, I was afraid that in std::allocator::allocate, the cast would be > reinterpret_cast, but apparently it isn't: > > template > constexpr T * > foo () > { > return static_cast (::operator new (sizeof (T))); > } > > constexpr int > bar () > { > auto a = foo (); > ::operator delete (a); > return 0; > } > > constexpr auto p = bar (); > > (this doesn't actually use placement new, because that does and should ATM > fail). So, maybe it is just the placement new that needs to be handled. > But, if one can cast the global replaceable allocator function result to > anything in constexpr and our implementation triggers on the cast rather > than on some spot coming from the new operator, shouldn't we use some new > C++ expression kind or say an internal function as an explicit cast in new > expression (placement or not) rather than any cast of the pointer? Again, instead of trying to allow whatever the body of std::allocator::allocate does, EDG just intercepts call to std::allocator::allocate and replaces it with something else that works for the constexpr case. That means it doesn't matter what the body of std::allocator::allocate does, and it doesn't matter whether operator new has been replaced by the program, because that code is never evaluated in constant expressions. > Note, clang++ rejects the above testcase with > /tmp/6.C:16:16: error: constexpr variable 'p' must be initialized by a > constant expression > constexpr auto p = bar (); >^ ~~ > /tmp/6.C:5:28: note: cannot allocate untyped memory in a constant > expression; use 'std::allocator::allocate' to allocate memory of type 'T' > return static_cast (::operator new (sizeof (T))); >^ > /tmp/6.C:11:12: note: in call to 'foo()' > auto a = foo (); >^ > /tmp/6.C:16:20: note: in call to 'bar()' > constexpr auto p = bar (); >^ > 1 error generated. > > Is it correct or not? The allocation function itself is not constexpr, on > the other side the wording says that the allocations should be elided in > constexpr contexts. I don't think Clang implements this feature yet, so I'd expect it to fail.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Status|RESOLVED|REOPENED Resolution|FIXED |--- --- Comment #12 from Jakub Jelinek --- Well, not fully. The paper has additions of constexpr keywords on the library side, but more importantly, we'll probably need some hacks in the compiler for the library side, but waiting for Jonathan with that. My understanding is that right now (perhaps that might change) placement new should not be treated as constexpr function, but it needs to be in certain STL templates (at least std::construct_at, something else?) Also, I was afraid that in std::allocator::allocate, the cast would be reinterpret_cast, but apparently it isn't: template constexpr T * foo () { return static_cast (::operator new (sizeof (T))); } constexpr int bar () { auto a = foo (); ::operator delete (a); return 0; } constexpr auto p = bar (); (this doesn't actually use placement new, because that does and should ATM fail). So, maybe it is just the placement new that needs to be handled. But, if one can cast the global replaceable allocator function result to anything in constexpr and our implementation triggers on the cast rather than on some spot coming from the new operator, shouldn't we use some new C++ expression kind or say an internal function as an explicit cast in new expression (placement or not) rather than any cast of the pointer? Note, clang++ rejects the above testcase with /tmp/6.C:16:16: error: constexpr variable 'p' must be initialized by a constant expression constexpr auto p = bar (); ^ ~~ /tmp/6.C:5:28: note: cannot allocate untyped memory in a constant expression; use 'std::allocator::allocate' to allocate memory of type 'T' return static_cast (::operator new (sizeof (T))); ^ /tmp/6.C:11:12: note: in call to 'foo()' auto a = foo (); ^ /tmp/6.C:16:20: note: in call to 'bar()' constexpr auto p = bar (); ^ 1 error generated. Is it correct or not? The allocation function itself is not constexpr, on the other side the wording says that the allocations should be elided in constexpr contexts.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jason Merrill changed: What|Removed |Added Status|NEW |RESOLVED Resolution|--- |FIXED Target Milestone|--- |10.0 --- Comment #11 from Jason Merrill --- Implemented.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #10 from Jakub Jelinek --- Author: jakub Date: Sat Oct 5 07:38:21 2019 New Revision: 276622 URL: https://gcc.gnu.org/viewcvs?rev=276622=gcc=rev Log: PR c++/91369 - Implement P0784R7: constexpr new c-family/ * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_constexpr_dynamic_alloc=201907 for -std=c++2a. cp/ * cp-tree.h (enum cp_tree_index): Add CPTI_HEAP_UNINIT_IDENTIFIER, CPTI_HEAP_IDENTIFIER and CPTI_HEAP_DELETED_IDENTIFIER. (heap_uninit_identifier, heap_identifier, heap_deleted_identifier): Define. (type_has_constexpr_destructor, build_new_constexpr_heap_type, cxx_constant_dtor): Declare. * class.c (type_maybe_constexpr_default_constructor): Make static. (type_maybe_constexpr_destructor, type_has_constexpr_destructor): New functions. (finalize_literal_type_property): For c++2a, don't clear CLASSTYPE_LITERAL_P for types without trivial destructors unless they have non-constexpr destructors. (explain_non_literal_class): For c++2a, complain about non-constexpr destructors rather than about non-trivial destructors. * constexpr.c: Include stor-layout.h. (struct constexpr_global_ctx): New type. (struct constexpr_ctx): Add global field, remove values and constexpr_ops_count. (cxx_replaceable_global_alloc_fn): New inline function. (cxx_eval_call_expression): For c++2a allow calls to replaceable global allocation functions, for new return address of a heap uninit var, for delete record its deletion. Change ctx->values->{get,put} to ctx->global->values.{get,put}. (non_const_var_error): Add auto_diagnostic_group sentinel. Emit special diagnostics for heap variables. (cxx_eval_store_expression): Change ctx->values->{get,put} to ctx->global->values.{get,put}. (cxx_eval_loop_expr): Initialize jump_target if NULL. Change new_ctx.values->remove to ctx->global->values.remove. (cxx_eval_constant_expression): Change *ctx->constexpr_ops_count to ctx->global->constexpr_ops_count. Change ctx->values->{get,put} to ctx->global->values.{get,put}. : Formatting fix. On cast of replaceable global allocation function to some pointer type, adjust the type of the heap variable and change name from heap_uninit_identifier to heap_identifier. (find_heap_var_refs): New function. (cxx_eval_outermost_constant_expr): Add constexpr_dtor argument, handle evaluation of constexpr dtors and add tracking of heap variables. Use tf_no_cleanup for get_target_expr_with_sfinae. (cxx_constant_value): Adjust cxx_eval_outermost_constant_expr caller. (cxx_constant_dtor): New function. (maybe_constant_value, fold_non_dependent_expr_template, maybe_constant_init_1): Adjust cxx_eval_outermost_constant_expr callers. (potential_constant_expression_1): Ignore clobbers. Allow COND_EXPR_IS_VEC_DELETE for c++2a. * decl.c (initialize_predefined_identifiers): Add heap identifiers. (decl_maybe_constant_destruction): New function. (cp_finish_decl): Don't clear TREE_READONLY for constexpr variables with non-trivial, but constexpr destructors. (register_dtor_fn): For constexpr variables with constexpr non-trivial destructors call cxx_maybe_build_cleanup instead of adding destructor calls at runtime. (expand_static_init): For constexpr variables with constexpr non-trivial destructors call cxx_maybe_build_cleanup. (grokdeclarator): Allow constexpr destructors for c++2a. Formatting fix. (cxx_maybe_build_cleanup): For constexpr variables with constexpr non-trivial destructors call cxx_constant_dtor instead of adding destructor calls at runtime. * init.c: Include stor-layout.h. (build_new_constexpr_heap_type, maybe_wrap_new_for_constexpr): New functions. (build_new_1): For c++2a and new[], add cast around the alloc call to help constexpr evaluation figure out the type of the heap storage. (build_vec_delete_1): Set DECL_INITIAL of tbase and emit a DECL_EXPR for it instead of initializing an uninitialized variable. * method.c: Include intl.h. (SFK_CTOR_P, SFK_DTOR_P, SFK_ASSIGN_P, SFK_COPY_P, SFK_MOVE_P): Move definitions earlier. (process_subob_fn): Add sfk argument, adjust non-constexpr call diagnostics based on it. (walk_field_subobs): Formatting fixes. Adjust process_subob_fn caller. (synthesized_method_base_walk): Likewise. (synthesized_method_walk): Set *constexpr_p to true for dtors in c++2a. Fix up DR number in comment. (implicitly_declare_fn): Formatting
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Attachment #46956|0 |1 is obsolete|| --- Comment #9 from Jakub Jelinek --- Created attachment 46967 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=46967=edit gcc10-pr91369.patch Updated patch.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added Attachment #46946|0 |1 is obsolete|| --- Comment #8 from Jakub Jelinek --- Created attachment 46956 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=46956=edit gcc10-pr91369.patch Updated patch. This patch implements the state if C++ drops the note that trivial destructors are constexpr destructors. If on the core reflector you agree on something else, please let me know and I'll change it. Other than that, I need to finish up the cookie_size support for array new (trying to represent that heap object as record with two fields, sizetype array covering cookie and class_type array covering the rest, but seems indirect ref folding isn't still happy about that, otherwise I'm out of ideas what else to test.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #7 from Jason Merrill --- (In reply to Jakub Jelinek from comment #6) > So, is the [class.dtor]/9 note just incorrect and should be removed, or > clarified somehow? I believe it shouldn't affect what actually is a literal > type or not, because the constructor of a class without virtual bases which > has a non-static data member that has virtual bases can't be constexpr either. I think a virtual base just shouldn't make the destructor non-constexpr, which would make the note correct. I've emailed CWG to that effect.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #6 from Jakub Jelinek --- Still need to add further testcase coverage and finish cookie support, but I ran into something that looks like a bug in the C++ standard. [dcl.constexpr]/3 says: if the function is a constructor or destructor, its class shall not have any virtual base classes; [dcl.constexpr]/5 says: The definition of a constexpr destructor whose function-body is not = delete shall additionally satisfy the following requirement: - for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall have a constexpr destructor. [class.dtor]/8 says: A destructor is trivial if it is not user-provided and if: - the destructor is not virtual, - all of the direct base classes of its class have trivial destructors, and - for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor. [class.dtor]/9 says: A defaulted destructor is a constexpr destructor if it satisfies the requirements for a constexpr destructor ([dcl.constexpr]). [ Note: In particular, a trivial destructor is a constexpr destructor. — end note ] Now, consider the g++.old-deja/g++.mike/p12306a.C or g++.dg/abi/empty7.C testcases in -std=c++2a modes, let's say the first one: class a { public: int i; }; class g : virtual public a { }; class b : virtual public a { int j; }; class c : public g, public b { }; class d { public: virtual class b* get() {return 0;} }; class f : public d { public: virtual class b* get() {return &_c;} c _c; }; int main(void) { f D; b* bp=D.get(); D._c.i = 42; return _c.i != >i; } The patch ICEs on this, because the implicit f::~f () is considered trivial (there are no virtual destructors in the testcase, nor any user-provided destructors), but it isn't a constexpr destructor, because while f doesn't have virtual bases, a non-static data member of f (_c) has virtual bases and thus c::~c () is not a constexpr destructor and because of that f::~f () can't be a constexpr destructor either. So, is the [class.dtor]/9 note just incorrect and should be removed, or clarified somehow? I believe it shouldn't affect what actually is a literal type or not, because the constructor of a class without virtual bases which has a non-static data member that has virtual bases can't be constexpr either. On the compiler side, it would mean the trivial destructor quick bail-out checks wouldn't actually work for when we ask whether the destructor is constexpr, though we could keep it perhaps in the literal_type_p checking, at least as long as a subobject with virtual bases really always forces non-constexpr constructors.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #5 from Jakub Jelinek --- Created attachment 46946 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=46946=edit gcc10-pr91369.patch Current WIP patch.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #4 from Jason Merrill --- (In reply to Jakub Jelinek from comment #3) > (In reply to Jason Merrill from comment #2) > > > should we mark them some way and either allow the first > > > cxx_fold_indirect_ref or the above code to change their type the first > > > time > > > they are stored? > > > > This would work, but wouldn't distinguish between a new-expression and the > > equivalent written by hand. I suppose a flag could make that distinction. > > Wouldn't user written code have REINTERPRET_CAST_P set on the cast and thus > be rejected? > Can user call the replaceable new operator directly in constexpr contexts in > some valid way? Ah, good point. They could call it directly, but couldn't do anything with the memory without a reinterpret_cast.
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #3 from Jakub Jelinek --- (In reply to Jason Merrill from comment #2) > > should we mark them some way and either allow the first > > cxx_fold_indirect_ref or the above code to change their type the first time > > they are stored? > > This would work, but wouldn't distinguish between a new-expression and the > equivalent written by hand. I suppose a flag could make that distinction. Wouldn't user written code have REINTERPRET_CAST_P set on the cast and thus be rejected? Can user call the replaceable new operator directly in constexpr contexts in some valid way?
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 --- Comment #2 from Jason Merrill --- (In reply to Jakub Jelinek from comment #1) > So, first of all, is it a good idea to represent the HEAP variables through > artifical VAR_DECLs? That makes sense to me. > I guess in the outermost constexpr evaluation context we'd need to track > which of those we have allocated and deallocated and do the checking that at > the end of outermost constexpr evaluation no allocations are left around, Sounds right. > and that we don't deallocate something that hasn't been allocated. I'd expect that to be detected during evaluation when we try to deallocate something that isn't constant or isn't the address of one of these artificial VAR_DECLs. > As we don't have the actual dynamic type they will have at the end of new > expression We should really fix that. Perhaps we should delay lowering new-expressions until genericization time? > should we mark them some way and either allow the first > cxx_fold_indirect_ref or the above code to change their type the first time > they are stored? This would work, but wouldn't distinguish between a new-expression and the equivalent written by hand. I suppose a flag could make that distinction. > Is placement new ok in constexpr contexts or not? No, placement new is not replaceable. > Can the global replaceable allocation functions be marked constexpr by the > user? No. > For the constexpr dtors, we need to make sure to mark trivial dtors as > constexpr, and default dtors as constexpr if they satisfy the rules, verify > the restrictions for user defined constexpr dtors, and check for constexpr > dtor in literal type check. Plus guess for constexpr variables we need to > at some point verify they are constexpr destructible (but on a copy of the > variable value, such that changes to the value during destruction don't > modify the actual value of the variable we use normally). Right. I think that's implied by the passage "An object a is said to have constant destruction if ... for a hypothetical expression e whose only effect is to destroy a, e would be a core constant expression if the lifetime of a and its non-mutable subobjects (but not its mutable subobjects) were considered to start within e."
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Jakub Jelinek changed: What|Removed |Added CC||jakub at gcc dot gnu.org, ||jason at gcc dot gnu.org, ||redi at gcc dot gnu.org --- Comment #1 from Jakub Jelinek --- Just playing around a little bit, on: struct S { constexpr S () : s (5) {}; int s; }; constexpr bool foo () { S *p = new S (); delete p; return true; } (so, trivial dtor rather than introducing constexpr dtors for now): --- gcc/cp/constexpr.c.jj 2019-08-27 12:26:39.335884288 +0200 +++ gcc/cp/constexpr.c 2019-08-30 18:49:59.673689644 +0200 @@ -1666,6 +1666,34 @@ cxx_eval_call_expression (const constexp lval, non_constant_p, overflow_p); if (!DECL_DECLARED_CONSTEXPR_P (fun)) { + if (cxx_dialect >= cxx2a + && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun)) + && CP_DECL_CONTEXT (fun) == global_namespace) + { + if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun))) + { + const int nargs = call_expr_nargs (t); + tree sz = NULL_TREE; + for (int i = 0; i < nargs; ++i) + { + tree arg = CALL_EXPR_ARG (t, i); + arg = cxx_eval_constant_expression (ctx, arg, false, + non_constant_p, + overflow_p); + VERIFY_CONSTANT (arg); + if (i == 0) + sz = arg; + } + gcc_assert (sz); + tree type = build_array_type_nelts (char_type_node, + tree_to_uhwi (sz)); + tree var = build_decl (loc, VAR_DECL, NULL_TREE, type); + DECL_ARTIFICIAL (var) = 1; + return fold_convert (ptr_type_node, build_address (var)); + } + else + /* FIXME */; + } if (!ctx->quiet) { if (!lambda_static_thunk_p (fun)) @@ -6243,7 +6271,12 @@ potential_constant_expression_1 (tree t, if (!DECL_DECLARED_CONSTEXPR_P (fun) /* Allow any built-in function; if the expansion isn't constant, we'll deal with that then. */ - && !fndecl_built_in_p (fun)) + && !fndecl_built_in_p (fun) + /* In C++2a, replaceable global allocation functions + are constant expressions. */ + && (cxx_dialect < cxx2a + || !IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fun)) + || CP_DECL_CONTEXT (fun) != global_namespace)) { if (flags & tf_error) { This fails because we represent the new expression as COMPOUND_EXPR which first calls the allocation function (handled by the above code) and then constructs the var (effectively through a reinterpret cast not marked as such), and fail on that: r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0, _base); if (r == NULL_TREE) { /* We couldn't fold to a constant value. Make sure it's not something we should have been able to fold. */ tree sub = op0; STRIP_NOPS (sub); if (TREE_CODE (sub) == ADDR_EXPR) { gcc_assert (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t))); /* DR 1188 says we don't have to deal with this. */ if (!ctx->quiet) error ("accessing value of %qE through a %qT glvalue in a " "constant expression", build_fold_indirect_ref (sub), TREE_TYPE (t)); *non_constant_p = true; return t; } if (lval && op0 != orig_op0) return build1 (INDIRECT_REF, TREE_TYPE (t), op0); if (!lval) VERIFY_CONSTANT (t); return t; } So, first of all, is it a good idea to represent the HEAP variables through artifical VAR_DECLs? I guess in the outermost constexpr evaluation context we'd need to track which of those we have allocated and deallocated and do the checking that at the end of outermost constexpr evaluation no allocations are left around, and that we don't deallocate something that hasn't been allocated. As we don't have the actual dynamic type they will have at the end of new expression, should we mark them some way and either allow the first cxx_fold_indirect_ref or the above code to change their type the first time they are stored? Is placement new ok in
[Bug c++/91369] Implement P0784R7: constexpr new
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91369 Marek Polacek changed: What|Removed |Added Status|UNCONFIRMED |NEW Last reconfirmed||2019-08-05 Ever confirmed|0 |1