@@ -126,7 +126,7 @@ pub struct OperandRef<'tcx, V> {
126126 pub layout : TyAndLayout < ' tcx > ,
127127}
128128
129- impl < V : CodegenObject > fmt:: Debug for OperandRef < ' _ , V > {
129+ impl < V : fmt :: Debug > fmt:: Debug for OperandRef < ' _ , V > {
130130 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
131131 write ! ( f, "OperandRef({:?} @ {:?})" , self . val, self . layout)
132132 }
@@ -319,16 +319,40 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
319319 OperandRef { val, layout }
320320 }
321321
322+ /// Extracts field `field_idx` from `self`, after downcasting to
323+ /// `downcast_variant` if it's specified.
324+ ///
325+ /// Reading from things like tuples or structs, which are always single-variant,
326+ /// don't need to pass a downcast variant since downcasting them to
327+ /// `FIRST_VARIANT` doesn't actually change anything.
328+ /// Things like enums and coroutines, though, must pass the variant from which
329+ /// they want to read unless they're specifically reading the tag field
330+ /// (which is the index at the type level, not inside one of the variants).
322331 pub ( crate ) fn extract_field < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
323332 & self ,
324333 fx : & mut FunctionCx < ' a , ' tcx , Bx > ,
325334 bx : & mut Bx ,
326- i : usize ,
335+ downcast_variant : Option < VariantIdx > ,
336+ field_idx : FieldIdx ,
327337 ) -> Self {
328- let field = self . layout . field ( bx. cx ( ) , i) ;
329- let offset = self . layout . fields . offset ( i) ;
338+ let layout = if let Some ( vidx) = downcast_variant {
339+ self . layout . for_variant ( bx. cx ( ) , vidx)
340+ } else {
341+ debug_assert ! (
342+ match self . layout. variants {
343+ Variants :: Empty => true ,
344+ Variants :: Single { index } => index == FIRST_VARIANT ,
345+ Variants :: Multiple { tag_field, .. } => tag_field == field_idx,
346+ } ,
347+ "Should have specified a variant to read field {field_idx:?} of {self:?}" ,
348+ ) ;
349+ self . layout
350+ } ;
351+
352+ let field = layout. field ( bx. cx ( ) , field_idx. as_usize ( ) ) ;
353+ let offset = layout. fields . offset ( field_idx. as_usize ( ) ) ;
330354
331- if !bx. is_backend_ref ( self . layout ) && bx. is_backend_ref ( field) {
355+ if !bx. is_backend_ref ( layout) && bx. is_backend_ref ( field) {
332356 // Part of https://github.com/rust-lang/compiler-team/issues/838
333357 span_bug ! (
334358 fx. mir. span,
@@ -338,27 +362,35 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
338362
339363 let val = if field. is_zst ( ) {
340364 OperandValue :: ZeroSized
341- } else if let BackendRepr :: SimdVector { .. } = self . layout . backend_repr {
365+ } else if let BackendRepr :: SimdVector { .. } = layout. backend_repr {
342366 // codegen_transmute_operand doesn't support SIMD, but since the previous
343367 // check handled ZSTs, the only possible field access into something SIMD
344368 // is to the `non_1zst_field` that's the same SIMD. (Other things, even
345369 // just padding, would change the wrapper's representation type.)
346- assert_eq ! ( field. size, self . layout. size) ;
370+ assert_eq ! ( field. size, layout. size) ;
347371 self . val
348- } else if field. size == self . layout . size {
349- assert_eq ! ( offset. bytes( ) , 0 ) ;
350- fx. codegen_transmute_operand ( bx, * self , field)
372+ } else if field. size == layout. size {
373+ debug_assert_eq ! ( offset. bytes( ) , 0 ) ;
374+ if downcast_variant. is_some ( ) || self . layout . ty . is_union ( ) {
375+ fx. codegen_transmute_operand ( bx, * self , field)
376+ } else {
377+ self . val
378+ }
351379 } else {
352- let ( in_scalar, imm) = match ( self . val , self . layout . backend_repr ) {
380+ let ( in_scalar, imm) = match ( self . val , layout. backend_repr ) {
353381 // Extract a scalar component from a pair.
354382 ( OperandValue :: Pair ( a_llval, b_llval) , BackendRepr :: ScalarPair ( a, b) ) => {
355- if offset. bytes ( ) == 0 {
383+ // This needs to look at `offset`, rather than `i`, because
384+ // for a type like `Option<u32>`, the first thing in the pair
385+ // is the tag, so `(_2 as Some).0` needs to read the *second*
386+ // thing in the pair despite it being "field zero".
387+ if offset == Size :: ZERO {
356388 assert_eq ! ( field. size, a. size( bx. cx( ) ) ) ;
357- ( Some ( a ) , a_llval)
389+ ( a , a_llval)
358390 } else {
359391 assert_eq ! ( offset, a. size( bx. cx( ) ) . align_to( b. align( bx. cx( ) ) . abi) ) ;
360392 assert_eq ! ( field. size, b. size( bx. cx( ) ) ) ;
361- ( Some ( b ) , b_llval)
393+ ( b , b_llval)
362394 }
363395 }
364396
@@ -367,30 +399,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
367399 }
368400 } ;
369401 OperandValue :: Immediate ( match field. backend_repr {
370- BackendRepr :: SimdVector { .. } => imm,
371- BackendRepr :: Scalar ( out_scalar) => {
372- let Some ( in_scalar) = in_scalar else {
373- span_bug ! (
374- fx. mir. span,
375- "OperandRef::extract_field({:?}): missing input scalar for output scalar" ,
376- self
377- )
378- } ;
379- if in_scalar != out_scalar {
380- // If the backend and backend_immediate types might differ,
381- // flip back to the backend type then to the new immediate.
382- // This avoids nop truncations, but still handles things like
383- // Bools in union fields needs to be truncated.
384- let backend = bx. from_immediate ( imm) ;
385- bx. to_immediate_scalar ( backend, out_scalar)
386- } else {
387- imm
388- }
402+ BackendRepr :: Scalar ( out_scalar) if downcast_variant. is_some ( ) => {
403+ // For a type like `Result<usize, &u32>` the layout is `Pair(i64, ptr)`.
404+ // But if we're reading the `Ok` payload, we need to turn that `ptr`
405+ // back into an integer. To avoid repeating logic we do that by
406+ // calling the transmute code, which is legal thanks to the size
407+ // assert we did when pulling it out of the pair.
408+ transmute_scalar ( bx, imm, in_scalar, out_scalar)
389409 }
410+ BackendRepr :: Scalar ( _) | BackendRepr :: SimdVector { .. } => imm,
390411 BackendRepr :: ScalarPair ( _, _) | BackendRepr :: Memory { .. } => bug ! ( ) ,
391412 } )
392413 } ;
393-
394414 OperandRef { val, layout : field }
395415 }
396416
@@ -438,7 +458,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
438458 let tag_op = match self . val {
439459 OperandValue :: ZeroSized => bug ! ( ) ,
440460 OperandValue :: Immediate ( _) | OperandValue :: Pair ( _, _) => {
441- self . extract_field ( fx, bx, tag_field. as_usize ( ) )
461+ self . extract_field ( fx, bx, None , tag_field)
442462 }
443463 OperandValue :: Ref ( place) => {
444464 let tag = place. with_type ( self . layout ) . project_field ( bx, tag_field. as_usize ( ) ) ;
@@ -929,34 +949,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
929949 ) -> Option < OperandRef < ' tcx , Bx :: Value > > {
930950 debug ! ( "maybe_codegen_consume_direct(place_ref={:?})" , place_ref) ;
931951
952+ let mut downcast_variant = None ;
932953 match self . locals [ place_ref. local ] {
933954 LocalRef :: Operand ( mut o) => {
934955 // Moves out of scalar and scalar pair fields are trivial.
935956 for elem in place_ref. projection . iter ( ) {
936- match elem {
957+ match * elem {
937958 mir:: ProjectionElem :: Field ( f, _) => {
938959 assert ! (
939960 !o. layout. ty. is_any_ptr( ) ,
940961 "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
941962 but tried to access field {f:?} of pointer {o:?}",
942963 ) ;
943- o = o. extract_field ( self , bx, f. index ( ) ) ;
964+ o = o. extract_field ( self , bx, downcast_variant, f) ;
965+ downcast_variant = None ;
944966 }
945- mir:: ProjectionElem :: Index ( _)
946- | mir:: ProjectionElem :: ConstantIndex { .. } => {
947- // ZSTs don't require any actual memory access.
948- // FIXME(eddyb) deduplicate this with the identical
949- // checks in `codegen_consume` and `extract_field`.
950- let elem = o. layout . field ( bx. cx ( ) , 0 ) ;
951- if elem. is_zst ( ) {
952- o = OperandRef :: zero_sized ( elem) ;
953- } else {
954- return None ;
955- }
967+ mir:: ProjectionElem :: Downcast ( _sym, variant_idx) => {
968+ downcast_variant = Some ( variant_idx) ;
956969 }
957970 _ => return None ,
958971 }
959972 }
973+ debug_assert_eq ! ( downcast_variant, None ) ;
960974
961975 Some ( o)
962976 }
0 commit comments