Skip to content

Commit

Permalink
Rollup merge of #89441 - Nadrieril:fix-89393, r=tmandry
Browse files Browse the repository at this point in the history
Normalize after substituting via `field.ty()`

Back in #72476 I hadn't understood where the problem was coming from, and only worked around the issue. What happens is that calling `field.ty()` on a field of a generic struct substitutes the appropriate generics but doesn't normalize the resulting type.
As a consumer of types I'm surprised that one would substitute without normalizing, feels like a footgun, so I added a comment.

Fixes #89393.
  • Loading branch information
Manishearth authored Oct 1, 2021
2 parents b458ecf + 68b76a4 commit 5ab1245
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 27 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1640,8 +1640,8 @@ impl ReprOptions {
}

impl<'tcx> FieldDef {
/// Returns the type of this field. The `subst` is typically obtained
/// via the second field of `TyKind::AdtDef`.
/// Returns the type of this field. The resulting type is not normalized. The `subst` is
/// typically obtained via the second field of `TyKind::AdtDef`.
pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> {
tcx.type_of(self.did).subst(tcx, subst)
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {

variant.fields.iter().enumerate().filter_map(move |(i, field)| {
let ty = field.ty(cx.tcx, substs);
// `field.ty()` doesn't normalize after substituting.
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
let is_uninhabited = cx.is_uninhabited(ty);

Expand Down Expand Up @@ -1671,7 +1673,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
write!(f, "{}", hi)
}
IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0`
Wildcard | Missing { .. } | NonExhaustive => write!(f, "_"),
Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
Or => {
for pat in self.iter_fields() {
write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,8 +781,7 @@ fn is_useful<'p, 'tcx>(

assert!(rows.iter().all(|r| r.len() == v.len()));

// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
let ty = matrix.heads().next().map_or(v.head().ty(), |r| r.ty());
let ty = v.head().ty();
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// check-pass

// From https://github.com/rust-lang/rust/issues/72476
// and https://github.com/rust-lang/rust/issues/89393

trait Trait {
type Projection;
}

struct A;
impl Trait for A {
type Projection = bool;
}

struct B;
impl Trait for B {
type Projection = (u32, u32);
}

struct Next<T: Trait>(T::Projection);

fn foo1(item: Next<A>) {
match item {
Next(true) => {}
Next(false) => {}
}
}

fn foo2(x: <A as Trait>::Projection) {
match x {
true => {}
false => {}
}
}

fn foo3(x: Next<B>) {
let Next((_, _)) = x;
match x {
Next((_, _)) => {}
}
}

fn foo4(x: <B as Trait>::Projection) {
let (_, _) = x;
match x {
(_, _) => {}
}
}

fn foo5<T: Trait>(x: <T as Trait>::Projection) {
match x {
_ => {}
}
}

fn main() {}
22 changes: 0 additions & 22 deletions src/test/ui/pattern/usefulness/issue-72476-associated-type.rs

This file was deleted.

0 comments on commit 5ab1245

Please sign in to comment.