From 5e8f1e80d8bff9995bbc34a959b8aa75e2a5cc00 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 9 Jul 2022 07:11:03 +0000 Subject: [PATCH] Check that RPITs constrained by a recursive call in a closure are compatible --- compiler/rustc_typeck/src/collect/type_of.rs | 148 +++++++++++++++---- src/test/ui/impl-trait/issue-99073-2.rs | 2 +- src/test/ui/impl-trait/issue-99073-2.stderr | 15 +- src/test/ui/impl-trait/issue-99073.rs | 6 +- src/test/ui/impl-trait/issue-99073.stderr | 18 +-- 5 files changed, 139 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 1d7406e00ad0a..64ac655e0c393 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -335,37 +335,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.mk_adt(def, substs) } ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { - find_opaque_ty_constraints(tcx, def_id) + find_opaque_ty_constraints_for_tait(tcx, def_id) } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { - let concrete_ty = tcx - .mir_borrowck(owner) - .concrete_opaque_types - .get(&def_id) - .copied() - .map(|concrete| concrete.ty) - .unwrap_or_else(|| { - let table = tcx.typeck(owner); - if let Some(_) = table.tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.ty_error() - } else { - table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }).expect("RPIT always have a hidden type from typeck") - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - concrete_ty + find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) @@ -519,7 +493,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { /// fn b() -> Foo { .. } /// ``` /// -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { +fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { use rustc_hir::{Expr, ImplItem, Item, TraitItem}; struct ConstraintLocator<'tcx> { @@ -660,6 +634,122 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { } } +fn find_opaque_ty_constraints_for_rpit( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + owner_def_id: LocalDefId, +) -> Ty<'_> { + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; + + struct ConstraintChecker<'tcx> { + tcx: TyCtxt<'tcx>, + + /// def_id of the opaque type whose defining uses are being checked + def_id: LocalDefId, + + found: ty::OpaqueHiddenType<'tcx>, + } + + impl ConstraintChecker<'_> { + #[instrument(skip(self), level = "debug")] + fn check(&self, def_id: LocalDefId) { + // Use borrowck to get the type with unerased regions. + let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + debug!(?concrete_opaque_types); + for &(def_id, concrete_type) in concrete_opaque_types { + if def_id != self.def_id { + // Ignore constraints for other opaque types. + continue; + } + + debug!(?concrete_type, "found constraint"); + + if concrete_type.ty != self.found.ty + && !(concrete_type, self.found).references_error() + { + self.found.report_mismatch(&concrete_type, self.tcx); + } + } + } + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure { .. } = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + trace!(?it.def_id); + self.check(it.def_id); + intravisit::walk_trait_item(self, it); + } + } + + let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); + + if let Some(concrete) = concrete { + let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); + debug!(?scope); + let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete }; + + match tcx.hir().get(scope) { + Node::Item(it) => intravisit::walk_item(&mut locator, it), + Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), + Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { + let table = tcx.typeck(owner_def_id); + if let Some(_) = table.tainted_by_errors { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.ty_error() + } else { + table + .concrete_opaque_types + .get(&def_id) + .copied() + .unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + Some(tcx.mk_diverging_default()) + }) + .expect("RPIT always have a hidden type from typeck") + } + }) +} + fn infer_placeholder_type<'a>( tcx: TyCtxt<'a>, def_id: LocalDefId, diff --git a/src/test/ui/impl-trait/issue-99073-2.rs b/src/test/ui/impl-trait/issue-99073-2.rs index bebd8286de9fe..14ac688806bda 100644 --- a/src/test/ui/impl-trait/issue-99073-2.rs +++ b/src/test/ui/impl-trait/issue-99073-2.rs @@ -7,7 +7,7 @@ fn main() { fn test(t: T, recurse: bool) -> impl Display { let f = || { let i: u32 = test::(-1, false); - //~^ ERROR mismatched types + //~^ ERROR concrete type differs from previous defining opaque type use println!("{i}"); }; if recurse { diff --git a/src/test/ui/impl-trait/issue-99073-2.stderr b/src/test/ui/impl-trait/issue-99073-2.stderr index c1e4b823c08e7..913bc8f5674ac 100644 --- a/src/test/ui/impl-trait/issue-99073-2.stderr +++ b/src/test/ui/impl-trait/issue-99073-2.stderr @@ -1,15 +1,14 @@ -error[E0308]: mismatched types +error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073-2.rs:9:22 | -LL | fn test(t: T, recurse: bool) -> impl Display { - | ------------ the expected opaque type -LL | let f = || { LL | let i: u32 = test::(-1, false); - | ^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^ expected `T`, got `u32` | - = note: expected opaque type `impl std::fmt::Display` - found type `u32` +note: previous use here + --> $DIR/issue-99073-2.rs:16:5 + | +LL | t + | ^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/impl-trait/issue-99073.rs b/src/test/ui/impl-trait/issue-99073.rs index 1d75f6086664f..7798e247df0a2 100644 --- a/src/test/ui/impl-trait/issue-99073.rs +++ b/src/test/ui/impl-trait/issue-99073.rs @@ -1,8 +1,8 @@ fn main() { - let _ = fix(|_: &dyn Fn()| {}); + let _ = fix(|_: &dyn Fn()| {}); } fn fix(f: F) -> impl Fn() { - move || f(fix(&f)) - //~^ ERROR mismatched types + move || f(fix(&f)) + //~^ ERROR concrete type differs from previous defining opaque type use } diff --git a/src/test/ui/impl-trait/issue-99073.stderr b/src/test/ui/impl-trait/issue-99073.stderr index b35d58093d5fc..5463679534968 100644 --- a/src/test/ui/impl-trait/issue-99073.stderr +++ b/src/test/ui/impl-trait/issue-99073.stderr @@ -1,14 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/issue-99073.rs:6:13 +error: concrete type differs from previous defining opaque type use + --> $DIR/issue-99073.rs:6:11 | -LL | fn fix(f: F) -> impl Fn() { - | --------- the expected opaque type -LL | move || f(fix(&f)) - | ^^^^^^^^^^ types differ +LL | move || f(fix(&f)) + | ^^^^^^^^^^ expected `[closure@$DIR/issue-99073.rs:6:3: 6:10]`, got `G` | - = note: expected opaque type `impl Fn()` - found type parameter `G` +note: previous use here + --> $DIR/issue-99073.rs:6:3 + | +LL | move || f(fix(&f)) + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`.