Check that RPITs constrained by a recursive call in a closure are compatible
This commit is contained in:
parent
90939e6bce
commit
5e8f1e80d8
5 changed files with 139 additions and 50 deletions
|
@ -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<T>() -> Foo<T, u32> { .. }
|
||||
/// ```
|
||||
///
|
||||
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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue