Skip to content

Commit 84c3fcd

Browse files
committed
rewrite the orphan check to use a type visitor
1 parent 7d0a182 commit 84c3fcd

File tree

1 file changed

+124
-169
lines changed

1 file changed

+124
-169
lines changed

compiler/rustc_trait_selection/src/traits/coherence.rs

+124-169
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
2222
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
2323
use rustc_middle::ty::subst::Subst;
2424
use rustc_middle::ty::visit::TypeVisitable;
25-
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
25+
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor};
2626
use rustc_span::symbol::sym;
2727
use rustc_span::DUMMY_SP;
2828
use std::fmt::Debug;
2929
use std::iter;
30+
use std::ops::ControlFlow;
3031

3132
/// Whether we do the orphan check relative to this crate or
3233
/// to some remote crate.
@@ -578,192 +579,146 @@ fn orphan_check_trait_ref<'tcx>(
578579
);
579580
}
580581

581-
// Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only
582-
// if at least one of the following is true:
583-
//
584-
// - Trait is a local trait
585-
// (already checked in orphan_check prior to calling this function)
586-
// - All of
587-
// - At least one of the types T0..=Tn must be a local type.
588-
// Let Ti be the first such type.
589-
// - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)
590-
//
591-
fn uncover_fundamental_ty<'tcx>(
592-
tcx: TyCtxt<'tcx>,
593-
ty: Ty<'tcx>,
594-
in_crate: InCrate,
595-
) -> Vec<Ty<'tcx>> {
596-
// FIXME: this is currently somewhat overly complicated,
597-
// but fixing this requires a more complicated refactor.
598-
if !contained_non_local_types(tcx, ty, in_crate).is_empty() {
599-
if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) {
600-
return inner_tys
601-
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
602-
.collect();
582+
let mut checker = OrphanChecker::new(tcx, in_crate);
583+
match trait_ref.visit_with(&mut checker) {
584+
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
585+
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
586+
// Does there exist some local type after the `ParamTy`.
587+
checker.search_first_local_ty = true;
588+
if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
589+
trait_ref.visit_with(&mut checker).break_value()
590+
{
591+
Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty)))
592+
} else {
593+
Err(OrphanCheckErr::UncoveredTy(ty, None))
603594
}
604595
}
605-
606-
vec![ty]
607-
}
608-
609-
let mut non_local_spans = vec![];
610-
for (i, input_ty) in trait_ref
611-
.substs
612-
.types()
613-
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
614-
.enumerate()
615-
{
616-
debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty);
617-
let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate);
618-
if non_local_tys.is_empty() {
619-
debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
620-
return Ok(());
621-
} else if let ty::Param(_) = input_ty.kind() {
622-
debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty);
623-
let local_type = trait_ref
624-
.substs
625-
.types()
626-
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
627-
.find(|&ty| ty_is_local_constructor(tcx, ty, in_crate));
628-
629-
debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
630-
631-
return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type));
632-
}
633-
634-
non_local_spans.extend(non_local_tys.into_iter().map(|input_ty| (input_ty, i == 0)));
596+
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
635597
}
636-
// If we exit above loop, never found a local type.
637-
debug!("orphan_check_trait_ref: no local type");
638-
Err(OrphanCheckErr::NonLocalInputType(non_local_spans))
639598
}
640599

641-
/// Returns a list of relevant non-local types for `ty`.
642-
///
643-
/// This is just `ty` itself unless `ty` is `#[fundamental]`,
644-
/// in which case we recursively look into this type.
645-
///
646-
/// If `ty` is local itself, this method returns an empty `Vec`.
647-
///
648-
/// # Examples
649-
///
650-
/// - `u32` is not local, so this returns `[u32]`.
651-
/// - for `Foo<u32>`, where `Foo` is a local type, this returns `[]`.
652-
/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`.
653-
/// - `Box<Foo<u32>>` returns `[]`, as `Box` is a fundamental type and `Foo` is local.
654-
fn contained_non_local_types<'tcx>(
600+
struct OrphanChecker<'tcx> {
655601
tcx: TyCtxt<'tcx>,
656-
ty: Ty<'tcx>,
657602
in_crate: InCrate,
658-
) -> Vec<Ty<'tcx>> {
659-
if ty_is_local_constructor(tcx, ty, in_crate) {
660-
Vec::new()
661-
} else {
662-
match fundamental_ty_inner_tys(tcx, ty) {
663-
Some(inner_tys) => {
664-
inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect()
665-
}
666-
None => vec![ty],
603+
in_self_ty: bool,
604+
/// Ignore orphan check failures and exclusively search for the first
605+
/// local type.
606+
search_first_local_ty: bool,
607+
non_local_tys: Vec<(Ty<'tcx>, bool)>,
608+
}
609+
610+
impl<'tcx> OrphanChecker<'tcx> {
611+
fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self {
612+
OrphanChecker {
613+
tcx,
614+
in_crate,
615+
in_self_ty: true,
616+
search_first_local_ty: false,
617+
non_local_tys: Vec::new(),
667618
}
668619
}
669-
}
670620

671-
/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the
672-
/// type parameters of the ADT, or `T`, respectively. For non-fundamental
673-
/// types, returns `None`.
674-
fn fundamental_ty_inner_tys<'tcx>(
675-
tcx: TyCtxt<'tcx>,
676-
ty: Ty<'tcx>,
677-
) -> Option<impl Iterator<Item = Ty<'tcx>>> {
678-
let (first_ty, rest_tys) = match *ty.kind() {
679-
ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()),
680-
ty::Adt(def, substs) if def.is_fundamental() => {
681-
let mut types = substs.types();
682-
683-
// FIXME(eddyb) actually validate `#[fundamental]` up-front.
684-
match types.next() {
685-
None => {
686-
tcx.sess.span_err(
687-
tcx.def_span(def.did()),
688-
"`#[fundamental]` requires at least one type parameter",
689-
);
690-
691-
return None;
692-
}
621+
fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
622+
self.non_local_tys.push((t, self.in_self_ty));
623+
ControlFlow::CONTINUE
624+
}
693625

