diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 924568a01a7..3b7c201f3ee 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -135,6 +135,11 @@ pub enum SelectionCandidate<'tcx> { /// `rustc_infer::traits::util::supertraits`. ObjectCandidate(usize), + /// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`. + /// The index is the position in the iterator returned by + /// `rustc_infer::traits::util::supertraits`. + TraitUpcastingUnsizeCandidate(usize), + BuiltinObjectCandidate, BuiltinUnsizeCandidate, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 4312cc94682..e18828fec3f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -690,19 +690,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "assemble_candidates_for_unsizing"); - let may_apply = match (source.kind(), target.kind()) { + match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // See `confirm_builtin_unsize_candidate` for more info. + // Upcast coercions permit several things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` + // + // Note that neither of the first two of these changes requires any + // change at runtime. The third needs to change pointer metadata at runtime. + // + // We always perform upcasting coercions when we can because of reason + // #2 (region bounds). let auto_traits_compatible = data_b .auto_traits() // All of a's auto traits need to be in b's auto traits. .all(|b| data_a.auto_traits().any(|a| a == b)); - auto_traits_compatible + if auto_traits_compatible { + let principal_def_id_a = data_a.principal_def_id(); + let principal_def_id_b = data_b.principal_def_id(); + if principal_def_id_a == principal_def_id_b { + // no cyclic + candidates.vec.push(BuiltinUnsizeCandidate); + } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { + // not casual unsizing, now check whether this is trait upcasting coercion. + let principal_a = data_a.principal().unwrap(); + let target_trait_did = principal_def_id_b.unwrap(); + let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); + for (idx, upcast_trait_ref) in + util::supertraits(self.tcx(), source_trait_ref).enumerate() + { + if upcast_trait_ref.def_id() == target_trait_did { + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + } + } + } } // `T` -> `Trait` - (_, &ty::Dynamic(..)) => true, + (_, &ty::Dynamic(..)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // Ambiguous handling is below `T` -> `Trait`, because inference // variables can still implement `Unsize` and nested @@ -710,26 +741,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { debug!("assemble_candidates_for_unsizing: ambiguous"); candidates.ambiguous = true; - false } // `[T; n]` -> `[T]` - (&ty::Array(..), &ty::Slice(_)) => true, + (&ty::Array(..), &ty::Slice(_)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // `Struct` -> `Struct` (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { - def_id_a == def_id_b + if def_id_a == def_id_b { + candidates.vec.push(BuiltinUnsizeCandidate); + } } // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + if tys_a.len() == tys_b.len() { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } - _ => false, + _ => {} }; - - if may_apply { - candidates.vec.push(BuiltinUnsizeCandidate); - } } fn assemble_candidates_for_trait_alias( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0c2099593a2..9dc9c21b519 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -118,6 +118,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_builtin_unsize_candidate(obligation)?; Ok(ImplSource::Builtin(data)) } + + TraitUpcastingUnsizeCandidate(idx) => { + let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; + Ok(ImplSource::Builtin(data)) + } } } @@ -685,6 +690,81 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) } + fn confirm_trait_upcasting_unsize_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + idx: usize, + ) -> Result>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + let target = self.infcx.shallow_resolve(target); + + debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); + + let mut nested = vec![]; + match (source.kind(), target.kind()) { + // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). + (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // See `assemble_candidates_for_unsizing` for more info. + // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. + let principal_a = data_a.principal().unwrap(); + let source_trait_ref = principal_a.with_self_ty(tcx, source); + let target_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); + assert_eq!(data_b.principal_def_id(), Some(target_trait_ref.def_id())); + let existential_predicate = target_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( + tcx, trait_ref, + )) + }); + let iter = Some(existential_predicate) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let existential_predicates = tcx.mk_poly_existential_predicates(iter); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(target, source_trait) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Register one obligation for 'a: 'b. + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + let outlives = ty::OutlivesPredicate(r_a, r_b); + nested.push(Obligation::with_depth( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(outlives).to_predicate(tcx), + )); + } + _ => bug!(), + }; + + Ok(ImplSourceBuiltinData { nested }) + } + fn confirm_builtin_unsize_candidate( &mut self, obligation: &TraitObligation<'tcx>, @@ -701,58 +781,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut nested = vec![]; match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { - // Upcast coercions permit several things: - // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` - // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` - // - // Note that neither of the first two of these changes requires any - // change at runtime. The third needs to change pointer metadata at runtime. - // - // We always perform upcasting coercions when we can because of reason - // #2 (region bounds). - + // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. - - let principal_a = data_a.principal(); - let principal_def_id_b = data_b.principal_def_id(); - - let existential_predicate = if let Some(principal_a) = principal_a { - let source_trait_ref = principal_a.with_self_ty(tcx, source); - let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?; - let upcast_idx = util::supertraits(tcx, source_trait_ref) - .position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - .ok_or_else(|| Unimplemented)?; - // FIXME(crlf0710): This is less than ideal, for example, - // if the trait is defined as `trait Foo: Bar + Bar`, - // the coercion from Box to Box> is actually ambiguous. - // We currently make this coercion fail for now. - // - // see #65991 for more information. - if util::supertraits(tcx, source_trait_ref) - .skip(upcast_idx + 1) - .any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - { - return Err(Unimplemented); - } - let target_trait_ref = - util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap(); - let existential_predicate = target_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - Some(existential_predicate) - } else if principal_def_id_b.is_none() { - None - } else { - return Err(Unimplemented); - }; - - let iter = existential_predicate + let iter = data_a + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( data_a diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 38dbacbf2ae..6ef18034298 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1500,6 +1500,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..) | ObjectCandidate(_) @@ -1517,6 +1518,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ParamCandidate(ref cand), @@ -1546,6 +1548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ) => true, @@ -1557,6 +1560,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ObjectCandidate(_) | ProjectionCandidate(_), @@ -1630,6 +1634,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ImplCandidate(_) @@ -1638,6 +1643,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ) => false, diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs index 1a0e5072843..79ddedd4187 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs @@ -9,12 +9,8 @@ trait Bar { } fn test_specific(x: &dyn Foo) { - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let _ = x as &dyn Bar; // OK + let _ = x as &dyn Bar; // OK } fn test_unknown_version(x: &dyn Foo) { @@ -24,9 +20,7 @@ fn test_unknown_version(x: &dyn Foo) { } fn test_infer_version(x: &dyn Foo) { - let a = x as &dyn Bar<_>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let a = x as &dyn Bar<_>; // OK let _: Option = a.bar(); } diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr index da647f16c3d..44f32e0cec9 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr @@ -1,43 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,33 +10,14 @@ LL | let _ = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | = note: required for the cast to the object type `dyn Bar<_>` -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let a = &x as &dyn Bar<_>; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - -error: aborting due to 8 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs index 326df74211e..32754c53803 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs @@ -13,9 +13,7 @@ fn test_specific(x: &dyn Foo) { } fn test_specific2(x: &dyn Foo) { - let _ = x as &dyn Bar; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar` is not satisfied + let _ = x as &dyn Bar; // OK } fn test_specific3(x: &dyn Foo) { diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr index 582eddc7aa0..4ae4c8552c1 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -1,24 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar; // FIXME: OK, eventually - | ^ the trait `Bar` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar` - error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar; // Error | ^^^^^^^^^^^^^^^^^^ invalid cast @@ -29,7 +10,7 @@ LL | let _ = &x as &dyn Bar; // Error | + error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar; // Error | ^ the trait `Bar` is not implemented for `&dyn Foo` @@ -37,7 +18,7 @@ LL | let _ = x as &dyn Bar; // Error = note: required for the cast to the object type `dyn Bar` error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,14 +29,14 @@ LL | let a = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | = note: required for the cast to the object type `dyn Bar<_>` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`.