From patchwork Tue Oct 15 17:35:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1997595 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=IrnHcOhM; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XSh8r6csTz1xv6 for ; Wed, 16 Oct 2024 04:36:11 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3E60E3858C53 for ; Tue, 15 Oct 2024 17:36:09 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 5E9623858D20 for ; Tue, 15 Oct 2024 17:35:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5E9623858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5E9623858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729013730; cv=none; b=UvXED9982ih7G6wPjB3THd5waC2pn7npfPj62lOrzVbQTIL9IAPn0gjjNXugv2si7PteHRSu6ytANC/uC95fWSh+h/3jPX6Clceao6Qma5ck/ZDxIuTZc7/S4ooj1o547q9ELG6WelUfWz2qiMGYZusbw5Ft76+qQS/h7Z54Kpg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729013730; c=relaxed/simple; bh=KSBNwpdx692zEKN8DMeiFYY4z9RKVmZTVFmV+65kwr8=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=PdHM805aGM5skZY12gKQg3XGfGJfrpdVAI3vxdVjCBcugp9+NHnCJ0+FtaEwhBcR+3NFTv7VJnkV2tPteDPbzdPeSVaiix8moCv9LtRK/RcyWgDaRvlE7zOgyIm0z4YsIpMzfimfv3CTUqAG2YJPXwbjKKiNAzj4ssYEo8qoj8w= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729013724; 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=C351Pih8PxvZ+zS9pzGVTjvQYsLGjRgp7eKEmtQLc9s=; b=IrnHcOhMT70ibm3X7FBYIL1T9KLl+hU1zg9t7r1K6hYw0RU4PoNNJSuup5B3D8zl4d3S60 uQhczNwD4YWBukTDNZ3HUnfG5hm/R3HgDgmTXOKhzJ68jirCLwBCO7j6fCGM2AQt+i3gm/ PwwzFqksdj/LgVvtRsksjDM1jOiD/60= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-658-w2YJdZBwP_aUUSzrPC7V6Q-1; Tue, 15 Oct 2024 13:35:22 -0400 X-MC-Unique: w2YJdZBwP_aUUSzrPC7V6Q-1 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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D1F231956096 for ; Tue, 15 Oct 2024 17:35:20 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.16]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D7F7F19560A2; Tue, 15 Oct 2024 17:35:18 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 49FHZGYu1539087 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 15 Oct 2024 19:35:16 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 49FHZFLc1537863; Tue, 15 Oct 2024 19:35:15 +0200 Date: Tue, 15 Oct 2024 19:35:15 +0200 From: Jakub Jelinek To: Joseph Myers Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c, v3: Implement C2Y N3355 - Named Loops [PR117022] Message-ID: References: <5a92f693-8b11-42c7-36c9-1cd36932a7e2@redhat.com> <9ab49d96-2c57-7f1b-f2b6-4f7ff05b756e@redhat.com> MIME-Version: 1.0 In-Reply-To: <9ab49d96-2c57-7f1b-f2b6-4f7ff05b756e@redhat.com> X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=no 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: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org On Tue, Oct 15, 2024 at 05:00:04PM +0000, Joseph Myers wrote: > What happens with a statement attribute on the iteration or switch > statement? > > label: [[]] for (;;) break label; Except for the omp::directive/omp::sequence attributes label is accepted as loop label. Those OpenMP attributes make the label ignored right now (it effectively is parsed as if there is a #pragma omp in between and loops are parsed completely differently). > There are no standard statement attributes (and the only non-OMP one we > handle is musttail, which isn't applicable on such statements) so this > example uses [[]]. The wording in the paper about "is an iteration or > switch statement" isn't wonderfully clear about the case of an iteration > or switch statement with attributes on it (and while we discussed the > pragma case in WG14, the attribute case wasn't mentioned). Whatever we > do, there should be an associated test. Added test coverage. > > /* Parse a statement, other than a labeled statement. CHAIN is a vector > > @@ -7662,6 +7706,7 @@ c_parser_statement (c_parser *parser, bo > > > > static void > > c_parser_statement_after_labels (c_parser *parser, bool *if_p, > > + tree before_labels, > > vec *chain, attr_state astate) > > Should update the comment on this function to mention the new parameter. > Likewise all other functions with this parameter added. Done. Here is a new version of the patch, tested on the dg.exp=*named-loops* tests fine, I think it doesn't need more testing given that it is just comment changes in code plus testsuite changes. 2024-10-12 Jakub Jelinek PR c/117022 gcc/c-family/ * c-common.def (FOR_STMT, WHILE_STMT, DO_STMT, BREAK_STMT, CONTINUE_STMT, SWITCH_STMT): Add an extra operand, *_NAME and document it. * c-common.h (bc_hash_map_t): New typedef. (struct bc_state): Add bc_hash_map member. (WHILE_NAME, DO_NAME, FOR_NAME, BREAK_NAME, CONTINUE_NAME, SWITCH_STMT_NAME): Define. * c-pretty-print.cc (c_pretty_printer::statement): Print BREAK_STMT or CONTINUE_STMT operand if any. * c-gimplify.cc (bc_hash_map): New static variable. (note_named_bc, release_named_bc): New functions. (save_bc_state): Save and clear bc_hash_map. (restore_bc_state): Assert NULL and restore bc_hash_map. (genericize_c_loop): Add NAME argument, call note_named_bc and release_named_bc if non-NULL around the body walk. (genericize_for_stmt, genericize_while_stmt, genericize_do_stmt): Adjust callers of it. (genericize_switch_stmt): Rename break_block variable to blab. Call note_named_bc and release_named_bc if SWITCH_STMT_NAME is non-NULL around the body walk. (genericize_continue_stmt): Handle non-NULL CONTINUE_NAME. (genericize_break_stmt): Handle non-NULL BREAK_NAME. (c_genericize): Delete and clear bc_hash_map. gcc/c/ * c-tree.h: Implement C2Y N3355 - Named loops. (C_DECL_LOOP_NAME, C_DECL_SWITCH_NAME, C_DECL_LOOP_SWITCH_NAME_VALID, C_DECL_LOOP_SWITCH_NAME_USED, IN_NAMED_STMT): Define. (c_get_loop_names, c_release_loop_names, c_finish_bc_name): Declare. (c_start_switch): Add NAME argument. (c_finish_bc_stmt): Likewise. * c-lang.h (struct language_function): Add loop_names and loop_names_hash members. * c-parser.cc (c_parser_external_declaration, c_parser_declaration_or_fndef, c_parser_struct_or_union_specifier, c_parser_parameter_declaration): Adjust c_parser_pragma caller. (get_before_labels): New function. (c_parser_compound_statement_nostart): Call get_before_labels when needed, adjust c_parser_pragma and c_parser_statement_after_labels callers. (c_parser_statement): Call get_before_labels first and pass it to c_parser_statement_after_labels. (c_parser_bc_name): New function. (c_parser_statement_after_labels): Add BEFORE_LABELS argument. Pass it down to c_parser_switch_statement, c_parser_while_statement, c_parser_do_statement, c_parser_for_statement and c_parser_pragma. Call c_parser_bc_name for RID_BREAK and RID_CONTINUE and pass it as another argument to c_finish_bc_stmt. (c_parser_if_body, c_parser_else_body): Call get_before_labels early and pass it to c_parser_statement_after_labels. (c_parser_switch_statement): Add BEFORE_LABELS argument. Call c_get_loop_names, if named, pass switch_name to c_start_switch, mark it valid and set IN_NAMED_STMT bit in in_statement before parsing body, otherwise clear IN_NAMED_STMT bit before that parsing. Run c_release_loop_names at the end. (c_parser_while_statement, c_parser_do_statement, c_parser_for_statement): Add BEFORE_LABELS argument. Call c_get_loop_names, if named, mark it valid and set IN_NAMED_STMT bit in in_statement before parsing body, otherwise clear IN_NAMED_STMT before that parsing, arrange for the loop name if used to be another *_STMT argument. (c_parser_objc_class_instance_variables, c_parser_objc_methodprotolist): Adjust c_parser_pragma callers. (c_parser_pragma): Add BEFORE_LABELS argument. Pass it down to c_parser_for_statement, c_parser_while_statement or c_parser_do_statement. (c_parser_omp_loop_nest, c_maybe_parse_omp_decl): Adjust c_parser_pragma callers. * c-decl.cc (loop_names, loop_names_hash): New static variables. (add_stmt): Set STATEMENT_LIST_HAS_LABEL after push_stmt_list rather than before it. (c_push_function_context): Save and clear loop_names and loop_names_hash. (c_pop_function_context): Release or delete, restore and clear loop_names and loop_names_hash. (c_get_loop_names, c_release_loop_names, c_finish_bc_name): New functions. * c-typeck.cc (c_start_switch): Add SWITCH_NAME argument, pass it down to build_stmt. (c_finish_bc_stmt): Add NAME argument. Mark of IN_NAMED_STMT bit of in_statement in swtiches. Use label for IN_OBJC_FOREACH only if name is NULL. If name is non-NULL and C_DECL_LOOP_NAME and C_DECL_SWITCH_NAME are both set, assume outer ObjC foreach and dig labels from DECL_CHAIN of name. Pass NAME to build_stmt otherwise. gcc/cp/ * semantics.cc (begin_while_stmt, begin_do_stmt, begin_for_stmt, finish_break_stmt, finish_continue_stmt, begin_switch_stmt): Pass another NULL_TREE to build_stmt calls. gcc/testsuite/ * gcc.dg/c23-named-loops-1.c: New test. * gcc.dg/c23-named-loops-5.c: New test. * gcc.dg/c2y-named-loops-1.c: New test. * gcc.dg/c2y-named-loops-2.c: New test. * gcc.dg/c2y-named-loops-4.c: New test. * gcc.dg/c2y-named-loops-5.c: New test. * gcc.dg/c2y-named-loops-6.c: New test. * gcc.dg/c2y-named-loops-7.c: New test. * gcc.dg/gnu99-named-loops-1.c: New test. * gcc.dg/gnu99-named-loops-2.c: New test. * gcc.dg/gnu99-named-loops-3.c: New test. * gcc.dg/gnu99-named-loops-4.c: New test. * gcc.dg/gnu2y-named-loops-3.c: New test. * gcc.dg/gomp/named-loops-1.c: New test. * gcc.dg/gomp/named-loops-2.c: New test. * objc.dg/named-loops-1.m: New test. Jakub --- gcc/c-family/c-common.def.jj 2024-10-12 10:40:15.807536031 +0200 +++ gcc/c-family/c-common.def 2024-10-15 19:10:03.854008532 +0200 @@ -58,28 +58,31 @@ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", DEFTREECODE (PAREN_SIZEOF_EXPR, "paren_sizeof_expr", tcc_expression, 1) /* Used to represent a `for' statement. The operands are - FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, and FOR_SCOPE, + FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE, and FOR_NAME respectively. */ -DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5) +DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 6) -/* Used to represent a 'while' statement. The operands are WHILE_COND - and WHILE_BODY, respectively. */ -DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 2) - -/* Used to represent a 'do' statement. The operands are DO_COND and - DO_BODY, respectively. */ -DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 2) - -/* Used to represent a 'break' statement. */ -DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 0) - -/* Used to represent a 'continue' statement. */ -DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 0) +/* Used to represent a 'while' statement. The operands are WHILE_COND, + WHILE_BODY, and WHILE_NAME, respectively. */ +DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 3) + +/* Used to represent a 'do' statement. The operands are DO_COND, DO_BODY, + and DO_NAME, respectively. */ +DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 3) + +/* Used to represent a 'break' statement. The operand BREAK_NAME is + the {FOR,WHILE,DO,SWITCH}_NAME to which it applies. NULL_TREE means + innermost. */ +DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 1) + +/* Used to represent a 'continue' statement. The operand CONTINUE_NAME is + the {FOR,WHILE,DO}_STMT to which it applies. NULL_TREE means innermost. */ +DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 1) /* Used to represent a 'switch' statement. The operands are - SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, and - SWITCH_STMT_SCOPE, respectively. */ -DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 4) + SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, SWITCH_STMT_SCOPE, + and SWITCH_STMT_NAME, respectively. */ +DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 5) /* Extensions for C++ Concepts. */ --- gcc/c-family/c-common.h.jj 2024-10-12 10:40:15.841535558 +0200 +++ gcc/c-family/c-common.h 2024-10-15 19:10:03.854008532 +0200 @@ -1216,9 +1216,13 @@ extern const char *c_get_substring_locat location_t *out_loc); /* In c-gimplify.cc. */ +typedef hash_map> bc_hash_map_t; typedef struct bc_state { tree bc_label[2]; + bc_hash_map_t *bc_hash_map; } bc_state_t; extern void save_bc_state (bc_state_t *); extern void restore_bc_state (bc_state_t *); @@ -1501,29 +1505,39 @@ extern tree build_userdef_literal (tree tree num_string); -/* WHILE_STMT accessors. These give access to the condition of the - while statement and the body of the while statement, respectively. */ +/* WHILE_STMT accessors. These give access to the condition of the + while statement, the body and name of the while statement, respectively. */ #define WHILE_COND(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0) #define WHILE_BODY(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1) +#define WHILE_NAME(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 2) -/* DO_STMT accessors. These give access to the condition of the do - statement and the body of the do statement, respectively. */ +/* DO_STMT accessors. These give access to the condition of the do + statement, the body and name of the do statement, respectively. */ #define DO_COND(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 0) #define DO_BODY(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 1) +#define DO_NAME(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 2) -/* FOR_STMT accessors. These give access to the init statement, - condition, update expression, and body of the for statement, +/* FOR_STMT accessors. These give access to the init statement, + condition, update expression, body and name of the for statement, respectively. */ #define FOR_INIT_STMT(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 0) #define FOR_COND(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 1) #define FOR_EXPR(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 2) #define FOR_BODY(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 3) #define FOR_SCOPE(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 4) +#define FOR_NAME(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 5) + +/* BREAK_STMT accessors. */ +#define BREAK_NAME(NODE) TREE_OPERAND (BREAK_STMT_CHECK (NODE), 0) + +/* CONTINUE_STMT accessors. */ +#define CONTINUE_NAME(NODE) TREE_OPERAND (CONTINUE_STMT_CHECK (NODE), 0) #define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0) #define SWITCH_STMT_BODY(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1) #define SWITCH_STMT_TYPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 2) #define SWITCH_STMT_SCOPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 3) +#define SWITCH_STMT_NAME(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 4) /* True if there are case labels for all possible values of switch cond, either because there is a default: case label or because the case label ranges cover all values. */ --- gcc/c-family/c-pretty-print.cc.jj 2024-10-12 10:40:15.932534293 +0200 +++ gcc/c-family/c-pretty-print.cc 2024-10-15 19:10:03.855008518 +0200 @@ -2943,8 +2943,23 @@ c_pretty_printer::statement (tree t) continue ; return expression(opt) ; */ case BREAK_STMT: + pp_string (this, "break"); + if (BREAK_NAME (t)) + { + pp_space (this); + pp_c_tree_decl_identifier (this, BREAK_NAME (t)); + } + pp_c_semicolon (this); + pp_needs_newline (this) = true; + break; + case CONTINUE_STMT: - pp_string (this, TREE_CODE (t) == BREAK_STMT ? "break" : "continue"); + pp_string (this, "continue"); + if (CONTINUE_NAME (t)) + { + pp_space (this); + pp_c_tree_decl_identifier (this, CONTINUE_NAME (t)); + } pp_c_semicolon (this); pp_needs_newline (this) = true; break; --- gcc/c-family/c-gimplify.cc.jj 2024-10-12 10:40:15.883534974 +0200 +++ gcc/c-family/c-gimplify.cc 2024-10-15 19:10:03.856008504 +0200 @@ -133,6 +133,10 @@ enum bc_t { bc_break = 0, bc_continue = linked through TREE_CHAIN. */ static tree bc_label[2]; +/* Hash map from loop/switch names (identified by LABEL_DECL) to + corresponding break and (if any) continue labels. */ +static bc_hash_map_t *bc_hash_map; + /* Begin a scope which can be exited by a break or continue statement. BC indicates which. @@ -172,6 +176,26 @@ finish_bc_block (tree *block, enum bc_t DECL_CHAIN (label) = NULL_TREE; } +/* For named loop or switch with NAME, remember corresponding break + label BLAB and continue label CLAB. */ + +static void +note_named_bc (tree name, tree blab, tree clab) +{ + if (bc_hash_map == NULL) + bc_hash_map = new bc_hash_map_t (32); + bc_hash_map->put (name, std::make_pair (blab, clab)); +} + +/* Remove NAME from the map after processing body of the loop or + switch. */ + +static void +release_named_bc (tree name) +{ + bc_hash_map->remove (name); +} + /* Allow saving and restoring break/continue state. */ void @@ -179,8 +203,10 @@ save_bc_state (bc_state_t *state) { state->bc_label[bc_break] = bc_label[bc_break]; state->bc_label[bc_continue] = bc_label[bc_continue]; + state->bc_hash_map = bc_hash_map; bc_label[bc_break] = NULL_TREE; bc_label[bc_continue] = NULL_TREE; + bc_hash_map = NULL; } void @@ -188,8 +214,10 @@ restore_bc_state (bc_state_t *state) { gcc_assert (bc_label[bc_break] == NULL); gcc_assert (bc_label[bc_continue] == NULL); + gcc_assert (bc_hash_map == NULL); bc_label[bc_break] = state->bc_label[bc_break]; bc_label[bc_continue] = state->bc_label[bc_continue]; + bc_hash_map = state->bc_hash_map; } /* Get the LABEL_EXPR to represent a break or continue statement @@ -229,8 +257,9 @@ expr_loc_or_loc (const_tree expr, locati static void genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body, - tree incr, bool cond_is_first, int *walk_subtrees, - void *data, walk_tree_fn func, walk_tree_lh lh) + tree incr, tree name, bool cond_is_first, + int *walk_subtrees, void *data, walk_tree_fn func, + walk_tree_lh lh) { tree blab, clab; tree entry = NULL, exit = NULL, t; @@ -245,10 +274,15 @@ genericize_c_loop (tree *stmt_p, locatio blab = begin_bc_block (bc_break, start_locus); clab = begin_bc_block (bc_continue, start_locus); + if (name) + note_named_bc (name, blab, clab); walk_tree_1 (&body, func, data, NULL, lh); *walk_subtrees = 0; + if (name) + release_named_bc (name); + /* If condition is zero don't generate a loop construct. */ if (cond && integer_zerop (cond)) { @@ -373,8 +407,8 @@ genericize_for_stmt (tree *stmt_p, int * } genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt), - FOR_BODY (stmt), FOR_EXPR (stmt), 1, walk_subtrees, - data, func, lh); + FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (stmt), 1, + walk_subtrees, data, func, lh); append_to_statement_list (loop, &expr); if (expr == NULL_TREE) expr = loop; @@ -389,8 +423,8 @@ genericize_while_stmt (tree *stmt_p, int { tree stmt = *stmt_p; genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt), - WHILE_BODY (stmt), NULL_TREE, 1, walk_subtrees, - data, func, lh); + WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt), 1, + walk_subtrees, data, func, lh); } /* Genericize a DO_STMT node *STMT_P. */ @@ -401,8 +435,8 @@ genericize_do_stmt (tree *stmt_p, int *w { tree stmt = *stmt_p; genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt), - DO_BODY (stmt), NULL_TREE, 0, walk_subtrees, - data, func, lh); + DO_BODY (stmt), NULL_TREE, DO_NAME (stmt), 0, + walk_subtrees, data, func, lh); } /* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR. */ @@ -412,7 +446,7 @@ genericize_switch_stmt (tree *stmt_p, in walk_tree_fn func, walk_tree_lh lh) { tree stmt = *stmt_p; - tree break_block, body, cond, type; + tree blab, body, cond, type; location_t stmt_locus = EXPR_LOCATION (stmt); body = SWITCH_STMT_BODY (stmt); @@ -423,19 +457,25 @@ genericize_switch_stmt (tree *stmt_p, in walk_tree_1 (&cond, func, data, NULL, lh); - break_block = begin_bc_block (bc_break, stmt_locus); + blab = begin_bc_block (bc_break, stmt_locus); + if (SWITCH_STMT_NAME (stmt)) + note_named_bc (SWITCH_STMT_NAME (stmt), blab, NULL_TREE); walk_tree_1 (&body, func, data, NULL, lh); + + if (SWITCH_STMT_NAME (stmt)) + release_named_bc (SWITCH_STMT_NAME (stmt)); + walk_tree_1 (&type, func, data, NULL, lh); *walk_subtrees = 0; - if (TREE_USED (break_block)) - SWITCH_BREAK_LABEL_P (break_block) = 1; - finish_bc_block (&body, bc_break, break_block); + if (TREE_USED (blab)) + SWITCH_BREAK_LABEL_P (blab) = 1; + finish_bc_block (&body, bc_break, blab); *stmt_p = build2_loc (stmt_locus, SWITCH_EXPR, type, cond, body); SWITCH_ALL_CASES_P (*stmt_p) = SWITCH_STMT_ALL_CASES_P (stmt); gcc_checking_assert (!SWITCH_STMT_NO_BREAK_P (stmt) - || !TREE_USED (break_block)); + || !TREE_USED (blab)); } /* Genericize a CONTINUE_STMT node *STMT_P. */ @@ -445,7 +485,16 @@ genericize_continue_stmt (tree *stmt_p) { tree stmt_list = NULL; tree pred = build_predict_expr (PRED_CONTINUE, NOT_TAKEN); - tree label = get_bc_label (bc_continue); + tree label; + if (CONTINUE_NAME (*stmt_p)) + { + tree_pair *slot = bc_hash_map->get (CONTINUE_NAME (*stmt_p)); + gcc_checking_assert (slot); + label = slot->second; + TREE_USED (label) = 1; + } + else + label = get_bc_label (bc_continue); location_t location = EXPR_LOCATION (*stmt_p); tree jump = build1_loc (location, GOTO_EXPR, void_type_node, label); append_to_statement_list_force (pred, &stmt_list); @@ -458,7 +507,16 @@ genericize_continue_stmt (tree *stmt_p) static void genericize_break_stmt (tree *stmt_p) { - tree label = get_bc_label (bc_break); + tree label; + if (BREAK_NAME (*stmt_p)) + { + tree_pair *slot = bc_hash_map->get (BREAK_NAME (*stmt_p)); + gcc_checking_assert (slot); + label = slot->first; + TREE_USED (label) = 1; + } + else + label = get_bc_label (bc_break); location_t location = EXPR_LOCATION (*stmt_p); *stmt_p = build1_loc (location, GOTO_EXPR, void_type_node, label); } @@ -615,6 +673,8 @@ c_genericize (tree fndecl) hash_set pset; walk_tree (&DECL_SAVED_TREE (fndecl), c_genericize_control_r, &pset, &pset); + delete bc_hash_map; + bc_hash_map = NULL; restore_bc_state (&save_state); pop_cfun (); } --- gcc/c/c-tree.h.jj 2024-10-12 10:40:16.164531067 +0200 +++ gcc/c/c-tree.h 2024-10-15 19:10:03.856008504 +0200 @@ -90,6 +90,24 @@ along with GCC; see the file COPYING3. #define C_DECL_BUILTIN_PROTOTYPE(EXP) \ DECL_LANG_FLAG_6 (FUNCTION_DECL_CHECK (EXP)) +/* For LABEL_DECLs marks canonical name of a loop. */ +#define C_DECL_LOOP_NAME(EXP) DECL_LANG_FLAG_3 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical name of a switch. During parsing of + ObjC foreach named loop both C_DECL_LOOP_NAME and C_DECL_SWITCH_NAME + are temporarily set. */ +#define C_DECL_SWITCH_NAME(EXP) DECL_LANG_FLAG_5 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical name of a loop or switch being + valid for use in break identifier or continue identifier statements. */ +#define C_DECL_LOOP_SWITCH_NAME_VALID(EXP) \ + DECL_LANG_FLAG_6 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical loop or switch names which were actually + used in one or more break identifier or continue identifier statements. */ +#define C_DECL_LOOP_SWITCH_NAME_USED(EXP) \ + DECL_LANG_FLAG_8 (LABEL_DECL_CHECK (EXP)) + /* Record whether a decl was declared register. This is strictly a front-end flag, whereas DECL_REGISTER is used for code generation; they may differ for structures with volatile fields. */ @@ -611,12 +629,15 @@ extern struct obstack parser_obstack; to IN_OMP_BLOCK if parsing OpenMP structured block and IN_OMP_FOR if parsing OpenMP loop. If parsing a switch statement, this is bitwise ORed with IN_SWITCH_STMT, unless parsing an - iteration-statement, OpenMP block or loop within that switch. */ + iteration-statement, OpenMP block or loop within that switch. + If the innermost iteration/switch statement is named, IN_NAMED_STMT + is additionally bitwise ORed into it. */ #define IN_SWITCH_STMT 1 #define IN_ITERATION_STMT 2 #define IN_OMP_BLOCK 4 #define IN_OMP_FOR 8 #define IN_OBJC_FOREACH 16 +#define IN_NAMED_STMT 32 extern unsigned char in_statement; extern bool switch_statement_break_seen_p; @@ -723,6 +744,9 @@ extern struct c_declspecs *declspecs_add struct c_declspecs *, tree); extern struct c_declspecs *finish_declspecs (struct c_declspecs *); extern size_t c_tree_size (enum tree_code); +extern int c_get_loop_names (tree, bool, tree *); +extern void c_release_loop_names (int); +extern tree c_finish_bc_name (location_t, tree, bool); /* in c-objc-common.cc */ extern bool c_objc_common_init (void); @@ -812,7 +836,7 @@ extern void process_init_element (locati extern tree build_compound_literal (location_t, tree, tree, bool, unsigned int, struct c_declspecs *); extern void check_compound_literal_type (location_t, struct c_type_name *); -extern tree c_start_switch (location_t, location_t, tree, bool); +extern tree c_start_switch (location_t, location_t, tree, bool, tree); extern void c_finish_switch (tree, tree); extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool, bool); @@ -828,7 +852,7 @@ extern tree c_finish_stmt_expr (location extern tree c_process_expr_stmt (location_t, tree); extern tree c_finish_expr_stmt (location_t, tree); extern tree c_finish_return (location_t, tree, tree, bool = false); -extern tree c_finish_bc_stmt (location_t, tree, bool); +extern tree c_finish_bc_stmt (location_t, tree, bool, tree); extern tree c_finish_goto_label (location_t, tree); extern tree c_finish_goto_ptr (location_t, c_expr val); extern tree c_expr_to_decl (tree, bool *, bool *); --- gcc/c/c-lang.h.jj 2024-10-12 10:40:16.047532694 +0200 +++ gcc/c/c-lang.h 2024-10-15 19:10:03.856008504 +0200 @@ -54,6 +54,8 @@ struct GTY(()) language_function { unsigned char x_in_statement; struct c_switch * GTY((skip)) x_switch_stack; struct c_arg_info * GTY((skip)) arg_info; + vec GTY((skip)) loop_names; + decl_tree_map * GTY((skip)) loop_names_hash; int returns_value; int returns_null; int returns_abnormally; --- gcc/c/c-parser.cc.jj 2024-10-15 19:09:09.716747876 +0200 +++ gcc/c/c-parser.cc 2024-10-15 19:13:39.533066744 +0200 @@ -1670,18 +1670,19 @@ static tree c_parser_compound_statement static location_t c_parser_compound_statement_nostart (c_parser *); static void c_parser_label (c_parser *, tree); static void c_parser_statement (c_parser *, bool *, location_t * = NULL); -static void c_parser_statement_after_labels (c_parser *, bool *, +static void c_parser_statement_after_labels (c_parser *, bool *, tree, vec * = NULL, attr_state = {}); static tree c_parser_c99_block_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec *); -static void c_parser_switch_statement (c_parser *, bool *); +static void c_parser_switch_statement (c_parser *, bool *, tree); static void c_parser_while_statement (c_parser *, bool, unsigned short, bool, - bool *); -static void c_parser_do_statement (c_parser *, bool, unsigned short, bool); + bool *, tree); +static void c_parser_do_statement (c_parser *, bool, unsigned short, bool, + tree); static void c_parser_for_statement (c_parser *, bool, unsigned short, bool, - bool *); + bool *, tree); static tree c_parser_asm_statement (c_parser *); static tree c_parser_asm_operands (c_parser *); static tree c_parser_asm_goto_operands (c_parser *); @@ -1735,7 +1736,7 @@ static void c_parser_omp_nothing (c_pars enum pragma_context { pragma_external, pragma_struct, pragma_param, pragma_stmt, pragma_compound }; -static bool c_parser_pragma (c_parser *, enum pragma_context, bool *); +static bool c_parser_pragma (c_parser *, enum pragma_context, bool *, tree); static bool c_parser_omp_cancellation_point (c_parser *, enum pragma_context); static bool c_parser_omp_target (c_parser *, enum pragma_context, bool *); static void c_parser_omp_begin (c_parser *); @@ -2038,7 +2039,7 @@ c_parser_external_declaration (c_parser break; case CPP_PRAGMA: mark_valid_location_for_stdc_pragma (true); - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); mark_valid_location_for_stdc_pragma (false); break; case CPP_PLUS: @@ -2377,7 +2378,7 @@ c_parser_declaration_or_fndef (c_parser while (parser->in_omp_attribute_pragma) { gcc_assert (c_parser_next_token_is (parser, CPP_PRAGMA)); - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); } c_parser_consume_token (parser); return; @@ -4071,7 +4072,7 @@ c_parser_struct_or_union_specifier (c_pa /* Accept #pragmas at struct scope. */ if (c_parser_next_token_is (parser, CPP_PRAGMA)) { - c_parser_pragma (parser, pragma_struct, NULL); + c_parser_pragma (parser, pragma_struct, NULL, NULL_TREE); continue; } /* Parse some comma-separated declarations, but not the @@ -5023,7 +5024,7 @@ c_parser_parameter_declaration (c_parser /* Accept #pragmas between parameter declarations. */ while (c_parser_next_token_is (parser, CPP_PRAGMA)) - c_parser_pragma (parser, pragma_param, NULL); + c_parser_pragma (parser, pragma_param, NULL, NULL_TREE); if (!c_parser_next_token_starts_declspecs (parser) && !c_parser_nth_token_starts_std_attributes (parser, 1)) @@ -7013,6 +7014,24 @@ c_parser_handle_musttail (c_parser *pars return std_attrs; } +/* Return a statement before optional series of LABEL_EXPR/CASE_LABEL_EXPRs. + Instead of collecting vectors of labels before each stmt just in case + the statement would be iteration or switch statement for named loops, + we just remember last emitted statement and let the iteration/switch + statement search backwards in cur_stmt_list until that stmt for loop + names if any. */ + +static tree +get_before_labels () +{ + if (!building_stmt_list_p ()) + return NULL_TREE; + tree_stmt_iterator tsi = tsi_last (cur_stmt_list); + if (tsi_end_p (tsi)) + return NULL_TREE; + return tsi_stmt (tsi); +} + /* Parse a compound statement except for the opening brace. This is used for parsing both compound statements and statement expressions (which follow different paths to handling the opening). */ @@ -7089,6 +7108,7 @@ c_parser_compound_statement_nostart (c_p c_parser_consume_token (parser); return endloc; } + tree before_labels = get_before_labels (); while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) { location_t loc = c_parser_peek_token (parser)->location; @@ -7119,6 +7139,7 @@ c_parser_compound_statement_nostart (c_p omp_for_parse_state->depth--; sl = push_stmt_list (); parser->error = false; + before_labels = get_before_labels (); continue; } else if (want_nested_loop @@ -7132,7 +7153,7 @@ c_parser_compound_statement_nostart (c_p tree pre_sl = pop_stmt_list (sl); tree nested_sl = push_stmt_list (); mark_valid_location_for_stdc_pragma (false); - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, NULL_TREE); nested_sl = pop_stmt_list (nested_sl); if (omp_for_parse_state->want_nested_loop) { @@ -7151,6 +7172,7 @@ c_parser_compound_statement_nostart (c_p sl = push_stmt_list (); } parser->error = false; + before_labels = get_before_labels (); continue; } else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7161,6 +7183,7 @@ c_parser_compound_statement_nostart (c_p do that, as an extension. */ /* FIXME: Maybe issue a warning or something here? */ c_parser_consume_token (parser); + before_labels = get_before_labels (); continue; } } @@ -7222,6 +7245,7 @@ c_parser_compound_statement_nostart (c_p "ISO C90 forbids mixed declarations and code"); last_stmt = fallthru_attr_p; last_label = false; + before_labels = get_before_labels (); } else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) { @@ -7263,6 +7287,7 @@ c_parser_compound_statement_nostart (c_p pedwarn_c90 (loc, OPT_Wdeclaration_after_statement, "ISO C90 forbids mixed declarations and code"); last_stmt = false; + before_labels = get_before_labels (); } else goto statement; @@ -7282,7 +7307,7 @@ c_parser_compound_statement_nostart (c_p omp_for_parse_state->want_nested_loop = false; if (c_parser_pragma (parser, last_label ? pragma_stmt : pragma_compound, - NULL)) + NULL, before_labels)) { last_label = false; last_stmt = true; @@ -7291,6 +7316,7 @@ c_parser_compound_statement_nostart (c_p } if (omp_for_parse_state) omp_for_parse_state->want_nested_loop = want_nested_loop; + before_labels = get_before_labels (); } else if (c_parser_next_token_is (parser, CPP_EOF)) { @@ -7310,6 +7336,7 @@ c_parser_compound_statement_nostart (c_p { error_at (loc, "% without a previous %"); c_parser_consume_token (parser); + before_labels = get_before_labels (); continue; } } @@ -7321,7 +7348,8 @@ c_parser_compound_statement_nostart (c_p last_stmt = true; mark_valid_location_for_stdc_pragma (false); if (!omp_for_parse_state) - c_parser_statement_after_labels (parser, NULL, NULL, a); + c_parser_statement_after_labels (parser, NULL, before_labels, + NULL, a); else { /* In canonical loop nest form, nested loops can only appear @@ -7331,9 +7359,10 @@ c_parser_compound_statement_nostart (c_p it must be intervening code. */ omp_for_parse_state->want_nested_loop = false; check_omp_intervening_code (parser); - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, before_labels); omp_for_parse_state->want_nested_loop = want_nested_loop; } + before_labels = get_before_labels (); } parser->error = false; @@ -7645,11 +7674,26 @@ c_parser_label (c_parser *parser, tree s static void c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) { + tree before_labels = get_before_labels (); attr_state a = c_parser_all_labels (parser); if (loc_after_labels) *loc_after_labels = c_parser_peek_token (parser)->location; parser->omp_attrs_forbidden_p = false; - c_parser_statement_after_labels (parser, if_p, NULL, a); + c_parser_statement_after_labels (parser, if_p, before_labels, NULL, a); +} + +/* Parse and handle optional identifier after break or continue keywords. */ + +static tree +c_parser_bc_name (c_parser *parser, bool is_break) +{ + if (!c_parser_next_token_is (parser, CPP_NAME)) + return NULL_TREE; + + c_token *tok = c_parser_peek_token (parser); + tree label = c_finish_bc_name (tok->location, tok->value, is_break); + c_parser_consume_token (parser); + return label; } /* Parse a statement, other than a labeled statement. CHAIN is a vector @@ -7658,10 +7702,14 @@ c_parser_statement (c_parser *parser, bo IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. ASTATE is an earlier parsed attribute state. */ + implement -Wparentheses. ASTATE is an earlier parsed attribute state. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_statement_after_labels (c_parser *parser, bool *if_p, + tree before_labels, vec *chain, attr_state astate) { location_t loc = c_parser_peek_token (parser)->location; @@ -7687,16 +7735,16 @@ c_parser_statement_after_labels (c_parse c_parser_if_statement (parser, if_p, chain); break; case RID_SWITCH: - c_parser_switch_statement (parser, if_p); + c_parser_switch_statement (parser, if_p, before_labels); break; case RID_WHILE: - c_parser_while_statement (parser, false, 0, false, if_p); + c_parser_while_statement (parser, false, 0, false, if_p, before_labels); break; case RID_DO: - c_parser_do_statement (parser, false, 0, false); + c_parser_do_statement (parser, false, 0, false, before_labels); break; case RID_FOR: - c_parser_for_statement (parser, false, 0, false, if_p); + c_parser_for_statement (parser, false, 0, false, if_p, before_labels); break; case RID_GOTO: c_parser_consume_token (parser); @@ -7720,11 +7768,13 @@ c_parser_statement_after_labels (c_parse goto expect_semicolon; case RID_CONTINUE: c_parser_consume_token (parser); - stmt = c_finish_bc_stmt (loc, objc_foreach_continue_label, false); + stmt = c_finish_bc_stmt (loc, objc_foreach_continue_label, false, + c_parser_bc_name (parser, false)); goto expect_semicolon; case RID_BREAK: c_parser_consume_token (parser); - stmt = c_finish_bc_stmt (loc, objc_foreach_break_label, true); + stmt = c_finish_bc_stmt (loc, objc_foreach_break_label, true, + c_parser_bc_name (parser, true)); goto expect_semicolon; case RID_RETURN: c_parser_consume_token (parser); @@ -7840,7 +7890,7 @@ c_parser_statement_after_labels (c_parse c_parser_consume_token (parser); break; case CPP_PRAGMA: - if (!c_parser_pragma (parser, pragma_stmt, if_p)) + if (!c_parser_pragma (parser, pragma_stmt, if_p, before_labels)) goto restart; break; default: @@ -7935,6 +7985,7 @@ c_parser_if_body (c_parser *parser, bool location_t body_loc_after_labels = UNKNOWN_LOCATION; token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree before_labels = get_before_labels (); c_parser_all_labels (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7951,7 +8002,7 @@ c_parser_if_body (c_parser *parser, bool else { body_loc_after_labels = c_parser_peek_token (parser)->location; - c_parser_statement_after_labels (parser, if_p); + c_parser_statement_after_labels (parser, if_p, before_labels); } token_indent_info next_tinfo @@ -7979,6 +8030,7 @@ c_parser_else_body (c_parser *parser, co token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); location_t body_loc_after_labels = UNKNOWN_LOCATION; + tree before_labels = get_before_labels (); c_parser_all_labels (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7994,7 +8046,7 @@ c_parser_else_body (c_parser *parser, co { if (!c_parser_next_token_is (parser, CPP_OPEN_BRACE)) body_loc_after_labels = c_parser_peek_token (parser)->location; - c_parser_statement_after_labels (parser, NULL, chain); + c_parser_statement_after_labels (parser, NULL, before_labels, chain); } token_indent_info next_tinfo @@ -8134,10 +8186,12 @@ c_parser_if_statement (c_parser *parser, switch-statement: switch (expression) statement -*/ + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void -c_parser_switch_statement (c_parser *parser, bool *if_p) +c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels) { struct c_expr ce; tree block, expr, body; @@ -8146,6 +8200,8 @@ c_parser_switch_statement (c_parser *par location_t switch_cond_loc; gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH)); c_parser_consume_token (parser); + tree switch_name; + int num_names = c_get_loop_names (before_labels, true, &switch_name); block = c_begin_compound_stmt (flag_isoc99); bool explicit_cast_p = false; matching_parens parens; @@ -8167,9 +8223,18 @@ c_parser_switch_statement (c_parser *par expr = error_mark_node; ce.original_type = error_mark_node; } - c_start_switch (switch_loc, switch_cond_loc, expr, explicit_cast_p); + tree stmt + = c_start_switch (switch_loc, switch_cond_loc, expr, explicit_cast_p, + switch_name); save_in_statement = in_statement; in_statement |= IN_SWITCH_STMT; + if (switch_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (switch_name) = 1; + in_statement |= IN_NAMED_STMT; + } + else + in_statement &= ~IN_NAMED_STMT; location_t loc_after_labels; bool open_brace_p = c_parser_peek_token (parser)->type == CPP_OPEN_BRACE; body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); @@ -8179,6 +8244,14 @@ c_parser_switch_statement (c_parser *par RID_SWITCH); c_finish_switch (body, ce.original_type); in_statement = save_in_statement; + if (num_names) + { + if (!C_DECL_LOOP_SWITCH_NAME_USED (switch_name)) + SWITCH_STMT_NAME (stmt) = NULL_TREE; + else + SWITCH_STMT_NO_BREAK_P (stmt) = 0; + c_release_loop_names (num_names); + } add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99)); c_parser_maybe_reclassify_token (parser); } @@ -8190,11 +8263,14 @@ c_parser_switch_statement (c_parser *par IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector, bool *if_p) + bool novector, bool *if_p, tree before_labels) { tree block, cond, body; unsigned char save_in_statement; @@ -8202,6 +8278,8 @@ c_parser_while_statement (c_parser *pars gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE)); token_indent_info while_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8231,6 +8309,11 @@ c_parser_while_statement (c_parser *pars integer_zero_node); save_in_statement = in_statement; in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8238,9 +8321,13 @@ c_parser_while_statement (c_parser *pars location_t loc_after_labels; bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE); body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); - add_stmt (build_stmt (loc, WHILE_STMT, cond, body)); + if (loop_name && !C_DECL_LOOP_SWITCH_NAME_USED (loop_name)) + loop_name = NULL_TREE; + add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name)); add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); c_parser_maybe_reclassify_token (parser); + if (num_names) + c_release_loop_names (num_names); token_indent_info next_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8257,16 +8344,20 @@ c_parser_while_statement (c_parser *pars do-statement: do statement while ( expression ) ; -*/ + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector) + bool novector, tree before_labels) { tree block, cond, body; unsigned char save_in_statement; location_t loc; gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8284,9 +8375,20 @@ c_parser_do_statement (c_parser *parser, loc = c_parser_peek_token (parser)->location; save_in_statement = in_statement; in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } body = c_parser_c99_block_statement (parser, NULL); c_parser_require_keyword (parser, RID_WHILE, "expected %"); in_statement = save_in_statement; + if (num_names) + { + if (!C_DECL_LOOP_SWITCH_NAME_USED (loop_name)) + loop_name = NULL_TREE; + c_release_loop_names (num_names); + } cond = c_parser_paren_condition (parser); if (ivdep && cond != error_mark_node) cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, @@ -8306,7 +8408,7 @@ c_parser_do_statement (c_parser *parser, if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) c_parser_skip_to_end_of_block_or_statement (parser); - add_stmt (build_stmt (loc, DO_STMT, cond, body)); + add_stmt (build_stmt (loc, DO_STMT, cond, body, loop_name)); add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); } @@ -8367,11 +8469,14 @@ c_parser_do_statement (c_parser *parser, IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector, bool *if_p) + bool novector, bool *if_p, tree before_labels) { tree block, cond, incr, body; unsigned char save_in_statement; @@ -8386,6 +8491,8 @@ c_parser_for_statement (c_parser *parser gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR)); token_indent_info for_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8578,9 +8685,22 @@ c_parser_for_statement (c_parser *parser save_objc_foreach_continue_label = objc_foreach_continue_label; objc_foreach_break_label = create_artificial_label (loc); objc_foreach_continue_label = create_artificial_label (loc); + if (loop_name) + { + gcc_checking_assert (!DECL_CHAIN (loop_name) + && !DECL_CHAIN (objc_foreach_break_label)); + C_DECL_SWITCH_NAME (loop_name) = 1; + DECL_CHAIN (loop_name) = objc_foreach_break_label; + DECL_CHAIN (objc_foreach_break_label) = objc_foreach_continue_label; + } } else in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8589,6 +8709,16 @@ c_parser_for_statement (c_parser *parser bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE); body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); + if (loop_name && is_foreach_statement) + { + gcc_checking_assert (DECL_CHAIN (loop_name) == objc_foreach_break_label + && (DECL_CHAIN (objc_foreach_break_label) + == objc_foreach_continue_label)); + C_DECL_SWITCH_NAME (loop_name) = 0; + DECL_CHAIN (loop_name) = NULL_TREE; + DECL_CHAIN (objc_foreach_break_label) = NULL_TREE; + } + if (is_foreach_statement) objc_finish_foreach_loop (for_loc, object_expression, collection_expression, body, @@ -8596,7 +8726,9 @@ c_parser_for_statement (c_parser *parser objc_foreach_continue_label); else add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr, - body, NULL_TREE)); + body, NULL_TREE, + loop_name && C_DECL_LOOP_SWITCH_NAME_USED (loop_name) + ? loop_name : NULL_TREE)); add_stmt (c_end_compound_stmt (for_loc, block, flag_isoc99 || c_dialect_objc ())); c_parser_maybe_reclassify_token (parser); @@ -8610,6 +8742,8 @@ c_parser_for_statement (c_parser *parser for_tinfo.location, RID_FOR); in_statement = save_in_statement; + if (num_names) + c_release_loop_names (num_names); if (is_foreach_statement) { objc_foreach_break_label = save_objc_foreach_break_label; @@ -13245,7 +13379,7 @@ c_parser_objc_class_instance_variables ( } else if (c_parser_next_token_is (parser, CPP_PRAGMA)) { - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); continue; } @@ -13516,7 +13650,7 @@ c_parser_objc_methodprotolist (c_parser c_parser_objc_methodproto (parser); break; case CPP_PRAGMA: - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); break; case CPP_EOF: return; @@ -14585,10 +14719,12 @@ c_parser_pragma_unroll (c_parser *parser /* Handle pragmas. Some OpenMP pragmas are associated with, and therefore should be considered, statements. ALLOW_STMT is true if we're within the context of a function and such pragmas are to be allowed. Returns - true if we actually parsed such a pragma. */ + true if we actually parsed such a pragma. BEFORE_LABELS is last statement + before possible labels, see get_before_labels description for details. */ static bool -c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p) +c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, + tree before_labels) { unsigned int id; const char *construct = NULL; @@ -14841,11 +14977,14 @@ c_parser_pragma (c_parser *parser, enum return false; } if (c_parser_next_token_is_keyword (parser, RID_FOR)) - c_parser_for_statement (parser, ivdep, unroll, novector, if_p); + c_parser_for_statement (parser, ivdep, unroll, novector, if_p, + before_labels); else if (c_parser_next_token_is_keyword (parser, RID_WHILE)) - c_parser_while_statement (parser, ivdep, unroll, novector, if_p); + c_parser_while_statement (parser, ivdep, unroll, novector, if_p, + before_labels); else - c_parser_do_statement (parser, ivdep, unroll, novector); + c_parser_do_statement (parser, ivdep, unroll, novector, + before_labels); } return true; @@ -22568,7 +22707,7 @@ c_parser_omp_loop_nest (c_parser *parser } break; default: - c_parser_pragma (parser, pragma_stmt, NULL); + c_parser_pragma (parser, pragma_stmt, NULL, void_list_node); break; } if (transform == NULL_TREE) @@ -25468,7 +25607,7 @@ c_maybe_parse_omp_decl (tree decl, tree parser->tokens = toks->address (); parser->tokens_avail = toks->length (); parser->in_omp_attribute_pragma = toks; - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); parser->in_omp_decl_attribute = NULL_TREE; return true; } --- gcc/c/c-decl.cc.jj 2024-10-12 10:40:15.999533361 +0200 +++ gcc/c/c-decl.cc 2024-10-15 19:10:03.861008436 +0200 @@ -162,6 +162,14 @@ vec *c #pragma omp begin assumes ... #pragma omp end assumes regions we are in. */ vec *current_omp_begin_assumes; + +/* Vector of loop names with C_DECL_LOOP_NAME or C_DECL_SWITCH_NAME marked + LABEL_DECL as the last and canonical for each loop or switch. */ +static vec loop_names; + +/* Hash table mapping LABEL_DECLs to the canonical LABEL_DECLs if LOOP_NAMES + vector becomes too long. */ +static decl_tree_map *loop_names_hash; /* Each c_binding structure describes one binding of an identifier to a decl. All the decls in a scope - irrespective of namespace - are @@ -694,13 +702,14 @@ add_stmt (tree t) SET_EXPR_LOCATION (t, input_location); } - if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) - STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; - /* Add T to the statement-tree. Non-side-effect statements need to be recorded during statement expressions. */ if (!building_stmt_list_p ()) push_stmt_list (); + + if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) + STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; + append_to_statement_list_force (t, &cur_stmt_list); return t; @@ -11683,6 +11692,10 @@ c_push_function_context (void) c_stmt_tree.x_cur_stmt_list = vec_safe_copy (c_stmt_tree.x_cur_stmt_list); p->x_in_statement = in_statement; p->x_switch_stack = c_switch_stack; + p->loop_names = loop_names; + loop_names = vNULL; + p->loop_names_hash = loop_names_hash; + loop_names_hash = NULL; p->arg_info = current_function_arg_info; p->returns_value = current_function_returns_value; p->returns_null = current_function_returns_null; @@ -11722,6 +11735,12 @@ c_pop_function_context (void) p->base.x_stmt_tree.x_cur_stmt_list = NULL; in_statement = p->x_in_statement; c_switch_stack = p->x_switch_stack; + loop_names.release (); + loop_names = p->loop_names; + p->loop_names = vNULL; + delete loop_names_hash; + loop_names_hash = p->loop_names_hash; + p->loop_names_hash = NULL; current_function_arg_info = p->arg_info; current_function_returns_value = p->returns_value; current_function_returns_null = p->returns_null; @@ -13804,4 +13823,212 @@ c_check_in_current_scope (tree decl) return b != NULL && B_IN_CURRENT_SCOPE (b); } +/* Search for loop or switch names. BEFORE_LABELS is last statement before + possible labels and SWITCH_P true for a switch, false for loops. + Searches through last statements in cur_stmt_list, stops when seeing + BEFORE_LABELs, or statement other than LABEL_EXPR or CASE_LABEL_EXPR. + Returns number of loop/switch names found and if any are found, sets + *LAST_P to the canonical loop/switch name LABEL_DECL. */ + +int +c_get_loop_names (tree before_labels, bool switch_p, tree *last_p) +{ + *last_p = NULL_TREE; + if (!building_stmt_list_p () + || !STATEMENT_LIST_HAS_LABEL (cur_stmt_list) + || before_labels == void_list_node) + return 0; + + int ret = 0; + tree last = NULL_TREE; + for (tree_stmt_iterator tsi = tsi_last (cur_stmt_list); + !tsi_end_p (tsi); tsi_prev (&tsi)) + { + tree stmt = tsi_stmt (tsi); + if (stmt == before_labels) + break; + else if (TREE_CODE (stmt) == LABEL_EXPR) + { + if (last == NULL_TREE) + last = LABEL_EXPR_LABEL (stmt); + else + { + loop_names.safe_push (LABEL_EXPR_LABEL (stmt)); + ++ret; + } + } + else if (TREE_CODE (stmt) != CASE_LABEL_EXPR) + break; + } + if (last) + { + if (switch_p) + C_DECL_SWITCH_NAME (last) = 1; + else + C_DECL_LOOP_NAME (last) = 1; + loop_names.safe_push (last); + ++ret; + if (loop_names.length () > 16) + { + unsigned int first = 0, i; + tree l, c = NULL_TREE; + if (loop_names_hash == NULL) + loop_names_hash = new decl_tree_map (ret); + else + first = loop_names.length () - ret; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + loop_names_hash->put (l, c); + if (i == first) + break; + } + } + *last_p = last; + } + return ret; +} + +/* Undoes what get_loop_names did when it returned NUM_NAMES. */ + +void +c_release_loop_names (int num_names) +{ + unsigned len = loop_names.length () - num_names; + if (loop_names_hash) + { + if (len <= 16) + { + delete loop_names_hash; + loop_names_hash = NULL; + } + else + { + unsigned int i; + tree l; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + loop_names_hash->remove (l); + if (i == len) + break; + } + } + } + loop_names.truncate (len); +} + +/* Finish processing of break or continue identifier operand. + NAME is the identifier operand of break or continue and + IS_BREAK is true iff it is break stmt. Returns the operand + to use for BREAK_STMT or CONTINUE_STMT, either NULL_TREE or + canonical loop/switch name LABEL_DECL. */ + +tree +c_finish_bc_name (location_t loc, tree name, bool is_break) +{ + tree label = NULL_TREE, lab; + pedwarn_c23 (loc, OPT_Wpedantic, + "ISO C does not support %qs statement with an identifier " + "operand before C2Y", is_break ? "break" : "continue"); + + /* If I_LABEL_DECL is NULL or not from current function, don't waste time + trying to find it among loop_names, it can't be there. */ + if (!loop_names.is_empty () + && current_function_scope + && (lab = I_LABEL_DECL (name)) + && DECL_CONTEXT (lab) == current_function_decl) + { + unsigned int i; + tree l, c = NULL_TREE; + if (loop_names_hash) + { + if (tree *val = loop_names_hash->get (lab)) + label = *val; + } + else + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + if (l == lab) + { + label = c; + break; + } + } + if (label) + TREE_USED (lab) = 1; + } + if (label == NULL_TREE) + { + auto_vec candidates; + unsigned int i; + tree l, c = NULL_TREE; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + if (is_break || C_DECL_LOOP_NAME (c)) + candidates.safe_push (IDENTIFIER_POINTER (DECL_NAME (l))); + } + const char *hint = find_closest_string (IDENTIFIER_POINTER (name), + &candidates); + if (hint) + { + gcc_rich_location richloc (loc); + richloc.add_fixit_replace (hint); + if (is_break) + error_at (&richloc, "% statement operand %qE does not " + "refer to a named loop or %; " + "did you mean %qs?", name, hint); + else + error_at (&richloc, "% statement operand %qE does not " + "refer to a named loop; did you mean %qs?", + name, hint); + } + else if (is_break) + error_at (loc, "% statement operand %qE does not refer to a " + "named loop or %", name); + else + error_at (loc, "% statement operand %qE does not refer to " + "a named loop", name); + } + else if (!C_DECL_LOOP_NAME (label) && !is_break) + { + auto_diagnostic_group d; + error_at (loc, "% statement operand %qE refers to a named " + "%", name); + inform (DECL_SOURCE_LOCATION (label), "% name defined here"); + label = NULL_TREE; + } + else if (!C_DECL_LOOP_SWITCH_NAME_VALID (label)) + { + auto_diagnostic_group d; + if (C_DECL_LOOP_NAME (label)) + { + error_at (loc, "%qs statement operand %qE refers to a loop outside " + "of its body", is_break ? "break" : "continue", name); + inform (DECL_SOURCE_LOCATION (label), "loop name defined here"); + } + else + { + error_at (loc, "% statement operand %qE refers to a " + "% outside of its body", name); + inform (DECL_SOURCE_LOCATION (label), + "% name defined here"); + } + label = NULL_TREE; + } + else if (label == loop_names.last () && (in_statement & IN_NAMED_STMT) != 0) + /* If it is just a fancy reference to the innermost construct, handle it + just like break; or continue; though tracking cheaply what is the + innermost loop for continue when nested in switches would require + another global variable and updating it. */ + label = NULL_TREE; + else + C_DECL_LOOP_SWITCH_NAME_USED (label) = 1; + return label; +} + #include "gt-c-c-decl.h" --- gcc/c/c-typeck.cc.jj 2024-10-15 19:09:09.784746947 +0200 +++ gcc/c/c-typeck.cc 2024-10-15 19:10:03.863008409 +0200 @@ -11943,7 +11943,7 @@ struct c_switch *c_switch_stack; tree c_start_switch (location_t switch_loc, location_t switch_cond_loc, - tree exp, bool explicit_cast_p) + tree exp, bool explicit_cast_p, tree switch_name) { tree orig_type = error_mark_node; bool bool_cond_p = false; @@ -11996,7 +11996,7 @@ c_start_switch (location_t switch_loc, /* Add this new SWITCH_STMT to the stack. */ cs = XNEW (struct c_switch); cs->switch_stmt = build_stmt (switch_loc, SWITCH_STMT, exp, - NULL_TREE, orig_type, NULL_TREE); + NULL_TREE, orig_type, NULL_TREE, switch_name); cs->orig_type = orig_type; cs->cases = splay_tree_new (case_compare, NULL, NULL); cs->bindings = c_get_switch_bindings (); @@ -12097,7 +12097,7 @@ c_finish_if_stmt (location_t if_locus, t } tree -c_finish_bc_stmt (location_t loc, tree label, bool is_break) +c_finish_bc_stmt (location_t loc, tree label, bool is_break, tree name) { /* In switch statements break is sometimes stylistically used after a return statement. This can lead to spurious warnings about @@ -12109,7 +12109,7 @@ c_finish_bc_stmt (location_t loc, tree l bool skip = !block_may_fallthru (cur_stmt_list); if (is_break) - switch (in_statement) + switch (in_statement & ~IN_NAMED_STMT) { case 0: error_at (loc, "break statement not within loop or switch"); @@ -12129,7 +12129,7 @@ c_finish_bc_stmt (location_t loc, tree l break; } else - switch (in_statement & ~IN_SWITCH_STMT) + switch (in_statement & ~(IN_SWITCH_STMT | IN_NAMED_STMT)) { case 0: error_at (loc, "continue statement not within a loop"); @@ -12148,14 +12148,24 @@ c_finish_bc_stmt (location_t loc, tree l if (skip) return NULL_TREE; else if ((in_statement & IN_OBJC_FOREACH) - && !(is_break && (in_statement & IN_SWITCH_STMT))) + && !(is_break && (in_statement & IN_SWITCH_STMT)) + && name == NULL_TREE) { /* The foreach expander produces low-level code using gotos instead of a structured loop construct. */ gcc_assert (label); return add_stmt (build_stmt (loc, GOTO_EXPR, label)); } - return add_stmt (build_stmt (loc, (is_break ? BREAK_STMT : CONTINUE_STMT))); + else if (name && C_DECL_LOOP_NAME (name) && C_DECL_SWITCH_NAME (name)) + { + label = DECL_CHAIN (name); + if (!is_break) + label = DECL_CHAIN (label); + /* Foreach expander from some outer level. */ + return add_stmt (build_stmt (loc, GOTO_EXPR, label)); + } + return add_stmt (build_stmt (loc, is_break ? BREAK_STMT : CONTINUE_STMT, + name)); } /* A helper routine for c_process_expr_stmt and c_finish_stmt_expr. */ --- gcc/cp/semantics.cc.jj 2024-10-12 10:40:16.252529844 +0200 +++ gcc/cp/semantics.cc 2024-10-15 19:10:03.865008381 +0200 @@ -1368,7 +1368,7 @@ tree begin_while_stmt (void) { tree r; - r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE); + r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE); add_stmt (r); WHILE_BODY (r) = do_pushlevel (sk_block); begin_cond (&WHILE_COND (r)); @@ -1425,7 +1425,8 @@ finish_while_stmt (tree while_stmt) tree begin_do_stmt (void) { - tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE); + tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE, + NULL_TREE); begin_maybe_infinite_loop (boolean_true_node); add_stmt (r); DO_BODY (r) = push_stmt_list (); @@ -1546,7 +1547,7 @@ begin_for_stmt (tree scope, tree init) tree r; r = build_stmt (input_location, FOR_STMT, NULL_TREE, NULL_TREE, - NULL_TREE, NULL_TREE, NULL_TREE); + NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); if (scope == NULL_TREE) { @@ -1783,7 +1784,7 @@ finish_break_stmt (void) if (!block_may_fallthru (cur_stmt_list)) return void_node; note_break_stmt (); - return add_stmt (build_stmt (input_location, BREAK_STMT)); + return add_stmt (build_stmt (input_location, BREAK_STMT, NULL_TREE)); } /* Finish a continue-statement. */ @@ -1791,7 +1792,7 @@ finish_break_stmt (void) tree finish_continue_stmt (void) { - return add_stmt (build_stmt (input_location, CONTINUE_STMT)); + return add_stmt (build_stmt (input_location, CONTINUE_STMT, NULL_TREE)); } /* Begin a switch-statement. Returns a new SWITCH_STMT if @@ -1803,7 +1804,8 @@ begin_switch_stmt (void) tree r, scope; scope = do_pushlevel (sk_cond); - r = build_stmt (input_location, SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE, scope); + r = build_stmt (input_location, SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE, + scope, NULL_TREE); begin_cond (&SWITCH_STMT_COND (r)); --- gcc/testsuite/gcc.dg/c23-named-loops-1.c.jj 2024-10-15 19:10:03.865008381 +0200 +++ gcc/testsuite/gcc.dg/c23-named-loops-1.c 2024-10-15 19:22:52.560529971 +0200 @@ -0,0 +1,144 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -pedantic-errors" } */ + +void +foo (int w) +{ + d: e: f:; + a: b: c: + for (int x = 0; x < 32; ++x) + { + if (x == 0) + continue a; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 1) + continue b; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 2) + continue c; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 31) + break b; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + } + int y = 0; + g: h: + #pragma GCC unroll 2 + while (y < 16) + { + ++y; + if (y == 12) + continue g; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 13) + continue h; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 14) + break g; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + } + i: j:; + k: l: + switch (y) + { + case 6: + break; + case 7: + break k; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + case 8: + break l; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + } + m: n: o: p: + for (int x = 0; x < 2; ++x) + q: r: s: t: + switch (x) + { + case 0: + u: v: + case 3: + w: x: + for (int y = 0; y < 2; ++y) + y: z: + for (int z = 0; z < 2; ++z) + aa: ab: ac: + for (int a = 0; a < 2; ++a) + ad: ae: af: + switch (a) + { + case 0: + if (w == 0) + break ae; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 1) + break ab; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 2) + break z; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 3) + break v; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 4) + break s; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 5) + break p; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 6) + break; + else if (w == 7) + continue aa; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 8) + continue y; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 9) + continue x; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 10) + continue m; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + ag: ah: + do + { + if (w == 11) + break ag; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else + continue ah; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + while (0); + break; + default: + break; + } + break; + default: + break; + } + [[]] [[]] ai: + [[]] [[]] aj: + [[]] [[]] ak: + [[]] [[]] [[]] + for (int x = 0; x < 32; ++x) + if (x == 31) + break ak; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 30) + break aj; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 29) + continue ai; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + al: + [[]] am: + [[]] + do + { + if (w == 42) + continue am; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 41) + break al; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + } + while (1); + an: + [[]] ao: + [[]] [[]] + while (w) + { + if (w == 40) + break ao; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 39) + continue an; /* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + [[]] ap: + [[]] aq: + [[]] + switch (w) + { + case 42: + break ap; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + default: + break aq; /* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */ + } +} --- gcc/testsuite/gcc.dg/c23-named-loops-5.c.jj 2024-10-15 19:10:03.865008381 +0200 +++ gcc/testsuite/gcc.dg/c23-named-loops-5.c 2024-10-15 19:10:03.865008381 +0200 @@ -0,0 +1,5 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -pedantic-errors -Wno-c23-c2y-compat" } */ + +#include "c23-named-loops-1.c" --- gcc/testsuite/gcc.dg/c2y-named-loops-1.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-1.c 2024-10-15 19:23:34.073964218 +0200 @@ -0,0 +1,144 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -Wc23-c2y-compat" } */ + +void +foo (int w) +{ + d: e: f:; + a: b: c: + for (int x = 0; x < 32; ++x) + { + if (x == 0) + continue a; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 1) + continue b; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 2) + continue c; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 31) + break b; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + int y = 0; + g: h: + #pragma GCC unroll 2 + while (y < 16) + { + ++y; + if (y == 12) + continue g; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 13) + continue h; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 14) + break g; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + i: j:; + k: l: + switch (y) + { + case 6: + break; + case 7: + break k; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + case 8: + break l; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + m: n: o: p: + for (int x = 0; x < 2; ++x) + q: r: s: t: + switch (x) + { + case 0: + u: v: + case 3: + w: x: + for (int y = 0; y < 2; ++y) + y: z: + for (int z = 0; z < 2; ++z) + aa: ab: ac: + for (int a = 0; a < 2; ++a) + ad: ae: af: + switch (a) + { + case 0: + if (w == 0) + break ae; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 1) + break ab; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 2) + break z; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 3) + break v; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 4) + break s; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 5) + break p; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 6) + break; + else if (w == 7) + continue aa; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 8) + continue y; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 9) + continue x; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 10) + continue m; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + ag: ah: + do + { + if (w == 11) + break ag; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else + continue ah; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + while (0); + break; + default: + break; + } + break; + default: + break; + } + [[]] [[]] ai: + [[]] [[]] aj: + [[]] [[]] ak: + [[]] [[]] [[]] + for (int x = 0; x < 32; ++x) + if (x == 31) + break ak; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 30) + break aj; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 29) + continue ai; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + al: + [[]] am: + [[]] + do + { + if (w == 42) + continue am; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 41) + break al; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + while (1); + an: + [[]] ao: + [[]] [[]] + while (w) + { + if (w == 40) + break ao; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 39) + continue an; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + [[]] ap: + [[]] aq: + [[]] + switch (w) + { + case 42: + break ap; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + default: + break aq; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } +} --- gcc/testsuite/gcc.dg/c2y-named-loops-2.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-2.c 2024-10-15 19:10:03.866008367 +0200 @@ -0,0 +1,45 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +void +foo (int x) +{ + label1: + for (int i = 0; i < 16; ++i) + another_label1: + for (int j = 0; j < 16; ++j) + break label2; /* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'; did you mean 'label1'\\\?" } */ + for (int i = 0; i < 16; ++i) + break label3; /* { dg-error "'break' statement operand 'label3' does not refer to a named loop or 'switch'" } */ + label4: /* { dg-message "'switch' name defined here" } */ + switch (x) + { + case 0: + for (int i = 0; i < 16; ++i) + continue label5; /* { dg-error "'continue' statement operand 'label5' does not refer to a named loop" } */ + break label4; + case 1: + for (int i = 0; i < 16; ++i) + continue label4; /* { dg-error "'continue' statement operand 'label4' refers to a named 'switch'" } */ + } + label6: + for (int i = 0; i < 16; ++i) + continue label7; /* { dg-error "'continue' statement operand 'label7' does not refer to a named loop; did you mean 'label6'\\\?" } */ + label2: + for (int i = 0; i < 16; ++i) + ; + label8:; + for (int i = 0; i < 16; ++i) + break label8; /* { dg-error "'break' statement operand 'label8' does not refer to a named loop or 'switch'" } */ + label9:; + for (int i = 0; i < 16; ++i) + continue label9; /* { dg-error "'continue' statement operand 'label9' does not refer to a named loop" } */ + label10: + ; + switch (x) + { + case 0: + break label10; /* { dg-error "'break' statement operand 'label10' does not refer to a named loop or 'switch'" } */ + } +} --- gcc/testsuite/gcc.dg/c2y-named-loops-4.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-4.c 2024-10-15 19:10:03.866008367 +0200 @@ -0,0 +1,159 @@ +/* N3355 - Named loops. */ +/* { dg-do run } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +extern void abort (void); + +void +foo (int x) +{ + int i, j, k, l, m; + label1: + for (i = 0; i < 2; ++i) + { + if (i == 1) + { + if (x != 11) + abort (); + return; + } + label2: + switch (i) + { + label3: + case 0: + for (j = 0; j < 2; ++j) + { + if (j == 1) + { + if (x != 8) + abort (); + return; + } + label4: + for (k = 0; k < 2; ++k) + { + if (k == 1) + { + if (x != 6) + abort (); + return; + } + l = 0; + label5: + while (l < 2) + { + if (l == 1) + { + if (x != 4) + abort (); + return; + } + ++l; + m = 0; + label6: + do + { + if (m == 1) + { + if (x != 2) + abort (); + return; + } + ++m; + label7: + switch (x) + { + case 0: + break label7; + case 1: + break label6; + case 2: + continue label6; + case 3: + break label5; + case 4: + continue label5; + case 5: + break label4; + case 6: + continue label4; + case 7: + break label3; + case 8: + continue label3; + case 9: + break label2; + case 10: + break label1; + case 11: + continue label1; + default: + abort (); + break; + } + if (x) + abort (); + return; + } + while (m < 2); + if (x != 1 || m != 1) + abort (); + return; + } + if (x != 3 || l != 1 || m != 1) + abort (); + return; + } + if (x != 5 || k != 0 || l != 1 || m != 1) + abort (); + return; + } + if (x != 7 || j != 0 || k != 0 || l != 1 || m != 1) + abort (); + return; + } + if (x != 9 || j != 0 || k != 0 || l != 1 || m != 1) + abort (); + return; + } + if (x != 10 || i != 0 || j != 0 || k != 0 || l != 1 || m != 1) + abort (); +} + +void +bar (int x) +{ + int i, j; + label1: + for (i = 0; i < 2; ++i) + { + if (i == 1) + { + if (x != 1) + abort (); + return; + } + for (j = 0; j < 2; ++j) + if (j == 1) + abort (); + else if (x == 0) + break label1; + else if (x == 1) + continue label1; + else + abort (); + abort (); + } + if (x != 0) + abort (); +} + +int +main () +{ + for (int n = 0; n <= 11; ++n) + foo (n); + bar (0); + bar (1); +} --- gcc/testsuite/gcc.dg/c2y-named-loops-5.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-5.c 2024-10-15 19:26:54.725229702 +0200 @@ -0,0 +1,5 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +#include "c2y-named-loops-1.c" --- gcc/testsuite/gcc.dg/c2y-named-loops-6.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-6.c 2024-10-15 19:10:03.866008367 +0200 @@ -0,0 +1,32 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -Wall" } */ + +void +foo (int x) +{ + lab0: + switch (x) + { + case 1: + ++x; + /* FALLTHRU */ + lab1: + case 2: + /* FALLTHRU */ + case 3: + lab2: + for (int i = 0; i < 4; ++i) + if (i == 0) + continue lab2; + else if (i == 1) + continue lab1; + else if (x == 2) + break lab1; + else + break lab0; + break; + default: + break; + } +} --- gcc/testsuite/gcc.dg/c2y-named-loops-7.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-7.c 2024-10-15 19:10:03.866008367 +0200 @@ -0,0 +1,32 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -Wall" } */ + +void +foo (int x) +{ + lab0: + switch (x) + { + case 1: + ++x; + [[fallthrough]]; + lab1: /* { dg-warning "label 'lab1' defined but not used" } */ + case 2: + [[fallthrough]]; + case 3: + lab2: + for (int i = 0; i < 4; ++i) + if (i == 0) + continue lab2; + else if (i == 1) + continue lab1; /* { dg-error "'continue' statement operand 'lab1' does not refer to a named loop; did you mean 'lab2'\\\?" } */ + else if (x == 2) + break lab1; /* { dg-error "'break' statement operand 'lab1' does not refer to a named loop or 'switch'; did you mean 'lab2'\\\?" } */ + else + break lab0; + break; + default: + break; + } +} --- gcc/testsuite/gcc.dg/gnu99-named-loops-1.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-1.c 2024-10-15 19:28:04.481279053 +0200 @@ -0,0 +1,144 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wpedantic" } */ + +void +foo (int w) +{ + d: e: f:; + a: b: c: + for (int x = 0; x < 32; ++x) + { + if (x == 0) + continue a; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 1) + continue b; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 2) + continue c; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (x == 31) + break b; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + int y = 0; + g: h: + #pragma GCC unroll 2 + while (y < 16) + { + ++y; + if (y == 12) + continue g; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 13) + continue h; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (y == 14) + break g; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + i: j:; + k: l: + switch (y) + { + case 6: + break; + case 7: + break k; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + case 8: + break l; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + m: n: o: p: + for (int x = 0; x < 2; ++x) + q: r: s: t: + switch (x) + { + case 0: + u: v: + case 3: + w: x: + for (int y = 0; y < 2; ++y) + y: z: + for (int z = 0; z < 2; ++z) + aa: ab: ac: + for (int a = 0; a < 2; ++a) + ad: ae: af: + switch (a) + { + case 0: + if (w == 0) + break ae; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 1) + break ab; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 2) + break z; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 3) + break v; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 4) + break s; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 5) + break p; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 6) + break; + else if (w == 7) + continue aa; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 8) + continue y; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 9) + continue x; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 10) + continue m; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + ag: ah: + do + { + if (w == 11) + break ag; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else + continue ah; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + while (0); + break; + default: + break; + } + break; + default: + break; + } + [[]] [[]] ai: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] [[]] aj: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] [[]] ak: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] [[]] [[]] /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + for (int x = 0; x < 32; ++x) + if (x == 31) + break ak; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 30) + break aj; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (x == 29) + continue ai; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + al: + [[]] am: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + do + { + if (w == 42) + continue am; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + else if (w == 41) + break al; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } + while (1); + an: + [[]] ao: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] [[]] /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + while (w) + { + if (w == 40) + break ao; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + else if (w == 39) + continue an; /* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */ + } + [[]] ap: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] aq: /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + [[]] /* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */ + switch (w) + { + case 42: + break ap; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + default: + break aq; /* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */ + } +} --- gcc/testsuite/gcc.dg/gnu99-named-loops-2.c.jj 2024-10-15 19:10:03.866008367 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-2.c 2024-10-15 19:10:03.866008367 +0200 @@ -0,0 +1,45 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99" } */ + +void +foo (int x) +{ + label1: + for (int i = 0; i < 16; ++i) + another_label1: + for (int j = 0; j < 16; ++j) + break label2; /* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'; did you mean 'label1'\\\?" } */ + for (int i = 0; i < 16; ++i) + break label3; /* { dg-error "'break' statement operand 'label3' does not refer to a named loop or 'switch'" } */ + label4: /* { dg-message "'switch' name defined here" } */ + switch (x) + { + case 0: + for (int i = 0; i < 16; ++i) + continue label5; /* { dg-error "'continue' statement operand 'label5' does not refer to a named loop" } */ + break label4; + case 1: + for (int i = 0; i < 16; ++i) + continue label4; /* { dg-error "'continue' statement operand 'label4' refers to a named 'switch'" } */ + } + label6: + for (int i = 0; i < 16; ++i) + continue label7; /* { dg-error "'continue' statement operand 'label7' does not refer to a named loop; did you mean 'label6'\\\?" } */ + label2: + for (int i = 0; i < 16; ++i) + ; + label8:; + for (int i = 0; i < 16; ++i) + break label8; /* { dg-error "'break' statement operand 'label8' does not refer to a named loop or 'switch'" } */ + label9:; + for (int i = 0; i < 16; ++i) + continue label9; /* { dg-error "'continue' statement operand 'label9' does not refer to a named loop" } */ + label10: + ; + switch (x) + { + case 0: + break label10; /* { dg-error "'break' statement operand 'label10' does not refer to a named loop or 'switch'" } */ + } +} --- gcc/testsuite/gcc.dg/gnu99-named-loops-3.c.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-3.c 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,117 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99" } */ + +void +foo (int x) +{ + for (int i = 0; i < 16; ++i) + { + int k; + label1: /* { dg-message "loop name defined here" } */ + for (int j = ({ if (x == 0) break label1; 0; }); j < 16; ++j) /* { dg-error "'break' statement operand 'label1' refers to a loop outside of its body" } */ + ; + label2: /* { dg-message "loop name defined here" } */ + for (int j = ({ if (x == 1) continue label2; 0; }); j < 16; ++j) /* { dg-error "'continue' statement operand 'label2' refers to a loop outside of its body" } */ + ; + label3: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < ({ if (x == 2) break label3; 16; }); ++j) /* { dg-error "'break' statement operand 'label3' refers to a loop outside of its body" } */ + ; + label4: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < ({ if (x == 3) continue label4; 16; }); ++j) /* { dg-error "'continue' statement operand 'label4' refers to a loop outside of its body" } */ + ; + label5: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < 16; j += ({ if (x == 4) break label5; 1; })) /* { dg-error "'break' statement operand 'label5' refers to a loop outside of its body" } */ + ; + label6: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < 16; j += ({ if (x == 5) continue label6; 1; })) /* { dg-error "'continue' statement operand 'label6' refers to a loop outside of its body" } */ + ; + k = 0; + label7: /* { dg-message "loop name defined here" } */ + while (k < ({ if (x == 6) break label7; 16; })) /* { dg-error "'break' statement operand 'label7' refers to a loop outside of its body" } */ + ++k; + k = 0; + label8: /* { dg-message "loop name defined here" } */ + while (k < ({ if (x == 7) continue label8; 16; })) /* { dg-error "'continue' statement operand 'label8' refers to a loop outside of its body" } */ + ++k; + k = 0; + label9: + do + ++k; + while (k <= ({ if (x == 8) break label9; 16; })); /* { dg-error "'break' statement operand 'label9' does not refer to a named loop or 'switch'" } */ + k = 0; + label10: + do + ++k; + while (k <= ({ if (x == 9) continue label10; 16; })); /* { dg-error "'continue' statement operand 'label10' does not refer to a named loop" } */ + label11: /* { dg-message "'switch' name defined here" } */ + switch (x + ({ if (x == 10) break label11; 0; })) /* { dg-error "'break' statement operand 'label11' refers to a 'switch' outside of its body" } */ + { + case 0: + break; + } + } + label12: + label13: + label14: + for (int i = 0; i < 32; ++i) + { + label15: + switch (i) + { + label16: + case 0: + label17: + label18: + label19: + label20: + label21: + label22: + label23: + label24: + label25: + label26: + label27: + label28: + label29: + label30: + for (int j = 0; j < 32; ++j) + { + if (j == 31) + continue label14; + else if (j == 30) + break label15; + void bar (void) + { + label31: + for (int k = 0; k < 32; ++k) + if (k == 31) + continue label31; + else if (k == 30) + break label31; + else if (k == 29) + continue label22; /* { dg-error "'continue' statement operand 'label22' does not refer to a named loop; did you mean 'label31'\\\?" } */ + else if (k == 28) + break label20; /* { dg-error "'break' statement operand 'label20' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + else if (k == 27) + break label15; /* { dg-error "'break' statement operand 'label15' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + else if (k == 26) + continue label13; /* { dg-error "'continue' statement operand 'label13' does not refer to a named loop; did you mean 'label31'\\\?" } */ + else if (k == 25) + break label12; /* { dg-error "'break' statement operand 'label12' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + } + bar (); + if (j == 29) + continue label22; + else if (j == 28) + break label20; + else if (j == 27) + break label15; + else if (j == 26) + continue label13; + else if (j == 25) + break label12; + } + } + } +} --- gcc/testsuite/gcc.dg/gnu99-named-loops-4.c.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-4.c 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,5 @@ +/* N3355 - Named loops. */ +/* { dg-do run } */ +/* { dg-options "-std=gnu99" } */ + +#include "c2y-named-loops-4.c" --- gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,117 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2y" } */ + +void +foo (int x) +{ + for (int i = 0; i < 16; ++i) + { + int k; + label1: /* { dg-message "loop name defined here" } */ + for (int j = ({ if (x == 0) break label1; 0; }); j < 16; ++j) /* { dg-error "'break' statement operand 'label1' refers to a loop outside of its body" } */ + ; + label2: /* { dg-message "loop name defined here" } */ + for (int j = ({ if (x == 1) continue label2; 0; }); j < 16; ++j) /* { dg-error "'continue' statement operand 'label2' refers to a loop outside of its body" } */ + ; + label3: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < ({ if (x == 2) break label3; 16; }); ++j) /* { dg-error "'break' statement operand 'label3' refers to a loop outside of its body" } */ + ; + label4: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < ({ if (x == 3) continue label4; 16; }); ++j) /* { dg-error "'continue' statement operand 'label4' refers to a loop outside of its body" } */ + ; + label5: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < 16; j += ({ if (x == 4) break label5; 1; })) /* { dg-error "'break' statement operand 'label5' refers to a loop outside of its body" } */ + ; + label6: /* { dg-message "loop name defined here" } */ + for (int j = 0; j < 16; j += ({ if (x == 5) continue label6; 1; })) /* { dg-error "'continue' statement operand 'label6' refers to a loop outside of its body" } */ + ; + k = 0; + label7: /* { dg-message "loop name defined here" } */ + while (k < ({ if (x == 6) break label7; 16; })) /* { dg-error "'break' statement operand 'label7' refers to a loop outside of its body" } */ + ++k; + k = 0; + label8: /* { dg-message "loop name defined here" } */ + while (k < ({ if (x == 7) continue label8; 16; })) /* { dg-error "'continue' statement operand 'label8' refers to a loop outside of its body" } */ + ++k; + k = 0; + label9: + do + ++k; + while (k <= ({ if (x == 8) break label9; 16; })); /* { dg-error "'break' statement operand 'label9' does not refer to a named loop or 'switch'" } */ + k = 0; + label10: + do + ++k; + while (k <= ({ if (x == 9) continue label10; 16; })); /* { dg-error "'continue' statement operand 'label10' does not refer to a named loop" } */ + label11: /* { dg-message "'switch' name defined here" } */ + switch (x + ({ if (x == 10) break label11; 0; })) /* { dg-error "'break' statement operand 'label11' refers to a 'switch' outside of its body" } */ + { + case 0: + break; + } + } + label12: + label13: + label14: + for (int i = 0; i < 32; ++i) + { + label15: + switch (i) + { + label16: + case 0: + label17: + label18: + label19: + label20: + label21: + label22: + label23: + label24: + label25: + label26: + label27: + label28: + label29: + label30: + for (int j = 0; j < 32; ++j) + { + if (j == 31) + continue label14; + else if (j == 30) + break label15; + void bar (void) + { + label31: + for (int k = 0; k < 32; ++k) + if (k == 31) + continue label31; + else if (k == 30) + break label31; + else if (k == 29) + continue label22; /* { dg-error "'continue' statement operand 'label22' does not refer to a named loop; did you mean 'label31'\\\?" } */ + else if (k == 28) + break label20; /* { dg-error "'break' statement operand 'label20' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + else if (k == 27) + break label15; /* { dg-error "'break' statement operand 'label15' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + else if (k == 26) + continue label13; /* { dg-error "'continue' statement operand 'label13' does not refer to a named loop; did you mean 'label31'\\\?" } */ + else if (k == 25) + break label12; /* { dg-error "'break' statement operand 'label12' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */ + } + bar (); + if (j == 29) + continue label22; + else if (j == 28) + break label20; + else if (j == 27) + break label15; + else if (j == 26) + continue label13; + else if (j == 25) + break label12; + } + } + } +} --- gcc/testsuite/gcc.dg/gomp/named-loops-1.c.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/gcc.dg/gomp/named-loops-1.c 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,101 @@ +/* Cases which will be IMHO always invalid in OpenMP, + just perhaps could have different diagnostic wording. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -fopenmp" } */ + +void +foo () +{ + label1: + for (int i = 0; i < 32; ++i) + #pragma omp parallel for + for (int j = 0; j < 32; ++j) + { + if (j == 31) + break label1; /* { dg-error "break statement used with OpenMP for loop" } */ + else if (j == 30) + continue label1; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + label2: + for (int k = 0; k < 32; ++k) + if (k == 31) + break label2; + else if (k == 30) + continue label2; + else if (k == 29) + break label1; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + else if (k == 28) + continue label1; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + } +} + +void +bar () +{ + label1: + #pragma omp parallel for + for (int i = 0; i < 32; ++i) + if (i == 31) + break label1; /* { dg-error "break' statement operand 'label1' does not refer to a named loop or 'switch'" } */ + /* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */ + label2: + #pragma omp parallel for collapse(2) + for (int i = 0; i < 32; ++i) + for (int j = 0; j < 32; ++j) + if (i == 31 && j == 31) + break label2; /* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'" } */ + /* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */ + else if (i == 31 && j == 30) + continue label2; /* { dg-error "'continue' statement operand 'label2' does not refer to a named loop" } */ +} + +void +baz () +{ + label1: + [[omp::directive (parallel for)]] + for (int i = 0; i < 32; ++i) + if (i == 31) + break label1; /* { dg-error "break' statement operand 'label1' does not refer to a named loop or 'switch'" } */ + /* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */ + label2: + [[omp::directive (parallel for, collapse(2))]] + for (int i = 0; i < 32; ++i) + for (int j = 0; j < 32; ++j) + if (i == 31 && j == 31) + break label2; /* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'" } */ + /* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */ + else if (i == 31 && j == 30) + continue label2; /* { dg-error "'continue' statement operand 'label2' does not refer to a named loop" } */ +} + +void +qux () +{ + label1: + #pragma omp parallel for collapse(2) + for (int i = 0; i < 32; ++i) /* { dg-error "not enough nested loops" } */ + label2: + for (int j = 0; j < 32; ++j) + if (j == 31) + break label1; /* { dg-error "'break' statement operand 'label1' does not refer to a named loop or 'switch'; did you mean 'label2'\\\?" } */ + else if (j == 30) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */ + else if (j == 29) + break label2; /* This is IMHO invalid too and currently just diagnosed by the not enough nested loops. */ +} + +void +garply () +{ + label1: + [[omp::directive (parallel for, collapse(2))]] + for (int i = 0; i < 32; ++i) /* { dg-error "not enough nested loops" } */ + label2: + for (int j = 0; j < 32; ++j) + if (j == 31) + break label1; /* { dg-error "'break' statement operand 'label1' does not refer to a named loop or 'switch'; did you mean 'label2'\\\?" } */ + else if (j == 30) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */ + else if (j == 29) + break label2; /* This is IMHO invalid too and currently just diagnosed by the not enough nested loops. */ +} --- gcc/testsuite/gcc.dg/gomp/named-loops-2.c.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/gcc.dg/gomp/named-loops-2.c 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,82 @@ +/* Cases which perhaps could be valid in OpenMP one day, but aren't + accepted right now. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -fopenmp" } */ + +void +foo () +{ + label1: + #pragma omp parallel for + for (int i = 0; i < 32; ++i) + if (i == 31) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop" } */ + else + { + label2: + for (int j = 0; j < 32; ++j) + if (j == 31) + continue label2; + else if (j == 30) + break label2; + else if (j == 29) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */ + } +} + +void +bar () +{ + label1: + [[omp::directive (parallel for)]] + for (int i = 0; i < 32; ++i) + if (i == 31) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop" } */ + else + { + label2: + for (int j = 0; j < 32; ++j) + if (j == 31) + continue label2; + else if (j == 30) + break label2; + else if (j == 29) + continue label1; /* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */ + } +} + +void +baz () +{ + label1: + #pragma omp parallel for collapse(2) + for (int i = 0; i < 32; ++i) /* { dg-error "not enough nested loops" } */ + label2: + for (int j = 0; j < 32; ++j) + label3: + for (int k = 0; k < 32; ++k) + if (k == 31) + continue label3; + else if (k == 30) + break label3; + else if (k == 29) + continue label2; +} + +void +qux () +{ + label1: + [[omp::directive (parallel for, collapse(2))]] + for (int i = 0; i < 32; ++i) /* { dg-error "not enough nested loops" } */ + label2: + for (int j = 0; j < 32; ++j) + label3: + for (int k = 0; k < 32; ++k) + if (k == 31) + continue label3; + else if (k == 30) + break label3; + else if (k == 29) + continue label2; +} --- gcc/testsuite/objc.dg/named-loops-1.m.jj 2024-10-15 19:10:03.867008354 +0200 +++ gcc/testsuite/objc.dg/named-loops-1.m 2024-10-15 19:10:03.867008354 +0200 @@ -0,0 +1,172 @@ +/* Test basic Objective-C foreach syntax. This tests iterations, with + the basic syntax 'for (object in array) statements' +*/ +/* { dg-do run } */ +/* { dg-skip-if "No NeXT fast enum. pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ +/* { dg-options "-mno-constant-cfstrings" { target *-*-darwin* } } */ +/* { dg-additional-sources "../objc-obj-c++-shared/nsconstantstring-class-impl.m" } */ +/* { dg-additional-options "-Wno-objc-root-class" } */ + +#include "../objc-obj-c++-shared/TestsuiteObject.m" +#ifndef __NEXT_RUNTIME__ +#include +#else +#include "../objc-obj-c++-shared/nsconstantstring-class.h" +#endif + +extern int printf (const char *, ...); +#include + +/* +struct __objcFastEnumerationState +{ + unsigned long state; + id *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; +*/ + + /* A mini-array implementation that can be used to test fast + enumeration. You create the array with some objects; you can + mutate the array, and you can fast-enumerate it. + */ +@interface MyArray : TestsuiteObject +{ + unsigned int length; + id *objects; + unsigned long mutated; +} +- (id) initWithLength: (unsigned int)l objects: (id *)o; +- (void) mutate; +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state + objects:(id *)stackbuf + count:(unsigned long)len; +@end + +@implementation MyArray : TestsuiteObject +- (id) initWithLength: (unsigned int)l + objects: (id *)o +{ + length = l; + objects = o; + mutated = 0; + return self; +} +- (void) mutate +{ + mutated = 1; +} +- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state + objects: (id*)stackbuf + count: (unsigned long)len +{ + unsigned long i, batch_size; + + /* We keep how many objects we served in the state->state counter. So the next batch + will contain up to length - state->state objects. */ + batch_size = length - state->state; + + /* Make obvious adjustments. */ + if (batch_size < 0) + batch_size = 0; + + if (batch_size > len) + batch_size = len; + + /* Copy the objects. */ + for (i = 0; i < batch_size; i++) + stackbuf[i] = objects[i]; + + state->state += batch_size; + state->itemsPtr = stackbuf; + state->mutationsPtr = &mutated; + + return batch_size; +} +@end + +void +foo (MyArray *array, int x) +{ + TestsuiteObject *object; + int i, j, k; + + label1: + for (i = 0; i < 2; ++i) + { + if (i == 1) + { + if (x != 5) + abort (); + return; + } + k = 0; + label2: + for (object in array) + { + if (k == 1) + { + if (x != 3) + abort (); + return; + } + ++k; + label3: + for (j = 0; j < 2; ++j) + { + if (j == 1) + { + if (x != 1) + abort (); + return; + } + label4: + switch (x) + { + case 0: + break label4; + case 1: + continue label3; + case 2: + break label3; + case 3: + continue label2; + case 4: + break label2; + case 5: + continue label1; + default: + break label1; + } + if (x != 0) + abort (); + return; + } + if (x != 2) + abort (); + return; + } + if (x != 4) + abort (); + return; + } + if (x <= 5) + abort (); +} + +int +main () +{ + MyArray *array; + id objects[2] = { @"object1", @"object2" }; + int i; + + array = [[MyArray alloc] initWithLength: 2 + objects: objects]; + for (i = 0; i < 6; ++i) + foo (array, i); + + return 0; +}