@@ -8,6 +8,7 @@ use super::{Certainty, EvalCtxt, Goal, QueryResult};
88use rustc_hir:: def_id:: DefId ;
99use rustc_infer:: infer:: InferCtxt ;
1010use rustc_infer:: traits:: query:: NoSolution ;
11+ use rustc_infer:: traits:: util:: supertraits;
1112use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
1213use rustc_middle:: ty:: { self , ToPredicate , Ty , TyCtxt } ;
1314use rustc_middle:: ty:: { TraitPredicate , TypeVisitable } ;
@@ -238,6 +239,180 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
238239 . to_predicate ( tcx) ,
239240 )
240241 }
242+
243+ fn consider_builtin_unsize_candidate (
244+ ecx : & mut EvalCtxt < ' _ , ' tcx > ,
245+ goal : Goal < ' tcx , Self > ,
246+ ) -> QueryResult < ' tcx > {
247+ let tcx = ecx. tcx ( ) ;
248+ let a_ty = goal. predicate . self_ty ( ) ;
249+ let b_ty = goal. predicate . trait_ref . substs . type_at ( 1 ) ;
250+ if b_ty. is_ty_var ( ) {
251+ return ecx. make_canonical_response ( Certainty :: AMBIGUOUS ) ;
252+ }
253+ ecx. infcx . probe ( |_| {
254+ match ( a_ty. kind ( ) , b_ty. kind ( ) ) {
255+ // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
256+ (
257+ & ty:: Dynamic ( a_data, a_region, ty:: Dyn ) ,
258+ & ty:: Dynamic ( b_data, b_region, ty:: Dyn ) ,
259+ ) => {
260+ // All of a's auto traits need to be in b's auto traits.
261+ let auto_traits_compatible = b_data
262+ . auto_traits ( )
263+ . all ( |b| a_data. auto_traits ( ) . any ( |a| a == b) ) ;
264+ if !auto_traits_compatible {
265+ return Err ( NoSolution ) ;
266+ }
267+
268+ // If the principal def ids match (or are both none), then we're not doing
269+ // trait upcasting. We're just removing auto traits (or shortening the lifetime).
270+ if a_data. principal_def_id ( ) == b_data. principal_def_id ( ) {
271+ // Require that all of the trait predicates from A match B, except for
272+ // the auto traits. We do this by constructing a new A type with B's
273+ // auto traits, and equating these types.
274+ let new_a_data = a_data
275+ . iter ( )
276+ . filter ( |a| {
277+ matches ! (
278+ a. skip_binder( ) ,
279+ ty:: ExistentialPredicate :: Trait ( _) | ty:: ExistentialPredicate :: Projection ( _)
280+ )
281+ } )
282+ . chain (
283+ b_data
284+ . auto_traits ( )
285+ . map ( ty:: ExistentialPredicate :: AutoTrait )
286+ . map ( ty:: Binder :: dummy) ,
287+ ) ;
288+ let new_a_data = tcx. mk_poly_existential_predicates ( new_a_data) ;
289+ let new_a_ty = tcx. mk_dynamic ( new_a_data, b_region, ty:: Dyn ) ;
290+
291+ // We also require that A's lifetime outlives B's lifetime.
292+ let mut nested_obligations = ecx. infcx . eq ( goal. param_env , new_a_ty, b_ty) ?;
293+ nested_obligations. push ( goal. with ( tcx, ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_region, b_region) ) ) ) ;
294+
295+ ecx. evaluate_all_and_make_canonical_response ( nested_obligations)
296+ } else if let Some ( a_principal) = a_data. principal ( )
297+ && let Some ( b_principal) = b_data. principal ( )
298+ && supertraits ( tcx, a_principal. with_self_ty ( tcx, a_ty) )
299+ . any ( |trait_ref| trait_ref. def_id ( ) == b_principal. def_id ( ) )
300+ {
301+ // FIXME: Intentionally ignoring `need_migrate_deref_output_trait_object` here for now.
302+ // Confirm upcasting candidate
303+ todo ! ( )
304+ } else {
305+ Err ( NoSolution )
306+ }
307+ }
308+ // `T` -> `dyn Trait` unsizing
309+ ( _, & ty:: Dynamic ( data, region, ty:: Dyn ) ) => {
310+ // Can only unsize to an object-safe type
311+ // FIXME: Can auto traits be *not* object safe?
312+ if data
313+ . auto_traits ( )
314+ . chain ( data. principal_def_id ( ) )
315+ . any ( |def_id| !tcx. is_object_safe ( def_id) )
316+ {
317+ return Err ( NoSolution ) ;
318+ }
319+
320+ let Some ( sized_def_id) = tcx. lang_items ( ) . sized_trait ( ) else {
321+ return Err ( NoSolution ) ;
322+ } ;
323+ let nested_goals: Vec < _ > = data
324+ . iter ( )
325+ // Check that the type implements all of the predicates of the def-id.
326+ // (i.e. the principal, all of the associated types match, and any auto traits)
327+ . map ( |pred| goal. with ( tcx, pred. with_self_ty ( tcx, a_ty) ) )
328+ . chain ( [
329+ // The type must be Sized to be unsized.
330+ goal. with (
331+ tcx,
332+ ty:: Binder :: dummy ( tcx. mk_trait_ref ( sized_def_id, [ a_ty] ) ) ,
333+ ) ,
334+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
335+ goal. with (
336+ tcx,
337+ ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_ty, region) ) ,
338+ ) ,
339+ ] )
340+ . collect ( ) ;
341+
342+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
343+ }
344+ // `[T; n]` -> `[T]` unsizing
345+ ( & ty:: Array ( a_elem_ty, ..) , & ty:: Slice ( b_elem_ty) ) => {
346+ // We just require that the element type stays the same
347+ let nested_goals = ecx. infcx . eq ( goal. param_env , a_elem_ty, b_elem_ty) ?;
348+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
349+ }
350+ // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
351+ ( & ty:: Adt ( a_def, a_substs) , & ty:: Adt ( b_def, b_substs) )
352+ if a_def. is_struct ( ) && a_def. did ( ) == b_def. did ( ) =>
353+ {
354+ let unsizing_params = tcx. unsizing_params_for_adt ( a_def. did ( ) ) ;
355+ // We must be unsizing some type parameters. This also implies
356+ // that the struct has a tail field.
357+ if unsizing_params. is_empty ( ) {
358+ return Err ( NoSolution ) ;
359+ }
360+
361+ let tail_field = a_def
362+ . non_enum_variant ( )
363+ . fields
364+ . last ( )
365+ . expect ( "expected unsized ADT to have a tail field" ) ;
366+ let tail_field_ty = tcx. bound_type_of ( tail_field. did ) ;
367+
368+ let a_tail_ty = tail_field_ty. subst ( tcx, a_substs) ;
369+ let b_tail_ty = tail_field_ty. subst ( tcx, b_substs) ;
370+
371+ // Substitute just the unsizing params from B into A. The type after
372+ // this substitution must be equal to B. This is so we don't unsize
373+ // unrelated type parameters.
374+ let new_a_substs = tcx. mk_substs ( a_substs. iter ( ) . enumerate ( ) . map ( |( i, a) | {
375+ if unsizing_params. contains ( i as u32 ) { b_substs[ i] } else { a }
376+ } ) ) ;
377+ let unsized_a_ty = tcx. mk_adt ( a_def, new_a_substs) ;
378+
379+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
380+ // types.
381+ let mut nested_goals = ecx. infcx . eq ( goal. param_env , unsized_a_ty, b_ty) ?;
382+ nested_goals. push ( goal. with (
383+ tcx,
384+ ty:: Binder :: dummy (
385+ tcx. mk_trait_ref ( goal. predicate . def_id ( ) , [ a_tail_ty, b_tail_ty] ) ,
386+ ) ,
387+ ) ) ;
388+
389+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
390+ }
391+ // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
392+ ( & ty:: Tuple ( a_tys) , & ty:: Tuple ( b_tys) )
393+ if a_tys. len ( ) == b_tys. len ( ) && !a_tys. is_empty ( ) =>
394+ {
395+ let ( a_last_ty, a_rest_tys) = a_tys. split_last ( ) . unwrap ( ) ;
396+ let b_last_ty = b_tys. last ( ) . unwrap ( ) ;
397+
398+ // Substitute just the tail field of B., and require that they're equal.
399+ let unsized_a_ty = tcx. mk_tup ( a_rest_tys. iter ( ) . chain ( [ b_last_ty] ) ) ;
400+ let mut nested_goals = ecx. infcx . eq ( goal. param_env , unsized_a_ty, b_ty) ?;
401+
402+ // Similar to ADTs, require that the rest of the fields are equal.
403+ nested_goals. push ( goal. with (
404+ tcx,
405+ ty:: Binder :: dummy (
406+ tcx. mk_trait_ref ( goal. predicate . def_id ( ) , [ * a_last_ty, * b_last_ty] ) ,
407+ ) ,
408+ ) ) ;
409+
410+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
411+ }
412+ _ => Err ( NoSolution ) ,
413+ }
414+ } )
415+ }
241416}
242417
243418impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments