@@ -17,8 +17,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
17
17
use rustc_infer:: traits:: ObligationCauseCode ;
18
18
use rustc_middle:: traits:: { BuiltinImplSource , SignatureMismatchData } ;
19
19
use rustc_middle:: ty:: {
20
- self , GenericArgs , GenericArgsRef , GenericParamDefKind , ToPolyTraitRef , TraitPredicate , Ty ,
21
- TyCtxt , Upcast ,
20
+ self , GenericArgs , GenericArgsRef , GenericParamDefKind , ToPolyTraitRef , Ty , TyCtxt , Upcast ,
22
21
} ;
23
22
use rustc_middle:: { bug, span_bug} ;
24
23
use rustc_span:: def_id:: DefId ;
@@ -292,90 +291,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
292
291
& mut self ,
293
292
obligation : & PolyTraitObligation < ' tcx > ,
294
293
) -> Result < Vec < PredicateObligation < ' tcx > > , SelectionError < ' tcx > > {
295
- use rustc_transmute:: { Answer , Condition } ;
296
- #[ instrument( level = "debug" , skip( tcx, obligation, predicate) ) ]
294
+ use rustc_transmute:: { Answer , Assume , Condition } ;
295
+
296
+ /// Generate sub-obligations for reference-to-reference transmutations.
297
+ fn reference_obligations < ' tcx > (
298
+ tcx : TyCtxt < ' tcx > ,
299
+ obligation : & PolyTraitObligation < ' tcx > ,
300
+ ( src_lifetime, src_ty, src_mut) : ( ty:: Region < ' tcx > , Ty < ' tcx > , Mutability ) ,
301
+ ( dst_lifetime, dst_ty, dst_mut) : ( ty:: Region < ' tcx > , Ty < ' tcx > , Mutability ) ,
302
+ assume : Assume ,
303
+ ) -> Vec < PredicateObligation < ' tcx > > {
304
+ let make_transmute_obl = |src, dst| {
305
+ let transmute_trait = obligation. predicate . def_id ( ) ;
306
+ let assume = obligation. predicate . skip_binder ( ) . trait_ref . args . const_at ( 2 ) ;
307
+ let trait_ref = ty:: TraitRef :: new (
308
+ tcx,
309
+ transmute_trait,
310
+ [
311
+ ty:: GenericArg :: from ( dst) ,
312
+ ty:: GenericArg :: from ( src) ,
313
+ ty:: GenericArg :: from ( assume) ,
314
+ ] ,
315
+ ) ;
316
+ Obligation :: with_depth (
317
+ tcx,
318
+ obligation. cause . clone ( ) ,
319
+ obligation. recursion_depth + 1 ,
320
+ obligation. param_env ,
321
+ obligation. predicate . rebind ( trait_ref) ,
322
+ )
323
+ } ;
324
+
325
+ let make_freeze_obl = |ty| {
326
+ let trait_ref = ty:: TraitRef :: new (
327
+ tcx,
328
+ tcx. require_lang_item ( LangItem :: Freeze , None ) ,
329
+ [ ty:: GenericArg :: from ( ty) ] ,
330
+ ) ;
331
+ Obligation :: with_depth (
332
+ tcx,
333
+ obligation. cause . clone ( ) ,
334
+ obligation. recursion_depth + 1 ,
335
+ obligation. param_env ,
336
+ trait_ref,
337
+ )
338
+ } ;
339
+
340
+ let make_outlives_obl = |target, region| {
341
+ let outlives = ty:: OutlivesPredicate ( target, region) ;
342
+ Obligation :: with_depth (
343
+ tcx,
344
+ obligation. cause . clone ( ) ,
345
+ obligation. recursion_depth + 1 ,
346
+ obligation. param_env ,
347
+ obligation. predicate . rebind ( outlives) ,
348
+ )
349
+ } ;
350
+
351
+ // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
352
+ // it is always the case that `Src` must be transmutable into `Dst`,
353
+ // and that that `'src` must outlive `'dst`.
354
+ let mut obls = vec ! [ make_transmute_obl( src_ty, dst_ty) ] ;
355
+ if !assume. lifetimes {
356
+ obls. push ( make_outlives_obl ( src_lifetime, dst_lifetime) ) ;
357
+ }
358
+
359
+ // Given a transmutation from `&Src`, both `Src` and `Dst` must be
360
+ // `Freeze`, otherwise, using the transmuted value could lead to
361
+ // data races.
362
+ if src_mut == Mutability :: Not {
363
+ obls. extend ( [ make_freeze_obl ( src_ty) , make_freeze_obl ( dst_ty) ] )
364
+ }
365
+
366
+ // Given a transmutation into `&'dst mut Dst`, it also must be the
367
+ // case that `Dst` is transmutable into `Src`. For example,
368
+ // transmuting bool -> u8 is OK as long as you can't update that u8
369
+ // to be > 1, because you could later transmute the u8 back to a
370
+ // bool and get undefined behavior. It also must be the case that
371
+ // `'dst` lives exactly as long as `'src`.
372
+ if dst_mut == Mutability :: Mut {
373
+ obls. push ( make_transmute_obl ( dst_ty, src_ty) ) ;
374
+ if !assume. lifetimes {
375
+ obls. push ( make_outlives_obl ( dst_lifetime, src_lifetime) ) ;
376
+ }
377
+ }
378
+
379
+ obls
380
+ }
381
+
382
+ /// Flatten the `Condition` tree into a conjunction of obligations.
383
+ #[ instrument( level = "debug" , skip( tcx, obligation) ) ]
297
384
fn flatten_answer_tree < ' tcx > (
298
385
tcx : TyCtxt < ' tcx > ,
299
386
obligation : & PolyTraitObligation < ' tcx > ,
300
- predicate : TraitPredicate < ' tcx > ,
301
387
cond : Condition < rustc_transmute:: layout:: rustc:: Ref < ' tcx > > ,
388
+ assume : Assume ,
302
389
) -> Vec < PredicateObligation < ' tcx > > {
303
390
match cond {
304
391
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
305
392
// Not possible until the trait solver supports disjunctions of obligations
306
393
Condition :: IfAll ( conds) | Condition :: IfAny ( conds) => conds
307
394
. into_iter ( )
308
- . flat_map ( |cond| flatten_answer_tree ( tcx, obligation, predicate , cond ) )
395
+ . flat_map ( |cond| flatten_answer_tree ( tcx, obligation, cond , assume ) )
309
396
. collect ( ) ,
310
- Condition :: IfTransmutable { src, dst } => {
311
- let transmute_trait = obligation. predicate . def_id ( ) ;
312
- let assume_const = predicate. trait_ref . args . const_at ( 2 ) ;
313
- let make_transmute_obl = |from_ty, to_ty| {
314
- let trait_ref = ty:: TraitRef :: new (
315
- tcx,
316
- transmute_trait,
317
- [
318
- ty:: GenericArg :: from ( to_ty) ,
319
- ty:: GenericArg :: from ( from_ty) ,
320
- ty:: GenericArg :: from ( assume_const) ,
321
- ] ,
322
- ) ;
323
- Obligation :: with_depth (
324
- tcx,
325
- obligation. cause . clone ( ) ,
326
- obligation. recursion_depth + 1 ,
327
- obligation. param_env ,
328
- trait_ref,
329
- )
330
- } ;
331
-
332
- let make_freeze_obl = |ty| {
333
- let trait_ref = ty:: TraitRef :: new (
334
- tcx,
335
- tcx. require_lang_item ( LangItem :: Freeze , None ) ,
336
- [ ty:: GenericArg :: from ( ty) ] ,
337
- ) ;
338
- Obligation :: with_depth (
339
- tcx,
340
- obligation. cause . clone ( ) ,
341
- obligation. recursion_depth + 1 ,
342
- obligation. param_env ,
343
- trait_ref,
344
- )
345
- } ;
346
-
347
- let mut obls = vec ! [ ] ;
348
-
349
- // If the source is a shared reference, it must be `Freeze`;
350
- // otherwise, transmuting could lead to data races.
351
- if src. mutability == Mutability :: Not {
352
- obls. extend ( [ make_freeze_obl ( src. ty ) , make_freeze_obl ( dst. ty ) ] )
353
- }
354
-
355
- // If Dst is mutable, check bidirectionally.
356
- // For example, transmuting bool -> u8 is OK as long as you can't update that u8
357
- // to be > 1, because you could later transmute the u8 back to a bool and get UB.
358
- match dst. mutability {
359
- Mutability :: Not => obls. push ( make_transmute_obl ( src. ty , dst. ty ) ) ,
360
- Mutability :: Mut => obls. extend ( [
361
- make_transmute_obl ( src. ty , dst. ty ) ,
362
- make_transmute_obl ( dst. ty , src. ty ) ,
363
- ] ) ,
364
- }
365
-
366
- obls
367
- }
397
+ Condition :: IfTransmutable { src, dst } => reference_obligations (
398
+ tcx,
399
+ obligation,
400
+ ( src. lifetime , src. ty , src. mutability ) ,
401
+ ( dst. lifetime , dst. ty , dst. mutability ) ,
402
+ assume,
403
+ ) ,
368
404
}
369
405
}
370
406
371
- // We erase regions here because transmutability calls layout queries,
372
- // which does not handle inference regions and doesn't particularly
373
- // care about other regions. Erasing late-bound regions is equivalent
374
- // to instantiating the binder with placeholders then erasing those
375
- // placeholder regions.
376
- let predicate = self
377
- . tcx ( )
378
- . erase_regions ( self . tcx ( ) . instantiate_bound_regions_with_erased ( obligation. predicate ) ) ;
407
+ let predicate = obligation. predicate . skip_binder ( ) ;
379
408
380
409
let Some ( assume) = rustc_transmute:: Assume :: from_const (
381
410
self . infcx . tcx ,
@@ -387,6 +416,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
387
416
388
417
let dst = predicate. trait_ref . args . type_at ( 0 ) ;
389
418
let src = predicate. trait_ref . args . type_at ( 1 ) ;
419
+
390
420
debug ! ( ?src, ?dst) ;
391
421
let mut transmute_env = rustc_transmute:: TransmuteTypeEnv :: new ( self . infcx ) ;
392
422
let maybe_transmutable = transmute_env. is_transmutable (
@@ -397,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
397
427
398
428
let fully_flattened = match maybe_transmutable {
399
429
Answer :: No ( _) => Err ( Unimplemented ) ?,
400
- Answer :: If ( cond) => flatten_answer_tree ( self . tcx ( ) , obligation, predicate , cond ) ,
430
+ Answer :: If ( cond) => flatten_answer_tree ( self . tcx ( ) , obligation, cond , assume ) ,
401
431
Answer :: Yes => vec ! [ ] ,
402
432
} ;
403
433
0 commit comments