about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-05-09 21:01:19 +0000
committerbors <bors@rust-lang.org>2020-05-09 21:01:19 +0000
commit0a3619c9e51a16d08cfd9a825cb89c6a49f80ffc (patch)
tree97ca6f473890d6bac407b65247791bba850f4532 /src
parentbad3bf622bded50a97c0a54e29350eada2a3a169 (diff)
parentff6e4ee7ad9b61d1ac1ae85fff14c4dacb66034f (diff)
downloadrust-0a3619c9e51a16d08cfd9a825cb89c6a49f80ffc.tar.gz
rust-0a3619c9e51a16d08cfd9a825cb89c6a49f80ffc.zip
Auto merge of #69530 - Aaron1011:perf/skip-coerce-var, r=nikomatsakis
[perf] Skip attempting to run coerce_unsized on an inference variable

See the included comment for a detailed explanation of why this is
sound.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/coercion.rs36
1 files changed, 35 insertions, 1 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 86cafa0b8ca..4ac3f2625ab 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -452,9 +452,43 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     // &[T; n] or &mut [T; n] -> &[T]
     // or &mut [T; n] -> &mut [T]
     // or &Concrete -> &Trait, etc.
-    fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tcx> {
+    fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
         debug!("coerce_unsized(source={:?}, target={:?})", source, target);
 
+        source = self.shallow_resolve(source);
+        target = self.shallow_resolve(target);
+        debug!("coerce_unsized: resolved source={:?} target={:?}", source, target);
+
+        // These 'if' statements require some explanation.
+        // The `CoerceUnsized` trait is special - it is only
+        // possible to write `impl CoerceUnsized<B> for A` where
+        // A and B have 'matching' fields. This rules out the following
+        // two types of blanket impls:
+        //
+        // `impl<T> CoerceUnsized<T> for SomeType`
+        // `impl<T> CoerceUnsized<SomeType> for T`
+        //
+        // Both of these trigger a special `CoerceUnsized`-related error (E0376)
+        //
+        // We can take advantage of this fact to avoid performing unecessary work.
+        // If either `source` or `target` is a type variable, then any applicable impl
+        // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
+        // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
+        // SomeType`).
+        //
+        // However, these are exactly the kinds of impls which are forbidden by
+        // the compiler! Therefore, we can be sure that coercion will always fail
+        // when either the source or target type is a type variable. This allows us
+        // to skip performing any trait selection, and immediately bail out.
+        if source.is_ty_var() {
+            debug!("coerce_unsized: source is a TyVar, bailing out");
+            return Err(TypeError::Mismatch);
+        }
+        if target.is_ty_var() {
+            debug!("coerce_unsized: target is a TyVar, bailing out");
+            return Err(TypeError::Mismatch);
+        }
+
         let traits =
             (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait());
         let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits {