diff mbox series

[C++] PR c++/91369 - Implement P0784R7: constexpr new

Message ID 20190927203104.GY15914@tucnak
State New
Headers show
Series [C++] PR c++/91369 - Implement P0784R7: constexpr new | expand

Commit Message

Jakub Jelinek Sept. 27, 2019, 8:31 p.m. UTC
Hi!

The following patch attempts to implement P0784R7, which includes constexpr
destructors and constexpr new/delete/new[]/delete[].

::operator new is allowed during constexpr evaluation and returns address of
an artificial VAR_DECL with special name.  At this point we don't really
know the type of the heap storage, just size.  Later on when we encounter
cast to the corresponding pointer type, we change the name of the var and
type to match the type from the new expression (for new[] we need to do
further stuff as at the point where build_new_1 is called, we might not know
the exact array size, but we shall know that during the constexpr
evaluation, and cookie handling also complicates it a little bit).
When we first store into such heap objects, a ctor is created for them on
the fly.  Finally, ::operator delete marks those heap VAR_DECLs as deleted
and cxx_eval_outermost_constant_expr checks if everything that has been
allocated has been also deallocated and verifies addresses of those heap
vars aren't leaking into the return value.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-09-27  Jakub Jelinek  <jakub@redhat.com>

	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, 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_ctx): Add heap_vars field.
	(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.
	(initialized_type): Handle destructors for c++2a.
	(cxx_fold_indirect_ref): Also handle array fields in structures.
	(non_const_var_error): Add auto_diagnostic_group sentinel.  Emit
	special diagnostics for heap variables.
	(cxx_eval_store_expression): Create ctor for heap variables on the
	first write.  Formatting fix.  Handle const_object_being_modified
	with array type.
	(cxx_eval_loop_expr): Initialize jump_target if NULL.
	(cxx_eval_constant_expression) <case CLEANUP_STMT>: If not skipping
	upon entry to body, run cleanup with the same *jump_target as it
	started to run the cleanup even if the body returns, breaks or
	continues.
	<case NOP_EXPR>: 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.  Allow CLEANUP_STMT.
	* decl.c (initialize_predefined_identifiers): Add heap identifiers.
	(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_constant_dtor 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_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 fix.
	* typeck2.c (store_init_value): Don't call cp_fully_fold_init on
	initializers of automatic non-constexpr variables in constexpr
	functions.
testsuite/
	* g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for
	c++2a.
	* g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics
	in c++17_down, adjust expected wording.
	* g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down.
	* g++.dg/cpp2a/constexpr-dtor1.C: New test.
	* g++.dg/cpp2a/constexpr-dtor2.C: New test.
	* g++.dg/cpp2a/constexpr-dtor3.C: New test.
	* g++.dg/cpp2a/constexpr-new1.C: New test.
	* g++.dg/cpp2a/constexpr-new2.C: New test.
	* g++.dg/cpp2a/constexpr-new3.C: New test.
	* g++.dg/cpp2a/constexpr-new4.C: New test.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and
	__cpp_constexpr_dynamic_alloc tests.  Tweak __cpp_* tests for c++2a
	features to use style like older features, including #ifdef test.
	* g++.dg/ext/is_literal_type3.C: New test.


	Jakub

Comments

Jason Merrill Oct. 1, 2019, 9:56 p.m. UTC | #1
On 9/27/19 4:31 PM, Jakub Jelinek wrote:
> Hi!
> 
> The following patch attempts to implement P0784R7, which includes constexpr
> destructors and constexpr new/delete/new[]/delete[].
> 
> ::operator new is allowed during constexpr evaluation and returns address of
> an artificial VAR_DECL with special name.  At this point we don't really
> know the type of the heap storage, just size.  Later on when we encounter
> cast to the corresponding pointer type, we change the name of the var and
> type to match the type from the new expression (for new[] we need to do
> further stuff as at the point where build_new_1 is called, we might not know
> the exact array size, but we shall know that during the constexpr
> evaluation, and cookie handling also complicates it a little bit).
> When we first store into such heap objects, a ctor is created for them on
> the fly.  Finally, ::operator delete marks those heap VAR_DECLs as deleted
> and cxx_eval_outermost_constant_expr checks if everything that has been
> allocated has been also deallocated and verifies addresses of those heap
> vars aren't leaking into the return value.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2019-09-27  Jakub Jelinek  <jakub@redhat.com>
> 
> 	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, 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_ctx): Add heap_vars field.
> 	(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.
> 	(initialized_type): Handle destructors for c++2a.
> 	(cxx_fold_indirect_ref): Also handle array fields in structures.
> 	(non_const_var_error): Add auto_diagnostic_group sentinel.  Emit
> 	special diagnostics for heap variables.
> 	(cxx_eval_store_expression): Create ctor for heap variables on the
> 	first write.  Formatting fix.  Handle const_object_being_modified
> 	with array type.
> 	(cxx_eval_loop_expr): Initialize jump_target if NULL.
> 	(cxx_eval_constant_expression) <case CLEANUP_STMT>: If not skipping
> 	upon entry to body, run cleanup with the same *jump_target as it
> 	started to run the cleanup even if the body returns, breaks or
> 	continues.
> 	<case NOP_EXPR>: 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.  Allow CLEANUP_STMT.
> 	* decl.c (initialize_predefined_identifiers): Add heap identifiers.
> 	(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_constant_dtor 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_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 fix.
> 	* typeck2.c (store_init_value): Don't call cp_fully_fold_init on
> 	initializers of automatic non-constexpr variables in constexpr
> 	functions.
> testsuite/
> 	* g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for
> 	c++2a.
> 	* g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics
> 	in c++17_down, adjust expected wording.
> 	* g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down.
> 	* g++.dg/cpp2a/constexpr-dtor1.C: New test.
> 	* g++.dg/cpp2a/constexpr-dtor2.C: New test.
> 	* g++.dg/cpp2a/constexpr-dtor3.C: New test.
> 	* g++.dg/cpp2a/constexpr-new1.C: New test.
> 	* g++.dg/cpp2a/constexpr-new2.C: New test.
> 	* g++.dg/cpp2a/constexpr-new3.C: New test.
> 	* g++.dg/cpp2a/constexpr-new4.C: New test.
> 	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and
> 	__cpp_constexpr_dynamic_alloc tests.  Tweak __cpp_* tests for c++2a
> 	features to use style like older features, including #ifdef test.
> 	* g++.dg/ext/is_literal_type3.C: New test.
> 
> --- gcc/c-family/c-cppbuiltin.c.jj	2019-09-26 21:34:21.188923996 +0200
> +++ gcc/c-family/c-cppbuiltin.c	2019-09-27 18:25:41.346059343 +0200
> @@ -989,6 +989,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_constinit=201907");
>   	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
>   	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
> +	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
>   	}
>         if (flag_concepts)
>   	cpp_define (pfile, "__cpp_concepts=201507");
> --- gcc/cp/cp-tree.h.jj	2019-09-27 12:22:54.042880699 +0200
> +++ gcc/cp/cp-tree.h	2019-09-27 18:25:40.903065957 +0200
> @@ -172,6 +172,9 @@ enum cp_tree_index
>       CPTI_VALUE_IDENTIFIER,
>       CPTI_FUN_IDENTIFIER,
>       CPTI_CLOSURE_IDENTIFIER,
> +    CPTI_HEAP_UNINIT_IDENTIFIER,
> +    CPTI_HEAP_IDENTIFIER,
> +    CPTI_HEAP_DELETED_IDENTIFIER,
>   
>       CPTI_LANG_NAME_C,
>       CPTI_LANG_NAME_CPLUSPLUS,
> @@ -310,6 +313,9 @@ extern GTY(()) tree cp_global_trees[CPTI
>   #define value_identifier		cp_global_trees[CPTI_VALUE_IDENTIFIER]
>   #define fun_identifier			cp_global_trees[CPTI_FUN_IDENTIFIER]
>   #define closure_identifier		cp_global_trees[CPTI_CLOSURE_IDENTIFIER]
> +#define heap_uninit_identifier		cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER]
> +#define heap_identifier			cp_global_trees[CPTI_HEAP_IDENTIFIER]
> +#define heap_deleted_identifier		cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER]
>   #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
>   #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
>   
> @@ -6324,6 +6330,7 @@ extern bool vbase_has_user_provided_move
>   extern tree default_init_uninitialized_part (tree);
>   extern bool trivial_default_constructor_is_constexpr (tree);
>   extern bool type_has_constexpr_default_constructor (tree);
> +extern bool type_has_constexpr_destructor	(tree);
>   extern bool type_has_virtual_destructor		(tree);
>   extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
>   extern bool classtype_has_non_deleted_move_ctor (tree);
> @@ -7729,6 +7736,7 @@ extern bool require_constant_expression
>   extern bool require_rvalue_constant_expression (tree);
>   extern bool require_potential_rvalue_constant_expression (tree);
>   extern tree cxx_constant_value			(tree, tree = NULL_TREE);
> +extern void cxx_constant_dtor			(tree, tree);
>   extern tree cxx_constant_init			(tree, tree = NULL_TREE);
>   extern tree maybe_constant_value		(tree, tree = NULL_TREE, bool = false);
>   extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
> --- gcc/cp/class.c.jj	2019-09-26 21:34:21.434920308 +0200
> +++ gcc/cp/class.c	2019-09-27 18:25:40.904065942 +0200
> @@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_
>   static tree end_of_base (tree);
>   static tree get_vcall_index (tree, tree);
>   static bool type_maybe_constexpr_default_constructor (tree);
> +static bool type_maybe_constexpr_destructor (tree);
>   static bool field_poverlapping_p (tree);
>   
>   /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
> @@ -5242,7 +5243,7 @@ type_has_constexpr_default_constructor (
>      without forcing a lazy declaration (which might cause undesired
>      instantiations).  */
>   
> -bool
> +static bool
>   type_maybe_constexpr_default_constructor (tree t)
>   {
>     if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t)
> @@ -5252,6 +5253,34 @@ type_maybe_constexpr_default_constructor
>     return type_has_constexpr_default_constructor (t);
>   }
>   
> +/* Returns true iff class T has a constexpr destructor.  */
> +
> +bool
> +type_has_constexpr_destructor (tree t)
> +{
> +  tree fns;
> +
> +  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
> +    /* Non-trivial, we need to check subobject destructors.  */
> +    lazily_declare_fn (sfk_destructor, t);
> +  fns = CLASSTYPE_DESTRUCTOR (t);
> +  return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
> +}
> +
> +/* Returns true iff class T has a constexpr destructor or has an
> +   implicitly declared destructor that we can't tell if it's constexpr
> +   without forcing a lazy declaration (which might cause undesired
> +   instantiations).  */
> +
> +static bool
> +type_maybe_constexpr_destructor (tree t)
> +{
> +  if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
> +    /* Assume it's constexpr.  */
> +    return true;
> +  return type_has_constexpr_destructor (t);
> +}
> +
>   /* Returns true iff class TYPE has a virtual destructor.  */
>   
>   bool
> @@ -5503,8 +5532,11 @@ finalize_literal_type_property (tree t)
>   {
>     tree fn;
>   
> -  if (cxx_dialect < cxx11
> -      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
> +  if (cxx_dialect < cxx11)
> +    CLASSTYPE_LITERAL_P (t) = false;
> +  else if (CLASSTYPE_LITERAL_P (t)
> +	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
> +	   && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t)))
>       CLASSTYPE_LITERAL_P (t) = false;
>     else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t))
>       CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17);
> @@ -5558,8 +5590,12 @@ explain_non_literal_class (tree t)
>       inform (UNKNOWN_LOCATION,
>   	    "  %qT is a closure type, which is only literal in "
>   	    "C++17 and later", t);
> -  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
> +  else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
>       inform (UNKNOWN_LOCATION, "  %q+T has a non-trivial destructor", t);
> +  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
> +	   && !type_maybe_constexpr_destructor (t))
> +    inform (UNKNOWN_LOCATION, "  %q+T does not have %<constexpr%> destructor",
> +	    t);
>     else if (CLASSTYPE_NON_AGGREGATE (t)
>   	   && !TYPE_HAS_TRIVIAL_DFLT (t)
>   	   && !LAMBDA_TYPE_P (t)
> --- gcc/cp/constexpr.c.jj	2019-09-27 20:33:37.600208356 +0200
> +++ gcc/cp/constexpr.c	2019-09-27 20:38:38.203710246 +0200
> @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
>   #include "gimple-fold.h"
>   #include "timevar.h"
>   #include "fold-const-call.h"
> +#include "stor-layout.h"
>   
>   static bool verify_constant (tree, bool, bool *, bool *);
>   #define VERIFY_CONSTANT(X)						\
> @@ -1031,6 +1032,9 @@ struct constexpr_ctx {
>        on simple constants or location wrappers) encountered during current
>        cxx_eval_outermost_constant_expr call.  */
>     HOST_WIDE_INT *constexpr_ops_count;
> +  /* Heap VAR_DECLs created during the evaluation of the outermost constant
> +     expression.  */
> +  vec<tree> *heap_vars;
>   
>     /* Whether we should error on a non-constant expression or fail quietly.  */
>     bool quiet;
> @@ -1666,6 +1670,58 @@ 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)
> +	{
> +	  const int nargs = call_expr_nargs (t);
> +	  tree arg0 = 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)
> +		arg0 = arg;
> +	    }
> +	  gcc_assert (arg0);
> +	  if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
> +	    {
> +	      tree type = build_array_type_nelts (char_type_node,
> +						  tree_to_uhwi (arg0));
> +	      tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier,
> +				     type);
> +	      DECL_ARTIFICIAL (var) = 1;
> +	      TREE_STATIC (var) = 1;
> +	      ctx->heap_vars->safe_push (var);
> +	      return fold_convert (ptr_type_node, build_address (var));
> +	    }
> +	  else
> +	    {
> +	      STRIP_NOPS (arg0);
> +	      if (TREE_CODE (arg0) == ADDR_EXPR
> +		  && VAR_P (TREE_OPERAND (arg0, 0)))
> +		{
> +		  tree var = TREE_OPERAND (arg0, 0);
> +		  if (DECL_NAME (var) == heap_uninit_identifier
> +		      || DECL_NAME (var) == heap_identifier)
> +		    {
> +		      DECL_NAME (var) = heap_deleted_identifier;
> +		      ctx->values->remove (var);
> +		      return void_node;
> +		    }
> +		  else if (DECL_NAME (var) == heap_deleted_identifier)
> +		    {
> +		      if (!ctx->quiet)
> +			error_at (loc, "deallocation of already deallocated "
> +				       "storage");
> +		      *non_constant_p = true;
> +		      return t;
> +		    }

Don't we need an error for trying to deallocate something that wasn't 
allocated within the constexpr evaluation?

> +		}
> +	    }
> +	}
>         if (!ctx->quiet)
>   	{
>   	  if (!lambda_static_thunk_p (fun))
> @@ -2998,8 +3054,8 @@ base_field_constructor_elt (vec<construc
>   }
>   
>   /* Some of the expressions fed to the constexpr mechanism are calls to
> -   constructors, which have type void.  In that case, return the type being
> -   initialized by the constructor.  */
> +   constructors or destructors, which have type void.  In that case,
> +   return the type being initialized by the constructor.  */
>   
>   static tree
>   initialized_type (tree t)
> @@ -3011,8 +3067,10 @@ initialized_type (tree t)
>       {
>         /* A constructor call has void type, so we need to look deeper.  */
>         tree fn = get_function_named_in_call (t);
> -      if (fn && TREE_CODE (fn) == FUNCTION_DECL
> -	  && DECL_CXX_CONSTRUCTOR_P (fn))
> +      if (fn
> +	  && TREE_CODE (fn) == FUNCTION_DECL
> +	  && (DECL_CXX_CONSTRUCTOR_P (fn)
> +	      || (cxx_dialect >= cxx2a && DECL_CXX_DESTRUCTOR_P (fn))))
>   	type = DECL_CONTEXT (fn);

Why is this needed?  A destructor doesn't initialize anything, so 
returning void seems appropriate.

>       }
>     else if (TREE_CODE (t) == COMPOUND_EXPR)
> @@ -3434,11 +3492,24 @@ cxx_fold_indirect_ref (location_t loc, t
>   	{
>   	  tree field = TYPE_FIELDS (optype);
>   	  for (; field; field = DECL_CHAIN (field))
> -	    if (TREE_CODE (field) == FIELD_DECL
> -		&& TREE_TYPE (field) != error_mark_node
> -		&& integer_zerop (byte_position (field))
> -		&& similar_type_p (TREE_TYPE (field), type))
> +	    if (TREE_CODE (field) != FIELD_DECL
> +		|| TREE_TYPE (field) == error_mark_node
> +		|| !integer_zerop (byte_position (field)))
> +	      continue;
> +	    else if (similar_type_p (TREE_TYPE (field), type))
>   	      return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE);
> +	    else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
> +		     && similar_type_p (TREE_TYPE (TREE_TYPE (field)), type))
> +	      {
> +		tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
> +		tree min_val = size_zero_node;
> +		if (type_domain && TYPE_MIN_VALUE (type_domain))
> +		  min_val = TYPE_MIN_VALUE (type_domain);
> +		op = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
> +				  op, field, NULL_TREE);
> +		return build4_loc (loc, ARRAY_REF, type, op, min_val,
> +				   NULL_TREE, NULL_TREE);
> +	      }
>   	}
>       }
>     else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
> @@ -3521,12 +3592,44 @@ cxx_fold_indirect_ref (location_t loc, t
>   	    {
>   	      tree field = TYPE_FIELDS (op00type);
>   	      for (; field; field = DECL_CHAIN (field))
> -		if (TREE_CODE (field) == FIELD_DECL
> -		    && TREE_TYPE (field) != error_mark_node
> -		    && tree_int_cst_equal (byte_position (field), op01)
> -		    && similar_type_p (TREE_TYPE (field), type))
> +		if (TREE_CODE (field) != FIELD_DECL
> +		    || TREE_TYPE (field) == error_mark_node)
> +		  continue;
> +		else if (tree_int_cst_equal (byte_position (field), op01)
> +			 && similar_type_p (TREE_TYPE (field), type))
>   		  return fold_build3 (COMPONENT_REF, type, op00,
>   				      field, NULL_TREE);
> +		else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
> +			 && tree_int_cst_le (byte_position (field), op01)
> +			 && similar_type_p (TREE_TYPE (TREE_TYPE (field)),
> +					    type))
> +		  {
> +		    tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
> +		    tree min_val = size_zero_node;
> +		    tree max_val = NULL_TREE;
> +		    if (type_domain && TYPE_MIN_VALUE (type_domain))
> +		      min_val = TYPE_MIN_VALUE (type_domain);
> +		    if (type_domain && TYPE_MAX_VALUE (type_domain))
> +		      max_val = TYPE_MAX_VALUE (type_domain);
> +		    offset_int off = wi::to_offset (op01);
> +		    off -= wi::to_offset (byte_position (field));
> +		    offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (type));
> +		    offset_int remainder;
> +		    off = wi::divmod_trunc (off, el_sz, SIGNED, &remainder);
> +		    if (remainder == 0
> +			&& TREE_CODE (min_val) == INTEGER_CST
> +			&& (max_val == NULL_TREE
> +			    || (TREE_CODE (max_val) == INTEGER_CST
> +				&& off <= wi::to_offset (max_val))))
> +		      {
> +			off = off + wi::to_offset (min_val);
> +			op00 = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
> +					    op00, field, NULL_TREE);
> +			op01 = wide_int_to_tree (sizetype, off);
> +			return build4_loc (loc, ARRAY_REF, type, op00, op01,
> +					   NULL_TREE, NULL_TREE);
> +		      }
> +		  }

I think we want to factor this function more, so we don't have the same 
code in multiple places for handling an array, and an array member, and 
a pointer to array.  Do you want to take a look at bug 71504 while 
you're touching this code?

> @@ -3645,7 +3748,23 @@ cxx_eval_indirect_ref (const constexpr_c
>   static void
>   non_const_var_error (tree r)
>   {
> +  auto_diagnostic_group d;
>     tree type = TREE_TYPE (r);
> +  if (DECL_NAME (r) == heap_uninit_identifier
> +      || DECL_NAME (r) == heap_identifier)
> +    {
> +      error ("the content of uninitialized storage is not usable "
> +	     "in a constant expression");
> +      inform (DECL_SOURCE_LOCATION (r), "allocated here");
> +      return;
> +    }
> +  if (DECL_NAME (r) == heap_deleted_identifier)
> +    {
> +      error ("use of allocated storage after deallocation in a "
> +	     "constant expression");
> +      inform (DECL_SOURCE_LOCATION (r), "allocated here");
> +      return;
> +    }
>     error ("the value of %qD is not usable in a constant "
>   	 "expression", r);
>     /* Avoid error cascade.  */
> @@ -3892,6 +4011,15 @@ cxx_eval_store_expression (const constex
>       valp = ctx->values->get (object);
>     else
>       valp = NULL;
> +  if (!valp
> +      && VAR_P (object)
> +      && DECL_NAME (object) == heap_identifier)
> +    {
> +      tree ctor = build_constructor (type, NULL);
> +      CONSTRUCTOR_NO_CLEARING (ctor) = true;
> +      ctx->values->put (object, ctor);
> +      valp = ctx->values->get (object);
> +    }

Instead of this, how about giving the object NULL_TREE value when we 
create it in cxx_eval_call_expression?

>     if (!valp)
>       {
>         /* A constant-expression cannot modify objects from outside the
> @@ -3905,7 +4033,7 @@ cxx_eval_store_expression (const constex
>     bool no_zero_init = true;
>   
>     releasing_vec ctors;
> -  while (!refs->is_empty())
> +  while (!refs->is_empty ())
>       {
>         if (*valp == NULL_TREE)
>   	{
> @@ -4046,7 +4174,9 @@ cxx_eval_store_expression (const constex
>     if (const_object_being_modified)
>       {
>         bool fail = false;
> -      if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
> +      tree const_objtype
> +	= strip_array_types (TREE_TYPE (const_object_being_modified));
> +      if (!CLASS_TYPE_P (const_objtype))

This looks like an unrelated bugfix; you might commit it (and the 
others) separately if that's convenient.

>   	fail = true;
>         else
>   	{
> @@ -4365,6 +4495,12 @@ cxx_eval_loop_expr (const constexpr_ctx
>   		    tree *jump_target)
>   {
>     constexpr_ctx new_ctx = *ctx;
> +  tree local_target;
> +  if (!jump_target)
> +    {
> +      local_target = NULL_TREE;
> +      jump_target = &local_target;
> +    }
>   
>     tree body, cond = NULL_TREE, expr = NULL_TREE;
>     int count = 0;
> @@ -4907,14 +5043,21 @@ cxx_eval_constant_expression (const cons
>         break;
>   
>       case CLEANUP_STMT:
> -      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> +      {
> +	tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
> +	r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> +					  non_constant_p, overflow_p,
> +					  jump_target);
> +	if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
> +	  /* Also evaluate the cleanup.  If we weren't skipping at the
> +	     start of the CLEANUP_BODY, change jump_target temporarily
> +	     to &initial_jump_target, so that even a return or break or
> +	     continue in the body doesn't skip the cleanup.  */

This also looks like an unrelated bugfix.

> +	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
>   					non_constant_p, overflow_p,
> -					jump_target);
> -      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
> -	/* Also evaluate the cleanup.  */
> -	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
> -				      non_constant_p, overflow_p,
> -				      jump_target);
> +					jump_target ? &initial_jump_target
> +					: NULL);
> +      }
>         break;
>   
>         /* These differ from cxx_eval_unary_expression in that this doesn't
> @@ -5203,8 +5346,7 @@ cxx_eval_constant_expression (const cons
>   	if (VOID_TYPE_P (type))
>   	  return void_node;
>   
> -	if (TREE_CODE (op) == PTRMEM_CST
> -	    && !TYPE_PTRMEM_P (type))
> +	if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
>   	  op = cplus_expand_constant (op);
>   
>   	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
> @@ -5258,6 +5400,81 @@ cxx_eval_constant_expression (const cons
>   	      }
>   	  }
>   
> +	if (INDIRECT_TYPE_P (type)
> +	    && TREE_CODE (op) == NOP_EXPR
> +	    && TREE_TYPE (op) == ptr_type_node
> +	    && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR
> +	    && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
> +	    && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0),
> +					0)) == heap_uninit_identifier)
> +	  {
> +	    tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0);
> +	    tree var_type = TREE_TYPE (type);
> +	    tree cookie_type = NULL_TREE;
> +	    bool array_p = false;
> +	    HOST_WIDE_INT cookie_size = 0;
> +	    if (TREE_CODE (var_type) == ARRAY_TYPE
> +		&& TYPE_DOMAIN (var_type) == NULL_TREE)
> +	      {
> +		var_type = TREE_TYPE (var_type);
> +		array_p = true;
> +	      }
> +	    else if (TREE_CODE (var_type) == RECORD_TYPE
> +		     && TYPE_NAME (var_type) == NULL_TREE)
> +	      if (tree fld1 = TYPE_FIELDS (var_type))
> +		if (TREE_CODE (fld1) == FIELD_DECL
> +		    && DECL_NAME (fld1) == NULL_TREE
> +		    && DECL_ARTIFICIAL (fld1)
> +		    && TREE_CODE (TREE_TYPE (fld1)) == ARRAY_TYPE
> +		    && COMPLETE_TYPE_P (TREE_TYPE (fld1)))
> +		  if (tree fld2 = DECL_CHAIN (fld1))
> +		    if (TREE_CODE (fld2) == FIELD_DECL
> +			&& DECL_NAME (fld2) == NULL_TREE
> +			&& DECL_ARTIFICIAL (fld2)
> +			&& TREE_CODE (TREE_TYPE (fld2)) == ARRAY_TYPE
> +			&& TYPE_DOMAIN (TREE_TYPE (fld2)) == NULL_TREE
> +			&& DECL_CHAIN (fld2) == NULL_TREE)

Maybe give the struct a magic name so you don't need to do as much 
checking of the FIELD_DECLs?

> +		      {
> +			var_type = TREE_TYPE (TREE_TYPE (fld2));
> +			array_p = true;
> +			cookie_type = TREE_TYPE (fld1);
> +			cookie_size = int_size_in_bytes (TREE_TYPE (fld1));
> +		      }
> +	    HOST_WIDE_INT sz1 = int_size_in_bytes (var_type);
> +	    HOST_WIDE_INT sz2 = int_size_in_bytes (TREE_TYPE (var));
> +	    if (sz1 <= sz2 && cookie_size <= sz2)
> +	      {
> +		DECL_NAME (var) = heap_identifier;
> +		if (array_p && sz1 > 0)
> +		  {
> +		    sz2 -= cookie_size;
> +		    sz2 /= sz1;
> +		    tree idx_type = build_index_type (size_int (sz2 - 1));
> +		    var_type = build_cplus_array_type (var_type, idx_type);
> +		    if (cookie_type)
> +		      {
> +			location_t loc = cp_expr_loc_or_input_loc (t);
> +			tree vtype = cxx_make_type (RECORD_TYPE);
> +			tree fld1 = build_decl (loc, FIELD_DECL, NULL_TREE,
> +						cookie_type);
> +			tree fld2 = build_decl (loc, FIELD_DECL, NULL_TREE,
> +						var_type);
> +			DECL_FIELD_CONTEXT (fld1) = vtype;
> +			DECL_FIELD_CONTEXT (fld2) = vtype;
> +			DECL_ARTIFICIAL (fld1) = true;
> +			DECL_ARTIFICIAL (fld2) = true;
> +			TYPE_FIELDS (vtype) = fld1;
> +			DECL_CHAIN (fld1) = fld2;
> +			layout_type (vtype);
> +			var_type = vtype;

So here you're completing the type of the array member of the struct.

> +		TREE_TYPE (var) = var_type;
> +		TREE_TYPE (TREE_OPERAND (op, 0))
> +		  = build_pointer_type (var_type);
> +	      }
> +	  }

Let's factor out all of this code, too.

> +
>   	if (op == oldop && tcode != UNARY_PLUS_EXPR)
>   	  /* We didn't fold at the top so we could check for ptr-int
>   	     conversion.  */
> @@ -5499,6 +5716,7 @@ instantiate_cx_fn_r (tree *tp, int *walk
>   
>     return NULL_TREE;
>   }
> +
>   static void
>   instantiate_constexpr_fns (tree t)
>   {
> @@ -5507,17 +5725,36 @@ instantiate_constexpr_fns (tree t)
>     input_location = loc;
>   }
>   
> +/* Look for heap variables in the expression *TP.  */
> +
> +static tree
> +find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
> +{
> +  if (VAR_P (*tp)
> +      && (DECL_NAME (*tp) == heap_uninit_identifier
> +	  || DECL_NAME (*tp) == heap_identifier
> +	  || DECL_NAME (*tp) == heap_deleted_identifier))
> +    return *tp;
> +
> +  if (TYPE_P (*tp))
> +    *walk_subtrees = 0;
> +  return NULL_TREE;
> +}
> +
>   /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
>      STRICT has the same sense as for constant_value_1: true if we only allow
>      conforming C++ constant expressions, or false if we want a constant value
>      even if it doesn't conform.
>      MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
> -   per P0595 even when ALLOW_NON_CONSTANT is true.  */
> +   per P0595 even when ALLOW_NON_CONSTANT is true.
> +   CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable.
> +   OBJECT must be non-NULL in that case.  */

>   static tree
>   cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
>   				  bool strict = true,
>   				  bool manifestly_const_eval = false,
> +				  bool constexpr_dtor = false,
>   				  tree object = NULL_TREE)
>   {
>     auto_timevar time (TV_CONSTEXPR);
> @@ -5525,16 +5762,23 @@ cxx_eval_outermost_constant_expr (tree t
>     bool non_constant_p = false;
>     bool overflow_p = false;
>     hash_map<tree,tree> map;
> +  auto_vec<tree, 16> heap_vars;
>     HOST_WIDE_INT constexpr_ctx_count = 0;
>   
>     constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
> -			&constexpr_ctx_count, allow_non_constant, strict,
> -			manifestly_const_eval || !allow_non_constant };
> +			&constexpr_ctx_count, &heap_vars, allow_non_constant,
> +			strict, manifestly_const_eval || !allow_non_constant };

As we add more stuff to constexpr_ctx, creating new ones on the stack 
becomes more and more expensive.  We should really split off the parts 
that change frequently: Maybe just ctor/object, maybe also 
call/save_exprs/...?

