@@ -4,10 +4,11 @@ use std::iter;
44
55use super :: assembly:: { self , Candidate , CandidateSource } ;
66use super :: infcx_ext:: InferCtxtExt ;
7- use super :: { Certainty , EvalCtxt , Goal , QueryResult } ;
7+ use super :: { CanonicalResponse , 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,206 @@ 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+ ( & ty:: Dynamic ( _, _, ty:: Dyn ) , & ty:: Dynamic ( _, _, ty:: Dyn ) ) => {
257+ // Dyn upcasting is handled separately, since due to upcasting,
258+ // when there are two supertraits that differ by substs, we
259+ // may return more than one query response.
260+ return Err ( NoSolution ) ;
261+ }
262+ // `T` -> `dyn Trait` unsizing
263+ ( _, & ty:: Dynamic ( data, region, ty:: Dyn ) ) => {
264+ // Can only unsize to an object-safe type
265+ if data
266+ . principal_def_id ( )
267+ . map_or ( false , |def_id| !tcx. check_is_object_safe ( def_id) )
268+ {
269+ return Err ( NoSolution ) ;
270+ }
271+
272+ let Some ( sized_def_id) = tcx. lang_items ( ) . sized_trait ( ) else {
273+ return Err ( NoSolution ) ;
274+ } ;
275+ let nested_goals: Vec < _ > = data
276+ . iter ( )
277+ // Check that the type implements all of the predicates of the def-id.
278+ // (i.e. the principal, all of the associated types match, and any auto traits)
279+ . map ( |pred| goal. with ( tcx, pred. with_self_ty ( tcx, a_ty) ) )
280+ . chain ( [
281+ // The type must be Sized to be unsized.
282+ goal. with (
283+ tcx,
284+ ty:: Binder :: dummy ( tcx. mk_trait_ref ( sized_def_id, [ a_ty] ) ) ,
285+ ) ,
286+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
287+ goal. with ( tcx, ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_ty, region) ) ) ,
288+ ] )
289+ . collect ( ) ;
290+
291+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
292+ }
293+ // `[T; n]` -> `[T]` unsizing
294+ ( & ty:: Array ( a_elem_ty, ..) , & ty:: Slice ( b_elem_ty) ) => {
295+ // We just require that the element type stays the same
296+ let nested_goals = ecx. infcx . eq ( goal. param_env , a_elem_ty, b_elem_ty) ?;
297+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
298+ }
299+ // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
300+ ( & ty:: Adt ( a_def, a_substs) , & ty:: Adt ( b_def, b_substs) )
301+ if a_def. is_struct ( ) && a_def. did ( ) == b_def. did ( ) =>
302+ {
303+ let unsizing_params = tcx. unsizing_params_for_adt ( a_def. did ( ) ) ;
304+ // We must be unsizing some type parameters. This also implies
305+ // that the struct has a tail field.
306+ if unsizing_params. is_empty ( ) {
307+ return Err ( NoSolution ) ;
308+ }
309+
310+ let tail_field = a_def
311+ . non_enum_variant ( )
312+ . fields
313+ . last ( )
314+ . expect ( "expected unsized ADT to have a tail field" ) ;
315+ let tail_field_ty = tcx. bound_type_of ( tail_field. did ) ;
316+
317+ let a_tail_ty = tail_field_ty. subst ( tcx, a_substs) ;
318+ let b_tail_ty = tail_field_ty. subst ( tcx, b_substs) ;
319+
320+ // Substitute just the unsizing params from B into A. The type after
321+ // this substitution must be equal to B. This is so we don't unsize
322+ // unrelated type parameters.
323+ let new_a_substs = tcx. mk_substs ( a_substs. iter ( ) . enumerate ( ) . map ( |( i, a) | {
324+ if unsizing_params. contains ( i as u32 ) { b_substs[ i] } else { a }
325+ } ) ) ;
326+ let unsized_a_ty = tcx. mk_adt ( a_def, new_a_substs) ;
327+
328+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
329+ // types.
330+ let mut nested_goals = ecx. infcx . eq ( goal. param_env , unsized_a_ty, b_ty) ?;
331+ nested_goals. push ( goal. with (
332+ tcx,
333+ ty:: Binder :: dummy (
334+ tcx. mk_trait_ref ( goal. predicate . def_id ( ) , [ a_tail_ty, b_tail_ty] ) ,
335+ ) ,
336+ ) ) ;
337+
338+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
339+ }
340+ // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
341+ ( & ty:: Tuple ( a_tys) , & ty:: Tuple ( b_tys) )
342+ if a_tys. len ( ) == b_tys. len ( ) && !a_tys. is_empty ( ) =>
343+ {
344+ let ( a_last_ty, a_rest_tys) = a_tys. split_last ( ) . unwrap ( ) ;
345+ let b_last_ty = b_tys. last ( ) . unwrap ( ) ;
346+
347+ // Substitute just the tail field of B., and require that they're equal.
348+ let unsized_a_ty = tcx. mk_tup ( a_rest_tys. iter ( ) . chain ( [ b_last_ty] ) ) ;
349+ let mut nested_goals = ecx. infcx . eq ( goal. param_env , unsized_a_ty, b_ty) ?;
350+
351+ // Similar to ADTs, require that the rest of the fields are equal.
352+ nested_goals. push ( goal. with (
353+ tcx,
354+ ty:: Binder :: dummy (
355+ tcx. mk_trait_ref ( goal. predicate . def_id ( ) , [ * a_last_ty, * b_last_ty] ) ,
356+ ) ,
357+ ) ) ;
358+
359+ ecx. evaluate_all_and_make_canonical_response ( nested_goals)
360+ }
361+ _ => Err ( NoSolution ) ,
362+ }
363+ } )
364+ }
365+
366+ fn consider_builtin_dyn_upcast_candidates (
367+ ecx : & mut EvalCtxt < ' _ , ' tcx > ,
368+ goal : Goal < ' tcx , Self > ,
369+ ) -> Vec < CanonicalResponse < ' tcx > > {
370+ let tcx = ecx. tcx ( ) ;
371+
372+ let a_ty = goal. predicate . self_ty ( ) ;
373+ let b_ty = goal. predicate . trait_ref . substs . type_at ( 1 ) ;
374+ let ty:: Dynamic ( a_data, a_region, ty:: Dyn ) = * a_ty. kind ( ) else {
375+ return vec ! [ ] ;
376+ } ;
377+ let ty:: Dynamic ( b_data, b_region, ty:: Dyn ) = * b_ty. kind ( ) else {
378+ return vec ! [ ] ;
379+ } ;
380+
381+ // All of a's auto traits need to be in b's auto traits.
382+ let auto_traits_compatible =
383+ b_data. auto_traits ( ) . all ( |b| a_data. auto_traits ( ) . any ( |a| a == b) ) ;
384+ if !auto_traits_compatible {
385+ return vec ! [ ] ;
386+ }
387+
388+ let mut unsize_dyn_to_principal = |principal : Option < ty:: PolyExistentialTraitRef < ' tcx > > | {
389+ ecx. infcx . probe ( |_| -> Result < _ , NoSolution > {
390+ // Require that all of the trait predicates from A match B, except for
391+ // the auto traits. We do this by constructing a new A type with B's
392+ // auto traits, and equating these types.
393+ let new_a_data = principal
394+ . into_iter ( )
395+ . map ( |trait_ref| trait_ref. map_bound ( ty:: ExistentialPredicate :: Trait ) )
396+ . chain ( a_data. iter ( ) . filter ( |a| {
397+ matches ! ( a. skip_binder( ) , ty:: ExistentialPredicate :: Projection ( _) )
398+ } ) )
399+ . chain (
400+ b_data
401+ . auto_traits ( )
402+ . map ( ty:: ExistentialPredicate :: AutoTrait )
403+ . map ( ty:: Binder :: dummy) ,
404+ ) ;
405+ let new_a_data = tcx. mk_poly_existential_predicates ( new_a_data) ;
406+ let new_a_ty = tcx. mk_dynamic ( new_a_data, b_region, ty:: Dyn ) ;
407+
408+ // We also require that A's lifetime outlives B's lifetime.
409+ let mut nested_obligations = ecx. infcx . eq ( goal. param_env , new_a_ty, b_ty) ?;
410+ nested_obligations. push (
411+ goal. with ( tcx, ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_region, b_region) ) ) ,
412+ ) ;
413+
414+ ecx. evaluate_all_and_make_canonical_response ( nested_obligations)
415+ } )
416+ } ;
417+
418+ let mut responses = vec ! [ ] ;
419+ // If the principal def ids match (or are both none), then we're not doing
420+ // trait upcasting. We're just removing auto traits (or shortening the lifetime).
421+ if a_data. principal_def_id ( ) == b_data. principal_def_id ( ) {
422+ if let Ok ( response) = unsize_dyn_to_principal ( a_data. principal ( ) ) {
423+ responses. push ( response) ;
424+ }
425+ } else if let Some ( a_principal) = a_data. principal ( )
426+ && let Some ( b_principal) = b_data. principal ( )
427+ {
428+ for super_trait_ref in supertraits ( tcx, a_principal. with_self_ty ( tcx, a_ty) ) {
429+ if super_trait_ref. def_id ( ) != b_principal. def_id ( ) {
430+ continue ;
431+ }
432+ let erased_trait_ref = super_trait_ref
433+ . map_bound ( |trait_ref| ty:: ExistentialTraitRef :: erase_self_ty ( tcx, trait_ref) ) ;
434+ if let Ok ( response) = unsize_dyn_to_principal ( Some ( erased_trait_ref) ) {
435+ responses. push ( response) ;
436+ }
437+ }
438+ }
439+
440+ responses
441+ }
241442}
242443
243444impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments