https://gcc.gnu.org/g:edbdafd8688939ddeeca395d7a997a2ce7308d2b
commit r17-5689-gedbdafd8688939ddeeca395d7a997a2ce7308d2b Author: Utkarsh Bahuguna <[email protected]> Date: Thu Oct 8 15:57:48 2026 +0530 gccrs: Fix ICE on rest patterns in enum tuple variants A tuple-variant pattern with `..` on an enum, such as `E::A(x, ..)`, hit an assert in the HAS_REST branches of the check and bindings visitors, which only knew the field layout of a struct. Read the fields of an enum variant through the payload union, as the NO_REST branches already do. Fixes Rust-GCC/gccrs#4978 Assisted-by: Claude (Opus 5.5) for tests. gcc/rust/ChangeLog: * backend/rust-compile-pattern.cc (CompilePatternCheckExpr::visit): Support rest patterns in enum tuple variants. (CompilePatternBindings::visit): Likewise. gcc/testsuite/ChangeLog: * rust/compile/irrefutable-struct.rs: Remove dg-ice, it compiles now. * rust/compile/tuple-variant-rest.rs: New test. * rust/execute/torture/tuple-variant-rest.rs: New test. Signed-off-by: Utkarsh Bahuguna <[email protected]> Diff: --- gcc/rust/backend/rust-compile-pattern.cc | 42 +++++++++--- gcc/testsuite/rust/compile/irrefutable-struct.rs | 7 +- gcc/testsuite/rust/compile/tuple-variant-rest.rs | 37 +++++++++++ .../rust/execute/torture/tuple-variant-rest.rs | 77 ++++++++++++++++++++++ 4 files changed, 148 insertions(+), 15 deletions(-) diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc index 309d8f1953ee..8ba8b300b9d3 100644 --- a/gcc/rust/backend/rust-compile-pattern.cc +++ b/gcc/rust/backend/rust-compile-pattern.cc @@ -416,15 +416,26 @@ CompilePatternCheckExpr::visit (HIR::TupleStructPattern &pattern) size_t num_patterns = items_has_rest.get_lower_patterns ().size () + items_has_rest.get_upper_patterns ().size (); - // enums cases shouldn't reach here - rust_assert (num_patterns <= variant->num_fields () - && (!adt->is_enum ())); + rust_assert (num_patterns <= variant->num_fields ()); + + tree fields_expr = match_scrutinee_expr; + if (adt->is_enum ()) + { + // find payload union field of scrutinee + tree payload_ref + = Backend::struct_field_expression (match_scrutinee_expr, 1, + pattern.get_locus ()); + + fields_expr + = Backend::struct_field_expression (payload_ref, variant_index, + pattern.get_locus ()); + } size_t tuple_field_index = 0; for (auto &pattern : items_has_rest.get_lower_patterns ()) { tree field_expr - = Backend::struct_field_expression (match_scrutinee_expr, + = Backend::struct_field_expression (fields_expr, tuple_field_index++, pattern->get_locus ()); tree check_expr_sub @@ -438,7 +449,7 @@ CompilePatternCheckExpr::visit (HIR::TupleStructPattern &pattern) for (auto &pattern : items_has_rest.get_upper_patterns ()) { tree field_expr - = Backend::struct_field_expression (match_scrutinee_expr, + = Backend::struct_field_expression (fields_expr, tuple_field_index++, pattern->get_locus ()); tree check_expr_sub @@ -841,15 +852,26 @@ CompilePatternBindings::visit (HIR::TupleStructPattern &pattern) size_t num_patterns = items_has_rest.get_lower_patterns ().size () + items_has_rest.get_upper_patterns ().size (); - // enums cases shouldn't reach here - rust_assert (num_patterns <= variant->num_fields () - && (!adt->is_enum ())); + rust_assert (num_patterns <= variant->num_fields ()); + + tree fields_expr = match_scrutinee_expr; + if (adt->is_enum ()) + { + // find payload union field of scrutinee + tree payload_ref + = Backend::struct_field_expression (match_scrutinee_expr, 1, + pattern.get_locus ()); + + fields_expr + = Backend::struct_field_expression (payload_ref, variant_index, + pattern.get_locus ()); + } size_t tuple_field_index = 0; for (auto &pattern : items_has_rest.get_lower_patterns ()) { tree binding - = Backend::struct_field_expression (match_scrutinee_expr, + = Backend::struct_field_expression (fields_expr, tuple_field_index++, pattern->get_locus ()); @@ -862,7 +884,7 @@ CompilePatternBindings::visit (HIR::TupleStructPattern &pattern) for (auto &pattern : items_has_rest.get_upper_patterns ()) { tree binding - = Backend::struct_field_expression (match_scrutinee_expr, + = Backend::struct_field_expression (fields_expr, tuple_field_index++, pattern->get_locus ()); diff --git a/gcc/testsuite/rust/compile/irrefutable-struct.rs b/gcc/testsuite/rust/compile/irrefutable-struct.rs index feeb1b32fe72..6bd7ed25b4a8 100644 --- a/gcc/testsuite/rust/compile/irrefutable-struct.rs +++ b/gcc/testsuite/rust/compile/irrefutable-struct.rs @@ -1,11 +1,8 @@ #![feature(no_core)] #![no_core] -// this test ICEs because in CompilePatternBindings::visit (HIR::TupleStructPattern &pattern) -// we are missing an implementation handling 'E::T(a, .., b)' with rest patterns -// once that is implemented, this test should pass -// { dg-ice "" } -// +// { dg-additional-options "-w" } + struct S { x: i32, y: i32, diff --git a/gcc/testsuite/rust/compile/tuple-variant-rest.rs b/gcc/testsuite/rust/compile/tuple-variant-rest.rs new file mode 100644 index 000000000000..2dba1855801e --- /dev/null +++ b/gcc/testsuite/rust/compile/tuple-variant-rest.rs @@ -0,0 +1,37 @@ +#![feature(no_core)] +#![no_core] + +// A rest pattern in an enum tuple variant used to ICE in the backend. + +pub enum E { + A(i32, i32, i32), + B, +} + +pub fn first(e: E) -> i32 { + match e { + E::A(x, ..) => x, + E::B => 0, + } +} + +pub fn last(e: E) -> i32 { + match e { + E::A(.., y) => y, + E::B => 0, + } +} + +pub fn ends(e: E) -> i32 { + match e { + E::A(x, .., y) => x + y, + E::B => 0, + } +} + +pub fn any(e: E) -> bool { + match e { + E::A(..) => true, + E::B => false, + } +} diff --git a/gcc/testsuite/rust/execute/torture/tuple-variant-rest.rs b/gcc/testsuite/rust/execute/torture/tuple-variant-rest.rs new file mode 100644 index 000000000000..ff9572b5f435 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/tuple-variant-rest.rs @@ -0,0 +1,77 @@ +#![feature(no_core)] +#![no_core] + +// A rest pattern in an enum tuple variant used to ICE in the backend. Check +// that the fields around `..` are read from the right variant. + +enum E { + A(i32, i32, i32), + B(i32, i32, i32), +} + +fn first(e: E) -> i32 { + match e { + E::A(x, ..) => x, + E::B(..) => 100, + } +} + +fn last(e: E) -> i32 { + match e { + E::A(.., y) => y, + E::B(..) => 100, + } +} + +fn ends(e: E) -> i32 { + match e { + E::A(x, .., y) => x * 100 + y, + E::B(..) => 100, + } +} + +fn last_of_b(e: E) -> i32 { + match e { + E::B(.., z) => z, + E::A(..) => 200, + } +} + +fn starts_with_seven(e: E) -> i32 { + match e { + E::A(7, ..) => 1, + E::A(..) => 2, + E::B(..) => 3, + } +} + +fn main() -> i32 { + if first(E::A(7, 11, 13)) != 7 { + return 1; + } + if last(E::A(7, 11, 13)) != 13 { + return 2; + } + if ends(E::A(7, 11, 13)) != 713 { + return 3; + } + if first(E::B(7, 11, 13)) != 100 { + return 4; + } + if last_of_b(E::B(17, 19, 23)) != 23 { + return 5; + } + if last_of_b(E::A(17, 19, 23)) != 200 { + return 6; + } + if starts_with_seven(E::A(7, 11, 13)) != 1 { + return 7; + } + if starts_with_seven(E::A(8, 11, 13)) != 2 { + return 8; + } + if starts_with_seven(E::B(7, 11, 13)) != 3 { + return 9; + } + 0 +}