>     tree type = initialized_type (t);
>     tree r = t;
>     if (VOID_TYPE_P (type))
> -    return t;
> +    {
> +      if (TREE_CODE (t) == BIND_EXPR && constexpr_dtor)
> +	/* Used for destructors of array elements.  */
> +	type = TREE_TYPE (object);
> +      else
> +	return t;
> +    } >     if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
>       {
>         /* In C++14 an NSDMI can participate in aggregate initialization,
> @@ -5544,8 +5788,22 @@ cxx_eval_outermost_constant_expr (tree t
>   	 update ctx.values for the VAR_DECL.  We use the same strategy
>   	 for C++11 constexpr constructors that refer to the object being
>   	 initialized.  */
> -      ctx.ctor = build_constructor (type, NULL);
> -      CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
> +      if (constexpr_dtor)
> +	{
> +	  gcc_assert (object && VAR_P (object));
> +	  gcc_assert (DECL_DECLARED_CONSTEXPR_P (object));
> +	  gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object));
> +	  ctx.ctor = unshare_expr (DECL_INITIAL (object));
> +	  TREE_READONLY (ctx.ctor) = false;
> +	  /* Temporarily force decl_really_constant_value to return false
> +	     for it, we want to use ctx.ctor for the current value instead.  */
> +	  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false;
> +	}
> +      else
> +	{
> +	  ctx.ctor = build_constructor (type, NULL);
> +	  CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
> +	}
>         if (!object)
>   	{
>   	  if (TREE_CODE (t) == TARGET_EXPR)
> @@ -5569,13 +5827,15 @@ cxx_eval_outermost_constant_expr (tree t
>     r = cxx_eval_constant_expression (&ctx, r,
>   				    false, &non_constant_p, &overflow_p);
>   
> -  verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
> +  if (!constexpr_dtor)
> +    verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
> +  else
> +    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
>   
>     /* Mutable logic is a bit tricky: we want to allow initialization of
>        constexpr variables with mutable members, but we can't copy those
>        members to another constexpr variable.  */
> -  if (TREE_CODE (r) == CONSTRUCTOR
> -      && CONSTRUCTOR_MUTABLE_POISON (r))
> +  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
>       {
>         if (!allow_non_constant)
>   	error ("%qE is not a constant expression because it refers to "
> @@ -5583,8 +5843,7 @@ cxx_eval_outermost_constant_expr (tree t
>         non_constant_p = true;
>       }
>   
> -  if (TREE_CODE (r) == CONSTRUCTOR
> -      && CONSTRUCTOR_NO_CLEARING (r))
> +  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
>       {
>         if (!allow_non_constant)
>   	error ("%qE is not a constant expression because it refers to "
> @@ -5593,6 +5852,32 @@ cxx_eval_outermost_constant_expr (tree t
>         non_constant_p = true;
>       }
>   
> +  if (!heap_vars.is_empty ())
> +    {
> +      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
> +						       NULL);

Doesn't verify_constant already complain about remaining references to 
allocated objects?

> +      unsigned int i;
> +      if (heap_var)
> +	{
> +	  if (!allow_non_constant && !non_constant_p)
> +	    error_at (DECL_SOURCE_LOCATION (heap_var),
> +		      "%qE is not a constant expression because it refers to "
> +		      "a result of %<operator new%>", t);
> +	  r = t;
> +	  non_constant_p = true;
> +	}
> +      FOR_EACH_VEC_ELT (heap_vars, i, heap_var)
> +	if (DECL_NAME (heap_var) != heap_deleted_identifier)
> +	  {
> +	    if (!allow_non_constant && !non_constant_p)
> +	      error_at (DECL_SOURCE_LOCATION (heap_var),
> +			"%qE is not a constant expression because allocated "
> +			"storage has not been deallocated", t);
> +	    r = t;
> +	    non_constant_p = true;
> +	  }
> +    }
> +
>     /* Technically we should check this for all subexpressions, but that
>        runs into problems with our internal representation of pointer
>        subtraction and the 5.19 rules are still in flux.  */
> @@ -5618,6 +5903,8 @@ cxx_eval_outermost_constant_expr (tree t
>   
>     if (non_constant_p && !allow_non_constant)
>       return error_mark_node;
> +  else if (constexpr_dtor)
> +    return r;
>     else if (non_constant_p && TREE_CONSTANT (r))
>       {
>         /* If __builtin_is_constant_evaluated () was evaluated to true
> @@ -5625,7 +5912,7 @@ cxx_eval_outermost_constant_expr (tree t
>   	 punt.  */
>         if (manifestly_const_eval)
>   	return cxx_eval_outermost_constant_expr (t, true, strict,
> -						 false, object);
> +						 false, false, object);
>         /* This isn't actually constant, so unset TREE_CONSTANT.
>   	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
>   	 it to be set if it is invariant address, even when it is not
> @@ -5653,7 +5940,7 @@ cxx_eval_outermost_constant_expr (tree t
>   	return t;
>         else if (TREE_CODE (t) != CONSTRUCTOR)
>   	{
> -	  r = get_target_expr (r);
> +	  r = get_target_expr_sfinae (r, tf_warning_or_error | tf_no_cleanup);
>   	  TREE_CONSTANT (r) = true;
>   	}
>       }
> @@ -5668,7 +5955,16 @@ cxx_eval_outermost_constant_expr (tree t
>   tree
>   cxx_constant_value (tree t, tree decl)
>   {
> -  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
> +  return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl);
> +}
> +
> +/* Like cxx_constant_value, but used for evaluation of constexpr destructors
> +   of constexpr variables.  The actual initializer of DECL is not modified.  */
> +
> +void
> +cxx_constant_dtor (tree t, tree decl)
> +{
> +  cxx_eval_outermost_constant_expr (t, false, true, true, true, decl);
>   }
>   
>   /* Helper routine for fold_simple function.  Either return simplified
> @@ -5772,14 +6068,14 @@ maybe_constant_value (tree t, tree decl,
>       return t;
>   
>     if (manifestly_const_eval)
> -    return cxx_eval_outermost_constant_expr (t, true, true, true, decl);
> +    return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl);
>   
>     if (cv_cache == NULL)
>       cv_cache = hash_map<tree, tree>::create_ggc (101);
>     if (tree *cached = cv_cache->get (t))
>       return *cached;
>   
> -  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
> +  r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl);
>     gcc_checking_assert (r == t
>   		       || CONVERT_EXPR_P (t)
>   		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
> @@ -5841,7 +6137,7 @@ fold_non_dependent_expr_template (tree t
>   
>         tree r = cxx_eval_outermost_constant_expr (t, true, true,
>   						 manifestly_const_eval,
> -						 NULL_TREE);
> +						 false, NULL_TREE);
>         /* cp_tree_equal looks through NOPs, so allow them.  */
>         gcc_checking_assert (r == t
>   			   || CONVERT_EXPR_P (t)
> @@ -5945,7 +6241,7 @@ maybe_constant_init_1 (tree t, tree decl
>     else
>       t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
>   					  /*strict*/false,
> -					  manifestly_const_eval, decl);
> +					  manifestly_const_eval, false, decl);
>     if (TREE_CODE (t) == TARGET_EXPR)
>       {
>         tree init = TARGET_EXPR_INITIAL (t);
> @@ -6239,7 +6535,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))

This is the second occurrence of this three-line test for a constexpr 
(de)allocation function, let's factor it out.

>   		  {
>   		    if (flags & tf_error)
>   		      {
> @@ -6468,6 +6769,9 @@ potential_constant_expression_1 (tree t,
>   	goto fail;
>         if (!RECUR (TREE_OPERAND (t, 0), any))
>   	return false;
> +      /* Just ignore clobbers.  */
> +      if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
> +	return true;
>         if (!RECUR (TREE_OPERAND (t, 1), rval))
>   	return false;
>         return true;
> @@ -6937,7 +7241,7 @@ potential_constant_expression_1 (tree t,
>        return true;
>   
>       case COND_EXPR:
> -      if (COND_EXPR_IS_VEC_DELETE (t))
> +      if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx2a)
>   	{
>   	  if (flags & tf_error)
>   	    error_at (loc, "%<delete[]%> is not a constant expression");
> @@ -6983,6 +7287,12 @@ potential_constant_expression_1 (tree t,
>         return true;
>   
>       case CLEANUP_STMT:
> +      if (!RECUR (CLEANUP_BODY (t), any))
> +	return false;
> +      if (!CLEANUP_EH_ONLY (t) && !RECUR (CLEANUP_EXPR (t), any))
> +	return false;
> +      return true;
> +
>       case EMPTY_CLASS_EXPR:
>       case PREDICT_EXPR:
>         return false;
> --- gcc/cp/decl.c.jj	2019-09-26 21:34:21.673916726 +0200
> +++ gcc/cp/decl.c	2019-09-27 18:25:40.898066032 +0200
> @@ -4146,6 +4146,9 @@ initialize_predefined_identifiers (void)
>       {"value", &value_identifier, cik_normal},
>       {"_FUN", &fun_identifier, cik_normal},
>       {"__closure", &closure_identifier, cik_normal},
> +    {"heap uninit", &heap_uninit_identifier, cik_normal},
> +    {"heap ", &heap_identifier, cik_normal},
> +    {"heap deleted", &heap_deleted_identifier, cik_normal},
>       {NULL, NULL, cik_normal}
>     };
>   
> @@ -7430,7 +7433,11 @@ cp_finish_decl (tree decl, tree init, bo
>   	    TREE_READONLY (decl) = 1;
>   
>   	  /* Likewise if it needs destruction.  */
> -	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
> +	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
> +	      && (cxx_dialect < cxx2a
> +		  || !DECL_DECLARED_CONSTEXPR_P (decl)
> +		  || !type_has_constexpr_destructor
> +						(strip_array_types (type))))

This could use a decl_maybe_constant_destruction predicate.

>   	    TREE_READONLY (decl) = 0;
>   	}
>   
> @@ -8319,6 +8326,27 @@ register_dtor_fn (tree decl)
>     if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
>       return void_node;
>   
> +  if (cxx_dialect >= cxx2a
> +      && DECL_DECLARED_CONSTEXPR_P (decl)
> +      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
> +      && type_has_constexpr_destructor (strip_array_types (type)))

...which we'd use again here.

> +    {
> +      int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR;
> +      tree addr, call;
> +
> +      if (TREE_CODE (type) == ARRAY_TYPE)
> +	addr = decl;
> +      else
> +	addr = build_address (decl);
> +
> +      call = build_delete (TREE_TYPE (addr), addr,
> +			   sfk_complete_destructor, flags, 0,
> +			   tf_warning_or_error);
> +      if (call != error_mark_node)
> +	cxx_constant_dtor (call, decl);
> +      return void_node;

Why not use the result of build_cleanup?

> +    }
> +
>     /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
>        "__aeabi_atexit"), and DECL is a class object, we can just pass the
>        destructor to "__cxa_atexit"; we don't have to build a temporary
> @@ -8432,11 +8460,15 @@ register_dtor_fn (tree decl)
>   static void
>   expand_static_init (tree decl, tree init)
>   {
> +  tree type = TREE_TYPE (decl);
>     gcc_assert (VAR_P (decl));
>     gcc_assert (TREE_STATIC (decl));
>   
>     /* Some variables require no dynamic initialization.  */
> -  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
> +  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))
> +      || (cxx_dialect >= cxx2a
> +	  && DECL_DECLARED_CONSTEXPR_P (decl)
> +	  && type_has_constexpr_destructor (strip_array_types (type))))
>       {
>         /* Make sure the destructor is callable.  */
>         cxx_maybe_build_cleanup (decl, tf_warning_or_error);
> @@ -12702,12 +12734,13 @@ grokdeclarator (const cp_declarator *dec
>   			      "a destructor cannot be %<concept%>");
>                       return error_mark_node;
>                     }
> -                if (constexpr_p)
> -                  {
> -                    error_at (declspecs->locations[ds_constexpr],
> -			      "a destructor cannot be %<constexpr%>");
> -                    return error_mark_node;
> -                  }
> +		if (constexpr_p && cxx_dialect < cxx2a)
> +		  {
> +		    error_at (declspecs->locations[ds_constexpr],
> +			      "%<constexpr%> destructors only available"
> +			      " with %<-std=c++2a%> or %<-std=gnu++2a%>");
> +		    return error_mark_node;
> +		  }
>   	      }
>   	    else if (sfk == sfk_constructor && friendp && !ctype)
>   	      {
> @@ -12744,10 +12777,11 @@ grokdeclarator (const cp_declarator *dec
>   	      }
>   
>   	    /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node.  */
> -	    function_context = (ctype != NULL_TREE) ?
> -	      decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE;
> -	    publicp = (! friendp || ! staticp)
> -	      && function_context == NULL_TREE;
> +	    function_context
> +	      = (ctype != NULL_TREE
> +		 ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE);
> +	    publicp = ((! friendp || ! staticp)
> +		       && function_context == NULL_TREE);
>   
>   	    decl = grokfndecl (ctype, type,
>   			       TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR
> @@ -16752,6 +16786,12 @@ cxx_maybe_build_cleanup (tree decl, tsub
>   	cleanup = error_mark_node;
>         else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
>   	/* Discard the call.  */;
> +      else if (cxx_dialect >= cxx2a
> +	       && VAR_P (decl)
> +	       && DECL_DECLARED_CONSTEXPR_P (decl)
> +	       && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
> +	       && type_has_constexpr_destructor (strip_array_types (type)))
> +	cxx_constant_dtor (call, decl);
>         else if (cleanup)
>   	cleanup = cp_build_compound_expr (cleanup, call, complain);
>         else
> --- gcc/cp/init.c.jj	2019-09-26 21:34:21.598917851 +0200
> +++ gcc/cp/init.c	2019-09-27 19:15:56.473042238 +0200
> @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.
>   #include "stringpool.h"
>   #include "attribs.h"
>   #include "asan.h"
> +#include "stor-layout.h"
>   
>   static bool begin_init_stmts (tree *, tree *);
>   static tree finish_init_stmts (bool, tree, tree);
> @@ -3332,6 +3333,48 @@ build_new_1 (vec<tree, va_gc> **placemen
>   	}
>       }
>   
> +  tree alloc_call_call = extract_call_expr (alloc_call);
> +  tree alloc_call_fndecl = NULL_TREE;
> +  if (alloc_call_call != error_mark_node)
> +    alloc_call_fndecl = cp_get_callee_fndecl_nofold (alloc_call_call);
> +  if (array_p
> +      && cxx_dialect >= cxx2a
> +      && (current_function_decl == NULL_TREE
> +	  || DECL_DECLARED_CONSTEXPR_P (current_function_decl))
> +      && alloc_call_fndecl
> +      && IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl))
> +      && CP_DECL_CONTEXT (alloc_call_fndecl) == global_namespace)
> +    {
> +      /* Help the constexpr code to find the right type for the heap variable
> +	 by adding a NOP_EXPR around alloc_call.
> +	 If not using cookies, use array type, otherwise structure with
> +	 two array types.  */

i.e.

struct {
   size_t cookie[N];
   elt array[];
};

I guess you want an array for the cookie rather than a non-array size_t 
data member to handle ARM cookies that also include the element size. 
Please include this in the comment.

> +      tree atype = build_cplus_array_type (elt_type, NULL_TREE);
> +      if (cookie_size)
> +	{
> +	  gcc_assert (tree_fits_uhwi_p (cookie_size));
> +	  unsigned HOST_WIDE_INT sz = tree_to_uhwi (cookie_size);
> +	  sz /= int_size_in_bytes (sizetype);
> +	  tree atype2 = build_index_type (size_int (sz - 1));
> +	  atype2 = build_cplus_array_type (sizetype, atype2);
> +	  tree atype3 = cxx_make_type (RECORD_TYPE);
> +	  tree fld1 = build_decl (input_location, FIELD_DECL, NULL_TREE,
> +				  atype2);
> +	  tree fld2 = build_decl (input_location, FIELD_DECL, NULL_TREE,
> +				  atype);
> +	  DECL_FIELD_CONTEXT (fld1) = atype3;
> +	  DECL_FIELD_CONTEXT (fld2) = atype3;
> +	  DECL_ARTIFICIAL (fld1) = true;
> +	  DECL_ARTIFICIAL (fld2) = true;
> +	  TYPE_FIELDS (atype3) = fld1;
> +	  DECL_CHAIN (fld1) = fld2;
> +	  layout_type (atype3);
> +	  atype = atype3;
> +	}
> +      pointer_type = build_pointer_type (atype);
> +      alloc_call = build_nop (pointer_type, alloc_call);
> +    }

All of this could be factored out into a function called something like 
maybe_wrap_new_for_constexpr.

> +
>     /* In the simple case, we can stop now.  */
>     pointer_type = build_pointer_type (type);
>     if (!cookie_size && !is_initialized)
> @@ -3905,17 +3948,11 @@ build_vec_delete_1 (tree base, tree maxi
>   			     fold_convert (sizetype, maxindex));
>   
>     tbase = create_temporary_var (ptype);
> -  tbase_init
> -    = cp_build_modify_expr (input_location, tbase, NOP_EXPR,
> -			    fold_build_pointer_plus_loc (input_location,
> -							 fold_convert (ptype,
> -								       base),
> -							 virtual_size),
> -			    complain);
> -  if (tbase_init == error_mark_node)
> -    return error_mark_node;
> -  controller = build3 (BIND_EXPR, void_type_node, tbase,
> -		       NULL_TREE, NULL_TREE);
> +  DECL_INITIAL (tbase)
> +    = fold_build_pointer_plus_loc (input_location, fold_convert (ptype, base),
> +				   virtual_size);
> +  tbase_init = build_stmt (input_location, DECL_EXPR, tbase);
> +  controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE);
>     TREE_SIDE_EFFECTS (controller) = 1;
>   
>     body = build1 (EXIT_EXPR, void_type_node,
> --- gcc/cp/method.c.jj	2019-09-26 21:34:21.331921851 +0200
> +++ gcc/cp/method.c	2019-09-27 18:25:40.902065972 +0200
> @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.
>   #include "cgraph.h"
>   #include "varasm.h"
>   #include "toplev.h"
> +#include "intl.h"
>   #include "common/common-target.h"
>   
>   static void do_build_copy_assign (tree);
> @@ -1237,12 +1238,24 @@ is_xible (enum tree_code code, tree to,
>     return !!expr;
>   }
>   
> +/* Categorize various special_function_kinds.  */
> +#define SFK_CTOR_P(sfk) \
> +  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
> +#define SFK_DTOR_P(sfk) \
> +  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
> +#define SFK_ASSIGN_P(sfk) \
> +  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
> +#define SFK_COPY_P(sfk) \
> +  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
> +#define SFK_MOVE_P(sfk) \
> +  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
> +
>   /* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
>      DELETED_P or give an error message MSG with argument ARG.  */
>   
>   static void
> -process_subob_fn (tree fn, tree *spec_p, bool *trivial_p,
> -		  bool *deleted_p, bool *constexpr_p,
> +process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p,
> +		  bool *trivial_p, bool *deleted_p, bool *constexpr_p,
>   		  bool diag, tree arg, bool dtor_from_ctor = false)
>   {
>     if (!fn || fn == error_mark_node)
> @@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p,
>         if (diag)
>   	{
>   	  inform (DECL_SOURCE_LOCATION (fn),
> -		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
> +		  SFK_DTOR_P (sfk)
> +		  ? G_("destructor calls non-%<constexpr%> %qD")

Not "defaulted"?

> +		  : G_("defaulted constructor calls non-%<constexpr%> %qD"),
> +		  fn);
>   	  explain_invalid_constexpr_fn (fn);
>   	}
>       }
>   }
>   
> -/* Categorize various special_function_kinds.  */
> -#define SFK_CTOR_P(sfk) \
> -  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
> -#define SFK_DTOR_P(sfk) \
> -  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
> -#define SFK_ASSIGN_P(sfk) \
> -  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
> -#define SFK_COPY_P(sfk) \
> -  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
> -#define SFK_MOVE_P(sfk) \
> -  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
> -
>   /* Subroutine of synthesized_method_walk to allow recursion into anonymous
>      aggregates.  If DTOR_FROM_CTOR is true, we're walking subobject destructors
>      called from a synthesized constructor, in which case we don't consider
> @@ -1318,8 +1322,7 @@ walk_field_subobs (tree fields, special_
>       {
>         tree mem_type, argtype, rval;
>   
> -      if (TREE_CODE (field) != FIELD_DECL
> -	  || DECL_ARTIFICIAL (field))
> +      if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
>   	continue;
>   
>         /* Variant members only affect deletedness.  In particular, they don't
> @@ -1457,7 +1460,7 @@ walk_field_subobs (tree fields, special_
>   
>         rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
>   
> -      process_subob_fn (rval, spec_p, trivial_p, deleted_p,
> +      process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
>   			constexpr_p, diag, field, dtor_from_ctor);
>       }
>   }
> @@ -1510,23 +1513,23 @@ synthesized_method_base_walk (tree binfo
>         && DECL_CONTEXT (*inheriting_ctor) == DECL_CONTEXT (rval))
>       *inheriting_ctor = DECL_CLONED_FUNCTION (rval);
>   
> -  process_subob_fn (rval, spec_p, trivial_p, deleted_p,
> +  process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
>   		    constexpr_p, diag, BINFO_TYPE (base_binfo));
> -  if (SFK_CTOR_P (sfk) &&
> -      (!BINFO_VIRTUAL_P (base_binfo)
> -       || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
> +  if (SFK_CTOR_P (sfk)
> +      && (!BINFO_VIRTUAL_P (base_binfo)
> +	  || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
>       {
>         /* In a constructor we also need to check the subobject
>   	 destructors for cleanup of partially constructed objects.  */
>         tree dtor = locate_fn_flags (base_binfo, complete_dtor_identifier,
>   				   NULL_TREE, flags,
>   				   diag ? tf_warning_or_error : tf_none);
> -	  /* Note that we don't pass down trivial_p; the subobject
> -	     destructors don't affect triviality of the constructor.  Nor
> -	     do they affect constexpr-ness (a constant expression doesn't
> -	     throw) or exception-specification (a throw from one of the
> -	     dtors would be a double-fault).  */
> -      process_subob_fn (dtor, NULL, NULL, deleted_p, NULL, false,
> +      /* Note that we don't pass down trivial_p; the subobject
> +	 destructors don't affect triviality of the constructor.  Nor
> +	 do they affect constexpr-ness (a constant expression doesn't
> +	 throw) or exception-specification (a throw from one of the
> +	 dtors would be a double-fault).  */
> +      process_subob_fn (dtor, sfk, NULL, NULL, deleted_p, NULL, false,
>   			BINFO_TYPE (base_binfo), /*dtor_from_ctor*/true);
>       }
>   
> @@ -1608,7 +1611,8 @@ synthesized_method_walk (tree ctype, spe
>   	member is a constexpr function.  */
>     if (constexpr_p)
>       *constexpr_p = (SFK_CTOR_P (sfk)
> -		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14));
> +		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
> +		    || (SFK_DTOR_P (sfk) && cxx_dialect >= cxx2a));
>   
>     bool expected_trivial = type_has_trivial_fn (ctype, sfk);
>     if (trivial_p)
> @@ -1704,8 +1708,8 @@ synthesized_method_walk (tree ctype, spe
>     else if (vec_safe_is_empty (vbases))
>       /* No virtual bases to worry about.  */;
>     else if (ABSTRACT_CLASS_TYPE_P (ctype) && cxx_dialect >= cxx14
> -	   /* DR 1658 specifis that vbases of abstract classes are
> -	      ignored for both ctors and dtors.  Except DR 2338
> +	   /* DR 1658 specifies that vbases of abstract classes are
> +	      ignored for both ctors and dtors.  Except DR 2336
>   	      overrides that skipping when determing the eh-spec of a
>   	      virtual destructor.  */
>   	   && sfk != sfk_virtual_destructor)
> @@ -2046,7 +2050,8 @@ implicitly_declare_fn (special_function_
>       constexpr_p = false;
>     /* A trivial copy/move constructor is also a constexpr constructor,
>        unless the class has virtual bases (7.1.5p4).  */
> -  else if (trivial_p && cxx_dialect >= cxx11
> +  else if (trivial_p
> +	   && cxx_dialect >= cxx11
>   	   && (kind == sfk_copy_constructor
>   	       || kind == sfk_move_constructor)
>   	   && !CLASSTYPE_VBASECLASSES (type))
> --- gcc/cp/typeck2.c.jj	2019-09-26 21:34:26.299847376 +0200
> +++ gcc/cp/typeck2.c	2019-09-27 18:25:40.899066017 +0200
> @@ -902,7 +902,13 @@ store_init_value (tree decl, tree init,
>   	    value = oldval;
>   	}
>       }
> -  value = cp_fully_fold_init (value);
> +  /* Don't fold initializers of automatic variables in constexpr functions,
> +     that might fold away something that needs to be diagnosed at constexpr
> +     evaluation time.  */
> +  if (!current_function_decl
> +      || !DECL_DECLARED_CONSTEXPR_P (current_function_decl)
> +      || TREE_STATIC (decl))
> +    value = cp_fully_fold_init (value);
>   
>     /* Handle aggregate NSDMI in non-constant initializers, too.  */
>     value = replace_placeholders (value, decl);
> --- gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C.jj	2016-03-05 07:46:49.554128302 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C	2019-09-27 20:26:06.317971681 +0200
> @@ -5,8 +5,9 @@ struct A { ~A(); };
>   constexpr int f(int i) { return i; }
>   constexpr int g(A* ap)
>   {
> -  return f((delete[] ap, 42)); // { dg-message "" }
> +  return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
>   }
>   
>   A a;
>   constexpr int i = g(&a);	// { dg-error "" }
> +				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
> --- gcc/testsuite/g++.dg/cpp0x/locations1.C.jj	2019-09-26 21:34:22.205908750 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/locations1.C	2019-09-27 18:25:41.346059343 +0200
> @@ -11,7 +11,7 @@ struct S
>   {
>     virtual S();  // { dg-error "3:constructors cannot be declared .virtual." }
>     constexpr int s = 1;  // { dg-error "3:non-static data member .s. declared .constexpr." }
> -  constexpr ~S();  // { dg-error "3:a destructor cannot be .constexpr." }
> +  constexpr ~S();  // { dg-error "3:'constexpr' destructors only available with" "" { target c++17_down } }
>   };
>   
>   typedef constexpr int my_int;  // { dg-error "9:.constexpr. cannot appear in a typedef declaration" }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-new.C.jj	2019-09-26 21:34:22.281907610 +0200
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-new.C	2019-09-27 18:25:41.303059984 +0200
> @@ -4,7 +4,7 @@ constexpr int *f4(bool b) {
>     if (b) {
>       return nullptr;
>     } else {
> -    return new int{42}; // { dg-error "call to non-.constexpr." }
> +    return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } }
>     }
>   }
>   static_assert(f4(true) == nullptr, "");
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C.jj	2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C	2019-09-27 18:25:41.320059731 +0200
> @@ -0,0 +1,9 @@
> +// P0784R7
> +// { dg-do compile { target c++11 } }
> +
> +struct S
> +{
> +  constexpr S () : s (0) {}
> +  constexpr ~S () {}	// { dg-error "'constexpr' destructors only available with" "" { target c++17_down } }
> +  int s;
> +};
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C.jj	2019-09-27 18:25:41.303059984 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C	2019-09-27 18:25:41.303059984 +0200
> @@ -0,0 +1,66 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +struct S
> +{
> +  constexpr S () : r (4), s (3) { --r; s -= 2; }
> +  constexpr ~S () { if (s == 1) s = 0; else asm (""); if (s == 0 && r == 3) r = 0; else asm (""); }
> +  int r, s;
> +};
> +struct T : public S
> +{
> +  constexpr T () : t (2) {}
> +  int t;
> +  S u;
> +};
> +struct U : public S
> +{
> +  constexpr U (int x) : u (x) {}
> +  constexpr ~U () = default;
> +  int u;
> +  S v;
> +};
> +
> +constexpr S a;
> +constexpr T b;
> +constexpr U c = 3;
> +static_assert (a.s == 1 && a.r == 3);
> +static_assert (b.s == 1 && b.r == 3 && b.t == 2 && b.u.s == 1 && b.u.r == 3);
> +static_assert (c.s == 1 && c.r == 3 && c.u == 3 && c.v.s == 1 && c.v.r == 3);
> +
> +void
> +foo ()
> +{
> +  static constexpr S d;
> +  static constexpr T e;
> +  static constexpr U f = 4;
> +  static_assert (d.s == 1 && d.r == 3);
> +  static_assert (e.s == 1 && e.r == 3 && e.t == 2 && e.u.s == 1 && e.u.r == 3);
> +  static_assert (f.s == 1 && f.r == 3 && f.u == 4 && f.v.s == 1 && f.v.r == 3);
> +  if (1)
> +    {
> +      constexpr S g;
> +      constexpr T h;
> +      constexpr U i = 5;
> +      static_assert (g.s == 1 && g.r == 3);
> +      static_assert (h.s == 1 && h.r == 3 && h.t == 2 && h.u.s == 1 && h.u.r == 3);
> +      static_assert (i.s == 1 && i.r == 3 && i.u == 5 && i.v.s == 1 && i.v.r == 3);
> +    }
> +}
> +
> +constexpr bool
> +bar ()
> +{
> +  S j;
> +  T k;
> +  U l = 6;
> +  if (j.s != 1 || j.r != 3)
> +    return false;
> +  if (k.s != 1 || k.r != 3 || k.t != 2 || k.u.s != 1 || k.u.r != 3)
> +    return false;
> +  if (l.s != 1 || l.r != 3 || l.u != 6 || l.v.s != 1 || l.v.r != 3)
> +    return false;
> +  return true;
> +}
> +
> +static_assert (bar ());
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2019-09-27 18:25:41.303059984 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2019-09-27 18:25:41.303059984 +0200
> @@ -0,0 +1,185 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +struct S
> +{
> +  constexpr S () : s (0) {}
> +  constexpr ~S () {}
> +  int s;
> +};
> +struct T	// { dg-message "'T' is not literal because" }
> +{		// { dg-message "'T' does not have 'constexpr' destructor" "" { target *-*-* } .-1 }
> +  constexpr T () : t (0) {}
> +  ~T () {}	// { dg-message "destructor calls non-'constexpr' 'T::~T\\(\\)'" }
> +  int t;
> +};
> +struct U : public S
> +{
> +  constexpr U () : u (0) {}
> +  constexpr ~U () = default;	// { dg-error "explicitly defaulted function 'constexpr U::~U\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
> +  int u;
> +  T t;
> +};
> +struct V : virtual public S
> +{
> +  V () : v (0) {}
> +  constexpr ~V () = default;	// { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
> +  int v;
> +};
> +struct W0
> +{
> +  constexpr W0 () : w (0) {}
> +  constexpr W0 (int x) : w (x) {}
> +  constexpr ~W0 () { if (w == 5) asm (""); w = 3; }
> +  int w;
> +};
> +struct W1
> +{
> +  constexpr W1 () : w (0) {}
> +  constexpr W1 (int x) : w (x) {}
> +  constexpr ~W1 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W2
> +{
> +  constexpr W2 () : w (0) {}
> +  constexpr W2 (int x) : w (x) {}
> +  constexpr ~W2 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W3
> +{
> +  constexpr W3 () : w (0) {}
> +  constexpr W3 (int x) : w (x) {}
> +  constexpr ~W3 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W4
> +{
> +  constexpr W4 () : w (0) {}
> +  constexpr W4 (int x) : w (x) {}
> +  constexpr ~W4 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W5
> +{
> +  constexpr W5 () : w (0) {}
> +  constexpr W5 (int x) : w (x) {}
> +  constexpr ~W5 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W6
> +{
> +  constexpr W6 () : w (0) {}
> +  constexpr W6 (int x) : w (x) {}
> +  constexpr ~W6 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W7
> +{
> +  constexpr W7 () : w (0) {}
> +  constexpr W7 (int x) : w (x) {}
> +  constexpr ~W7 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct W8
> +{
> +  constexpr W8 () : w (0) {}
> +  constexpr W8 (int x) : w (x) {}
> +  constexpr ~W8 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
> +							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
> +  int w;
> +};
> +struct X : public T
> +{
> +  constexpr X () : x (0) {}
> +  constexpr ~X () = default;	// { dg-error "explicitly defaulted function 'constexpr X::~X\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
> +  int x;
> +};
> +constexpr S s;
> +constexpr T t;	// { dg-error "the type 'const T' of 'constexpr' variable 't' is not literal" }
> +constexpr W0 w1;
> +constexpr W0 w2 = 12;
> +constexpr W1 w3 = 5;	// { dg-message "in 'constexpr' expansion of" }
> +constexpr W0 w4[3] = { 1, 2, 3 };
> +constexpr W2 w5[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
> +
> +void
> +f1 ()
> +{
> +  constexpr S s2;
> +  constexpr W0 w6;
> +  constexpr W0 w7 = 12;
> +  constexpr W3 w8 = 5;	// { dg-message "in 'constexpr' expansion of" }
> +  constexpr W0 w9[3] = { 1, 2, 3 };
> +  constexpr W4 w10[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
> +}
> +
> +constexpr int
> +f2 ()
> +{
> +  constexpr S s3;
> +  constexpr W0 w11;
> +  constexpr W0 w12 = 12;
> +  constexpr W5 w13 = 5;	// { dg-message "in 'constexpr' expansion of" }
> +  constexpr W0 w14[3] = { 1, 2, 3 };
> +  constexpr W6 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
> +  return 0;
> +}
> +
> +constexpr int
> +f3 ()
> +{
> +  S s3;
> +  W0 w11;
> +  W0 w12 = 12;
> +  W0 w14[3] = { 1, 2, 3 };
> +  return 0;
> +}
> +
> +constexpr int x3 = f3 ();
> +
> +constexpr int
> +f4 ()
> +{
> +  W7 w13 = 5;
> +  return 0;
> +}
> +
> +constexpr int x4 = f4 ();	// { dg-message "in 'constexpr' expansion of" }
> +
> +constexpr int
> +f5 ()
> +{
> +  W8 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
> +  return 0;
> +}
> +
> +constexpr int x5 = f5 ();	// { dg-message "in 'constexpr' expansion of" }
> +
> +void
> +f6 ()
> +{
> +  constexpr T t2;	// { dg-error "the type 'const T' of 'constexpr' variable 't2' is not literal" }
> +}
> +
> +constexpr int
> +f7 ()
> +{
> +  constexpr T t3;	// { dg-error "the type 'const T' of 'constexpr' variable 't3' is not literal" }
> +  return 0;
> +}
> +
> +constexpr int
> +f8 ()
> +{
> +  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
> +  return 0;
> +}
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C.jj	2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C	2019-09-27 20:06:50.047298452 +0200
> @@ -0,0 +1,39 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +struct S { constexpr S () : s (5) {} constexpr S (int x) : s (x) {} int s; };
> +
> +constexpr bool
> +foo ()
> +{
> +  int r = 0;
> +  S *p = new S ();
> +  p->s += 3;
> +  r += p->s;
> +  delete p;
> +  p = new S (12);
> +  p->s = p->s * 2;
> +  r += p->s;
> +  delete p;
> +  int *q = new int;
> +  *q = 25;
> +  r += *q;
> +  delete q;
> +  q = new int (1);
> +  r += *q;
> +  if (!q)
> +    return false;
> +  delete q;
> +  q = new int[5]{1,2,3,4,5};
> +  r += q[0] + q[4];
> +  delete[] q;
> +  q = new int[4];
> +  q[0] = 6;
> +  q[1] = 7;
> +  q[3] = 8;
> +  r += q[0] + q[1] + q[3];
> +  delete[] q;
> +  return r == 5 + 3 + 2 * 12 + 25 + 1 + 1 + 5 + 6 + 7 + 8;
> +}
> +constexpr bool a = foo ();
> +static_assert (a);
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C.jj	2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C	2019-09-27 20:23:16.107524585 +0200
> @@ -0,0 +1,21 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +template <int N>
> +constexpr bool
> +foo (const char (&x)[N])
> +{
> +  int **p = new int *[N];
> +  for (int i = 0; i < N; i++)
> +    p[i] = new int (x[i]);
> +  for (int i = 0; i < N; i++)
> +    if (*p[i] != x[i])
> +      return false;
> +  for (int i = 0; i < N; ++i)
> +    delete p[i];
> +  delete[] p;
> +  return true;
> +}
> +
> +constexpr bool a = foo ("foobar");
> +static_assert (a);
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C.jj	2019-09-27 18:25:41.320059731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-09-27 18:25:41.320059731 +0200
> @@ -0,0 +1,63 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +constexpr int *
> +f1 ()
> +{
> +  return new int (2);		// { dg-error "is not a constant expression because it refers to a result of 'operator new'" }
> +}
> +
> +constexpr auto v1 = f1 ();
> +
> +constexpr bool
> +f2 ()
> +{
> +  int *p = new int (3);		// { dg-error "is not a constant expression because allocated storage has not been deallocated" }
> +  return false;
> +}
> +
> +constexpr auto v2 = f2 ();
> +
> +constexpr bool
> +f3 ()
> +{
> +  int *p = new int (3);
> +  int *q = p;
> +  delete p;
> +  delete q;			// { dg-error "deallocation of already deallocated storage" }
> +  return false;
> +}
> +
> +constexpr auto v3 = f3 ();	// { dg-message "in 'constexpr' expansion of" }
> +
> +constexpr bool
> +f4 (int *p)
> +{
> +  delete p;			// { dg-error "call to non-'constexpr' function" }
> +  return false;
> +}
> +
> +int q;
> +constexpr auto v4 = f4 (&q);	// { dg-message "in 'constexpr' expansion of" }
> +
> +constexpr bool
> +f5 ()
> +{
> +  int *p = new int;		// { dg-message "allocated here" }
> +  return *p == 1;
> +}
> +
> +constexpr auto v5 = f5 ();	// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
> +				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
> +
> +constexpr bool
> +f6 ()
> +{
> +  int *p = new int (2);		// { dg-message "allocated here" }
> +  int *q = p;
> +  delete p;
> +  return *q == 2;
> +}
> +
> +constexpr auto v6 = f6 ();	// { dg-error "use of allocated storage after deallocation in a constant expression" }
> +				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1  }
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C.jj	2019-09-27 18:25:41.319059745 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C	2019-09-27 20:07:33.928641928 +0200
> @@ -0,0 +1,29 @@
> +// P0784R7
> +// { dg-do compile { target c++2a } }
> +
> +struct S
> +{
> +  constexpr S () : s (0) { s++; }
> +  constexpr S (int x) : s (x) { s += 2; }
> +  constexpr ~S () { if (s != 35) asm (""); s = 5; }
> +  int s;
> +};
> +
> +constexpr bool
> +foo ()
> +{
> +  S *p = new S (7);
> +  if (p->s != 9) return false;
> +  p->s = 35;
> +  delete p;
> +  p = new S[3] { 11, 13, 15 };
> +  if (p[0].s != 13 || p[1].s != 15 || p[2].s != 17) return false;
> +  p[0].s = 35;
> +  p[2].s = 35;
> +  p[1].s = 35;
> +  delete[] p;
> +  return true;
> +}
> +
> +constexpr bool a = foo ();
> +static_assert (a);
> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj	2019-09-26 21:34:22.341906710 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2019-09-27 18:25:41.319059745 +0200
> @@ -430,16 +430,34 @@
>   
>   // C++20 features
>   
> -#if __cpp_conditional_explicit != 201806
> -# error "__cpp_conditional_explicit != 201806"
> +#ifndef __cpp_conditional_explicit
> +#  error "__cpp_conditional_explicit"
> +#elif __cpp_conditional_explicit != 201806
> +#  error "__cpp_conditional_explicit != 201806"
>   #endif
>   
> -#if __cpp_nontype_template_parameter_class != 201806
> -# error "__cpp_nontype_template_parameter_class != 201806"
> +#ifndef __cpp_nontype_template_parameter_class
> +#  error "__cpp_nontype_template_parameter_class"
> +#elif __cpp_nontype_template_parameter_class != 201806
> +#  error "__cpp_nontype_template_parameter_class != 201806"
>   #endif
>   
> -#if __cpp_impl_destroying_delete != 201806
> -# error "__cpp_impl_destroying_delete != 201806"
> +#ifndef __cpp_impl_destroying_delete
> +#  error "__cpp_impl_destroying_delete"
> +#elif __cpp_impl_destroying_delete != 201806
> +#  error "__cpp_impl_destroying_delete != 201806"
> +#endif
> +
> +#ifndef __cpp_constinit
> +#  error "__cpp_constinit"
> +#elif __cpp_constinit != 201907
> +#  error "__cpp_constinit != 201907"
> +#endif
> +
> +#ifndef __cpp_constexpr_dynamic_alloc
> +#  error "__cpp_constexpr_dynamic_alloc"
> +#elif __cpp_constexpr_dynamic_alloc != 201907
> +#  error "__cpp_constexpr_dynamic_alloc != 201907"
>   #endif
>   
>   #ifdef __has_cpp_attribute
> @@ -484,8 +502,6 @@
>   #  error "__has_cpp_attribute"
>   #endif
>   
> -// C++2A features:
> -
>   #ifndef __cpp_char8_t
>   #  error "__cpp_char8_t"
>   #elif __cpp_char8_t != 201811
> --- gcc/testsuite/g++.dg/ext/is_literal_type3.C.jj	2019-09-27 18:25:40.904065942 +0200
> +++ gcc/testsuite/g++.dg/ext/is_literal_type3.C	2019-09-27 18:25:40.904065942 +0200
> @@ -0,0 +1,26 @@
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> +  constexpr S () : n{} { }
> +  ~S () { n = 1; }
> +  int n;
> +};
> +
> +static_assert(!__is_literal_type(S), "");
> +
> +#ifdef __cpp_constexpr_dynamic_alloc
> +struct T {
> +  constexpr T () : n{} { }
> +  constexpr ~T () { n = 1; }
> +  int n;
> +};
> +
> +static_assert(__is_literal_type(T), "");
> +
> +struct U : public T {
> +  constexpr U () : u{} { }
> +  int u;
> +};
> +
> +static_assert(__is_literal_type(U), "");
> +#endif
> 
> 	Jakub
>
Jakub Jelinek Oct. 2, 2019, 11:36 a.m. UTC | #2
Hi!

Thanks for the review, let me start with the unrelated bugfixes then and
deal with the rest later.

On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote:
> > @@ -3905,7 +4033,7 @@ cxx_eval_store_expression (const constex
> >     bool no_zero_init = true;
> >     releasing_vec ctors;
> > -  while (!refs->is_empty())
> > +  while (!refs->is_empty ())
> >       {
> >         if (*valp == NULL_TREE)
> >   	{
> > @@ -4046,7 +4174,9 @@ cxx_eval_store_expression (const constex
> >     if (const_object_being_modified)
> >       {
> >         bool fail = false;
> > -      if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
> > +      tree const_objtype
> > +	= strip_array_types (TREE_TYPE (const_object_being_modified));
> > +      if (!CLASS_TYPE_P (const_objtype))
> 
> This looks like an unrelated bugfix; you might commit it (and the others)
> separately if that's convenient.
> 
> > @@ -4907,14 +5043,21 @@ cxx_eval_constant_expression (const cons
> >         break;
> >       case CLEANUP_STMT:
> > -      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> > +      {
> > +	tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
> > +	r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
> > +					  non_constant_p, overflow_p,
> > +					  jump_target);
> > +	if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
> > +	  /* Also evaluate the cleanup.  If we weren't skipping at the
> > +	     start of the CLEANUP_BODY, change jump_target temporarily
> > +	     to &initial_jump_target, so that even a return or break or
> > +	     continue in the body doesn't skip the cleanup.  */
> 
> This also looks like an unrelated bugfix.

Both are only partially related, I think they can go in separately, but at
least for the first one I haven't managed to come up with a testcase where
it would matter and nothing in e.g. check-c++-all comes there with ARRAY_TYPE,
is it ok for trunk without a testcase (first attached patch)?

As for the second bugfix, I think it should be accompanied with the
potential_constant_expression_1 change, and there I've actually managed to
come up with a testcase where it matters, though it is using a GCC extension
(generally, CLEANUP_STMTs won't appear in constexpr functions that often
because of the requirement that variables in them have literal type and
literal types have trivial destructors, so really no cleanups in that case).

The testcase where it makes a difference is:

constexpr void
cleanup (int *x)
{
  if (x)
    asm ("");
}

constexpr void
cleanup2 (int *x)
{
}

constexpr bool
foo ()
{
  int a __attribute__((cleanup (cleanup))) = 1;
  return true;
}

constexpr bool
bar ()
{
  int a __attribute__((cleanup (cleanup2))) = 1;
  return true;
}

constexpr auto x = foo ();
constexpr auto y = bar ();

With vanilla trunk, one gets a weird message:
test.C:27:24: error: ‘constexpr bool foo()’ called in a constant expression
   27 | constexpr auto x = foo ();
      |                    ~~~~^~
test.C:28:24: error: ‘constexpr bool bar()’ called in a constant expression
   28 | constexpr auto y = bar ();
      |                    ~~~~^~
That is because we call potential_constant_expression_1 on the foo (or
bar) body, see CLEANUP_STMT in there and punt.  With just the
potential_constant_expression_1 change to handle CLEANUP_STMT, we actually
accept it, which is wrong.
Finally, with the whole patch attached below, we reject foo () call and
accept bar ():
test.C:27:24:   in ‘constexpr’ expansion of ‘foo()’
test.C:27:25:   in ‘constexpr’ expansion of ‘cleanup((& a))’
test.C:5:5: error: inline assembly is not a constant expression
    5 |     asm ("");
      |     ^~~
test.C:5:5: note: only unevaluated inline assembly is allowed in a ‘constexpr’ function in C++2a

Is the testcase ok in the second patch?  Are those patches ok for trunk?

	Jakub
2019-10-02  Jakub Jelinek  <jakub@redhat.com>

	* constexpr.c (cxx_eval_store_expression): Formatting fix.  Handle
	const_object_being_modified with array type.

--- gcc/cp/constexpr.c.jj	2019-09-27 20:33:37.600208356 +0200
+++ gcc/cp/constexpr.c	2019-09-27 20:38:38.203710246 +0200
@@ -3905,7 +4033,7 @@ cxx_eval_store_expression (const constex
   bool no_zero_init = true;
 
   releasing_vec ctors;
-  while (!refs->is_empty())
+  while (!refs->is_empty ())
     {
       if (*valp == NULL_TREE)
 	{
@@ -4046,7 +4174,9 @@ cxx_eval_store_expression (const constex
   if (const_object_being_modified)
     {
       bool fail = false;
-      if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
+      tree const_objtype
+	= strip_array_types (TREE_TYPE (const_object_being_modified));
+      if (!CLASS_TYPE_P (const_objtype))
 	fail = true;
       else
 	{
2019-10-02  Jakub Jelinek  <jakub@redhat.com>

	* constexpr.c (cxx_eval_constant_expression) <case CLEANUP_STMT>: If
	not skipping upon entry to body, run cleanup with the same *jump_target
	as it started to run the cleanup even if the body returns, breaks or
	continues.
	(potential_constant_expression_1): Allow CLEANUP_STMT.

	* g++.dg/ext/constexpr-attr-cleanup1.C: New test.

--- gcc/cp/constexpr.c.jj	2019-10-02 13:15:21.822352849 +0200
+++ gcc/cp/constexpr.c	2019-10-02 13:15:53.527879667 +0200
@@ -4907,14 +4907,21 @@ cxx_eval_constant_expression (const cons
       break;
 
     case CLEANUP_STMT:
-      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+      {
+	tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
+	r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	  /* Also evaluate the cleanup.  If we weren't skipping at the
+	     start of the CLEANUP_BODY, change jump_target temporarily
+	     to &initial_jump_target, so that even a return or break or
+	     continue in the body doesn't skip the cleanup.  */
+	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
 					non_constant_p, overflow_p,
-					jump_target);
-      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
-	/* Also evaluate the cleanup.  */
-	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
-				      non_constant_p, overflow_p,
-				      jump_target);
+					jump_target ? &initial_jump_target
+					: NULL);
+      }
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -6983,6 +6990,12 @@ potential_constant_expression_1 (tree t,
       return true;
 
     case CLEANUP_STMT:
+      if (!RECUR (CLEANUP_BODY (t), any))
+	return false;
+      if (!CLEANUP_EH_ONLY (t) && !RECUR (CLEANUP_EXPR (t), any))
+	return false;
+      return true;
+
     case EMPTY_CLASS_EXPR:
     case PREDICT_EXPR:
       return false;
--- gcc/testsuite/g++.dg/ext/constexpr-attr-cleanup1.C.jj	2019-10-02 13:32:11.973282291 +0200
+++ gcc/testsuite/g++.dg/ext/constexpr-attr-cleanup1.C	2019-10-02 13:32:56.192624120 +0200
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++2a } }
+
+constexpr void
+cleanup (int *x)
+{
+  if (x)
+    asm ("");		// { dg-error "inline assembly is not a constant expression" }
+}			// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+
+constexpr void
+cleanup2 (int *x)
+{
+}
+
+constexpr bool
+foo ()
+{
+  int a __attribute__((cleanup (cleanup))) = 1;
+  return true;
+}
+
+constexpr bool
+bar ()
+{
+  int a __attribute__((cleanup (cleanup2))) = 1;
+  return true;
+}
+
+constexpr auto x = foo ();	// { dg-message "in 'constexpr' expansion of" }
+constexpr auto y = bar ();
Jason Merrill Oct. 2, 2019, 3:22 p.m. UTC | #3
On 10/2/19 7:36 AM, Jakub Jelinek wrote:
> Hi!
> 
> Thanks for the review, let me start with the unrelated bugfixes then and
> deal with the rest later.
> 
> On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote:
>>> @@ -3905,7 +4033,7 @@ cxx_eval_store_expression (const constex
>>>      bool no_zero_init = true;
>>>      releasing_vec ctors;
>>> -  while (!refs->is_empty())
>>> +  while (!refs->is_empty ())
>>>        {
>>>          if (*valp == NULL_TREE)
>>>    	{
>>> @@ -4046,7 +4174,9 @@ cxx_eval_store_expression (const constex
>>>      if (const_object_being_modified)
>>>        {
>>>          bool fail = false;
>>> -      if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
>>> +      tree const_objtype
>>> +	= strip_array_types (TREE_TYPE (const_object_being_modified));
>>> +      if (!CLASS_TYPE_P (const_objtype))
>>
>> This looks like an unrelated bugfix; you might commit it (and the others)
>> separately if that's convenient.
>>
>>> @@ -4907,14 +5043,21 @@ cxx_eval_constant_expression (const cons
>>>          break;
>>>        case CLEANUP_STMT:
>>> -      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
>>> +      {
>>> +	tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
>>> +	r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
>>> +					  non_constant_p, overflow_p,
>>> +					  jump_target);
>>> +	if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
>>> +	  /* Also evaluate the cleanup.  If we weren't skipping at the
>>> +	     start of the CLEANUP_BODY, change jump_target temporarily
>>> +	     to &initial_jump_target, so that even a return or break or
>>> +	     continue in the body doesn't skip the cleanup.  */
>>
>> This also looks like an unrelated bugfix.
> 
> Both are only partially related, I think they can go in separately, but at
> least for the first one I haven't managed to come up with a testcase where
> it would matter and nothing in e.g. check-c++-all comes there with ARRAY_TYPE,
> is it ok for trunk without a testcase (first attached patch)?
> 
> As for the second bugfix, I think it should be accompanied with the
> potential_constant_expression_1 change, and there I've actually managed to
> come up with a testcase where it matters, though it is using a GCC extension
> (generally, CLEANUP_STMTs won't appear in constexpr functions that often
> because of the requirement that variables in them have literal type and
> literal types have trivial destructors, so really no cleanups in that case).
> 
> The testcase where it makes a difference is:
> 
> constexpr void
> cleanup (int *x)
> {
>    if (x)
>      asm ("");
> }
> 
> constexpr void
> cleanup2 (int *x)
> {
> }
> 
> constexpr bool
> foo ()
> {
>    int a __attribute__((cleanup (cleanup))) = 1;
>    return true;
> }
> 
> constexpr bool
> bar ()
> {
>    int a __attribute__((cleanup (cleanup2))) = 1;
>    return true;
> }
> 
> constexpr auto x = foo ();
> constexpr auto y = bar ();
> 
> With vanilla trunk, one gets a weird message:
> test.C:27:24: error: ‘constexpr bool foo()’ called in a constant expression
>     27 | constexpr auto x = foo ();
>        |                    ~~~~^~
> test.C:28:24: error: ‘constexpr bool bar()’ called in a constant expression
>     28 | constexpr auto y = bar ();
>        |                    ~~~~^~
> That is because we call potential_constant_expression_1 on the foo (or
> bar) body, see CLEANUP_STMT in there and punt.  With just the
> potential_constant_expression_1 change to handle CLEANUP_STMT, we actually
> accept it, which is wrong.
> Finally, with the whole patch attached below, we reject foo () call and
> accept bar ():
> test.C:27:24:   in ‘constexpr’ expansion of ‘foo()’
> test.C:27:25:   in ‘constexpr’ expansion of ‘cleanup((& a))’
> test.C:5:5: error: inline assembly is not a constant expression
>      5 |     asm ("");
>        |     ^~~
> test.C:5:5: note: only unevaluated inline assembly is allowed in a ‘constexpr’ function in C++2a
> 
> Is the testcase ok in the second patch?  Are those patches ok for trunk?

OK, thanks.

Jason
Jakub Jelinek Oct. 3, 2019, 6:38 p.m. UTC | #4
On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote:
> > +		  else if (DECL_NAME (var) == heap_deleted_identifier)
> > +		    {
> > +		      if (!ctx->quiet)
> > +			error_at (loc, "deallocation of already deallocated "
> > +				       "storage");
> > +		      *non_constant_p = true;
> > +		      return t;
> > +		    }
> 
> Don't we need an error for trying to deallocate something that wasn't
> allocated within the constexpr evaluation?

We don't need it, but it might be a good idea.  Right now it will just fall
through into the non-constexpr call in a constexpr context diagnostics,
while a message that it isn't constexpr because it is trying to deallocate
something that hasn't been allocated might be clearer.  Will add.

> > @@ -3011,8 +3067,10 @@ initialized_type (tree t)
> >       {
> >         /* A constructor call has void type, so we need to look deeper.  */
> >         tree fn = get_function_named_in_call (t);
> > -      if (fn && TREE_CODE (fn) == FUNCTION_DECL
> > -	  && DECL_CXX_CONSTRUCTOR_P (fn))
> > +      if (fn
> > +	  && TREE_CODE (fn) == FUNCTION_DECL
> > +	  && (DECL_CXX_CONSTRUCTOR_P (fn)
> > +	      || (cxx_dialect >= cxx2a && DECL_CXX_DESTRUCTOR_P (fn))))
> >   	type = DECL_CONTEXT (fn);
> 
> Why is this needed?  A destructor doesn't initialize anything, so returning
> void seems appropriate.

This was needed initially, because the evaluate outermost expr if it sees
void type just returns early, does nothing.  But later I found out that while
the above worked for the normal dtor case, for the array dtor case it didn't
work anyway and I've added an extra argument.  So this is not needed anymore
and will delete.

> I think we want to factor this function more, so we don't have the same code
> in multiple places for handling an array, and an array member, and a pointer
> to array.  Do you want to take a look at bug 71504 while you're touching
> this code?

Patch posted separately.

> > +  if (!valp
> > +      && VAR_P (object)
> > +      && DECL_NAME (object) == heap_identifier)
> > +    {
> > +      tree ctor = build_constructor (type, NULL);
> > +      CONSTRUCTOR_NO_CLEARING (ctor) = true;
> > +      ctx->values->put (object, ctor);
> > +      valp = ctx->values->get (object);
> > +    }
> 
> Instead of this, how about giving the object NULL_TREE value when we create
> it in cxx_eval_call_expression?

Will try.
> > +	      if (tree fld1 = TYPE_FIELDS (var_type))
> > +		if (TREE_CODE (fld1) == FIELD_DECL
> > +		    && DECL_NAME (fld1) == NULL_TREE
> > +		    && DECL_ARTIFICIAL (fld1)
> > +		    && TREE_CODE (TREE_TYPE (fld1)) == ARRAY_TYPE
> > +		    && COMPLETE_TYPE_P (TREE_TYPE (fld1)))
> > +		  if (tree fld2 = DECL_CHAIN (fld1))
> > +		    if (TREE_CODE (fld2) == FIELD_DECL
> > +			&& DECL_NAME (fld2) == NULL_TREE
> > +			&& DECL_ARTIFICIAL (fld2)
> > +			&& TREE_CODE (TREE_TYPE (fld2)) == ARRAY_TYPE
> > +			&& TYPE_DOMAIN (TREE_TYPE (fld2)) == NULL_TREE
> > +			&& DECL_CHAIN (fld2) == NULL_TREE)
> 
> Maybe give the struct a magic name so you don't need to do as much checking
> of the FIELD_DECLs?

Ok.

> So here you're completing the type of the array member of the struct.
> 
> > +		TREE_TYPE (var) = var_type;
> > +		TREE_TYPE (TREE_OPERAND (op, 0))
> > +		  = build_pointer_type (var_type);
> > +	      }
> > +	  }
> 
> Let's factor out all of this code, too.

Yes, it is used twice, so factoring it out makes a lot of sense.

> > @@ -5525,16 +5762,23 @@ cxx_eval_outermost_constant_expr (tree t
> >     bool non_constant_p = false;
> >     bool overflow_p = false;
> >     hash_map<tree,tree> map;
> > +  auto_vec<tree, 16> heap_vars;
> >     HOST_WIDE_INT constexpr_ctx_count = 0;
> >     constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
> > -			&constexpr_ctx_count, allow_non_constant, strict,
> > -			manifestly_const_eval || !allow_non_constant };
> > +			&constexpr_ctx_count, &heap_vars, allow_non_constant,
> > +			strict, manifestly_const_eval || !allow_non_constant };
> 
> As we add more stuff to constexpr_ctx, creating new ones on the stack
> becomes more and more expensive.  We should really split off the parts that
> change frequently: Maybe just ctor/object, maybe also call/save_exprs/...?

I was thinking about creating a constexpr_outermost_ctx that would include
things that are global and don't change, at least the count and this vector.
Not sure about the bool fields, we access them way too often that it might
be too ugly to change all the ctx->quiet to ctx->outermost->quite or
similar.

> > @@ -5593,6 +5852,32 @@ cxx_eval_outermost_constant_expr (tree t
> >         non_constant_p = true;
> >       }
> > +  if (!heap_vars.is_empty ())
> > +    {
> > +      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
> > +						       NULL);
> 
> Doesn't verify_constant already complain about remaining references to
> allocated objects?

I believe it doesn't, because the heap VAR_DECLs are TREE_STATIC (things
really don't work at all if they aren't).

> > @@ -6239,7 +6535,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))
> 
> This is the second occurrence of this three-line test for a constexpr
> (de)allocation function, let's factor it out.

Ok, will factor that out.

> i.e.
> 
> struct {
>   size_t cookie[N];
>   elt array[];
> };
> 
> I guess you want an array for the cookie rather than a non-array size_t data
> member to handle ARM cookies that also include the element size. Please
> include this in the comment.

That is actually what I'm using.  When build_new_1 creates it, it is
struct { size_t cookie[N]; T array[]; }; and when constexpr cast
rewrites it it becomes struct { size_t cookie[N]; T array[M]; };
with the M bound computed from the allocation size.

> > @@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p,
> >         if (diag)
> >   	{
> >   	  inform (DECL_SOURCE_LOCATION (fn),
> > -		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
> > +		  SFK_DTOR_P (sfk)
> > +		  ? G_("destructor calls non-%<constexpr%> %qD")
> 
> Not "defaulted"?

Will fix.

	Jakub
Jason Merrill Oct. 3, 2019, 8:07 p.m. UTC | #5
On 10/3/19 2:38 PM, Jakub Jelinek wrote:
> On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote:
>>> +		  else if (DECL_NAME (var) == heap_deleted_identifier)
>>> +		    {
>>> +		      if (!ctx->quiet)
>>> +			error_at (loc, "deallocation of already deallocated "
>>> +				       "storage");
>>> +		      *non_constant_p = true;
>>> +		      return t;
>>> +		    }
>>
>> Don't we need an error for trying to deallocate something that wasn't
>> allocated within the constexpr evaluation?
> 
> We don't need it, but it might be a good idea.  Right now it will just fall
> through into the non-constexpr call in a constexpr context diagnostics,
> while a message that it isn't constexpr because it is trying to deallocate
> something that hasn't been allocated might be clearer.  Will add.
> 
>>> @@ -3011,8 +3067,10 @@ initialized_type (tree t)
>>>        {
>>>          /* A constructor call has void type, so we need to look deeper.  */
>>>          tree fn = get_function_named_in_call (t);
>>> -      if (fn && TREE_CODE (fn) == FUNCTION_DECL
>>> -	  && DECL_CXX_CONSTRUCTOR_P (fn))
>>> +      if (fn
>>> +	  && TREE_CODE (fn) == FUNCTION_DECL
>>> +	  && (DECL_CXX_CONSTRUCTOR_P (fn)
>>> +	      || (cxx_dialect >= cxx2a && DECL_CXX_DESTRUCTOR_P (fn))))
>>>    	type = DECL_CONTEXT (fn);
>>
>> Why is this needed?  A destructor doesn't initialize anything, so returning
>> void seems appropriate.
> 
> This was needed initially, because the evaluate outermost expr if it sees
> void type just returns early, does nothing.  But later I found out that while
> the above worked for the normal dtor case, for the array dtor case it didn't
> work anyway and I've added an extra argument.  So this is not needed anymore
> and will delete.
> 
>> I think we want to factor this function more, so we don't have the same code
>> in multiple places for handling an array, and an array member, and a pointer
>> to array.  Do you want to take a look at bug 71504 while you're touching
>> this code?
> 
> Patch posted separately.
> 
>>> +  if (!valp
>>> +      && VAR_P (object)
>>> +      && DECL_NAME (object) == heap_identifier)
>>> +    {
>>> +      tree ctor = build_constructor (type, NULL);
>>> +      CONSTRUCTOR_NO_CLEARING (ctor) = true;
>>> +      ctx->values->put (object, ctor);
>>> +      valp = ctx->values->get (object);
>>> +    }
>>
>> Instead of this, how about giving the object NULL_TREE value when we create
>> it in cxx_eval_call_expression?
> 
> Will try.
>>> +	      if (tree fld1 = TYPE_FIELDS (var_type))
>>> +		if (TREE_CODE (fld1) == FIELD_DECL
>>> +		    && DECL_NAME (fld1) == NULL_TREE
>>> +		    && DECL_ARTIFICIAL (fld1)
>>> +		    && TREE_CODE (TREE_TYPE (fld1)) == ARRAY_TYPE
>>> +		    && COMPLETE_TYPE_P (TREE_TYPE (fld1)))
>>> +		  if (tree fld2 = DECL_CHAIN (fld1))
>>> +		    if (TREE_CODE (fld2) == FIELD_DECL
>>> +			&& DECL_NAME (fld2) == NULL_TREE
>>> +			&& DECL_ARTIFICIAL (fld2)
>>> +			&& TREE_CODE (TREE_TYPE (fld2)) == ARRAY_TYPE
>>> +			&& TYPE_DOMAIN (TREE_TYPE (fld2)) == NULL_TREE
>>> +			&& DECL_CHAIN (fld2) == NULL_TREE)
>>
>> Maybe give the struct a magic name so you don't need to do as much checking
>> of the FIELD_DECLs?
> 
> Ok.
> 
>> So here you're completing the type of the array member of the struct.
>>
>>> +		TREE_TYPE (var) = var_type;
>>> +		TREE_TYPE (TREE_OPERAND (op, 0))
>>> +		  = build_pointer_type (var_type);
>>> +	      }
>>> +	  }
>>
>> Let's factor out all of this code, too.
> 
> Yes, it is used twice, so factoring it out makes a lot of sense.
> 
>>> @@ -5525,16 +5762,23 @@ cxx_eval_outermost_constant_expr (tree t
>>>      bool non_constant_p = false;
>>>      bool overflow_p = false;
>>>      hash_map<tree,tree> map;
>>> +  auto_vec<tree, 16> heap_vars;
>>>      HOST_WIDE_INT constexpr_ctx_count = 0;
>>>      constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
>>> -			&constexpr_ctx_count, allow_non_constant, strict,
>>> -			manifestly_const_eval || !allow_non_constant };
>>> +			&constexpr_ctx_count, &heap_vars, allow_non_constant,
>>> +			strict, manifestly_const_eval || !allow_non_constant };
>>
>> As we add more stuff to constexpr_ctx, creating new ones on the stack
>> becomes more and more expensive.  We should really split off the parts that
>> change frequently: Maybe just ctor/object, maybe also call/save_exprs/...?
> 
> I was thinking about creating a constexpr_outermost_ctx that would include
> things that are global and don't change, at least the count and this vector.
> Not sure about the bool fields, we access them way too often that it might
> be too ugly to change all the ctx->quiet to ctx->outermost->quite or
> similar.
> 
>>> @@ -5593,6 +5852,32 @@ cxx_eval_outermost_constant_expr (tree t
>>>          non_constant_p = true;
>>>        }
>>> +  if (!heap_vars.is_empty ())
>>> +    {
>>> +      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
>>> +						       NULL);
>>
>> Doesn't verify_constant already complain about remaining references to
>> allocated objects?
> 
> I believe it doesn't, because the heap VAR_DECLs are TREE_STATIC (things
> really don't work at all if they aren't).

Ah, sure.  I suppose you could clear TREE_STATIC from them before the 
verify_constant in this function?

>>> @@ -6239,7 +6535,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))
>>
>> This is the second occurrence of this three-line test for a constexpr
>> (de)allocation function, let's factor it out.
> 
> Ok, will factor that out.
> 
>> i.e.
>>
>> struct {
>>    size_t cookie[N];
>>    elt array[];
>> };
>>
>> I guess you want an array for the cookie rather than a non-array size_t data
>> member to handle ARM cookies that also include the element size. Please
>> include this in the comment.
> 
> That is actually what I'm using.  When build_new_1 creates it, it is
> struct { size_t cookie[N]; T array[]; }; and when constexpr cast
> rewrites it it becomes struct { size_t cookie[N]; T array[M]; };
> with the M bound computed from the allocation size.

Yep, I'd like to see that in the comments.

>>> @@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p,
>>>          if (diag)
>>>    	{
>>>    	  inform (DECL_SOURCE_LOCATION (fn),
>>> -		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
>>> +		  SFK_DTOR_P (sfk)
>>> +		  ? G_("destructor calls non-%<constexpr%> %qD")
>>
>> Not "defaulted"?
> 
> Will fix.
> 
> 	Jakub
>
Jakub Jelinek Oct. 4, 2019, 5:50 p.m. UTC | #6
On Thu, Oct 03, 2019 at 04:07:14PM -0400, Jason Merrill wrote:
> > I believe it doesn't, because the heap VAR_DECLs are TREE_STATIC (things
> > really don't work at all if they aren't).
> 
> Ah, sure.  I suppose you could clear TREE_STATIC from them before the
> verify_constant in this function?

That works, but generates uglier diagnostics.
Otherwise, I've tried to address all your comments.

So, here is the updated patch without the find_heap_var_refs removal and
TREE_STATIC clearing (tested so far just with
make check-c++-all RUNTESTFLAGS="--target_board=unix\{-m32,-m64\} dg.exp='constexpr-array* eval-order* is_literal* constexpr-new* constexpr-dtor* constexpr-delete* locations1.C feat-cxx*'
) plus attached incremental patch for the find_heap_var_refs removal and
TREE_STATIC clearing.  The difference can be seen in the incremental diff,
the ugly part is
error: '(int*)(& heap deleted)' is not a constant expression
but it will show up only if the constant to be verified contains pointers to
deallocated heap, if it still contains pointers to allocated heap, it will
instead complain about allocated heap not being deallocated.

2019-10-04  Jakub Jelinek  <jakub@redhat.com>

	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_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}.
	<case NOP_EXPR>: 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 fix.
	* typeck2.c (store_init_value): Don't call cp_fully_fold_init on
	initializers of automatic non-constexpr variables in constexpr
	functions.
testsuite/
	* g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for
	c++2a.
	* g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics
	in c++17_down, adjust expected wording.
	* g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down.
	* g++.dg/cpp2a/constexpr-dtor1.C: New test.
	* g++.dg/cpp2a/constexpr-dtor2.C: New test.
	* g++.dg/cpp2a/constexpr-dtor3.C: New test.
	* g++.dg/cpp2a/constexpr-new1.C: New test.
	* g++.dg/cpp2a/constexpr-new2.C: New test.
	* g++.dg/cpp2a/constexpr-new3.C: New test.
	* g++.dg/cpp2a/constexpr-new4.C: New test.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and
	__cpp_constexpr_dynamic_alloc tests.  Tweak __cpp_* tests for c++2a
	features to use style like older features, including #ifdef test.
	* g++.dg/ext/is_literal_type3.C: New test.

--- gcc/c-family/c-cppbuiltin.c.jj	2019-10-03 17:55:28.657054188 +0200
+++ gcc/c-family/c-cppbuiltin.c	2019-10-04 12:49:21.610028880 +0200
@@ -989,6 +989,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constinit=201907");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
+	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
 	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
--- gcc/cp/cp-tree.h.jj	2019-10-03 17:55:28.526056167 +0200
+++ gcc/cp/cp-tree.h	2019-10-04 18:51:24.935599025 +0200
@@ -172,6 +172,9 @@ enum cp_tree_index
     CPTI_VALUE_IDENTIFIER,
     CPTI_FUN_IDENTIFIER,
     CPTI_CLOSURE_IDENTIFIER,
+    CPTI_HEAP_UNINIT_IDENTIFIER,
+    CPTI_HEAP_IDENTIFIER,
+    CPTI_HEAP_DELETED_IDENTIFIER,
 
     CPTI_LANG_NAME_C,
     CPTI_LANG_NAME_CPLUSPLUS,
@@ -310,6 +313,9 @@ extern GTY(()) tree cp_global_trees[CPTI
 #define value_identifier		cp_global_trees[CPTI_VALUE_IDENTIFIER]
 #define fun_identifier			cp_global_trees[CPTI_FUN_IDENTIFIER]
 #define closure_identifier		cp_global_trees[CPTI_CLOSURE_IDENTIFIER]
+#define heap_uninit_identifier		cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER]
+#define heap_identifier			cp_global_trees[CPTI_HEAP_IDENTIFIER]
+#define heap_deleted_identifier		cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER]
 #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
 #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
 
@@ -6342,6 +6348,7 @@ extern bool vbase_has_user_provided_move
 extern tree default_init_uninitialized_part (tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
 extern bool type_has_constexpr_default_constructor (tree);
+extern bool type_has_constexpr_destructor	(tree);
 extern bool type_has_virtual_destructor		(tree);
 extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
 extern bool classtype_has_non_deleted_move_ctor (tree);
@@ -6648,6 +6655,7 @@ extern tree build_offset_ref			(tree, tr
 extern tree throw_bad_array_new_length		(void);
 extern bool type_has_new_extended_alignment	(tree);
 extern unsigned malloc_alignment		(void);
+extern tree build_new_constexpr_heap_type	(tree, tree, tree);
 extern tree build_new				(vec<tree, va_gc> **, tree, tree,
 						 vec<tree, va_gc> **, int,
                                                  tsubst_flags_t);
@@ -7747,6 +7755,7 @@ extern bool require_constant_expression
 extern bool require_rvalue_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
+extern void cxx_constant_dtor			(tree, tree);
 extern tree cxx_constant_init			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE, bool = false);
 extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
--- gcc/cp/class.c.jj	2019-10-03 17:55:28.554055744 +0200
+++ gcc/cp/class.c	2019-10-04 12:49:21.336033076 +0200
@@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_
 static tree end_of_base (tree);
 static tree get_vcall_index (tree, tree);
 static bool type_maybe_constexpr_default_constructor (tree);
+static bool type_maybe_constexpr_destructor (tree);
 static bool field_poverlapping_p (tree);
 
 /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
@@ -5242,7 +5243,7 @@ type_has_constexpr_default_constructor (
    without forcing a lazy declaration (which might cause undesired
    instantiations).  */
 
-bool
+static bool
 type_maybe_constexpr_default_constructor (tree t)
 {
   if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t)
@@ -5252,6 +5253,34 @@ type_maybe_constexpr_default_constructor
   return type_has_constexpr_default_constructor (t);
 }
 
+/* Returns true iff class T has a constexpr destructor.  */
+
+bool
+type_has_constexpr_destructor (tree t)
+{
+  tree fns;
+
+  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Non-trivial, we need to check subobject destructors.  */
+    lazily_declare_fn (sfk_destructor, t);
+  fns = CLASSTYPE_DESTRUCTOR (t);
+  return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
+}
+
+/* Returns true iff class T has a constexpr destructor or has an
+   implicitly declared destructor that we can't tell if it's constexpr
+   without forcing a lazy declaration (which might cause undesired
+   instantiations).  */
+
+static bool
+type_maybe_constexpr_destructor (tree t)
+{
+  if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Assume it's constexpr.  */
+    return true;
+  return type_has_constexpr_destructor (t);
+}
+
 /* Returns true iff class TYPE has a virtual destructor.  */
 
 bool
@@ -5503,8 +5532,11 @@ finalize_literal_type_property (tree t)
 {
   tree fn;
 
-  if (cxx_dialect < cxx11
-      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  if (cxx_dialect < cxx11)
+    CLASSTYPE_LITERAL_P (t) = false;
+  else if (CLASSTYPE_LITERAL_P (t)
+	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t)))
     CLASSTYPE_LITERAL_P (t) = false;
   else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t))
     CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17);
@@ -5558,8 +5590,12 @@ explain_non_literal_class (tree t)
     inform (UNKNOWN_LOCATION,
 	    "  %qT is a closure type, which is only literal in "
 	    "C++17 and later", t);
-  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
     inform (UNKNOWN_LOCATION, "  %q+T has a non-trivial destructor", t);
+  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && !type_maybe_constexpr_destructor (t))
+    inform (UNKNOWN_LOCATION, "  %q+T does not have %<constexpr%> destructor",
+	    t);
   else if (CLASSTYPE_NON_AGGREGATE (t)
 	   && !TYPE_HAS_TRIVIAL_DFLT (t)
 	   && !LAMBDA_TYPE_P (t)
--- gcc/cp/constexpr.c.jj	2019-10-04 08:54:52.902007011 +0200
+++ gcc/cp/constexpr.c	2019-10-04 19:17:43.984309712 +0200
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
 #include "gimple-fold.h"
 #include "timevar.h"
 #include "fold-const-call.h"
+#include "stor-layout.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -1006,17 +1007,35 @@ enum constexpr_switch_state {
   css_default_processing
 };
 
+/* The constexpr expansion context part which needs one instance per
+   cxx_eval_outermost_constant_expr invocation.  VALUES is a map of values of
+   variables initialized within the expression.  */
+
+struct constexpr_global_ctx {
+  /* Values for any temporaries or local variables within the
+     constant-expression. */
+  hash_map<tree,tree> values;
+  /* Number of cxx_eval_constant_expression calls (except skipped ones,
+     on simple constants or location wrappers) encountered during current
+     cxx_eval_outermost_constant_expr call.  */
+  HOST_WIDE_INT constexpr_ops_count;
+  /* Heap VAR_DECLs created during the evaluation of the outermost constant
+     expression.  */
+  auto_vec<tree, 16> heap_vars;
+  /* Constructor.  */
+  constexpr_global_ctx () : constexpr_ops_count (0) {}
+};
+
 /* The constexpr expansion context.  CALL is the current function
    expansion, CTOR is the current aggregate initializer, OBJECT is the
-   object being initialized by CTOR, either a VAR_DECL or a _REF.  VALUES
-   is a map of values of variables initialized within the expression.  */
+   object being initialized by CTOR, either a VAR_DECL or a _REF.    */
 
 struct constexpr_ctx {
+  /* The part of the context that needs to be unique to the whole
+     cxx_eval_outermost_constant_expr invocation.  */
+  constexpr_global_ctx *global;
   /* The innermost call we're evaluating.  */
   constexpr_call *call;
-  /* Values for any temporaries or local variables within the
-     constant-expression. */
-  hash_map<tree,tree> *values;
   /* SAVE_EXPRs that we've seen within the current LOOP_EXPR.  NULL if we
      aren't inside a loop.  */
   vec<tree> *save_exprs;
@@ -1027,10 +1046,6 @@ struct constexpr_ctx {
   tree object;
   /* If inside SWITCH_EXPR.  */
   constexpr_switch_state *css_state;
-  /* Number of cxx_eval_constant_expression calls (except skipped ones,
-     on simple constants or location wrappers) encountered during current
-     cxx_eval_outermost_constant_expr call.  */
-  HOST_WIDE_INT *constexpr_ops_count;
 
   /* Whether we should error on a non-constant expression or fail quietly.  */
   bool quiet;
@@ -1656,6 +1671,64 @@ 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)
+	{
+	  const int nargs = call_expr_nargs (t);
+	  tree arg0 = 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)
+		arg0 = arg;
+	    }
+	  gcc_assert (arg0);
+	  if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
+	    {
+	      tree type = build_array_type_nelts (char_type_node,
+						  tree_to_uhwi (arg0));
+	      tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier,
+				     type);
+	      DECL_ARTIFICIAL (var) = 1;
+	      TREE_STATIC (var) = 1;
+	      ctx->global->heap_vars.safe_push (var);
+	      ctx->global->values.put (var, NULL_TREE);
+	      return fold_convert (ptr_type_node, build_address (var));
+	    }
+	  else
+	    {
+	      STRIP_NOPS (arg0);
+	      if (TREE_CODE (arg0) == ADDR_EXPR
+		  && VAR_P (TREE_OPERAND (arg0, 0)))
+		{
+		  tree var = TREE_OPERAND (arg0, 0);
+		  if (DECL_NAME (var) == heap_uninit_identifier
+		      || DECL_NAME (var) == heap_identifier)
+		    {
+		      DECL_NAME (var) = heap_deleted_identifier;
+		      ctx->global->values.remove (var);
+		      return void_node;
+		    }
+		  else if (DECL_NAME (var) == heap_deleted_identifier)
+		    {
+		      if (!ctx->quiet)
+			error_at (loc, "deallocation of already deallocated "
+				       "storage");
+		      *non_constant_p = true;
+		      return t;
+		    }
+		}
+	      if (!ctx->quiet)
+		error_at (loc, "deallocation of storage that was "
+			       "not previously allocated");
+	      *non_constant_p = true;
+	      return t;
+	    }
+	}
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -1675,7 +1748,7 @@ cxx_eval_call_expression (const constexp
       new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
       tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
       CONSTRUCTOR_NO_CLEARING (ctor) = true;
-      ctx->values->put (new_ctx.object, ctor);
+      ctx->global->values.put (new_ctx.object, ctor);
       ctx = &new_ctx;
     }
 
@@ -1877,7 +1950,7 @@ cxx_eval_call_expression (const constexp
 	      arg = unshare_constructor (arg);
 	      if (TREE_CODE (arg) == CONSTRUCTOR)
 		vec_safe_push (ctors, arg);
-	      ctx->values->put (remapped, arg);
+	      ctx->global->values.put (remapped, arg);
 	      remapped = DECL_CHAIN (remapped);
 	    }
 	  /* Add the RESULT_DECL to the values map, too.  */
@@ -1887,11 +1960,11 @@ cxx_eval_call_expression (const constexp
 	      slot = AGGR_INIT_EXPR_SLOT (t);
 	      tree addr = build_address (slot);
 	      addr = build_nop (TREE_TYPE (res), addr);
-	      ctx->values->put (res, addr);
-	      ctx->values->put (slot, NULL_TREE);
+	      ctx->global->values.put (res, addr);
+	      ctx->global->values.put (slot, NULL_TREE);
 	    }
 	  else
-	    ctx->values->put (res, NULL_TREE);
+	    ctx->global->values.put (res, NULL_TREE);
 
 	  /* Track the callee's evaluated SAVE_EXPRs so that we can forget
 	     their values after the call.  */
@@ -1916,7 +1989,7 @@ cxx_eval_call_expression (const constexp
 	    result = void_node;
 	  else
 	    {
-	      result = *ctx->values->get (slot ? slot : res);
+	      result = *ctx->global->values.get (slot ? slot : res);
 	      if (result == NULL_TREE && !*non_constant_p)
 		{
 		  if (!ctx->quiet)
@@ -1934,8 +2007,8 @@ cxx_eval_call_expression (const constexp
 	      && CLASS_TYPE_P (TREE_TYPE (new_obj))
 	      && CP_TYPE_CONST_P (TREE_TYPE (new_obj)))
 	    {
-	      /* Subobjects might not be stored in ctx->values but we can
-		 get its CONSTRUCTOR by evaluating *this.  */
+	      /* Subobjects might not be stored in ctx->global->values but we
+		 can get its CONSTRUCTOR by evaluating *this.  */
 	      tree e = cxx_eval_constant_expression (ctx, new_obj,
 						     /*lval*/false,
 						     non_constant_p,
@@ -1947,17 +2020,17 @@ cxx_eval_call_expression (const constexp
 	  unsigned int i;
 	  tree save_expr;
 	  FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-	    ctx_with_save_exprs.values->remove (save_expr);
+	    ctx->global->values.remove (save_expr);
 
 	  /* Remove the parms/result from the values map.  Is it worth
 	     bothering to do this when the map itself is only live for
 	     one constexpr evaluation?  If so, maybe also clear out
 	     other vars from call, maybe in BIND_EXPR handling?  */
-	  ctx->values->remove (res);
+	  ctx->global->values.remove (res);
 	  if (slot)
-	    ctx->values->remove (slot);
+	    ctx->global->values.remove (slot);
 	  for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
-	    ctx->values->remove (parm);
+	    ctx->global->values.remove (parm);
 
 	  /* Free any parameter CONSTRUCTORs we aren't returning directly.  */
 	  while (!ctors->is_empty ())
@@ -3077,7 +3150,7 @@ verify_ctor_sanity (const constexpr_ctx
 			  (TREE_TYPE (type), TREE_TYPE (otype)))));
     }
   gcc_assert (!ctx->object || !DECL_P (ctx->object)
-	      || *(ctx->values->get (ctx->object)) == ctx->ctor);
+	      || *(ctx->global->values.get (ctx->object)) == ctx->ctor);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -3610,7 +3683,23 @@ cxx_eval_indirect_ref (const constexpr_c
 static void
 non_const_var_error (tree r)
 {
+  auto_diagnostic_group d;
   tree type = TREE_TYPE (r);
+  if (DECL_NAME (r) == heap_uninit_identifier
+      || DECL_NAME (r) == heap_identifier)
+    {
+      error ("the content of uninitialized storage is not usable "
+	     "in a constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
+  if (DECL_NAME (r) == heap_deleted_identifier)
+    {
+      error ("use of allocated storage after deallocation in a "
+	     "constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
   error ("the value of %qD is not usable in a constant "
 	 "expression", r);
   /* Avoid error cascade.  */
@@ -3854,7 +3943,7 @@ cxx_eval_store_expression (const constex
        DECL_NAME to handle TARGET_EXPR temporaries, which are fair game.  */
     valp = NULL;
   else if (DECL_P (object))
-    valp = ctx->values->get (object);
+    valp = ctx->global->values.get (object);
   else
     valp = NULL;
   if (!valp)
@@ -4056,7 +4145,7 @@ cxx_eval_store_expression (const constex
 					   non_constant_p, overflow_p);
       if (ctors->is_empty())
 	/* The hash table might have moved since the get earlier.  */
-	valp = ctx->values->get (object);
+	valp = ctx->global->values.get (object);
     }
 
   /* Don't share a CONSTRUCTOR that might be changed later.  */
@@ -4332,6 +4421,12 @@ cxx_eval_loop_expr (const constexpr_ctx
 		    tree *jump_target)
 {
   constexpr_ctx new_ctx = *ctx;
+  tree local_target;
+  if (!jump_target)
+    {
+      local_target = NULL_TREE;
+      jump_target = &local_target;
+    }
 
   tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
@@ -4410,7 +4505,7 @@ cxx_eval_loop_expr (const constexpr_ctx
       unsigned int i;
       tree save_expr;
       FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-	new_ctx.values->remove (save_expr);
+	ctx->global->values.remove (save_expr);
       save_exprs.truncate (0);
 
       if (++count >= constexpr_loop_limit)
@@ -4434,7 +4529,7 @@ cxx_eval_loop_expr (const constexpr_ctx
   unsigned int i;
   tree save_expr;
   FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-    new_ctx.values->remove (save_expr);
+    ctx->global->values.remove (save_expr);
 
   return NULL_TREE;
 }
@@ -4586,14 +4681,14 @@ cxx_eval_constant_expression (const cons
     }
 
   /* Avoid excessively long constexpr evaluations.  */
-  if (++*ctx->constexpr_ops_count >= constexpr_ops_limit)
+  if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit)
     {
       if (!ctx->quiet)
 	error_at (cp_expr_loc_or_input_loc (t),
 		  "%<constexpr%> evaluation operation count exceeds limit of "
 		  "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)",
 		  constexpr_ops_limit);
-      *ctx->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT);
+      ctx->global->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT);
       *non_constant_p = true;
       return t;
     }
@@ -4610,7 +4705,7 @@ cxx_eval_constant_expression (const cons
       /* We ask for an rvalue for the RESULT_DECL when indirecting
 	 through an invisible reference, or in named return value
 	 optimization.  */
-      if (tree *p = ctx->values->get (t))
+      if (tree *p = ctx->global->values.get (t))
 	return *p;
       else
 	{
@@ -4666,7 +4761,7 @@ cxx_eval_constant_expression (const cons
 	  && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
 	r = TARGET_EXPR_INITIAL (r);
       if (VAR_P (r))
-	if (tree *p = ctx->values->get (r))
+	if (tree *p = ctx->global->values.get (r))
 	  if (*p != NULL_TREE)
 	    r = *p;
       if (DECL_P (r))
@@ -4693,7 +4788,7 @@ cxx_eval_constant_expression (const cons
     case PARM_DECL:
       if (lval && !TYPE_REF_P (TREE_TYPE (t)))
 	/* glvalue use.  */;
-      else if (tree *p = ctx->values->get (r))
+      else if (tree *p = ctx->global->values.get (r))
 	r = *p;
       else if (lval)
 	/* Defer in case this is only used for its type.  */;
@@ -4735,7 +4830,7 @@ cxx_eval_constant_expression (const cons
 	    new_ctx.object = r;
 	    new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
 	    CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
-	    new_ctx.values->put (r, new_ctx.ctor);
+	    ctx->global->values.put (r, new_ctx.ctor);
 	    ctx = &new_ctx;
 	  }
 
@@ -4751,12 +4846,12 @@ cxx_eval_constant_expression (const cons
 	    if (CLASS_TYPE_P (TREE_TYPE (r))
 		&& CP_TYPE_CONST_P (TREE_TYPE (r)))
 	      TREE_READONLY (init) = true;
-	    ctx->values->put (r, init);
+	    ctx->global->values.put (r, init);
 	  }
 	else if (ctx == &new_ctx)
 	  /* We gave it a CONSTRUCTOR above.  */;
 	else
-	  ctx->values->put (r, NULL_TREE);
+	  ctx->global->values.put (r, NULL_TREE);
       }
       break;
 
@@ -4782,7 +4877,7 @@ cxx_eval_constant_expression (const cons
 	  new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL);
 	  CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
 	  new_ctx.object = TARGET_EXPR_SLOT (t);
-	  ctx->values->put (new_ctx.object, new_ctx.ctor);
+	  ctx->global->values.put (new_ctx.object, new_ctx.ctor);
 	  ctx = &new_ctx;
 	}
       /* Pass false for 'lval' because this indicates
@@ -4797,7 +4892,7 @@ cxx_eval_constant_expression (const cons
 	{
 	  tree slot = TARGET_EXPR_SLOT (t);
 	  r = unshare_constructor (r);
-	  ctx->values->put (slot, r);
+	  ctx->global->values.put (slot, r);
 	  return slot;
 	}
       break;
@@ -4837,13 +4932,13 @@ cxx_eval_constant_expression (const cons
 
     case SAVE_EXPR:
       /* Avoid evaluating a SAVE_EXPR more than once.  */
-      if (tree *p = ctx->values->get (t))
+      if (tree *p = ctx->global->values.get (t))
 	r = *p;
       else
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
 					    non_constant_p, overflow_p);
-	  ctx->values->put (t, r);
+	  ctx->global->values.put (t, r);
 	  if (ctx->save_exprs)
 	    ctx->save_exprs->safe_push (t);
 	}
@@ -5177,8 +5272,7 @@ cxx_eval_constant_expression (const cons
 	if (VOID_TYPE_P (type))
 	  return void_node;
 
-	if (TREE_CODE (op) == PTRMEM_CST
-	    && !TYPE_PTRMEM_P (type))
+	if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
 
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
@@ -5232,6 +5326,34 @@ cxx_eval_constant_expression (const cons
 	      }
 	  }
 
+	if (INDIRECT_TYPE_P (type)
+	    && TREE_CODE (op) == NOP_EXPR
+	    && TREE_TYPE (op) == ptr_type_node
+	    && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR
+	    && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
+	    && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0),
+					0)) == heap_uninit_identifier)
+	  {
+	    tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0);
+	    tree var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	    tree elt_type = TREE_TYPE (type);
+	    tree cookie_size = NULL_TREE;
+	    if (TREE_CODE (elt_type) == RECORD_TYPE
+		&& TYPE_NAME (elt_type) == heap_identifier)
+	      {
+		tree fld1 = TYPE_FIELDS (elt_type);
+		tree fld2 = DECL_CHAIN (fld1);
+		elt_type = TREE_TYPE (TREE_TYPE (fld2));
+		cookie_size = TYPE_SIZE_UNIT (TREE_TYPE (fld1));
+	      }
+	    DECL_NAME (var) = heap_identifier;
+	    TREE_TYPE (var)
+	      = build_new_constexpr_heap_type (elt_type, cookie_size,
+					       var_size);
+	    TREE_TYPE (TREE_OPERAND (op, 0))
+	      = build_pointer_type (TREE_TYPE (var));
+	  }
+
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
@@ -5473,6 +5595,7 @@ instantiate_cx_fn_r (tree *tp, int *walk
 
   return NULL_TREE;
 }
+
 static void
 instantiate_constexpr_fns (tree t)
 {
@@ -5481,34 +5604,58 @@ instantiate_constexpr_fns (tree t)
   input_location = loc;
 }
 
+/* Look for heap variables in the expression *TP.  */
+
+static tree
+find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
+{
+  if (VAR_P (*tp)
+      && (DECL_NAME (*tp) == heap_uninit_identifier
+	  || DECL_NAME (*tp) == heap_identifier
+	  || DECL_NAME (*tp) == heap_deleted_identifier))
+    return *tp;
+
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+  return NULL_TREE;
+}
+
 /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
    STRICT has the same sense as for constant_value_1: true if we only allow
    conforming C++ constant expressions, or false if we want a constant value
    even if it doesn't conform.
    MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
-   per P0595 even when ALLOW_NON_CONSTANT is true.  */
+   per P0595 even when ALLOW_NON_CONSTANT is true.
+   CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable.
+   OBJECT must be non-NULL in that case.  */
 
 static tree
 cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 				  bool strict = true,
 				  bool manifestly_const_eval = false,
+				  bool constexpr_dtor = false,
 				  tree object = NULL_TREE)
 {
   auto_timevar time (TV_CONSTEXPR);
 
   bool non_constant_p = false;
   bool overflow_p = false;
-  hash_map<tree,tree> map;
-  HOST_WIDE_INT constexpr_ctx_count = 0;
 
-  constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
-			&constexpr_ctx_count, allow_non_constant, strict,
+  constexpr_global_ctx global_ctx;
+  constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL,
+			allow_non_constant, strict,
 			manifestly_const_eval || !allow_non_constant };
 
   tree type = initialized_type (t);
   tree r = t;
   if (VOID_TYPE_P (type))
-    return t;
+    {
+      if (constexpr_dtor)
+	/* Used for destructors of array elements.  */
+	type = TREE_TYPE (object);
+      else
+	return t;
+    }
   if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
     {
       /* In C++14 an NSDMI can participate in aggregate initialization,
@@ -5518,8 +5665,22 @@ cxx_eval_outermost_constant_expr (tree t
 	 update ctx.values for the VAR_DECL.  We use the same strategy
 	 for C++11 constexpr constructors that refer to the object being
 	 initialized.  */
-      ctx.ctor = build_constructor (type, NULL);
-      CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+      if (constexpr_dtor)
+	{
+	  gcc_assert (object && VAR_P (object));
+	  gcc_assert (DECL_DECLARED_CONSTEXPR_P (object));
+	  gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object));
+	  ctx.ctor = unshare_expr (DECL_INITIAL (object));
+	  TREE_READONLY (ctx.ctor) = false;
+	  /* Temporarily force decl_really_constant_value to return false
+	     for it, we want to use ctx.ctor for the current value instead.  */
+	  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false;
+	}
+      else
+	{
+	  ctx.ctor = build_constructor (type, NULL);
+	  CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+	}
       if (!object)
 	{
 	  if (TREE_CODE (t) == TARGET_EXPR)
@@ -5532,7 +5693,7 @@ cxx_eval_outermost_constant_expr (tree t
 	gcc_assert (same_type_ignoring_top_level_qualifiers_p
 		    (type, TREE_TYPE (object)));
       if (object && DECL_P (object))
-	map.put (object, ctx.ctor);
+	global_ctx.values.put (object, ctx.ctor);
       if (TREE_CODE (r) == TARGET_EXPR)
 	/* Avoid creating another CONSTRUCTOR when we expand the
 	   TARGET_EXPR.  */
@@ -5543,13 +5704,15 @@ cxx_eval_outermost_constant_expr (tree t
   r = cxx_eval_constant_expression (&ctx, r,
 				    false, &non_constant_p, &overflow_p);
 
-  verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  if (!constexpr_dtor)
+    verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  else
+    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
 
   /* Mutable logic is a bit tricky: we want to allow initialization of
      constexpr variables with mutable members, but we can't copy those
      members to another constexpr variable.  */
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_MUTABLE_POISON (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5557,8 +5720,7 @@ cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_NO_CLEARING (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5567,6 +5729,32 @@ cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
+  if (!global_ctx.heap_vars.is_empty ())
+    {
+      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
+						       NULL);
+      unsigned int i;
+      if (heap_var)
+	{
+	  if (!allow_non_constant && !non_constant_p)
+	    error_at (DECL_SOURCE_LOCATION (heap_var),
+		      "%qE is not a constant expression because it refers to "
+		      "a result of %<operator new%>", t);
+	  r = t;
+	  non_constant_p = true;
+	}
+      FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var)
+	if (DECL_NAME (heap_var) != heap_deleted_identifier)
+	  {
+	    if (!allow_non_constant && !non_constant_p)
+	      error_at (DECL_SOURCE_LOCATION (heap_var),
+			"%qE is not a constant expression because allocated "
+			"storage has not been deallocated", t);
+	    r = t;
+	    non_constant_p = true;
+	  }
+    }
+
   /* Technically we should check this for all subexpressions, but that
      runs into problems with our internal representation of pointer
      subtraction and the 5.19 rules are still in flux.  */
@@ -5592,6 +5780,8 @@ cxx_eval_outermost_constant_expr (tree t
 
   if (non_constant_p && !allow_non_constant)
     return error_mark_node;
+  else if (constexpr_dtor)
+    return r;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
       /* If __builtin_is_constant_evaluated () was evaluated to true
@@ -5599,7 +5789,7 @@ cxx_eval_outermost_constant_expr (tree t
 	 punt.  */
       if (manifestly_const_eval)
 	return cxx_eval_outermost_constant_expr (t, true, strict,
-						 false, object);
+						 false, false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
 	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
 	 it to be set if it is invariant address, even when it is not
@@ -5627,7 +5817,7 @@ cxx_eval_outermost_constant_expr (tree t
 	return t;
       else if (TREE_CODE (t) != CONSTRUCTOR)
 	{
-	  r = get_target_expr (r);
+	  r = get_target_expr_sfinae (r, tf_warning_or_error | tf_no_cleanup);
 	  TREE_CONSTANT (r) = true;
 	}
     }
@@ -5642,7 +5832,16 @@ cxx_eval_outermost_constant_expr (tree t
 tree
 cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
+  return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl);
+}
+
+/* Like cxx_constant_value, but used for evaluation of constexpr destructors
+   of constexpr variables.  The actual initializer of DECL is not modified.  */
+
+void
+cxx_constant_dtor (tree t, tree decl)
+{
+  cxx_eval_outermost_constant_expr (t, false, true, true, true, decl);
 }
 
 /* Helper routine for fold_simple function.  Either return simplified
@@ -5746,14 +5945,14 @@ maybe_constant_value (tree t, tree decl,
     return t;
 
   if (manifestly_const_eval)
-    return cxx_eval_outermost_constant_expr (t, true, true, true, decl);
+    return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl);
 
   if (cv_cache == NULL)
     cv_cache = hash_map<tree, tree>::create_ggc (101);
   if (tree *cached = cv_cache->get (t))
     return *cached;
 
-  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
+  r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl);
   gcc_checking_assert (r == t
 		       || CONVERT_EXPR_P (t)
 		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
@@ -5815,7 +6014,7 @@ fold_non_dependent_expr_template (tree t
 
       tree r = cxx_eval_outermost_constant_expr (t, true, true,
 						 manifestly_const_eval,
-						 NULL_TREE);
+						 false, NULL_TREE);
       /* cp_tree_equal looks through NOPs, so allow them.  */
       gcc_checking_assert (r == t
 			   || CONVERT_EXPR_P (t)
@@ -5919,7 +6118,7 @@ maybe_constant_init_1 (tree t, tree decl
   else
     t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
 					  /*strict*/false,
-					  manifestly_const_eval, decl);
+					  manifestly_const_eval, false, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -6213,7 +6412,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)
 		      {
@@ -6442,6 +6646,9 @@ potential_constant_expression_1 (tree t,
 	goto fail;
       if (!RECUR (TREE_OPERAND (t, 0), any))
 	return false;
+      /* Just ignore clobbers.  */
+      if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
+	return true;
       if (!RECUR (TREE_OPERAND (t, 1), rval))
 	return false;
       return true;
@@ -6911,7 +7118,7 @@ potential_constant_expression_1 (tree t,
      return true;
 
     case COND_EXPR:
-      if (COND_EXPR_IS_VEC_DELETE (t))
+      if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx2a)
 	{
 	  if (flags & tf_error)
 	    error_at (loc, "%<delete[]%> is not a constant expression");
--- gcc/cp/decl.c.jj	2019-10-03 17:55:28.394058162 +0200
+++ gcc/cp/decl.c	2019-10-04 17:29:43.736406584 +0200
@@ -4146,6 +4146,9 @@ initialize_predefined_identifiers (void)
     {"value", &value_identifier, cik_normal},
     {"_FUN", &fun_identifier, cik_normal},
     {"__closure", &closure_identifier, cik_normal},
+    {"heap uninit", &heap_uninit_identifier, cik_normal},
+    {"heap ", &heap_identifier, cik_normal},
+    {"heap deleted", &heap_deleted_identifier, cik_normal},
     {NULL, NULL, cik_normal}
   };
 
@@ -7006,6 +7009,19 @@ notice_forced_label_r (tree *tp, int *wa
   return NULL_TREE;
 }
 
+/* Return true if DECL has either a trivial destructor, or for C++2A
+   is constexpr and has a constexpr destructor.  */
+
+static bool
+decl_maybe_constant_destruction (tree decl)
+{
+  tree type = TREE_TYPE (decl);
+  return (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)
+	  || (cxx_dialect >= cxx2a
+	      && DECL_DECLARED_CONSTEXPR_P (decl)
+	      && type_has_constexpr_destructor (strip_array_types (type))));
+}
+
 /* Finish processing of a declaration;
    install its line number and initial value.
    If the length of an array type is not known before,
@@ -7430,7 +7446,7 @@ cp_finish_decl (tree decl, tree init, bo
 	    TREE_READONLY (decl) = 1;
 
 	  /* Likewise if it needs destruction.  */
-	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+	  if (!decl_maybe_constant_destruction (decl))
 	    TREE_READONLY (decl) = 0;
 	}
 
@@ -8312,6 +8328,13 @@ register_dtor_fn (tree decl)
   if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
     return void_node;
 
+  if (decl_maybe_constant_destruction (decl)
+      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+    {
+      cxx_maybe_build_cleanup (decl, tf_warning_or_error);
+      return void_node;
+    }
+
   /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
      "__aeabi_atexit"), and DECL is a class object, we can just pass the
      destructor to "__cxa_atexit"; we don't have to build a temporary
@@ -8429,7 +8452,7 @@ expand_static_init (tree decl, tree init
   gcc_assert (TREE_STATIC (decl));
 
   /* Some variables require no dynamic initialization.  */
-  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
+  if (decl_maybe_constant_destruction (decl))
     {
       /* Make sure the destructor is callable.  */
       cxx_maybe_build_cleanup (decl, tf_warning_or_error);
@@ -12697,12 +12720,13 @@ grokdeclarator (const cp_declarator *dec
 			      "a destructor cannot be %<concept%>");
                     return error_mark_node;
                   }
-                if (constexpr_p)
-                  {
-                    error_at (declspecs->locations[ds_constexpr],
-			      "a destructor cannot be %<constexpr%>");
-                    return error_mark_node;
-                  }
+		if (constexpr_p && cxx_dialect < cxx2a)
+		  {
+		    error_at (declspecs->locations[ds_constexpr],
+			      "%<constexpr%> destructors only available"
+			      " with %<-std=c++2a%> or %<-std=gnu++2a%>");
+		    return error_mark_node;
+		  }
 	      }
 	    else if (sfk == sfk_constructor && friendp && !ctype)
 	      {
@@ -12739,10 +12763,11 @@ grokdeclarator (const cp_declarator *dec
 	      }
 
 	    /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node.  */
-	    function_context = (ctype != NULL_TREE) ?
-	      decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE;
-	    publicp = (! friendp || ! staticp)
-	      && function_context == NULL_TREE;
+	    function_context
+	      = (ctype != NULL_TREE
+		 ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE);
+	    publicp = ((! friendp || ! staticp)
+		       && function_context == NULL_TREE);
 
 	    decl = grokfndecl (ctype, type,
 			       TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR
@@ -16742,6 +16767,10 @@ cxx_maybe_build_cleanup (tree decl, tsub
 	cleanup = error_mark_node;
       else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
 	/* Discard the call.  */;
+      else if (VAR_P (decl)
+	       && decl_maybe_constant_destruction (decl)
+	       && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+	cxx_constant_dtor (call, decl);
       else if (cleanup)
 	cleanup = cp_build_compound_expr (cleanup, call, complain);
       else
--- gcc/cp/init.c.jj	2019-10-03 17:55:28.337059023 +0200
+++ gcc/cp/init.c	2019-10-04 18:49:19.592533986 +0200
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "stor-layout.h"
 
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
@@ -2860,6 +2861,82 @@ std_placement_new_fn_p (tree alloc_fn)
   return false;
 }
 
+/* For element type ELT_TYPE, return the appropriate type of the heap object
+   containing such element(s).  COOKIE_SIZE is NULL or the size of cookie
+   in bytes.  FULL_SIZE is NULL if it is unknown how big the heap allocation
+   will be, otherwise size of the heap object.  If COOKIE_SIZE is NULL,
+   return array type ELT_TYPE[FULL_SIZE / sizeof(ELT_TYPE)], otherwise return
+   struct { size_t[COOKIE_SIZE/sizeof(size_t)]; ELT_TYPE[N]; }
+   where N is nothing (flexible array member) if FULL_SIZE is NULL, otherwise
+   it is computed such that the size of the struct fits into FULL_SIZE.  */
+
+tree
+build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree full_size)
+{
+  gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size));
+  gcc_assert (full_size == NULL_TREE || tree_fits_uhwi_p (full_size));
+  unsigned HOST_WIDE_INT csz = cookie_size ? tree_to_uhwi (cookie_size) : 0;
+  tree itype2 = NULL_TREE;
+  if (full_size)
+    {
+      unsigned HOST_WIDE_INT fsz = tree_to_uhwi (full_size);
+      gcc_assert (fsz >= csz);
+      fsz -= csz;
+      fsz /= int_size_in_bytes (elt_type);
+      itype2 = build_index_type (size_int (fsz - 1));
+      if (!cookie_size)
+	return build_cplus_array_type (elt_type, itype2);
+    }
+  else
+    gcc_assert (cookie_size);
+  csz /= int_size_in_bytes (sizetype);
+  tree itype1 = build_index_type (size_int (csz - 1));
+  tree atype1 = build_cplus_array_type (sizetype, itype1);
+  tree atype2 = build_cplus_array_type (elt_type, itype2);
+  tree rtype = cxx_make_type (RECORD_TYPE);
+  TYPE_NAME (rtype) = heap_identifier;
+  tree fld1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype1);
+  tree fld2 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype2);
+  DECL_FIELD_CONTEXT (fld1) = rtype;
+  DECL_FIELD_CONTEXT (fld2) = rtype;
+  DECL_ARTIFICIAL (fld1) = true;
+  DECL_ARTIFICIAL (fld2) = true;
+  TYPE_FIELDS (rtype) = fld1;
+  DECL_CHAIN (fld1) = fld2;
+  layout_type (rtype);
+  return rtype;
+}
+
+/* Help the constexpr code to find the right type for the heap variable
+   by adding a NOP_EXPR around ALLOC_CALL if needed for cookie_size.
+   Return ALLOC_CALL or ALLOC_CALL cast to a pointer to
+   struct { size_t[cookie_size/sizeof(size_t)]; elt_type[]; }.  */
+
+static tree
+maybe_wrap_new_for_constexpr (tree alloc_call, tree elt_type, tree cookie_size)
+{
+  if (cxx_dialect < cxx2a)
+    return alloc_call;
+
+  if (current_function_decl != NULL_TREE
+      && !DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+    return alloc_call;
+  
+  tree call_expr = extract_call_expr (alloc_call);
+  if (call_expr == error_mark_node)
+    return alloc_call;
+
+  tree alloc_call_fndecl = cp_get_callee_fndecl_nofold (call_expr);
+  if (alloc_call_fndecl == NULL_TREE
+      || !IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl))
+      || CP_DECL_CONTEXT (alloc_call_fndecl) != global_namespace)
+    return alloc_call;
+
+  tree rtype = build_new_constexpr_heap_type (elt_type, cookie_size,
+					      NULL_TREE);
+  return build_nop (build_pointer_type (rtype), alloc_call);
+}
+
 /* Generate code for a new-expression, including calling the "operator
    new" function, initializing the object, and, if an exception occurs
    during construction, cleaning up.  The arguments are as for
@@ -3327,6 +3404,10 @@ build_new_1 (vec<tree, va_gc> **placemen
 	}
     }
 
+  if (cookie_size)
+    alloc_call = maybe_wrap_new_for_constexpr (alloc_call, elt_type,
+					       cookie_size);
+
   /* In the simple case, we can stop now.  */
   pointer_type = build_pointer_type (type);
   if (!cookie_size && !is_initialized)
@@ -3902,17 +3983,11 @@ build_vec_delete_1 (tree base, tree maxi
 			     fold_convert (sizetype, maxindex));
 
   tbase = create_temporary_var (ptype);
-  tbase_init
-    = cp_build_modify_expr (input_location, tbase, NOP_EXPR,
-			    fold_build_pointer_plus_loc (input_location,
-							 fold_convert (ptype,
-								       base),
-							 virtual_size),
-			    complain);
-  if (tbase_init == error_mark_node)
-    return error_mark_node;
-  controller = build3 (BIND_EXPR, void_type_node, tbase,
-		       NULL_TREE, NULL_TREE);
+  DECL_INITIAL (tbase)
+    = fold_build_pointer_plus_loc (input_location, fold_convert (ptype, base),
+				   virtual_size);
+  tbase_init = build_stmt (input_location, DECL_EXPR, tbase);
+  controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE);
   TREE_SIDE_EFFECTS (controller) = 1;
 
   body = build1 (EXIT_EXPR, void_type_node,
--- gcc/cp/method.c.jj	2019-10-03 17:55:28.466057074 +0200
+++ gcc/cp/method.c	2019-10-04 16:56:11.509087416 +0200
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.
 #include "cgraph.h"
 #include "varasm.h"
 #include "toplev.h"
+#include "intl.h"
 #include "common/common-target.h"
 
 static void do_build_copy_assign (tree);
@@ -1237,12 +1238,24 @@ is_xible (enum tree_code code, tree to,
   return !!expr;
 }
 
+/* Categorize various special_function_kinds.  */
+#define SFK_CTOR_P(sfk) \
+  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
+#define SFK_DTOR_P(sfk) \
+  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
+#define SFK_ASSIGN_P(sfk) \
+  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
+#define SFK_COPY_P(sfk) \
+  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
+#define SFK_MOVE_P(sfk) \
+  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
+
 /* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
    DELETED_P or give an error message MSG with argument ARG.  */
 
 static void
-process_subob_fn (tree fn, tree *spec_p, bool *trivial_p,
-		  bool *deleted_p, bool *constexpr_p,
+process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p,
+		  bool *trivial_p, bool *deleted_p, bool *constexpr_p,
 		  bool diag, tree arg, bool dtor_from_ctor = false)
 {
   if (!fn || fn == error_mark_node)
@@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p,
       if (diag)
 	{
 	  inform (DECL_SOURCE_LOCATION (fn),
-		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
+		  SFK_DTOR_P (sfk)
+		  ? G_("defaulted destructor calls non-%<constexpr%> %qD")
+		  : G_("defaulted constructor calls non-%<constexpr%> %qD"),
+		  fn);
 	  explain_invalid_constexpr_fn (fn);
 	}
     }
 }
 
-/* Categorize various special_function_kinds.  */
-#define SFK_CTOR_P(sfk) \
-  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
-#define SFK_DTOR_P(sfk) \
-  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
-#define SFK_ASSIGN_P(sfk) \
-  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
-#define SFK_COPY_P(sfk) \
-  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
-#define SFK_MOVE_P(sfk) \
-  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
-
 /* Subroutine of synthesized_method_walk to allow recursion into anonymous
    aggregates.  If DTOR_FROM_CTOR is true, we're walking subobject destructors
    called from a synthesized constructor, in which case we don't consider
@@ -1318,8 +1322,7 @@ walk_field_subobs (tree fields, special_
     {
       tree mem_type, argtype, rval;
 
-      if (TREE_CODE (field) != FIELD_DECL
-	  || DECL_ARTIFICIAL (field))
+      if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
 	continue;
 
       /* Variant members only affect deletedness.  In particular, they don't
@@ -1457,7 +1460,7 @@ walk_field_subobs (tree fields, special_
 
       rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
 
-      process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+      process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 			constexpr_p, diag, field, dtor_from_ctor);
     }
 }
@@ -1510,23 +1513,23 @@ synthesized_method_base_walk (tree binfo
       && DECL_CONTEXT (*inheriting_ctor) == DECL_CONTEXT (rval))
     *inheriting_ctor = DECL_CLONED_FUNCTION (rval);
 
-  process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+  process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 		    constexpr_p, diag, BINFO_TYPE (base_binfo));
-  if (SFK_CTOR_P (sfk) &&
-      (!BINFO_VIRTUAL_P (base_binfo)
-       || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
+  if (SFK_CTOR_P (sfk)
+      && (!BINFO_VIRTUAL_P (base_binfo)
+	  || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
     {
       /* In a constructor we also need to check the subobject
 	 destructors for cleanup of partially constructed objects.  */
       tree dtor = locate_fn_flags (base_binfo, complete_dtor_identifier,
 				   NULL_TREE, flags,
 				   diag ? tf_warning_or_error : tf_none);
-	  /* Note that we don't pass down trivial_p; the subobject
-	     destructors don't affect triviality of the constructor.  Nor
-	     do they affect constexpr-ness (a constant expression doesn't
-	     throw) or exception-specification (a throw from one of the
-	     dtors would be a double-fault).  */
-      process_subob_fn (dtor, NULL, NULL, deleted_p, NULL, false,
+      /* Note that we don't pass down trivial_p; the subobject
+	 destructors don't affect triviality of the constructor.  Nor
+	 do they affect constexpr-ness (a constant expression doesn't
+	 throw) or exception-specification (a throw from one of the
+	 dtors would be a double-fault).  */
+      process_subob_fn (dtor, sfk, NULL, NULL, deleted_p, NULL, false,
 			BINFO_TYPE (base_binfo), /*dtor_from_ctor*/true);
     }
 
@@ -1608,7 +1611,8 @@ synthesized_method_walk (tree ctype, spe
 	member is a constexpr function.  */
   if (constexpr_p)
     *constexpr_p = (SFK_CTOR_P (sfk)
-		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14));
+		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
+		    || (SFK_DTOR_P (sfk) && cxx_dialect >= cxx2a));
 
   bool expected_trivial = type_has_trivial_fn (ctype, sfk);
   if (trivial_p)
@@ -1704,8 +1708,8 @@ synthesized_method_walk (tree ctype, spe
   else if (vec_safe_is_empty (vbases))
     /* No virtual bases to worry about.  */;
   else if (ABSTRACT_CLASS_TYPE_P (ctype) && cxx_dialect >= cxx14
-	   /* DR 1658 specifis that vbases of abstract classes are
-	      ignored for both ctors and dtors.  Except DR 2338
+	   /* DR 1658 specifies that vbases of abstract classes are
+	      ignored for both ctors and dtors.  Except DR 2336
 	      overrides that skipping when determing the eh-spec of a
 	      virtual destructor.  */
 	   && sfk != sfk_virtual_destructor)
@@ -2046,7 +2050,8 @@ implicitly_declare_fn (special_function_
     constexpr_p = false;
   /* A trivial copy/move constructor is also a constexpr constructor,
      unless the class has virtual bases (7.1.5p4).  */
-  else if (trivial_p && cxx_dialect >= cxx11
+  else if (trivial_p
+	   && cxx_dialect >= cxx11
 	   && (kind == sfk_copy_constructor
 	       || kind == sfk_move_constructor)
 	   && !CLASSTYPE_VBASECLASSES (type))
--- gcc/cp/typeck2.c.jj	2019-10-03 17:55:28.312059400 +0200
+++ gcc/cp/typeck2.c	2019-10-04 12:49:21.328033198 +0200
@@ -902,7 +902,13 @@ store_init_value (tree decl, tree init,
 	    value = oldval;
 	}
     }
-  value = cp_fully_fold_init (value);
+  /* Don't fold initializers of automatic variables in constexpr functions,
+     that might fold away something that needs to be diagnosed at constexpr
+     evaluation time.  */
+  if (!current_function_decl
+      || !DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+      || TREE_STATIC (decl))
+    value = cp_fully_fold_init (value);
 
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C.jj	2019-10-03 17:55:29.185046209 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C	2019-10-04 12:49:21.521030243 +0200
@@ -5,8 +5,9 @@ struct A { ~A(); };
 constexpr int f(int i) { return i; }
 constexpr int g(A* ap)
 {
-  return f((delete[] ap, 42)); // { dg-message "" }
+  return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
 }
 
 A a;
 constexpr int i = g(&a);	// { dg-error "" }
+				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
--- gcc/testsuite/g++.dg/cpp0x/locations1.C.jj	2019-10-03 17:55:29.130047041 +0200
+++ gcc/testsuite/g++.dg/cpp0x/locations1.C	2019-10-04 12:49:21.610028880 +0200
@@ -11,7 +11,7 @@ struct S
 {
   virtual S();  // { dg-error "3:constructors cannot be declared .virtual." }
   constexpr int s = 1;  // { dg-error "3:non-static data member .s. declared .constexpr." }
-  constexpr ~S();  // { dg-error "3:a destructor cannot be .constexpr." }
+  constexpr ~S();  // { dg-error "3:'constexpr' destructors only available with" "" { target c++17_down } }
 };
 
 typedef constexpr int my_int;  // { dg-error "9:.constexpr. cannot appear in a typedef declaration" }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-new.C.jj	2019-10-03 17:55:29.284044714 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-new.C	2019-10-04 12:49:21.369032571 +0200
@@ -4,7 +4,7 @@ constexpr int *f4(bool b) {
   if (b) {
     return nullptr;
   } else {
-    return new int{42}; // { dg-error "call to non-.constexpr." }
+    return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } }
   }
 }
 static_assert(f4(true) == nullptr, "");
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,9 @@
+// P0784R7
+// { dg-do compile { target c++11 } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}	// { dg-error "'constexpr' destructors only available with" "" { target c++17_down } }
+  int s;
+};
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C.jj	2019-10-04 12:49:21.465031101 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,66 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : r (4), s (3) { --r; s -= 2; }
+  constexpr ~S () { if (s == 1) s = 0; else asm (""); if (s == 0 && r == 3) r = 0; else asm (""); }
+  int r, s;
+};
+struct T : public S
+{
+  constexpr T () : t (2) {}
+  int t;
+  S u;
+};
+struct U : public S
+{
+  constexpr U (int x) : u (x) {}
+  constexpr ~U () = default;
+  int u;
+  S v;
+};
+
+constexpr S a;
+constexpr T b;
+constexpr U c = 3;
+static_assert (a.s == 1 && a.r == 3);
+static_assert (b.s == 1 && b.r == 3 && b.t == 2 && b.u.s == 1 && b.u.r == 3);
+static_assert (c.s == 1 && c.r == 3 && c.u == 3 && c.v.s == 1 && c.v.r == 3);
+
+void
+foo ()
+{
+  static constexpr S d;
+  static constexpr T e;
+  static constexpr U f = 4;
+  static_assert (d.s == 1 && d.r == 3);
+  static_assert (e.s == 1 && e.r == 3 && e.t == 2 && e.u.s == 1 && e.u.r == 3);
+  static_assert (f.s == 1 && f.r == 3 && f.u == 4 && f.v.s == 1 && f.v.r == 3);
+  if (1)
+    {
+      constexpr S g;
+      constexpr T h;
+      constexpr U i = 5;
+      static_assert (g.s == 1 && g.r == 3);
+      static_assert (h.s == 1 && h.r == 3 && h.t == 2 && h.u.s == 1 && h.u.r == 3);
+      static_assert (i.s == 1 && i.r == 3 && i.u == 5 && i.v.s == 1 && i.v.r == 3);
+    }
+}
+
+constexpr bool
+bar ()
+{
+  S j;
+  T k;
+  U l = 6;
+  if (j.s != 1 || j.r != 3)
+    return false;
+  if (k.s != 1 || k.r != 3 || k.t != 2 || k.u.s != 1 || k.u.r != 3)
+    return false;
+  if (l.s != 1 || l.r != 3 || l.u != 6 || l.v.s != 1 || l.v.r != 3)
+    return false;
+  return true;
+}
+
+static_assert (bar ());
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2019-10-04 16:56:50.216495821 +0200
@@ -0,0 +1,185 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}
+  int s;
+};
+struct T	// { dg-message "'T' is not literal because" }
+{		// { dg-message "'T' does not have 'constexpr' destructor" "" { target *-*-* } .-1 }
+  constexpr T () : t (0) {}
+  ~T () {}	// { dg-message "defaulted destructor calls non-'constexpr' 'T::~T\\(\\)'" }
+  int t;
+};
+struct U : public S
+{
+  constexpr U () : u (0) {}
+  constexpr ~U () = default;	// { dg-error "explicitly defaulted function 'constexpr U::~U\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int u;
+  T t;
+};
+struct V : virtual public S
+{
+  V () : v (0) {}
+  constexpr ~V () = default;	// { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int v;
+};
+struct W0
+{
+  constexpr W0 () : w (0) {}
+  constexpr W0 (int x) : w (x) {}
+  constexpr ~W0 () { if (w == 5) asm (""); w = 3; }
+  int w;
+};
+struct W1
+{
+  constexpr W1 () : w (0) {}
+  constexpr W1 (int x) : w (x) {}
+  constexpr ~W1 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W2
+{
+  constexpr W2 () : w (0) {}
+  constexpr W2 (int x) : w (x) {}
+  constexpr ~W2 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W3
+{
+  constexpr W3 () : w (0) {}
+  constexpr W3 (int x) : w (x) {}
+  constexpr ~W3 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W4
+{
+  constexpr W4 () : w (0) {}
+  constexpr W4 (int x) : w (x) {}
+  constexpr ~W4 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W5
+{
+  constexpr W5 () : w (0) {}
+  constexpr W5 (int x) : w (x) {}
+  constexpr ~W5 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W6
+{
+  constexpr W6 () : w (0) {}
+  constexpr W6 (int x) : w (x) {}
+  constexpr ~W6 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W7
+{
+  constexpr W7 () : w (0) {}
+  constexpr W7 (int x) : w (x) {}
+  constexpr ~W7 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W8
+{
+  constexpr W8 () : w (0) {}
+  constexpr W8 (int x) : w (x) {}
+  constexpr ~W8 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct X : public T
+{
+  constexpr X () : x (0) {}
+  constexpr ~X () = default;	// { dg-error "explicitly defaulted function 'constexpr X::~X\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int x;
+};
+constexpr S s;
+constexpr T t;	// { dg-error "the type 'const T' of 'constexpr' variable 't' is not literal" }
+constexpr W0 w1;
+constexpr W0 w2 = 12;
+constexpr W1 w3 = 5;	// { dg-message "in 'constexpr' expansion of" }
+constexpr W0 w4[3] = { 1, 2, 3 };
+constexpr W2 w5[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f1 ()
+{
+  constexpr S s2;
+  constexpr W0 w6;
+  constexpr W0 w7 = 12;
+  constexpr W3 w8 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w9[3] = { 1, 2, 3 };
+  constexpr W4 w10[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+}
+
+constexpr int
+f2 ()
+{
+  constexpr S s3;
+  constexpr W0 w11;
+  constexpr W0 w12 = 12;
+  constexpr W5 w13 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w14[3] = { 1, 2, 3 };
+  constexpr W6 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int
+f3 ()
+{
+  S s3;
+  W0 w11;
+  W0 w12 = 12;
+  W0 w14[3] = { 1, 2, 3 };
+  return 0;
+}
+
+constexpr int x3 = f3 ();
+
+constexpr int
+f4 ()
+{
+  W7 w13 = 5;
+  return 0;
+}
+
+constexpr int x4 = f4 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr int
+f5 ()
+{
+  W8 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int x5 = f5 ();	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f6 ()
+{
+  constexpr T t2;	// { dg-error "the type 'const T' of 'constexpr' variable 't2' is not literal" }
+}
+
+constexpr int
+f7 ()
+{
+  constexpr T t3;	// { dg-error "the type 'const T' of 'constexpr' variable 't3' is not literal" }
+  return 0;
+}
+
+constexpr int
+f8 ()
+{
+  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
+  return 0;
+}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,39 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S { constexpr S () : s (5) {} constexpr S (int x) : s (x) {} int s; };
+
+constexpr bool
+foo ()
+{
+  int r = 0;
+  S *p = new S ();
+  p->s += 3;
+  r += p->s;
+  delete p;
+  p = new S (12);
+  p->s = p->s * 2;
+  r += p->s;
+  delete p;
+  int *q = new int;
+  *q = 25;
+  r += *q;
+  delete q;
+  q = new int (1);
+  r += *q;
+  if (!q)
+    return false;
+  delete q;
+  q = new int[5]{1,2,3,4,5};
+  r += q[0] + q[4];
+  delete[] q;
+  q = new int[4];
+  q[0] = 6;
+  q[1] = 7;
+  q[3] = 8;
+  r += q[0] + q[1] + q[3];
+  delete[] q;
+  return r == 5 + 3 + 2 * 12 + 25 + 1 + 1 + 5 + 6 + 7 + 8;
+}
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C.jj	2019-10-04 12:49:21.465031101 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C	2019-10-04 12:49:21.465031101 +0200
@@ -0,0 +1,21 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+template <int N>
+constexpr bool
+foo (const char (&x)[N])
+{
+  int **p = new int *[N];
+  for (int i = 0; i < N; i++)
+    p[i] = new int (x[i]);
+  for (int i = 0; i < N; i++)
+    if (*p[i] != x[i])
+      return false;
+  for (int i = 0; i < N; ++i)
+    delete p[i];
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ("foobar");
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-10-04 19:19:25.410753359 +0200
@@ -0,0 +1,73 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+constexpr int *
+f1 ()
+{
+  return new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+}
+
+constexpr auto v1 = f1 ();
+
+constexpr bool
+f2 ()
+{
+  int *p = new int (3);		// { dg-error "is not a constant expression because allocated storage has not been deallocated" }
+  return false;
+}
+
+constexpr auto v2 = f2 ();
+
+constexpr bool
+f3 ()
+{
+  int *p = new int (3);
+  int *q = p;
+  delete p;
+  delete q;			// { dg-error "deallocation of already deallocated storage" }
+  return false;
+}
+
+constexpr auto v3 = f3 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f4 (int *p)
+{
+  delete p;			// { dg-error "deallocation of storage that was not previously allocated" }
+  return false;
+}
+
+int q;
+constexpr auto v4 = f4 (&q);	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f5 ()
+{
+  int *p = new int;		// { dg-message "allocated here" }
+  return *p == 1;
+}
+
+constexpr auto v5 = f5 ();	// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
+
+constexpr bool
+f6 ()
+{
+  int *p = new int (2);		// { dg-message "allocated here" }
+  int *q = p;
+  delete p;
+  return *q == 2;
+}
+
+constexpr auto v6 = f6 ();	// { dg-error "use of allocated storage after deallocation in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1  }
+
+constexpr int *
+f7 ()
+{
+  int *p = new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+  delete p;
+  return p;
+}
+
+constexpr auto v7 = f7 ();
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C.jj	2019-10-04 12:49:21.429031653 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C	2019-10-04 12:49:21.429031653 +0200
@@ -0,0 +1,29 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) { s++; }
+  constexpr S (int x) : s (x) { s += 2; }
+  constexpr ~S () { if (s != 35) asm (""); s = 5; }
+  int s;
+};
+
+constexpr bool
+foo ()
+{
+  S *p = new S (7);
+  if (p->s != 9) return false;
+  p->s = 35;
+  delete p;
+  p = new S[3] { 11, 13, 15 };
+  if (p[0].s != 13 || p[1].s != 15 || p[2].s != 17) return false;
+  p[0].s = 35;
+  p[2].s = 35;
+  p[1].s = 35;
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj	2019-10-03 17:55:29.421042643 +0200
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2019-10-04 12:49:21.464031117 +0200
@@ -430,16 +430,34 @@
 
 // C++20 features
 
-#if __cpp_conditional_explicit != 201806
-# error "__cpp_conditional_explicit != 201806"
+#ifndef __cpp_conditional_explicit
+#  error "__cpp_conditional_explicit"
+#elif __cpp_conditional_explicit != 201806
+#  error "__cpp_conditional_explicit != 201806"
 #endif
 
-#if __cpp_nontype_template_parameter_class != 201806
-# error "__cpp_nontype_template_parameter_class != 201806"
+#ifndef __cpp_nontype_template_parameter_class
+#  error "__cpp_nontype_template_parameter_class"
+#elif __cpp_nontype_template_parameter_class != 201806
+#  error "__cpp_nontype_template_parameter_class != 201806"
 #endif
 
-#if __cpp_impl_destroying_delete != 201806
-# error "__cpp_impl_destroying_delete != 201806"
+#ifndef __cpp_impl_destroying_delete
+#  error "__cpp_impl_destroying_delete"
+#elif __cpp_impl_destroying_delete != 201806
+#  error "__cpp_impl_destroying_delete != 201806"
+#endif
+
+#ifndef __cpp_constinit
+#  error "__cpp_constinit"
+#elif __cpp_constinit != 201907
+#  error "__cpp_constinit != 201907"
+#endif
+
+#ifndef __cpp_constexpr_dynamic_alloc
+#  error "__cpp_constexpr_dynamic_alloc"
+#elif __cpp_constexpr_dynamic_alloc != 201907
+#  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
 #ifdef __has_cpp_attribute
@@ -484,8 +502,6 @@
 #  error "__has_cpp_attribute"
 #endif
 
-// C++2A features:
-
 #ifndef __cpp_char8_t
 #  error "__cpp_char8_t"
 #elif __cpp_char8_t != 201811
--- gcc/testsuite/g++.dg/ext/is_literal_type3.C.jj	2019-10-04 12:49:21.338033046 +0200
+++ gcc/testsuite/g++.dg/ext/is_literal_type3.C	2019-10-04 12:49:21.338033046 +0200
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++11 } }
+
+struct S {
+  constexpr S () : n{} { }
+  ~S () { n = 1; }
+  int n;
+};
+
+static_assert(!__is_literal_type(S), "");
+
+#ifdef __cpp_constexpr_dynamic_alloc
+struct T {
+  constexpr T () : n{} { }
+  constexpr ~T () { n = 1; }
+  int n;
+};
+
+static_assert(__is_literal_type(T), "");
+
+struct U : public T {
+  constexpr U () : u{} { }
+  int u;
+};
+
+static_assert(__is_literal_type(U), "");
+#endif


	Jakub
--- gcc/cp/constexpr.c	2019-10-04 19:17:43.984309712 +0200
+++ gcc/cp/constexpr.c	2019-10-04 19:12:27.539166890 +0200
@@ -5604,22 +5604,6 @@
   input_location = loc;
 }
 
-/* Look for heap variables in the expression *TP.  */
-
-static tree
-find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
-{
-  if (VAR_P (*tp)
-      && (DECL_NAME (*tp) == heap_uninit_identifier
-	  || DECL_NAME (*tp) == heap_identifier
-	  || DECL_NAME (*tp) == heap_deleted_identifier))
-    return *tp;
-
-  if (TYPE_P (*tp))
-    *walk_subtrees = 0;
-  return NULL_TREE;
-}
-
 /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
    STRICT has the same sense as for constant_value_1: true if we only allow
    conforming C++ constant expressions, or false if we want a constant value
@@ -5704,6 +5688,25 @@
   r = cxx_eval_constant_expression (&ctx, r,
 				    false, &non_constant_p, &overflow_p);
 
+  if (!global_ctx.heap_vars.is_empty ())
+    {
+      unsigned int i;
+      tree heap_var;
+      FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var)
+	{
+	  TREE_STATIC (heap_var) = false;
+	  if (DECL_NAME (heap_var) != heap_deleted_identifier)
+	    {
+	      if (!allow_non_constant && !non_constant_p)
+		error_at (DECL_SOURCE_LOCATION (heap_var),
+			  "%qE is not a constant expression because allocated "
+			  "storage has not been deallocated", t);
+	      r = t;
+	      non_constant_p = true;
+	    }
+	}
+    }
+
   if (!constexpr_dtor)
     verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
   else
@@ -5729,32 +5732,6 @@
       non_constant_p = true;
     }
 
-  if (!global_ctx.heap_vars.is_empty ())
-    {
-      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
-						       NULL);
-      unsigned int i;
-      if (heap_var)
-	{
-	  if (!allow_non_constant && !non_constant_p)
-	    error_at (DECL_SOURCE_LOCATION (heap_var),
-		      "%qE is not a constant expression because it refers to "
-		      "a result of %<operator new%>", t);
-	  r = t;
-	  non_constant_p = true;
-	}
-      FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var)
-	if (DECL_NAME (heap_var) != heap_deleted_identifier)
-	  {
-	    if (!allow_non_constant && !non_constant_p)
-	      error_at (DECL_SOURCE_LOCATION (heap_var),
-			"%qE is not a constant expression because allocated "
-			"storage has not been deallocated", t);
-	    r = t;
-	    non_constant_p = true;
-	  }
-    }
-
   /* Technically we should check this for all subexpressions, but that
      runs into problems with our internal representation of pointer
      subtraction and the 5.19 rules are still in flux.  */
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-10-04 19:19:25.410753359 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-10-04 19:16:19.447606898 +0200
@@ -4,7 +4,7 @@
 constexpr int *
 f1 ()
 {
-  return new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+  return new int (2);		// { dg-error "is not a constant expression because allocated storage has not been deallocated" }
 }
 
 constexpr auto v1 = f1 ();
@@ -68,6 +68,6 @@
-  int *p = new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+  int *p = new int (2);
   delete p;
   return p;
 }
 
-constexpr auto v7 = f7 ();
+constexpr auto v7 = f7 ();	// { dg-error "is not a constant expression" }
Jason Merrill Oct. 4, 2019, 7:34 p.m. UTC | #7
On 10/4/19 1:50 PM, Jakub Jelinek wrote:
> On Thu, Oct 03, 2019 at 04:07:14PM -0400, Jason Merrill wrote:
>>> I believe it doesn't, because the heap VAR_DECLs are TREE_STATIC (things
>>> really don't work at all if they aren't).
>>
>> Ah, sure.  I suppose you could clear TREE_STATIC from them before the
>> verify_constant in this function?
> 
> That works, but generates uglier diagnostics.
> Otherwise, I've tried to address all your comments.
> 
> So, here is the updated patch without the find_heap_var_refs removal and
> TREE_STATIC clearing (tested so far just with
> make check-c++-all RUNTESTFLAGS="--target_board=unix\{-m32,-m64\} dg.exp='constexpr-array* eval-order* is_literal* constexpr-new* constexpr-dtor* constexpr-delete* locations1.C feat-cxx*'
> ) plus attached incremental patch for the find_heap_var_refs removal and
> TREE_STATIC clearing.  The difference can be seen in the incremental diff,
> the ugly part is
> error: '(int*)(& heap deleted)' is not a constant expression
> but it will show up only if the constant to be verified contains pointers to
> deallocated heap, if it still contains pointers to allocated heap, it will
> instead complain about allocated heap not being deallocated.
> 
> 2019-10-04  Jakub Jelinek  <jakub@redhat.com>
> 
> 	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_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}.
> 	<case NOP_EXPR>: 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 fix.
> 	* typeck2.c (store_init_value): Don't call cp_fully_fold_init on
> 	initializers of automatic non-constexpr variables in constexpr
> 	functions.
> testsuite/
> 	* g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for
> 	c++2a.
> 	* g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics
> 	in c++17_down, adjust expected wording.
> 	* g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down.
> 	* g++.dg/cpp2a/constexpr-dtor1.C: New test.
> 	* g++.dg/cpp2a/constexpr-dtor2.C: New test.
> 	* g++.dg/cpp2a/constexpr-dtor3.C: New test.
> 	* g++.dg/cpp2a/constexpr-new1.C: New test.
> 	* g++.dg/cpp2a/constexpr-new2.C: New test.
> 	* g++.dg/cpp2a/constexpr-new3.C: New test.
> 	* g++.dg/cpp2a/constexpr-new4.C: New test.
> 	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and
> 	__cpp_constexpr_dynamic_alloc tests.  Tweak __cpp_* tests for c++2a
> 	features to use style like older features, including #ifdef test.
> 	* g++.dg/ext/is_literal_type3.C: New test.
> 
> --- gcc/c-family/c-cppbuiltin.c.jj	2019-10-03 17:55:28.657054188 +0200
> +++ gcc/c-family/c-cppbuiltin.c	2019-10-04 12:49:21.610028880 +0200
> @@ -989,6 +989,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_constinit=201907");
>   	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
>   	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
> +	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
>   	}
>         if (flag_concepts)
>   	cpp_define (pfile, "__cpp_concepts=201507");
> --- gcc/cp/cp-tree.h.jj	2019-10-03 17:55:28.526056167 +0200
> +++ gcc/cp/cp-tree.h	2019-10-04 18:51:24.935599025 +0200
> @@ -172,6 +172,9 @@ enum cp_tree_index
>       CPTI_VALUE_IDENTIFIER,
>       CPTI_FUN_IDENTIFIER,
>       CPTI_CLOSURE_IDENTIFIER,
> +    CPTI_HEAP_UNINIT_IDENTIFIER,
> +    CPTI_HEAP_IDENTIFIER,
> +    CPTI_HEAP_DELETED_IDENTIFIER,
>   
>       CPTI_LANG_NAME_C,
>       CPTI_LANG_NAME_CPLUSPLUS,
> @@ -310,6 +313,9 @@ extern GTY(()) tree cp_global_trees[CPTI
>   #define value_identifier		cp_global_trees[CPTI_VALUE_IDENTIFIER]
>   #define fun_identifier			cp_global_trees[CPTI_FUN_IDENTIFIER]
>   #define closure_identifier		cp_global_trees[CPTI_CLOSURE_IDENTIFIER]
> +#define heap_uninit_identifier		cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER]
> +#define heap_identifier			cp_global_trees[CPTI_HEAP_IDENTIFIER]
> +#define heap_deleted_identifier		cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER]
>   #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
>   #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
>   
> @@ -6342,6 +6348,7 @@ extern bool vbase_has_user_provided_move
>   extern tree default_init_uninitialized_part (tree);
>   extern bool trivial_default_constructor_is_constexpr (tree);
>   extern bool type_has_constexpr_default_constructor (tree);
> +extern bool type_has_constexpr_destructor	(tree);
>   extern bool type_has_virtual_destructor		(tree);
>   extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
>   extern bool classtype_has_non_deleted_move_ctor (tree);
> @@ -6648,6 +6655,7 @@ extern tree build_offset_ref			(tree, tr
>   extern tree throw_bad_array_new_length		(void);
>   extern bool type_has_new_extended_alignment	(tree);
>   extern unsigned malloc_alignment		(void);
> +extern tree build_new_constexpr_heap_type	(tree, tree, tree);
>   extern tree build_new				(vec<tree, va_gc> **, tree, tree,
>   						 vec<tree, va_gc> **, int,
>                                                    tsubst_flags_t);
> @@ -7747,6 +7755,7 @@ extern bool require_constant_expression
>   extern bool require_rvalue_constant_expression (tree);
>   extern bool require_potential_rvalue_constant_expression (tree);
>   extern tree cxx_constant_value			(tree, tree = NULL_TREE);
> +extern void cxx_constant_dtor			(tree, tree);
>   extern tree cxx_constant_init			(tree, tree = NULL_TREE);
>   extern tree maybe_constant_value		(tree, tree = NULL_TREE, bool = false);
>   extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
> --- gcc/cp/class.c.jj	2019-10-03 17:55:28.554055744 +0200
> +++ gcc/cp/class.c	2019-10-04 12:49:21.336033076 +0200
> @@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_
>   static tree end_of_base (tree);
>   static tree get_vcall_index (tree, tree);
>   static bool type_maybe_constexpr_default_constructor (tree);
> +static bool type_maybe_constexpr_destructor (tree);
>   static bool field_poverlapping_p (tree);
>   
>   /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
> @@ -5242,7 +5243,7 @@ type_has_constexpr_default_constructor (
>      without forcing a lazy declaration (which might cause undesired
>      instantiations).  */
>   
> -bool
> +static bool
>   type_maybe_constexpr_default_constructor (tree t)
>   {
>     if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t)
> @@ -5252,6 +5253,34 @@ type_maybe_constexpr_default_constructor
>     return type_has_constexpr_default_constructor (t);
>   }
>   
> +/* Returns true iff class T has a constexpr destructor.  */
> +
> +bool
> +type_has_constexpr_destructor (tree t)
> +{
> +  tree fns;
> +
> +  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
> +    /* Non-trivial, we need to check subobject destructors.  */
> +    lazily_declare_fn (sfk_destructor, t);
> +  fns = CLASSTYPE_DESTRUCTOR (t);
> +  return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
> +}
> +
> +/* Returns true iff class T has a constexpr destructor or has an
> +   implicitly declared destructor that we can't tell if it's constexpr
> +   without forcing a lazy declaration (which might cause undesired
> +   instantiations).  */
> +
> +static bool
> +type_maybe_constexpr_destructor (tree t)
> +{
> +  if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
> +    /* Assume it's constexpr.  */
> +    return true;
> +  return type_has_constexpr_destructor (t);
> +}
> +
>   /* Returns true iff class TYPE has a virtual destructor.  */
>   
>   bool
> @@ -5503,8 +5532,11 @@ finalize_literal_type_property (tree t)
>   {
>     tree fn;
>   
> -  if (cxx_dialect < cxx11
> -      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
> +  if (cxx_dialect < cxx11)
> +    CLASSTYPE_LITERAL_P (t) = false;
> +  else if (CLASSTYPE_LITERAL_P (t)
> +	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
> +	   && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t)))
>       CLASSTYPE_LITERAL_P (t) = false;
>     else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t))
>       CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17);
> @@ -5558,8 +5590,12 @@ explain_non_literal_class (tree t)
>       inform (UNKNOWN_LOCATION,
>   	    "  %qT is a closure type, which is only literal in "
>   	    "C++17 and later", t);
> -  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
> +  else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
>       inform (UNKNOWN_LOCATION, "  %q+T has a non-trivial destructor", t);
> +  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
> +	   && !type_maybe_constexpr_destructor (t))
> +    inform (UNKNOWN_LOCATION, "  %q+T does not have %<constexpr%> destructor",
> +	    t);
>     else if (CLASSTYPE_NON_AGGREGATE (t)
>   	   && !TYPE_HAS_TRIVIAL_DFLT (t)
>   	   && !LAMBDA_TYPE_P (t)
> --- gcc/cp/constexpr.c.jj	2019-10-04 08:54:52.902007011 +0200
> +++ gcc/cp/constexpr.c	2019-10-04 19:17:43.984309712 +0200
> @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
>   #include "gimple-fold.h"
>   #include "timevar.h"
>   #include "fold-const-call.h"
> +#include "stor-layout.h"
>   
>   static bool verify_constant (tree, bool, bool *, bool *);
>   #define VERIFY_CONSTANT(X)						\
> @@ -1006,17 +1007,35 @@ enum constexpr_switch_state {
>     css_default_processing
>   };
>   
> +/* The constexpr expansion context part which needs one instance per
> +   cxx_eval_outermost_constant_expr invocation.  VALUES is a map of values of
> +   variables initialized within the expression.  */
> +
> +struct constexpr_global_ctx {
> +  /* Values for any temporaries or local variables within the
> +     constant-expression. */
> +  hash_map<tree,tree> values;
> +  /* Number of cxx_eval_constant_expression calls (except skipped ones,
> +     on simple constants or location wrappers) encountered during current
> +     cxx_eval_outermost_constant_expr call.  */
> +  HOST_WIDE_INT constexpr_ops_count;
> +  /* Heap VAR_DECLs created during the evaluation of the outermost constant
> +     expression.  */
> +  auto_vec<tree, 16> heap_vars;
> +  /* Constructor.  */
> +  constexpr_global_ctx () : constexpr_ops_count (0) {}
> +};
> +
>   /* The constexpr expansion context.  CALL is the current function
>      expansion, CTOR is the current aggregate initializer, OBJECT is the
> -   object being initialized by CTOR, either a VAR_DECL or a _REF.  VALUES
> -   is a map of values of variables initialized within the expression.  */
> +   object being initialized by CTOR, either a VAR_DECL or a _REF.    */
>   
>   struct constexpr_ctx {
> +  /* The part of the context that needs to be unique to the whole
> +     cxx_eval_outermost_constant_expr invocation.  */
> +  constexpr_global_ctx *global;
>     /* The innermost call we're evaluating.  */
>     constexpr_call *call;
> -  /* Values for any temporaries or local variables within the
> -     constant-expression. */
> -  hash_map<tree,tree> *values;
>     /* SAVE_EXPRs that we've seen within the current LOOP_EXPR.  NULL if we
>        aren't inside a loop.  */
>     vec<tree> *save_exprs;
> @@ -1027,10 +1046,6 @@ struct constexpr_ctx {
>     tree object;
>     /* If inside SWITCH_EXPR.  */
>     constexpr_switch_state *css_state;
> -  /* Number of cxx_eval_constant_expression calls (except skipped ones,
> -     on simple constants or location wrappers) encountered during current
> -     cxx_eval_outermost_constant_expr call.  */
> -  HOST_WIDE_INT *constexpr_ops_count;
>   
>     /* Whether we should error on a non-constant expression or fail quietly.  */
>     bool quiet;
> @@ -1656,6 +1671,64 @@ 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)

I'd still like to factor this test out.  OK with that change.

Thanks,
Jason
Jakub Jelinek Oct. 5, 2019, 7:39 a.m. UTC | #8
On Fri, Oct 04, 2019 at 03:34:39PM -0400, Jason Merrill wrote:
> > @@ -1656,6 +1671,64 @@ 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)
> 
> I'd still like to factor this test out.  OK with that change.

Ok, thanks, here is what I've committed after another bootstrap/regtest on
x86_64-linux and i686-linux:

2019-10-05  Jakub Jelinek  <jakub@redhat.com>

	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}.
	<case NOP_EXPR>: 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 fix.
	* typeck2.c (store_init_value): Don't call cp_fully_fold_init on
	initializers of automatic non-constexpr variables in constexpr
	functions.
testsuite/
	* g++.dg/cpp0x/constexpr-delete2.C: Adjust expected diagnostics for
	c++2a.
	* g++.dg/cpp0x/locations1.C: Only expect constexpr ~S() diagnostics
	in c++17_down, adjust expected wording.
	* g++.dg/cpp1y/constexpr-new.C: Only expect diagnostics in c++17_down.
	* g++.dg/cpp2a/constexpr-dtor1.C: New test.
	* g++.dg/cpp2a/constexpr-dtor2.C: New test.
	* g++.dg/cpp2a/constexpr-dtor3.C: New test.
	* g++.dg/cpp2a/constexpr-new1.C: New test.
	* g++.dg/cpp2a/constexpr-new2.C: New test.
	* g++.dg/cpp2a/constexpr-new3.C: New test.
	* g++.dg/cpp2a/constexpr-new4.C: New test.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_constinit and
	__cpp_constexpr_dynamic_alloc tests.  Tweak __cpp_* tests for c++2a
	features to use style like older features, including #ifdef test.
	* g++.dg/ext/is_literal_type3.C: New test.

--- gcc/c-family/c-cppbuiltin.c.jj	2019-10-03 17:55:28.657054188 +0200
+++ gcc/c-family/c-cppbuiltin.c	2019-10-04 12:49:21.610028880 +0200
@@ -989,6 +989,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constinit=201907");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
+	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
 	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
--- gcc/cp/cp-tree.h.jj	2019-10-03 17:55:28.526056167 +0200
+++ gcc/cp/cp-tree.h	2019-10-04 18:51:24.935599025 +0200
@@ -172,6 +172,9 @@ enum cp_tree_index
     CPTI_VALUE_IDENTIFIER,
     CPTI_FUN_IDENTIFIER,
     CPTI_CLOSURE_IDENTIFIER,
+    CPTI_HEAP_UNINIT_IDENTIFIER,
+    CPTI_HEAP_IDENTIFIER,
+    CPTI_HEAP_DELETED_IDENTIFIER,
 
     CPTI_LANG_NAME_C,
     CPTI_LANG_NAME_CPLUSPLUS,
@@ -310,6 +313,9 @@ extern GTY(()) tree cp_global_trees[CPTI
 #define value_identifier		cp_global_trees[CPTI_VALUE_IDENTIFIER]
 #define fun_identifier			cp_global_trees[CPTI_FUN_IDENTIFIER]
 #define closure_identifier		cp_global_trees[CPTI_CLOSURE_IDENTIFIER]
+#define heap_uninit_identifier		cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER]
+#define heap_identifier			cp_global_trees[CPTI_HEAP_IDENTIFIER]
+#define heap_deleted_identifier		cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER]
 #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
 #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
 
@@ -6342,6 +6348,7 @@ extern bool vbase_has_user_provided_move
 extern tree default_init_uninitialized_part (tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
 extern bool type_has_constexpr_default_constructor (tree);
+extern bool type_has_constexpr_destructor	(tree);
 extern bool type_has_virtual_destructor		(tree);
 extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
 extern bool classtype_has_non_deleted_move_ctor (tree);
@@ -6648,6 +6655,7 @@ extern tree build_offset_ref			(tree, tr
 extern tree throw_bad_array_new_length		(void);
 extern bool type_has_new_extended_alignment	(tree);
 extern unsigned malloc_alignment		(void);
+extern tree build_new_constexpr_heap_type	(tree, tree, tree);
 extern tree build_new				(vec<tree, va_gc> **, tree, tree,
 						 vec<tree, va_gc> **, int,
                                                  tsubst_flags_t);
@@ -7747,6 +7755,7 @@ extern bool require_constant_expression
 extern bool require_rvalue_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
+extern void cxx_constant_dtor			(tree, tree);
 extern tree cxx_constant_init			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE, bool = false);
 extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
--- gcc/cp/class.c.jj	2019-10-03 17:55:28.554055744 +0200
+++ gcc/cp/class.c	2019-10-04 12:49:21.336033076 +0200
@@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_
 static tree end_of_base (tree);
 static tree get_vcall_index (tree, tree);
 static bool type_maybe_constexpr_default_constructor (tree);
+static bool type_maybe_constexpr_destructor (tree);
 static bool field_poverlapping_p (tree);
 
 /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
@@ -5242,7 +5243,7 @@ type_has_constexpr_default_constructor (
    without forcing a lazy declaration (which might cause undesired
    instantiations).  */
 
-bool
+static bool
 type_maybe_constexpr_default_constructor (tree t)
 {
   if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t)
@@ -5252,6 +5253,34 @@ type_maybe_constexpr_default_constructor
   return type_has_constexpr_default_constructor (t);
 }
 
+/* Returns true iff class T has a constexpr destructor.  */
+
+bool
+type_has_constexpr_destructor (tree t)
+{
+  tree fns;
+
+  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Non-trivial, we need to check subobject destructors.  */
+    lazily_declare_fn (sfk_destructor, t);
+  fns = CLASSTYPE_DESTRUCTOR (t);
+  return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
+}
+
+/* Returns true iff class T has a constexpr destructor or has an
+   implicitly declared destructor that we can't tell if it's constexpr
+   without forcing a lazy declaration (which might cause undesired
+   instantiations).  */
+
+static bool
+type_maybe_constexpr_destructor (tree t)
+{
+  if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Assume it's constexpr.  */
+    return true;
+  return type_has_constexpr_destructor (t);
+}
+
 /* Returns true iff class TYPE has a virtual destructor.  */
 
 bool
@@ -5503,8 +5532,11 @@ finalize_literal_type_property (tree t)
 {
   tree fn;
 
-  if (cxx_dialect < cxx11
-      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  if (cxx_dialect < cxx11)
+    CLASSTYPE_LITERAL_P (t) = false;
+  else if (CLASSTYPE_LITERAL_P (t)
+	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t)))
     CLASSTYPE_LITERAL_P (t) = false;
   else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t))
     CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17);
@@ -5558,8 +5590,12 @@ explain_non_literal_class (tree t)
     inform (UNKNOWN_LOCATION,
 	    "  %qT is a closure type, which is only literal in "
 	    "C++17 and later", t);
-  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
     inform (UNKNOWN_LOCATION, "  %q+T has a non-trivial destructor", t);
+  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && !type_maybe_constexpr_destructor (t))
+    inform (UNKNOWN_LOCATION, "  %q+T does not have %<constexpr%> destructor",
+	    t);
   else if (CLASSTYPE_NON_AGGREGATE (t)
 	   && !TYPE_HAS_TRIVIAL_DFLT (t)
 	   && !LAMBDA_TYPE_P (t)
--- gcc/cp/constexpr.c.jj	2019-10-04 08:54:52.902007011 +0200
+++ gcc/cp/constexpr.c	2019-10-04 21:45:42.751663070 +0200
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
 #include "gimple-fold.h"
 #include "timevar.h"
 #include "fold-const-call.h"
+#include "stor-layout.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -1006,17 +1007,35 @@ enum constexpr_switch_state {
   css_default_processing
 };
 
+/* The constexpr expansion context part which needs one instance per
+   cxx_eval_outermost_constant_expr invocation.  VALUES is a map of values of
+   variables initialized within the expression.  */
+
+struct constexpr_global_ctx {
+  /* Values for any temporaries or local variables within the
+     constant-expression. */
+  hash_map<tree,tree> values;
+  /* Number of cxx_eval_constant_expression calls (except skipped ones,
+     on simple constants or location wrappers) encountered during current
+     cxx_eval_outermost_constant_expr call.  */
+  HOST_WIDE_INT constexpr_ops_count;
+  /* Heap VAR_DECLs created during the evaluation of the outermost constant
+     expression.  */
+  auto_vec<tree, 16> heap_vars;
+  /* Constructor.  */
+  constexpr_global_ctx () : constexpr_ops_count (0) {}
+};
+
 /* The constexpr expansion context.  CALL is the current function
    expansion, CTOR is the current aggregate initializer, OBJECT is the
-   object being initialized by CTOR, either a VAR_DECL or a _REF.  VALUES
-   is a map of values of variables initialized within the expression.  */
+   object being initialized by CTOR, either a VAR_DECL or a _REF.    */
 
 struct constexpr_ctx {
+  /* The part of the context that needs to be unique to the whole
+     cxx_eval_outermost_constant_expr invocation.  */
+  constexpr_global_ctx *global;
   /* The innermost call we're evaluating.  */
   constexpr_call *call;
-  /* Values for any temporaries or local variables within the
-     constant-expression. */
-  hash_map<tree,tree> *values;
   /* SAVE_EXPRs that we've seen within the current LOOP_EXPR.  NULL if we
      aren't inside a loop.  */
   vec<tree> *save_exprs;
@@ -1027,10 +1046,6 @@ struct constexpr_ctx {
   tree object;
   /* If inside SWITCH_EXPR.  */
   constexpr_switch_state *css_state;
-  /* Number of cxx_eval_constant_expression calls (except skipped ones,
-     on simple constants or location wrappers) encountered during current
-     cxx_eval_outermost_constant_expr call.  */
-  HOST_WIDE_INT *constexpr_ops_count;
 
   /* Whether we should error on a non-constant expression or fail quietly.  */
   bool quiet;
@@ -1578,6 +1593,17 @@ modifying_const_object_error (tree expr,
   inform (location_of (obj), "originally declared %<const%> here");
 }
 
+/* Return true if FNDECL is a replaceable global allocation function that
+   should be useable during constant expression evaluation.  */
+
+static inline bool
+cxx_replaceable_global_alloc_fn (tree fndecl)
+{
+  return (cxx_dialect >= cxx2a
+	  && IDENTIFIER_NEWDEL_OP_P (DECL_NAME (fndecl))
+	  && CP_DECL_CONTEXT (fndecl) == global_namespace);
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1656,6 +1682,62 @@ cxx_eval_call_expression (const constexp
 					   lval, non_constant_p, overflow_p);
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
+      if (cxx_replaceable_global_alloc_fn (fun))
+	{
+	  const int nargs = call_expr_nargs (t);
+	  tree arg0 = 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)
+		arg0 = arg;
+	    }
+	  gcc_assert (arg0);
+	  if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
+	    {
+	      tree type = build_array_type_nelts (char_type_node,
+						  tree_to_uhwi (arg0));
+	      tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier,
+				     type);
+	      DECL_ARTIFICIAL (var) = 1;
+	      TREE_STATIC (var) = 1;
+	      ctx->global->heap_vars.safe_push (var);
+	      ctx->global->values.put (var, NULL_TREE);
+	      return fold_convert (ptr_type_node, build_address (var));
+	    }
+	  else
+	    {
+	      STRIP_NOPS (arg0);
+	      if (TREE_CODE (arg0) == ADDR_EXPR
+		  && VAR_P (TREE_OPERAND (arg0, 0)))
+		{
+		  tree var = TREE_OPERAND (arg0, 0);
+		  if (DECL_NAME (var) == heap_uninit_identifier
+		      || DECL_NAME (var) == heap_identifier)
+		    {
+		      DECL_NAME (var) = heap_deleted_identifier;
+		      ctx->global->values.remove (var);
+		      return void_node;
+		    }
+		  else if (DECL_NAME (var) == heap_deleted_identifier)
+		    {
+		      if (!ctx->quiet)
+			error_at (loc, "deallocation of already deallocated "
+				       "storage");
+		      *non_constant_p = true;
+		      return t;
+		    }
+		}
+	      if (!ctx->quiet)
+		error_at (loc, "deallocation of storage that was "
+			       "not previously allocated");
+	      *non_constant_p = true;
+	      return t;
+	    }
+	}
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -1675,7 +1757,7 @@ cxx_eval_call_expression (const constexp
       new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
       tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
       CONSTRUCTOR_NO_CLEARING (ctor) = true;
-      ctx->values->put (new_ctx.object, ctor);
+      ctx->global->values.put (new_ctx.object, ctor);
       ctx = &new_ctx;
     }
 
@@ -1877,7 +1959,7 @@ cxx_eval_call_expression (const constexp
 	      arg = unshare_constructor (arg);
 	      if (TREE_CODE (arg) == CONSTRUCTOR)
 		vec_safe_push (ctors, arg);
-	      ctx->values->put (remapped, arg);
+	      ctx->global->values.put (remapped, arg);
 	      remapped = DECL_CHAIN (remapped);
 	    }
 	  /* Add the RESULT_DECL to the values map, too.  */
@@ -1887,11 +1969,11 @@ cxx_eval_call_expression (const constexp
 	      slot = AGGR_INIT_EXPR_SLOT (t);
 	      tree addr = build_address (slot);
 	      addr = build_nop (TREE_TYPE (res), addr);
-	      ctx->values->put (res, addr);
-	      ctx->values->put (slot, NULL_TREE);
+	      ctx->global->values.put (res, addr);
+	      ctx->global->values.put (slot, NULL_TREE);
 	    }
 	  else
-	    ctx->values->put (res, NULL_TREE);
+	    ctx->global->values.put (res, NULL_TREE);
 
 	  /* Track the callee's evaluated SAVE_EXPRs so that we can forget
 	     their values after the call.  */
@@ -1916,7 +1998,7 @@ cxx_eval_call_expression (const constexp
 	    result = void_node;
 	  else
 	    {
-	      result = *ctx->values->get (slot ? slot : res);
+	      result = *ctx->global->values.get (slot ? slot : res);
 	      if (result == NULL_TREE && !*non_constant_p)
 		{
 		  if (!ctx->quiet)
@@ -1934,8 +2016,8 @@ cxx_eval_call_expression (const constexp
 	      && CLASS_TYPE_P (TREE_TYPE (new_obj))
 	      && CP_TYPE_CONST_P (TREE_TYPE (new_obj)))
 	    {
-	      /* Subobjects might not be stored in ctx->values but we can
-		 get its CONSTRUCTOR by evaluating *this.  */
+	      /* Subobjects might not be stored in ctx->global->values but we
+		 can get its CONSTRUCTOR by evaluating *this.  */
 	      tree e = cxx_eval_constant_expression (ctx, new_obj,
 						     /*lval*/false,
 						     non_constant_p,
@@ -1947,17 +2029,17 @@ cxx_eval_call_expression (const constexp
 	  unsigned int i;
 	  tree save_expr;
 	  FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-	    ctx_with_save_exprs.values->remove (save_expr);
+	    ctx->global->values.remove (save_expr);
 
 	  /* Remove the parms/result from the values map.  Is it worth
 	     bothering to do this when the map itself is only live for
 	     one constexpr evaluation?  If so, maybe also clear out
 	     other vars from call, maybe in BIND_EXPR handling?  */
-	  ctx->values->remove (res);
+	  ctx->global->values.remove (res);
 	  if (slot)
-	    ctx->values->remove (slot);
+	    ctx->global->values.remove (slot);
 	  for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
-	    ctx->values->remove (parm);
+	    ctx->global->values.remove (parm);
 
 	  /* Free any parameter CONSTRUCTORs we aren't returning directly.  */
 	  while (!ctors->is_empty ())
@@ -3077,7 +3159,7 @@ verify_ctor_sanity (const constexpr_ctx
 			  (TREE_TYPE (type), TREE_TYPE (otype)))));
     }
   gcc_assert (!ctx->object || !DECL_P (ctx->object)
-	      || *(ctx->values->get (ctx->object)) == ctx->ctor);
+	      || *(ctx->global->values.get (ctx->object)) == ctx->ctor);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -3610,7 +3692,23 @@ cxx_eval_indirect_ref (const constexpr_c
 static void
 non_const_var_error (tree r)
 {
+  auto_diagnostic_group d;
   tree type = TREE_TYPE (r);
+  if (DECL_NAME (r) == heap_uninit_identifier
+      || DECL_NAME (r) == heap_identifier)
+    {
+      error ("the content of uninitialized storage is not usable "
+	     "in a constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
+  if (DECL_NAME (r) == heap_deleted_identifier)
+    {
+      error ("use of allocated storage after deallocation in a "
+	     "constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
   error ("the value of %qD is not usable in a constant "
 	 "expression", r);
   /* Avoid error cascade.  */
@@ -3854,7 +3952,7 @@ cxx_eval_store_expression (const constex
        DECL_NAME to handle TARGET_EXPR temporaries, which are fair game.  */
     valp = NULL;
   else if (DECL_P (object))
-    valp = ctx->values->get (object);
+    valp = ctx->global->values.get (object);
   else
     valp = NULL;
   if (!valp)
@@ -4056,7 +4154,7 @@ cxx_eval_store_expression (const constex
 					   non_constant_p, overflow_p);
       if (ctors->is_empty())
 	/* The hash table might have moved since the get earlier.  */
-	valp = ctx->values->get (object);
+	valp = ctx->global->values.get (object);
     }
 
   /* Don't share a CONSTRUCTOR that might be changed later.  */
@@ -4332,6 +4430,12 @@ cxx_eval_loop_expr (const constexpr_ctx
 		    tree *jump_target)
 {
   constexpr_ctx new_ctx = *ctx;
+  tree local_target;
+  if (!jump_target)
+    {
+      local_target = NULL_TREE;
+      jump_target = &local_target;
+    }
 
   tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
@@ -4410,7 +4514,7 @@ cxx_eval_loop_expr (const constexpr_ctx
       unsigned int i;
       tree save_expr;
       FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-	new_ctx.values->remove (save_expr);
+	ctx->global->values.remove (save_expr);
       save_exprs.truncate (0);
 
       if (++count >= constexpr_loop_limit)
@@ -4434,7 +4538,7 @@ cxx_eval_loop_expr (const constexpr_ctx
   unsigned int i;
   tree save_expr;
   FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
-    new_ctx.values->remove (save_expr);
+    ctx->global->values.remove (save_expr);
 
   return NULL_TREE;
 }
@@ -4586,14 +4690,14 @@ cxx_eval_constant_expression (const cons
     }
 
   /* Avoid excessively long constexpr evaluations.  */
-  if (++*ctx->constexpr_ops_count >= constexpr_ops_limit)
+  if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit)
     {
       if (!ctx->quiet)
 	error_at (cp_expr_loc_or_input_loc (t),
 		  "%<constexpr%> evaluation operation count exceeds limit of "
 		  "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)",
 		  constexpr_ops_limit);
-      *ctx->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT);
+      ctx->global->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT);
       *non_constant_p = true;
       return t;
     }
@@ -4610,7 +4714,7 @@ cxx_eval_constant_expression (const cons
       /* We ask for an rvalue for the RESULT_DECL when indirecting
 	 through an invisible reference, or in named return value
 	 optimization.  */
-      if (tree *p = ctx->values->get (t))
+      if (tree *p = ctx->global->values.get (t))
 	return *p;
       else
 	{
@@ -4666,7 +4770,7 @@ cxx_eval_constant_expression (const cons
 	  && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
 	r = TARGET_EXPR_INITIAL (r);
       if (VAR_P (r))
-	if (tree *p = ctx->values->get (r))
+	if (tree *p = ctx->global->values.get (r))
 	  if (*p != NULL_TREE)
 	    r = *p;
       if (DECL_P (r))
@@ -4693,7 +4797,7 @@ cxx_eval_constant_expression (const cons
     case PARM_DECL:
       if (lval && !TYPE_REF_P (TREE_TYPE (t)))
 	/* glvalue use.  */;
-      else if (tree *p = ctx->values->get (r))
+      else if (tree *p = ctx->global->values.get (r))
 	r = *p;
       else if (lval)
 	/* Defer in case this is only used for its type.  */;
@@ -4735,7 +4839,7 @@ cxx_eval_constant_expression (const cons
 	    new_ctx.object = r;
 	    new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
 	    CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
-	    new_ctx.values->put (r, new_ctx.ctor);
+	    ctx->global->values.put (r, new_ctx.ctor);
 	    ctx = &new_ctx;
 	  }
 
@@ -4751,12 +4855,12 @@ cxx_eval_constant_expression (const cons
 	    if (CLASS_TYPE_P (TREE_TYPE (r))
 		&& CP_TYPE_CONST_P (TREE_TYPE (r)))
 	      TREE_READONLY (init) = true;
-	    ctx->values->put (r, init);
+	    ctx->global->values.put (r, init);
 	  }
 	else if (ctx == &new_ctx)
 	  /* We gave it a CONSTRUCTOR above.  */;
 	else
-	  ctx->values->put (r, NULL_TREE);
+	  ctx->global->values.put (r, NULL_TREE);
       }
       break;
 
@@ -4782,7 +4886,7 @@ cxx_eval_constant_expression (const cons
 	  new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL);
 	  CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
 	  new_ctx.object = TARGET_EXPR_SLOT (t);
-	  ctx->values->put (new_ctx.object, new_ctx.ctor);
+	  ctx->global->values.put (new_ctx.object, new_ctx.ctor);
 	  ctx = &new_ctx;
 	}
       /* Pass false for 'lval' because this indicates
@@ -4797,7 +4901,7 @@ cxx_eval_constant_expression (const cons
 	{
 	  tree slot = TARGET_EXPR_SLOT (t);
 	  r = unshare_constructor (r);
-	  ctx->values->put (slot, r);
+	  ctx->global->values.put (slot, r);
 	  return slot;
 	}
       break;
@@ -4837,13 +4941,13 @@ cxx_eval_constant_expression (const cons
 
     case SAVE_EXPR:
       /* Avoid evaluating a SAVE_EXPR more than once.  */
-      if (tree *p = ctx->values->get (t))
+      if (tree *p = ctx->global->values.get (t))
 	r = *p;
       else
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
 					    non_constant_p, overflow_p);
-	  ctx->values->put (t, r);
+	  ctx->global->values.put (t, r);
 	  if (ctx->save_exprs)
 	    ctx->save_exprs->safe_push (t);
 	}
@@ -5177,8 +5281,7 @@ cxx_eval_constant_expression (const cons
 	if (VOID_TYPE_P (type))
 	  return void_node;
 
-	if (TREE_CODE (op) == PTRMEM_CST
-	    && !TYPE_PTRMEM_P (type))
+	if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
 
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
@@ -5232,6 +5335,34 @@ cxx_eval_constant_expression (const cons
 	      }
 	  }
 
+	if (INDIRECT_TYPE_P (type)
+	    && TREE_CODE (op) == NOP_EXPR
+	    && TREE_TYPE (op) == ptr_type_node
+	    && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR
+	    && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
+	    && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0),
+					0)) == heap_uninit_identifier)
+	  {
+	    tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0);
+	    tree var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	    tree elt_type = TREE_TYPE (type);
+	    tree cookie_size = NULL_TREE;
+	    if (TREE_CODE (elt_type) == RECORD_TYPE
+		&& TYPE_NAME (elt_type) == heap_identifier)
+	      {
+		tree fld1 = TYPE_FIELDS (elt_type);
+		tree fld2 = DECL_CHAIN (fld1);
+		elt_type = TREE_TYPE (TREE_TYPE (fld2));
+		cookie_size = TYPE_SIZE_UNIT (TREE_TYPE (fld1));
+	      }
+	    DECL_NAME (var) = heap_identifier;
+	    TREE_TYPE (var)
+	      = build_new_constexpr_heap_type (elt_type, cookie_size,
+					       var_size);
+	    TREE_TYPE (TREE_OPERAND (op, 0))
+	      = build_pointer_type (TREE_TYPE (var));
+	  }
+
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
@@ -5473,6 +5604,7 @@ instantiate_cx_fn_r (tree *tp, int *walk
 
   return NULL_TREE;
 }
+
 static void
 instantiate_constexpr_fns (tree t)
 {
@@ -5481,34 +5613,58 @@ instantiate_constexpr_fns (tree t)
   input_location = loc;
 }
 
+/* Look for heap variables in the expression *TP.  */
+
+static tree
+find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
+{
+  if (VAR_P (*tp)
+      && (DECL_NAME (*tp) == heap_uninit_identifier
+	  || DECL_NAME (*tp) == heap_identifier
+	  || DECL_NAME (*tp) == heap_deleted_identifier))
+    return *tp;
+
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+  return NULL_TREE;
+}
+
 /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
    STRICT has the same sense as for constant_value_1: true if we only allow
    conforming C++ constant expressions, or false if we want a constant value
    even if it doesn't conform.
    MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
-   per P0595 even when ALLOW_NON_CONSTANT is true.  */
+   per P0595 even when ALLOW_NON_CONSTANT is true.
+   CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable.
+   OBJECT must be non-NULL in that case.  */
 
 static tree
 cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 				  bool strict = true,
 				  bool manifestly_const_eval = false,
+				  bool constexpr_dtor = false,
 				  tree object = NULL_TREE)
 {
   auto_timevar time (TV_CONSTEXPR);
 
   bool non_constant_p = false;
   bool overflow_p = false;
-  hash_map<tree,tree> map;
-  HOST_WIDE_INT constexpr_ctx_count = 0;
 
-  constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
-			&constexpr_ctx_count, allow_non_constant, strict,
+  constexpr_global_ctx global_ctx;
+  constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL,
+			allow_non_constant, strict,
 			manifestly_const_eval || !allow_non_constant };
 
   tree type = initialized_type (t);
   tree r = t;
   if (VOID_TYPE_P (type))
-    return t;
+    {
+      if (constexpr_dtor)
+	/* Used for destructors of array elements.  */
+	type = TREE_TYPE (object);
+      else
+	return t;
+    }
   if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
     {
       /* In C++14 an NSDMI can participate in aggregate initialization,
@@ -5518,8 +5674,22 @@ cxx_eval_outermost_constant_expr (tree t
 	 update ctx.values for the VAR_DECL.  We use the same strategy
 	 for C++11 constexpr constructors that refer to the object being
 	 initialized.  */
-      ctx.ctor = build_constructor (type, NULL);
-      CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+      if (constexpr_dtor)
+	{
+	  gcc_assert (object && VAR_P (object));
+	  gcc_assert (DECL_DECLARED_CONSTEXPR_P (object));
+	  gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object));
+	  ctx.ctor = unshare_expr (DECL_INITIAL (object));
+	  TREE_READONLY (ctx.ctor) = false;
+	  /* Temporarily force decl_really_constant_value to return false
+	     for it, we want to use ctx.ctor for the current value instead.  */
+	  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false;
+	}
+      else
+	{
+	  ctx.ctor = build_constructor (type, NULL);
+	  CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+	}
       if (!object)
 	{
 	  if (TREE_CODE (t) == TARGET_EXPR)
@@ -5532,7 +5702,7 @@ cxx_eval_outermost_constant_expr (tree t
 	gcc_assert (same_type_ignoring_top_level_qualifiers_p
 		    (type, TREE_TYPE (object)));
       if (object && DECL_P (object))
-	map.put (object, ctx.ctor);
+	global_ctx.values.put (object, ctx.ctor);
       if (TREE_CODE (r) == TARGET_EXPR)
 	/* Avoid creating another CONSTRUCTOR when we expand the
 	   TARGET_EXPR.  */
@@ -5543,13 +5713,15 @@ cxx_eval_outermost_constant_expr (tree t
   r = cxx_eval_constant_expression (&ctx, r,
 				    false, &non_constant_p, &overflow_p);
 
-  verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  if (!constexpr_dtor)
+    verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  else
+    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
 
   /* Mutable logic is a bit tricky: we want to allow initialization of
      constexpr variables with mutable members, but we can't copy those
      members to another constexpr variable.  */
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_MUTABLE_POISON (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5557,8 +5729,7 @@ cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_NO_CLEARING (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5567,6 +5738,32 @@ cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
+  if (!global_ctx.heap_vars.is_empty ())
+    {
+      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
+						       NULL);
+      unsigned int i;
+      if (heap_var)
+	{
+	  if (!allow_non_constant && !non_constant_p)
+	    error_at (DECL_SOURCE_LOCATION (heap_var),
+		      "%qE is not a constant expression because it refers to "
+		      "a result of %<operator new%>", t);
+	  r = t;
+	  non_constant_p = true;
+	}
+      FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var)
+	if (DECL_NAME (heap_var) != heap_deleted_identifier)
+	  {
+	    if (!allow_non_constant && !non_constant_p)
+	      error_at (DECL_SOURCE_LOCATION (heap_var),
+			"%qE is not a constant expression because allocated "
+			"storage has not been deallocated", t);
+	    r = t;
+	    non_constant_p = true;
+	  }
+    }
+
   /* Technically we should check this for all subexpressions, but that
      runs into problems with our internal representation of pointer
      subtraction and the 5.19 rules are still in flux.  */
@@ -5592,6 +5789,8 @@ cxx_eval_outermost_constant_expr (tree t
 
   if (non_constant_p && !allow_non_constant)
     return error_mark_node;
+  else if (constexpr_dtor)
+    return r;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
       /* If __builtin_is_constant_evaluated () was evaluated to true
@@ -5599,7 +5798,7 @@ cxx_eval_outermost_constant_expr (tree t
 	 punt.  */
       if (manifestly_const_eval)
 	return cxx_eval_outermost_constant_expr (t, true, strict,
-						 false, object);
+						 false, false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
 	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
 	 it to be set if it is invariant address, even when it is not
@@ -5627,7 +5826,7 @@ cxx_eval_outermost_constant_expr (tree t
 	return t;
       else if (TREE_CODE (t) != CONSTRUCTOR)
 	{
-	  r = get_target_expr (r);
+	  r = get_target_expr_sfinae (r, tf_warning_or_error | tf_no_cleanup);
 	  TREE_CONSTANT (r) = true;
 	}
     }
@@ -5642,7 +5841,16 @@ cxx_eval_outermost_constant_expr (tree t
 tree
 cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
+  return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl);
+}
+
+/* Like cxx_constant_value, but used for evaluation of constexpr destructors
+   of constexpr variables.  The actual initializer of DECL is not modified.  */
+
+void
+cxx_constant_dtor (tree t, tree decl)
+{
+  cxx_eval_outermost_constant_expr (t, false, true, true, true, decl);
 }
 
 /* Helper routine for fold_simple function.  Either return simplified
@@ -5746,14 +5954,14 @@ maybe_constant_value (tree t, tree decl,
     return t;
 
   if (manifestly_const_eval)
-    return cxx_eval_outermost_constant_expr (t, true, true, true, decl);
+    return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl);
 
   if (cv_cache == NULL)
     cv_cache = hash_map<tree, tree>::create_ggc (101);
   if (tree *cached = cv_cache->get (t))
     return *cached;
 
-  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
+  r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl);
   gcc_checking_assert (r == t
 		       || CONVERT_EXPR_P (t)
 		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
@@ -5815,7 +6023,7 @@ fold_non_dependent_expr_template (tree t
 
       tree r = cxx_eval_outermost_constant_expr (t, true, true,
 						 manifestly_const_eval,
-						 NULL_TREE);
+						 false, NULL_TREE);
       /* cp_tree_equal looks through NOPs, so allow them.  */
       gcc_checking_assert (r == t
 			   || CONVERT_EXPR_P (t)
@@ -5919,7 +6127,7 @@ maybe_constant_init_1 (tree t, tree decl
   else
     t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
 					  /*strict*/false,
-					  manifestly_const_eval, decl);
+					  manifestly_const_eval, false, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -6213,7 +6421,10 @@ 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_replaceable_global_alloc_fn (fun))
 		  {
 		    if (flags & tf_error)
 		      {
@@ -6442,6 +6653,9 @@ potential_constant_expression_1 (tree t,
 	goto fail;
       if (!RECUR (TREE_OPERAND (t, 0), any))
 	return false;
+      /* Just ignore clobbers.  */
+      if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
+	return true;
       if (!RECUR (TREE_OPERAND (t, 1), rval))
 	return false;
       return true;
@@ -6911,7 +7125,7 @@ potential_constant_expression_1 (tree t,
      return true;
 
     case COND_EXPR:
-      if (COND_EXPR_IS_VEC_DELETE (t))
+      if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx2a)
 	{
 	  if (flags & tf_error)
 	    error_at (loc, "%<delete[]%> is not a constant expression");
--- gcc/cp/decl.c.jj	2019-10-03 17:55:28.394058162 +0200
+++ gcc/cp/decl.c	2019-10-05 00:54:35.188994360 +0200
@@ -4146,6 +4146,9 @@ initialize_predefined_identifiers (void)
     {"value", &value_identifier, cik_normal},
     {"_FUN", &fun_identifier, cik_normal},
     {"__closure", &closure_identifier, cik_normal},
+    {"heap uninit", &heap_uninit_identifier, cik_normal},
+    {"heap ", &heap_identifier, cik_normal},
+    {"heap deleted", &heap_deleted_identifier, cik_normal},
     {NULL, NULL, cik_normal}
   };
 
@@ -7006,6 +7009,19 @@ notice_forced_label_r (tree *tp, int *wa
   return NULL_TREE;
 }
 
+/* Return true if DECL has either a trivial destructor, or for C++2A
+   is constexpr and has a constexpr destructor.  */
+
+static bool
+decl_maybe_constant_destruction (tree decl, tree type)
+{
+  return (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)
+	  || (cxx_dialect >= cxx2a
+	      && VAR_P (decl)
+	      && DECL_DECLARED_CONSTEXPR_P (decl)
+	      && type_has_constexpr_destructor (strip_array_types (type))));
+}
+
 /* Finish processing of a declaration;
    install its line number and initial value.
    If the length of an array type is not known before,
@@ -7430,7 +7446,7 @@ cp_finish_decl (tree decl, tree init, bo
 	    TREE_READONLY (decl) = 1;
 
 	  /* Likewise if it needs destruction.  */
-	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+	  if (!decl_maybe_constant_destruction (decl, type))
 	    TREE_READONLY (decl) = 0;
 	}
 
@@ -8312,6 +8328,13 @@ register_dtor_fn (tree decl)
   if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
     return void_node;
 
+  if (decl_maybe_constant_destruction (decl, type)
+      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+    {
+      cxx_maybe_build_cleanup (decl, tf_warning_or_error);
+      return void_node;
+    }
+
   /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
      "__aeabi_atexit"), and DECL is a class object, we can just pass the
      destructor to "__cxa_atexit"; we don't have to build a temporary
@@ -8429,7 +8452,7 @@ expand_static_init (tree decl, tree init
   gcc_assert (TREE_STATIC (decl));
 
   /* Some variables require no dynamic initialization.  */
-  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
+  if (decl_maybe_constant_destruction (decl, TREE_TYPE (decl)))
     {
       /* Make sure the destructor is callable.  */
       cxx_maybe_build_cleanup (decl, tf_warning_or_error);
@@ -12697,12 +12720,13 @@ grokdeclarator (const cp_declarator *dec
 			      "a destructor cannot be %<concept%>");
                     return error_mark_node;
                   }
-                if (constexpr_p)
-                  {
-                    error_at (declspecs->locations[ds_constexpr],
-			      "a destructor cannot be %<constexpr%>");
-                    return error_mark_node;
-                  }
+		if (constexpr_p && cxx_dialect < cxx2a)
+		  {
+		    error_at (declspecs->locations[ds_constexpr],
+			      "%<constexpr%> destructors only available"
+			      " with %<-std=c++2a%> or %<-std=gnu++2a%>");
+		    return error_mark_node;
+		  }
 	      }
 	    else if (sfk == sfk_constructor && friendp && !ctype)
 	      {
@@ -12739,10 +12763,11 @@ grokdeclarator (const cp_declarator *dec
 	      }
 
 	    /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node.  */
-	    function_context = (ctype != NULL_TREE) ?
-	      decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE;
-	    publicp = (! friendp || ! staticp)
-	      && function_context == NULL_TREE;
+	    function_context
+	      = (ctype != NULL_TREE
+		 ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE);
+	    publicp = ((! friendp || ! staticp)
+		       && function_context == NULL_TREE);
 
 	    decl = grokfndecl (ctype, type,
 			       TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR
@@ -16742,6 +16767,9 @@ cxx_maybe_build_cleanup (tree decl, tsub
 	cleanup = error_mark_node;
       else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
 	/* Discard the call.  */;
+      else if (decl_maybe_constant_destruction (decl, type)
+	       && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+	cxx_constant_dtor (call, decl);
       else if (cleanup)
 	cleanup = cp_build_compound_expr (cleanup, call, complain);
       else
--- gcc/cp/init.c.jj	2019-10-03 17:55:28.337059023 +0200
+++ gcc/cp/init.c	2019-10-04 18:49:19.592533986 +0200
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "stor-layout.h"
 
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
@@ -2860,6 +2861,82 @@ std_placement_new_fn_p (tree alloc_fn)
   return false;
 }
 
+/* For element type ELT_TYPE, return the appropriate type of the heap object
+   containing such element(s).  COOKIE_SIZE is NULL or the size of cookie
+   in bytes.  FULL_SIZE is NULL if it is unknown how big the heap allocation
+   will be, otherwise size of the heap object.  If COOKIE_SIZE is NULL,
+   return array type ELT_TYPE[FULL_SIZE / sizeof(ELT_TYPE)], otherwise return
+   struct { size_t[COOKIE_SIZE/sizeof(size_t)]; ELT_TYPE[N]; }
+   where N is nothing (flexible array member) if FULL_SIZE is NULL, otherwise
+   it is computed such that the size of the struct fits into FULL_SIZE.  */
+
+tree
+build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree full_size)
+{
+  gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size));
+  gcc_assert (full_size == NULL_TREE || tree_fits_uhwi_p (full_size));
+  unsigned HOST_WIDE_INT csz = cookie_size ? tree_to_uhwi (cookie_size) : 0;
+  tree itype2 = NULL_TREE;
+  if (full_size)
+    {
+      unsigned HOST_WIDE_INT fsz = tree_to_uhwi (full_size);
+      gcc_assert (fsz >= csz);
+      fsz -= csz;
+      fsz /= int_size_in_bytes (elt_type);
+      itype2 = build_index_type (size_int (fsz - 1));
+      if (!cookie_size)
+	return build_cplus_array_type (elt_type, itype2);
+    }
+  else
+    gcc_assert (cookie_size);
+  csz /= int_size_in_bytes (sizetype);
+  tree itype1 = build_index_type (size_int (csz - 1));
+  tree atype1 = build_cplus_array_type (sizetype, itype1);
+  tree atype2 = build_cplus_array_type (elt_type, itype2);
+  tree rtype = cxx_make_type (RECORD_TYPE);
+  TYPE_NAME (rtype) = heap_identifier;
+  tree fld1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype1);
+  tree fld2 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype2);
+  DECL_FIELD_CONTEXT (fld1) = rtype;
+  DECL_FIELD_CONTEXT (fld2) = rtype;
+  DECL_ARTIFICIAL (fld1) = true;
+  DECL_ARTIFICIAL (fld2) = true;
+  TYPE_FIELDS (rtype) = fld1;
+  DECL_CHAIN (fld1) = fld2;
+  layout_type (rtype);
+  return rtype;
+}
+
+/* Help the constexpr code to find the right type for the heap variable
+   by adding a NOP_EXPR around ALLOC_CALL if needed for cookie_size.
+   Return ALLOC_CALL or ALLOC_CALL cast to a pointer to
+   struct { size_t[cookie_size/sizeof(size_t)]; elt_type[]; }.  */
+
+static tree
+maybe_wrap_new_for_constexpr (tree alloc_call, tree elt_type, tree cookie_size)
+{
+  if (cxx_dialect < cxx2a)
+    return alloc_call;
+
+  if (current_function_decl != NULL_TREE
+      && !DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+    return alloc_call;
+  
+  tree call_expr = extract_call_expr (alloc_call);
+  if (call_expr == error_mark_node)
+    return alloc_call;
+
+  tree alloc_call_fndecl = cp_get_callee_fndecl_nofold (call_expr);
+  if (alloc_call_fndecl == NULL_TREE
+      || !IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl))
+      || CP_DECL_CONTEXT (alloc_call_fndecl) != global_namespace)
+    return alloc_call;
+
+  tree rtype = build_new_constexpr_heap_type (elt_type, cookie_size,
+					      NULL_TREE);
+  return build_nop (build_pointer_type (rtype), alloc_call);
+}
+
 /* Generate code for a new-expression, including calling the "operator
    new" function, initializing the object, and, if an exception occurs
    during construction, cleaning up.  The arguments are as for
@@ -3327,6 +3404,10 @@ build_new_1 (vec<tree, va_gc> **placemen
 	}
     }
 
+  if (cookie_size)
+    alloc_call = maybe_wrap_new_for_constexpr (alloc_call, elt_type,
+					       cookie_size);
+
   /* In the simple case, we can stop now.  */
   pointer_type = build_pointer_type (type);
   if (!cookie_size && !is_initialized)
@@ -3902,17 +3983,11 @@ build_vec_delete_1 (tree base, tree maxi
 			     fold_convert (sizetype, maxindex));
 
   tbase = create_temporary_var (ptype);
-  tbase_init
-    = cp_build_modify_expr (input_location, tbase, NOP_EXPR,
-			    fold_build_pointer_plus_loc (input_location,
-							 fold_convert (ptype,
-								       base),
-							 virtual_size),
-			    complain);
-  if (tbase_init == error_mark_node)
-    return error_mark_node;
-  controller = build3 (BIND_EXPR, void_type_node, tbase,
-		       NULL_TREE, NULL_TREE);
+  DECL_INITIAL (tbase)
+    = fold_build_pointer_plus_loc (input_location, fold_convert (ptype, base),
+				   virtual_size);
+  tbase_init = build_stmt (input_location, DECL_EXPR, tbase);
+  controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE);
   TREE_SIDE_EFFECTS (controller) = 1;
 
   body = build1 (EXIT_EXPR, void_type_node,
--- gcc/cp/method.c.jj	2019-10-03 17:55:28.466057074 +0200
+++ gcc/cp/method.c	2019-10-04 16:56:11.509087416 +0200
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.
 #include "cgraph.h"
 #include "varasm.h"
 #include "toplev.h"
+#include "intl.h"
 #include "common/common-target.h"
 
 static void do_build_copy_assign (tree);
@@ -1237,12 +1238,24 @@ is_xible (enum tree_code code, tree to,
   return !!expr;
 }
 
+/* Categorize various special_function_kinds.  */
+#define SFK_CTOR_P(sfk) \
+  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
+#define SFK_DTOR_P(sfk) \
+  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
+#define SFK_ASSIGN_P(sfk) \
+  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
+#define SFK_COPY_P(sfk) \
+  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
+#define SFK_MOVE_P(sfk) \
+  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
+
 /* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
    DELETED_P or give an error message MSG with argument ARG.  */
 
 static void
-process_subob_fn (tree fn, tree *spec_p, bool *trivial_p,
-		  bool *deleted_p, bool *constexpr_p,
+process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p,
+		  bool *trivial_p, bool *deleted_p, bool *constexpr_p,
 		  bool diag, tree arg, bool dtor_from_ctor = false)
 {
   if (!fn || fn == error_mark_node)
@@ -1283,24 +1296,15 @@ process_subob_fn (tree fn, tree *spec_p,
       if (diag)
 	{
 	  inform (DECL_SOURCE_LOCATION (fn),
-		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
+		  SFK_DTOR_P (sfk)
+		  ? G_("defaulted destructor calls non-%<constexpr%> %qD")
+		  : G_("defaulted constructor calls non-%<constexpr%> %qD"),
+		  fn);
 	  explain_invalid_constexpr_fn (fn);
 	}
     }
 }
 
-/* Categorize various special_function_kinds.  */
-#define SFK_CTOR_P(sfk) \
-  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
-#define SFK_DTOR_P(sfk) \
-  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
-#define SFK_ASSIGN_P(sfk) \
-  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
-#define SFK_COPY_P(sfk) \
-  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
-#define SFK_MOVE_P(sfk) \
-  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
-
 /* Subroutine of synthesized_method_walk to allow recursion into anonymous
    aggregates.  If DTOR_FROM_CTOR is true, we're walking subobject destructors
    called from a synthesized constructor, in which case we don't consider
@@ -1318,8 +1322,7 @@ walk_field_subobs (tree fields, special_
     {
       tree mem_type, argtype, rval;
 
-      if (TREE_CODE (field) != FIELD_DECL
-	  || DECL_ARTIFICIAL (field))
+      if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
 	continue;
 
       /* Variant members only affect deletedness.  In particular, they don't
@@ -1457,7 +1460,7 @@ walk_field_subobs (tree fields, special_
 
       rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
 
-      process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+      process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 			constexpr_p, diag, field, dtor_from_ctor);
     }
 }
@@ -1510,23 +1513,23 @@ synthesized_method_base_walk (tree binfo
       && DECL_CONTEXT (*inheriting_ctor) == DECL_CONTEXT (rval))
     *inheriting_ctor = DECL_CLONED_FUNCTION (rval);
 
-  process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+  process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 		    constexpr_p, diag, BINFO_TYPE (base_binfo));
-  if (SFK_CTOR_P (sfk) &&
-      (!BINFO_VIRTUAL_P (base_binfo)
-       || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
+  if (SFK_CTOR_P (sfk)
+      && (!BINFO_VIRTUAL_P (base_binfo)
+	  || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
     {
       /* In a constructor we also need to check the subobject
 	 destructors for cleanup of partially constructed objects.  */
       tree dtor = locate_fn_flags (base_binfo, complete_dtor_identifier,
 				   NULL_TREE, flags,
 				   diag ? tf_warning_or_error : tf_none);
-	  /* Note that we don't pass down trivial_p; the subobject
-	     destructors don't affect triviality of the constructor.  Nor
-	     do they affect constexpr-ness (a constant expression doesn't
-	     throw) or exception-specification (a throw from one of the
-	     dtors would be a double-fault).  */
-      process_subob_fn (dtor, NULL, NULL, deleted_p, NULL, false,
+      /* Note that we don't pass down trivial_p; the subobject
+	 destructors don't affect triviality of the constructor.  Nor
+	 do they affect constexpr-ness (a constant expression doesn't
+	 throw) or exception-specification (a throw from one of the
+	 dtors would be a double-fault).  */
+      process_subob_fn (dtor, sfk, NULL, NULL, deleted_p, NULL, false,
 			BINFO_TYPE (base_binfo), /*dtor_from_ctor*/true);
     }
 
@@ -1608,7 +1611,8 @@ synthesized_method_walk (tree ctype, spe
 	member is a constexpr function.  */
   if (constexpr_p)
     *constexpr_p = (SFK_CTOR_P (sfk)
-		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14));
+		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
+		    || (SFK_DTOR_P (sfk) && cxx_dialect >= cxx2a));
 
   bool expected_trivial = type_has_trivial_fn (ctype, sfk);
   if (trivial_p)
@@ -1704,8 +1708,8 @@ synthesized_method_walk (tree ctype, spe
   else if (vec_safe_is_empty (vbases))
     /* No virtual bases to worry about.  */;
   else if (ABSTRACT_CLASS_TYPE_P (ctype) && cxx_dialect >= cxx14
-	   /* DR 1658 specifis that vbases of abstract classes are
-	      ignored for both ctors and dtors.  Except DR 2338
+	   /* DR 1658 specifies that vbases of abstract classes are
+	      ignored for both ctors and dtors.  Except DR 2336
 	      overrides that skipping when determing the eh-spec of a
 	      virtual destructor.  */
 	   && sfk != sfk_virtual_destructor)
@@ -2046,7 +2050,8 @@ implicitly_declare_fn (special_function_
     constexpr_p = false;
   /* A trivial copy/move constructor is also a constexpr constructor,
      unless the class has virtual bases (7.1.5p4).  */
-  else if (trivial_p && cxx_dialect >= cxx11
+  else if (trivial_p
+	   && cxx_dialect >= cxx11
 	   && (kind == sfk_copy_constructor
 	       || kind == sfk_move_constructor)
 	   && !CLASSTYPE_VBASECLASSES (type))
--- gcc/cp/typeck2.c.jj	2019-10-03 17:55:28.312059400 +0200
+++ gcc/cp/typeck2.c	2019-10-04 12:49:21.328033198 +0200
@@ -902,7 +902,13 @@ store_init_value (tree decl, tree init,
 	    value = oldval;
 	}
     }
-  value = cp_fully_fold_init (value);
+  /* Don't fold initializers of automatic variables in constexpr functions,
+     that might fold away something that needs to be diagnosed at constexpr
+     evaluation time.  */
+  if (!current_function_decl
+      || !DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+      || TREE_STATIC (decl))
+    value = cp_fully_fold_init (value);
 
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C.jj	2019-10-03 17:55:29.185046209 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C	2019-10-04 12:49:21.521030243 +0200
@@ -5,8 +5,9 @@ struct A { ~A(); };
 constexpr int f(int i) { return i; }
 constexpr int g(A* ap)
 {
-  return f((delete[] ap, 42)); // { dg-message "" }
+  return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
 }
 
 A a;
 constexpr int i = g(&a);	// { dg-error "" }
+				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
--- gcc/testsuite/g++.dg/cpp0x/locations1.C.jj	2019-10-03 17:55:29.130047041 +0200
+++ gcc/testsuite/g++.dg/cpp0x/locations1.C	2019-10-04 12:49:21.610028880 +0200
@@ -11,7 +11,7 @@ struct S
 {
   virtual S();  // { dg-error "3:constructors cannot be declared .virtual." }
   constexpr int s = 1;  // { dg-error "3:non-static data member .s. declared .constexpr." }
-  constexpr ~S();  // { dg-error "3:a destructor cannot be .constexpr." }
+  constexpr ~S();  // { dg-error "3:'constexpr' destructors only available with" "" { target c++17_down } }
 };
 
 typedef constexpr int my_int;  // { dg-error "9:.constexpr. cannot appear in a typedef declaration" }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-new.C.jj	2019-10-03 17:55:29.284044714 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-new.C	2019-10-04 12:49:21.369032571 +0200
@@ -4,7 +4,7 @@ constexpr int *f4(bool b) {
   if (b) {
     return nullptr;
   } else {
-    return new int{42}; // { dg-error "call to non-.constexpr." }
+    return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } }
   }
 }
 static_assert(f4(true) == nullptr, "");
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,9 @@
+// P0784R7
+// { dg-do compile { target c++11 } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}	// { dg-error "'constexpr' destructors only available with" "" { target c++17_down } }
+  int s;
+};
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C.jj	2019-10-04 12:49:21.465031101 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,66 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : r (4), s (3) { --r; s -= 2; }
+  constexpr ~S () { if (s == 1) s = 0; else asm (""); if (s == 0 && r == 3) r = 0; else asm (""); }
+  int r, s;
+};
+struct T : public S
+{
+  constexpr T () : t (2) {}
+  int t;
+  S u;
+};
+struct U : public S
+{
+  constexpr U (int x) : u (x) {}
+  constexpr ~U () = default;
+  int u;
+  S v;
+};
+
+constexpr S a;
+constexpr T b;
+constexpr U c = 3;
+static_assert (a.s == 1 && a.r == 3);
+static_assert (b.s == 1 && b.r == 3 && b.t == 2 && b.u.s == 1 && b.u.r == 3);
+static_assert (c.s == 1 && c.r == 3 && c.u == 3 && c.v.s == 1 && c.v.r == 3);
+
+void
+foo ()
+{
+  static constexpr S d;
+  static constexpr T e;
+  static constexpr U f = 4;
+  static_assert (d.s == 1 && d.r == 3);
+  static_assert (e.s == 1 && e.r == 3 && e.t == 2 && e.u.s == 1 && e.u.r == 3);
+  static_assert (f.s == 1 && f.r == 3 && f.u == 4 && f.v.s == 1 && f.v.r == 3);
+  if (1)
+    {
+      constexpr S g;
+      constexpr T h;
+      constexpr U i = 5;
+      static_assert (g.s == 1 && g.r == 3);
+      static_assert (h.s == 1 && h.r == 3 && h.t == 2 && h.u.s == 1 && h.u.r == 3);
+      static_assert (i.s == 1 && i.r == 3 && i.u == 5 && i.v.s == 1 && i.v.r == 3);
+    }
+}
+
+constexpr bool
+bar ()
+{
+  S j;
+  T k;
+  U l = 6;
+  if (j.s != 1 || j.r != 3)
+    return false;
+  if (k.s != 1 || k.r != 3 || k.t != 2 || k.u.s != 1 || k.u.r != 3)
+    return false;
+  if (l.s != 1 || l.r != 3 || l.u != 6 || l.v.s != 1 || l.v.r != 3)
+    return false;
+  return true;
+}
+
+static_assert (bar ());
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2019-10-04 16:56:50.216495821 +0200
@@ -0,0 +1,185 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}
+  int s;
+};
+struct T	// { dg-message "'T' is not literal because" }
+{		// { dg-message "'T' does not have 'constexpr' destructor" "" { target *-*-* } .-1 }
+  constexpr T () : t (0) {}
+  ~T () {}	// { dg-message "defaulted destructor calls non-'constexpr' 'T::~T\\(\\)'" }
+  int t;
+};
+struct U : public S
+{
+  constexpr U () : u (0) {}
+  constexpr ~U () = default;	// { dg-error "explicitly defaulted function 'constexpr U::~U\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int u;
+  T t;
+};
+struct V : virtual public S
+{
+  V () : v (0) {}
+  constexpr ~V () = default;	// { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int v;
+};
+struct W0
+{
+  constexpr W0 () : w (0) {}
+  constexpr W0 (int x) : w (x) {}
+  constexpr ~W0 () { if (w == 5) asm (""); w = 3; }
+  int w;
+};
+struct W1
+{
+  constexpr W1 () : w (0) {}
+  constexpr W1 (int x) : w (x) {}
+  constexpr ~W1 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W2
+{
+  constexpr W2 () : w (0) {}
+  constexpr W2 (int x) : w (x) {}
+  constexpr ~W2 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W3
+{
+  constexpr W3 () : w (0) {}
+  constexpr W3 (int x) : w (x) {}
+  constexpr ~W3 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W4
+{
+  constexpr W4 () : w (0) {}
+  constexpr W4 (int x) : w (x) {}
+  constexpr ~W4 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W5
+{
+  constexpr W5 () : w (0) {}
+  constexpr W5 (int x) : w (x) {}
+  constexpr ~W5 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W6
+{
+  constexpr W6 () : w (0) {}
+  constexpr W6 (int x) : w (x) {}
+  constexpr ~W6 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W7
+{
+  constexpr W7 () : w (0) {}
+  constexpr W7 (int x) : w (x) {}
+  constexpr ~W7 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W8
+{
+  constexpr W8 () : w (0) {}
+  constexpr W8 (int x) : w (x) {}
+  constexpr ~W8 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct X : public T
+{
+  constexpr X () : x (0) {}
+  constexpr ~X () = default;	// { dg-error "explicitly defaulted function 'constexpr X::~X\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int x;
+};
+constexpr S s;
+constexpr T t;	// { dg-error "the type 'const T' of 'constexpr' variable 't' is not literal" }
+constexpr W0 w1;
+constexpr W0 w2 = 12;
+constexpr W1 w3 = 5;	// { dg-message "in 'constexpr' expansion of" }
+constexpr W0 w4[3] = { 1, 2, 3 };
+constexpr W2 w5[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f1 ()
+{
+  constexpr S s2;
+  constexpr W0 w6;
+  constexpr W0 w7 = 12;
+  constexpr W3 w8 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w9[3] = { 1, 2, 3 };
+  constexpr W4 w10[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+}
+
+constexpr int
+f2 ()
+{
+  constexpr S s3;
+  constexpr W0 w11;
+  constexpr W0 w12 = 12;
+  constexpr W5 w13 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w14[3] = { 1, 2, 3 };
+  constexpr W6 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int
+f3 ()
+{
+  S s3;
+  W0 w11;
+  W0 w12 = 12;
+  W0 w14[3] = { 1, 2, 3 };
+  return 0;
+}
+
+constexpr int x3 = f3 ();
+
+constexpr int
+f4 ()
+{
+  W7 w13 = 5;
+  return 0;
+}
+
+constexpr int x4 = f4 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr int
+f5 ()
+{
+  W8 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int x5 = f5 ();	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f6 ()
+{
+  constexpr T t2;	// { dg-error "the type 'const T' of 'constexpr' variable 't2' is not literal" }
+}
+
+constexpr int
+f7 ()
+{
+  constexpr T t3;	// { dg-error "the type 'const T' of 'constexpr' variable 't3' is not literal" }
+  return 0;
+}
+
+constexpr int
+f8 ()
+{
+  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
+  return 0;
+}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C	2019-10-04 12:49:21.464031117 +0200
@@ -0,0 +1,39 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S { constexpr S () : s (5) {} constexpr S (int x) : s (x) {} int s; };
+
+constexpr bool
+foo ()
+{
+  int r = 0;
+  S *p = new S ();
+  p->s += 3;
+  r += p->s;
+  delete p;
+  p = new S (12);
+  p->s = p->s * 2;
+  r += p->s;
+  delete p;
+  int *q = new int;
+  *q = 25;
+  r += *q;
+  delete q;
+  q = new int (1);
+  r += *q;
+  if (!q)
+    return false;
+  delete q;
+  q = new int[5]{1,2,3,4,5};
+  r += q[0] + q[4];
+  delete[] q;
+  q = new int[4];
+  q[0] = 6;
+  q[1] = 7;
+  q[3] = 8;
+  r += q[0] + q[1] + q[3];
+  delete[] q;
+  return r == 5 + 3 + 2 * 12 + 25 + 1 + 1 + 5 + 6 + 7 + 8;
+}
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C.jj	2019-10-04 12:49:21.465031101 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C	2019-10-04 12:49:21.465031101 +0200
@@ -0,0 +1,21 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+template <int N>
+constexpr bool
+foo (const char (&x)[N])
+{
+  int **p = new int *[N];
+  for (int i = 0; i < N; i++)
+    p[i] = new int (x[i]);
+  for (int i = 0; i < N; i++)
+    if (*p[i] != x[i])
+      return false;
+  for (int i = 0; i < N; ++i)
+    delete p[i];
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ("foobar");
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C.jj	2019-10-04 12:49:21.464031117 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-10-04 19:19:25.410753359 +0200
@@ -0,0 +1,73 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+constexpr int *
+f1 ()
+{
+  return new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+}
+
+constexpr auto v1 = f1 ();
+
+constexpr bool
+f2 ()
+{
+  int *p = new int (3);		// { dg-error "is not a constant expression because allocated storage has not been deallocated" }
+  return false;
+}
+
+constexpr auto v2 = f2 ();
+
+constexpr bool
+f3 ()
+{
+  int *p = new int (3);
+  int *q = p;
+  delete p;
+  delete q;			// { dg-error "deallocation of already deallocated storage" }
+  return false;
+}
+
+constexpr auto v3 = f3 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f4 (int *p)
+{
+  delete p;			// { dg-error "deallocation of storage that was not previously allocated" }
+  return false;
+}
+
+int q;
+constexpr auto v4 = f4 (&q);	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f5 ()
+{
+  int *p = new int;		// { dg-message "allocated here" }
+  return *p == 1;
+}
+
+constexpr auto v5 = f5 ();	// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
+
+constexpr bool
+f6 ()
+{
+  int *p = new int (2);		// { dg-message "allocated here" }
+  int *q = p;
+  delete p;
+  return *q == 2;
+}
+
+constexpr auto v6 = f6 ();	// { dg-error "use of allocated storage after deallocation in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1  }
+
+constexpr int *
+f7 ()
+{
+  int *p = new int (2);		// { dg-error "is not a constant expression because it refers to a result of" }
+  delete p;
+  return p;
+}
+
+constexpr auto v7 = f7 ();
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C.jj	2019-10-04 12:49:21.429031653 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C	2019-10-04 12:49:21.429031653 +0200
@@ -0,0 +1,29 @@
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) { s++; }
+  constexpr S (int x) : s (x) { s += 2; }
+  constexpr ~S () { if (s != 35) asm (""); s = 5; }
+  int s;
+};
+
+constexpr bool
+foo ()
+{
+  S *p = new S (7);
+  if (p->s != 9) return false;
+  p->s = 35;
+  delete p;
+  p = new S[3] { 11, 13, 15 };
+  if (p[0].s != 13 || p[1].s != 15 || p[2].s != 17) return false;
+  p[0].s = 35;
+  p[2].s = 35;
+  p[1].s = 35;
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj	2019-10-03 17:55:29.421042643 +0200
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2019-10-04 12:49:21.464031117 +0200
@@ -430,16 +430,34 @@
 
 // C++20 features
 
-#if __cpp_conditional_explicit != 201806
-# error "__cpp_conditional_explicit != 201806"
+#ifndef __cpp_conditional_explicit
+#  error "__cpp_conditional_explicit"
+#elif __cpp_conditional_explicit != 201806
+#  error "__cpp_conditional_explicit != 201806"
 #endif
 
-#if __cpp_nontype_template_parameter_class != 201806
-# error "__cpp_nontype_template_parameter_class != 201806"
+#ifndef __cpp_nontype_template_parameter_class
+#  error "__cpp_nontype_template_parameter_class"
+#elif __cpp_nontype_template_parameter_class != 201806
+#  error "__cpp_nontype_template_parameter_class != 201806"
 #endif
 
-#if __cpp_impl_destroying_delete != 201806
-# error "__cpp_impl_destroying_delete != 201806"
+#ifndef __cpp_impl_destroying_delete
+#  error "__cpp_impl_destroying_delete"
+#elif __cpp_impl_destroying_delete != 201806
+#  error "__cpp_impl_destroying_delete != 201806"
+#endif
+
+#ifndef __cpp_constinit
+#  error "__cpp_constinit"
+#elif __cpp_constinit != 201907
+#  error "__cpp_constinit != 201907"
+#endif
+
+#ifndef __cpp_constexpr_dynamic_alloc
+#  error "__cpp_constexpr_dynamic_alloc"
+#elif __cpp_constexpr_dynamic_alloc != 201907
+#  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
 #ifdef __has_cpp_attribute
@@ -484,8 +502,6 @@
 #  error "__has_cpp_attribute"
 #endif
 
-// C++2A features:
-
 #ifndef __cpp_char8_t
 #  error "__cpp_char8_t"
 #elif __cpp_char8_t != 201811
--- gcc/testsuite/g++.dg/ext/is_literal_type3.C.jj	2019-10-04 12:49:21.338033046 +0200
+++ gcc/testsuite/g++.dg/ext/is_literal_type3.C	2019-10-04 12:49:21.338033046 +0200
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++11 } }
+
+struct S {
+  constexpr S () : n{} { }
+  ~S () { n = 1; }
+  int n;
+};
+
+static_assert(!__is_literal_type(S), "");
+
+#ifdef __cpp_constexpr_dynamic_alloc
+struct T {
+  constexpr T () : n{} { }
+  constexpr ~T () { n = 1; }
+  int n;
+};
+
+static_assert(__is_literal_type(T), "");
+
+struct U : public T {
+  constexpr U () : u{} { }
+  int u;
+};
+
+static_assert(__is_literal_type(U), "");
+#endif


	Jakub
diff mbox series

Patch

--- gcc/c-family/c-cppbuiltin.c.jj	2019-09-26 21:34:21.188923996 +0200
+++ gcc/c-family/c-cppbuiltin.c	2019-09-27 18:25:41.346059343 +0200
@@ -989,6 +989,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constinit=201907");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
+	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
 	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
--- gcc/cp/cp-tree.h.jj	2019-09-27 12:22:54.042880699 +0200
+++ gcc/cp/cp-tree.h	2019-09-27 18:25:40.903065957 +0200
@@ -172,6 +172,9 @@  enum cp_tree_index
     CPTI_VALUE_IDENTIFIER,
     CPTI_FUN_IDENTIFIER,
     CPTI_CLOSURE_IDENTIFIER,
+    CPTI_HEAP_UNINIT_IDENTIFIER,
+    CPTI_HEAP_IDENTIFIER,
+    CPTI_HEAP_DELETED_IDENTIFIER,
 
     CPTI_LANG_NAME_C,
     CPTI_LANG_NAME_CPLUSPLUS,
@@ -310,6 +313,9 @@  extern GTY(()) tree cp_global_trees[CPTI
 #define value_identifier		cp_global_trees[CPTI_VALUE_IDENTIFIER]
 #define fun_identifier			cp_global_trees[CPTI_FUN_IDENTIFIER]
 #define closure_identifier		cp_global_trees[CPTI_CLOSURE_IDENTIFIER]
+#define heap_uninit_identifier		cp_global_trees[CPTI_HEAP_UNINIT_IDENTIFIER]
+#define heap_identifier			cp_global_trees[CPTI_HEAP_IDENTIFIER]
+#define heap_deleted_identifier		cp_global_trees[CPTI_HEAP_DELETED_IDENTIFIER]
 #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
 #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
 
@@ -6324,6 +6330,7 @@  extern bool vbase_has_user_provided_move
 extern tree default_init_uninitialized_part (tree);
 extern bool trivial_default_constructor_is_constexpr (tree);
 extern bool type_has_constexpr_default_constructor (tree);
+extern bool type_has_constexpr_destructor	(tree);
 extern bool type_has_virtual_destructor		(tree);
 extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
 extern bool classtype_has_non_deleted_move_ctor (tree);
@@ -7729,6 +7736,7 @@  extern bool require_constant_expression
 extern bool require_rvalue_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
+extern void cxx_constant_dtor			(tree, tree);
 extern tree cxx_constant_init			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE, bool = false);
 extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
--- gcc/cp/class.c.jj	2019-09-26 21:34:21.434920308 +0200
+++ gcc/cp/class.c	2019-09-27 18:25:40.904065942 +0200
@@ -206,6 +206,7 @@  static int empty_base_at_nonzero_offset_
 static tree end_of_base (tree);
 static tree get_vcall_index (tree, tree);
 static bool type_maybe_constexpr_default_constructor (tree);
+static bool type_maybe_constexpr_destructor (tree);
 static bool field_poverlapping_p (tree);
 
 /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
@@ -5242,7 +5243,7 @@  type_has_constexpr_default_constructor (
    without forcing a lazy declaration (which might cause undesired
    instantiations).  */
 
-bool
+static bool
 type_maybe_constexpr_default_constructor (tree t)
 {
   if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DEFAULT_CTOR (t)
@@ -5252,6 +5253,34 @@  type_maybe_constexpr_default_constructor
   return type_has_constexpr_default_constructor (t);
 }
 
+/* Returns true iff class T has a constexpr destructor.  */
+
+bool
+type_has_constexpr_destructor (tree t)
+{
+  tree fns;
+
+  if (CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Non-trivial, we need to check subobject destructors.  */
+    lazily_declare_fn (sfk_destructor, t);
+  fns = CLASSTYPE_DESTRUCTOR (t);
+  return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
+}
+
+/* Returns true iff class T has a constexpr destructor or has an
+   implicitly declared destructor that we can't tell if it's constexpr
+   without forcing a lazy declaration (which might cause undesired
+   instantiations).  */
+
+static bool
+type_maybe_constexpr_destructor (tree t)
+{
+  if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
+    /* Assume it's constexpr.  */
+    return true;
+  return type_has_constexpr_destructor (t);
+}
+
 /* Returns true iff class TYPE has a virtual destructor.  */
 
 bool
@@ -5503,8 +5532,11 @@  finalize_literal_type_property (tree t)
 {
   tree fn;
 
-  if (cxx_dialect < cxx11
-      || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  if (cxx_dialect < cxx11)
+    CLASSTYPE_LITERAL_P (t) = false;
+  else if (CLASSTYPE_LITERAL_P (t)
+	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && (cxx_dialect < cxx2a || !type_maybe_constexpr_destructor (t)))
     CLASSTYPE_LITERAL_P (t) = false;
   else if (CLASSTYPE_LITERAL_P (t) && LAMBDA_TYPE_P (t))
     CLASSTYPE_LITERAL_P (t) = (cxx_dialect >= cxx17);
@@ -5558,8 +5590,12 @@  explain_non_literal_class (tree t)
     inform (UNKNOWN_LOCATION,
 	    "  %qT is a closure type, which is only literal in "
 	    "C++17 and later", t);
-  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+  else if (cxx_dialect < cxx2a && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
     inform (UNKNOWN_LOCATION, "  %q+T has a non-trivial destructor", t);
+  else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+	   && !type_maybe_constexpr_destructor (t))
+    inform (UNKNOWN_LOCATION, "  %q+T does not have %<constexpr%> destructor",
+	    t);
   else if (CLASSTYPE_NON_AGGREGATE (t)
 	   && !TYPE_HAS_TRIVIAL_DFLT (t)
 	   && !LAMBDA_TYPE_P (t)
--- gcc/cp/constexpr.c.jj	2019-09-27 20:33:37.600208356 +0200
+++ gcc/cp/constexpr.c	2019-09-27 20:38:38.203710246 +0200
@@ -34,6 +34,7 @@  along with GCC; see the file COPYING3.
 #include "gimple-fold.h"
 #include "timevar.h"
 #include "fold-const-call.h"
+#include "stor-layout.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -1031,6 +1032,9 @@  struct constexpr_ctx {
      on simple constants or location wrappers) encountered during current
      cxx_eval_outermost_constant_expr call.  */
   HOST_WIDE_INT *constexpr_ops_count;
+  /* Heap VAR_DECLs created during the evaluation of the outermost constant
+     expression.  */
+  vec<tree> *heap_vars;
 
   /* Whether we should error on a non-constant expression or fail quietly.  */
   bool quiet;
@@ -1666,6 +1670,58 @@  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)
+	{
+	  const int nargs = call_expr_nargs (t);
+	  tree arg0 = 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)
+		arg0 = arg;
+	    }
+	  gcc_assert (arg0);
+	  if (IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
+	    {
+	      tree type = build_array_type_nelts (char_type_node,
+						  tree_to_uhwi (arg0));
+	      tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier,
+				     type);
+	      DECL_ARTIFICIAL (var) = 1;
+	      TREE_STATIC (var) = 1;
+	      ctx->heap_vars->safe_push (var);
+	      return fold_convert (ptr_type_node, build_address (var));
+	    }
+	  else
+	    {
+	      STRIP_NOPS (arg0);
+	      if (TREE_CODE (arg0) == ADDR_EXPR
+		  && VAR_P (TREE_OPERAND (arg0, 0)))
+		{
+		  tree var = TREE_OPERAND (arg0, 0);
+		  if (DECL_NAME (var) == heap_uninit_identifier
+		      || DECL_NAME (var) == heap_identifier)
+		    {
+		      DECL_NAME (var) = heap_deleted_identifier;
+		      ctx->values->remove (var);
+		      return void_node;
+		    }
+		  else if (DECL_NAME (var) == heap_deleted_identifier)
+		    {
+		      if (!ctx->quiet)
+			error_at (loc, "deallocation of already deallocated "
+				       "storage");
+		      *non_constant_p = true;
+		      return t;
+		    }
+		}
+	    }
+	}
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -2998,8 +3054,8 @@  base_field_constructor_elt (vec<construc
 }
 
 /* Some of the expressions fed to the constexpr mechanism are calls to
-   constructors, which have type void.  In that case, return the type being
-   initialized by the constructor.  */
+   constructors or destructors, which have type void.  In that case,
+   return the type being initialized by the constructor.  */
 
 static tree
 initialized_type (tree t)
@@ -3011,8 +3067,10 @@  initialized_type (tree t)
     {
       /* A constructor call has void type, so we need to look deeper.  */
       tree fn = get_function_named_in_call (t);
-      if (fn && TREE_CODE (fn) == FUNCTION_DECL
-	  && DECL_CXX_CONSTRUCTOR_P (fn))
+      if (fn
+	  && TREE_CODE (fn) == FUNCTION_DECL
+	  && (DECL_CXX_CONSTRUCTOR_P (fn)
+	      || (cxx_dialect >= cxx2a && DECL_CXX_DESTRUCTOR_P (fn))))
 	type = DECL_CONTEXT (fn);
     }
   else if (TREE_CODE (t) == COMPOUND_EXPR)
@@ -3434,11 +3492,24 @@  cxx_fold_indirect_ref (location_t loc, t
 	{
 	  tree field = TYPE_FIELDS (optype);
 	  for (; field; field = DECL_CHAIN (field))
-	    if (TREE_CODE (field) == FIELD_DECL
-		&& TREE_TYPE (field) != error_mark_node
-		&& integer_zerop (byte_position (field))
-		&& similar_type_p (TREE_TYPE (field), type))
+	    if (TREE_CODE (field) != FIELD_DECL
+		|| TREE_TYPE (field) == error_mark_node
+		|| !integer_zerop (byte_position (field)))
+	      continue;
+	    else if (similar_type_p (TREE_TYPE (field), type))
 	      return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE);
+	    else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
+		     && similar_type_p (TREE_TYPE (TREE_TYPE (field)), type))
+	      {
+		tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
+		tree min_val = size_zero_node;
+		if (type_domain && TYPE_MIN_VALUE (type_domain))
+		  min_val = TYPE_MIN_VALUE (type_domain);
+		op = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
+				  op, field, NULL_TREE);
+		return build4_loc (loc, ARRAY_REF, type, op, min_val,
+				   NULL_TREE, NULL_TREE);
+	      }
 	}
     }
   else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
@@ -3521,12 +3592,44 @@  cxx_fold_indirect_ref (location_t loc, t
 	    {
 	      tree field = TYPE_FIELDS (op00type);
 	      for (; field; field = DECL_CHAIN (field))
-		if (TREE_CODE (field) == FIELD_DECL
-		    && TREE_TYPE (field) != error_mark_node
-		    && tree_int_cst_equal (byte_position (field), op01)
-		    && similar_type_p (TREE_TYPE (field), type))
+		if (TREE_CODE (field) != FIELD_DECL
+		    || TREE_TYPE (field) == error_mark_node)
+		  continue;
+		else if (tree_int_cst_equal (byte_position (field), op01)
+			 && similar_type_p (TREE_TYPE (field), type))
 		  return fold_build3 (COMPONENT_REF, type, op00,
 				      field, NULL_TREE);
+		else if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
+			 && tree_int_cst_le (byte_position (field), op01)
+			 && similar_type_p (TREE_TYPE (TREE_TYPE (field)),
+					    type))
+		  {
+		    tree type_domain = TYPE_DOMAIN (TREE_TYPE (field));
+		    tree min_val = size_zero_node;
+		    tree max_val = NULL_TREE;
+		    if (type_domain && TYPE_MIN_VALUE (type_domain))
+		      min_val = TYPE_MIN_VALUE (type_domain);
+		    if (type_domain && TYPE_MAX_VALUE (type_domain))
+		      max_val = TYPE_MAX_VALUE (type_domain);
+		    offset_int off = wi::to_offset (op01);
+		    off -= wi::to_offset (byte_position (field));
+		    offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (type));
+		    offset_int remainder;
+		    off = wi::divmod_trunc (off, el_sz, SIGNED, &remainder);
+		    if (remainder == 0
+			&& TREE_CODE (min_val) == INTEGER_CST
+			&& (max_val == NULL_TREE
+			    || (TREE_CODE (max_val) == INTEGER_CST
+				&& off <= wi::to_offset (max_val))))
+		      {
+			off = off + wi::to_offset (min_val);
+			op00 = fold_build3 (COMPONENT_REF, TREE_TYPE (field),
+					    op00, field, NULL_TREE);
+			op01 = wide_int_to_tree (sizetype, off);
+			return build4_loc (loc, ARRAY_REF, type, op00, op01,
+					   NULL_TREE, NULL_TREE);
+		      }
+		  }
 	    }
 	}
     }
@@ -3645,7 +3748,23 @@  cxx_eval_indirect_ref (const constexpr_c
 static void
 non_const_var_error (tree r)
 {
+  auto_diagnostic_group d;
   tree type = TREE_TYPE (r);
+  if (DECL_NAME (r) == heap_uninit_identifier
+      || DECL_NAME (r) == heap_identifier)
+    {
+      error ("the content of uninitialized storage is not usable "
+	     "in a constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
+  if (DECL_NAME (r) == heap_deleted_identifier)
+    {
+      error ("use of allocated storage after deallocation in a "
+	     "constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+      return;
+    }
   error ("the value of %qD is not usable in a constant "
 	 "expression", r);
   /* Avoid error cascade.  */
@@ -3892,6 +4011,15 @@  cxx_eval_store_expression (const constex
     valp = ctx->values->get (object);
   else
     valp = NULL;
+  if (!valp
+      && VAR_P (object)
+      && DECL_NAME (object) == heap_identifier)
+    {
+      tree ctor = build_constructor (type, NULL);
+      CONSTRUCTOR_NO_CLEARING (ctor) = true;
+      ctx->values->put (object, ctor);
+      valp = ctx->values->get (object);
+    }
   if (!valp)
     {
       /* A constant-expression cannot modify objects from outside the
@@ -3905,7 +4033,7 @@  cxx_eval_store_expression (const constex
   bool no_zero_init = true;
 
   releasing_vec ctors;
-  while (!refs->is_empty())
+  while (!refs->is_empty ())
     {
       if (*valp == NULL_TREE)
 	{
@@ -4046,7 +4174,9 @@  cxx_eval_store_expression (const constex
   if (const_object_being_modified)
     {
       bool fail = false;
-      if (!CLASS_TYPE_P (TREE_TYPE (const_object_being_modified)))
+      tree const_objtype
+	= strip_array_types (TREE_TYPE (const_object_being_modified));
+      if (!CLASS_TYPE_P (const_objtype))
 	fail = true;
       else
 	{
@@ -4365,6 +4495,12 @@  cxx_eval_loop_expr (const constexpr_ctx
 		    tree *jump_target)
 {
   constexpr_ctx new_ctx = *ctx;
+  tree local_target;
+  if (!jump_target)
+    {
+      local_target = NULL_TREE;
+      jump_target = &local_target;
+    }
 
   tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
@@ -4907,14 +5043,21 @@  cxx_eval_constant_expression (const cons
       break;
 
     case CLEANUP_STMT:
-      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+      {
+	tree initial_jump_target = jump_target ? *jump_target : NULL_TREE;
+	r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	  /* Also evaluate the cleanup.  If we weren't skipping at the
+	     start of the CLEANUP_BODY, change jump_target temporarily
+	     to &initial_jump_target, so that even a return or break or
+	     continue in the body doesn't skip the cleanup.  */
+	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
 					non_constant_p, overflow_p,
-					jump_target);
-      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
-	/* Also evaluate the cleanup.  */
-	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
-				      non_constant_p, overflow_p,
-				      jump_target);
+					jump_target ? &initial_jump_target
+					: NULL);
+      }
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -5203,8 +5346,7 @@  cxx_eval_constant_expression (const cons
 	if (VOID_TYPE_P (type))
 	  return void_node;
 
-	if (TREE_CODE (op) == PTRMEM_CST
-	    && !TYPE_PTRMEM_P (type))
+	if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
 
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
@@ -5258,6 +5400,81 @@  cxx_eval_constant_expression (const cons
 	      }
 	  }
 
+	if (INDIRECT_TYPE_P (type)
+	    && TREE_CODE (op) == NOP_EXPR
+	    && TREE_TYPE (op) == ptr_type_node
+	    && TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR
+	    && VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
+	    && DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0),
+					0)) == heap_uninit_identifier)
+	  {
+	    tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0);
+	    tree var_type = TREE_TYPE (type);
+	    tree cookie_type = NULL_TREE;
+	    bool array_p = false;
+	    HOST_WIDE_INT cookie_size = 0;
+	    if (TREE_CODE (var_type) == ARRAY_TYPE
+		&& TYPE_DOMAIN (var_type) == NULL_TREE)
+	      {
+		var_type = TREE_TYPE (var_type);
+		array_p = true;
+	      }
+	    else if (TREE_CODE (var_type) == RECORD_TYPE
+		     && TYPE_NAME (var_type) == NULL_TREE)
+	      if (tree fld1 = TYPE_FIELDS (var_type))
+		if (TREE_CODE (fld1) == FIELD_DECL
+		    && DECL_NAME (fld1) == NULL_TREE
+		    && DECL_ARTIFICIAL (fld1)
+		    && TREE_CODE (TREE_TYPE (fld1)) == ARRAY_TYPE
+		    && COMPLETE_TYPE_P (TREE_TYPE (fld1)))
+		  if (tree fld2 = DECL_CHAIN (fld1))
+		    if (TREE_CODE (fld2) == FIELD_DECL
+			&& DECL_NAME (fld2) == NULL_TREE
+			&& DECL_ARTIFICIAL (fld2)
+			&& TREE_CODE (TREE_TYPE (fld2)) == ARRAY_TYPE
+			&& TYPE_DOMAIN (TREE_TYPE (fld2)) == NULL_TREE
+			&& DECL_CHAIN (fld2) == NULL_TREE)
+		      {
+			var_type = TREE_TYPE (TREE_TYPE (fld2));
+			array_p = true;
+			cookie_type = TREE_TYPE (fld1);
+			cookie_size = int_size_in_bytes (TREE_TYPE (fld1));
+		      }
+	    HOST_WIDE_INT sz1 = int_size_in_bytes (var_type);
+	    HOST_WIDE_INT sz2 = int_size_in_bytes (TREE_TYPE (var));
+	    if (sz1 <= sz2 && cookie_size <= sz2)
+	      {
+		DECL_NAME (var) = heap_identifier;
+		if (array_p && sz1 > 0)
+		  {
+		    sz2 -= cookie_size;
+		    sz2 /= sz1;
+		    tree idx_type = build_index_type (size_int (sz2 - 1));
+		    var_type = build_cplus_array_type (var_type, idx_type);
+		    if (cookie_type)
+		      {
+			location_t loc = cp_expr_loc_or_input_loc (t);
+			tree vtype = cxx_make_type (RECORD_TYPE);
+			tree fld1 = build_decl (loc, FIELD_DECL, NULL_TREE,
+						cookie_type);
+			tree fld2 = build_decl (loc, FIELD_DECL, NULL_TREE,
+						var_type);
+			DECL_FIELD_CONTEXT (fld1) = vtype;
+			DECL_FIELD_CONTEXT (fld2) = vtype;
+			DECL_ARTIFICIAL (fld1) = true;
+			DECL_ARTIFICIAL (fld2) = true;
+			TYPE_FIELDS (vtype) = fld1;
+			DECL_CHAIN (fld1) = fld2;
+			layout_type (vtype);
+			var_type = vtype;
+		      }
+		  }
+		TREE_TYPE (var) = var_type;
+		TREE_TYPE (TREE_OPERAND (op, 0))
+		  = build_pointer_type (var_type);
+	      }
+	  }
+
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
@@ -5499,6 +5716,7 @@  instantiate_cx_fn_r (tree *tp, int *walk
 
   return NULL_TREE;
 }
+
 static void
 instantiate_constexpr_fns (tree t)
 {
@@ -5507,17 +5725,36 @@  instantiate_constexpr_fns (tree t)
   input_location = loc;
 }
 
+/* Look for heap variables in the expression *TP.  */
+
+static tree
+find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
+{
+  if (VAR_P (*tp)
+      && (DECL_NAME (*tp) == heap_uninit_identifier
+	  || DECL_NAME (*tp) == heap_identifier
+	  || DECL_NAME (*tp) == heap_deleted_identifier))
+    return *tp;
+
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+  return NULL_TREE;
+}
+
 /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
    STRICT has the same sense as for constant_value_1: true if we only allow
    conforming C++ constant expressions, or false if we want a constant value
    even if it doesn't conform.
    MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
-   per P0595 even when ALLOW_NON_CONSTANT is true.  */
+   per P0595 even when ALLOW_NON_CONSTANT is true.
+   CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable.
+   OBJECT must be non-NULL in that case.  */
 
 static tree
 cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 				  bool strict = true,
 				  bool manifestly_const_eval = false,
+				  bool constexpr_dtor = false,
 				  tree object = NULL_TREE)
 {
   auto_timevar time (TV_CONSTEXPR);
@@ -5525,16 +5762,23 @@  cxx_eval_outermost_constant_expr (tree t
   bool non_constant_p = false;
   bool overflow_p = false;
   hash_map<tree,tree> map;
+  auto_vec<tree, 16> heap_vars;
   HOST_WIDE_INT constexpr_ctx_count = 0;
 
   constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
-			&constexpr_ctx_count, allow_non_constant, strict,
-			manifestly_const_eval || !allow_non_constant };
+			&constexpr_ctx_count, &heap_vars, allow_non_constant,
+			strict, manifestly_const_eval || !allow_non_constant };
 
   tree type = initialized_type (t);
   tree r = t;
   if (VOID_TYPE_P (type))
-    return t;
+    {
+      if (TREE_CODE (t) == BIND_EXPR && constexpr_dtor)
+	/* Used for destructors of array elements.  */
+	type = TREE_TYPE (object);
+      else
+	return t;
+    }
   if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
     {
       /* In C++14 an NSDMI can participate in aggregate initialization,
@@ -5544,8 +5788,22 @@  cxx_eval_outermost_constant_expr (tree t
 	 update ctx.values for the VAR_DECL.  We use the same strategy
 	 for C++11 constexpr constructors that refer to the object being
 	 initialized.  */
-      ctx.ctor = build_constructor (type, NULL);
-      CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+      if (constexpr_dtor)
+	{
+	  gcc_assert (object && VAR_P (object));
+	  gcc_assert (DECL_DECLARED_CONSTEXPR_P (object));
+	  gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object));
+	  ctx.ctor = unshare_expr (DECL_INITIAL (object));
+	  TREE_READONLY (ctx.ctor) = false;
+	  /* Temporarily force decl_really_constant_value to return false
+	     for it, we want to use ctx.ctor for the current value instead.  */
+	  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false;
+	}
+      else
+	{
+	  ctx.ctor = build_constructor (type, NULL);
+	  CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
+	}
       if (!object)
 	{
 	  if (TREE_CODE (t) == TARGET_EXPR)
@@ -5569,13 +5827,15 @@  cxx_eval_outermost_constant_expr (tree t
   r = cxx_eval_constant_expression (&ctx, r,
 				    false, &non_constant_p, &overflow_p);
 
-  verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  if (!constexpr_dtor)
+    verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+  else
+    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
 
   /* Mutable logic is a bit tricky: we want to allow initialization of
      constexpr variables with mutable members, but we can't copy those
      members to another constexpr variable.  */
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_MUTABLE_POISON (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5583,8 +5843,7 @@  cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
-  if (TREE_CODE (r) == CONSTRUCTOR
-      && CONSTRUCTOR_NO_CLEARING (r))
+  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -5593,6 +5852,32 @@  cxx_eval_outermost_constant_expr (tree t
       non_constant_p = true;
     }
 
+  if (!heap_vars.is_empty ())
+    {
+      tree heap_var = cp_walk_tree_without_duplicates (&r, find_heap_var_refs,
+						       NULL);
+      unsigned int i;
+      if (heap_var)
+	{
+	  if (!allow_non_constant && !non_constant_p)
+	    error_at (DECL_SOURCE_LOCATION (heap_var),
+		      "%qE is not a constant expression because it refers to "
+		      "a result of %<operator new%>", t);
+	  r = t;
+	  non_constant_p = true;
+	}
+      FOR_EACH_VEC_ELT (heap_vars, i, heap_var)
+	if (DECL_NAME (heap_var) != heap_deleted_identifier)
+	  {
+	    if (!allow_non_constant && !non_constant_p)
+	      error_at (DECL_SOURCE_LOCATION (heap_var),
+			"%qE is not a constant expression because allocated "
+			"storage has not been deallocated", t);
+	    r = t;
+	    non_constant_p = true;
+	  }
+    }
+
   /* Technically we should check this for all subexpressions, but that
      runs into problems with our internal representation of pointer
      subtraction and the 5.19 rules are still in flux.  */
@@ -5618,6 +5903,8 @@  cxx_eval_outermost_constant_expr (tree t
 
   if (non_constant_p && !allow_non_constant)
     return error_mark_node;
+  else if (constexpr_dtor)
+    return r;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
       /* If __builtin_is_constant_evaluated () was evaluated to true
@@ -5625,7 +5912,7 @@  cxx_eval_outermost_constant_expr (tree t
 	 punt.  */
       if (manifestly_const_eval)
 	return cxx_eval_outermost_constant_expr (t, true, strict,
-						 false, object);
+						 false, false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
 	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
 	 it to be set if it is invariant address, even when it is not
@@ -5653,7 +5940,7 @@  cxx_eval_outermost_constant_expr (tree t
 	return t;
       else if (TREE_CODE (t) != CONSTRUCTOR)
 	{
-	  r = get_target_expr (r);
+	  r = get_target_expr_sfinae (r, tf_warning_or_error | tf_no_cleanup);
 	  TREE_CONSTANT (r) = true;
 	}
     }
@@ -5668,7 +5955,16 @@  cxx_eval_outermost_constant_expr (tree t
 tree
 cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
+  return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl);
+}
+
+/* Like cxx_constant_value, but used for evaluation of constexpr destructors
+   of constexpr variables.  The actual initializer of DECL is not modified.  */
+
+void
+cxx_constant_dtor (tree t, tree decl)
+{
+  cxx_eval_outermost_constant_expr (t, false, true, true, true, decl);
 }
 
 /* Helper routine for fold_simple function.  Either return simplified
@@ -5772,14 +6068,14 @@  maybe_constant_value (tree t, tree decl,
     return t;
 
   if (manifestly_const_eval)
-    return cxx_eval_outermost_constant_expr (t, true, true, true, decl);
+    return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl);
 
   if (cv_cache == NULL)
     cv_cache = hash_map<tree, tree>::create_ggc (101);
   if (tree *cached = cv_cache->get (t))
     return *cached;
 
-  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
+  r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl);
   gcc_checking_assert (r == t
 		       || CONVERT_EXPR_P (t)
 		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
@@ -5841,7 +6137,7 @@  fold_non_dependent_expr_template (tree t
 
       tree r = cxx_eval_outermost_constant_expr (t, true, true,
 						 manifestly_const_eval,
-						 NULL_TREE);
+						 false, NULL_TREE);
       /* cp_tree_equal looks through NOPs, so allow them.  */
       gcc_checking_assert (r == t
 			   || CONVERT_EXPR_P (t)
@@ -5945,7 +6241,7 @@  maybe_constant_init_1 (tree t, tree decl
   else
     t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
 					  /*strict*/false,
-					  manifestly_const_eval, decl);
+					  manifestly_const_eval, false, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -6239,7 +6535,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)
 		      {
@@ -6468,6 +6769,9 @@  potential_constant_expression_1 (tree t,
 	goto fail;
       if (!RECUR (TREE_OPERAND (t, 0), any))
 	return false;
+      /* Just ignore clobbers.  */
+      if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
+	return true;
       if (!RECUR (TREE_OPERAND (t, 1), rval))
 	return false;
       return true;
@@ -6937,7 +7241,7 @@  potential_constant_expression_1 (tree t,
      return true;
 
     case COND_EXPR:
-      if (COND_EXPR_IS_VEC_DELETE (t))
+      if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx2a)
 	{
 	  if (flags & tf_error)
 	    error_at (loc, "%<delete[]%> is not a constant expression");
@@ -6983,6 +7287,12 @@  potential_constant_expression_1 (tree t,
       return true;
 
     case CLEANUP_STMT:
+      if (!RECUR (CLEANUP_BODY (t), any))
+	return false;
+      if (!CLEANUP_EH_ONLY (t) && !RECUR (CLEANUP_EXPR (t), any))
+	return false;
+      return true;
+
     case EMPTY_CLASS_EXPR:
     case PREDICT_EXPR:
       return false;
--- gcc/cp/decl.c.jj	2019-09-26 21:34:21.673916726 +0200
+++ gcc/cp/decl.c	2019-09-27 18:25:40.898066032 +0200
@@ -4146,6 +4146,9 @@  initialize_predefined_identifiers (void)
     {"value", &value_identifier, cik_normal},
     {"_FUN", &fun_identifier, cik_normal},
     {"__closure", &closure_identifier, cik_normal},
+    {"heap uninit", &heap_uninit_identifier, cik_normal},
+    {"heap ", &heap_identifier, cik_normal},
+    {"heap deleted", &heap_deleted_identifier, cik_normal},
     {NULL, NULL, cik_normal}
   };
 
@@ -7430,7 +7433,11 @@  cp_finish_decl (tree decl, tree init, bo
 	    TREE_READONLY (decl) = 1;
 
 	  /* Likewise if it needs destruction.  */
-	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
+	      && (cxx_dialect < cxx2a
+		  || !DECL_DECLARED_CONSTEXPR_P (decl)
+		  || !type_has_constexpr_destructor
+						(strip_array_types (type))))
 	    TREE_READONLY (decl) = 0;
 	}
 
@@ -8319,6 +8326,27 @@  register_dtor_fn (tree decl)
   if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
     return void_node;
 
+  if (cxx_dialect >= cxx2a
+      && DECL_DECLARED_CONSTEXPR_P (decl)
+      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
+      && type_has_constexpr_destructor (strip_array_types (type)))
+    {
+      int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR;
+      tree addr, call;
+
+      if (TREE_CODE (type) == ARRAY_TYPE)
+	addr = decl;
+      else
+	addr = build_address (decl);
+
+      call = build_delete (TREE_TYPE (addr), addr,
+			   sfk_complete_destructor, flags, 0,
+			   tf_warning_or_error);
+      if (call != error_mark_node)
+	cxx_constant_dtor (call, decl);
+      return void_node;
+    }
+
   /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
      "__aeabi_atexit"), and DECL is a class object, we can just pass the
      destructor to "__cxa_atexit"; we don't have to build a temporary
@@ -8432,11 +8460,15 @@  register_dtor_fn (tree decl)
 static void
 expand_static_init (tree decl, tree init)
 {
+  tree type = TREE_TYPE (decl);
   gcc_assert (VAR_P (decl));
   gcc_assert (TREE_STATIC (decl));
 
   /* Some variables require no dynamic initialization.  */
-  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
+  if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))
+      || (cxx_dialect >= cxx2a
+	  && DECL_DECLARED_CONSTEXPR_P (decl)
+	  && type_has_constexpr_destructor (strip_array_types (type))))
     {
       /* Make sure the destructor is callable.  */
       cxx_maybe_build_cleanup (decl, tf_warning_or_error);
@@ -12702,12 +12734,13 @@  grokdeclarator (const cp_declarator *dec
 			      "a destructor cannot be %<concept%>");
                     return error_mark_node;
                   }
-                if (constexpr_p)
-                  {
-                    error_at (declspecs->locations[ds_constexpr],
-			      "a destructor cannot be %<constexpr%>");
-                    return error_mark_node;
-                  }
+		if (constexpr_p && cxx_dialect < cxx2a)
+		  {
+		    error_at (declspecs->locations[ds_constexpr],
+			      "%<constexpr%> destructors only available"
+			      " with %<-std=c++2a%> or %<-std=gnu++2a%>");
+		    return error_mark_node;
+		  }
 	      }
 	    else if (sfk == sfk_constructor && friendp && !ctype)
 	      {
@@ -12744,10 +12777,11 @@  grokdeclarator (const cp_declarator *dec
 	      }
 
 	    /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node.  */
-	    function_context = (ctype != NULL_TREE) ?
-	      decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE;
-	    publicp = (! friendp || ! staticp)
-	      && function_context == NULL_TREE;
+	    function_context
+	      = (ctype != NULL_TREE
+		 ? decl_function_context (TYPE_MAIN_DECL (ctype)) : NULL_TREE);
+	    publicp = ((! friendp || ! staticp)
+		       && function_context == NULL_TREE);
 
 	    decl = grokfndecl (ctype, type,
 			       TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR
@@ -16752,6 +16786,12 @@  cxx_maybe_build_cleanup (tree decl, tsub
 	cleanup = error_mark_node;
       else if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
 	/* Discard the call.  */;
+      else if (cxx_dialect >= cxx2a
+	       && VAR_P (decl)
+	       && DECL_DECLARED_CONSTEXPR_P (decl)
+	       && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
+	       && type_has_constexpr_destructor (strip_array_types (type)))
+	cxx_constant_dtor (call, decl);
       else if (cleanup)
 	cleanup = cp_build_compound_expr (cleanup, call, complain);
       else
--- gcc/cp/init.c.jj	2019-09-26 21:34:21.598917851 +0200
+++ gcc/cp/init.c	2019-09-27 19:15:56.473042238 +0200
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "stor-layout.h"
 
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
@@ -3332,6 +3333,48 @@  build_new_1 (vec<tree, va_gc> **placemen
 	}
     }
 
+  tree alloc_call_call = extract_call_expr (alloc_call);
+  tree alloc_call_fndecl = NULL_TREE;
+  if (alloc_call_call != error_mark_node)
+    alloc_call_fndecl = cp_get_callee_fndecl_nofold (alloc_call_call);
+  if (array_p
+      && cxx_dialect >= cxx2a
+      && (current_function_decl == NULL_TREE
+	  || DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+      && alloc_call_fndecl
+      && IDENTIFIER_NEW_OP_P (DECL_NAME (alloc_call_fndecl))
+      && CP_DECL_CONTEXT (alloc_call_fndecl) == global_namespace)
+    {
+      /* Help the constexpr code to find the right type for the heap variable
+	 by adding a NOP_EXPR around alloc_call.
+	 If not using cookies, use array type, otherwise structure with
+	 two array types.  */
+      tree atype = build_cplus_array_type (elt_type, NULL_TREE);
+      if (cookie_size)
+	{
+	  gcc_assert (tree_fits_uhwi_p (cookie_size));
+	  unsigned HOST_WIDE_INT sz = tree_to_uhwi (cookie_size);
+	  sz /= int_size_in_bytes (sizetype);
+	  tree atype2 = build_index_type (size_int (sz - 1));
+	  atype2 = build_cplus_array_type (sizetype, atype2);
+	  tree atype3 = cxx_make_type (RECORD_TYPE);
+	  tree fld1 = build_decl (input_location, FIELD_DECL, NULL_TREE,
+				  atype2);
+	  tree fld2 = build_decl (input_location, FIELD_DECL, NULL_TREE,
+				  atype);
+	  DECL_FIELD_CONTEXT (fld1) = atype3;
+	  DECL_FIELD_CONTEXT (fld2) = atype3;
+	  DECL_ARTIFICIAL (fld1) = true;
+	  DECL_ARTIFICIAL (fld2) = true;
+	  TYPE_FIELDS (atype3) = fld1;
+	  DECL_CHAIN (fld1) = fld2;
+	  layout_type (atype3);
+	  atype = atype3;
+	}
+      pointer_type = build_pointer_type (atype);
+      alloc_call = build_nop (pointer_type, alloc_call);
+    }
+
   /* In the simple case, we can stop now.  */
   pointer_type = build_pointer_type (type);
   if (!cookie_size && !is_initialized)
@@ -3905,17 +3948,11 @@  build_vec_delete_1 (tree base, tree maxi
 			     fold_convert (sizetype, maxindex));
 
   tbase = create_temporary_var (ptype);
-  tbase_init
-    = cp_build_modify_expr (input_location, tbase, NOP_EXPR,
-			    fold_build_pointer_plus_loc (input_location,
-							 fold_convert (ptype,
-								       base),
-							 virtual_size),
-			    complain);
-  if (tbase_init == error_mark_node)
-    return error_mark_node;
-  controller = build3 (BIND_EXPR, void_type_node, tbase,
-		       NULL_TREE, NULL_TREE);
+  DECL_INITIAL (tbase)
+    = fold_build_pointer_plus_loc (input_location, fold_convert (ptype, base),
+				   virtual_size);
+  tbase_init = build_stmt (input_location, DECL_EXPR, tbase);
+  controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE);
   TREE_SIDE_EFFECTS (controller) = 1;
 
   body = build1 (EXIT_EXPR, void_type_node,
--- gcc/cp/method.c.jj	2019-09-26 21:34:21.331921851 +0200
+++ gcc/cp/method.c	2019-09-27 18:25:40.902065972 +0200
@@ -30,6 +30,7 @@  along with GCC; see the file COPYING3.
 #include "cgraph.h"
 #include "varasm.h"
 #include "toplev.h"
+#include "intl.h"
 #include "common/common-target.h"
 
 static void do_build_copy_assign (tree);
@@ -1237,12 +1238,24 @@  is_xible (enum tree_code code, tree to,
   return !!expr;
 }
 
+/* Categorize various special_function_kinds.  */
+#define SFK_CTOR_P(sfk) \
+  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
+#define SFK_DTOR_P(sfk) \
+  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
+#define SFK_ASSIGN_P(sfk) \
+  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
+#define SFK_COPY_P(sfk) \
+  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
+#define SFK_MOVE_P(sfk) \
+  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
+
 /* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
    DELETED_P or give an error message MSG with argument ARG.  */
 
 static void
-process_subob_fn (tree fn, tree *spec_p, bool *trivial_p,
-		  bool *deleted_p, bool *constexpr_p,
+process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p,
+		  bool *trivial_p, bool *deleted_p, bool *constexpr_p,
 		  bool diag, tree arg, bool dtor_from_ctor = false)
 {
   if (!fn || fn == error_mark_node)
@@ -1283,24 +1296,15 @@  process_subob_fn (tree fn, tree *spec_p,
       if (diag)
 	{
 	  inform (DECL_SOURCE_LOCATION (fn),
-		  "defaulted constructor calls non-%<constexpr%> %qD", fn);
+		  SFK_DTOR_P (sfk)
+		  ? G_("destructor calls non-%<constexpr%> %qD")
+		  : G_("defaulted constructor calls non-%<constexpr%> %qD"),
+		  fn);
 	  explain_invalid_constexpr_fn (fn);
 	}
     }
 }
 
-/* Categorize various special_function_kinds.  */
-#define SFK_CTOR_P(sfk) \
-  ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
-#define SFK_DTOR_P(sfk) \
-  ((sfk) == sfk_destructor || (sfk) == sfk_virtual_destructor)
-#define SFK_ASSIGN_P(sfk) \
-  ((sfk) == sfk_copy_assignment || (sfk) == sfk_move_assignment)
-#define SFK_COPY_P(sfk) \
-  ((sfk) == sfk_copy_constructor || (sfk) == sfk_copy_assignment)
-#define SFK_MOVE_P(sfk) \
-  ((sfk) == sfk_move_constructor || (sfk) == sfk_move_assignment)
-
 /* Subroutine of synthesized_method_walk to allow recursion into anonymous
    aggregates.  If DTOR_FROM_CTOR is true, we're walking subobject destructors
    called from a synthesized constructor, in which case we don't consider
@@ -1318,8 +1322,7 @@  walk_field_subobs (tree fields, special_
     {
       tree mem_type, argtype, rval;
 
-      if (TREE_CODE (field) != FIELD_DECL
-	  || DECL_ARTIFICIAL (field))
+      if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
 	continue;
 
       /* Variant members only affect deletedness.  In particular, they don't
@@ -1457,7 +1460,7 @@  walk_field_subobs (tree fields, special_
 
       rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
 
-      process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+      process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 			constexpr_p, diag, field, dtor_from_ctor);
     }
 }
@@ -1510,23 +1513,23 @@  synthesized_method_base_walk (tree binfo
       && DECL_CONTEXT (*inheriting_ctor) == DECL_CONTEXT (rval))
     *inheriting_ctor = DECL_CLONED_FUNCTION (rval);
 
-  process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+  process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p,
 		    constexpr_p, diag, BINFO_TYPE (base_binfo));
-  if (SFK_CTOR_P (sfk) &&
-      (!BINFO_VIRTUAL_P (base_binfo)
-       || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
+  if (SFK_CTOR_P (sfk)
+      && (!BINFO_VIRTUAL_P (base_binfo)
+	  || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (BINFO_TYPE (base_binfo))))
     {
       /* In a constructor we also need to check the subobject
 	 destructors for cleanup of partially constructed objects.  */
       tree dtor = locate_fn_flags (base_binfo, complete_dtor_identifier,
 				   NULL_TREE, flags,
 				   diag ? tf_warning_or_error : tf_none);
-	  /* Note that we don't pass down trivial_p; the subobject
-	     destructors don't affect triviality of the constructor.  Nor
-	     do they affect constexpr-ness (a constant expression doesn't
-	     throw) or exception-specification (a throw from one of the
-	     dtors would be a double-fault).  */
-      process_subob_fn (dtor, NULL, NULL, deleted_p, NULL, false,
+      /* Note that we don't pass down trivial_p; the subobject
+	 destructors don't affect triviality of the constructor.  Nor
+	 do they affect constexpr-ness (a constant expression doesn't
+	 throw) or exception-specification (a throw from one of the
+	 dtors would be a double-fault).  */
+      process_subob_fn (dtor, sfk, NULL, NULL, deleted_p, NULL, false,
 			BINFO_TYPE (base_binfo), /*dtor_from_ctor*/true);
     }
 
@@ -1608,7 +1611,8 @@  synthesized_method_walk (tree ctype, spe
 	member is a constexpr function.  */
   if (constexpr_p)
     *constexpr_p = (SFK_CTOR_P (sfk)
-		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14));
+		    || (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
+		    || (SFK_DTOR_P (sfk) && cxx_dialect >= cxx2a));
 
   bool expected_trivial = type_has_trivial_fn (ctype, sfk);
   if (trivial_p)
@@ -1704,8 +1708,8 @@  synthesized_method_walk (tree ctype, spe
   else if (vec_safe_is_empty (vbases))
     /* No virtual bases to worry about.  */;
   else if (ABSTRACT_CLASS_TYPE_P (ctype) && cxx_dialect >= cxx14
-	   /* DR 1658 specifis that vbases of abstract classes are
-	      ignored for both ctors and dtors.  Except DR 2338
+	   /* DR 1658 specifies that vbases of abstract classes are
+	      ignored for both ctors and dtors.  Except DR 2336
 	      overrides that skipping when determing the eh-spec of a
 	      virtual destructor.  */
 	   && sfk != sfk_virtual_destructor)
@@ -2046,7 +2050,8 @@  implicitly_declare_fn (special_function_
     constexpr_p = false;
   /* A trivial copy/move constructor is also a constexpr constructor,
      unless the class has virtual bases (7.1.5p4).  */
-  else if (trivial_p && cxx_dialect >= cxx11
+  else if (trivial_p
+	   && cxx_dialect >= cxx11
 	   && (kind == sfk_copy_constructor
 	       || kind == sfk_move_constructor)
 	   && !CLASSTYPE_VBASECLASSES (type))
--- gcc/cp/typeck2.c.jj	2019-09-26 21:34:26.299847376 +0200
+++ gcc/cp/typeck2.c	2019-09-27 18:25:40.899066017 +0200
@@ -902,7 +902,13 @@  store_init_value (tree decl, tree init,
 	    value = oldval;
 	}
     }
-  value = cp_fully_fold_init (value);
+  /* Don't fold initializers of automatic variables in constexpr functions,
+     that might fold away something that needs to be diagnosed at constexpr
+     evaluation time.  */
+  if (!current_function_decl
+      || !DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+      || TREE_STATIC (decl))
+    value = cp_fully_fold_init (value);
 
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C.jj	2016-03-05 07:46:49.554128302 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C	2019-09-27 20:26:06.317971681 +0200
@@ -5,8 +5,9 @@  struct A { ~A(); };
 constexpr int f(int i) { return i; }
 constexpr int g(A* ap)
 {
-  return f((delete[] ap, 42)); // { dg-message "" }
+  return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
 }
 
 A a;
 constexpr int i = g(&a);	// { dg-error "" }
+				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
--- gcc/testsuite/g++.dg/cpp0x/locations1.C.jj	2019-09-26 21:34:22.205908750 +0200
+++ gcc/testsuite/g++.dg/cpp0x/locations1.C	2019-09-27 18:25:41.346059343 +0200
@@ -11,7 +11,7 @@  struct S
 {
   virtual S();  // { dg-error "3:constructors cannot be declared .virtual." }
   constexpr int s = 1;  // { dg-error "3:non-static data member .s. declared .constexpr." }
-  constexpr ~S();  // { dg-error "3:a destructor cannot be .constexpr." }
+  constexpr ~S();  // { dg-error "3:'constexpr' destructors only available with" "" { target c++17_down } }
 };
 
 typedef constexpr int my_int;  // { dg-error "9:.constexpr. cannot appear in a typedef declaration" }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-new.C.jj	2019-09-26 21:34:22.281907610 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-new.C	2019-09-27 18:25:41.303059984 +0200
@@ -4,7 +4,7 @@  constexpr int *f4(bool b) {
   if (b) {
     return nullptr;
   } else {
-    return new int{42}; // { dg-error "call to non-.constexpr." }
+    return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } }
   }
 }
 static_assert(f4(true) == nullptr, "");
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C.jj	2019-09-27 18:25:41.320059731 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor1.C	2019-09-27 18:25:41.320059731 +0200
@@ -0,0 +1,9 @@ 
+// P0784R7
+// { dg-do compile { target c++11 } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}	// { dg-error "'constexpr' destructors only available with" "" { target c++17_down } }
+  int s;
+};
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C.jj	2019-09-27 18:25:41.303059984 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor2.C	2019-09-27 18:25:41.303059984 +0200
@@ -0,0 +1,66 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : r (4), s (3) { --r; s -= 2; }
+  constexpr ~S () { if (s == 1) s = 0; else asm (""); if (s == 0 && r == 3) r = 0; else asm (""); }
+  int r, s;
+};
+struct T : public S
+{
+  constexpr T () : t (2) {}
+  int t;
+  S u;
+};
+struct U : public S
+{
+  constexpr U (int x) : u (x) {}
+  constexpr ~U () = default;
+  int u;
+  S v;
+};
+
+constexpr S a;
+constexpr T b;
+constexpr U c = 3;
+static_assert (a.s == 1 && a.r == 3);
+static_assert (b.s == 1 && b.r == 3 && b.t == 2 && b.u.s == 1 && b.u.r == 3);
+static_assert (c.s == 1 && c.r == 3 && c.u == 3 && c.v.s == 1 && c.v.r == 3);
+
+void
+foo ()
+{
+  static constexpr S d;
+  static constexpr T e;
+  static constexpr U f = 4;
+  static_assert (d.s == 1 && d.r == 3);
+  static_assert (e.s == 1 && e.r == 3 && e.t == 2 && e.u.s == 1 && e.u.r == 3);
+  static_assert (f.s == 1 && f.r == 3 && f.u == 4 && f.v.s == 1 && f.v.r == 3);
+  if (1)
+    {
+      constexpr S g;
+      constexpr T h;
+      constexpr U i = 5;
+      static_assert (g.s == 1 && g.r == 3);
+      static_assert (h.s == 1 && h.r == 3 && h.t == 2 && h.u.s == 1 && h.u.r == 3);
+      static_assert (i.s == 1 && i.r == 3 && i.u == 5 && i.v.s == 1 && i.v.r == 3);
+    }
+}
+
+constexpr bool
+bar ()
+{
+  S j;
+  T k;
+  U l = 6;
+  if (j.s != 1 || j.r != 3)
+    return false;
+  if (k.s != 1 || k.r != 3 || k.t != 2 || k.u.s != 1 || k.u.r != 3)
+    return false;
+  if (l.s != 1 || l.r != 3 || l.u != 6 || l.v.s != 1 || l.v.r != 3)
+    return false;
+  return true;
+}
+
+static_assert (bar ());
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2019-09-27 18:25:41.303059984 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2019-09-27 18:25:41.303059984 +0200
@@ -0,0 +1,185 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) {}
+  constexpr ~S () {}
+  int s;
+};
+struct T	// { dg-message "'T' is not literal because" }
+{		// { dg-message "'T' does not have 'constexpr' destructor" "" { target *-*-* } .-1 }
+  constexpr T () : t (0) {}
+  ~T () {}	// { dg-message "destructor calls non-'constexpr' 'T::~T\\(\\)'" }
+  int t;
+};
+struct U : public S
+{
+  constexpr U () : u (0) {}
+  constexpr ~U () = default;	// { dg-error "explicitly defaulted function 'constexpr U::~U\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int u;
+  T t;
+};
+struct V : virtual public S
+{
+  V () : v (0) {}
+  constexpr ~V () = default;	// { dg-error "explicitly defaulted function 'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int v;
+};
+struct W0
+{
+  constexpr W0 () : w (0) {}
+  constexpr W0 (int x) : w (x) {}
+  constexpr ~W0 () { if (w == 5) asm (""); w = 3; }
+  int w;
+};
+struct W1
+{
+  constexpr W1 () : w (0) {}
+  constexpr W1 (int x) : w (x) {}
+  constexpr ~W1 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W2
+{
+  constexpr W2 () : w (0) {}
+  constexpr W2 (int x) : w (x) {}
+  constexpr ~W2 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W3
+{
+  constexpr W3 () : w (0) {}
+  constexpr W3 (int x) : w (x) {}
+  constexpr ~W3 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W4
+{
+  constexpr W4 () : w (0) {}
+  constexpr W4 (int x) : w (x) {}
+  constexpr ~W4 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W5
+{
+  constexpr W5 () : w (0) {}
+  constexpr W5 (int x) : w (x) {}
+  constexpr ~W5 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W6
+{
+  constexpr W6 () : w (0) {}
+  constexpr W6 (int x) : w (x) {}
+  constexpr ~W6 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W7
+{
+  constexpr W7 () : w (0) {}
+  constexpr W7 (int x) : w (x) {}
+  constexpr ~W7 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct W8
+{
+  constexpr W8 () : w (0) {}
+  constexpr W8 (int x) : w (x) {}
+  constexpr ~W8 () { if (w == 5) asm (""); w = 3; }	// { dg-error "inline assembly is not a constant expression" }
+							// { dg-message "only unevaluated inline assembly is allowed in a 'constexpr' function" "" { target *-*-* } .-1 }
+  int w;
+};
+struct X : public T
+{
+  constexpr X () : x (0) {}
+  constexpr ~X () = default;	// { dg-error "explicitly defaulted function 'constexpr X::~X\\(\\)' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr'" }
+  int x;
+};
+constexpr S s;
+constexpr T t;	// { dg-error "the type 'const T' of 'constexpr' variable 't' is not literal" }
+constexpr W0 w1;
+constexpr W0 w2 = 12;
+constexpr W1 w3 = 5;	// { dg-message "in 'constexpr' expansion of" }
+constexpr W0 w4[3] = { 1, 2, 3 };
+constexpr W2 w5[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f1 ()
+{
+  constexpr S s2;
+  constexpr W0 w6;
+  constexpr W0 w7 = 12;
+  constexpr W3 w8 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w9[3] = { 1, 2, 3 };
+  constexpr W4 w10[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+}
+
+constexpr int
+f2 ()
+{
+  constexpr S s3;
+  constexpr W0 w11;
+  constexpr W0 w12 = 12;
+  constexpr W5 w13 = 5;	// { dg-message "in 'constexpr' expansion of" }
+  constexpr W0 w14[3] = { 1, 2, 3 };
+  constexpr W6 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int
+f3 ()
+{
+  S s3;
+  W0 w11;
+  W0 w12 = 12;
+  W0 w14[3] = { 1, 2, 3 };
+  return 0;
+}
+
+constexpr int x3 = f3 ();
+
+constexpr int
+f4 ()
+{
+  W7 w13 = 5;
+  return 0;
+}
+
+constexpr int x4 = f4 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr int
+f5 ()
+{
+  W8 w15[3] = { 4, 5, 6 };	// { dg-message "in 'constexpr' expansion of" }
+  return 0;
+}
+
+constexpr int x5 = f5 ();	// { dg-message "in 'constexpr' expansion of" }
+
+void
+f6 ()
+{
+  constexpr T t2;	// { dg-error "the type 'const T' of 'constexpr' variable 't2' is not literal" }
+}
+
+constexpr int
+f7 ()
+{
+  constexpr T t3;	// { dg-error "the type 'const T' of 'constexpr' variable 't3' is not literal" }
+  return 0;
+}
+
+constexpr int
+f8 ()
+{
+  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
+  return 0;
+}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C.jj	2019-09-27 18:25:41.320059731 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new1.C	2019-09-27 20:06:50.047298452 +0200
@@ -0,0 +1,39 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S { constexpr S () : s (5) {} constexpr S (int x) : s (x) {} int s; };
+
+constexpr bool
+foo ()
+{
+  int r = 0;
+  S *p = new S ();
+  p->s += 3;
+  r += p->s;
+  delete p;
+  p = new S (12);
+  p->s = p->s * 2;
+  r += p->s;
+  delete p;
+  int *q = new int;
+  *q = 25;
+  r += *q;
+  delete q;
+  q = new int (1);
+  r += *q;
+  if (!q)
+    return false;
+  delete q;
+  q = new int[5]{1,2,3,4,5};
+  r += q[0] + q[4];
+  delete[] q;
+  q = new int[4];
+  q[0] = 6;
+  q[1] = 7;
+  q[3] = 8;
+  r += q[0] + q[1] + q[3];
+  delete[] q;
+  return r == 5 + 3 + 2 * 12 + 25 + 1 + 1 + 5 + 6 + 7 + 8;
+}
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C.jj	2019-09-27 18:25:41.320059731 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new2.C	2019-09-27 20:23:16.107524585 +0200
@@ -0,0 +1,21 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+template <int N>
+constexpr bool
+foo (const char (&x)[N])
+{
+  int **p = new int *[N];
+  for (int i = 0; i < N; i++)
+    p[i] = new int (x[i]);
+  for (int i = 0; i < N; i++)
+    if (*p[i] != x[i])
+      return false;
+  for (int i = 0; i < N; ++i)
+    delete p[i];
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ("foobar");
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C.jj	2019-09-27 18:25:41.320059731 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C	2019-09-27 18:25:41.320059731 +0200
@@ -0,0 +1,63 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+constexpr int *
+f1 ()
+{
+  return new int (2);		// { dg-error "is not a constant expression because it refers to a result of 'operator new'" }
+}
+
+constexpr auto v1 = f1 ();
+
+constexpr bool
+f2 ()
+{
+  int *p = new int (3);		// { dg-error "is not a constant expression because allocated storage has not been deallocated" }
+  return false;
+}
+
+constexpr auto v2 = f2 ();
+
+constexpr bool
+f3 ()
+{
+  int *p = new int (3);
+  int *q = p;
+  delete p;
+  delete q;			// { dg-error "deallocation of already deallocated storage" }
+  return false;
+}
+
+constexpr auto v3 = f3 ();	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f4 (int *p)
+{
+  delete p;			// { dg-error "call to non-'constexpr' function" }
+  return false;
+}
+
+int q;
+constexpr auto v4 = f4 (&q);	// { dg-message "in 'constexpr' expansion of" }
+
+constexpr bool
+f5 ()
+{
+  int *p = new int;		// { dg-message "allocated here" }
+  return *p == 1;
+}
+
+constexpr auto v5 = f5 ();	// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
+
+constexpr bool
+f6 ()
+{
+  int *p = new int (2);		// { dg-message "allocated here" }
+  int *q = p;
+  delete p;
+  return *q == 2;
+}
+
+constexpr auto v6 = f6 ();	// { dg-error "use of allocated storage after deallocation in a constant expression" }
+				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1  }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C.jj	2019-09-27 18:25:41.319059745 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new4.C	2019-09-27 20:07:33.928641928 +0200
@@ -0,0 +1,29 @@ 
+// P0784R7
+// { dg-do compile { target c++2a } }
+
+struct S
+{
+  constexpr S () : s (0) { s++; }
+  constexpr S (int x) : s (x) { s += 2; }
+  constexpr ~S () { if (s != 35) asm (""); s = 5; }
+  int s;
+};
+
+constexpr bool
+foo ()
+{
+  S *p = new S (7);
+  if (p->s != 9) return false;
+  p->s = 35;
+  delete p;
+  p = new S[3] { 11, 13, 15 };
+  if (p[0].s != 13 || p[1].s != 15 || p[2].s != 17) return false;
+  p[0].s = 35;
+  p[2].s = 35;
+  p[1].s = 35;
+  delete[] p;
+  return true;
+}
+
+constexpr bool a = foo ();
+static_assert (a);
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj	2019-09-26 21:34:22.341906710 +0200
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2019-09-27 18:25:41.319059745 +0200
@@ -430,16 +430,34 @@ 
 
 // C++20 features
 
-#if __cpp_conditional_explicit != 201806
-# error "__cpp_conditional_explicit != 201806"
+#ifndef __cpp_conditional_explicit
+#  error "__cpp_conditional_explicit"
+#elif __cpp_conditional_explicit != 201806
+#  error "__cpp_conditional_explicit != 201806"
 #endif
 
-#if __cpp_nontype_template_parameter_class != 201806
-# error "__cpp_nontype_template_parameter_class != 201806"
+#ifndef __cpp_nontype_template_parameter_class
+#  error "__cpp_nontype_template_parameter_class"
+#elif __cpp_nontype_template_parameter_class != 201806
+#  error "__cpp_nontype_template_parameter_class != 201806"
 #endif
 
-#if __cpp_impl_destroying_delete != 201806
-# error "__cpp_impl_destroying_delete != 201806"
+#ifndef __cpp_impl_destroying_delete
+#  error "__cpp_impl_destroying_delete"
+#elif __cpp_impl_destroying_delete != 201806
+#  error "__cpp_impl_destroying_delete != 201806"
+#endif
+
+#ifndef __cpp_constinit
+#  error "__cpp_constinit"
+#elif __cpp_constinit != 201907
+#  error "__cpp_constinit != 201907"
+#endif
+
+#ifndef __cpp_constexpr_dynamic_alloc
+#  error "__cpp_constexpr_dynamic_alloc"
+#elif __cpp_constexpr_dynamic_alloc != 201907
+#  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
 #ifdef __has_cpp_attribute
@@ -484,8 +502,6 @@ 
 #  error "__has_cpp_attribute"
 #endif
 
-// C++2A features:
-
 #ifndef __cpp_char8_t
 #  error "__cpp_char8_t"
 #elif __cpp_char8_t != 201811
--- gcc/testsuite/g++.dg/ext/is_literal_type3.C.jj	2019-09-27 18:25:40.904065942 +0200
+++ gcc/testsuite/g++.dg/ext/is_literal_type3.C	2019-09-27 18:25:40.904065942 +0200
@@ -0,0 +1,26 @@ 
+// { dg-do compile { target c++11 } }
+
+struct S {
+  constexpr S () : n{} { }
+  ~S () { n = 1; }
+  int n;
+};
+
+static_assert(!__is_literal_type(S), "");
+
+#ifdef __cpp_constexpr_dynamic_alloc
+struct T {
+  constexpr T () : n{} { }
+  constexpr ~T () { n = 1; }
+  int n;
+};
+
+static_assert(__is_literal_type(T), "");
+
+struct U : public T {
+  constexpr U () : u{} { }
+  int u;
+};
+
+static_assert(__is_literal_type(U), "");
+#endif