From: Utkarsh Bahuguna <[email protected]>

Unit variants in patterns were treated as wildcards, so a match naming
only some of them still looked exhaustive. Lower a path pattern that
names a variant of the scrutinee's enum to that variant. issues/3726.rs
only matched one of its two variants, so restore the or-pattern from the
issue there.

Fixes Rust-GCC/gccrs#2311

Assisted-by: Claude Opus 5.5 (Anthropic)

gcc/rust/ChangeLog:

        * checks/errors/rust-hir-pattern-analysis.cc (lower_pattern): Lower
        path patterns that name an enum variant.

gcc/testsuite/ChangeLog:

        * rust/compile/exhaustiveness9.rs: New test.
        * rust/compile/exhaustiveness10.rs: New test.
        * rust/compile/issues/3726.rs: Match both variants.
        * rust/compile/issues/issue-2311.rs: New test.

Signed-off-by: Utkarsh Bahuguna <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/691624530ad1b3c3c915193851cf04af246e15ea

The commit has been mentioned in the following issue(s):
 - Rust-GCC/gccrs#2311: https://github.com/Rust-GCC/gccrs/issues/2311

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4976

 .../errors/rust-hir-pattern-analysis.cc       | 26 +++++--
 .../rust/compile/exhaustiveness10.rs          | 24 +++++++
 gcc/testsuite/rust/compile/exhaustiveness9.rs | 70 +++++++++++++++++++
 gcc/testsuite/rust/compile/issues/3726.rs     |  4 +-
 .../rust/compile/issues/issue-2311.rs         | 19 +++++
 5 files changed, 138 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/exhaustiveness10.rs
 create mode 100644 gcc/testsuite/rust/compile/exhaustiveness9.rs
 create mode 100644 gcc/testsuite/rust/compile/issues/issue-2311.rs

diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc 
b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
index 3c6270bf2..8043ec220 100644
--- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
+++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
@@ -1384,11 +1384,29 @@ lower_pattern (Resolver::TypeCheckContext *ctx, 
HIR::Pattern &pattern,
       break;
     case HIR::Pattern::PatternType::PATH:
       {
-       // TODO: support constants, associated constants, enum variants and
-       // structs
+       // A unit variant of the scrutinee's enum is a variant constructor
+       // without fields. Anything else is still treated as a wildcard.
+       // TODO: support constants, associated constants and unit structs
        // https://doc.rust-lang.org/reference/patterns.html#path-patterns
-       // unimplemented. Treat this pattern as wildcard for now.
-       return DeconstructedPat::make_wildcard (pattern.get_locus ());
+       // The type is not destructured on purpose: split_constructors sees the
+       // column's type as it is, and asserts that it is an ADT.
+       if (scrutinee_ty->get_kind () != TyTy::TypeKind::ADT)
+         return DeconstructedPat::make_wildcard (pattern.get_locus ());
+
+       TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (scrutinee_ty);
+       HirId variant_id = UNKNOWN_HIRID;
+       if (!adt->is_enum ()
+           || !ctx->lookup_variant_definition (
+             pattern.get_mappings ().get_hirid (), &variant_id))
+         return DeconstructedPat::make_wildcard (pattern.get_locus ());
+
+       TyTy::VariantDef *variant;
+       int variant_idx;
+       if (!adt->lookup_variant_by_id (variant_id, &variant, &variant_idx))
+         return DeconstructedPat::make_wildcard (pattern.get_locus ());
+
+       return DeconstructedPat (Constructor::make_variant (variant_idx), 0, {},
+                                pattern.get_locus ());
       }
       break;
     case HIR::Pattern::PatternType::REFERENCE:
diff --git a/gcc/testsuite/rust/compile/exhaustiveness10.rs 
b/gcc/testsuite/rust/compile/exhaustiveness10.rs
new file mode 100644
index 000000000..a0b3a6366
--- /dev/null
+++ b/gcc/testsuite/rust/compile/exhaustiveness10.rs
@@ -0,0 +1,24 @@
+#![feature(no_core)]
+#![no_core]
+
+// A unit variant inside a macro-expanded tuple struct pattern is checked like
+// any other, see Rust-GCC/gccrs#3726.
+
+pub enum TypeCtor {
+    Slice,
+    Array,
+}
+pub struct ApplicationTy(TypeCtor);
+
+macro_rules! ty_app {
+    ($ctor:pat) => {
+        ApplicationTy($ctor)
+    };
+}
+
+pub fn foo(ty: ApplicationTy) {
+    match ty {
+        // { dg-error "non-exhaustive patterns: 'ApplicationTy . 0: 
TypeCtor::Slice .' not covered" "" { target *-*-* } .-1 }
+        ty_app!(TypeCtor::Array) => {}
+    }
+}
diff --git a/gcc/testsuite/rust/compile/exhaustiveness9.rs 
b/gcc/testsuite/rust/compile/exhaustiveness9.rs
new file mode 100644
index 000000000..d3aa3024b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/exhaustiveness9.rs
@@ -0,0 +1,70 @@
+#![feature(no_core)]
+#![no_core]
+
+// Unit variants used to be treated as wildcards, so any match on them looked
+// exhaustive.
+
+enum E {
+    A,
+    B,
+}
+
+enum Three {
+    A,
+    B,
+    C,
+}
+
+enum Mixed {
+    A,
+    B(i32),
+    C,
+}
+
+const C: E = E::A;
+
+fn missing_arm(e: E) {
+    match e {
+        // { dg-error "non-exhaustive patterns: 'E::B' not covered" "" { 
target *-*-* } .-1 }
+        E::A => {}
+    }
+}
+
+fn mixed(m: Mixed) {
+    match m {
+        // { dg-error "non-exhaustive patterns: 'Mixed::C' not covered" "" { 
target *-*-* } .-1 }
+        Mixed::A => {}
+        Mixed::B(_) => {}
+    }
+}
+
+// These are valid matches
+fn all_covered(e: E) {
+    match e {
+        E::A => {}
+        E::B => {}
+    }
+}
+
+fn with_wildcard(t: Three) {
+    match t {
+        Three::A => {}
+        _ => {}
+    }
+}
+
+fn through_reference(e: &E) {
+    match e {
+        &E::A => {}
+        &E::B => {}
+    }
+}
+
+fn const_arm(e: E) {
+    match e {
+        C => {}
+        E::B => {}
+    }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/issues/3726.rs 
b/gcc/testsuite/rust/compile/issues/3726.rs
index 3c773099a..a0bf19c7f 100644
--- a/gcc/testsuite/rust/compile/issues/3726.rs
+++ b/gcc/testsuite/rust/compile/issues/3726.rs
@@ -14,7 +14,9 @@ macro_rules! ty_app {
 }
 
 pub fn foo(ty: ApplicationTy) {
+    // Both variants, as in the reproducer of Rust-GCC/gccrs#3726: a match
+    // with only `TypeCtor::Array` is not exhaustive.
     match ty {
-        ty_app!(TypeCtor::Array) => {}
+        ty_app!(TypeCtor::Array) | ty_app!(TypeCtor::Slice) => {}
     }
 }
diff --git a/gcc/testsuite/rust/compile/issues/issue-2311.rs 
b/gcc/testsuite/rust/compile/issues/issue-2311.rs
new file mode 100644
index 000000000..277025e16
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issues/issue-2311.rs
@@ -0,0 +1,19 @@
+#![feature(no_core)]
+#![no_core]
+
+// Rust-GCC/gccrs#2311: unit variants in patterns used to be treated as
+// wildcards, so this match was accepted.
+
+enum Terminator {
+    HastaLaVistaBaby,
+    TalkToMyHand,
+}
+
+fn main() {
+    let x = Terminator::HastaLaVistaBaby;
+
+    match x {
+        // { dg-error "non-exhaustive patterns: 'Terminator::HastaLaVistaBaby' 
not covered" "" { target *-*-* } .-1 }
+        Terminator::TalkToMyHand => {}
+    }
+}

base-commit: e7f01140e0aba4476d6c78ec5ba103913584940e
-- 
2.56.0

Reply via email to