11using System ;
2+ using System . Collections . Generic ;
23using System . Diagnostics ;
4+ using System . Linq ;
5+ using System . Reflection ;
36using System . Runtime . InteropServices ;
47using System . Runtime . Serialization ;
58
@@ -79,41 +82,80 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
7982 BorrowedReference bases = Runtime . PyTuple_GetItem ( args , 1 ) ;
8083 BorrowedReference dict = Runtime . PyTuple_GetItem ( args , 2 ) ;
8184
82- // We do not support multiple inheritance, so the bases argument
83- // should be a 1-item tuple containing the type we are subtyping.
84- // That type must itself have a managed implementation. We check
85- // that by making sure its metatype is the CLR metatype.
85+ // Extract interface types and base class types.
86+ var interfaces = new List < Type > ( ) ;
8687
87- if ( Runtime . PyTuple_Size ( bases ) != 1 )
88- {
89- return Exceptions . RaiseTypeError ( "cannot use multiple inheritance with managed classes" ) ;
90- }
88+ // More than one base type case be declared, but an exception will be thrown
89+ // if more than one is a class/not an interface.
90+ var baseTypes = new List < ClassBase > ( ) ;
9191
92- BorrowedReference base_type = Runtime . PyTuple_GetItem ( bases , 0 ) ;
93- BorrowedReference mt = Runtime . PyObject_TYPE ( base_type ) ;
94-
95- if ( ! ( mt == PyCLRMetaType || mt == Runtime . PyTypeType ) )
92+ var baseClassCount = Runtime . PyTuple_Size ( bases ) ;
93+ if ( baseClassCount == 0 )
9694 {
97- return Exceptions . RaiseTypeError ( "invalid metatype " ) ;
95+ return Exceptions . RaiseTypeError ( "zero base classes " ) ;
9896 }
9997
100- // Ensure that the reflected type is appropriate for subclassing,
101- // disallowing subclassing of delegates, enums and array types.
102-
103- if ( GetManagedObject ( base_type ) is ClassBase cb )
98+ for ( nint i = 0 ; i < baseClassCount ; i ++ )
10499 {
105- try
100+ var baseTypeIt = Runtime . PyTuple_GetItem ( bases , ( int ) i ) ;
101+
102+ if ( GetManagedObject ( baseTypeIt ) is ClassBase classBaseIt )
106103 {
107- if ( ! cb . CanSubclass ( ) )
104+ if ( ! classBaseIt . type . Valid )
105+ {
106+ return Exceptions . RaiseTypeError ( "Invalid type used as a super type." ) ;
107+ }
108+ if ( classBaseIt . type . Value . IsInterface )
108109 {
109- return Exceptions . RaiseTypeError ( "delegates, enums and array types cannot be subclassed" ) ;
110+ interfaces . Add ( classBaseIt . type . Value ) ;
110111 }
112+ else
113+ {
114+ baseTypes . Add ( classBaseIt ) ;
115+ }
116+ }
117+ else
118+ {
119+ return Exceptions . RaiseTypeError ( "Non .NET type used as super class for meta type. This is not supported." ) ;
111120 }
112- catch ( SerializationException )
121+ }
122+ // if the base type count is 0, there might still be interfaces to implement.
123+ if ( baseTypes . Count == 0 )
124+ {
125+ baseTypes . Add ( new ClassBase ( typeof ( object ) ) ) ;
126+ }
127+
128+ // Multiple inheritance is not supported, unless the other types are interfaces
129+ if ( baseTypes . Count > 1 )
130+ {
131+ var types = string . Join ( ", " , baseTypes . Select ( baseType => baseType . type . Value ) ) ;
132+ return Exceptions . RaiseTypeError ( $ "Multiple inheritance with managed classes cannot be used. Types: { types } ") ;
133+ }
134+
135+ // check if the list of interfaces contains no duplicates.
136+ if ( interfaces . Distinct ( ) . Count ( ) != interfaces . Count )
137+ {
138+ // generate a string containing the problematic types.
139+ var duplicateTypes = interfaces . GroupBy ( type => type )
140+ . Where ( typeGroup => typeGroup . Count ( ) > 1 )
141+ . Select ( typeGroup => typeGroup . Key ) ;
142+ var duplicateTypesString = string . Join ( ", " , duplicateTypes ) ;
143+
144+ return Exceptions . RaiseTypeError ( $ "An interface can only be implemented once. Duplicate types: { duplicateTypesString } ") ;
145+ }
146+
147+ var cb = baseTypes [ 0 ] ;
148+ try
149+ {
150+ if ( ! cb . CanSubclass ( ) )
113151 {
114- return Exceptions . RaiseTypeError ( $ "Underlying C# Base class { cb . type } has been deleted ") ;
152+ return Exceptions . RaiseTypeError ( "delegates, enums and array types cannot be subclassed ") ;
115153 }
116154 }
155+ catch ( SerializationException )
156+ {
157+ return Exceptions . RaiseTypeError ( $ "Underlying C# Base class { cb . type } has been deleted") ;
158+ }
117159
118160 BorrowedReference slots = Runtime . PyDict_GetItem ( dict , PyIdentifier . __slots__ ) ;
119161 if ( slots != null )
@@ -130,10 +172,12 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
130172 using var clsDict = new PyDict ( dict ) ;
131173 if ( clsDict . HasKey ( "__assembly__" ) || clsDict . HasKey ( "__namespace__" ) )
132174 {
133- return TypeManager . CreateSubType ( name , base_type , clsDict ) ;
175+ return TypeManager . CreateSubType ( name , baseTypes [ 0 ] , interfaces , clsDict ) ;
134176 }
135177 }
136178
179+ var base_type = Runtime . PyTuple_GetItem ( bases , 0 ) ;
180+
137181 // otherwise just create a basic type without reflecting back into the managed side.
138182 IntPtr func = Util . ReadIntPtr ( Runtime . PyTypeType , TypeOffset . tp_new ) ;
139183 NewReference type = NativeCall . Call_3 ( func , tp , args , kw ) ;
0 commit comments