@@ -365,30 +365,115 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
365365 None
366366 } )
367367 }
368- // The `write_via_move` intrinsic needs to be special-cased very early to avoid
369- // introducing unnecessary copies that can be hard to remove again later:
370- // `write_via_move(ptr, val)` becomes `*ptr = val` but without any dropping.
368+ // Some intrinsics are handled here because they desperately want to avoid introducing
369+ // unnecessary copies.
371370 ExprKind :: Call { ty, fun, ref args, .. }
372- if let ty:: FnDef ( def_id, _generic_args ) = ty. kind ( )
371+ if let ty:: FnDef ( def_id, generic_args ) = ty. kind ( )
373372 && let Some ( intrinsic) = this. tcx . intrinsic ( def_id)
374- && intrinsic. name == sym:: write_via_move =>
373+ && matches ! ( intrinsic. name, sym:: write_via_move | sym :: init_box_via_move ) =>
375374 {
376375 // We still have to evaluate the callee expression as normal (but we don't care
377376 // about its result).
378377 let _fun = unpack ! ( block = this. as_local_operand( block, fun) ) ;
379- // The destination must have unit type (so we don't actually have to store anything
380- // into it).
381- assert ! ( destination. ty( & this. local_decls, this. tcx) . ty. is_unit( ) ) ;
382378
383- // Compile this to an assignment of the argument into the destination.
384- let [ ptr, val] = * * args else {
385- span_bug ! ( expr_span, "invalid write_via_move call" )
386- } ;
387- let Some ( ptr) = unpack ! ( block = this. as_local_operand( block, ptr) ) . place ( ) else {
388- span_bug ! ( expr_span, "invalid write_via_move call" )
389- } ;
390- let ptr_deref = ptr. project_deeper ( & [ ProjectionElem :: Deref ] , this. tcx ) ;
391- this. expr_into_dest ( ptr_deref, block, val)
379+ match intrinsic. name {
380+ sym:: write_via_move => {
381+ // `write_via_move(ptr, val)` becomes `*ptr = val` but without any dropping.
382+
383+ // The destination must have unit type (so we don't actually have to store anything
384+ // into it).
385+ assert ! ( destination. ty( & this. local_decls, this. tcx) . ty. is_unit( ) ) ;
386+
387+ // Compile this to an assignment of the argument into the destination.
388+ let [ ptr, val] = * * args else {
389+ span_bug ! ( expr_span, "invalid write_via_move call" )
390+ } ;
391+ let Some ( ptr) = unpack ! ( block = this. as_local_operand( block, ptr) ) . place ( )
392+ else {
393+ span_bug ! ( expr_span, "invalid write_via_move call" )
394+ } ;
395+ let ptr_deref = ptr. project_deeper ( & [ ProjectionElem :: Deref ] , this. tcx ) ;
396+ this. expr_into_dest ( ptr_deref, block, val)
397+ }
398+ sym:: init_box_via_move => {
399+ // `write_via_move(b, val)` becomes
400+ // ```
401+ // *transmute::<_, *mut T>(b) = val;
402+ // transmute::<_, Box<T>>(b)
403+ // ```
404+ let t = generic_args. type_at ( 0 ) ;
405+ let [ b, val] = * * args else {
406+ span_bug ! ( expr_span, "invalid init_box_via_move call" )
407+ } ;
408+ let Some ( b) = unpack ! ( block = this. as_local_operand( block, b) ) . place ( )
409+ else {
410+ span_bug ! ( expr_span, "invalid init_box_via_move call" )
411+ } ;
412+ // Project to the pointer inside `b`. We have to keep `b` in scope to ensure
413+ // it gets dropped. After the first projection we can transmute which is
414+ // easier.
415+ let ty:: Adt ( box_adt_def, box_adt_args) =
416+ b. ty ( & this. local_decls , this. tcx ) . ty . kind ( )
417+ else {
418+ span_bug ! ( expr_span, "invalid init_box_via_move call" )
419+ } ;
420+ let unique_field =
421+ this. tcx . adt_def ( box_adt_def. did ( ) ) . non_enum_variant ( ) . fields
422+ [ rustc_abi:: FieldIdx :: ZERO ]
423+ . did ;
424+ let Some ( unique_def) =
425+ this. tcx . type_of ( unique_field) . instantiate_identity ( ) . ty_adt_def ( )
426+ else {
427+ span_bug ! (
428+ this. tcx. def_span( unique_field) ,
429+ "expected Box to contain Unique"
430+ )
431+ } ;
432+ let unique_ty =
433+ Ty :: new_adt ( this. tcx , unique_def, this. tcx . mk_args ( & [ box_adt_args[ 0 ] ] ) ) ;
434+ let b_field = b. project_deeper (
435+ & [ ProjectionElem :: Field ( rustc_abi:: FieldIdx :: ZERO , unique_ty) ] ,
436+ this. tcx ,
437+ ) ;
438+ // `ptr` is `b` transmuted to `*mut T`.
439+ let ptr_ty = Ty :: new_mut_ptr ( this. tcx , t) ;
440+ let ptr = this. local_decls . push ( LocalDecl :: new ( ptr_ty, expr_span) ) ;
441+ this. cfg . push (
442+ block,
443+ Statement :: new ( source_info, StatementKind :: StorageLive ( ptr) ) ,
444+ ) ;
445+ this. cfg . push_assign (
446+ block,
447+ source_info,
448+ Place :: from ( ptr) ,
449+ // Needs to be a `Copy` so that `b` still gets dropped if `val` panics.
450+ Rvalue :: Cast ( CastKind :: Transmute , Operand :: Copy ( b_field) , ptr_ty) ,
451+ ) ;
452+ // Store `val` into `ptr`.
453+ let ptr_deref =
454+ Place :: from ( ptr) . project_deeper ( & [ ProjectionElem :: Deref ] , this. tcx ) ;
455+ unpack ! ( block = this. expr_into_dest( ptr_deref, block, val) ) ;
456+ // Return `ptr` transmuted to `Box<T>`.
457+ this. cfg . push_assign (
458+ block,
459+ source_info,
460+ destination,
461+ Rvalue :: Cast (
462+ CastKind :: Transmute ,
463+ // Move from `b` so that does not get dropped any more.
464+ Operand :: Move ( b) ,
465+ Ty :: new_box ( this. tcx , t) ,
466+ ) ,
467+ ) ;
468+ // We don't need `ptr` any more.
469+ this. cfg . push (
470+ block,
471+ Statement :: new ( source_info, StatementKind :: StorageDead ( ptr) ) ,
472+ ) ;
473+ block. unit ( )
474+ }
475+ _ => rustc_middle:: bug!( ) ,
476+ }
392477 }
393478 ExprKind :: Call { ty : _, fun, ref args, from_hir_call, fn_span } => {
394479 let fun = unpack ! ( block = this. as_local_operand( block, fun) ) ;
0 commit comments