694-
Some(first_ty) => (first_ty, types),
695-
}
626+
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
627+
if self.search_first_local_ty {
628+
ControlFlow::CONTINUE
629+
} else {
630+
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
696631
}
697-
_ => return None,
698-
};
632+
}
633+
634+
fn def_id_is_local(&mut self, def_id: DefId) -> bool {
635+
match self.in_crate {
636+
InCrate::Local => def_id.is_local(),
637+
InCrate::Remote => false,
638+
}
639+
}
640+
}
699641

700-
Some(iter::once(first_ty).chain(rest_tys))
642+
enum OrphanCheckEarlyExit<'tcx> {
643+
ParamTy(Ty<'tcx>),
644+
LocalTy(Ty<'tcx>),
701645
}
702646

703-
fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
704-
match in_crate {
705-
// The type is local to *this* crate - it will not be
706-
// local in any other crate.
707-
InCrate::Remote => false,
708-
InCrate::Local => def_id.is_local(),
647+
impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> {
648+
type BreakTy = OrphanCheckEarlyExit<'tcx>;
649+
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
650+
ControlFlow::CONTINUE
709651
}
710-
}
711652

712-
fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
713-
debug!("ty_is_local_constructor({:?})", ty);
714-
715-
match *ty.kind() {
716-
ty::Bool
717-
| ty::Char
718-
| ty::Int(..)
719-
| ty::Uint(..)
720-
| ty::Float(..)
721-
| ty::Str
722-
| ty::FnDef(..)
723-
| ty::FnPtr(_)
724-
| ty::Array(..)
725-
| ty::Slice(..)
726-
| ty::RawPtr(..)
727-
| ty::Ref(..)
728-
| ty::Never
729-
| ty::Tuple(..)
730-
| ty::Param(..)
731-
| ty::Projection(..) => false,
732-
733-
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate {
734-
InCrate::Local => false,
735-
// The inference variable might be unified with a local
736-
// type in that remote crate.
737-
InCrate::Remote => true,
738-
},
739-
740-
ty::Adt(def, _) => def_id_is_local(def.did(), in_crate),
741-
ty::Foreign(did) => def_id_is_local(did, in_crate),
742-
743-
ty::Dynamic(ref tt, ..) => {
744-
if let Some(principal) = tt.principal() {
745-
def_id_is_local(principal.def_id(), in_crate)
746-
} else {
747-
false
653+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
654+
let result = match *ty.kind() {
655+
ty::Bool
656+
| ty::Char
657+
| ty::Int(..)
658+
| ty::Uint(..)
659+
| ty::Float(..)
660+
| ty::Str
661+
| ty::FnDef(..)
662+
| ty::FnPtr(_)
663+
| ty::Array(..)
664+
| ty::Slice(..)
665+
| ty::RawPtr(..)
666+
| ty::Never
667+
| ty::Tuple(..)
668+
| ty::Projection(..) => self.found_non_local_ty(ty),
669+
670+
ty::Param(..) => self.found_param_ty(ty),
671+
672+
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
673+
InCrate::Local => self.found_non_local_ty(ty),
674+
// The inference variable might be unified with a local
675+
// type in that remote crate.
676+
InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
677+
},
678+
679+
// For fundamental types, we just look inside of them.
680+
ty::Ref(_, ty, _) => ty.visit_with(self),
681+
ty::Adt(def, substs) => {
682+
if self.def_id_is_local(def.did()) {
683+
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
684+
} else if def.is_fundamental() {
685+
substs.visit_with(self)
686+
} else {
687+
self.found_non_local_ty(ty)
688+
}
748689
}
749-
}
690+
ty::Foreign(def_id) => {
691+
if self.def_id_is_local(def_id) {
692+
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
693+
} else {
694+
self.found_non_local_ty(ty)
695+
}
696+
}
697+
ty::Dynamic(tt, ..) => {
698+
let principal = tt.principal().map(|p| p.def_id());
699+
if principal.map_or(false, |p| self.def_id_is_local(p)) {
700+
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
701+
} else {
702+
self.found_non_local_ty(ty)
703+
}
704+
}
705+
ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
706+
ty::Opaque(..) | ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
707+
self.tcx.sess.delay_span_bug(
708+
DUMMY_SP,
709+
format!("ty_is_local invoked on closure or generator: {:?}", ty),
710+
);
711+
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
712+
}
713+
};
714+
// A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
715+
// the first type we visit is always the self type.
716+
self.in_self_ty = false;
717+
result
718+
}
750719

751-
ty::Error(_) => true,
752-
753-
// These variants should never appear during coherence checking because they
754-
// cannot be named directly.
755-
//
756-
// They could be indirectly used through an opaque type. While using opaque types
757-
// in impls causes an error, this path can still be hit afterwards.
758-
//
759-
// See `test/ui/coherence/coherence-with-closure.rs` for an example where this
760-
// could happens.
761-
ty::Opaque(..) | ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
762-
tcx.sess.delay_span_bug(
763-
DUMMY_SP,
764-
format!("ty_is_local invoked on closure or generator: {:?}", ty),
765-
);
766-
true
767-
}
720+
// FIXME: Constants should participate in orphan checking.
721+
fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
722+
ControlFlow::CONTINUE
768723
}
769724
}

0 commit comments

Comments
 (0)