From patchwork Sun Sep 17 12:46:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 141147 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:172:b0:3f2:4152:657d with SMTP id h50csp2114211vqi; Sun, 17 Sep 2023 05:47:15 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEU+xU2UkEgODh8M8DFBpwcAOBBStOz5H1S5uB9RDf0b82wl8/HLD7dytdD65szLtFKJSh+ X-Received: by 2002:a2e:9b8f:0:b0:2bc:df3f:7140 with SMTP id z15-20020a2e9b8f000000b002bcdf3f7140mr5623321lji.17.1694954835132; Sun, 17 Sep 2023 05:47:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1694954835; cv=none; d=google.com; s=arc-20160816; b=0/0gd8l4S5VST2LIPmlNP6aKNA5bBFJrN3vy5Lwk7I0U++EqMPMmai7e+4GhK3glca dLfqymAY8n7pH6uGX4xaIJGwQY2N5XWv2lq35gpSuxfEgzdxqVqXzjKbUamV+D1ZqSsp 2KJI3BJe7tkkjLOAys1RSnoBTGmyl9vjjcK/ObdHqOKpsvC+PmlelT3VWZmRZR3/SoyE TwTW1ojjvcqnXXegDHYFtFkCNr5auQ/Rv0qRtIksIbJKEGnoBkCmko0Wfy0aBFZjMeZM CuQNyyidPnRKYf/4UUjlZVkB2DU1l9mLsHxhIv29/BoGtAf/Hw3O2zZd5GzbvcrE4kY5 3FpQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:date:dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=zniS8vM8UaM9jq3UUgHdIDSSJesPttCn95P82/AWn+k=; fh=Px0ai+PRlAML1LDC3BukPvCf8nEgqik/SD+oF2SuaP0=; b=F1aP40svzqSy1FBLo8Gq9QQ/mAncmoaxhVrUJFEgUDJYyWbF21rhr/MqaDpuHbCgZw SbjPh8EqfJyJBbTEnSQQ1lYY43jBnO+9IH4FWrn0wq2H8gvrHr5kprTaR1XY4Vc6EEgc ONWedWS7fdlczOb+ZQxhXCzZC2zeopLeq67h5jvaA+Xn7msFN78NWtfCGFi1IBgkdrud UrUynHIOL+yG/AxhAJJjbzk1Xs1FmfijdUpvz+9gMXFIbk7eEEtGQmSHF1qhHMHPBiMe svLa77DlIoG12hTfKYd9tnaR7cEl+dgBX7bf4BOYOHeEpFNxtgLIp3tREfHLE1bwdi35 t7qw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=u8LgOcO6; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id g25-20020a1709061c9900b009a1bf605ebesi6744828ejh.847.2023.09.17.05.47.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 Sep 2023 05:47:15 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=u8LgOcO6; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AC6B93858417 for ; Sun, 17 Sep 2023 12:47:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AC6B93858417 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1694954833; bh=zniS8vM8UaM9jq3UUgHdIDSSJesPttCn95P82/AWn+k=; h=Date:To:Cc:Subject:References:In-Reply-To:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=u8LgOcO6dfLvXXQ7Bn3IXGvu2rNU33344YdpvceRic0S5wf/yP1LWaXfSOW0h3y89 07IMkd2qteMQkwG6dtosA+7mVf/sHoe01ju6QQmb0GHWFuNZ5j/W5KxyyryvN9MZsc AbnMNzuJENXePlKrUktAuSUv8CbpmGNpyppP4Mr8= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by sourceware.org (Postfix) with ESMTPS id CA2443858D32 for ; Sun, 17 Sep 2023 12:46:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CA2443858D32 Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1c3cbfa40d6so33000175ad.1 for ; Sun, 17 Sep 2023 05:46:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1694954779; x=1695559579; h=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=zniS8vM8UaM9jq3UUgHdIDSSJesPttCn95P82/AWn+k=; b=HZoDbuwDF2ffieJGFuvzC3LNeNQstSvIGAkJNc5mDNnz7BbKH6IYcu2QW9ipnZXoRs K6T9rdDzp3oFCFaLgLuoURHQQBBqXk6aEQWV9yTJujwFsHQ8V0QONq+IKs9KFN79am9G wBSxVqSViLIsQ1RJ4sgoTjxiVy7BM1L3BJY//4unj7gb2q48BeG1KGZkDSlylHYYkcov O1uy9TS7XoaqgCDOQ3hyidsN6E34b5rs6FxYsIVbArzNr2dIsr+BJFyb5fIWojYy7X/K uN7QhxAwZoFEnOzi4/G0xXCkokgMpQqhyXJr640YVLbAr2cBc2MZWX7oeuUGO+/4dHJ2 6Wrw== X-Gm-Message-State: AOJu0YxtDL1VQTdPZrmCTzmgImGGNokTyTuTxhppLpIWfk7dlC7b8p/e WqpElAiWJn86lxUKnBBaLeB4aa2jpVc= X-Received: by 2002:a17:902:f544:b0:1bc:16cf:fc30 with SMTP id h4-20020a170902f54400b001bc16cffc30mr8649508plf.63.1694954779327; Sun, 17 Sep 2023 05:46:19 -0700 (PDT) Received: from Thaum.localdomain ([124.168.125.94]) by smtp.gmail.com with ESMTPSA id b5-20020a170902ed0500b001c47541ecd7sm992140pld.250.2023.09.17.05.46.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 Sep 2023 05:46:18 -0700 (PDT) Date: Sun, 17 Sep 2023 22:46:14 +1000 To: gcc-patches@gcc.gnu.org Cc: Jason Merrill Subject: [PATCH v2] c++: Catch indirect change of active union member in constexpr [PR101631] Message-ID: References: <053faf76-f918-7527-4a41-755a18d0018a@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, PDS_OTHER_BAD_TLD, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, 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: , X-Patchwork-Original-From: Nathaniel Shead via Gcc-patches From: Nathaniel Shead Reply-To: Nathaniel Shead Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1775837950597371702 X-GMAIL-MSGID: 1777288961348984454 Ping for https://gcc.gnu.org/pipermail/gcc-patches/2023-September/629084.html -- >8 -- This patch adds checks for attempting to change the active member of a union by methods other than a member access expression. To be able to properly distinguish `*(&u.a) = ` from `u.a = `, this patch redoes the solution for c++/59950 to avoid extranneous *&; it seems that the only case that needed the workaround was when copying empty classes. Additionally, this patch ensures that constructors for a union field mark that field as the active member before entering the call itself; this ensures that modifications of the field within the constructor's body don't cause false positives (as these will not appear to be member access expressions). This means that we no longer need to start the lifetime of empty union members after the constructor body completes. PR c++/101631 gcc/cp/ChangeLog: * call.cc (build_over_call): Fold more indirect refs for trivial assignment op. * class.cc (type_has_non_deleted_trivial_default_ctor): Create. * constexpr.cc (cxx_eval_call_expression): Start lifetime of union member before entering constructor. (cxx_eval_store_expression): Check for accessing inactive union member indirectly. * cp-tree.h (type_has_non_deleted_trivial_default_ctor): Forward declare. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-union2.C: New test. * g++.dg/cpp2a/constexpr-union3.C: New test. * g++.dg/cpp2a/constexpr-union4.C: New test. * g++.dg/cpp2a/constexpr-union5.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/call.cc | 11 +- gcc/cp/class.cc | 8 ++ gcc/cp/constexpr.cc | 105 ++++++++++++------ gcc/cp/cp-tree.h | 1 + gcc/testsuite/g++.dg/cpp2a/constexpr-union2.C | 30 +++++ gcc/testsuite/g++.dg/cpp2a/constexpr-union3.C | 45 ++++++++ gcc/testsuite/g++.dg/cpp2a/constexpr-union4.C | 29 +++++ gcc/testsuite/g++.dg/cpp2a/constexpr-union5.C | 55 +++++++++ 8 files changed, 246 insertions(+), 38 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-union2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-union3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-union4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-union5.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 23e458d3252..3372c88f182 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10358,10 +10358,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) && DECL_OVERLOADED_OPERATOR_IS (fn, NOP_EXPR) && trivial_fn_p (fn)) { - /* Don't use cp_build_fold_indirect_ref, op= returns an lvalue even if - the object argument isn't one. */ - tree to = cp_build_indirect_ref (input_location, argarray[0], - RO_ARROW, complain); + tree to = cp_build_fold_indirect_ref (argarray[0]); tree type = TREE_TYPE (to); tree as_base = CLASSTYPE_AS_BASE (type); tree arg = argarray[1]; @@ -10369,7 +10366,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (is_really_empty_class (type, /*ignore_vptr*/true)) { - /* Avoid copying empty classes. */ + /* Avoid copying empty classes, but ensure op= returns an lvalue even + if the object argument isn't one. This isn't needed in other cases + since MODIFY_EXPR is always considered an lvalue. */ + to = cp_build_addr_expr (to, tf_none); + to = cp_build_indirect_ref (input_location, to, RO_ARROW, complain); val = build2 (COMPOUND_EXPR, type, arg, to); suppress_warning (val, OPT_Wunused); } diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 778759237dc..43898dabbe7 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -5651,6 +5651,14 @@ type_has_virtual_destructor (tree type) return (dtor && DECL_VIRTUAL_P (dtor)); } +/* True iff class TYPE has a non-deleted trivial default + constructor. */ + +bool type_has_non_deleted_trivial_default_ctor (tree type) +{ + return TYPE_HAS_TRIVIAL_DFLT (type) && locate_ctor (type); +} + /* Returns true iff T, a class, has a move-assignment or move-constructor. Does not lazily declare either. If USER_P is false, any move function will do. If it is true, the diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 8bd5c4a47f8..b82e87be974 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3141,40 +3141,34 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false, non_constant_p, overflow_p); + /* If this is a constructor, we are beginning the lifetime of the + object we are initialising. */ + if (new_obj + && DECL_CONSTRUCTOR_P (fun) + && TREE_CODE (new_obj) == COMPONENT_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (new_obj, 0))) == UNION_TYPE) + { + tree activate = build2 (INIT_EXPR, TREE_TYPE (new_obj), + new_obj, + build_constructor (TREE_TYPE (new_obj), + NULL)); + cxx_eval_constant_expression (ctx, activate, + lval, non_constant_p, overflow_p); + ggc_free (activate); + } + tree jump_target = NULL_TREE; cxx_eval_constant_expression (&call_ctx, body, vc_discard, non_constant_p, overflow_p, &jump_target); if (DECL_CONSTRUCTOR_P (fun)) - { - /* This can be null for a subobject constructor call, in - which case what we care about is the initialization - side-effects rather than the value. We could get at the - value by evaluating *this, but we don't bother; there's - no need to put such a call in the hash table. */ - result = lval ? ctx->object : ctx->ctor; - - /* If we've just evaluated a subobject constructor call for an - empty union member, it might not have produced a side effect - that actually activated the union member. So produce such a - side effect now to ensure the union appears initialized. */ - if (!result && new_obj - && TREE_CODE (new_obj) == COMPONENT_REF - && TREE_CODE (TREE_TYPE - (TREE_OPERAND (new_obj, 0))) == UNION_TYPE - && is_really_empty_class (TREE_TYPE (new_obj), - /*ignore_vptr*/false)) - { - tree activate = build2 (MODIFY_EXPR, TREE_TYPE (new_obj), - new_obj, - build_constructor (TREE_TYPE (new_obj), - NULL)); - cxx_eval_constant_expression (ctx, activate, lval, - non_constant_p, overflow_p); - ggc_free (activate); - } - } + /* This can be null for a subobject constructor call, in + which case what we care about is the initialization + side-effects rather than the value. We could get at the + value by evaluating *this, but we don't bother; there's + no need to put such a call in the hash table. */ + result = lval ? ctx->object : ctx->ctor; else if (VOID_TYPE_P (TREE_TYPE (res))) result = void_node; else @@ -6049,6 +6043,14 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, mutable_p) && const_object_being_modified == NULL_TREE) const_object_being_modified = probe; + + /* Track named member accesses for unions to validate modifications + that change active member. */ + if (!evaluated && TREE_CODE (probe) == COMPONENT_REF) + vec_safe_push (refs, probe); + else + vec_safe_push (refs, NULL_TREE); + vec_safe_push (refs, elt); vec_safe_push (refs, TREE_TYPE (probe)); probe = ob; @@ -6057,6 +6059,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, case REALPART_EXPR: gcc_assert (probe == target); + vec_safe_push (refs, NULL_TREE); vec_safe_push (refs, probe); vec_safe_push (refs, TREE_TYPE (probe)); probe = TREE_OPERAND (probe, 0); @@ -6064,6 +6067,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, case IMAGPART_EXPR: gcc_assert (probe == target); + vec_safe_push (refs, NULL_TREE); vec_safe_push (refs, probe); vec_safe_push (refs, TREE_TYPE (probe)); probe = TREE_OPERAND (probe, 0); @@ -6152,6 +6156,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, enum tree_code code = TREE_CODE (type); tree reftype = refs->pop(); tree index = refs->pop(); + bool is_access_expr = refs->pop() != NULL_TREE; if (code == COMPLEX_TYPE) { @@ -6192,10 +6197,16 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, type = reftype; - if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp) - && CONSTRUCTOR_ELT (*valp, 0)->index != index) + if (code == UNION_TYPE + && TREE_CODE (t) == MODIFY_EXPR + && (CONSTRUCTOR_NELTS (*valp) == 0 + || CONSTRUCTOR_ELT (*valp, 0)->index != index)) { - if (cxx_dialect < cxx20) + /* We changed the active member of a union. Ensure that this is + valid. */ + bool has_active_member = CONSTRUCTOR_NELTS (*valp) != 0; + tree inner = strip_array_types (reftype); + if (has_active_member && cxx_dialect < cxx20) { if (!ctx->quiet) error_at (cp_expr_loc_or_input_loc (t), @@ -6205,8 +6216,36 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, index); *non_constant_p = true; } - else if (TREE_CODE (t) == MODIFY_EXPR - && CONSTRUCTOR_NO_CLEARING (*valp)) + else if (!is_access_expr + || (CLASS_TYPE_P (inner) + && !type_has_non_deleted_trivial_default_ctor (inner))) + { + /* Diagnose changing active union member after initialisation + without a valid member access expression, as described in + [class.union.general] p5. */ + if (!ctx->quiet) + { + if (has_active_member) + error_at (cp_expr_loc_or_input_loc (t), + "accessing %qD member instead of initialized " + "%qD member in constant expression", + index, CONSTRUCTOR_ELT (*valp, 0)->index); + else + error_at (cp_expr_loc_or_input_loc (t), + "accessing uninitialized member %qD", + index); + if (is_access_expr) + { + inform (DECL_SOURCE_LOCATION (index), + "%qD does not implicitly begin its lifetime " + "because %qT does not have a non-deleted " + "trivial default constructor", + index, inner); + } + } + *non_constant_p = true; + } + else if (has_active_member && CONSTRUCTOR_NO_CLEARING (*valp)) { /* Diagnose changing the active union member while the union is in the process of being initialized. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 608d6310e53..fa0d5cdf52b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6791,6 +6791,7 @@ 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 type_has_non_deleted_trivial_default_ctor (tree); extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared); extern bool classtype_has_non_deleted_move_ctor (tree); extern tree classtype_has_depr_implicit_copy (tree); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union2.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-union2.C new file mode 100644 index 00000000000..1712395d7e7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union2.C @@ -0,0 +1,30 @@ +// PR c++/101631 +// { dg-do compile { target c++20 } } + +struct sso { + union { + int buf[10]; + int* alloc; + }; +}; + +constexpr bool direct() { + sso val; + val.alloc = nullptr; + val.buf[5] = 42; + return true; +} +constexpr bool ok = direct(); + + +constexpr void perform_assignment(int& left, int right) noexcept { + left = right; // { dg-error "accessing .+ member instead of initialized" } +} + +constexpr bool indirect() { + sso val; + val.alloc = nullptr; + perform_assignment(val.buf[5], 42); // { dg-message "in .constexpr. expansion" } + return true; +} +constexpr bool err = indirect(); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-union3.C new file mode 100644 index 00000000000..6d30bb2498f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union3.C @@ -0,0 +1,45 @@ +// { dg-do compile { target c++20 } } + +struct S +{ + union { + char buf[8]; + char* ptr; + }; + unsigned len; + + constexpr S(const char* s, unsigned n) + { + char* p; + if (n > 7) + p = ptr = new char[n+1]; + else + p = buf; + for (len = 0; len < n; ++len) + p[len] = s[len]; // { dg-error "accessing uninitialized member" } + p[len] = '\0'; + } + + constexpr ~S() + { + if (len > 7) + delete[] ptr; + } +}; + +constexpr bool test1() +{ + S s("test", 4); // { dg-message "in .constexpr. expansion" } + return true; +} + +constexpr bool a = test1(); // { dg-message "in .constexpr. expansion" } + + +constexpr bool test2() +{ + S s("hello world", 11); + return true; +} + +constexpr bool b = test2(); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union4.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-union4.C new file mode 100644 index 00000000000..429ab203ac2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union4.C @@ -0,0 +1,29 @@ +// { dg-do compile { target c++20 } } + +// from [class.union.general] p5 + +union A { int x; int y[4]; }; +struct B { A a; }; +union C { B b; int k; }; +constexpr int f() { + C c; // does not start lifetime of any union member + c.b.a.y[3] = 4; // OK, S(c.b.a.y[3]) contains c.b and c.b.a.y; + // creates objects to hold union members c.b and c.b.a.y + return c.b.a.y[3]; // OK, c.b.a.y refers to newly created object (see [basic.life]) +} +constexpr int a = f(); + +struct X { const int a; int b; }; +union Y { X x; int k; };// { dg-message "does not implicitly begin its lifetime" } +constexpr int g() { + Y y = { { 1, 2 } }; // OK, y.x is active union member ([class.mem]) + int n = y.x.a; + y.k = 4; // OK, ends lifetime of y.x, y.k is active member of union + + y.x.b = n; // { dg-error "accessing .* member instead of initialized .* member" } + // undefined behavior: y.x.b modified outside its lifetime, + // S(y.x.b) is empty because X's default constructor is deleted, + // so union member y.x's lifetime does not implicitly start + return 0; +} +constexpr int b = g(); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-union5.C new file mode 100644 index 00000000000..3da43a1cf40 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union5.C @@ -0,0 +1,55 @@ +// { dg-do compile { target c++20 } } + +union U { int a; int b; int c[2]; }; + +constexpr int test1() { + U u; + u.a = 10; + *&u.b = 20; // { dg-error "accessing" } + return u.b; +} +constexpr int a = test1(); // { dg-message "in .constexpr. expansion" } + +constexpr int test2() { + U u; + u.a = 10; + (0, u.b) = 20; // { dg-error "accessing" } + return u.b; +} +constexpr int b = test2(); // { dg-message "in .constexpr. expansion" } + +constexpr int test3() { + U u; + u.a = 0; + int* p = &u.b; + p[u.a] = 10; // { dg-error "accessing" } + return u.b; +} +constexpr int c = test3(); // { dg-message "in .constexpr. expansion" } + +constexpr int test4() { + U u; + u.a = 0; + int* p = &u.b; + u.a[p] = 10; // { dg-error "accessing" } + return u.b; +} +constexpr int d = test4(); // { dg-message "in .constexpr. expansion" } + +struct S { U u[10]; }; +constexpr int test5() { + S s; + s.u[4].a = 10; + 6[s.u].b = 15; + return 4[s.u].a + s.u[6].b; +} +static_assert(test5() == 25); + +constexpr int test6() { + U u; + u.a = 5; + u.c[0] = 3; + 1[u.c] = 8; + return 1[u.c] + u.c[0]; +} +static_assert(test6() == 11);