From patchwork Tue Jul 30 21:56:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 1966774 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=A4IGHyi2; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WYTZw1sMyz1ybV for ; Wed, 31 Jul 2024 07:56:40 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6E314385E458 for ; Tue, 30 Jul 2024 21:56:38 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id A10A83858424 for ; Tue, 30 Jul 2024 21:56:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A10A83858424 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A10A83858424 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722376580; cv=none; b=o/2QyFMe1uEqpY6gl2eKI1JCZq64W32rELGLCiYWe/ayYJZix/3cSa0PSuMS31Rxr+y8s3WMjrCfnpWRYnIUL0zp+ohFf4ibfEEhDAUoyRK3y53/fW+EnU1RXqsbiAukqWIhFqGiQfxx+wwsQJ6dizNRg6S4cg8ZF0e9WE7glFk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722376580; c=relaxed/simple; bh=Ru0QxMU/SIn8PJa+l66/Lg0LiafWQZ0d73e0U6sdgGw=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=HNwl3UaysmC66fQA+npZxuNggqSoEg8NyOT0ssgkz266Cop5rAixaxlCQ4i37o1jX276+hbzaghwZycJq20ukWbWrxrSnqgnHjFjELVrwFZJ+ZT4+oWlgqnG2AwNxvD9/J9PLkXWXnk4CEFanOwUlYdNwNZR+Zj/wjFJJh81LaE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722376577; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=q1zBpLQRVEQQZHeHuaZ28s4I2o+OXIxiZCWEnTDLEzQ=; b=A4IGHyi221KaXIEZWSAjApymNZ/N6mTgB19l9ingsoRMdfOyCsE++eQt3l29fAPkPb1p3b bWeOZZHaXsiKFQgUq+9h+TBoR1Pm1SnvBN7qFFsf8vD6AvJYIJTXlQl4W/4uJcnmoF0mvQ dgJ1XTBClEDTY/Clzr6FopzKkjEM4OU= Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-519-Oh76YNlvORGwnXtbW4ZFTw-1; Tue, 30 Jul 2024 17:56:15 -0400 X-MC-Unique: Oh76YNlvORGwnXtbW4ZFTw-1 Received: by mail-qt1-f198.google.com with SMTP id d75a77b69052e-44feeac4be2so72663081cf.2 for ; Tue, 30 Jul 2024 14:56:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722376575; x=1722981375; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=q1zBpLQRVEQQZHeHuaZ28s4I2o+OXIxiZCWEnTDLEzQ=; b=QAlDh8oy3fMvopk3dZlFrXX8vUGHAsDurpeafciBN7Ic5gApTDsHVTYbxRgwElub9P GcmcbEEoYG3tjSOye0Acr9gAfgSbK4NXdXoyQFILcVykPm+R5fKMAT3zc76FwJVyEBPO 5EzvdVUJOppDtkgbRGCt6o8JRuL+qAU1tnMxwV1I3rp01xMqfQysZAtBRZ9w0PJa52ZX Mu0FeD9+fQFBeaUIGBcbaRrtX1isYJALzbOvzwz1QeLnGMbsxdFrrgQy1m7S4HWI9Eva m4mLNhmgma8Gxkmqv/gGyVAGMM/mcdOD2jv0gBfvBC6TOhH7aM+sgr0JofKmu14yzahe dKsg== X-Gm-Message-State: AOJu0YwrZgl4XaN+PzCPTbliUnZI18KHN9qBLHOiReANmOJEOynlbpor mziE2GunP4UrAnCS3jtvbebns58sdjVx8lNqoVUPLSFIYdC0FFPKOoOoMt6akGHe4S9HJF/g2MI BNn2YOnG+5lRl5xksdMNxCYEN4/DxiZtcLXuAZ4a/W+hvUbSembpy+PqaJibFwb8= X-Received: by 2002:ac8:588c:0:b0:447:dff2:335 with SMTP id d75a77b69052e-45004dae7f9mr161505551cf.23.1722376575026; Tue, 30 Jul 2024 14:56:15 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFM7MDqgQYaf9QdLYdNd3ImuoCr4bHosUodKTBozLICGYb+HkBFtwGlo7A6JIONvAhVH+053A== X-Received: by 2002:ac8:588c:0:b0:447:dff2:335 with SMTP id d75a77b69052e-45004dae7f9mr161505311cf.23.1722376574488; Tue, 30 Jul 2024 14:56:14 -0700 (PDT) Received: from redhat.com ([2603:7000:9500:34a5::1db4]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-44fe8412a8dsm54282661cf.94.2024.07.30.14.56.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 30 Jul 2024 14:56:14 -0700 (PDT) Date: Tue, 30 Jul 2024 17:56:12 -0400 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: [PATCH v3] c++: array new with value-initialization, again [PR115645] Message-ID: References: <20240717213349.460422-1-polacek@redhat.com> <121474f8-1dcf-44e2-bf57-2a30e5b581ab@redhat.com> <8712fb9c-2cf3-476a-8f00-9a2f2c9b02bb@redhat.com> MIME-Version: 1.0 In-Reply-To: <8712fb9c-2cf3-476a-8f00-9a2f2c9b02bb@redhat.com> User-Agent: Mutt/2.2.12 (2023-09-09) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-12.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org On Tue, Jul 30, 2024 at 05:38:37PM -0400, Jason Merrill wrote: > On 7/30/24 4:59 PM, Marek Polacek wrote: > > On Mon, Jul 29, 2024 at 06:34:40PM -0400, Jason Merrill wrote: > > > On 7/29/24 4:18 PM, Marek Polacek wrote: > > > > On Tue, Jul 23, 2024 at 05:18:52PM -0400, Jason Merrill wrote: > > > > > On 7/17/24 5:33 PM, Marek Polacek wrote: > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > > > > > > > Hmm, I thought I had replied to this already. > > > > > > > > > > > -- >8 -- > > > > > > Unfortunately, my r15-1946 fix broke the attached testcase. In it, > > > > > > we no longer go into the > > > > > > /* P1009: Array size deduction in new-expressions. */ > > > > > > block, and instead generate an operator new [] call along with a loop > > > > > > in build_new_1, which we can't constexpr-evaluate. So this patch > > > > > > reverts r15-1946 and uses CONSTRUCTOR_IS_PAREN_INIT to distinguish > > > > > > between () and {} to fix the original testcase (anew7.C). > > > > > > > > > > > > PR c++/115645 > > > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > > > * call.cc (convert_like_internal) : Don't report errors > > > > > > about calling an explicit constructor when the constructor was marked > > > > > > CONSTRUCTOR_IS_PAREN_INIT. > > > > > > * init.cc (build_new): Revert r15-1946. Set CONSTRUCTOR_IS_PAREN_INIT. > > > > > > (build_vec_init): Maybe set CONSTRUCTOR_IS_PAREN_INIT. > > > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > > > * g++.dg/cpp2a/constexpr-new23.C: New test. > > > > > > --- > > > > > > gcc/cp/call.cc | 2 ++ > > > > > > gcc/cp/init.cc | 17 ++++----- > > > > > > gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C | 38 ++++++++++++++++++++ > > > > > > 3 files changed, 49 insertions(+), 8 deletions(-) > > > > > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C > > > > > > > > > > > > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc > > > > > > index a5d3426b70c..2d94d5e0d07 100644 > > > > > > --- a/gcc/cp/call.cc > > > > > > +++ b/gcc/cp/call.cc > > > > > > @@ -8592,6 +8592,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, > > > > > > && BRACE_ENCLOSED_INITIALIZER_P (expr) > > > > > > /* Unless this is for direct-list-initialization. */ > > > > > > && (!CONSTRUCTOR_IS_DIRECT_INIT (expr) || convs->need_temporary_p) > > > > > > + /* And it wasn't a ()-init. */ > > > > > > + && !CONSTRUCTOR_IS_PAREN_INIT (expr) > > > > > > /* And in C++98 a default constructor can't be explicit. */ > > > > > > && cxx_dialect >= cxx11) > > > > > > { > > > > > > diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc > > > > > > index e9561c146d7..4138a6077dd 100644 > > > > > > --- a/gcc/cp/init.cc > > > > > > +++ b/gcc/cp/init.cc > > > > > > @@ -4005,20 +4005,17 @@ build_new (location_t loc, vec **placement, tree type, > > > > > > /* P1009: Array size deduction in new-expressions. */ > > > > > > const bool array_p = TREE_CODE (type) == ARRAY_TYPE; > > > > > > if (*init > > > > > > - /* If the array didn't specify its bound, we have to deduce it. */ > > > > > > - && ((array_p && !TYPE_DOMAIN (type)) > > > > > > - /* For C++20 array with parenthesized-init, we have to process > > > > > > - the parenthesized-list. But don't do it for (), which is > > > > > > - value-initialization, and INIT should stay empty. */ > > > > > > - || (cxx_dialect >= cxx20 > > > > > > - && (array_p || nelts) > > > > > > - && !(*init)->is_empty ()))) > > > > > > + /* If ARRAY_P, we have to deduce the array bound. For C++20 paren-init, > > > > > > + we have to process the parenthesized-list. But don't do it for (), > > > > > > + which is value-initialization, and INIT should stay empty. */ > > > > > > + && (array_p || (cxx_dialect >= cxx20 && nelts && !(*init)->is_empty ()))) > > > > > > { > > > > > > /* This means we have 'new T[]()'. */ > > > > > > if ((*init)->is_empty ()) > > > > > > { > > > > > > tree ctor = build_constructor (init_list_type_node, NULL); > > > > > > CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true; > > > > > > + CONSTRUCTOR_IS_PAREN_INIT (ctor) = true; > > > > > > vec_safe_push (*init, ctor); > > > > > > } > > > > > > tree &elt = (**init)[0]; > > > > > > @@ -4735,6 +4732,9 @@ build_vec_init (tree base, tree maxindex, tree init, > > > > > > bool do_static_init = (DECL_P (obase) && TREE_STATIC (obase)); > > > > > > bool empty_list = false; > > > > > > + const bool paren_init_p = (init > > > > > > + && TREE_CODE (init) == CONSTRUCTOR > > > > > > + && CONSTRUCTOR_IS_PAREN_INIT (init)); > > > > > > > > > > I think rather than recognizing paren-init in general, we want to recognize > > > > > () specifically, and set explicit_value_init_p... > > > > > > > > > > > if (init && BRACE_ENCLOSED_INITIALIZER_P (init) > > > > > > && CONSTRUCTOR_NELTS (init) == 0) > > > > > > /* Skip over the handling of non-empty init lists. */ > > > > > > @@ -4927,6 +4927,7 @@ build_vec_init (tree base, tree maxindex, tree init, > > > > > > || TREE_CODE (type) == ARRAY_TYPE)) > > > > > > { > > > > > > init = build_constructor (init_list_type_node, NULL); > > > > > > + CONSTRUCTOR_IS_PAREN_INIT (init) = paren_init_p; > > > > > > } > > > > > > else > > > > > > { > > > > > > > > > > ...by taking the else branch here. Then we shouldn't need the convert_like > > > > > change. > > > > > > > > Unfortunately that then breaks Jon's test (constexpr-new23.C which this > > > > patch is adding). The problem is that if we do *not* create a new {}, and > > > > do explicit_value_init_p, we end up with > > > > > > > > int[1] * D.2643; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2643 = (int[1] *) D.2642) >>>; > > > > int[1] * D.2644; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2644 = D.2643) >>>; > > > > TARGET_EXPR ; > > > > <<< Unknown tree: for_stmt > > > > D.2645 > -1 > > > > < > > > *(int[1] &) int * D.2646; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2646 = (int *) D.2644) >>>; > > > > int * D.2647; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2647 = D.2646) >>>; > > > > TARGET_EXPR ; > > > > <<< Unknown tree: for_stmt > > > > > > > > D.2648 > -1 > > > > > > > > < > > > *D.2647 = 0, --D.2648 >>>>>; > > > > <<< Unknown tree: expr_stmt > > > > (void) ++D.2647 >>>; > > > > >>>; > > > > D.2646, --D.2645 >>>>>; > > > > <<< Unknown tree: expr_stmt > > > > (void) ++D.2644 >>>; > > > > >>>; > > > > D.2643 > > > > > > > > rather than: > > > > > > > > int[1] * D.2643; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2643 = (int[1] *) D.2642) >>>; > > > > int[1] * D.2644; > > > > <<< Unknown tree: expr_stmt > > > > (void) (D.2644 = D.2643) >>>; > > > > TARGET_EXPR ; > > > > <<< Unknown tree: for_stmt > > > > D.2645 > -1 > > > > < > > > *D.2644 = {}, --D.2645 >>>>>; > > > > <<< Unknown tree: expr_stmt > > > > (void) ++D.2644 >>>; > > > > >>>; > > > > D.2643 > > > > > > > > In the former, the "*D.2647 = 0" assignment is what breaks constexpr, > > > > which then complains: > > > > > > > > constexpr-new23.C:16:16: error: accessing 'test_array()::U::arr' member instead of initialized 'test_array()::U::x' member in constant expression > > > > 16 | return ::new((void*)p) T[1](); > > > > | ^~~~~~~~~~~~~~~~~~~~~~ > > > > constexpr-new23.C:31:9: note: initializing 'test_array()::U::arr' requires a member access expression as the left operand of the assignment > > > > 31 | int arr[4]; > > > > > > > > > > > > If there is no bug in constexpr, then it looks like we need to > > > > initialize using a {} rather than a loop that assigns 0 to each > > > > element. > > > > > > Ah, thanks. > > > > > > It looks like the first bug is that build_vec_init wrongly leaves try_const > > > false for this case (without your patch) because int doesn't have a > > > constexpr default constructor, failing to consider that value-initialization > > > of scalars is constexpr. > > > > Oh wow, I should have noticed that. > > > > > Then, once we're into the looping initialization, we aren't expressing it in > > > a way that will satisfy the strict checking in constexpr evaluation; it > > > needs to initialize the array, not just its elements. > > > > > > I expect we could fix that with something like > > > > > > > /* Start array lifetime before accessing elements. */ > > > > if (TREE_CODE (atype) == ARRAY_TYPE) > > > > { > > > > elt_init = build_constructor (atype, nullptr); > > > > CONSTRUCTOR_NO_CLEARING (elt_init) = true; > > > > for_stmt = build2 (INIT_EXPR, atype, obase, elt_init); > > > > finish_expr_stmt (for_stmt); > > > > } > > > > > > but if we're only concerned about constexpr, fixing the first bug ought to > > > be enough; in constant evaluation if we don't get a constant initializer > > > we're failing anyway. > > > > This patch fixes the first bug. Thanks! > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > Unfortunately, my r15-1946 fix broke the attached testcase; the > > constexpr evaluation reported an error about not being able to > > evaluate the code emitted by build_vec_init. Jason figured out > > it's because we were wrongly setting try_const to false, where > > in fact it should have been true. Value-initialization of scalars > > is constexpr, so we should check that alongside of > > type_has_constexpr_default_constructor. > > > > PR c++/115645 > > > > gcc/cp/ChangeLog: > > > > * init.cc (build_vec_init): When initializing a scalar type, try to > > create a constant initializer. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp2a/constexpr-new23.C: New test. > > --- > > gcc/cp/init.cc | 4 ++- > > gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C | 38 ++++++++++++++++++++ > > 2 files changed, 41 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C > > > > diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc > > index e9561c146d7..a3a97e2c128 100644 > > --- a/gcc/cp/init.cc > > +++ b/gcc/cp/init.cc > > @@ -4724,7 +4724,9 @@ build_vec_init (tree base, tree maxindex, tree init, > > && TREE_CONSTANT (maxindex) > > && (init ? TREE_CODE (init) == CONSTRUCTOR > > : (type_has_constexpr_default_constructor > > - (inner_elt_type))) > > + (inner_elt_type) > > + /* Value-initialization of scalars is constexpr. */ > > + || SCALAR_TYPE_P (inner_elt_type))) > > I think we also want to check explicit_value_init_p, since default-init of > scalars is not constexpr. > > I don't think we'd actually get here in that case, as callers like > build_new_1 avoid calling build_vec_init at all, but I'd still like to be > correct. Yeah, OK, updated. So far dg.exp passed. Bootstrapped/regtested running on x86_64-pc-linux-gnu, ok for trunk if all is good? -- >8 -- Unfortunately, my r15-1946 fix broke the attached testcase; the constexpr evaluation reported an error about not being able to evaluate the code emitted by build_vec_init. Jason figured out it's because we were wrongly setting try_const to false, where in fact it should have been true. Value-initialization of scalars is constexpr, so we should check that alongside of type_has_constexpr_default_constructor. PR c++/115645 gcc/cp/ChangeLog: * init.cc (build_vec_init): When initializing a scalar type, try to create a constant initializer. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-new23.C: New test. --- gcc/cp/init.cc | 5 ++- gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C | 38 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C base-commit: 4883c9571f5fb8fc7e873bb8a31aa164c5cfd0e0 diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index e9561c146d7..de82152bd1d 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -4724,7 +4724,10 @@ build_vec_init (tree base, tree maxindex, tree init, && TREE_CONSTANT (maxindex) && (init ? TREE_CODE (init) == CONSTRUCTOR : (type_has_constexpr_default_constructor - (inner_elt_type))) + (inner_elt_type) + /* Value-initialization of scalars is constexpr. */ + || (explicit_value_init_p + && SCALAR_TYPE_P (inner_elt_type)))) && (literal_type_p (inner_elt_type) || TYPE_HAS_CONSTEXPR_CTOR (inner_elt_type))); vec *const_vec = NULL; diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C new file mode 100644 index 00000000000..1abbef18fae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C @@ -0,0 +1,38 @@ +// PR c++/115645 +// { dg-do compile { target c++20 } } + +using size_t = decltype(sizeof(0)); + +void* operator new(size_t, void* p) { return p; } +void* operator new[](size_t, void* p) { return p; } + +#define VERIFY(C) if (!(C)) throw + +namespace std { + template + constexpr T* construct_at(T* p) + { + if constexpr (__is_array(T)) + return ::new((void*)p) T[1](); + else + return ::new((void*)p) T(); + } +} + +constexpr void +test_array() +{ + int arr[1] { 99 }; + std::construct_at(&arr); + VERIFY( arr[0] == 0 ); + + union U { + long long x = -1; + int arr[4]; + } u; + + auto p = std::construct_at(&u.arr); + VERIFY( (*p)[0] == 0 ); +} + +static_assert( [] { test_array(); return true; }() );