@@ -49,8 +49,8 @@ def __init__(self: struct_impl, resolver: Resolver | None = None, **kwargs: ...)
4949 # struct overrides the __init__ method, so we need to call the parent class __init__ method
5050 obj .__init__ (self , resolver )
5151
52- self . _struct_name = self .__class__ .__name__
53- self . _members = {}
52+ object . __setattr__ ( self , " _struct_name" , self .__class__ .__name__ )
53+ object . __setattr__ ( self , " _members" , {})
5454
5555 reference_type = self ._reference_struct
5656 self ._inflate_struct_attributes (self ._inflater , resolver , reference_type )
@@ -69,6 +69,17 @@ def __getattribute__(self: struct_impl, name: str) -> object:
6969 pass
7070 return super ().__getattribute__ (name )
7171
72+ def __setattr__ (self : struct_impl , name : str , value : object ) -> None :
73+ """Set an attribute, delegating to member.value for struct fields."""
74+ try :
75+ members = object .__getattribute__ (self , "_members" )
76+ if name in members :
77+ members [name ].value = value
78+ return
79+ except AttributeError :
80+ pass
81+ object .__setattr__ (self , name , value )
82+
7283 def __new__ (cls : struct_impl , * args : ..., ** kwargs : ...) -> Self :
7384 """Create a new struct."""
7485 # Skip the __new__ method of the parent class
@@ -82,9 +93,10 @@ def _inflate_struct_attributes(
8293 reference_type : type ,
8394 ) -> None :
8495 current_offset = 0
96+ max_alignment = 1
8597 bf_tracker = BitfieldTracker ()
8698 aligned = getattr (reference_type , "_aligned_" , False )
87- self . _member_offsets = {}
99+ object . __setattr__ ( self , " _member_offsets" , {})
88100
89101 for name , annotation , reference in iterate_annotation_chain (reference_type , terminate_at = struct ):
90102 if name == "_aligned_" :
@@ -103,7 +115,9 @@ def _inflate_struct_attributes(
103115 if bitfield_field :
104116 if aligned and bf_tracker .needs_new_group (bitfield_field ):
105117 current_offset += bf_tracker .flush ()
106- current_offset = _align_offset (current_offset , alignment_of (bitfield_field .backing_type ))
118+ field_align = alignment_of (bitfield_field .backing_type )
119+ max_alignment = max (max_alignment , field_align )
120+ current_offset = _align_offset (current_offset , field_align )
107121 self ._member_offsets [name ] = current_offset
108122 result , offset_delta = bf_tracker .create_bitfield (
109123 bitfield_field , inflater , resolver , current_offset ,
@@ -112,7 +126,18 @@ def _inflate_struct_attributes(
112126 else :
113127 current_offset += bf_tracker .flush ()
114128 if aligned and explicit_offset is None :
115- current_offset = _align_offset (current_offset , alignment_of (resolved_type ))
129+ # Try alignment from the resolved type directly; for closures
130+ # (e.g. union inflaters) alignment_of can't inspect them, so
131+ # fall back to creating a probe instance.
132+ field_align = alignment_of (resolved_type )
133+ if field_align <= 1 :
134+ try :
135+ probe = resolved_type (resolver .relative_from_own (current_offset , 0 ))
136+ field_align = alignment_of (probe )
137+ except (ValueError , TypeError ):
138+ pass
139+ max_alignment = max (max_alignment , field_align )
140+ current_offset = _align_offset (current_offset , field_align )
116141 self ._member_offsets [name ] = current_offset
117142 result = resolved_type (resolver .relative_from_own (current_offset , 0 ))
118143 current_offset += size_of (result )
@@ -121,16 +146,22 @@ def _inflate_struct_attributes(
121146
122147 current_offset += bf_tracker .flush ()
123148
149+ # Apply tail padding for aligned structs
150+ if aligned :
151+ if isinstance (aligned , int ) and aligned is not True :
152+ max_alignment = max (max_alignment , aligned )
153+ current_offset = _align_offset (current_offset , max_alignment )
154+
124155 # For VLA structs, size must be computed dynamically since the count
125156 # can change at runtime. Detect VLA by duck-typing: vla_impl has a
126157 # _count_member attribute that plain array_impl does not.
127158 members = object .__getattribute__ (self , "_members" )
128159 last_member = list (members .values ())[- 1 ] if members else None
129160 if last_member is not None and hasattr (last_member , "_count_member" ):
130- last_name = list (self . _members .keys ())[- 1 ]
131- self . _vla_fixed_offset = self ._member_offsets [last_name ]
161+ last_name = list (members .keys ())[- 1 ]
162+ object . __setattr__ ( self , " _vla_fixed_offset" , self ._member_offsets [last_name ])
132163 else :
133- self . size = current_offset
164+ object . __setattr__ ( self , " size" , current_offset )
134165
135166 @staticmethod
136167 def _resolve_field (
@@ -316,7 +347,7 @@ def freeze(self: struct_impl) -> None:
316347
317348 def reset (self : struct_impl ) -> None :
318349 """Reset each member to its frozen value."""
319- if not self . _frozen :
350+ if not object . __getattribute__ ( self , " _frozen" ) :
320351 raise RuntimeError ("Cannot reset a struct that has not been frozen." )
321352
322353 members = object .__getattribute__ (self , "_members" )
0 commit comments