@@ -365,30 +365,112 @@ 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+ // Make sure `StorageDead` gets emitted.
446+ this. schedule_drop_storage_and_value ( expr_span, this. local_scope ( ) , ptr) ;
447+ this. cfg . push_assign (
448+ block,
449+ source_info,
450+ Place :: from ( ptr) ,
451+ // Needs to be a `Copy` so that `b` still gets dropped if `val` panics.
452+ Rvalue :: Cast ( CastKind :: Transmute , Operand :: Copy ( b_field) , ptr_ty) ,
453+ ) ;
454+ // Store `val` into `ptr`.
455+ let ptr_deref =
456+ Place :: from ( ptr) . project_deeper ( & [ ProjectionElem :: Deref ] , this. tcx ) ;
457+ unpack ! ( block = this. expr_into_dest( ptr_deref, block, val) ) ;
458+ // Return `ptr` transmuted to `Box<T>`.
459+ this. cfg . push_assign (
460+ block,
461+ source_info,
462+ destination,
463+ Rvalue :: Cast (
464+ CastKind :: Transmute ,
465+ // Move from `b` so that does not get dropped any more.
466+ Operand :: Move ( b) ,
467+ Ty :: new_box ( this. tcx , t) ,
468+ ) ,
469+ ) ;
470+ block. unit ( )
471+ }
472+ _ => rustc_middle:: bug!( ) ,
473+ }
392474 }
393475 ExprKind :: Call { ty : _, fun, ref args, from_hir_call, fn_span } => {
394476 let fun = unpack ! ( block = this. as_local_operand( block, fun) ) ;
0 commit comments