about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-04-15 15:47:25 +1000
committerGitHub <noreply@github.com>2025-04-15 15:47:25 +1000
commitaa9a80cc34065197a5ea8e4c58efa30b04de1919 (patch)
treefe5df4700757ea1bd89431f0a21f6e79a3981144
parentb8413ab8edb382cd4c55497a07a02bbc165005fe (diff)
parent7ad16974b995746b9156d90b8b80fb137afcd1e9 (diff)
downloadrust-aa9a80cc34065197a5ea8e4c58efa30b04de1919.tar.gz
rust-aa9a80cc34065197a5ea8e4c58efa30b04de1919.zip
Rollup merge of #138393 - oli-obk:pattern-type-in-pattern, r=BoxyUwU
Allow const patterns of matches to contain pattern types

Trying to pattern match on a type containing a pattern type will currently fail with an ICE

```rust
error: internal compiler error: compiler/rustc_mir_build/src/builder/matches/test.rs:459:18: invalid type for non-scalar compare: (u32) is 1..
  --> src/main.rs:22:5
   |
22 |     TWO => {}
   |     ^^^
```

because the compiler tries to generate a MIR `BinOp(Eq)` operation on a pattern type, which is not supported. While we could support that, there are side effects of allowing this (none that would compile, but the compiler would simultaneously think it could `==` pattern types and that it could not because `PartialEq` is not implemented. So instead I change the logic for pattern matching to transmute pattern types to their base type before comparing.

r? ```@BoxyUwU```

cc #123646 ```@scottmcm``` ```@joshtriplett```
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs14
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/test.rs29
-rw-r--r--tests/ui/type/pattern_types/derives.rs3
-rw-r--r--tests/ui/type/pattern_types/derives.stderr2
-rw-r--r--tests/ui/type/pattern_types/derives_fail.rs26
-rw-r--r--tests/ui/type/pattern_types/derives_fail.stderr74
-rw-r--r--tests/ui/type/pattern_types/matching.rs26
-rw-r--r--tests/ui/type/pattern_types/matching_fail.rs25
-rw-r--r--tests/ui/type/pattern_types/matching_fail.stderr43
9 files changed, 233 insertions, 9 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 05e0bb3f9f3..3c00b819a96 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1557,11 +1557,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::Transmute => {
-                        span_mirbug!(
-                            self,
-                            rvalue,
-                            "Unexpected CastKind::Transmute, which is not permitted in Analysis MIR",
-                        );
+                        let ty_from = op.ty(self.body, tcx);
+                        match ty_from.kind() {
+                            ty::Pat(base, _) if base == ty => {}
+                            _ => span_mirbug!(
+                                self,
+                                rvalue,
+                                "Unexpected CastKind::Transmute {ty_from:?} -> {ty:?}, which is not permitted in Analysis MIR",
+                            ),
+                        }
                     }
                 }
             }
diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs
index d1f9d4c34fe..57cf8fa719c 100644
--- a/compiler/rustc_mir_build/src/builder/matches/test.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/test.rs
@@ -140,8 +140,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let success_block = target_block(TestBranch::Success);
                 let fail_block = target_block(TestBranch::Failure);
 
-                let expect_ty = value.ty();
-                let expect = self.literal_operand(test.span, value);
+                let mut expect_ty = value.ty();
+                let mut expect = self.literal_operand(test.span, value);
 
                 let mut place = place;
                 let mut block = block;
@@ -174,6 +174,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         place = ref_str;
                         ty = ref_str_ty;
                     }
+                    &ty::Pat(base, _) => {
+                        assert_eq!(ty, value.ty());
+                        assert!(base.is_trivially_pure_clone_copy());
+
+                        let transmuted_place = self.temp(base, test.span);
+                        self.cfg.push_assign(
+                            block,
+                            self.source_info(scrutinee_span),
+                            transmuted_place,
+                            Rvalue::Cast(CastKind::Transmute, Operand::Copy(place), base),
+                        );
+
+                        let transmuted_expect = self.temp(base, test.span);
+                        self.cfg.push_assign(
+                            block,
+                            self.source_info(test.span),
+                            transmuted_expect,
+                            Rvalue::Cast(CastKind::Transmute, expect, base),
+                        );
+
+                        place = transmuted_place;
+                        expect = Operand::Copy(transmuted_expect);
+                        ty = base;
+                        expect_ty = base;
+                    }
                     _ => {}
                 }
 
