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 &param : function.get_function_params ())
+    {
+      TyTy::BaseType *param_ty;
+      bool ok
+       = tyctx.lookup_type (param.get_mappings ().get_hirid (), &param_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" }
+}

Reply via email to