From patchwork Tue Sep 1 15:53:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 142647 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F24334BA798A for ; Tue, 1 Sep 2026 15:54:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F24334BA798A Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=fMpYdvIS 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.133.124]) by sourceware.org (Postfix) with ESMTP id 59E804BA2E35 for ; Tue, 1 Sep 2026 15:53:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 59E804BA2E35 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine 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 59E804BA2E35 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788278021; cv=none; b=OBYfdVe1iWhqXQS6Hrb38uFeS4QC0lH3Ebym7cnKU59nkcI0tQQ6lmaT4l1ag7pPCRXypyxxyuohibrowR4jOKyt5u0MH9Yu+ry/IXgLtnY53Z5Q+q+1aRCJtcWUO5/siP3IFTXheeYFvipM51SDvRdEMO9py2nJpvD/GRrxH1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788278021; c=relaxed/simple; bh=VARfDS+glPMcWUm+o5t9AKlBh+zSXC5QS/j8klk9+yY=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=f6ydnhVHifpk0cDofkflJ+mrGnzyxGujOSpWj2gHFU9aEV7M11VR0BZN4QXbOajsh/sEYW1vBGjitY4Du/AkpYnsS9is9tVY3q8RHFzus8/DAJUV82i64IWUnl44RJDDBIFMjs8F+tWtYhlaI8ygdEnbIvKAWLRY36eSVNs/uDs= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=fMpYdvIS DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 59E804BA2E35 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1788278021; 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:in-reply-to:in-reply-to: references:references; bh=uf1pEYVMkYGycmp4jrVL8PU8n+VnpZ4DBeaZz+OOZnw=; b=fMpYdvIS2kRPKFmwAakqszPAQdYejjHIa7OUR4DOmhvONQlhDKHIEEX7jNf2kMlhf5lW/Z lLzXfC4JYZjkyTlPI3WHPoOjAFFaDJdhWmv2K+sgDhy7HY7RBJvoIHf/0kVskv0kWbdtxc Rfv/gnGHU3X71CuNVvOtESflzPNxTJc= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-100-3yYDx0IHNm2fd5-R5Gsl1w-1; Tue, 01 Sept 2026 11:53:28 -0400 X-MC-Unique: 3yYDx0IHNm2fd5-R5Gsl1w-1 X-Mimecast-MFC-AGG-ID: 3yYDx0IHNm2fd5-R5Gsl1w_1788278007 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8C8A81802668; Tue, 1 Sep 2026 15:53:27 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.44.33.31]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 671F21955E72; Tue, 1 Sep 2026 15:53:26 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.18.1/8.18.1) with ESMTPS id 681FrLOu676564 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 1 Sep 2026 17:53:21 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.18.1/8.18.1/Submit) id 681FrIb5676561; Tue, 1 Sep 2026 17:53:18 +0200 Date: Tue, 1 Sep 2026 17:53:15 +0200 From: Jakub Jelinek To: Jason Merrill Cc: Jonathan Wakely , Tomasz Kaminski , gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: [PATCH] c++, libstdc++, v4: Implement C++26 P2641R4 - Checking if a union alternative is active Message-ID: References: <525f03ba-b6be-44cb-b32f-7b915fe1d859@redhat.com> <12a690ac-f201-482b-9971-22fe29724037@redhat.com> <75ca9a28-91b6-475b-8e03-7b55cae58dae@redhat.com> MIME-Version: 1.0 In-Reply-To: <75ca9a28-91b6-475b-8e03-7b55cae58dae@redhat.com> X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: KA7Kgxj2f4DOM0pJWQd9lrTuo3-LMWtbY-lD5m7dEhM_1788278007 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on 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: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org On Tue, Sep 01, 2026 at 11:01:41AM -0400, Jason Merrill wrote: > This seems backwards to me, since void_node is also used for out-of-lifetime > subobjects (which are still within storage duration). Incremental: Jakub --- a/gcc/cp/constexpr.cc 2026-08-31 21:24:22.190633669 +0200 +++ b/gcc/cp/constexpr.cc 2026-09-01 17:29:28.984871915 +0200 @@ -1181,7 +1181,7 @@ enum constexpr_switch_state { class constexpr_global_ctx { /* Values for any temporaries or local variables within the constant-expression. Objects outside their lifetime have - value 'void_node' or 'void_list_node', the latter if they are outside + value 'void_node' or 'void_list_node', the former if they are outside of lifetime but still before storage has been deallocated. */ hash_map values; public: @@ -1285,7 +1285,7 @@ public: if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL) - values.put (t, past_storage_end ? void_node : void_list_node); + values.put (t, past_storage_end ? void_list_node : void_node); else values.remove (t); } @@ -2628,7 +2628,7 @@ cxx_eval_is_within_lifetime (const const check_mutable = true; } } - if (!valp || *valp == void_node) + if (!valp || *valp == void_list_node) { if (!ctx->quiet) { @@ -2656,7 +2656,7 @@ cxx_eval_is_within_lifetime (const const return t; } tree val = *valp; - if (val == void_list_node) + if (val == void_node) return boolean_false_node; unsigned i; tree ref; seems to work just fine too, so if you prefer that, the following patch includes that. Tested so far with GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make -j32 check-g++ 2026-09-01 Jakub Jelinek gcc/cp/ * cp-tree.h: Implement C++26 P2641R4 - Checking if a union alternative is active. (enum cp_built_in_function): Add CP_BUILT_IN_IS_WITHIN_LIFETIME. * tree.cc (builtin_valid_in_constant_expr_p): Handle CP_BUILT_IN_IS_WITHIN_LIFETIME. * decl.cc (cxx_init_decl_processing): Create decl for CP_BUILT_IN_IS_WITHIN_LIFETIME. * semantics.cc (finish_call_expr): Perform semantic checking of __builtin_is_within_lifetime. * constexpr.cc (constexpr_global_ctx::get_value): New method. (eval_and_check_array_index): Add forward declaration. (cxx_eval_is_within_lifetime): New function. (cxx_eval_builtin_function_call): Handle CP_BUILT_IN_IS_WITHIN_LIFETIME. gcc/testsuite/ * g++.dg/cpp26/within-lifetime1.C: New test. * g++.dg/cpp26/within-lifetime2.C: New test. * g++.dg/cpp26/within-lifetime3.C: New test. * g++.dg/cpp26/within-lifetime4.C: New test. * g++.dg/cpp26/within-lifetime5.C: New test. * g++.dg/cpp26/within-lifetime6.C: New test. * g++.dg/cpp26/within-lifetime7.C: New test. libstdc++-v3/ * include/bits/version.def (within_lifetime): New. * include/bits/version.h: Regenerate. * include/std/type_traits: Define __glibcxx_want_within_lifetime before including bits/version.h. (std::is_within_lifetime): New function template. * src/c++23/std.cc.in (std::is_within_lifetime): Export. --- a/gcc/cp/cp-tree.h 2026-08-29 14:18:54.467771290 +0200 +++ b/gcc/cp/cp-tree.h 2026-08-31 17:18:05.988079258 +0200 @@ -7211,6 +7211,7 @@ enum cp_built_in_function { CP_BUILT_IN_CONSTEXPR_DIAG, CP_BUILT_IN_CURRENT_EXCEPTION, CP_BUILT_IN_UNCAUGHT_EXCEPTIONS, + CP_BUILT_IN_IS_WITHIN_LIFETIME, CP_BUILT_IN_LAST }; --- a/gcc/cp/tree.cc 2026-08-31 09:40:40.507713716 +0200 +++ b/gcc/cp/tree.cc 2026-08-31 17:18:06.022184287 +0200 @@ -578,6 +578,7 @@ builtin_valid_in_constant_expr_p (const_ case CP_BUILT_IN_CONSTEXPR_DIAG: case CP_BUILT_IN_CURRENT_EXCEPTION: case CP_BUILT_IN_UNCAUGHT_EXCEPTIONS: + case CP_BUILT_IN_IS_WITHIN_LIFETIME: return true; default: break; --- a/gcc/cp/decl.cc 2026-08-31 17:12:10.714890019 +0200 +++ b/gcc/cp/decl.cc 2026-08-31 17:18:06.023574417 +0200 @@ -5673,6 +5673,12 @@ cxx_init_decl_processing (void) BUILT_IN_FRONTEND, NULL, NULL_TREE); set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF); + decl = add_builtin_function ("__builtin_is_within_lifetime", + bool_vaftype, CP_BUILT_IN_IS_WITHIN_LIFETIME, + BUILT_IN_FRONTEND, NULL, NULL_TREE); + set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF); + SET_DECL_IMMEDIATE_FUNCTION_P (decl); + integer_two_node = build_int_cst (NULL_TREE, 2); /* Guess at the initial static decls size. */ --- a/gcc/cp/semantics.cc 2026-08-27 17:41:43.924633203 +0200 +++ b/gcc/cp/semantics.cc 2026-08-31 17:18:06.025090753 +0200 @@ -3540,6 +3540,32 @@ finish_call_expr (tree fn, vec needs a single " + "argument"); + return error_mark_node; + } + tree arg = (**args)[0]; + if (error_operand_p (arg)) + return error_mark_node; + tree ptype = TREE_TYPE (arg); + if (!POINTER_TYPE_P (ptype)) + { + if (complain & tf_error) + error ("%<__builtin_is_within_lifetime%> argument type " + "%qT is not pointer type", ptype); + return error_mark_node; + } + } + /* A call to a namespace-scope function. */ result = build_new_function_call (fn, args, orig_complain); } --- a/gcc/cp/constexpr.cc 2026-08-31 09:35:42.611573034 +0200 +++ b/gcc/cp/constexpr.cc 2026-09-01 17:29:28.984871915 +0200 @@ -1180,8 +1180,9 @@ enum constexpr_switch_state { class constexpr_global_ctx { /* Values for any temporaries or local variables within the - constant-expression. Objects outside their lifetime have - value 'void_node'. */ + constant-expression. Objects outside their lifetime have + value 'void_node' or 'void_list_node', the former if they are outside + of lifetime but still before storage has been deallocated. */ hash_map values; public: /* Number of cxx_eval_constant_expression calls (except skipped ones, @@ -1242,24 +1243,28 @@ public: bool is_outside_lifetime (tree t) { if (tree *p = values.get (t)) - if (*p == void_node) + if (*p == void_node || *p == void_list_node) return true; return false; } tree get_value (tree t) { if (tree *p = values.get (t)) - if (*p != void_node) + if (*p != void_node && *p != void_list_node) return *p; return NULL_TREE; } + tree *get_raw_value_ptr (tree t) + { + return values.get (t); + } tree *get_value_ptr (tree t, bool initializing) { if (modifiable && !modifiable->contains (t)) return nullptr; if (tree *p = values.get (t)) { - if (*p != void_node) + if (*p != void_node && *p != void_list_node) return p; else if (initializing) { @@ -1275,12 +1280,12 @@ public: if (!already_in_map && modifiable) modifiable->add (t); } - void destroy_value (tree t) + void destroy_value (tree t, bool past_storage_end = true) { if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL) - values.put (t, void_node); + values.put (t, past_storage_end ? void_list_node : void_node); else values.remove (t); } @@ -2533,6 +2538,253 @@ cxx_eval_constexpr_diag (const constexpr return void_node; } +static tree eval_and_check_array_index (const constexpr_ctx *, tree, bool, + bool *, bool *, tree *); + +/* Attempt to evaluate T which represents a call to + __builtin_is_within_lifetime. */ + +static tree +cxx_eval_is_within_lifetime (const constexpr_ctx *ctx, tree t, + bool *non_constant_p, bool *overflow_p, + tree *jump_target) +{ + location_t loc = EXPR_LOCATION (t); + tree arg = CALL_EXPR_ARG (t, 0); + arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + if (*non_constant_p) + return t; + /* Need to strip casts to pointers to void, but should preserve + pointer casts within the innermost pointer to void cast if + any, so that e.g. pointers to heap allocations are handled + correctly. */ + tree p = arg; + while (CONVERT_EXPR_P (p) || TREE_CODE (p) == NON_LVALUE_EXPR) + { + if (!TYPE_PTR_P (TREE_TYPE (p))) + break; + if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (p)))) + arg = TREE_OPERAND (p, 0); + p = TREE_OPERAND (p, 0); + } + if (integer_zerop (arg)) + { + if (!ctx->quiet) + error_at (loc, "%qs called with a null pointer", + "__builtin_is_within_lifetime"); + *non_constant_p = true; + return t; + } + if (POINTER_TYPE_P (TREE_TYPE (arg)) + && (TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == METHOD_TYPE)) + { + if (!ctx->quiet) + error_at (loc, "%qs called with pointer to function", + "__builtin_is_within_lifetime"); + *non_constant_p = true; + return t; + } + arg = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (arg)), arg); + arg = cxx_eval_constant_expression (ctx, arg, vc_glvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + if (*non_constant_p) + return t; + auto_vec refs; + tree obj; + for (obj = arg; ; obj = TREE_OPERAND (obj, 0)) + { + switch (TREE_CODE (obj)) + { + case COMPONENT_REF: + case ARRAY_REF: + case REALPART_EXPR: + case IMAGPART_EXPR: + refs.safe_push (obj); + continue; + default: + break; + } + break; + } + tree *valp = nullptr; + bool check_mutable = false; + if (DECL_P (obj)) + { + valp = ctx->global->get_raw_value_ptr (obj); + if (!valp + && VAR_P (obj) + && TREE_STATIC (obj) + && decl_constant_var_p (obj)) + { + valp = &DECL_INITIAL (obj); + check_mutable = true; + } + } + if (!valp || *valp == void_list_node) + { + if (!ctx->quiet) + { + auto_diagnostic_group d; + if (DECL_P (obj) && DECL_NAME (obj) == heap_deleted_identifier) + { + error_at (loc, "%qs on allocated storage after deallocation " + "is not a constant expression", + "__builtin_is_within_lifetime"); + inform (DECL_SOURCE_LOCATION (obj), "allocated here"); + } + else if (DECL_P (obj) && ctx->global->is_outside_lifetime (obj)) + { + error_at (loc, "%qs on %qE outside its lifetime is not a " + "constant expression", + "__builtin_is_within_lifetime", obj); + inform (DECL_SOURCE_LOCATION (obj), "declared here"); + } + else + error_at (loc, "%qs on %qE from outside current evaluation " + "is not a constant expression", + "__builtin_is_within_lifetime", obj); + } + *non_constant_p = true; + return t; + } + tree val = *valp; + if (val == void_node) + return boolean_false_node; + unsigned i; + tree ref; + /* Magic value used for val when inside the following loop when + inside of an omitted part of initializer due to it being + uninitialized. In this case return boolean_true_node unless + there is some union member access, in unitialized union + no member is within lifetime. */ + const tree val_uninit = global_namespace; + /* Magic value used for val when inside the following loop when + inside of an omitted part of initializer due to it being + zero initialized. In this case return boolean_true_node unless + there is some union member access except for the first member. */ + const tree val_zero_init = std_node; + if (val == NULL_TREE) + val = val_uninit; + FOR_EACH_VEC_ELT_REVERSE (refs, i, ref) + switch (TREE_CODE (ref)) + { + case REALPART_EXPR: + case IMAGPART_EXPR: + if (TREE_CODE (val) == COMPLEX_EXPR + && (TREE_OPERAND (val, TREE_CODE (ref) == IMAGPART_EXPR) + == void_node)) + return boolean_false_node; + return boolean_true_node; + case COMPONENT_REF: + if (check_mutable && DECL_MUTABLE_P (TREE_OPERAND (ref, 1))) + { + if (!ctx->quiet) + error_at (loc, "%qs on % sub-object %qD", + "__builtin_is_within_lifetime", + TREE_OPERAND (ref, 1)); + *non_constant_p = true; + return t; + } + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == UNION_TYPE) + { + tree union_type = TREE_TYPE (TREE_OPERAND (ref, 0)); + if (val == val_zero_init) + { + if (TREE_OPERAND (ref, 1) + != next_aggregate_field (TYPE_FIELDS (union_type))) + return boolean_false_node; + continue; + } + else if (val == val_uninit) + return boolean_false_node; + else + { + gcc_assert (TREE_CODE (val) == CONSTRUCTOR); + if (CONSTRUCTOR_NELTS (val) == 0) + { + if (CONSTRUCTOR_NO_CLEARING (val)) + return boolean_false_node; + tree first + = next_aggregate_field (TYPE_FIELDS (union_type)); + if (first != TREE_OPERAND (ref, 1)) + return boolean_false_node; + val = val_zero_init; + continue; + } + else + { + if (CONSTRUCTOR_ELT (val, 0)->index + != TREE_OPERAND (ref, 1)) + return boolean_false_node; + val = CONSTRUCTOR_ELT (val, 0)->value; + if (val == void_node) + return boolean_false_node; + continue; + } + } + } + if (val == val_zero_init || val == val_uninit) + continue; + gcc_assert (TREE_CODE (val) == CONSTRUCTOR); + unsigned int j; + tree field, value; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), j, field, value) + if (field == TREE_OPERAND (ref, 1)) + { + if (value == void_node) + return boolean_false_node; + val = value; + ref = NULL_TREE; + break; + } + if (ref == NULL_TREE) + continue; + if (CONSTRUCTOR_NO_CLEARING (val)) + val = val_uninit; + else + val = val_zero_init; + continue; + case ARRAY_REF: + field = eval_and_check_array_index (ctx, ref, false, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + if (*non_constant_p) + return t; + if (val == val_zero_init || val == val_uninit) + continue; + if (TREE_CODE (val) == STRING_CST) + return boolean_true_node; + gcc_assert (TREE_CODE (val) == CONSTRUCTOR); + HOST_WIDE_INT idx; + idx = find_array_ctor_elt (val, field, false); + if (idx != -1) + { + val = CONSTRUCTOR_ELT (val, idx)->value; + if (val == void_node) + return boolean_false_node; + continue; + } + if (CONSTRUCTOR_NO_CLEARING (val)) + val = val_uninit; + else + val = val_zero_init; + continue; + default: + gcc_unreachable (); + } + return boolean_true_node; +} + /* Attempt to evaluate T which represents a call to a builtin function. We assume here that all builtin functions evaluate to scalar types represented by _CST nodes. */ @@ -2606,6 +2858,10 @@ cxx_eval_builtin_function_call (const co return cxx_eval_constexpr_diag (ctx, t, non_constant_p, overflow_p, jump_target); + case CP_BUILT_IN_IS_WITHIN_LIFETIME: + return cxx_eval_is_within_lifetime (ctx, t, non_constant_p, overflow_p, + jump_target); + default: break; } @@ -8171,7 +8427,8 @@ cxx_eval_store_expression (const constex if (CLOBBER_KIND (init) >= CLOBBER_OBJECT_END && refs->is_empty ()) { - ctx->global->destroy_value (object); + ctx->global->destroy_value (object, (CLOBBER_KIND (init) + > CLOBBER_OBJECT_END)); return void_node; } --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-08-31 17:18:06.027032693 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime1.C 2026-08-31 17:18:06.027032693 +0200 @@ -0,0 +1,353 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +#if __has_builtin(__builtin_is_within_lifetime) +namespace std { + template + consteval bool + is_within_lifetime (const T *p) noexcept + { + return __builtin_is_within_lifetime (p); + } +} +#endif + +namespace std { + template + constexpr T + bit_cast (const F &f) noexcept + { + return __builtin_bit_cast (T, f); + } +} + +constexpr char d = 0; +struct E { char a; union F { int b; long c; short d; struct G { int e; } f[2]; } g; }; +constexpr E e = {}; +constexpr E f = { .a = 1, .g = { .f = {} } }; +constexpr int s = 42; +constexpr int t[2] = {}; + +consteval int +foo () +{ + char a = 0; + struct B { int b; } b = {}; + struct C : B { int c[2]; union D { int d; long e; } f; } c = {}; + union H { union I { int a; union J { int b; long c; } d; } e; long f; } g = {}; + H h; + C i; + char j; + bool k = true; + if (!std::is_within_lifetime (&a)) + return __LINE__; + if (!std::is_within_lifetime (&b)) + return __LINE__; + if (!std::is_within_lifetime (&b.b)) + return __LINE__; + if (!std::is_within_lifetime (&c)) + return __LINE__; + if (!std::is_within_lifetime (&c.b)) + return __LINE__; + if (!std::is_within_lifetime (&c.c)) + return __LINE__; + if (!std::is_within_lifetime (&c.c[1])) + return __LINE__; + if (!std::is_within_lifetime (&c.f)) + return __LINE__; + if (!std::is_within_lifetime (&c.f.d)) + return __LINE__; + if (std::is_within_lifetime (&c.f.e)) + return __LINE__; + if (!std::is_within_lifetime (&d)) + return __LINE__; + if (!std::is_within_lifetime (&e)) + return __LINE__; + if (!std::is_within_lifetime (&e.a)) + return __LINE__; + if (!std::is_within_lifetime (&e.g)) + return __LINE__; + if (!std::is_within_lifetime (&e.g.b)) + return __LINE__; + if (std::is_within_lifetime (&e.g.c)) + return __LINE__; + if (std::is_within_lifetime (&e.g.d)) + return __LINE__; + if (std::is_within_lifetime (&e.g.f)) + return __LINE__; + if (!std::is_within_lifetime (&f)) + return __LINE__; + if (!std::is_within_lifetime (&f.a)) + return __LINE__; + if (!std::is_within_lifetime (&f.g)) + return __LINE__; + if (std::is_within_lifetime (&f.g.b)) + return __LINE__; + if (std::is_within_lifetime (&f.g.c)) + return __LINE__; + if (std::is_within_lifetime (&f.g.d)) + return __LINE__; + if (!std::is_within_lifetime (&f.g.f)) + return __LINE__; + if (!std::is_within_lifetime (&f.g.f[0])) + return __LINE__; + if (!std::is_within_lifetime (&f.g.f[0].e)) + return __LINE__; + if (!std::is_within_lifetime (&f.g.f[1].e)) + return __LINE__; + if (!std::is_within_lifetime (&g)) + return __LINE__; + if (!std::is_within_lifetime (&g.e)) + return __LINE__; + if (std::is_within_lifetime (&g.f)) + return __LINE__; + if (!std::is_within_lifetime (&g.e.a)) + return __LINE__; + if (std::is_within_lifetime (&g.e.d)) + return __LINE__; + g.e.d.c = 1; + if (!std::is_within_lifetime (&g)) + return __LINE__; + if (!std::is_within_lifetime (&g.e)) + return __LINE__; + if (std::is_within_lifetime (&g.f)) + return __LINE__; + if (std::is_within_lifetime (&g.e.a)) + return __LINE__; + if (!std::is_within_lifetime (&g.e.d)) + return __LINE__; + if (std::is_within_lifetime (&g.e.d.b)) + return __LINE__; + if (!std::is_within_lifetime (&g.e.d.c)) + return __LINE__; + g.f = 42; + if (!std::is_within_lifetime (&g)) + return __LINE__; + if (std::is_within_lifetime (&g.e)) + return __LINE__; + if (!std::is_within_lifetime (&g.f)) + return __LINE__; + if (!std::is_within_lifetime (&h)) + return __LINE__; + if (std::is_within_lifetime (&h.e)) + return __LINE__; + if (std::is_within_lifetime (&h.f)) + return __LINE__; + if (!std::is_within_lifetime (&i)) + return __LINE__; + if (!std::is_within_lifetime (&i.b)) + return __LINE__; + if (!std::is_within_lifetime (&i.c[1])) + return __LINE__; + if (!std::is_within_lifetime (&i.f)) + return __LINE__; + if (std::is_within_lifetime (&i.f.d)) + return __LINE__; + if (std::is_within_lifetime (&i.f.e)) + return __LINE__; + if (!std::is_within_lifetime (&j)) + return __LINE__; + if (!std::is_within_lifetime (&k)) + return __LINE__; + unsigned char l = std::bit_cast (k); + if (!std::is_within_lifetime (&l)) + return __LINE__; + struct {} m; + if (!std::is_within_lifetime (&m)) + return __LINE__; + unsigned char n = std::bit_cast (m); + if (!std::is_within_lifetime (&n)) + return __LINE__; + int o; + if (!std::is_within_lifetime (static_cast (&o))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&o))) + return __LINE__; + volatile int p; + if (!std::is_within_lifetime (const_cast (&p))) + return __LINE__; + struct { union { union { struct { int a; long b; } c; int d; } e; int f; } g; int h; } q; + if (!std::is_within_lifetime (&q)) + return __LINE__; + if (!std::is_within_lifetime (&q.g)) + return __LINE__; + if (!std::is_within_lifetime (&q.h)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e)) + return __LINE__; + if (std::is_within_lifetime (&q.g.f)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.d)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.a)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.b)) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.h))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.f))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.d))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.a))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.b))) + return __LINE__; + q.g.f = 0; + if (!std::is_within_lifetime (&q)) + return __LINE__; + if (!std::is_within_lifetime (&q.g)) + return __LINE__; + if (!std::is_within_lifetime (&q.h)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.f)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.d)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.a)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.b)) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.h))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.f))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.d))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.a))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.b))) + return __LINE__; + q.g.e.d = 0; + if (!std::is_within_lifetime (&q)) + return __LINE__; + if (!std::is_within_lifetime (&q.g)) + return __LINE__; + if (!std::is_within_lifetime (&q.h)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e)) + return __LINE__; + if (std::is_within_lifetime (&q.g.f)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e.d)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.a)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.c.b)) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.h))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.f))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e.d))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.a))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.c.b))) + return __LINE__; + q.g.e.c.a = 0; + if (!std::is_within_lifetime (&q)) + return __LINE__; + if (!std::is_within_lifetime (&q.g)) + return __LINE__; + if (!std::is_within_lifetime (&q.h)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e)) + return __LINE__; + if (std::is_within_lifetime (&q.g.f)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e.c)) + return __LINE__; + if (std::is_within_lifetime (&q.g.e.d)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e.c.a)) + return __LINE__; + if (!std::is_within_lifetime (&q.g.e.c.b)) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.h))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.f))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e.c))) + return __LINE__; + if (std::is_within_lifetime (static_cast (&q.g.e.d))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e.c.a))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&q.g.e.c.b))) + return __LINE__; + struct { union { int a; short b; }; mutable int c; } r = { .b = 42 }; + if (!std::is_within_lifetime (&r)) + return __LINE__; + if (std::is_within_lifetime (&r.a)) + return __LINE__; + if (!std::is_within_lifetime (&r.b)) + return __LINE__; + if (!std::is_within_lifetime (&r.c)) + return __LINE__; + if (!std::is_within_lifetime (&s)) + return __LINE__; + if (!std::is_within_lifetime (const_cast (&s))) + return __LINE__; + if (!std::is_within_lifetime (const_cast (&s))) + return __LINE__; + if (!std::is_within_lifetime (static_cast (&s))) + return __LINE__; + if (!std::is_within_lifetime (t)) + return __LINE__; + if (!std::is_within_lifetime (t + 0)) + return __LINE__; + if (!std::is_within_lifetime (t + 1)) + return __LINE__; + int u[4]; + if (!std::is_within_lifetime (&u)) + return __LINE__; + if (!std::is_within_lifetime (&u[2])) + return __LINE__; + u[1] = 42; + if (!std::is_within_lifetime (&u)) + return __LINE__; + if (!std::is_within_lifetime (&u[2])) + return __LINE__; + return 0; +} + +static_assert (foo () == 0); --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C 2026-08-31 17:18:06.027133114 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime2.C 2026-08-31 17:18:06.027133114 +0200 @@ -0,0 +1,65 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +#include "../cpp2a/construct_at.h" + +namespace std { + template + consteval bool + is_within_lifetime (const T *p) noexcept + { + return __builtin_is_within_lifetime (p); + } +} + +consteval bool +foo (int &x, char &y) +{ + if (std::is_within_lifetime (&x) || std::is_within_lifetime (&y)) + return false; + std::construct_at (&y, 42); + if (std::is_within_lifetime (&x) || !std::is_within_lifetime (&y)) + return false; + std::construct_at (&x, 41); + if (!std::is_within_lifetime (&x) || std::is_within_lifetime (&y)) + return false; + return true; +} + +static_assert ([] { union { int a; char b; } u; return foo (u.a, u.b); } ()); +static_assert ([] { union { int a; char b; }; return foo (a, b); } ()); +static_assert ([] { struct { union { int a; char b; }; } s; return foo (s.a, s.b); } ()); +static_assert ([] { struct { union { int a; char b; } u; } s; return foo (s.u.a, s.u.b); } ()); + +consteval bool +bar () +{ + union { union { int a; long b; } c; short d; }; + if (std::is_within_lifetime (&d) + || std::is_within_lifetime (&c) + || std::is_within_lifetime (&c.a) + || std::is_within_lifetime (&c.b)) + return false; + std::construct_at (&d); + if (!std::is_within_lifetime (&d) + || std::is_within_lifetime (&c) + || std::is_within_lifetime (&c.a) + || std::is_within_lifetime (&c.b)) + return false; + std::construct_at (&c); + std::construct_at (&c.b); + if (std::is_within_lifetime (&d) + || !std::is_within_lifetime (&c) + || std::is_within_lifetime (&c.a) + || !std::is_within_lifetime (&c.b)) + return false; + std::construct_at (&c.a); + if (std::is_within_lifetime (&d) + || !std::is_within_lifetime (&c) + || !std::is_within_lifetime (&c.a) + || std::is_within_lifetime (&c.b)) + return false; + return true; +} + +static_assert (bar ()); --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C 2026-08-31 17:18:06.027254926 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime3.C 2026-08-31 17:51:06.105294794 +0200 @@ -0,0 +1,244 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +#include "../cpp2a/construct_at.h" + +namespace std { + template + consteval bool + is_within_lifetime (const T *p) noexcept + { + return __builtin_is_within_lifetime (p); + } +} + +consteval bool +foo (bool x) +{ + std::allocator a; + auto b = a.allocate (1); +#if 0 + // [allocator.members]/5 says it should start lifetime of the array + // but not its elements, and b points to the first element. + if (std::is_within_lifetime (b)) + return false; +#endif + std::construct_at (b); + if (!std::is_within_lifetime (b)) + return false; + std::destroy_at (b); + if (std::is_within_lifetime (b)) + return false; + a.deallocate (b, 1); + if (x) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on allocated storage after deallocation is not a constant expression" } + return true; +} + +static_assert (foo (false)); +bool a = foo (true); // { dg-error "call to consteval function 'foo\\\(true\\\)' is not a constant expression" } + +consteval bool +bar (int x) +{ + int *a; + decltype (nullptr) *b; + { + int c = 42; + a = &c; + if (!std::is_within_lifetime (a)) + return false; + } + { + int c = 42; + if (x == 1) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + } + if (x == 2) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + { + int c[42] = {}; + a = &c[40]; + if (!std::is_within_lifetime (a)) + return false; + } + if (x == 3) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + { + decltype (nullptr) d = nullptr; + b = &d; + if (!std::is_within_lifetime (b)) + return false; + } + { + decltype (nullptr) d = nullptr; + if (x == 4) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + } + if (x == 5) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + { + decltype (nullptr) d[42] = {}; + b = &d[40]; + if (!std::is_within_lifetime (b)) + return false; + } + if (x == 6) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + { + int c; + a = &c; + if (!std::is_within_lifetime (a)) + return false; + } + { + int c; + if (x == 7) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + } + if (x == 8) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + { + int c[42]; + a = &c[40]; + if (!std::is_within_lifetime (a)) + return false; + } + if (x == 9) + __builtin_is_within_lifetime (a); // { dg-error "'__builtin_is_within_lifetime' on 'c' outside its lifetime is not a constant expression" } + { + decltype (nullptr) d; + b = &d; + if (!std::is_within_lifetime (b)) + return false; + } + { + decltype (nullptr) d; + if (x == 10) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + } + if (x == 11) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + { + decltype (nullptr) d[42]; + b = &d[40]; + if (!std::is_within_lifetime (b)) + return false; + } + if (x == 12) + __builtin_is_within_lifetime (b); // { dg-error "'__builtin_is_within_lifetime' on 'd' outside its lifetime is not a constant expression" } + int e[2]; + if (!std::is_within_lifetime (&e) + || !std::is_within_lifetime (&e[0]) + || !std::is_within_lifetime (&e[1])) + return true; + std::destroy_at (&e[0]); + if (!std::is_within_lifetime (&e) + || std::is_within_lifetime (&e[0]) + || !std::is_within_lifetime (&e[1])) + return false; + std::construct_at (&e[0]); + std::destroy_at (&e[1]); + if (!std::is_within_lifetime (&e) + || !std::is_within_lifetime (&e[0]) + || std::is_within_lifetime (&e[1])) + return false; + std::construct_at (&e[1]); + if (!std::is_within_lifetime (&e) + || !std::is_within_lifetime (&e[0]) + || !std::is_within_lifetime (&e[1])) + return false; + struct { int a, b; } f; + if (!std::is_within_lifetime (&f) + || !std::is_within_lifetime (&f.a) + || !std::is_within_lifetime (&f.b)) + return true; + std::destroy_at (&f.a); + if (!std::is_within_lifetime (&f) + || std::is_within_lifetime (&f.a) + || !std::is_within_lifetime (&f.b)) + return false; + std::construct_at (&f.a); + std::destroy_at (&f.b); + if (!std::is_within_lifetime (&f) + || !std::is_within_lifetime (&f.a) + || std::is_within_lifetime (&f.b)) + return false; + std::construct_at (&f.b); + if (!std::is_within_lifetime (&f) + || !std::is_within_lifetime (&f.a) + || !std::is_within_lifetime (&f.b)) + return false; + return true; +} + +static_assert (bar (0)); +bool b = bar (1); // { dg-error "call to consteval function 'bar\\\(1\\\)' is not a constant expression" } +bool c = bar (2); // { dg-error "call to consteval function 'bar\\\(2\\\)' is not a constant expression" } +bool d = bar (3); // { dg-error "call to consteval function 'bar\\\(3\\\)' is not a constant expression" } +bool e = bar (4); // { dg-error "call to consteval function 'bar\\\(4\\\)' is not a constant expression" } +bool f = bar (5); // { dg-error "call to consteval function 'bar\\\(5\\\)' is not a constant expression" } +bool g = bar (6); // { dg-error "call to consteval function 'bar\\\(6\\\)' is not a constant expression" } +bool h = bar (7); // { dg-error "call to consteval function 'bar\\\(7\\\)' is not a constant expression" } +bool i = bar (8); // { dg-error "call to consteval function 'bar\\\(8\\\)' is not a constant expression" } +bool j = bar (9); // { dg-error "call to consteval function 'bar\\\(9\\\)' is not a constant expression" } +bool k = bar (10); // { dg-error "call to consteval function 'bar\\\(10\\\)' is not a constant expression" } +bool l = bar (11); // { dg-error "call to consteval function 'bar\\\(11\\\)' is not a constant expression" } +bool m = bar (12); // { dg-error "call to consteval function 'bar\\\(12\\\)' is not a constant expression" } + +struct A { int a = 42; bool b = std::is_within_lifetime (&a); }; +constexpr A n; + +struct B { consteval B () : a (42), b (std::is_within_lifetime (&a)) {} int a; bool b; }; +constexpr B o; + +struct C { consteval C () { __builtin_is_within_lifetime (this); } } p; + +struct D { + consteval D () : a (0), b (0), c (0) {} + consteval D (int x, int y, int z) + : a (x), b (y + std::is_within_lifetime (&this->a) * 32 + + std::is_within_lifetime (&this->b) * 64 + + std::is_within_lifetime (&this->c) * 128), c (z) {} + constexpr ~D () {} + int a, b, c; +}; + +consteval int +qux () +{ + D a[2] = {}; + std::destroy_at (&a[0]); + std::construct_at (&a[0], 4, 5, 6); + return a[0].a + a[0].b + a[0].c; +} + +// FIXME: In mem-initializer of b, a should be already constructed, +// so within lifetime, but b is in the middle of construction and c +// construction has not started yet. +static_assert (qux () == 4 + 5 + 6 + 32); // { dg-bogus "note: the comparison reduces to '\\\(239 == 47\\\)'" "" { xfail *-*-* } } + // { dg-bogus "static assertion failed" "" { xfail *-*-* } .-1 } + +consteval bool +corge (bool x) +{ + int *p; + { + int a; + p = &a; + if (!std::is_within_lifetime (&a)) + return false; + std::destroy_at (&a); + if (std::is_within_lifetime (&a)) + return false; + std::construct_at (&a); + if (!std::is_within_lifetime (&a)) + return false; + } + if (x) + __builtin_is_within_lifetime (p); // { dg-error "'__builtin_is_within_lifetime' on 'a' outside its lifetime is not a constant expression" } + return true; +} + +static_assert (corge (false)); +bool q = corge (true); // { dg-error "call to consteval function 'corge\\\(true\\\)' is not a constant expression" } --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C 2026-08-31 17:18:06.027418748 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime4.C 2026-08-31 17:18:06.027418748 +0200 @@ -0,0 +1,157 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } + +template +consteval bool baz (const T *p) { return __builtin_is_within_lifetime (p); } +// { dg-error "'__builtin_is_within_lifetime' on 'a' from outside current evaluation is not a constant expression" "" { target *-*-* } .-1 } +bool a = baz (&a); // { dg-error "is not a constant expression" } +constexpr bool b = false; + +void +foo (int *p) +{ + __builtin_is_within_lifetime (&b); + __builtin_is_within_lifetime (nullptr); // { dg-error "'__builtin_is_within_lifetime' argument type 'std::nullptr_t' is not pointer type" } + __builtin_is_within_lifetime (static_cast (nullptr)); // { dg-error "is not a constant expression" } + // { dg-error "'__builtin_is_within_lifetime' called with a null pointer" "" { target *-*-* } .-1 } + __builtin_is_within_lifetime (&foo); // { dg-error "is not a constant expression" } + // { dg-error "'__builtin_is_within_lifetime' called with pointer to function" "" { target *-*-* } .-1 } + __builtin_is_within_lifetime (0); // { dg-error "'__builtin_is_within_lifetime' argument type 'int' is not pointer type" } + __builtin_is_within_lifetime (0.0); // { dg-error "'__builtin_is_within_lifetime' argument type 'double' is not pointer type" } + __builtin_is_within_lifetime (); // { dg-error "'__builtin_is_within_lifetime' needs a single argument" } + __builtin_is_within_lifetime (&b, &b); // { dg-error "'__builtin_is_within_lifetime' needs a single argument" } + __builtin_is_within_lifetime (p); // { dg-error "call to consteval function '__builtin_is_within_lifetime\\\(p\\\)' is not a constant expression" } + // { dg-error "'p' is not a constant expression" "" { target *-*-* } .-1 } +} + +extern int v; + +consteval bool +bar (int x) +{ + switch (x) + { + case 0: __builtin_is_within_lifetime (&b); break; + case 1: __builtin_is_within_lifetime (static_cast (nullptr)); break; // { dg-error "'__builtin_is_within_lifetime' called with a null pointer" } + case 2: __builtin_is_within_lifetime (&foo); break; // { dg-error "'__builtin_is_within_lifetime' called with pointer to function" } + case 3: __builtin_is_within_lifetime (&v); break; // { dg-error "'__builtin_is_within_lifetime' on 'v' from outside current evaluation is not a constant expression" } + } + return true; +} + +static_assert (bar (0)); +bool c = bar (1); // { dg-error "is not a constant expression" } +bool d = bar (2); // { dg-error "is not a constant expression" } +bool e = bar (3); // { dg-error "is not a constant expression" } + +constexpr struct { union { int a; short b; }; mutable int c; } g = { .b = 42 }; +constexpr int s = 42; +constexpr int t[2] = {}; + +consteval bool +qux (int x) +{ + if (!__builtin_is_within_lifetime (&g)) + return false; + if (__builtin_is_within_lifetime (&g.a)) + return false; + if (!__builtin_is_within_lifetime (&g.b)) + return false; + if (x == 1) + __builtin_is_within_lifetime (&g.c); // { dg-error "'__builtin_is_within_lifetime' on 'mutable' sub-object '::c'" } + else if (x == 2) + __builtin_is_within_lifetime (&s + 1); // { dg-error "'__builtin_is_within_lifetime' on '\\\*\\\(\\\(\\\& s\\\) \\\+ 4\\\)' from outside current evaluation is not a constant expression" } + else if (x == 3) + __builtin_is_within_lifetime (t + 2); // { dg-error "array subscript value '2' is outside the bounds of array 't' of type 'const int \\\[2\\\]'" } + return true; +} + +static_assert (qux (0)); +bool h = qux (1); // { dg-error "is not a constant expression" } +bool i = qux (2); // { dg-error "is not a constant expression" } +bool j = qux (3); // { dg-error "is not a constant expression" } + +struct A { + constexpr A () {} + constexpr A (const A &) {} + constexpr ~A () {} +}; + +template +constexpr T & +corge (T &&x) +{ + return static_cast (x); +} + +consteval bool +fred () +{ + static_assert (__builtin_is_within_lifetime (&corge (0))); + static_assert (__builtin_is_within_lifetime (&corge (A {}))); + if (!__builtin_is_within_lifetime (&corge (0))) + return false; + if (!__builtin_is_within_lifetime (&corge (A {}))) + return false; + return true; +} + +static_assert (fred ()); + +constexpr const int &k = 0; +static_assert (__builtin_is_within_lifetime (&k)); + +template +consteval T * +waldo () +{ + T t; + return &t; // { dg-warning "address of local variable 't' returned" } + +} + +constexpr bool l = __builtin_is_within_lifetime (waldo ()); // { dg-error "'__builtin_is_within_lifetime' on 't' outside its lifetime is not a constant expression" } +constexpr bool m = __builtin_is_within_lifetime (waldo ()); // { dg-error "'__builtin_is_within_lifetime' on 't' outside its lifetime is not a constant expression" } + +template +struct integral_constant +{ + static constexpr T value = V; + using value_type = T; + using type = integral_constant ; + constexpr operator value_type () const noexcept { return value; } + constexpr value_type operator () () const noexcept { return value; } +}; + +template +using bool_constant = integral_constant ; + +using true_type = bool_constant ; +using false_type = bool_constant ; + +constexpr int n = 42; + +template +concept B = bool_constant <__builtin_is_within_lifetime (T ())>::value; + +static_assert (B <[] { return &n; }>); +static_assert (!B <[] { return static_cast (nullptr); }>); +static_assert (!B <[] { return static_cast (&foo); }>); + +template +constexpr true_type +garply () requires B +{ + return {}; +} + +template +false_type +garply () +{ + return {}; +} + +static_assert (decltype (garply <[] { return &n; }> ())::value); +static_assert (!decltype (garply <[] { return static_cast (nullptr); }> ())::value); +true_type (*o) () = &garply <[] { return &n; }>; --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C 2026-08-31 17:18:06.027556081 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime5.C 2026-08-31 17:18:06.027556081 +0200 @@ -0,0 +1,46 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++23 } } +// { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } } + +namespace std { + template + consteval bool + is_within_lifetime (const T *p) noexcept + { + return __builtin_is_within_lifetime (p); + } +} + +struct OptBool { + union { bool b; char c; }; + + // note: this assumes common implementation properties for bool and char: + // * sizeof (bool) == sizeof (char), and + // * the value representations for true and false are distinct + // from the value representation for 2 + constexpr OptBool () : c (2) { } + constexpr OptBool (bool b) : b (b) { } + + constexpr bool has_value () const + { + if consteval + { + return std::is_within_lifetime (&b); // during constant evaluation, cannot read from c + } + else + { + return c != 2; // during runtime, must read from c + } + } + + constexpr const bool &operator * () const { return b; } +}; + +constexpr OptBool disengaged; +constexpr OptBool engaged (true); +static_assert (!disengaged.has_value ()); +static_assert (engaged.has_value ()); +static_assert (*engaged); +constexpr OptBool engaged2 (false); +static_assert (engaged2.has_value ()); +static_assert (!*engaged2); --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C 2026-08-31 17:18:06.027652156 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime6.C 2026-08-31 17:18:06.027652156 +0200 @@ -0,0 +1,41 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++26 } } +// { dg-skip-if "sizeof (bool) > 1" { powerpc-*-darwin* } } + +#include + +#if __cpp_lib_within_lifetime >= 202306L +struct OptBool { + union { bool b; char c; }; + + // note: this assumes common implementation properties for bool and char: + // * sizeof (bool) == sizeof (char), and + // * the value representations for true and false are distinct + // from the value representation for 2 + constexpr OptBool () : c (2) { } + constexpr OptBool (bool b) : b (b) { } + + constexpr bool has_value () const + { + if consteval + { + return std::is_within_lifetime (&b); // during constant evaluation, cannot read from c + } + else + { + return c != 2; // during runtime, must read from c + } + } + + constexpr const bool &operator * () const { return b; } +}; +#endif + +constexpr OptBool disengaged; +constexpr OptBool engaged (true); +static_assert (!disengaged.has_value ()); +static_assert (engaged.has_value ()); +static_assert (*engaged); +constexpr OptBool engaged2 (false); +static_assert (engaged2.has_value ()); +static_assert (!*engaged2); --- a/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C 2026-08-31 17:18:06.027754340 +0200 +++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime7.C 2026-08-31 17:18:06.027754340 +0200 @@ -0,0 +1,75 @@ +// P3450R1 - Extend std::is_within_lifetime +// { dg-do compile { target c++20 } } +// { dg-options "" } + +#include "../cpp2a/construct_at.h" + +consteval bool +foo (int n) +{ + int a[n]; + if (!__builtin_is_within_lifetime (&a)) + return false; + if (!__builtin_is_within_lifetime (&a[0])) + return false; + if (!__builtin_is_within_lifetime (&a[n / 2])) + return false; + if (!__builtin_is_within_lifetime (&a[n - 1])) + return false; + std::destroy_at (&a[0]); + if (!__builtin_is_within_lifetime (&a)) + return false; + if (__builtin_is_within_lifetime (&a[0])) + return false; + if (!__builtin_is_within_lifetime (&a[n / 2])) + return false; + if (!__builtin_is_within_lifetime (&a[n - 1])) + return false; + std::construct_at (&a[0]); + std::destroy_at (&a[n / 2]); + if (!__builtin_is_within_lifetime (&a)) + return false; + if (!__builtin_is_within_lifetime (&a[0])) + return false; + if (__builtin_is_within_lifetime (&a[n / 2])) + return false; + if (!__builtin_is_within_lifetime (&a[n - 1])) + return false; + std::construct_at (&a[n / 2]); + std::destroy_at (&a[n - 1]); + if (!__builtin_is_within_lifetime (&a)) + return false; + if (!__builtin_is_within_lifetime (&a[0])) + return false; + if (!__builtin_is_within_lifetime (&a[n / 2])) + return false; + if (__builtin_is_within_lifetime (&a[n - 1])) + return false; + std::construct_at (&a[n - 1]); + _Complex double b = 1.0; + if (!__builtin_is_within_lifetime (&b)) + return false; + if (!__builtin_is_within_lifetime (&__real__ b)) + return false; + if (!__builtin_is_within_lifetime (&__imag__ b)) + return false; + std::destroy_at (&__real__ b); + if (!__builtin_is_within_lifetime (&b)) + return false; + if (__builtin_is_within_lifetime (&__real__ b)) + return false; + if (!__builtin_is_within_lifetime (&__imag__ b)) + return false; + std::construct_at (&__real__ b); + std::destroy_at (&__imag__ b); + if (!__builtin_is_within_lifetime (&b)) + return false; + if (!__builtin_is_within_lifetime (&__real__ b)) + return false; + if (__builtin_is_within_lifetime (&__imag__ b)) + return false; + std::construct_at (&__imag__ b); + return true; +} + +static_assert (foo (42)); --- a/libstdc++-v3/include/bits/version.def 2026-08-27 17:41:43.926633177 +0200 +++ b/libstdc++-v3/include/bits/version.def 2026-08-31 17:18:06.028272627 +0200 @@ -2497,6 +2497,15 @@ ftms = { }; ftms = { + name = within_lifetime; + values = { + v = 202306; + cxxmin = 26; + extra_cond = "__has_builtin(__builtin_is_within_lifetime)"; + }; +}; + +ftms = { name = hardened_array; values = { v = 202502; --- a/libstdc++-v3/include/bits/version.h 2026-08-27 17:41:43.926633177 +0200 +++ b/libstdc++-v3/include/bits/version.h 2026-08-31 17:18:06.028561440 +0200 @@ -2749,6 +2749,16 @@ #endif /* !defined(__cpp_lib_valarray) */ #undef __glibcxx_want_valarray +#if !defined(__cpp_lib_within_lifetime) +# if (__cplusplus > 202302L) && (__has_builtin(__builtin_is_within_lifetime)) +# define __glibcxx_within_lifetime 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_within_lifetime) +# define __cpp_lib_within_lifetime 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_within_lifetime) */ +#undef __glibcxx_want_within_lifetime + #if !defined(__cpp_lib_hardened_array) # if (__cplusplus >= 201103L) && (defined(_GLIBCXX_ASSERTIONS)) # define __glibcxx_hardened_array 202502L --- a/libstdc++-v3/include/std/type_traits 2026-08-27 17:41:43.927633164 +0200 +++ b/libstdc++-v3/include/std/type_traits 2026-08-31 17:18:06.028956596 +0200 @@ -66,6 +66,7 @@ #define __glibcxx_want_type_trait_variable_templates #define __glibcxx_want_unwrap_ref #define __glibcxx_want_void_t +#define __glibcxx_want_within_lifetime #include extern "C++" @@ -4400,6 +4401,13 @@ template= 202306L // C++ >= 26 + template + consteval bool + is_within_lifetime(const _Tp* __p) noexcept + { return __builtin_is_within_lifetime (__p); } +#endif + /// @} group metaprogramming _GLIBCXX_END_NAMESPACE_VERSION --- a/libstdc++-v3/src/c++23/std.cc.in 2026-08-27 17:41:43.927633164 +0200 +++ b/libstdc++-v3/src/c++23/std.cc.in 2026-08-31 17:18:06.029869556 +0200 @@ -3610,6 +3610,9 @@ export namespace std using std::is_structural; using std::is_structural_v; #endif +#if __cpp_lib_within_lifetime >= 202306L + using std::is_within_lifetime; +#endif } //