11//! Functions concerning immediate values and operands, and reading from operands.
22//! All high-level functions to read from memory work on operands as sources.
33
4- use std:: convert:: TryFrom ;
54use std:: fmt:: Write ;
65
76use rustc_hir:: def:: Namespace ;
@@ -15,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants};
1514
1615use super :: {
1716 alloc_range, from_known_layout, mir_assign_valid_types, AllocId , ConstValue , Frame , GlobalId ,
18- InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , Place , PlaceTy , Pointer ,
17+ InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , MemPlaceMeta , Place , PlaceTy , Pointer ,
1918 PointerArithmetic , Provenance , Scalar , ScalarMaybeUninit ,
2019} ;
2120
@@ -253,6 +252,11 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
253252 ImmTy { imm, layout }
254253 }
255254
255+ #[ inline]
256+ pub fn uninit ( layout : TyAndLayout < ' tcx > ) -> Self {
257+ ImmTy { imm : Immediate :: Uninit , layout }
258+ }
259+
256260 #[ inline]
257261 pub fn try_from_uint ( i : impl Into < u128 > , layout : TyAndLayout < ' tcx > ) -> Option < Self > {
258262 Some ( Self :: from_scalar ( Scalar :: try_from_uint ( i, layout. size ) ?, layout) )
@@ -280,6 +284,41 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
280284 }
281285}
282286
287+ impl < ' tcx , Tag : Provenance > OpTy < ' tcx , Tag > {
288+ pub fn len ( & self , cx : & impl HasDataLayout ) -> InterpResult < ' tcx , u64 > {
289+ if self . layout . is_unsized ( ) {
290+ // There are no unsized immediates.
291+ self . assert_mem_place ( ) . len ( cx)
292+ } else {
293+ match self . layout . fields {
294+ abi:: FieldsShape :: Array { count, .. } => Ok ( count) ,
295+ _ => bug ! ( "len not supported on sized type {:?}" , self . layout. ty) ,
296+ }
297+ }
298+ }
299+
300+ pub fn offset (
301+ & self ,
302+ offset : Size ,
303+ meta : MemPlaceMeta < Tag > ,
304+ layout : TyAndLayout < ' tcx > ,
305+ cx : & impl HasDataLayout ,
306+ ) -> InterpResult < ' tcx , Self > {
307+ match self . try_as_mplace ( ) {
308+ Ok ( mplace) => Ok ( mplace. offset ( offset, meta, layout, cx) ?. into ( ) ) ,
309+ Err ( imm) => {
310+ assert ! (
311+ matches!( * imm, Immediate :: Uninit ) ,
312+ "Scalar/ScalarPair cannot be offset into"
313+ ) ;
314+ assert ! ( !meta. has_meta( ) ) ; // no place to store metadata here
315+ // Every part of an uninit is uninit.
316+ Ok ( ImmTy :: uninit ( layout) . into ( ) )
317+ }
318+ }
319+ }
320+ }
321+
283322impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
284323 /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
285324 /// Returns `None` if the layout does not permit loading this as a value.
@@ -296,11 +335,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
296335 }
297336
298337 let Some ( alloc) = self . get_place_alloc ( mplace) ? else {
299- return Ok ( Some ( ImmTy {
300- // zero-sized type can be left uninit
301- imm : Immediate :: Uninit ,
302- layout : mplace. layout ,
303- } ) ) ;
338+ // zero-sized type can be left uninit
339+ return Ok ( Some ( ImmTy :: uninit ( mplace. layout ) ) ) ;
304340 } ;
305341
306342 // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@@ -367,6 +403,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
367403 /// This flag exists only for validity checking.
368404 ///
369405 /// This is an internal function that should not usually be used; call `read_immediate` instead.
406+ /// ConstProp needs it, though.
370407 pub fn read_immediate_raw (
371408 & self ,
372409 src : & OpTy < ' tcx , M :: PointerTag > ,
@@ -421,123 +458,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
421458 Ok ( str)
422459 }
423460
424- /// Projection functions
425- pub fn operand_field (
426- & self ,
427- op : & OpTy < ' tcx , M :: PointerTag > ,
428- field : usize ,
429- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
430- let base = match op. try_as_mplace ( ) {
431- Ok ( ref mplace) => {
432- // We can reuse the mplace field computation logic for indirect operands.
433- let field = self . mplace_field ( mplace, field) ?;
434- return Ok ( field. into ( ) ) ;
435- }
436- Err ( value) => value,
437- } ;
438-
439- let field_layout = base. layout . field ( self , field) ;
440- let offset = base. layout . fields . offset ( field) ;
441- // This makes several assumptions about what layouts we will encounter; we match what
442- // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
443- let field_val: Immediate < _ > = match ( * base, base. layout . abi ) {
444- // the field contains no information, can be left uninit
445- _ if field_layout. is_zst ( ) => Immediate :: Uninit ,
446- // the field covers the entire type
447- _ if field_layout. size == base. layout . size => {
448- assert ! ( match ( base. layout. abi, field_layout. abi) {
449- ( Abi :: Scalar ( ..) , Abi :: Scalar ( ..) ) => true ,
450- ( Abi :: ScalarPair ( ..) , Abi :: ScalarPair ( ..) ) => true ,
451- _ => false ,
452- } ) ;
453- assert ! ( offset. bytes( ) == 0 ) ;
454- * base
455- }
456- // extract fields from types with `ScalarPair` ABI
457- ( Immediate :: ScalarPair ( a_val, b_val) , Abi :: ScalarPair ( a, b) ) => {
458- assert ! ( matches!( field_layout. abi, Abi :: Scalar ( ..) ) ) ;
459- Immediate :: from ( if offset. bytes ( ) == 0 {
460- debug_assert_eq ! ( field_layout. size, a. size( self ) ) ;
461- a_val
462- } else {
463- debug_assert_eq ! ( offset, a. size( self ) . align_to( b. align( self ) . abi) ) ;
464- debug_assert_eq ! ( field_layout. size, b. size( self ) ) ;
465- b_val
466- } )
467- }
468- _ => span_bug ! (
469- self . cur_span( ) ,
470- "invalid field access on immediate {}, layout {:#?}" ,
471- base,
472- base. layout
473- ) ,
474- } ;
475-
476- Ok ( OpTy { op : Operand :: Immediate ( field_val) , layout : field_layout, align : None } )
477- }
478-
479- pub fn operand_index (
480- & self ,
481- op : & OpTy < ' tcx , M :: PointerTag > ,
482- index : u64 ,
483- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
484- if let Ok ( index) = usize:: try_from ( index) {
485- // We can just treat this as a field.
486- self . operand_field ( op, index)
487- } else {
488- // Indexing into a big array. This must be an mplace.
489- let mplace = op. assert_mem_place ( ) ;
490- Ok ( self . mplace_index ( & mplace, index) ?. into ( ) )
491- }
492- }
493-
494- pub fn operand_downcast (
495- & self ,
496- op : & OpTy < ' tcx , M :: PointerTag > ,
497- variant : VariantIdx ,
498- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
499- Ok ( match op. try_as_mplace ( ) {
500- Ok ( ref mplace) => self . mplace_downcast ( mplace, variant) ?. into ( ) ,
501- Err ( ..) => {
502- // Downcasts only change the layout.
503- // (In particular, no check about whether this is even the active variant -- that's by design,
504- // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
505- let layout = op. layout . for_variant ( self , variant) ;
506- OpTy { layout, ..* op }
507- }
508- } )
509- }
510-
511- #[ instrument( skip( self ) , level = "debug" ) ]
512- pub fn operand_projection (
513- & self ,
514- base : & OpTy < ' tcx , M :: PointerTag > ,
515- proj_elem : mir:: PlaceElem < ' tcx > ,
516- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
517- use rustc_middle:: mir:: ProjectionElem :: * ;
518- Ok ( match proj_elem {
519- Field ( field, _) => self . operand_field ( base, field. index ( ) ) ?,
520- Downcast ( _, variant) => self . operand_downcast ( base, variant) ?,
521- Deref => self . deref_operand ( base) ?. into ( ) ,
522- Subslice { .. } | ConstantIndex { .. } | Index ( _) => {
523- // The rest should only occur as mplace, we do not use Immediates for types
524- // allowing such operations. This matches place_projection forcing an allocation.
525- let mplace = base. assert_mem_place ( ) ;
526- self . mplace_projection ( & mplace, proj_elem) ?. into ( )
527- }
528- } )
529- }
530-
531461 /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
532462 /// Also returns the number of elements.
463+ ///
464+ /// Can (but does not always) trigger UB if `op` is uninitialized.
533465 pub fn operand_to_simd (
534466 & self ,
535- base : & OpTy < ' tcx , M :: PointerTag > ,
467+ op : & OpTy < ' tcx , M :: PointerTag > ,
536468 ) -> InterpResult < ' tcx , ( MPlaceTy < ' tcx , M :: PointerTag > , u64 ) > {
537469 // Basically we just transmute this place into an array following simd_size_and_type.
538470 // This only works in memory, but repr(simd) types should never be immediates anyway.
539- assert ! ( base. layout. ty. is_simd( ) ) ;
540- self . mplace_to_simd ( & base. assert_mem_place ( ) )
471+ assert ! ( op. layout. ty. is_simd( ) ) ;
472+ match op. try_as_mplace ( ) {
473+ Ok ( mplace) => self . mplace_to_simd ( & mplace) ,
474+ Err ( imm) => match * imm {
475+ Immediate :: Uninit => {
476+ throw_ub ! ( InvalidUninitBytes ( None ) )
477+ }
478+ Immediate :: Scalar ( ..) | Immediate :: ScalarPair ( ..) => {
479+ bug ! ( "arrays/slices can never have Scalar/ScalarPair layout" )
480+ }
481+ } ,
482+ }
541483 }
542484
543485 /// Read from a local. Will not actually access the local if reading from a ZST.
@@ -582,30 +524,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
582524 /// avoid allocations.
583525 pub fn eval_place_to_op (
584526 & self ,
585- place : mir:: Place < ' tcx > ,
527+ mir_place : mir:: Place < ' tcx > ,
586528 layout : Option < TyAndLayout < ' tcx > > ,
587529 ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
588530 // Do not use the layout passed in as argument if the base we are looking at
589531 // here is not the entire place.
590- let layout = if place . projection . is_empty ( ) { layout } else { None } ;
532+ let layout = if mir_place . projection . is_empty ( ) { layout } else { None } ;
591533
592- let base_op = self . local_to_op ( self . frame ( ) , place. local , layout) ?;
593-
594- let op = place
595- . projection
596- . iter ( )
597- . try_fold ( base_op, |op, elem| self . operand_projection ( & op, elem) ) ?;
534+ let mut op = self . local_to_op ( self . frame ( ) , mir_place. local , layout) ?;
535+ // Using `try_fold` turned out to be bad for performance, hence the loop.
536+ for elem in mir_place. projection . iter ( ) {
537+ op = self . operand_projection ( & op, elem) ?
538+ }
598539
599540 trace ! ( "eval_place_to_op: got {:?}" , * op) ;
600541 // Sanity-check the type we ended up with.
601- debug_assert ! ( mir_assign_valid_types(
602- * self . tcx,
603- self . param_env,
604- self . layout_of( self . subst_from_current_frame_and_normalize_erasing_regions(
605- place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
606- ) ?) ?,
607- op. layout,
608- ) ) ;
542+ debug_assert ! (
543+ mir_assign_valid_types(
544+ * self . tcx,
545+ self . param_env,
546+ self . layout_of( self . subst_from_current_frame_and_normalize_erasing_regions(
547+ mir_place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
548+ ) ?) ?,
549+ op. layout,
550+ ) ,
551+ "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}" ,
552+ mir_place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty,
553+ op. layout. ty,
554+ ) ;
609555 Ok ( op)
610556 }
611557
0 commit comments