1
1
use std:: ops:: ControlFlow ;
2
2
3
+ use rustc_hir as hir;
3
4
use rustc_hir:: def_id:: DefId ;
4
5
use rustc_infer:: infer:: { DefineOpaqueTypes , InferCtxt , InferOk } ;
5
6
use rustc_infer:: traits:: util:: supertraits;
@@ -11,7 +12,7 @@ use rustc_middle::traits::{
11
12
ImplSource , ImplSourceObjectData , ImplSourceTraitUpcastingData , ImplSourceUserDefinedData ,
12
13
ObligationCause , SelectionError ,
13
14
} ;
14
- use rustc_middle:: ty:: { self , TyCtxt } ;
15
+ use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
15
16
use rustc_span:: DUMMY_SP ;
16
17
17
18
use crate :: solve:: assembly:: { BuiltinImplSource , Candidate , CandidateSource } ;
@@ -113,6 +114,12 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
113
114
) ,
114
115
) => rematch_object ( self , goal, nested_obligations) ,
115
116
117
+ ( Certainty :: Maybe ( _) , CandidateSource :: BuiltinImpl ( BuiltinImplSource :: Misc ) )
118
+ if self . tcx . lang_items ( ) . unsize_trait ( ) == Some ( goal. predicate . def_id ( ) ) =>
119
+ {
120
+ rematch_unsize ( self , goal, nested_obligations)
121
+ }
122
+
116
123
// Technically some builtin impls have nested obligations, but if
117
124
// `Certainty::Yes`, then they should've all been verified and don't
118
125
// need re-checking.
@@ -232,6 +239,9 @@ fn rematch_object<'tcx>(
232
239
{
233
240
assert_eq ! ( source_kind, ty:: Dyn , "cannot upcast dyn*" ) ;
234
241
if let ty:: Dynamic ( data, _, ty:: Dyn ) = goal. predicate . trait_ref . substs . type_at ( 1 ) . kind ( ) {
242
+ // FIXME: We also need to ensure that the source lifetime outlives the
243
+ // target lifetime. This doesn't matter for codegen, though, and only
244
+ // *really* matters if the goal's certainty is ambiguous.
235
245
( true , data. principal ( ) . unwrap ( ) . with_self_ty ( infcx. tcx , self_ty) )
236
246
} else {
237
247
bug ! ( )
@@ -305,3 +315,136 @@ fn rematch_object<'tcx>(
305
315
ImplSource :: Object ( ImplSourceObjectData { vtable_base, nested } )
306
316
} ) )
307
317
}
318
+
319
+ /// The `Unsize` trait is particularly important to coercion, so we try rematch it.
320
+ /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
321
+ /// goal assembly in the solver, both for soundness and in order to avoid ICEs.
322
+ fn rematch_unsize < ' tcx > (
323
+ infcx : & InferCtxt < ' tcx > ,
324
+ goal : Goal < ' tcx , ty:: TraitPredicate < ' tcx > > ,
325
+ mut nested : Vec < PredicateObligation < ' tcx > > ,
326
+ ) -> SelectionResult < ' tcx , Selection < ' tcx > > {
327
+ let tcx = infcx. tcx ;
328
+ let a_ty = goal. predicate . self_ty ( ) ;
329
+ let b_ty = goal. predicate . trait_ref . substs . type_at ( 1 ) ;
330
+
331
+ match ( a_ty. kind ( ) , b_ty. kind ( ) ) {
332
+ ( _, & ty:: Dynamic ( data, region, ty:: Dyn ) ) => {
333
+ // Check that the type implements all of the predicates of the def-id.
334
+ // (i.e. the principal, all of the associated types match, and any auto traits)
335
+ nested. extend ( data. iter ( ) . map ( |pred| {
336
+ Obligation :: new (
337
+ infcx. tcx ,
338
+ ObligationCause :: dummy ( ) ,
339
+ goal. param_env ,
340
+ pred. with_self_ty ( tcx, a_ty) ,
341
+ )
342
+ } ) ) ;
343
+ // The type must be Sized to be unsized.
344
+ let sized_def_id = tcx. require_lang_item ( hir:: LangItem :: Sized , None ) ;
345
+ nested. push ( Obligation :: new (
346
+ infcx. tcx ,
347
+ ObligationCause :: dummy ( ) ,
348
+ goal. param_env ,
349
+ ty:: TraitRef :: new ( tcx, sized_def_id, [ a_ty] ) ,
350
+ ) ) ;
351
+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
352
+ nested. push ( Obligation :: new (
353
+ infcx. tcx ,
354
+ ObligationCause :: dummy ( ) ,
355
+ goal. param_env ,
356
+ ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_ty, region) ) ,
357
+ ) ) ;
358
+ }
359
+ // `[T; n]` -> `[T]` unsizing
360
+ ( & ty:: Array ( a_elem_ty, ..) , & ty:: Slice ( b_elem_ty) ) => {
361
+ nested. extend (
362
+ infcx
363
+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
364
+ . eq ( DefineOpaqueTypes :: No , a_elem_ty, b_elem_ty)
365
+ . expect ( "expected rematch to succeed" )
366
+ . into_obligations ( ) ,
367
+ ) ;
368
+ }
369
+ // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
370
+ ( & ty:: Adt ( a_def, a_substs) , & ty:: Adt ( b_def, b_substs) )
371
+ if a_def. is_struct ( ) && a_def. did ( ) == b_def. did ( ) =>
372
+ {
373
+ let unsizing_params = tcx. unsizing_params_for_adt ( a_def. did ( ) ) ;
374
+ // We must be unsizing some type parameters. This also implies
375
+ // that the struct has a tail field.
376
+ if unsizing_params. is_empty ( ) {
377
+ bug ! ( "expected rematch to succeed" )
378
+ }
379
+
380
+ let tail_field = a_def
381
+ . non_enum_variant ( )
382
+ . fields
383
+ . raw
384
+ . last ( )
385
+ . expect ( "expected unsized ADT to have a tail field" ) ;
386
+ let tail_field_ty = tcx. type_of ( tail_field. did ) ;
387
+
388
+ let a_tail_ty = tail_field_ty. subst ( tcx, a_substs) ;
389
+ let b_tail_ty = tail_field_ty. subst ( tcx, b_substs) ;
390
+
391
+ // Substitute just the unsizing params from B into A. The type after
392
+ // this substitution must be equal to B. This is so we don't unsize
393
+ // unrelated type parameters.
394
+ let new_a_substs =
395
+ tcx. mk_substs_from_iter ( a_substs. iter ( ) . enumerate ( ) . map ( |( i, a) | {
396
+ if unsizing_params. contains ( i as u32 ) { b_substs[ i] } else { a }
397
+ } ) ) ;
398
+ let unsized_a_ty = Ty :: new_adt ( tcx, a_def, new_a_substs) ;
399
+
400
+ nested. extend (
401
+ infcx
402
+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
403
+ . eq ( DefineOpaqueTypes :: No , unsized_a_ty, b_ty)
404
+ . expect ( "expected rematch to succeed" )
405
+ . into_obligations ( ) ,
406
+ ) ;
407
+
408
+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
409
+ // types.
410
+ nested. push ( Obligation :: new (
411
+ tcx,
412
+ ObligationCause :: dummy ( ) ,
413
+ goal. param_env ,
414
+ ty:: TraitRef :: new ( tcx, goal. predicate . def_id ( ) , [ a_tail_ty, b_tail_ty] ) ,
415
+ ) ) ;
416
+ }
417
+ // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
418
+ ( & ty:: Tuple ( a_tys) , & ty:: Tuple ( b_tys) )
419
+ if a_tys. len ( ) == b_tys. len ( ) && !a_tys. is_empty ( ) =>
420
+ {
421
+ let ( a_last_ty, a_rest_tys) = a_tys. split_last ( ) . unwrap ( ) ;
422
+ let b_last_ty = b_tys. last ( ) . unwrap ( ) ;
423
+
424
+ // Substitute just the tail field of B., and require that they're equal.
425
+ let unsized_a_ty =
426
+ Ty :: new_tup_from_iter ( tcx, a_rest_tys. iter ( ) . chain ( [ b_last_ty] ) . copied ( ) ) ;
427
+ nested. extend (
428
+ infcx
429
+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
430
+ . eq ( DefineOpaqueTypes :: No , unsized_a_ty, b_ty)
431
+ . expect ( "expected rematch to succeed" )
432
+ . into_obligations ( ) ,
433
+ ) ;
434
+
435
+ // Similar to ADTs, require that the rest of the fields are equal.
436
+ nested. push ( Obligation :: new (
437
+ tcx,
438
+ ObligationCause :: dummy ( ) ,
439
+ goal. param_env ,
440
+ ty:: TraitRef :: new ( tcx, goal. predicate . def_id ( ) , [ * a_last_ty, * b_last_ty] ) ,
441
+ ) ) ;
442
+ }
443
+ // FIXME: We *could* ICE here if either:
444
+ // 1. the certainty is `Certainty::Yes`,
445
+ // 2. we're in codegen (which should mean `Certainty::Yes`).
446
+ _ => return Ok ( None ) ,
447
+ }
448
+
449
+ Ok ( Some ( ImplSource :: Builtin ( nested) ) )
450
+ }
0 commit comments