@@ -8,6 +8,7 @@ use super::{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,180 @@ 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
+ (
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
+ }
241
416
}
242
417
243
418
impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments