From patchwork Tue Apr 7 06:55:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 132735 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 103A64BA2E13 for ; Tue, 7 Apr 2026 06:57:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 103A64BA2E13 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=A78nsJB6 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 DBCC44BA2E0E for ; Tue, 7 Apr 2026 06:55:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DBCC44BA2E0E 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 DBCC44BA2E0E 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=1775544934; cv=none; b=FBfRR4O43Ao+CRyVg0reg2REklxN/ZqqPfuEJ7E7E3UHionf8n32S7GeZTK/uqJQfPvgn2mdeFWMPxVbnpqUB7IiXLqQ1jIBtM0Ij+VSeX2qTEcnzUKU0riUzuqu46YGBCNJ5A1QoUYX24NGFM16NethiAIH4O87TFDGfo+wtFU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775544934; c=relaxed/simple; bh=/ICTNcp7mRkoI5SGvVsDT/5jgs7v1gZ6pKXvzFsZHRQ=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=h/Lu2emprA6omN+Tf34uefLiKzcjoM4BtCY1omWP31zKzgfW3/VYsFx95AIpctNyKkoB9OGJlKcRIv8IVQv6srmi2Kexa472wXDR17pZjIPK/eRtUDNKCGjODcASumb6uBdNzcuJlL6mima/Wyhu8lxi6ALjd6iS4bzx0dTU/14= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DBCC44BA2E0E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1775544933; 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; bh=VRH2CbEdGw//w2BcTsf7R/70f3PWoIDomfU6n1+xjHk=; b=A78nsJB61Igu6diJLH7Se1dUA4hsuPpV/gID7RdJnnZ1GFMLWe+JW92V3WCna9XAayfghA lnElfjl9h1d8QOhI9XanWnPtbRUkEoSF/RZQ0wRyO/gPgE0lN5PXpd1apLMjCfCXOGHVDF yOTlspW36cuvQIYpWz1F1mJ4BIhYjBQ= Received: from mx-prod-mc-06.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-345-F0zupIzNN1CriMTjCp-7Zw-1; Tue, 07 Apr 2026 02:55:32 -0400 X-MC-Unique: F0zupIzNN1CriMTjCp-7Zw-1 X-Mimecast-MFC-AGG-ID: F0zupIzNN1CriMTjCp-7Zw_1775544931 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 202641800283 for ; Tue, 7 Apr 2026 06:55:31 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.44.48.95]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7A13C1800762; Tue, 7 Apr 2026 06:55:30 +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 6376tROM1620688 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 7 Apr 2026 08:55:27 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.18.1/8.18.1/Submit) id 6376tRCe1620686; Tue, 7 Apr 2026 08:55:27 +0200 Date: Tue, 7 Apr 2026 08:55:27 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: Implement CWG3140 - Allowing expansion over non-constant std::array Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: OWmh1mzYPiWHTS7W8E4fkMQ39DQbfW_ZKq7RDYNMcVU_1775544931 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, PROLO_LEO1, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP 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 Hi! The following patch implements CWG3140, which allows non-constexpr iterating expansion statements over non-constexpr ranges like std::array where the range size is still known at compile time. On the other side it breaks iterating expansion statements without constexpr keyword on range-decl over ranges where to find out the range size the range needs to be accessed. In some cases one will have to just add the constexpr keyword, in other cases even that doesn't have to work (e.g. when the iterator is constexpr except for operator* or some non-constexpr conversion operator is involved). Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-04-07 Jakub Jelinek * cp-tree.h: Implement C++26 CWG3140 - Allowing expansion over non-constant std::array. (cp_build_range_for_decls): Change last argument from bool to tree. (build_range_temp): Likewise and default it to NULL_TREE rather than false. * parser.cc (build_range_temp): Remove expansion_stmt_p argument, add expansion_stmt_decl. Don't call cp_build_qualified_type if expansion_stmt_decl was not declared constexpr. (cp_build_range_for_decls): Remove expansion_stmt_p argument, add expansion_stmt_decl. Pass expansion_stmt_decl to build_range_temp. Don't set TREE_STATIC, TREE_PUBLIC, DECL_COMMON, DECL_INTERFACE_KNOWN, DECL_DECLARED_CONSTEXPR_P and TREE_READONLY on range_temp if expansion_stmt_decl was not declared constexpr. Don't call cp_build_qualified_type on begin type nor set TREE_STATIC etc. on begin if expansion_stmt_decl was not declared constexpr. If expansion_stmt_decl is non-NULL, don't build end at all, instead pass begin_expr and end_expr in end_p[0] and end_p[1] to the caller. (cp_convert_range_for): Adjust cp_build_range_for_decls caller. * pt.cc (finish_expansion_stmt): Likewise. Use begin_expr and end_expr instead of begin and end variables from cp_build_range_for_decls, make them non-constant and avoid spurious -Wunused-result/nodiscard diagnostics. * g++.dg/cpp26/expansion-stmt11.C: Expect some extra diagnostics for C++11. * g++.dg/cpp26/expansion-stmt13.C: Likewise. Make it dg-do compile test for c++11_only and dg-do run only for c++14 and above. * g++.dg/cpp26/expansion-stmt16.C: Adjust expected diagnostics. * g++.dg/cpp26/expansion-stmt19.C: Expect some extra diagnostics for C++11. Make it dg-do compile test for c++11_only and dg-do run only for c++14 and above. * g++.dg/cpp26/expansion-stmt25.C (foo): Test both constexpr range-for-decl and non-constexpr, adjust expected diagnostics. * g++.dg/cpp26/expansion-stmt30.C: Adjust expected diagnostics. * g++.dg/cpp26/expansion-stmt35.C: New test. * g++.dg/cpp26/expansion-stmt36.C: New test. * g++.dg/cpp26/expansion-stmt37.C: New test. * g++.dg/cpp26/expansion-stmt38.C: New test. Jakub --- gcc/cp/cp-tree.h.jj 2026-04-03 20:46:22.845328260 +0200 +++ gcc/cp/cp-tree.h 2026-04-06 09:59:23.095379300 +0200 @@ -8028,10 +8028,10 @@ extern tree clone_attrs (tree); extern bool maybe_clone_body (tree); /* In parser.cc */ -extern tree cp_build_range_for_decls (location_t, tree, tree *, bool); +extern tree cp_build_range_for_decls (location_t, tree, tree *, tree); extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool, tree, bool); -extern tree build_range_temp (tree, bool = false); +extern tree build_range_temp (tree, tree = NULL_TREE); extern tree cp_perform_range_for_lookup (tree, tree *, tree *, tsubst_flags_t = tf_warning_or_error); extern void cp_convert_omp_range_for (tree &, tree &, tree &, --- gcc/cp/parser.cc.jj 2026-04-04 11:32:27.440454554 +0200 +++ gcc/cp/parser.cc 2026-04-06 09:59:23.100379216 +0200 @@ -15854,16 +15854,17 @@ cp_parser_range_for (cp_parser *parser, builds up the range temporary. */ tree -build_range_temp (tree range_expr, bool expansion_stmt_p /* = false */) +build_range_temp (tree range_expr, tree expansion_stmt_decl /* = NULL_TREE */) { tree range_type, auto_node; - if (expansion_stmt_p) + if (expansion_stmt_decl) { /* Build const decltype(auto) __range = range_expr; - range_expr provided by the caller already is (range_expr). */ - auto_node = make_decltype_auto (); - range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST); + range_type = auto_node = make_decltype_auto (); + if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl)) + range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST); } else { @@ -16007,11 +16008,13 @@ warn_for_range_copy (tree decl, tree exp /* Helper function for cp_convert_range_for and finish_expansion_stmt. Build the __range, __begin and __end declarations. Return the - __begin VAR_DECL, set *END_P to the __end VAR_DECL. */ + __begin VAR_DECL, set *END_P to the __end VAR_DECL. If + EXPANSION_STMT_DECL, don't create __end and instead store + begin_expr to END_P[0] and end_expr to END_P[1]. */ tree cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p, - bool expansion_stmt_p) + tree expansion_stmt_decl) { tree iter_type, begin_expr, end_expr; @@ -16023,30 +16026,34 @@ cp_build_range_for_decls (location_t loc { tree range_temp; - if (!expansion_stmt_p + if (!expansion_stmt_decl && VAR_P (range_expr) && array_of_runtime_bound_p (TREE_TYPE (range_expr))) /* Can't bind a reference to an array of runtime bound. */ range_temp = range_expr; else { - if (expansion_stmt_p) + if (expansion_stmt_decl) { /* Build constexpr decltype(auto) __for_range = (range_expr); */ location_t range_loc = cp_expr_loc_or_loc (range_expr, loc); range_expr = finish_parenthesized_expr (cp_expr (range_expr, range_loc)); - range_temp = build_range_temp (range_expr, true); + range_temp = build_range_temp (range_expr, expansion_stmt_decl); /* When P2686R4 is fully implemented, these 3 sets of TREE_STATIC (on range_temp, begin and end) should be removed as per - CWG3044. */ - TREE_STATIC (range_temp) = 1; - TREE_PUBLIC (range_temp) = 0; - DECL_COMMON (range_temp) = 0; - DECL_INTERFACE_KNOWN (range_temp) = 1; - DECL_DECLARED_CONSTEXPR_P (range_temp) = 1; - TREE_READONLY (range_temp) = 1; + CWG3044. If expansion_stmt_decl is not constexpr, we don't + need the static though. */ + if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl)) + { + TREE_STATIC (range_temp) = 1; + TREE_PUBLIC (range_temp) = 0; + DECL_COMMON (range_temp) = 0; + DECL_INTERFACE_KNOWN (range_temp) = 1; + DECL_DECLARED_CONSTEXPR_P (range_temp) = 1; + TREE_READONLY (range_temp) = 1; + } } else /* Build auto &&__for_range = range_expr; */ @@ -16063,12 +16070,14 @@ cp_build_range_for_decls (location_t loc } /* The new for initialization statement. */ - if (expansion_stmt_p && !TYPE_REF_P (iter_type)) + if (expansion_stmt_decl + && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl) + && !TYPE_REF_P (iter_type)) iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST); tree begin = build_decl (loc, VAR_DECL, for_begin__identifier, iter_type); TREE_USED (begin) = 1; DECL_ARTIFICIAL (begin) = 1; - if (expansion_stmt_p) + if (expansion_stmt_decl && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl)) { TREE_STATIC (begin) = 1; DECL_DECLARED_CONSTEXPR_P (begin) = 1; @@ -16079,21 +16088,18 @@ cp_build_range_for_decls (location_t loc /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); - if (cxx_dialect >= cxx17) + if (expansion_stmt_decl) { - iter_type = cv_unqualified (TREE_TYPE (end_expr)); - if (expansion_stmt_p && !TYPE_REF_P (iter_type)) - iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST); + end_p[0] = begin_expr; + end_p[1] = end_expr; + return begin; } + + if (cxx_dialect >= cxx17) + iter_type = cv_unqualified (TREE_TYPE (end_expr)); tree end = build_decl (loc, VAR_DECL, for_end__identifier, iter_type); TREE_USED (end) = 1; DECL_ARTIFICIAL (end) = 1; - if (expansion_stmt_p) - { - TREE_STATIC (end) = 1; - DECL_DECLARED_CONSTEXPR_P (end) = 1; - TREE_READONLY (end) = 1; - } pushdecl (end); cp_finish_decl (end, end_expr, /*is_constant_init*/false, NULL_TREE, @@ -16149,7 +16155,7 @@ cp_convert_range_for (tree statement, tr if (range_decl == error_mark_node) range_expr = error_mark_node; tree begin - = cp_build_range_for_decls (input_location, range_expr, &end, false); + = cp_build_range_for_decls (input_location, range_expr, &end, NULL_TREE); finish_init_stmt (statement); --- gcc/cp/pt.cc.jj 2026-04-04 11:07:32.000119351 +0200 +++ gcc/cp/pt.cc 2026-04-06 10:59:16.537372085 +0200 @@ -33329,34 +33329,46 @@ finish_expansion_stmt (tree expansion_st if (kind == esk_iterating) { /* Iterating expansion statements. */ - tree end; - begin = cp_build_range_for_decls (loc, expansion_init, &end, true); - if (!error_operand_p (begin) && !error_operand_p (end)) + tree exprs[2]; + begin = cp_build_range_for_decls (loc, expansion_init, exprs, range_decl); + if (!error_operand_p (begin) + && !error_operand_p (exprs[0]) + && !error_operand_p (exprs[1])) { /* In the standard this is all evaluated inside of a consteval lambda. So, force in_immediate_context () around this. */ in_consteval_if_p_temp_override icip; in_consteval_if_p = true; - tree i - = build_target_expr_with_type (begin, - cv_unqualified (TREE_TYPE (begin)), - tf_warning_or_error); + tree b = exprs[0], e = exprs[1]; + if (TREE_CODE (b) == TARGET_EXPR) + b = TARGET_EXPR_INITIAL (b); + if (TREE_CODE (e) == TARGET_EXPR) + e = TARGET_EXPR_INITIAL (e); + b = force_target_expr (cv_unqualified (TREE_TYPE (b)), b, + tf_warning_or_error); + e = force_target_expr (cv_unqualified (TREE_TYPE (e)), e, + tf_warning_or_error); tree w = build_stmt (loc, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); tree r = get_target_expr (build_zero_cst (ptrdiff_type_node)); - tree iinc = build_x_unary_op (loc, PREINCREMENT_EXPR, - TARGET_EXPR_SLOT (i), NULL_TREE, + tree binc = build_x_unary_op (loc, PREINCREMENT_EXPR, + TARGET_EXPR_SLOT (b), NULL_TREE, tf_warning_or_error); tree rinc = build2 (PREINCREMENT_EXPR, ptrdiff_type_node, TARGET_EXPR_SLOT (r), build_int_cst (ptrdiff_type_node, 1)); - WHILE_BODY (w) = build_compound_expr (loc, iinc, rinc); - WHILE_COND (w) = build_x_binary_op (loc, NE_EXPR, i, ERROR_MARK, - end, ERROR_MARK, NULL_TREE, NULL, + WHILE_BODY (w) = build_compound_expr (loc, binc, rinc); + WHILE_COND (w) = build_x_binary_op (loc, NE_EXPR, b, ERROR_MARK, + e, ERROR_MARK, NULL_TREE, NULL, tf_warning_or_error); - tree e = build_compound_expr (loc, r, i); - e = build_compound_expr (loc, e, w); - e = build_compound_expr (loc, e, TARGET_EXPR_SLOT (r)); + { + warning_sentinel wur (warn_unused_result); + e = build_compound_expr (loc, b, e); + e = build_compound_expr (loc, r, e); + e = build_compound_expr (loc, e, w); + e = build_compound_expr (loc, e, TARGET_EXPR_SLOT (r)); + } + e = fold_build_cleanup_point_expr (TREE_TYPE (e), e); e = cxx_constant_value (e); if (tree_fits_uhwi_p (e)) n = tree_to_uhwi (e); @@ -33481,13 +33493,18 @@ finish_expansion_stmt (tree expansion_st tf_warning_or_error); auto_node = make_auto (); iter_type = do_auto_deduction (auto_node, iter_init, auto_node); - if (!TYPE_REF_P (iter_type)) + if (DECL_DECLARED_CONSTEXPR_P (range_decl) + && !TYPE_REF_P (iter_type)) iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST); iter = build_decl (loc, VAR_DECL, NULL_TREE, iter_type); TREE_USED (iter) = 1; DECL_ARTIFICIAL (iter) = 1; - TREE_STATIC (iter) = 1; - DECL_DECLARED_CONSTEXPR_P (iter) = 1; + if (DECL_DECLARED_CONSTEXPR_P (range_decl)) + { + TREE_STATIC (iter) = 1; + DECL_DECLARED_CONSTEXPR_P (iter) = 1; + TREE_READONLY (iter) = 1; + } pushdecl (iter); cp_finish_decl (iter, iter_init, /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); --- gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C.jj 2026-03-27 10:17:15.653305952 +0100 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C 2026-04-06 11:10:33.407077674 +0200 @@ -9,7 +9,7 @@ struct T { using type = T; int s; }; T d = { 8 }; struct U { constexpr const S *begin () const { return &c[0]; } - constexpr const S *end () const { return &c[s]; } + constexpr const S *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } } int s; }; struct V { int a; long b; double c; }; @@ -29,7 +29,7 @@ foo () template for (auto g : u) // { dg-warning "'template for' only available with" "" { target c++23_down } } { decltype(g)::type h = g; - } + } // { dg-message "was not declared 'constexpr'" "" { target c++11_down } } V v = { 9, 10L, 11.0 }; template for (auto g : v) // { dg-warning "'template for' only available with" "" { target c++23_down } } { --- gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C.jj 2026-03-27 10:17:15.653305952 +0100 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C 2026-04-07 08:21:54.478390430 +0200 @@ -1,5 +1,6 @@ // C++26 P1306R5 - Expansion statements -// { dg-do run { target c++11 } } +// { dg-do run { target c++14 } } +// { dg-do compile { target c++11_only } } // { dg-options "" } namespace std { @@ -11,7 +12,7 @@ struct S { int s; }; constexpr S c[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } }; struct U { constexpr const S *begin () const { return &c[0]; } - constexpr const S *end () const { return &c[s]; } + constexpr const S *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } } int s; }; constexpr U u1 = { 3 }, u2 = { 0 }; @@ -45,7 +46,7 @@ foo () template for (auto h = 2; constexpr auto g : u1) // { dg-warning "'template for' only available with" "" { target c++23_down } } r += g.s + h; template for (long long h = ++r; auto g : u2) // { dg-warning "'template for' only available with" "" { target c++23_down } } - __builtin_abort (); + __builtin_abort (); // { dg-message "was not declared 'constexpr'" "" { target c++11_down } } return r; } --- gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C.jj 2026-03-27 10:17:15.653305952 +0100 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C 2026-04-06 11:18:26.642182557 +0200 @@ -49,12 +49,11 @@ foo () template for (constexpr auto g : d) // { dg-warning "'template for' only available with" "" { target c++23_down } } ; // { dg-error "'d' is not a constant expression" "" { target c++14 } .-1 } // { dg-error "call to non-'constexpr' function 'const A\\\* C::begin\\\(\\\) const'" "" { target c++11_down } .-1 } - // { dg-error "call to non-'constexpr' function 'const A\\\* C::end\\\(\\\) const'" "" { target c++11_down } .-2 } - // { dg-error "the type 'const C' of 'constexpr' variable '__for_range ' is not literal" "" { target c++11_down } .-3 } + // { dg-error "the type 'const C' of 'constexpr' variable '__for_range ' is not literal" "" { target c++11_down } .-2 } constexpr D e = { 3 }; template for (constexpr auto g : e) // { dg-warning "'template for' only available with" "" { target c++23_down } } ; // { dg-error "'e' is not a constant expression" "" { target c++14 } .-1 } - // { dg-error "call to non-'constexpr' function 'const A\\\* D::end\\\(\\\) const'" "" { target *-*-* } .-1 } + // { dg-error "call to non-'constexpr' function 'const A\\\* D::end\\\(\\\) const'" "" { target c++11_down } .-1 } constexpr E f = { 3 }; template for (constexpr auto g : f) // { dg-warning "'template for' only available with" "" { target c++23_down } } ; // { dg-error "'f' is not a constant expression" "" { target c++14 } .-1 } --- gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C.jj 2026-03-27 10:17:15.653305952 +0100 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C 2026-04-07 08:22:10.880110548 +0200 @@ -1,5 +1,6 @@ // C++26 P1306R5 - Expansion statements -// { dg-do run { target c++11 } } +// { dg-do run { target c++14 } } +// { dg-do compile { target c++11_only } } // { dg-options "" } namespace std { @@ -18,7 +19,7 @@ template struct std::tuple_elemen constexpr V c[] = { { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 } }; struct U { constexpr const V *begin () const { return &c[0]; } - constexpr const V *end () const { return &c[s]; } + constexpr const V *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } } int s; }; constexpr U u1 = { 3 }, u2 = { 0 }; @@ -51,6 +52,7 @@ foo () template for (long long h = ++r; auto [...i, j] : u2) // { dg-warning "'template for' only available with" "" { target c++23_down } } __builtin_abort (); // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 } // { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } .-2 } + // { dg-message "was not declared 'constexpr'" "" { target c++11_down } .-3 } return r; } --- gcc/testsuite/g++.dg/cpp26/expansion-stmt25.C.jj 2026-04-01 19:09:34.867401760 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt25.C 2026-04-06 09:59:23.103559220 +0200 @@ -22,6 +22,8 @@ namespace N void foo () { - template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } + template for (constexpr auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } ; // { dg-error "no match for 'operator-' in '__for_begin - __for_begin ' \\\(operand types are 'const A' and 'const A'\\\)" "" { target *-*-* } .-1 } + template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } + ; // { dg-error "no match for 'operator-' in '__for_begin - __for_begin ' \\\(operand types are 'A' and 'A'\\\)" "" { target *-*-* } .-1 } } --- gcc/testsuite/g++.dg/cpp26/expansion-stmt30.C.jj 2026-03-27 10:17:15.654305935 +0100 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt30.C 2026-04-06 11:12:43.266911186 +0200 @@ -30,7 +30,7 @@ foo () for (auto i : N::S {}) // { dg-error "call of overloaded 'begin\\\(N::S\\\&\\\)' is ambiguous" } ; template for (auto i : N::S {}) - ; // { dg-error "call of overloaded 'begin\\\(const N::S\\\&\\\)' is ambiguous" } + ; // { dg-error "call of overloaded 'begin\\\(N::S\\\&\\\)' is ambiguous" } template for (auto i : V {}) ; // { dg-error "cannot be used as a function" } template for (auto i : O::B {}) --- gcc/testsuite/g++.dg/cpp26/expansion-stmt35.C.jj 2026-04-06 11:37:50.160784900 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt35.C 2026-04-06 11:53:22.034260816 +0200 @@ -0,0 +1,60 @@ +// CWG3140 - Allowing expansion over non-constant std::array +// { dg-do compile { target c++14 } } +// { dg-options "" } + +struct A +{ + int x; + constexpr explicit A (int v) : x(v) {} + constexpr A &operator ++ () { ++x; return *this; } + constexpr int operator * () const { return x; } + constexpr bool operator != (const A &o) const { return x != o.x; } + constexpr A operator + (int o) const { A r (x + o); return r; } + constexpr int operator - (const A &o) const { return x - o.x; } +}; + +namespace N +{ + struct B { constexpr B (int n) : b (n) {} int b; }; + constexpr A begin (const B &) { return A (0); } + constexpr A end (const B &x) { return A (x.b); } // { dg-error "is not usable in a constant expression" } + struct C { }; + constexpr A begin (const C &) { return A (0); } + constexpr A end (const C &x) { return A (6); } +} + +int +foo () +{ + int r = 0; + template for (constexpr auto m : N::B (3)) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += m; + return r; +} + +int +bar () +{ + int r = 0; + template for (auto m : N::B (3)) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += m; // { dg-message "was not declared 'constexpr'" } + return r; +} + +int +baz () +{ + int r = 0; + template for (constexpr auto m : N::C ()) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += m; + return r; +} + +int +qux () +{ + int r = 0; + template for (auto m : N::C ()) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += m; + return r; +} --- gcc/testsuite/g++.dg/cpp26/expansion-stmt36.C.jj 2026-04-06 11:37:57.111669031 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt36.C 2026-04-06 11:55:13.017411979 +0200 @@ -0,0 +1,21 @@ +// CWG3140 - Allowing expansion over non-constant std::array +// { dg-do run { target c++17 } } +// { dg-options "" } + +#include + +std::array +foo () +{ + return { 1, 2, 3, 4, 5 }; +} + +int +main () +{ + int r = 0; + template for (auto n : foo ()) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += n; + if (r != 15) + __builtin_abort (); +} --- gcc/testsuite/g++.dg/cpp26/expansion-stmt37.C.jj 2026-04-06 11:39:45.982855213 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt37.C 2026-04-06 12:00:39.720970271 +0200 @@ -0,0 +1,19 @@ +// CWG3140 - Allowing expansion over non-constant std::array +// { dg-do compile { target c++17 } } +// { dg-options "" } + +#include + +std::array +foo () +{ + return { 1, 2, 3, 4, 5 }; +} + +void +bar () +{ + int r = 0; + template for (constexpr auto n : foo ()) // { dg-warning "'template for' only available with" "" { target c++23_down } } + r += n; // { dg-error " call to non-'constexpr' function" "" { target *-*-* } .-1 } +} --- gcc/testsuite/g++.dg/cpp26/expansion-stmt38.C.jj 2026-04-06 11:40:59.562629467 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt38.C 2026-04-06 11:59:16.769351934 +0200 @@ -0,0 +1,36 @@ +// CWG3140 - Allowing expansion over non-constant std::array +// { dg-do run { target c++17 } } +// { dg-options "" } +// { dg-additional-options "-frange-for-ext-temps" { target c++20_down } } + +#include + +struct A { + A () { a++; } + A (const A &) { a++; } + ~A () { a--; } + static int a; +}; +int A::a = 0; + +std::array +foo (A, A, A) +{ + return { 1, 2, 3, 4, 5 }; +} + +int +main () +{ + int r = 0; + if (A::a != 0) + __builtin_abort (); + template for (auto n : foo (A (), A (), A ())) // { dg-warning "'template for' only available with" "" { target c++23_down } } + { + if (A::a != 3) + __builtin_abort (); + r += n; + } + if (r != 15 || A::a != 0) + __builtin_abort (); +}