diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 3720b74d92e07..e99470202a0aa 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -455,6 +455,36 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tcx> { debug!("coerce_unsized(source={:?}, target={:?})", source, target); + // These 'if' statements require some explanation. + // The `CoerceUnsized` trait is special - it is only + // possible to write `impl CoerceUnsized for A` where + // A and B have 'matching' fields. This rules out the following + // two types of blanket impls: + // + // `impl CoerceUnsized for SomeType` + // `impl CoerceUnsized 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 CoerceUnsized for T`) + // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized 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 self.shallow_resolve(source).is_ty_var() { + debug!("coerce_unsized: source is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + if self.shallow_resolve(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 {