@@ -4,10 +4,11 @@ use std::iter;
4
4
5
5
use super :: assembly:: { self , Candidate , CandidateSource } ;
6
6
use super :: infcx_ext:: InferCtxtExt ;
7
- use super :: { Certainty , EvalCtxt , Goal , QueryResult } ;
7
+ use super :: { CanonicalResponse , Certainty , EvalCtxt , Goal , QueryResult } ;
8
8
use rustc_hir:: def_id:: DefId ;
9
9
use rustc_infer:: infer:: InferCtxt ;
10
10
use rustc_infer:: traits:: query:: NoSolution ;
11
+ use rustc_infer:: traits:: util:: supertraits;
11
12
use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
12
13
use rustc_middle:: ty:: { self , ToPredicate , Ty , TyCtxt } ;
13
14
use rustc_middle:: ty:: { TraitPredicate , TypeVisitable } ;
@@ -238,6 +239,206 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
238
239
. to_predicate ( tcx) ,
239
240
)
240
241
}
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
+ }
241
442
}
242
443
243
444
impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments