Auto merge of #78779 - LeSeulArtichaut:ty-visitor-return, r=oli-obk
Introduce `TypeVisitor::BreakTy` Implements MCP rust-lang/compiler-team#383. r? `@ghost` cc `@lcnr` `@oli-obk` ~~Blocked on FCP in rust-lang/compiler-team#383.~~
This commit is contained in:
commit
e0ef0fc392
32 changed files with 232 additions and 213 deletions
|
@ -692,12 +692,15 @@ impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<OP>
|
|||
where
|
||||
OP: FnMut(ty::Region<'tcx>),
|
||||
{
|
||||
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ControlFlow<()> {
|
||||
fn visit_binder<T: TypeFoldable<'tcx>>(
|
||||
&mut self,
|
||||
t: &ty::Binder<T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
t.as_ref().skip_binder().visit_with(self);
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> {
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match *r {
|
||||
// ignore bound regions, keep visiting
|
||||
ty::ReLateBound(_, _) => ControlFlow::CONTINUE,
|
||||
|
@ -708,7 +711,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> {
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// We're only interested in types involving regions
|
||||
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
|
||||
return ControlFlow::CONTINUE;
|
||||
|
|
|
@ -78,7 +78,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
|||
Concrete,
|
||||
}
|
||||
let mut failure_kind = FailureKind::Concrete;
|
||||
walk_abstract_const(tcx, ct, |node| match node {
|
||||
walk_abstract_const::<!, _>(tcx, ct, |node| match node {
|
||||
Node::Leaf(leaf) => {
|
||||
let leaf = leaf.subst(tcx, ct.substs);
|
||||
if leaf.has_infer_types_or_consts() {
|
||||
|
@ -574,19 +574,19 @@ pub(super) fn try_unify_abstract_consts<'tcx>(
|
|||
// on `ErrorReported`.
|
||||
}
|
||||
|
||||
pub fn walk_abstract_const<'tcx, F>(
|
||||
pub fn walk_abstract_const<'tcx, R, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: AbstractConst<'tcx>,
|
||||
mut f: F,
|
||||
) -> ControlFlow<()>
|
||||
) -> ControlFlow<R>
|
||||
where
|
||||
F: FnMut(Node<'tcx>) -> ControlFlow<()>,
|
||||
F: FnMut(Node<'tcx>) -> ControlFlow<R>,
|
||||
{
|
||||
fn recurse<'tcx>(
|
||||
fn recurse<'tcx, R>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: AbstractConst<'tcx>,
|
||||
f: &mut dyn FnMut(Node<'tcx>) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
f: &mut dyn FnMut(Node<'tcx>) -> ControlFlow<R>,
|
||||
) -> ControlFlow<R> {
|
||||
let root = ct.root();
|
||||
f(root)?;
|
||||
match root {
|
||||
|
|
|
@ -771,7 +771,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
|
|||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> {
|
||||
type BreakTy = ();
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match t.kind() {
|
||||
ty::Param(_) => {
|
||||
if t == self.tcx.types.self_param {
|
||||
|
@ -812,7 +814,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: &ty::Const<'tcx>) -> ControlFlow<()> {
|
||||
fn visit_const(&mut self, ct: &ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// First check if the type of this constant references `Self`.
|
||||
self.visit_ty(ct.ty)?;
|
||||
|
||||
|
@ -844,7 +846,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow<()> {
|
||||
fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::PredicateAtom::ConstEvaluatable(def, substs) = pred.skip_binders() {
|
||||
// FIXME(const_evaluatable_checked): We should probably deduplicate the logic for
|
||||
// `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to
|
||||
|
|
|
@ -55,9 +55,7 @@ pub fn search_for_structural_match_violation<'tcx>(
|
|||
) -> Option<NonStructuralMatchTy<'tcx>> {
|
||||
// FIXME: we should instead pass in an `infcx` from the outside.
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let mut search = Search { infcx, span, found: None, seen: FxHashSet::default() };
|
||||
ty.visit_with(&mut search);
|
||||
search.found
|
||||
ty.visit_with(&mut Search { infcx, span, seen: FxHashSet::default() }).break_value()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -116,9 +114,6 @@ struct Search<'a, 'tcx> {
|
|||
|
||||
infcx: InferCtxt<'a, 'tcx>,
|
||||
|
||||
/// Records first ADT that does not implement a structural-match trait.
|
||||
found: Option<NonStructuralMatchTy<'tcx>>,
|
||||
|
||||
/// Tracks ADTs previously encountered during search, so that
|
||||
/// we will not recur on them again.
|
||||
seen: FxHashSet<hir::def_id::DefId>,
|
||||
|
@ -135,38 +130,33 @@ impl Search<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> {
|
||||
type BreakTy = NonStructuralMatchTy<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
debug!("Search visiting ty: {:?}", ty);
|
||||
|
||||
let (adt_def, substs) = match *ty.kind() {
|
||||
ty::Adt(adt_def, substs) => (adt_def, substs),
|
||||
ty::Param(_) => {
|
||||
self.found = Some(NonStructuralMatchTy::Param);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Param);
|
||||
}
|
||||
ty::Dynamic(..) => {
|
||||
self.found = Some(NonStructuralMatchTy::Dynamic);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Dynamic);
|
||||
}
|
||||
ty::Foreign(_) => {
|
||||
self.found = Some(NonStructuralMatchTy::Foreign);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Foreign);
|
||||
}
|
||||
ty::Opaque(..) => {
|
||||
self.found = Some(NonStructuralMatchTy::Opaque);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Opaque);
|
||||
}
|
||||
ty::Projection(..) => {
|
||||
self.found = Some(NonStructuralMatchTy::Projection);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Projection);
|
||||
}
|
||||
ty::Generator(..) | ty::GeneratorWitness(..) => {
|
||||
self.found = Some(NonStructuralMatchTy::Generator);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Generator);
|
||||
}
|
||||
ty::Closure(..) => {
|
||||
self.found = Some(NonStructuralMatchTy::Closure);
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Closure);
|
||||
}
|
||||
ty::RawPtr(..) => {
|
||||
// structural-match ignores substructure of
|
||||
|
@ -206,8 +196,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
|
||||
ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
|
||||
// First check all contained types and then tell the caller to continue searching.
|
||||
ty.super_visit_with(self);
|
||||
return ControlFlow::CONTINUE;
|
||||
return ty.super_visit_with(self);
|
||||
}
|
||||
ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
|
||||
bug!("unexpected type during structural-match checking: {:?}", ty);
|
||||
|
@ -227,8 +216,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
|
||||
if !self.type_marked_structural(ty) {
|
||||
debug!("Search found ty: {:?}", ty);
|
||||
self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
|
||||
return ControlFlow::BREAK;
|
||||
return ControlFlow::Break(NonStructuralMatchTy::Adt(&adt_def));
|
||||
}
|
||||
|
||||
// structural-match does not care about the
|
||||
|
@ -244,20 +232,11 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
// even though we skip super_visit_with, we must recur on
|
||||
// fields of ADT.
|
||||
let tcx = self.tcx();
|
||||
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
|
||||
adt_def.all_fields().map(|field| field.ty(tcx, substs)).try_for_each(|field_ty| {
|
||||
let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
|
||||
debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
|
||||
|
||||
if ty.visit_with(self).is_break() {
|
||||
// found an ADT without structural-match; halt visiting!
|
||||
assert!(self.found.is_some());
|
||||
return ControlFlow::BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
// Even though we do not want to recur on substs, we do
|
||||
// want our caller to continue its own search.
|
||||
ControlFlow::CONTINUE
|
||||
ty.visit_with(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue