Repository: rust-lang/rust
Pull request #140707: Structurally normalize in range pattern checking in HIR typeck
Author: login: compiler-errors
Labels: name: T-compiler
description: Relevant to the compiler team, which will review and decide on the PR/issue.
name: S-waiting-on-bors
description: Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Created: 2025-05-06T16:43:19Z | Merged: 2025-05-09T00:42:24Z

Fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/200

r? lcnr


Diff:
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index f9502153afdc5..17d48184dd971 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -650,14 +650,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match &pat.kind {
             // Type checking these product-like types successfully always require
             // that the expected type be of those types and not reference types.
-            PatKind::Tuple(..)
-            | PatKind::Range(..)
-            | PatKind::Slice(..) => AdjustMode::peel_all(),
+            PatKind::Tuple(..) | PatKind::Range(..) | PatKind::Slice(..) => AdjustMode::peel_all(),
             // When checking an explicit deref pattern, only peel reference types.
             // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
             // patterns may want `PeelKind::Implicit`, stopping on encountering a box.
-            | PatKind::Box(_)
-            | PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
+            PatKind::Box(_) | PatKind::Deref(_) => {
+                AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat }
+            }
             // A never pattern behaves somewhat like a literal or unit variant.
             PatKind::Never => AdjustMode::peel_all(),
             // For patterns with paths, how we peel the scrutinee depends on the path's resolution.
@@ -679,7 +678,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && self.tcx.features().deref_patterns()
                     && !matches!(lt.kind, PatExprKind::Lit { .. })
                 {
-                    span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
+                    span_bug!(
+                        lt.span,
+                        "FIXME(deref_patterns): adjust mode unimplemented for {:?}",
+                        lt.kind
+                    );
                 }
                 // Call `resolve_vars_if_possible` here for inline const blocks.
                 let lit_ty = self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt));
@@ -687,17 +690,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if self.tcx.features().deref_patterns() {
                     let mut peeled_ty = lit_ty;
                     let mut pat_ref_layers = 0;
-                    while let ty::Ref(_, inner_ty, mutbl) = *peeled_ty.kind() {
+                    while let ty::Ref(_, inner_ty, mutbl) =
+                        *self.try_structurally_resolve_type(pat.span, peeled_ty).kind()
+                    {
                         // We rely on references at the head of constants being immutable.
                         debug_assert!(mutbl.is_not());
                         pat_ref_layers += 1;
                         peeled_ty = inner_ty;
                     }
-                    AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: None, pat_ref_layers } }
+                    AdjustMode::Peel {
+                        kind: PeelKind::Implicit { until_adt: None, pat_ref_layers },
+                    }
                 } else {
                     if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() }
                 }
-            },
+            }
 
             // Ref patterns are complicated, we handle them in `check_pat_ref`.
             PatKind::Ref(..)
@@ -928,6 +935,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // be peeled to `str` while ty here is still `&str`, if we don't
                 // err early here, a rather confusing unification error will be
                 // emitted instead).
+                let ty = self.try_structurally_resolve_type(expr.span, ty);
                 let fail =
                     !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error());
                 Some((fail, ty, expr.span))
diff --git a/tests/ui/pattern/normalize-ty-in-range.rs b/tests/ui/pattern/normalize-ty-in-range.rs
new file mode 100644
index 0000000000000..f0d22362608e1
--- /dev/null
+++ b/tests/ui/pattern/normalize-ty-in-range.rs
@@ -0,0 +1,24 @@
+//@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/200>.
+// Make sure we structurally normalize in range pattern checking in HIR typeck.
+
+trait Foo {
+    type Bar;
+}
+
+impl Foo for () {
+    type Bar = i32;
+}
+
+fn main() {
+    const X: <() as Foo>::Bar = 0;
+
+    match 0 {
+        X..=X => {}
+        _ => {}
+    }
+}


Reviews:
user: login: lcnr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T17:37:14Z
user: login: compiler-errors
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T17:37:59Z

Review comments:
diff_hunk: @@ -866,6 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // be peeled to `str` while ty here is still `&str`, if we don't
                 // err early here, a rather confusing unification error will be
                 // emitted instead).
+                let ty = self.structurally_resolve_type(expr.span, ty);
path: compiler/rustc_hir_typeck/src/pat.rs
user: login: lcnr
body: do we need to do the same in `fn calc_adjust_mode` given that this also structurally matches on `check_pat_expr_unadjusted`?
created_at: 2025-05-06T17:37:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -866,6 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // be peeled to `str` while ty here is still `&str`, if we don't
                 // err early here, a rather confusing unification error will be
                 // emitted instead).
+                let ty = self.structurally_resolve_type(expr.span, ty);
path: compiler/rustc_hir_typeck/src/pat.rs
user: login: compiler-errors
body: possibly
created_at: 2025-05-06T17:37:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: rust-log-analyzer
created_at: 2025-05-06T17:07:45Z
body: 
The job **`x86_64-gnu-llvm-19`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14865101332/job/41739890843) [(plain)](https://github.com/rust-lang/rust/commit/d7eddef6ca085f14170da3bfa25fdcad3e5458dd/checks/41739890843/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
#18 exporting to docker image format
#18 sending tarball 20.3s done
#18 DONE 33.9s
##[endgroup]
Setting extra environment values for docker:  --env ENABLE_GCC_CODEGEN=1 --env GCC_EXEC_PREFIX=/usr/lib/gcc/
[CI_JOB_NAME=x86_64-gnu-llvm-19]
[CI_JOB_NAME=x86_64-gnu-llvm-19]
debug: `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` configured.
---
sccache: Listening on address 127.0.0.1:4226
##[group]Configure the build
configure: processing command line
configure: 
configure: build.configure-args := ['--build=x86_64-unknown-linux-gnu', '--llvm-root=/usr/lib/llvm-19', '--enable-llvm-link-shared', '--set', 'rust.randomize-layout=true', '--set', 'rust.thin-lto-import-instr-limit=10', '--set', 'build.print-step-timings', '--enable-verbose-tests', '--set', 'build.metrics', '--enable-verbose-configure', '--enable-sccache', '--disable-manage-submodules', '--enable-locked-deps', '--enable-cargo-native-static', '--set', 'rust.codegen-units-std=1', '--set', 'dist.compression-profile=balanced', '--dist-compression-formats=xz', '--set', 'rust.lld=false', '--disable-dist-src', '--release-channel=nightly', '--enable-debug-assertions', '--enable-overflow-checks', '--enable-llvm-assertions', '--set', 'rust.verify-llvm-ir', '--set', 'rust.codegen-backends=llvm,cranelift,gcc', '--set', 'llvm.static-libstdcpp', '--set', 'gcc.download-ci-gcc=true', '--enable-new-symbol-mangling']
configure: build.build          := x86_64-unknown-linux-gnu
configure: target.x86_64-unknown-linux-gnu.llvm-config := /usr/lib/llvm-19/bin/llvm-config
configure: llvm.link-shared     := True
configure: rust.randomize-layout := True
configure: rust.thin-lto-import-instr-limit := 10
---
---- [ui] tests/ui/pattern/issue-88074-pat-range-type-inference.rs stdout ----

error: test compilation failed although it shouldn't!
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/pattern/issue-88074-pat-range-type-inference.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=x86_64-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/pattern/issue-88074-pat-range-type-inference" "-A" "unused" "-A" "internal_features" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers"
stdout: none
--- stderr -------------------------------
error[E0282]: type annotations needed
##[error]  --> /checkout/tests/ui/pattern/issue-88074-pat-range-type-inference.rs:13:9
   |
LL |         Zero::ZERO ..= 1 => {},
   |         ^^^^^^^^^^ cannot infer type

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.
---
- error[E0029]: only `char` and numeric types are allowed in range patterns
+ error[E0282]: type annotations needed
2   --> $DIR/issue-88074-pat-range-type-inference-err.rs:11:9
3    |
4 LL |         Zero::ZERO ..= Zero::ZERO => {},

-    |         ----------^^^^^----------
-    |         |              |
-    |         |              this is of type `String` but it should be `char` or numeric
-    |         this is of type `String` but it should be `char` or numeric
+    |         ^^^^^^^^^^ cannot infer type
9 
10 error[E0282]: type annotations needed
11   --> $DIR/issue-88074-pat-range-type-inference-err.rs:19:9

12    |
13 LL |         Zero::ZERO ..= Zero::ZERO => {},
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
+    |         ^^^^^^^^^^ cannot infer type
15 
16 error: aborting due to 2 previous errors
17 
---
To only update this specific test, also pass `--test-args pattern/issue-88074-pat-range-type-inference-err.rs`

error: 1 errors occurred comparing output.
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=x86_64-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/pattern/issue-88074-pat-range-type-inference-err" "-A" "unused" "-A" "internal_features" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers"
stdout: none
--- stderr -------------------------------
error[E0282]: type annotations needed
##[error]  --> /checkout/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs:11:9
   |
LL |         Zero::ZERO ..= Zero::ZERO => {},
   |         ^^^^^^^^^^ cannot infer type

error[E0282]: type annotations needed
##[error]  --> /checkout/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs:19:9
   |
LL |         Zero::ZERO ..= Zero::ZERO => {},
   |         ^^^^^^^^^^ cannot infer type

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0282`.
```

</details>

author_association: COLLABORATOR
user: login: lcnr
created_at: 2025-05-08T15:02:57Z
body: @bors r+ rollup
author_association: CONTRIBUTOR
user: login: bors
created_at: 2025-05-08T15:03:00Z
body: :pushpin: Commit 9af6ee50ed8630590ed02ca1c284d07b65d84d10 has been approved by `lcnr`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust) for this repository.

<!-- @bors r=lcnr 9af6ee50ed8630590ed02ca1c284d07b65d84d10 -->
<!-- homu: {"type":"Approved","sha":"9af6ee50ed8630590ed02ca1c284d07b65d84d10","approver":"lcnr","queue":"https://bors.rust-lang.org/homu/queue/rust"} -->
author_association: COLLABORATOR