From 8412b939d1cf375c8e478e39a5ac9d7260e4c23c Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Sat, 2 Nov 2019 00:26:17 +0100 Subject: [PATCH] PR c++/91369 - Implement P0784R7: constexpr new 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. From-SVN: r277732 --- gcc/cp/ChangeLog | 11 +++ gcc/cp/constexpr.c | 40 +++++++++- gcc/cp/cp-tree.h | 6 ++ gcc/cp/init.c | 15 ++++ gcc/testsuite/ChangeLog | 4 + gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C | 83 +++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 33 ++++++++ 7 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 0b828528958..50da698107d 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2019-11-02 Jakub Jelinek + + 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. + 2019-11-01 Nathan Sidwell * class.c (check_field_decls): Refactor. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 75db0b3c72d..4baaac06252 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1638,6 +1638,28 @@ is_std_construct_at (tree fndecl) return name && id_equal (name, "construct_at"); } +/* Return true if FNDECL is std::allocator::{,de}allocate. */ + +static inline bool +is_std_allocator_allocate (tree fndecl) +{ + tree name = DECL_NAME (fndecl); + if (name == NULL_TREE + || !(id_equal (name, "allocate") || id_equal (name, "deallocate"))) + return false; + + tree ctx = DECL_CONTEXT (fndecl); + if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx)) + return false; + + tree decl = TYPE_MAIN_DECL (ctx); + name = DECL_NAME (decl); + if (name == NULL_TREE || !id_equal (name, "allocator")) + return false; + + return decl_in_std_namespace_p (decl); +} + /* Subroutine of cxx_eval_constant_expression. Evaluate the call expression tree T in the context of OLD_CALL expression evaluation. */ @@ -1716,7 +1738,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, lval, non_constant_p, overflow_p); if (!DECL_DECLARED_CONSTEXPR_P (fun)) { - if (cxx_replaceable_global_alloc_fn (fun)) + if (TREE_CODE (t) == CALL_EXPR + && cxx_replaceable_global_alloc_fn (fun) + && (CALL_FROM_NEW_OR_DELETE_P (t) + || (ctx->call + && ctx->call->fundef + && is_std_allocator_allocate (ctx->call->fundef->decl)))) { const int nargs = call_expr_nargs (t); tree arg0 = NULL_TREE; @@ -1774,7 +1801,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } /* Allow placement new in std::construct_at, just return the second argument. */ - if (cxx_placement_new_fn (fun) + if (TREE_CODE (t) == CALL_EXPR + && cxx_placement_new_fn (fun) && ctx->call && ctx->call->fundef && is_std_construct_at (ctx->call->fundef->decl)) @@ -6508,9 +6536,15 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, && !fndecl_built_in_p (fun) /* In C++2a, replaceable global allocation functions are constant expressions. */ - && !cxx_replaceable_global_alloc_fn (fun) + && (!cxx_replaceable_global_alloc_fn (fun) + || TREE_CODE (t) != CALL_EXPR + || (!CALL_FROM_NEW_OR_DELETE_P (t) + && (current_function_decl == NULL_TREE + || !is_std_allocator_allocate + (current_function_decl)))) /* Allow placement new in std::construct_at. */ && (!cxx_placement_new_fn (fun) + || TREE_CODE (t) != CALL_EXPR || current_function_decl == NULL_TREE || !is_std_construct_at (current_function_decl))) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 42d5111ac0f..6c9731f24db 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -448,6 +448,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR) IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR) TINFO_VAR_DECLARED_CONSTINIT (in TEMPLATE_INFO) + CALL_FROM_NEW_OR_DELETE_P (in CALL_EXPR) 3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out). ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -3791,6 +3792,11 @@ struct GTY(()) lang_decl { should be performed at instantiation time. */ #define KOENIG_LOOKUP_P(NODE) TREE_LANG_FLAG_0 (CALL_EXPR_CHECK (NODE)) +/* In a CALL_EXPR, true for allocator calls from new or delete + expressions. */ +#define CALL_FROM_NEW_OR_DELETE_P(NODE) \ + TREE_LANG_FLAG_2 (CALL_EXPR_CHECK (NODE)) + /* True if the arguments to NODE should be evaluated in left-to-right order regardless of PUSH_ARGS_REVERSED. */ #define CALL_EXPR_ORDERED_ARGS(NODE) \ diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 1c51e26febe..f86cf551039 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -3404,6 +3404,10 @@ build_new_1 (vec **placement, tree type, tree nelts, } } + tree alloc_call_expr = extract_call_expr (alloc_call); + if (TREE_CODE (alloc_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (alloc_call_expr) = 1; + if (cookie_size) alloc_call = maybe_wrap_new_for_constexpr (alloc_call, elt_type, cookie_size); @@ -4046,6 +4050,10 @@ build_vec_delete_1 (tree base, tree maxindex, tree type, /*placement=*/NULL_TREE, /*alloc_fn=*/NULL_TREE, complain); + + tree deallocate_call_expr = extract_call_expr (deallocate_expr); + if (TREE_CODE (deallocate_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (deallocate_call_expr) = 1; } body = loop; @@ -4955,6 +4963,13 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete, if (!deleting) return expr; + if (do_delete) + { + tree do_delete_call_expr = extract_call_expr (do_delete); + if (TREE_CODE (do_delete_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (do_delete_call_expr) = 1; + } + if (do_delete && !TREE_SIDE_EFFECTS (expr)) expr = do_delete; else if (do_delete) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 40e33f91ea9..c5293582e9a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,9 @@ 2019-11-02 Jakub Jelinek + PR c++/91369 - Implement P0784R7: constexpr new + * g++.dg/cpp2a/constexpr-new6.C: New test. + * g++.dg/cpp2a/constexpr-new7.C: New test. + * gcc.dg/pr36902.c: Terminate dg-warning regexp string. 2019-11-01 Martin Sebor diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C new file mode 100644 index 00000000000..5c0c2b6f967 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C @@ -0,0 +1,83 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +namespace std +{ + inline namespace _8 { } + namespace _8 { + + typedef __SIZE_TYPE__ size_t; + + template + struct allocator + { + constexpr allocator () noexcept {} + + constexpr T *allocate (size_t n) + { return static_cast (::operator new (n * sizeof(T))); } + + constexpr void + deallocate (T *p, size_t n) + { ::operator delete (p); } + }; + + template + U __declval (int); + template + T __declval (long); + template + auto declval () noexcept -> decltype (__declval (0)); + + template + struct remove_reference + { typedef T type; }; + template + struct remove_reference + { typedef T type; }; + template + struct remove_reference + { typedef T type; }; + + template + constexpr T && + forward (typename std::remove_reference::type &t) noexcept + { return static_cast (t); } + + template + constexpr T && + forward (typename std::remove_reference::type &&t) noexcept + { return static_cast (t); } + + template + constexpr auto + construct_at (T *l, A &&... a) + noexcept (noexcept (::new ((void *) 0) T (std::declval ()...))) + -> decltype (::new ((void *) 0) T (std::declval ()...)) + { return ::new ((void *) l) T (std::forward (a)...); } + + template + constexpr inline void + destroy_at (T *l) + { l->~T (); } + } +} + +inline void *operator new (std::size_t, void *p) noexcept +{ return p; } + +constexpr bool +foo () +{ + std::allocator a; + auto p = a.allocate (2); + std::construct_at (p, 1); + std::construct_at (p + 1, 2); + if (p[0] != 1 || p[1] != 2) + throw 1; + std::destroy_at (p); + std::destroy_at (p + 1); + a.deallocate (p, 2); + return true; +} + +static_assert (foo ()); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C new file mode 100644 index 00000000000..5fc130e4432 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C @@ -0,0 +1,33 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +namespace std +{ + typedef __SIZE_TYPE__ size_t; +} + +inline void *operator new (std::size_t, void *p) noexcept +{ return p; } +void *operator new (std::size_t) noexcept; + +constexpr bool +foo () +{ + auto p = static_cast (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } + *p = 1; + ::operator delete (p); + return false; +} + +struct S { constexpr S () : s (0) {} int s; }; + +constexpr bool +bar () +{ + auto p = static_cast (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } + auto q = new (p) S (); + q->s++; + q->~S (); + ::operator delete (p); + return false; +} -- 2.30.2