From patchwork Thu Oct 31 17:10:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1187613 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-512176-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Qm9+Hc4C"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="WG8GKTJi"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 473sKC5fJJz9sP3 for ; Fri, 1 Nov 2019 04:10:39 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :in-reply-to:content-type:content-transfer-encoding; q=dns; s= default; b=LPG78HlPl/pYvN53wsqh7p9cE8ygIdS+kDEJnOhAMyTfvVMaUWqzu yct3CcediG6KF2+Af1ax0/5ea8IVHhYIaz+5KfOV995+IxD3RCXmfUjr0AwiXqHD FLFlef93E43HqDv8LnbR6Vg/kEGMMc6zi2sIgpA5bVyn9RxHqm/XAQ= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :in-reply-to:content-type:content-transfer-encoding; s=default; bh=z+qxU+zGTVHkXBaMQG9xfmNmxEA=; b=Qm9+Hc4CNaXLo8CvDuAZirjOZ6Dn RTspyaOEEhs5EtdIYMug511K253LVcF5GOTnZJZdp0RNg/fRf2ju3/MNgtfE41j0 qGhHxe9VxOtc3OR3XV8sdN6kbGY5MBuNVCdn8y7CphHCo5J/RZ7sV6MJ+F3uQUAh gxAh1Tu9lYb/c4Y= Received: (qmail 100844 invoked by alias); 31 Oct 2019 17:10:30 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 100836 invoked by uid 89); 31 Oct 2019 17:10:29 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-7.6 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3 autolearn=ham version=3.3.1 spammy=20191005, 2019-10-05, spe X-HELO: us-smtp-1.mimecast.com Received: from us-smtp-delivery-1.mimecast.com (HELO us-smtp-1.mimecast.com) (205.139.110.120) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 31 Oct 2019 17:10:27 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1572541826; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Gm1FX25xNNjUR0BoDSIHUuymrIj07Yddpr/LKSdeFTg=; b=WG8GKTJiFA0B+SsDHLqlPgsDpiqy+BGzCjIKkG0YG72DSi7j3PEu6A00Cxr2NEwRKqad56 SmjdWLUbgMCEOYsUiDfgrS5dCjEGSojCiaYfFdBkcTtmAmEP14cuEfSZXdHlGRs/bAQzw7 4/u7bsTzCgdhbz6mrWEH94G3gesl1pQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-426-FdMOwznSMcW20qLgNC7PWw-1; Thu, 31 Oct 2019 13:10:24 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 9720B2A3 for ; Thu, 31 Oct 2019 17:10:23 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.36.118.135]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 3F3935DA32; Thu, 31 Oct 2019 17:10:23 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.15.2/8.15.2) with ESMTP id x9VHALjq031539; Thu, 31 Oct 2019 18:10:21 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.15.2/8.15.2/Submit) id x9VHAJbg031538; Thu, 31 Oct 2019 18:10:19 +0100 Date: Thu, 31 Oct 2019 18:10:19 +0100 From: Jakub Jelinek To: Jason Merrill Cc: Jonathan Wakely , gcc-patches@gcc.gnu.org Subject: [C++ PATCH] P0784R7 constexpr new further fix (PR c++/91369) Message-ID: <20191031171019.GM4650@tucnak> Reply-To: Jakub Jelinek References: <20191024091154.GL2116@tucnak> <20191030220529.GB4650@tucnak> MIME-Version: 1.0 In-Reply-To: <20191030220529.GB4650@tucnak> User-Agent: Mutt/1.11.3 (2019-02-01) X-Mimecast-Spam-Score: 0 Content-Disposition: inline X-IsSubscribed: yes On Wed, Oct 30, 2019 at 11:05:29PM +0100, Jakub Jelinek wrote: > > Looks like there used to be a TREE_CALLS_NEW flag in TREE_LANG_FLAG_1, but > > that flag is now free for CALL_EXPR. > > I'll try a CALL_EXPR flag first. TREE_LANG_FLAG_1 is also STMT_IS_FULL_EXPR_P, and while it is mostly guarded with STATEMENT_CODE_P checks, in add_stmt it is not. Are we sure that we never add_stmt a CALL_EXPR? I've picked up TREE_LANG_FLAG_2 instead which looked unused on CALL_EXPR. Below is the patch that rejects ::operator new and ::operator delete calls used directly by users (with the STL exteption), but doesn't try to diagnose what is discussed below. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? > > > Another thing is that even with that change, > > > std::allocator a; > > > auto p = a.allocate (1); > > > *p = 1; > > > a.deallocate (p, 1); > > > would be accepted during constexpr evaluation, because allocate already > > > has the cast which turns "heap uninit" variable into "heap " and assigns > > > it a type, so there is nothing that will prevent the store from succeeding. > > > > What's wrong with the store? > > If it is fine, the better. I'd still think that > struct S { constexpr S () : s (0) {}; int s; }; > std::allocator a; > auto p = a.allocate (1); > p->s = 1; > a.deallocate (p, 1); > would still be invalid though, because that type needs constructing and > the construction didn't happen in that case. Or: > struct S { constexpr S () : s (0) {}; int s; }; > std::allocator a; > auto p = a.allocate (1); > std::construct_at (p); > p->s++; // ok > std::destruct_at (p); > p->s = 1; // shouldn't this be an error? > a.deallocate (p, 1); > but I admit I haven't tried to back that up by some standard wording, just a > general principle that objects with TYPE_ADDRESSABLE types need to be > constructed first and destructed last and accesses to it are only valid in > between those in normal code and constexpr should flag any UB as errors. So, are objects in the heap with vacuous initialization ok and objects with non-trivial constructors not ok without std::construct_at to begin the lifetime? What about objects with trivial constructor but non-trivial constexpr destructor, is it ok to set them after std::destruct_at or direct p->~S (); invocation without an intervening std::construct_at? Also, shouldn't it be invalid if objects with non-trivial destructor are constructed using std::construct_at but not destructed before deallocate? 2019-10-31 Jakub Jelinek PR c++/91369 - Implement P0784R7: constexpr new * cp-tree.h (CALL_FROM_NEW_OR_DELETE_P): Define. * init.c (build_new_1, build_vec_delete_1, build_delete): Set CALL_FROM_NEW_OR_DELETE_P on the CALL_EXPR to allocator functions. * constexpr.c (is_std_allocator_allocate): Only allow global replaceable allocator functions if CALL_FROM_NEW_OR_DELETE_P or in std::allocate::{,de}allocate. (potential_constant_expression_1): Likewise. * g++.dg/cpp2a/constexpr-new6.C: New test. * g++.dg/cpp2a/constexpr-new7.C: New test. Jakub --- gcc/cp/cp-tree.h.jj 2019-10-31 08:10:14.515368058 +0100 +++ gcc/cp/cp-tree.h 2019-10-31 09:00:45.798418129 +0100 @@ -448,6 +448,7 @@ extern GTY(()) tree cp_global_trees[CPTI LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR) IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR) TINFO_VAR_DECLARED_CONSTINIT (in TEMPLATE_INFO) + CALL_FROM_NEW_OR_DELETE_P (in CALL_EXPR) 3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out). ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -3791,6 +3792,11 @@ struct GTY(()) lang_decl { should be performed at instantiation time. */ #define KOENIG_LOOKUP_P(NODE) TREE_LANG_FLAG_0 (CALL_EXPR_CHECK (NODE)) +/* In a CALL_EXPR, true for allocator calls from new or delete + expressions. */ +#define CALL_FROM_NEW_OR_DELETE_P(NODE) \ + TREE_LANG_FLAG_2 (CALL_EXPR_CHECK (NODE)) + /* True if the arguments to NODE should be evaluated in left-to-right order regardless of PUSH_ARGS_REVERSED. */ #define CALL_EXPR_ORDERED_ARGS(NODE) \ --- gcc/cp/init.c.jj 2019-10-05 09:36:39.249674512 +0200 +++ gcc/cp/init.c 2019-10-31 09:25:11.870698471 +0100 @@ -3404,6 +3404,10 @@ build_new_1 (vec **placemen } } + tree alloc_call_expr = extract_call_expr (alloc_call); + if (TREE_CODE (alloc_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (alloc_call_expr) = 1; + if (cookie_size) alloc_call = maybe_wrap_new_for_constexpr (alloc_call, elt_type, cookie_size); @@ -4046,6 +4050,10 @@ build_vec_delete_1 (tree base, tree maxi /*placement=*/NULL_TREE, /*alloc_fn=*/NULL_TREE, complain); + + tree deallocate_call_expr = extract_call_expr (deallocate_expr); + if (TREE_CODE (deallocate_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (deallocate_call_expr) = 1; } body = loop; @@ -4955,6 +4963,13 @@ build_delete (tree otype, tree addr, spe if (!deleting) return expr; + if (do_delete) + { + tree do_delete_call_expr = extract_call_expr (do_delete); + if (TREE_CODE (do_delete_call_expr) == CALL_EXPR) + CALL_FROM_NEW_OR_DELETE_P (do_delete_call_expr) = 1; + } + if (do_delete && !TREE_SIDE_EFFECTS (expr)) expr = do_delete; else if (do_delete) --- gcc/cp/constexpr.c.jj 2019-10-30 22:54:29.574952119 +0100 +++ gcc/cp/constexpr.c 2019-10-31 10:12:27.074761211 +0100 @@ -1638,6 +1638,28 @@ is_std_construct_at (tree fndecl) return name && id_equal (name, "construct_at"); } +/* Return true if FNDECL is std::allocator::{,de}allocate. */ + +static inline bool +is_std_allocator_allocate (tree fndecl) +{ + tree name = DECL_NAME (fndecl); + if (name == NULL_TREE + || !(id_equal (name, "allocate") || id_equal (name, "deallocate"))) + return false; + + tree ctx = DECL_CONTEXT (fndecl); + if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx)) + return false; + + tree decl = TYPE_MAIN_DECL (ctx); + name = DECL_NAME (decl); + if (name == NULL_TREE || !id_equal (name, "allocator")) + return false; + + return decl_in_std_namespace_p (decl); +} + /* Subroutine of cxx_eval_constant_expression. Evaluate the call expression tree T in the context of OLD_CALL expression evaluation. */ @@ -1716,7 +1738,12 @@ cxx_eval_call_expression (const constexp lval, non_constant_p, overflow_p); if (!DECL_DECLARED_CONSTEXPR_P (fun)) { - if (cxx_replaceable_global_alloc_fn (fun)) + if (TREE_CODE (t) == CALL_EXPR + && cxx_replaceable_global_alloc_fn (fun) + && (CALL_FROM_NEW_OR_DELETE_P (t) + || (ctx->call + && ctx->call->fundef + && is_std_allocator_allocate (ctx->call->fundef->decl)))) { const int nargs = call_expr_nargs (t); tree arg0 = NULL_TREE; @@ -1774,7 +1801,8 @@ cxx_eval_call_expression (const constexp } /* Allow placement new in std::construct_at, just return the second argument. */ - if (cxx_placement_new_fn (fun) + if (TREE_CODE (t) == CALL_EXPR + && cxx_placement_new_fn (fun) && ctx->call && ctx->call->fundef && is_std_construct_at (ctx->call->fundef->decl)) @@ -6508,9 +6536,15 @@ potential_constant_expression_1 (tree t, && !fndecl_built_in_p (fun) /* In C++2a, replaceable global allocation functions are constant expressions. */ - && !cxx_replaceable_global_alloc_fn (fun) + && (!cxx_replaceable_global_alloc_fn (fun) + || TREE_CODE (t) != CALL_EXPR + || (!CALL_FROM_NEW_OR_DELETE_P (t) + && (current_function_decl == NULL_TREE + || !is_std_allocator_allocate + (current_function_decl)))) /* Allow placement new in std::construct_at. */ && (!cxx_placement_new_fn (fun) + || TREE_CODE (t) != CALL_EXPR || current_function_decl == NULL_TREE || !is_std_construct_at (current_function_decl))) { --- gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C.jj 2019-10-31 09:53:48.606090420 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new6.C 2019-10-31 10:59:59.168577427 +0100 @@ -0,0 +1,83 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +namespace std +{ + inline namespace _8 { } + namespace _8 { + + typedef __SIZE_TYPE__ size_t; + + template + struct allocator + { + constexpr allocator () noexcept {} + + constexpr T *allocate (size_t n) + { return static_cast (::operator new (n * sizeof(T))); } + + constexpr void + deallocate (T *p, size_t n) + { ::operator delete (p); } + }; + + template + U __declval (int); + template + T __declval (long); + template + auto declval () noexcept -> decltype (__declval (0)); + + template + struct remove_reference + { typedef T type; }; + template + struct remove_reference + { typedef T type; }; + template + struct remove_reference + { typedef T type; }; + + template + constexpr T && + forward (typename std::remove_reference::type &t) noexcept + { return static_cast (t); } + + template + constexpr T && + forward (typename std::remove_reference::type &&t) noexcept + { return static_cast (t); } + + template + constexpr auto + construct_at (T *l, A &&... a) + noexcept (noexcept (::new ((void *) 0) T (std::declval ()...))) + -> decltype (::new ((void *) 0) T (std::declval ()...)) + { return ::new ((void *) l) T (std::forward (a)...); } + + template + constexpr inline void + destroy_at (T *l) + { l->~T (); } + } +} + +inline void *operator new (std::size_t, void *p) noexcept +{ return p; } + +constexpr bool +foo () +{ + std::allocator a; + auto p = a.allocate (2); + std::construct_at (p, 1); + std::construct_at (p + 1, 2); + if (p[0] != 1 || p[1] != 2) + throw 1; + std::destroy_at (p); + std::destroy_at (p + 1); + a.deallocate (p, 2); + return true; +} + +static_assert (foo ()); --- gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C.jj 2019-10-31 10:58:33.227908692 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C 2019-10-31 10:14:10.702155762 +0100 @@ -0,0 +1,33 @@ +// P0784R7 +// { dg-do compile { target c++2a } } + +namespace std +{ + typedef __SIZE_TYPE__ size_t; +} + +inline void *operator new (std::size_t, void *p) noexcept +{ return p; } +void *operator new (std::size_t) noexcept; + +constexpr bool +foo () +{ + auto p = static_cast (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } + *p = 1; + ::operator delete (p); + return false; +} + +struct S { constexpr S () : s (0) {} int s; }; + +constexpr bool +bar () +{ + auto p = static_cast (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } + auto q = new (p) S (); + q->s++; + q->~S (); + ::operator delete (p); + return false; +}