@@ -33,6 +33,9 @@ public static partial class CTypes {
3333 /// </summary>
3434 [ PythonType , PythonHidden ]
3535 public class StructType : PythonType , INativeType {
36+
37+ private enum LayoutKind { Msvc , Gcc }
38+
3639 [ DisallowNull ]
3740 internal Field [ ] ? _fields ; // not null after type construction completes
3841 private int ? _size , _alignment , _pack ;
@@ -252,6 +255,7 @@ internal static PythonType MakeSystemType(Type underlyingSystemType) {
252255 return PythonType . SetPythonType ( underlyingSystemType , new StructType ( underlyingSystemType ) ) ;
253256 }
254257
258+
255259 [ MemberNotNull ( nameof ( _fields ) , nameof ( _size ) , nameof ( _alignment ) ) ]
256260 private void SetFields ( object ? fields ) {
257261 lock ( this ) {
@@ -263,11 +267,12 @@ private void SetFields(object? fields) {
263267 List < Field > allFields = GetBaseSizeAlignmentAndFields ( out int size , out int alignment ) ;
264268
265269 IList < string > ? anonFields = GetAnonymousFields ( this ) ;
270+ LayoutKind layout = GetStructLayout ( this ) ;
266271
267272 foreach ( object fieldDef in fieldDefList ) {
268273 GetFieldInfo ( this , fieldDef , out string fieldName , out INativeType cdata , out bitCount ) ;
269274
270- int fieldOffset = UpdateSizeAndAlignment ( cdata , bitCount , ref lastType , ref size , ref alignment , ref curBitCount ) ;
275+ int fieldOffset = UpdateSizeAndAlignment ( cdata , bitCount , layout , ref lastType , ref size , ref alignment , ref curBitCount ) ;
271276
272277 var newField = new Field ( fieldName , cdata , fieldOffset , allFields . Count , bitCount , curBitCount - bitCount ) ;
273278 allFields . Add ( newField ) ;
@@ -292,6 +297,7 @@ private void SetFields(object? fields) {
292297 }
293298 }
294299
300+
295301 internal static void CheckAnonymousFields ( List < Field > allFields , IList < string > ? anonFields ) {
296302 if ( anonFields != null ) {
297303 foreach ( string s in anonFields ) {
@@ -365,9 +371,10 @@ private List<Field> GetBaseSizeAlignmentAndFields(out int size, out int alignmen
365371 foreach ( PythonType pt in BaseTypes ) {
366372 if ( pt is StructType st ) {
367373 st . EnsureFinal ( ) ;
374+ LayoutKind layout = GetStructLayout ( st ) ;
368375 foreach ( Field f in st . _fields ) {
369376 allFields . Add ( f ) ;
370- UpdateSizeAndAlignment ( f . NativeType , f . BitCount , ref lastType , ref size , ref alignment , ref totalBitCount ) ;
377+ UpdateSizeAndAlignment ( f . NativeType , f . BitCount , layout , ref lastType , ref size , ref alignment , ref totalBitCount ) ;
371378
372379 if ( f . NativeType == this ) {
373380 throw StructureCannotContainSelf ( ) ;
@@ -404,7 +411,7 @@ private List<Field> GetBaseSizeAlignmentAndFields(out int size, out int alignmen
404411 /// On return, the count is updated with the number of occupied bits.</param>
405412 /// <returns>
406413 /// The offset of the processed field within the struct. If the processed field was a bitfield, this is the offset of its container unit.</returns>
407- private int UpdateSizeAndAlignment ( INativeType cdata , int ? bitCount , ref INativeType ? lastType , ref int size , ref int alignment , ref int ? totalBitCount ) {
414+ private int UpdateSizeAndAlignment ( INativeType cdata , int ? bitCount , LayoutKind layout , ref INativeType ? lastType , ref int size , ref int alignment , ref int ? totalBitCount ) {
408415 int fieldOffset ;
409416 if ( bitCount != null ) {
410417 // process a bitfield
@@ -413,7 +420,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
413420
414421 if ( _pack != null ) throw new NotImplementedException ( "pack with bitfields" ) ; // TODO: implement
415422
416- if ( UseMsvcBitfieldAlignmentRules ) {
423+ if ( layout is LayoutKind . Msvc ) {
417424 if ( totalBitCount != null ) { // there is already a bitfield container open
418425 // under the MSVC rules, only bitfields of type that has the same size/alignment, are packed into the same container unit
419426 if ( lastType ! . Size != cdata . Size || lastType . Alignment != cdata . Alignment ) {
@@ -443,7 +450,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
443450 totalBitCount = bitCount ;
444451 lastType = cdata ;
445452 }
446- } else { // GCC bitfield alignment rules
453+ } else if ( layout is LayoutKind . Gcc ) {
447454 // under the GCC rules, all bitfields are packed into the same container unit or an overlapping container unit of a different type,
448455 // as long as they fit and match the alignment
449456 int containerOffset = AlignBack ( size , cdata . Alignment ) ; // TODO: _pack
@@ -460,6 +467,8 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
460467 fieldOffset = size = containerOffset ;
461468 totalBitCount = containerBitCount + bitCount ;
462469 lastType = cdata ;
470+ } else {
471+ throw new InvalidOperationException ( "unknown layout kind" ) ;
463472 }
464473 alignment = Math . Max ( alignment , lastType ! . Alignment ) ; // TODO: _pack
465474 } else {
@@ -500,6 +509,20 @@ internal void EnsureFinal() {
500509 }
501510 }
502511
512+
513+ private static LayoutKind GetStructLayout ( PythonType type ) {
514+ if ( type . TryGetBoundAttr ( type . Context . SharedContext , type , "_layout_" , out object layout ) && layout is not null ) {
515+ if ( Converter . TryConvertToString ( layout , out string ? layoutName ) ) {
516+ if ( layoutName . StartsWith ( "ms" , StringComparison . Ordinal ) ) return LayoutKind . Msvc ;
517+ if ( layoutName . StartsWith ( "gcc" , StringComparison . Ordinal ) ) return LayoutKind . Gcc ;
518+ }
519+ throw PythonOps . ValueError ( "unknown _layout_: {0}" , layout ) ;
520+ }
521+ // default layout for structs is platform dependent
522+ return RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? LayoutKind . Msvc : LayoutKind . Gcc ;
523+ }
524+
525+
503526 /// <summary>
504527 /// If our size/alignment hasn't been initialized then grabs the size/alignment
505528 /// from all of our base classes. If later new _fields_ are added we'll be
@@ -525,9 +548,6 @@ private void EnsureSizeAndAlignment() {
525548
526549 private static int AlignBack ( int length , int size )
527550 => length & ~ ( size - 1 ) ;
528-
529- private static bool UseMsvcBitfieldAlignmentRules
530- => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
531551 }
532552 }
533553}
0 commit comments