From patchwork Fri Oct 11 07:09:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1995920 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=eXipvlzC; 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 4XPySp4swJz1xtp for ; Fri, 11 Oct 2024 18:10:34 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C2E6F385781B for ; Fri, 11 Oct 2024 07:10:32 +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 E7F7D3858D26 for ; Fri, 11 Oct 2024 07:09:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E7F7D3858D26 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 E7F7D3858D26 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=1728630603; cv=none; b=qKPbck+WmIPUeZmHVjNyuX3wuoDFiq94oAZoHlqijqOaF/r9K4G8cE6g56LgYpFqbUqBGT/cKFkijpzHhu/3M31y9RmK6P+Mtw/ciLHVtbsJztCnoIFDSlLLWmN+618LzetUWS4gNek1NrM+fnaZqlzuhz97M79sAkaLwJK9C64= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728630603; c=relaxed/simple; bh=xkg2KGweOUn7agGgCaHrLmTn2tLdPEQJjdPgTLeMTXg=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=aXJYqm1Vj+6XwRaLpv1gr/lg1XdPhYl54HR7tvlyjujTMBY7A46+GXeM2QR8WqLWjv3JVE3X6TkikPxGPnY33/viHJDsdkPWM5oUI6YsGIhVtqzWflXk1OBBOh5CAgU23k2iBuYAR2ptY69Re+TA+F2LHzzyRr0p9Ga/65T77LM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1728630596; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type; bh=r7amj7OjJXpFQvKoI9QKmhaGzAT0ydSjFErRdqhf2fg=; b=eXipvlzCMr+gvfzMSIPbSP4c+hzsLByH8wlcH9l29qqXhYPWfgCRg7cVQSGTBo9Ua7A7YE u+/ebOjiHDXC7Iv/tQSgzHA4xIgVxWmaWGdxhqitu1JolF2GCv4D+cBiW10lLlpjk0b5Fn DbO7EeLQAh7Plhb405qxks+GsRN7iP4= Received: from mx-prod-mc-04.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-686-guTWiToPN3i1k5i9daWBAA-1; Fri, 11 Oct 2024 03:09:53 -0400 X-MC-Unique: guTWiToPN3i1k5i9daWBAA-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (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-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 275EA19560AD for ; Fri, 11 Oct 2024 07:09:53 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.61]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1B08D19560AA; Fri, 11 Oct 2024 07:09:51 +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 49B79mPw2540558 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 11 Oct 2024 09:09:49 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 49B79m3O2540557; Fri, 11 Oct 2024 09:09:48 +0200 Date: Fri, 11 Oct 2024 09:09:48 +0200 From: Jakub Jelinek To: "Joseph S. Myers" Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c: Implement C2Y N3355 - Named Loops [PR117022] Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-14.4 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, 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 Hi! The following patch implements the C2Y N3355 - Named Loops paper. I've tried to implement it lazily, rather than proactively e.g. push labels to a vector just in case the following statement is iteration statement, switch statement or one of the loop pragmas followed by iteration statement the patch just notes the last statement in cur_stmt_list if any before c_parser_label/c_parser_all_labels and passes it down to the iteration/switch statement parsing routines, which then search backward for LABEL_EXPRs before they reach the given stop statement. The patch then adds one extra argument to {FOR,WHILE,DO,BREAK,CONTINUE,SWITCH}_STMT, which is set to a canonical name LABEL_DECL (the last named label before the construct). If one just refers to the innermost construct with a fancy name, it is in the end parsed the same as break/continue without an identifier (i.e. NULL_TREE argument), and if a loop or switch has name(s) but break/continue to that isn't used, the name is set to NULL_TREE. At c-gimplify.cc time the name is then pushed into a hash map mapping it to a pair of labels. I've implemented it also for ObjC foreach loops (which have break/continue handled during parsing, not during c-gimplify.cc). As for OpenMP/OpenACC, the patch right now pretends no OpenMP loop has a name, until something different is decided in the standard. As shown in the testcases, most break identifier/continue identifier cases aren't really useful in OpenMP code, a break identifier or continue identifier jumping out of an OpenMP region is certainly invalid (such regions have to be single entry single exit, so escaping it through goto/break lab/continue lab violates that), similarly break is disallowed in the innermost OpenMP nested loop, just continue is allowed, so the only thing that would make sense for OpenMP (second gomp testcase) would be allowing to give name to the innermost loop in OpenMP canonical loop nest (except that labels aren't allowed in the syntax right now in between the loops) and only continue to that label. For collapse(1) loops that would be a label before the #pragma or [[omp::directive (parallel for)]] etc. And of course, what already works fine in the patch is break/continue to non-OpenMP loops nested in OpenMP loops. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2024-10-11 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/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/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-01-03 22:33:37.511703242 +0100 +++ gcc/c-family/c-common.def 2024-10-10 17:45:40.339267609 +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-09-10 17:31:15.237019906 +0200 +++ gcc/c-family/c-common.h 2024-10-10 17:45:40.340267596 +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-09-25 17:25:07.118362089 +0200 +++ gcc/c-family/c-pretty-print.cc 2024-10-10 17:45:40.340267596 +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-06-06 22:11:22.789433332 +0200 +++ gcc/c-family/c-gimplify.cc 2024-10-10 17:45:40.341267582 +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-09 13:23:03.957530675 +0200 +++ gcc/c/c-tree.h 2024-10-10 18:25:04.153837402 +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); @@ -813,7 +837,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); @@ -829,7 +853,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-01-03 22:33:37.522703090 +0100 +++ gcc/c/c-lang.h 2024-10-10 17:45:40.339267609 +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-09 13:23:03.960530634 +0200 +++ gcc/c/c-parser.cc 2024-10-10 18:23:34.238070527 +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)) @@ -7150,6 +7151,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). */ @@ -7226,6 +7245,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; @@ -7256,6 +7276,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 @@ -7269,7 +7290,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) { @@ -7288,6 +7309,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)) @@ -7298,6 +7320,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; } } @@ -7359,6 +7382,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)) { @@ -7400,6 +7424,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; @@ -7419,7 +7444,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; @@ -7428,6 +7453,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)) { @@ -7447,6 +7473,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; } } @@ -7458,7 +7485,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 @@ -7468,9 +7496,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; @@ -7782,11 +7811,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 @@ -7799,6 +7843,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) { location_t loc = c_parser_peek_token (parser)->location; @@ -7824,16 +7869,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); @@ -7857,11 +7902,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); @@ -7977,7 +8024,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: @@ -8072,6 +8119,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)) @@ -8088,7 +8136,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 @@ -8116,6 +8164,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)) @@ -8131,7 +8180,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 @@ -8274,7 +8323,7 @@ c_parser_if_statement (c_parser *parser, */ 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; @@ -8283,6 +8332,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; @@ -8304,9 +8355,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); @@ -8316,6 +8376,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); } @@ -8331,7 +8399,7 @@ c_parser_switch_statement (c_parser *par 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; @@ -8339,6 +8407,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) { @@ -8368,6 +8438,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)); @@ -8375,9 +8450,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)); @@ -8398,12 +8477,14 @@ c_parser_while_statement (c_parser *pars 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) { @@ -8421,9 +8502,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, @@ -8443,7 +8535,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)); } @@ -8508,7 +8600,7 @@ c_parser_do_statement (c_parser *parser, 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; @@ -8523,6 +8615,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) { @@ -8715,9 +8809,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)); @@ -8726,6 +8833,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, @@ -8733,7 +8850,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); @@ -8747,6 +8866,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; @@ -13448,7 +13569,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; } @@ -13719,7 +13840,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; @@ -14791,7 +14912,8 @@ c_parser_pragma_unroll (c_parser *parser true if we actually parsed such a pragma. */ 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; @@ -15044,11 +15166,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; @@ -22771,7 +22896,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) @@ -25671,7 +25796,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-07 21:23:25.835994350 +0200 +++ gcc/c/c-decl.cc 2024-10-10 18:16:01.535279006 +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,210 @@ 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 == 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-09 13:23:03.962530606 +0200 +++ gcc/c/c-typeck.cc 2024-10-10 18:28:09.413296711 +0200 @@ -12288,7 +12288,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; @@ -12341,7 +12341,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 (); @@ -12442,7 +12442,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 @@ -12454,7 +12454,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"); @@ -12474,7 +12474,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"); @@ -12493,14 +12493,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-03 20:10:07.360515713 +0200 +++ gcc/cp/semantics.cc 2024-10-10 17:45:40.327267774 +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/c2y-named-loops-1.c.jj 2024-10-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-1.c 2024-10-10 17:45:40.328267761 +0200 @@ -0,0 +1,5 @@ +/* N3355 - Named loops. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +#include "gnu99-named-loops-1.c" --- gcc/testsuite/gcc.dg/c2y-named-loops-2.c.jj 2024-10-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-2.c 2024-10-10 17:45:40.328267761 +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-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/c2y-named-loops-4.c 2024-10-10 17:45:40.328267761 +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/gnu99-named-loops-1.c.jj 2024-10-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-1.c 2024-10-10 17:45:40.328267761 +0200 @@ -0,0 +1,102 @@ +/* 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; + } +} --- gcc/testsuite/gcc.dg/gnu99-named-loops-2.c.jj 2024-10-10 17:45:40.327267774 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-2.c 2024-10-10 17:45:40.327267774 +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-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-3.c 2024-10-10 17:45:40.328267761 +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-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/gnu99-named-loops-4.c 2024-10-10 17:45:40.328267761 +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-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c 2024-10-10 17:45:40.328267761 +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-10 17:45:40.327267774 +0200 +++ gcc/testsuite/gcc.dg/gomp/named-loops-1.c 2024-10-10 17:45:40.327267774 +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-10 17:45:40.328267761 +0200 +++ gcc/testsuite/gcc.dg/gomp/named-loops-2.c 2024-10-10 17:45:40.328267761 +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-10 17:55:33.976125322 +0200 +++ gcc/testsuite/objc.dg/named-loops-1.m 2024-10-10 18:12:37.605076906 +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; +}