diff --git a/tests/ui/type/pattern_types/derives.rs b/tests/ui/type/pattern_types/derives.rs
index 3878c47554d..a3959b38317 100644
--- a/tests/ui/type/pattern_types/derives.rs
+++ b/tests/ui/type/pattern_types/derives.rs
@@ -1,4 +1,5 @@
-//! Check that pattern types don't implement traits of their base automatically
+//! Check that pattern types don't implement traits of their base automatically.
+//! Exceptions are `Clone` and `Copy`, which have builtin impls for pattern types.
 
 #![feature(pattern_types)]
 #![feature(pattern_type_macro)]
diff --git a/tests/ui/type/pattern_types/derives.stderr b/tests/ui/type/pattern_types/derives.stderr
index f59617ebc45..2d83684b152 100644
--- a/tests/ui/type/pattern_types/derives.stderr
+++ b/tests/ui/type/pattern_types/derives.stderr
@@ -1,5 +1,5 @@
 error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999`
-  --> $DIR/derives.rs:10:20
+  --> $DIR/derives.rs:11:20
    |
 LL | #[derive(Clone, Copy, PartialEq)]
    |                       --------- in this derive macro expansion
diff --git a/tests/ui/type/pattern_types/derives_fail.rs b/tests/ui/type/pattern_types/derives_fail.rs
new file mode 100644
index 00000000000..a3fbad66720
--- /dev/null
+++ b/tests/ui/type/pattern_types/derives_fail.rs
@@ -0,0 +1,26 @@
+//! Check that pattern types don't implement traits of their base automatically.
+//! Exceptions are `Clone` and `Copy`, which have bultin impls for pattern types.
+
+#![feature(pattern_types)]
+#![feature(pattern_type_macro)]
+
+use std::pat::pattern_type;
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+#[repr(transparent)]
+struct Nanoseconds(NanoI32);
+//~^ ERROR: the trait bound `(i32) is 0..=999999999: Eq` is not satisfied
+//~| ERROR: `(i32) is 0..=999999999` doesn't implement `Debug`
+//~| ERROR: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied
+//~| ERROR: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied
+//~| ERROR: the trait bound `(i32) is 0..=999999999: Default` is not satisfied
+//~| ERROR: can't compare `(i32) is 0..=999999999` with `_`
+//~| ERROR: `==` cannot be applied
+
+type NanoI32 = crate::pattern_type!(i32 is 0..=999_999_999);
+
+fn main() {
+    let x = Nanoseconds(unsafe { std::mem::transmute(42) });
+    let y = x.clone();
+    if y == x {}
+}
diff --git a/tests/ui/type/pattern_types/derives_fail.stderr b/tests/ui/type/pattern_types/derives_fail.stderr
new file mode 100644
index 00000000000..78bef726341
--- /dev/null
+++ b/tests/ui/type/pattern_types/derives_fail.stderr
@@ -0,0 +1,74 @@
+error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999`
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                       --------- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^
+
+error[E0277]: the trait bound `(i32) is 0..=999999999: Eq` is not satisfied
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                  -- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ the trait `Eq` is not implemented for `(i32) is 0..=999999999`
+   |
+note: required by a bound in `AssertParamIsEq`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
+
+error[E0277]: `(i32) is 0..=999999999` doesn't implement `Debug`
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                      ----- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ `(i32) is 0..=999999999` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |
+   = help: the trait `Debug` is not implemented for `(i32) is 0..=999999999`
+
+error[E0277]: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                             --- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ the trait `Ord` is not implemented for `(i32) is 0..=999999999`
+
+error[E0277]: can't compare `(i32) is 0..=999999999` with `_`
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                                  ---------- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ no implementation for `(i32) is 0..=999999999 < _` and `(i32) is 0..=999999999 > _`
+   |
+   = help: the trait `PartialOrd<_>` is not implemented for `(i32) is 0..=999999999`
+
+error[E0277]: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                                              ---- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ the trait `Hash` is not implemented for `(i32) is 0..=999999999`
+
+error[E0277]: the trait bound `(i32) is 0..=999999999: Default` is not satisfied
+  --> $DIR/derives_fail.rs:11:20
+   |
+LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)]
+   |                                                                    ------- in this derive macro expansion
+LL | #[repr(transparent)]
+LL | struct Nanoseconds(NanoI32);
+   |                    ^^^^^^^ the trait `Default` is not implemented for `(i32) is 0..=999999999`
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0277, E0369.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/type/pattern_types/matching.rs b/tests/ui/type/pattern_types/matching.rs
new file mode 100644
index 00000000000..b8463a8e822
--- /dev/null
+++ b/tests/ui/type/pattern_types/matching.rs
@@ -0,0 +1,26 @@
+#![feature(pattern_types, pattern_type_macro, structural_match)]
+
+//@ check-pass
+
+use std::marker::StructuralPartialEq;
+use std::pat::pattern_type;
+
+struct Thing(pattern_type!(u32 is 1..));
+
+impl StructuralPartialEq for Thing {}
+impl PartialEq for Thing {
+    fn eq(&self, other: &Thing) -> bool {
+        unsafe { std::mem::transmute::<_, u32>(self.0) == std::mem::transmute::<_, u32>(other.0) }
+    }
+}
+
+impl Eq for Thing {}
+
+const TWO: Thing = Thing(2);
+
+const _: () = match TWO {
+    TWO => {}
+    _ => unreachable!(),
+};
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/matching_fail.rs b/tests/ui/type/pattern_types/matching_fail.rs
new file mode 100644
index 00000000000..8e2c741e3e0
--- /dev/null
+++ b/tests/ui/type/pattern_types/matching_fail.rs
@@ -0,0 +1,25 @@
+#![feature(pattern_types, pattern_type_macro, structural_match)]
+
+use std::pat::pattern_type;
+
+const THREE: pattern_type!(u32 is 1..) = 3;
+
+const _: () = match THREE {
+    THREE => {}
+    //~^ ERROR non-structural type
+    _ => unreachable!(),
+};
+
+const _: () = match THREE {
+    3 => {}
+    //~^ ERROR mismatched types
+    _ => unreachable!(),
+};
+
+const _: () = match 3 {
+    THREE => {}
+    //~^ ERROR mismatched types
+    _ => unreachable!(),
+};
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/matching_fail.stderr b/tests/ui/type/pattern_types/matching_fail.stderr
new file mode 100644
index 00000000000..446180d80f2
--- /dev/null
+++ b/tests/ui/type/pattern_types/matching_fail.stderr
@@ -0,0 +1,43 @@
+error: constant of non-structural type `(u32) is 1..` in a pattern
+  --> $DIR/matching_fail.rs:8:5
+   |
+LL | const THREE: pattern_type!(u32 is 1..) = 3;
+   | -------------------------------------- constant defined here
+...
+LL |     THREE => {}
+   |     ^^^^^ constant of non-structural type
+   |
+   = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
+
+error[E0308]: mismatched types
+  --> $DIR/matching_fail.rs:14:5
+   |
+LL | const _: () = match THREE {
+   |                     ----- this expression has type `(u32) is 1..`
+LL |     3 => {}
+   |     ^ expected `(u32) is 1..`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/matching_fail.rs:20:5
+   |
+LL | const THREE: pattern_type!(u32 is 1..) = 3;
+   | -------------------------------------- constant defined here
+...
+LL | const _: () = match 3 {
+   |                     - this expression has type `{integer}`
+LL |     THREE => {}
+   |     ^^^^^
+   |     |
+   |     expected integer, found `(u32) is 1..`
+   |     `THREE` is interpreted as a constant, not a new binding
+   |     help: introduce a new binding instead: `other_three`
+   |
+   = note:      expected type `{integer}`
+           found pattern type `(u32) is 1..`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.