https://gcc.gnu.org/g:ff64d77c9a37c716b7b4ca0a6b5920626ff34360
commit r17-3106-gff64d77c9a37c716b7b4ca0a6b5920626ff34360 Author: Egas Ribeiro <[email protected]> Date: Tue May 19 18:08:14 2026 +0100 gccrs: rust: Enforce irrefutability in let bindings and function parameters Add diagnostics for refutable patterns appearing in positions that require irrefutability: let bindings (without an else clause) and function parameters. Uses the type-aware is_refutable(BaseType&) introduced in the previous commit. The existing syntactic check for the function parameters case done during AST lowering is reduced to handle only bare literal patterns, with the comprehensive check deferred to PatternChecker after the type checker populates the necessary type information. Add comprehensive tests that cover the implemented refutability checks. Addresses: Rust-GCC#2082 gcc/rust/ChangeLog: * checks/errors/rust-hir-pattern-analysis.cc (PatternChecker::visit): Diagnose refutable patterns in Function parameters and LetStmt bindings. * hir/rust-ast-lower-item.cc (ASTLoweringItem::visit): Remove pattern kind whitelist; only error on bare literal patterns at lowering. gcc/testsuite/ChangeLog: * rust/compile/irrefutable-alt.rs: New test. * rust/compile/irrefutable-basic.rs: New test. * rust/compile/irrefutable-identifier-binding.rs: New test. * rust/compile/irrefutable-nested.rs: New test. * rust/compile/irrefutable-path.rs: New test. * rust/compile/irrefutable-range.rs: New test. * rust/compile/irrefutable-reference.rs: New test. * rust/compile/irrefutable-slice.rs: New test. * rust/compile/irrefutable-struct.rs: New test. * rust/compile/irrefutable-tuple.rs: New test. * rust/compile/refutable-alt.rs: New test. * rust/compile/refutable-binding.rs: New test. * rust/compile/refutable-literal-let.rs: New test. * rust/compile/refutable-literal-parameter.rs: New test. * rust/compile/refutable-path.rs: New test. * rust/compile/refutable-range.rs: New test. * rust/compile/refutable-reference.rs: New test. * rust/compile/refutable-struct.rs: New test. * rust/compile/refutable-tuple.rs: New test. Co-authored-by: João Novo <[email protected]> Signed-off-by: Egas Ribeiro <[email protected]> Diff: --- .../checks/errors/rust-hir-pattern-analysis.cc | 25 +++++++++++++++++ gcc/rust/hir/rust-ast-lower-item.cc | 15 +++------- gcc/testsuite/rust/compile/irrefutable-alt.rs | 11 ++++++++ gcc/testsuite/rust/compile/irrefutable-basic.rs | 16 +++++++++++ .../rust/compile/irrefutable-identifier-binding.rs | 8 ++++++ gcc/testsuite/rust/compile/irrefutable-nested.rs | 10 +++++++ gcc/testsuite/rust/compile/irrefutable-path.rs | 12 ++++++++ gcc/testsuite/rust/compile/irrefutable-range.rs | 9 ++++++ .../rust/compile/irrefutable-reference.rs | 6 ++++ gcc/testsuite/rust/compile/irrefutable-slice.rs | 14 ++++++++++ gcc/testsuite/rust/compile/irrefutable-struct.rs | 32 ++++++++++++++++++++++ gcc/testsuite/rust/compile/irrefutable-tuple.rs | 9 ++++++ gcc/testsuite/rust/compile/refutable-alt.rs | 12 ++++++++ gcc/testsuite/rust/compile/refutable-binding.rs | 6 ++++ .../rust/compile/refutable-literal-let.rs | 5 ++++ .../rust/compile/refutable-literal-parameter.rs | 4 +++ gcc/testsuite/rust/compile/refutable-path.rs | 14 ++++++++++ gcc/testsuite/rust/compile/refutable-range.rs | 9 ++++++ gcc/testsuite/rust/compile/refutable-reference.rs | 6 ++++ gcc/testsuite/rust/compile/refutable-struct.rs | 23 ++++++++++++++++ gcc/testsuite/rust/compile/refutable-tuple.rs | 10 +++++++ 21 files changed, 245 insertions(+), 11 deletions(-) diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc index ed1bd49baa67..81f872fdcc04 100644 --- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc +++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc @@ -484,6 +484,17 @@ PatternChecker::visit (UseDeclaration &) void PatternChecker::visit (Function &function) { + for (auto ¶m : function.get_function_params ()) + { + TyTy::BaseType *param_ty; + bool ok + = tyctx.lookup_type (param.get_mappings ().get_hirid (), ¶m_ty); + rust_assert (ok); + + if (param.get_param_name ().is_refutable (*param_ty)) + rust_error_at (param.get_locus (), ErrorCode::E0005, + "refutable pattern in function parameter"); + } function.get_definition ().accept_vis (*this); } @@ -684,6 +695,20 @@ PatternChecker::visit (LetStmt &stmt) { if (stmt.has_init_expr ()) stmt.get_init_expr ().accept_vis (*this); + + // skip let-else (allows refutable patterns) + if (stmt.has_else_expr ()) + return; + + TyTy::BaseType *binding_ty; + bool ok = tyctx.lookup_type (stmt.get_pattern ().get_mappings ().get_hirid (), + &binding_ty); + if (!ok) + return; // type-check failed earlier + + if (stmt.get_pattern ().is_refutable (*binding_ty)) + rust_error_at (stmt.get_pattern ().get_locus (), ErrorCode::E0005, + "refutable pattern in local binding"); } void diff --git a/gcc/rust/hir/rust-ast-lower-item.cc b/gcc/rust/hir/rust-ast-lower-item.cc index 96b6aefbc6a4..7f278902a090 100644 --- a/gcc/rust/hir/rust-ast-lower-item.cc +++ b/gcc/rust/hir/rust-ast-lower-item.cc @@ -458,20 +458,13 @@ ASTLoweringItem::visit (AST::Function &function) switch (param.get_pattern ().get_pattern_kind ()) { - case AST::Pattern::Kind::Identifier: - case AST::Pattern::Kind::Wildcard: - case AST::Pattern::Kind::Tuple: - case AST::Pattern::Kind::Struct: - case AST::Pattern::Kind::TupleStruct: - case AST::Pattern::Kind::Reference: - case AST::Pattern::Kind::Grouped: - case AST::Pattern::Kind::Slice: - case AST::Pattern::Kind::Rest: - break; - default: + case AST::Pattern::Kind::Literal: rust_error_at (param.get_locus (), "refutable pattern in function argument"); continue; + default: + // defer checking for when we have type information after lowering + break; } auto translated_type = std::unique_ptr<HIR::Type> ( diff --git a/gcc/testsuite/rust/compile/irrefutable-alt.rs b/gcc/testsuite/rust/compile/irrefutable-alt.rs new file mode 100644 index 000000000000..edc8a39ce09a --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-alt.rs @@ -0,0 +1,11 @@ +#![feature(no_core)] +#![no_core] +enum E { + A, + B, +} +fn a((E::A | E::B): E) {} // { dg-bogus {sorry, unimplemented: alt pattern} TODO { xfail *-*-* } } + // { dg-ice "" } +fn main() { + let (E::A | E::B): E; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-basic.rs b/gcc/testsuite/rust/compile/irrefutable-basic.rs new file mode 100644 index 000000000000..f86d396d9e15 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-basic.rs @@ -0,0 +1,16 @@ +#![feature(no_core)] +#![no_core] +fn _a(_: i32) {} +fn _b(_x: i32) {} +fn _d((_x, _y): (i32, i32)) {} +fn _e((_x, .., _y): (i32, i32, i32, i32)) {} +fn _f((..): (i32, i32)) {} +fn _g((_x, (..)): (i32, (i32, i32))) {} +fn main() { + let _ = 1; + let _x = 1; + let (_a, _b) = (1, 2); + let (_a, .., _b) = (1, 2, 3, 4); + let (..) = (1, 2); + let (_a, (..)) = (1, (2, 3)); +} diff --git a/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs b/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs new file mode 100644 index 000000000000..5cd30efb1952 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs @@ -0,0 +1,8 @@ +#![feature(no_core)] +#![no_core] +fn _a(_x @ _: i32) {} +fn _b(_x @ (_a, _b): (i32, i32)) {} +fn main() { + let _x @ _ = (1, 2); + let _x @ (_a, _b) = (1, 2); +} diff --git a/gcc/testsuite/rust/compile/irrefutable-nested.rs b/gcc/testsuite/rust/compile/irrefutable-nested.rs new file mode 100644 index 000000000000..1f3b8cb06166 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-nested.rs @@ -0,0 +1,10 @@ +#![feature(no_core)] +#![no_core] +fn _a(((_x, _y), _z): ((i32, i32), i32)) {} +fn _b((_x, (_y, _z)): (i32, (i32, i32))) {} +fn _c(&&_x: &&i32) {} +fn main() { + let ((_a, _b), _c) = ((1, 2), 3); + let (_a, (_b, _c)) = (1, (2, 3)); + let &&_x: &&i32; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-path.rs b/gcc/testsuite/rust/compile/irrefutable-path.rs new file mode 100644 index 000000000000..202673dc4f47 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-path.rs @@ -0,0 +1,12 @@ +#![feature(no_core)] +#![no_core] +enum E { + A, +} + +// TODO: this test ICEs because Path patterns in function parameters aren't fully implemented. +// once implementation is added this test should compile normally +fn a(E::A: E) {} // { dg-ice "" } +fn main() { + let E::A: E; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-range.rs b/gcc/testsuite/rust/compile/irrefutable-range.rs new file mode 100644 index 000000000000..89e535025fa7 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-range.rs @@ -0,0 +1,9 @@ +#![feature(no_core)] +#![no_core] +// TODO: remove xfails once parsing of 0.. in fn params and let bindings is implemented. +// { dg-do compile { xfail *-*-* } } +// { dg-excess-errors "parsing of open-ended ranges in patterns not implemented" { xfail *-*-* } } +fn a(0..: u8) {} +fn main() { + let 0..: u8; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-reference.rs b/gcc/testsuite/rust/compile/irrefutable-reference.rs new file mode 100644 index 000000000000..73bd14e5fa5f --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-reference.rs @@ -0,0 +1,6 @@ +#![feature(no_core)] +#![no_core] +fn _c(&_x: &i32) {} +fn main() { + let &_x = &1; // { dg-bogus {sorry, unimplemented: reference pattern let} TODO { xfail *-*-* } } +} diff --git a/gcc/testsuite/rust/compile/irrefutable-slice.rs b/gcc/testsuite/rust/compile/irrefutable-slice.rs new file mode 100644 index 000000000000..810c7da52471 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-slice.rs @@ -0,0 +1,14 @@ +#![feature(no_core)] +#![no_core] +// TODO: this test ICEs because slices in function parameters aren't fully implemented. +// once implementation is added this test should compile normally +// { dg-ice "" } +fn a([a, b, c]: [i32; 3]) {} +fn b([a, .., c]: [i32; 3]) {} +fn c([a, .., c]: [i32; 5]) {} +fn main() { + let [..]: [i32]; + let [a, b, c]: [i32; 3]; + let [a, .., c]: [i32; 3]; + let [a, .., c]: [i32; 5]; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-struct.rs b/gcc/testsuite/rust/compile/irrefutable-struct.rs new file mode 100644 index 000000000000..feeb1b32fe72 --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-struct.rs @@ -0,0 +1,32 @@ +#![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 "" } +// +struct S { + x: i32, + y: i32, +} +struct T(i32, i32); +enum E { + T(i32, i32), +} +fn a(S { x, y }: S) {} +fn b(S { x: a, y: b }: S) {} +fn c(S { x: _, y: _ }: S) {} +fn d(T(a, b): T) {} +fn e(T(a, .., b): T) {} +fn g(E::T(a, .., b): E) {} +fn h(E::T(a, b): E) {} +fn main() { + let S { x, y }: S; + let S { x: a, y: b }: S; + let S { x: _, y: _ }: S; + let T(a, b): T; + let T(a, .., b): T; + let E::T(a, .., b): E; + let E::T(a, b): E; +} diff --git a/gcc/testsuite/rust/compile/irrefutable-tuple.rs b/gcc/testsuite/rust/compile/irrefutable-tuple.rs new file mode 100644 index 000000000000..9b64e7f77d2b --- /dev/null +++ b/gcc/testsuite/rust/compile/irrefutable-tuple.rs @@ -0,0 +1,9 @@ +#![feature(no_core)] +#![no_core] +enum E { + A(i32), +} +fn _f((E::A(x), y): (E, i32)) {} +fn main() { + let (E::A(x), y): (E, i32); +} diff --git a/gcc/testsuite/rust/compile/refutable-alt.rs b/gcc/testsuite/rust/compile/refutable-alt.rs new file mode 100644 index 000000000000..18184be9a831 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-alt.rs @@ -0,0 +1,12 @@ +#![feature(no_core)] +#![no_core] +enum E { + A, + B, + C, +} +fn a((E::A | E::B): E) {} // { dg-bogus {sorry, unimplemented: alt pattern} TODO { xfail *-*-* } } + // { dg-ice "" } +fn main() { + let (E::A | E::B): E; +} diff --git a/gcc/testsuite/rust/compile/refutable-binding.rs b/gcc/testsuite/rust/compile/refutable-binding.rs new file mode 100644 index 000000000000..f0c7366e7886 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-binding.rs @@ -0,0 +1,6 @@ +#![feature(no_core)] +#![no_core] +fn f(a @ 1: i32){} // { dg-error "refutable pattern" } +fn main() { + let a @ 1: i32; // { dg-error "refutable pattern" } +} diff --git a/gcc/testsuite/rust/compile/refutable-literal-let.rs b/gcc/testsuite/rust/compile/refutable-literal-let.rs new file mode 100644 index 000000000000..4aaa92e82b02 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-literal-let.rs @@ -0,0 +1,5 @@ +#![feature(no_core)] +#![no_core] +fn main() { + let 1: i32; // { dg-error "refutable pattern in local" } +} diff --git a/gcc/testsuite/rust/compile/refutable-literal-parameter.rs b/gcc/testsuite/rust/compile/refutable-literal-parameter.rs new file mode 100644 index 000000000000..cae7d8f06468 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-literal-parameter.rs @@ -0,0 +1,4 @@ +#![feature(no_core)] +#![no_core] +fn a(1: i32) {} // { dg-error "refutable pattern in function" } + diff --git a/gcc/testsuite/rust/compile/refutable-path.rs b/gcc/testsuite/rust/compile/refutable-path.rs new file mode 100644 index 000000000000..89da0453acdb --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-path.rs @@ -0,0 +1,14 @@ +#![feature(no_core)] +#![no_core] +// { dg-ice "" } +enum E { + A, + B, +} +const A: u32 = 0; +fn a(E::A: E) {} +fn b(A: u32) {} +fn main() { + let A: u32; + let E::A: E; +} diff --git a/gcc/testsuite/rust/compile/refutable-range.rs b/gcc/testsuite/rust/compile/refutable-range.rs new file mode 100644 index 000000000000..8e6cd9ce84e1 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-range.rs @@ -0,0 +1,9 @@ +// TODO: remove dg-ice once range refutability is implemented. +// Once implemented, this should emit E0005 instead of ICEing. +#![feature(no_core)] +#![no_core] +// { dg-ice "" } +fn a(1..=5: i32) {} // { dg-bogus {sorry, unimplemented: range pattern} "catch E0005 once implemented" { xfail *-*-* } } +fn main() { + let 1..=5: i32; +} diff --git a/gcc/testsuite/rust/compile/refutable-reference.rs b/gcc/testsuite/rust/compile/refutable-reference.rs new file mode 100644 index 000000000000..3357f67f1292 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-reference.rs @@ -0,0 +1,6 @@ +#![feature(no_core)] +#![no_core] +fn a(&1: &i32) {} // { dg-error "refutable pattern" } +fn main() { + let &1: &i32; // { dg-error "refutable pattern" } +} diff --git a/gcc/testsuite/rust/compile/refutable-struct.rs b/gcc/testsuite/rust/compile/refutable-struct.rs new file mode 100644 index 000000000000..63097582c0c9 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-struct.rs @@ -0,0 +1,23 @@ +#![feature(no_core)] +#![no_core] +struct S { + x: i32, + y: i32, +} +struct T(i32, i32); +enum E { + T(i32, i32), + U(i32), +} +fn a(S { x, y: 1 }: S) {} // { dg-error "refutable pattern" } +fn b(S { x: 1, y: _ }: S) {} // { dg-error "refutable pattern" } +fn c(T(1, _): T) {} // { dg-error "refutable pattern" } +fn d(T(1, ..): T) {} // { dg-error "refutable pattern" } +fn e(E::T(_, _): E) {} // { dg-error "refutable pattern" } +fn main() { + let S { x, y: 1 }: S; // { dg-error "refutable pattern" } + let S { x: 1, y: _ }: S; // { dg-error "refutable pattern" } + let T(1, _): T; // { dg-error "refutable pattern" } + let T(1, ..): T; // { dg-error "refutable pattern" } + let E::T(_, _): E; // { dg-error "refutable pattern" } +} diff --git a/gcc/testsuite/rust/compile/refutable-tuple.rs b/gcc/testsuite/rust/compile/refutable-tuple.rs new file mode 100644 index 000000000000..5fbc4eba8123 --- /dev/null +++ b/gcc/testsuite/rust/compile/refutable-tuple.rs @@ -0,0 +1,10 @@ +#![feature(no_core)] +#![no_core] +fn a((x, 1): (i32, i32)) {} // { dg-error "refutable pattern" } +fn b((x, .., 1): (i32, i32, i32)) {} // { dg-error "refutable pattern" } +fn c((1, (..)): (i32, (i32, i32))) {} // { dg-error "refutable pattern" } +fn main() { + let (a, 1) = (1, 2); // { dg-error "refutable pattern" } + let (x, .., 1): (i32, i32, i32); // { dg-error "refutable pattern" } + let (1, (..)): (i32, (i32, i32)); // { dg-error "refutable pattern" } +}
