about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2020-03-20 05:28:17 +0200
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2020-04-06 21:55:43 +0300
commit0d4705b0097a132cbb721095997d91090e8cdb21 (patch)
tree766aee5abc5f59d8b01b70db73acc230b3439f16
parente53c42c0b304c809d5f20bfb849e77312735a399 (diff)
downloadrust-0d4705b0097a132cbb721095997d91090e8cdb21.tar.gz
rust-0d4705b0097a132cbb721095997d91090e8cdb21.zip
traits/coherence: stop using `Ty::walk_shallow`.
-rw-r--r--src/librustc_trait_selection/traits/coherence.rs69
-rw-r--r--src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs4
2 files changed, 52 insertions, 21 deletions
diff --git a/src/librustc_trait_selection/traits/coherence.rs b/src/librustc_trait_selection/traits/coherence.rs
index a642ae98231..4a71585ec1c 100644
--- a/src/librustc_trait_selection/traits/coherence.rs
+++ b/src/librustc_trait_selection/traits/coherence.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
+use std::iter;
 
 /// Whether we do the orphan check relative to this crate or
 /// to some remote crate.
@@ -378,11 +379,17 @@ fn orphan_check_trait_ref<'tcx>(
         ty: Ty<'tcx>,
         in_crate: InCrate,
     ) -> Vec<Ty<'tcx>> {
-        if fundamental_ty(ty) && ty_is_non_local(ty, in_crate).is_some() {
-            ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect()
-        } else {
-            vec![ty]
+        // FIXME(eddyb) figure out if this is redundant with `ty_is_non_local`,
+        // or maybe if this should be calling `ty_is_non_local_constructor`.
+        if ty_is_non_local(tcx, ty, in_crate).is_some() {
+            if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) {
+                return inner_tys
+                    .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
+                    .collect();
+            }
         }
+
+        vec![ty]
     }
 
     let mut non_local_spans = vec![];
@@ -390,7 +397,7 @@ fn orphan_check_trait_ref<'tcx>(
         trait_ref.input_types().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).enumerate()
     {
         debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty);
-        let non_local_tys = ty_is_non_local(input_ty, in_crate);
+        let non_local_tys = ty_is_non_local(tcx, input_ty, in_crate);
         if non_local_tys.is_none() {
             debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
             return Ok(());
@@ -416,30 +423,53 @@ fn orphan_check_trait_ref<'tcx>(
     Err(OrphanCheckErr::NonLocalInputType(non_local_spans))
 }
 
-fn ty_is_non_local<'t>(ty: Ty<'t>, in_crate: InCrate) -> Option<Vec<Ty<'t>>> {
+fn ty_is_non_local(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Option<Vec<Ty<'tcx>>> {
     match ty_is_non_local_constructor(ty, in_crate) {
         Some(ty) => {
-            if !fundamental_ty(ty) {
-                Some(vec![ty])
-            } else {
-                let tys: Vec<_> = ty
-                    .walk_shallow()
-                    .filter_map(|t| ty_is_non_local(t, in_crate))
-                    .flat_map(|i| i)
+            if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) {
+                let tys: Vec<_> = inner_tys
+                    .filter_map(|ty| ty_is_non_local(tcx, ty, in_crate))
+                    .flatten()
                     .collect();
                 if tys.is_empty() { None } else { Some(tys) }
+            } else {
+                Some(vec![ty])
             }
         }
         None => None,
     }
 }
 
-fn fundamental_ty(ty: Ty<'_>) -> bool {
-    match ty.kind {
-        ty::Ref(..) => true,
-        ty::Adt(def, _) => def.is_fundamental(),
-        _ => false,
-    }
+/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the
+/// type parameters of the ADT, or `T`, respectively. For non-fundamental
+/// types, returns `None`.
+fn fundamental_ty_inner_tys(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<impl Iterator<Item = Ty<'tcx>>> {
+    let (first_ty, rest_tys) = match ty.kind {
+        ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()),
+        ty::Adt(def, substs) if def.is_fundamental() => {
+            let mut types = substs.types();
+
+            // FIXME(eddyb) actually validate `#[fundamental]` up-front.
+            match types.next() {
+                None => {
+                    tcx.sess.span_err(
+                        tcx.def_span(def.did),
+                        "`#[fundamental]` requires at least one type parameter",
+                    );
+
+                    return None;
+                }
+
+                Some(first_ty) => (first_ty, types),
+            }
+        }
+        _ => return None,
+    };
+
+    Some(iter::once(first_ty).chain(rest_tys))
 }
 
 fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
@@ -451,6 +481,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
     }
 }
 
+// FIXME(eddyb) this can just return `bool` as it always returns `Some(ty)` or `None`.
 fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option<Ty<'_>> {
     debug!("ty_is_non_local_constructor({:?})", ty);
 
diff --git a/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs b/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs
index 49b3abc99b7..bc1e18b657f 100644
--- a/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs
+++ b/src/test/ui/coherence/impl-foreign-for-locally-defined-fundamental.rs
@@ -8,8 +8,8 @@ extern crate coherence_lib as lib;
 use lib::*;
 
 #[fundamental]
-struct Local;
+struct Local<T>(T);
 
-impl Remote for Local {}
+impl Remote for Local<()> {}
 
 fn main() {}