@@ -271,6 +271,11 @@ export function* emitStorageChainLoad(
271271 }
272272 }
273273
274+ // A struct is a copy in memory, not the slot's word
275+ if ( currentOrigin && Type . isStruct ( currentOrigin ) ) {
276+ return yield * emitStorageStructCopy ( currentSlot , currentOrigin , node ) ;
277+ }
278+
274279 // Check if the last access was a struct field to get packed field info
275280 let byteOffset = 0 ;
276281 let fieldSize = 32 ; // Default to full slot
@@ -305,6 +310,117 @@ export function* emitStorageChainLoad(
305310 return Ir . Value . temp ( loadTempId , valueType ) ;
306311}
307312
313+ /**
314+ * Copy a struct from storage, starting at `slot`, into new memory, and
315+ * return its address. A struct in memory has one word per field, in
316+ * order; a struct field is the address of its own copy.
317+ */
318+ export function * emitStorageStructCopy (
319+ slot : Ir . Value ,
320+ struct : Type . Struct ,
321+ node : Ast . Node | undefined ,
322+ ) : Process < Ir . Value > {
323+ const debug = node ? yield * Process . Debug . forAstNode ( node ) : { } ;
324+ const uint256 = Ir . Type . Scalar . uint256 ;
325+ const constant = ( value : bigint ) => Ir . Value . constant ( value , uint256 ) ;
326+
327+ const address = yield * Process . Variables . newTemp ( ) ;
328+ yield * Process . Instructions . emit ( {
329+ kind : "allocate" ,
330+ location : "memory" ,
331+ size : constant ( BigInt ( struct . fields . size * 32 ) ) ,
332+ dest : address ,
333+ operationDebug : debug ,
334+ } as Ir . Instruction ) ;
335+
336+ let index = 0 ;
337+ for ( const [ name , fieldType ] of struct . fields ) {
338+ const layout = struct . layout . get ( name ) ;
339+ if ( ! layout ) {
340+ throw new Error ( `Field ${ name } not found in struct ${ struct . name } ` ) ;
341+ }
342+
343+ // The field's slot
344+ let fieldSlot = slot ;
345+ if ( layout . byteOffset >= 32 ) {
346+ const temp = yield * Process . Variables . newTemp ( ) ;
347+ yield * Process . Instructions . emit ( {
348+ kind : "compute_slot" ,
349+ slotKind : "field" ,
350+ base : slot ,
351+ fieldOffset : layout . byteOffset ,
352+ dest : temp ,
353+ operationDebug : debug ,
354+ } as Ir . Instruction . ComputeSlot ) ;
355+ fieldSlot = Ir . Value . temp ( temp , uint256 ) ;
356+ }
357+
358+ let value : Ir . Value ;
359+ if ( Type . isStruct ( fieldType ) ) {
360+ value = yield * emitStorageStructCopy ( fieldSlot , fieldType , node ) ;
361+ } else if (
362+ Type . isElementary ( fieldType ) &&
363+ ! ( Type . Elementary . isBytes ( fieldType ) && fieldType . size === undefined ) &&
364+ ! Type . Elementary . isString ( fieldType )
365+ ) {
366+ const irType = fromBugType ( fieldType ) ;
367+ const temp = yield * Process . Variables . newTemp ( ) ;
368+ yield * Process . Instructions . emit ( {
369+ kind : "read" ,
370+ location : "storage" ,
371+ slot : fieldSlot ,
372+ offset : constant ( BigInt ( layout . byteOffset % 32 ) ) ,
373+ length : constant ( BigInt ( getFieldSize ( irType ) ) ) ,
374+ type : irType ,
375+ dest : temp ,
376+ operationDebug : debug ,
377+ } as Ir . Instruction . Read ) ;
378+ value = Ir . Value . temp ( temp , irType ) ;
379+ } else {
380+ yield * Process . Errors . report (
381+ new IrgenError (
382+ `Cannot copy struct ${ struct . name } from storage: its field ` +
383+ `${ name } is a ${ Type . format ( fieldType ) } , and only value ` +
384+ `and struct fields can be copied to memory` ,
385+ node ?. loc ?? undefined ,
386+ Severity . Error ,
387+ ) ,
388+ ) ;
389+ value = constant ( 0n ) ;
390+ }
391+
392+ // The field's word in memory
393+ let offset = Ir . Value . temp ( address , uint256 ) ;
394+ if ( index > 0 ) {
395+ const temp = yield * Process . Variables . newTemp ( ) ;
396+ yield * Process . Instructions . emit (
397+ Ir . Instruction . ComputeOffset . field (
398+ "memory" ,
399+ offset ,
400+ name ,
401+ index * 32 ,
402+ temp ,
403+ debug ,
404+ ) ,
405+ ) ;
406+ offset = Ir . Value . temp ( temp , uint256 ) ;
407+ }
408+
409+ yield * Process . Instructions . emit ( {
410+ kind : "write" ,
411+ location : "memory" ,
412+ offset,
413+ length : constant ( 32n ) ,
414+ value,
415+ operationDebug : debug ,
416+ } as Ir . Instruction . Write ) ;
417+
418+ index ++ ;
419+ }
420+
421+ return Ir . Value . temp ( address , Ir . Type . Ref . memory ( ) ) ;
422+ }
423+
308424/**
309425 * Emit a storage write for an access chain
310426 */
0 commit comments