@@ -17,8 +17,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
1717use rustc_infer:: traits:: ObligationCauseCode ;
1818use rustc_middle:: traits:: { BuiltinImplSource , SignatureMismatchData } ;
1919use rustc_middle:: ty:: {
20- self , GenericArgs , GenericArgsRef , GenericParamDefKind , ToPolyTraitRef , TraitPredicate , Ty ,
21- TyCtxt , Upcast ,
20+ self , GenericArgs , GenericArgsRef , GenericParamDefKind , ToPolyTraitRef , Ty , TyCtxt , Upcast ,
2221} ;
2322use rustc_middle:: { bug, span_bug} ;
2423use rustc_span:: def_id:: DefId ;
@@ -292,90 +291,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
292291 & mut self ,
293292 obligation : & PolyTraitObligation < ' tcx > ,
294293 ) -> 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) ) ]
297384 fn flatten_answer_tree < ' tcx > (
298385 tcx : TyCtxt < ' tcx > ,
299386 obligation : & PolyTraitObligation < ' tcx > ,
300- predicate : TraitPredicate < ' tcx > ,
301387 cond : Condition < rustc_transmute:: layout:: rustc:: Ref < ' tcx > > ,
388+ assume : Assume ,
302389 ) -> Vec < PredicateObligation < ' tcx > > {
303390 match cond {
304391 // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
305392 // Not possible until the trait solver supports disjunctions of obligations
306393 Condition :: IfAll ( conds) | Condition :: IfAny ( conds) => conds
307394 . into_iter ( )
308- . flat_map ( |cond| flatten_answer_tree ( tcx, obligation, predicate , cond ) )
395+ . flat_map ( |cond| flatten_answer_tree ( tcx, obligation, cond , assume ) )
309396 . 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+ ) ,
368404 }
369405 }
370406
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 ( ) ;
379408
380409 let Some ( assume) = rustc_transmute:: Assume :: from_const (
381410 self . infcx . tcx ,
@@ -387,6 +416,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
387416
388417 let dst = predicate. trait_ref . args . type_at ( 0 ) ;
389418 let src = predicate. trait_ref . args . type_at ( 1 ) ;
419+
390420 debug ! ( ?src, ?dst) ;
391421 let mut transmute_env = rustc_transmute:: TransmuteTypeEnv :: new ( self . infcx ) ;
392422 let maybe_transmutable = transmute_env. is_transmutable (
@@ -397,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
397427
398428 let fully_flattened = match maybe_transmutable {
399429 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 ) ,
401431 Answer :: Yes => vec ! [ ] ,
402432 } ;
403433
0 commit comments