Skip to content

Commit 975d4cd

Browse files
committed
py/obj: Fix MP_OBJ_TYPE_SET_SLOT_IF_EXISTS macro for MSVC compatibility.
The previous implementation used a do-while(0) pattern which MSVC doesn't accept. Replace with a conditional expression using the comma operator that works with both GCC and MSVC compilers. This fixes Windows port compilation error C2121. Signed-off-by: Andrew Leech <[email protected]>
1 parent dcd0348 commit 975d4cd

File tree

9 files changed

+259
-164
lines changed

9 files changed

+259
-164
lines changed

py/obj.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -805,12 +805,8 @@ typedef struct _mp_obj_full_type_t {
805805
#define MP_OBJ_TYPE_GET_SLOT(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(t)->slots[(t)->slot_index_##f - 1])
806806
#define MP_OBJ_TYPE_GET_SLOT_OR_NULL(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(MP_OBJ_TYPE_HAS_SLOT(t, f) ? MP_OBJ_TYPE_GET_SLOT(t, f) : NULL))
807807
#define MP_OBJ_TYPE_SET_SLOT(t, f, v, n) ((t)->slot_index_##f = (n) + 1, (t)->slots[(n)] = (void *)v)
808-
#define MP_OBJ_TYPE_SET_SLOT_IF_EXISTS(t, f, v, n) do { \
809-
void *_v = (void *)(v); \
810-
if (_v != NULL) { \
811-
MP_OBJ_TYPE_SET_SLOT(t, f, _v, n); \
812-
} \
813-
} while (0)
808+
#define MP_OBJ_TYPE_SET_SLOT_IF_EXISTS(t, f, v, n) \
809+
((v) ? (MP_OBJ_TYPE_SET_SLOT(t, f, v, n), (void)0) : (void)0)
814810
#define MP_OBJ_TYPE_OFFSETOF_SLOT(f) (offsetof(mp_obj_type_t, slot_index_##f))
815811
#define MP_OBJ_TYPE_HAS_SLOT_BY_OFFSET(t, offset) (*(uint8_t *)((char *)(t) + (offset)) != 0)
816812

test_debug.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,44 @@
55
print("Testing type.__init__ access:")
66
print(f"hasattr(type, '__init__'): {hasattr(type, '__init__')}")
77

8+
89
class CustomMeta(type):
910
pass
1011

12+
1113
print(f"hasattr(CustomMeta, '__init__'): {hasattr(CustomMeta, '__init__')}")
1214

15+
1316
class RegularClass:
1417
pass
1518

19+
1620
print(f"hasattr(RegularClass, '__init__'): {hasattr(RegularClass, '__init__')}")
1721

1822
# Test deep hierarchy
1923
print("\nTesting deep hierarchy:")
2024
init_order = []
2125

26+
2227
class Meta1(type):
2328
def __init__(cls, name, bases, attrs):
2429
print(f"Meta1.__init__ called for {name}")
2530
super().__init__(name, bases, attrs)
26-
init_order.append('Meta1')
31+
init_order.append("Meta1")
32+
2733

2834
class Meta2(Meta1):
2935
def __init__(cls, name, bases, attrs):
3036
print(f"Meta2.__init__ called for {name}")
3137
super().__init__(name, bases, attrs)
32-
init_order.append('Meta2')
38+
init_order.append("Meta2")
39+
3340

3441
try:
42+
3543
class Test(metaclass=Meta2):
3644
pass
45+
3746
print(f"init_order: {init_order}")
3847
except Exception as e:
39-
print(f"Error creating Test: {e}")
48+
print(f"Error creating Test: {e}")

test_metaclass_edge_cases.py

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import traceback
55

6+
67
def test_case(name, func):
78
"""Helper to run test cases and catch errors."""
89
try:
@@ -14,21 +15,25 @@ def test_case(name, func):
1415
traceback.print_exc()
1516
return False
1617

18+
1719
# Test 1: Basic super().__init__() in metaclass
1820
def test_basic_metaclass_super():
1921
"""The original crash scenario."""
22+
2023
class Meta(type):
2124
def __init__(cls, name, bases, attrs):
2225
super().__init__(name, bases, attrs)
2326

2427
class Test(metaclass=Meta):
2528
pass
2629

27-
assert Test.__name__ == 'Test'
30+
assert Test.__name__ == "Test"
31+
2832

2933
# Test 2: Multiple inheritance in metaclass hierarchy
3034
def test_metaclass_multiple_inheritance():
3135
"""Test metaclass with multiple inheritance."""
36+
3237
class MetaA(type):
3338
def __init__(cls, name, bases, attrs):
3439
super().__init__(name, bases, attrs)
@@ -48,17 +53,19 @@ class Test(metaclass=MetaC):
4853
pass
4954

5055
# MRO should be MetaC -> MetaA -> MetaB -> type
51-
assert hasattr(Test, 'from_a')
52-
assert hasattr(Test, 'from_c')
56+
assert hasattr(Test, "from_a")
57+
assert hasattr(Test, "from_c")
5358
# MetaB's __init__ may or may not be called depending on MRO implementation
5459

60+
5561
# Test 3: type.__init__ should accept exactly 1 or 4 arguments (MicroPython)
5662
# or 1 or 3 arguments (CPython)
5763
def test_type_init_signature():
5864
"""Verify type.__init__ signature."""
5965
# Test what signatures are accepted
6066
import sys
61-
is_micropython = sys.implementation.name == 'micropython'
67+
68+
is_micropython = sys.implementation.name == "micropython"
6269

6370
# 1 argument (just self) should work
6471
type.__init__(type)
@@ -69,49 +76,53 @@ class Meta(type):
6976

7077
if is_micropython:
7178
# MicroPython: 4 arguments (self, name, bases, dict)
72-
type.__init__(Meta, 'Test', (), {})
79+
type.__init__(Meta, "Test", (), {})
7380
expected_msg = "takes 1 or 4 arguments"
7481
else:
7582
# CPython: 3 arguments (name, bases, dict) - self is implicit
76-
type.__init__(Meta, 'Test', ())
83+
type.__init__(Meta, "Test", ())
7784
expected_msg = "takes 1 or 3 arguments"
7885

7986
# Other argument counts should fail
8087
try:
81-
type.__init__(type, 'extra')
88+
type.__init__(type, "extra")
8289
assert False, "Should have raised TypeError"
8390
except TypeError as e:
8491
assert expected_msg in str(e)
8592

8693
try:
87-
type.__init__(type, 'a', 'b', 'c', 'd', 'e')
94+
type.__init__(type, "a", "b", "c", "d", "e")
8895
assert False, "Should have raised TypeError"
8996
except TypeError as e:
9097
assert expected_msg in str(e)
9198

99+
92100
# Test 4: Regular classes should NOT have type.__init__
93101
def test_regular_class_no_type_init():
94102
"""Regular classes shouldn't expose type.__init__."""
103+
95104
class RegularClass:
96105
pass
97106

98107
# This depends on implementation details - in CPython, hasattr returns True
99108
# but the attribute lookup goes through descriptors
100109
# The key is that calling it shouldn't create a new type
101-
if hasattr(RegularClass, '__init__'):
110+
if hasattr(RegularClass, "__init__"):
102111
# If it exists, calling it with wrong args should behave correctly
103112
try:
104113
# This should either not be type.__init__ or should fail appropriately
105-
result = RegularClass.__init__(RegularClass, 'name', (), {})
114+
result = RegularClass.__init__(RegularClass, "name", (), {})
106115
# If it succeeded, it shouldn't have created a new type
107116
assert result is None or not isinstance(result, type)
108117
except TypeError:
109118
# Expected - regular class __init__ has different signature
110119
pass
111120

121+
112122
# Test 5: Custom metaclass with type as explicit base
113123
def test_explicit_type_base():
114124
"""Test metaclass that explicitly inherits from type."""
125+
115126
class Meta(type):
116127
def __init__(cls, name, bases, attrs):
117128
# Explicit call to type.__init__ instead of super()
@@ -121,23 +132,27 @@ def __init__(cls, name, bases, attrs):
121132
class Test(metaclass=Meta):
122133
pass
123134

124-
assert hasattr(Test, 'initialized')
135+
assert hasattr(Test, "initialized")
125136
assert Test.initialized == True
126137

138+
127139
# Test 6: Metaclass without __init__ should work
128140
def test_metaclass_no_init():
129141
"""Metaclass without __init__ should inherit type's."""
142+
130143
class Meta(type):
131144
pass # No __init__ defined
132145

133146
class Test(metaclass=Meta):
134147
pass
135148

136-
assert Test.__name__ == 'Test'
149+
assert Test.__name__ == "Test"
150+
137151

138152
# Test 7: Deep metaclass hierarchy
139153
def test_deep_metaclass_hierarchy():
140154
"""Test deeply nested metaclass inheritance."""
155+
141156
class Meta1(type):
142157
def __init__(cls, name, bases, attrs):
143158
super().__init__(name, bases, attrs)
@@ -160,9 +175,11 @@ class Test(metaclass=Meta3):
160175
assert Test.level2 == True
161176
assert Test.level3 == True
162177

178+
163179
# Test 8: super() in metaclass without __init__ override
164180
def test_super_in_other_metaclass_method():
165181
"""Test super() in metaclass methods other than __init__."""
182+
166183
class Meta(type):
167184
def custom_method(cls):
168185
# This should work - super() in non-__init__ context
@@ -172,24 +189,27 @@ class Test(metaclass=Meta):
172189
pass
173190

174191
# This tests that super() works in other contexts
175-
assert Test.custom_method() == 'Test'
192+
assert Test.custom_method() == "Test"
193+
176194

177195
# Test 9: type.__init__ should be a no-op
178196
def test_type_init_is_noop():
179197
"""Verify type.__init__ doesn't modify anything."""
198+
180199
class Meta(type):
181200
pass
182201

183202
# Create a class normally
184-
Test1 = Meta('Test1', (), {'x': 1})
203+
Test1 = Meta("Test1", (), {"x": 1})
185204

186205
# Call type.__init__ again - should be no-op
187-
type.__init__(Test1, 'DifferentName', (), {'y': 2})
206+
type.__init__(Test1, "DifferentName", (), {"y": 2})
188207

189208
# Name and attributes should be unchanged
190-
assert Test1.__name__ == 'Test1'
191-
assert hasattr(Test1, 'x')
192-
assert not hasattr(Test1, 'y')
209+
assert Test1.__name__ == "Test1"
210+
assert hasattr(Test1, "x")
211+
assert not hasattr(Test1, "y")
212+
193213

194214
# Test 10: Verify the fix doesn't break instance __init__
195215
def test_instance_init_still_works():
@@ -198,29 +218,30 @@ def test_instance_init_still_works():
198218

199219
class Base:
200220
def __init__(self):
201-
init_called.append('Base')
221+
init_called.append("Base")
202222

203223
class Derived(Base):
204224
def __init__(self):
205225
super().__init__()
206-
init_called.append('Derived')
226+
init_called.append("Derived")
207227

208228
obj = Derived()
209-
assert init_called == ['Base', 'Derived']
229+
assert init_called == ["Base", "Derived"]
230+
210231

211232
# Run all tests
212-
if __name__ == '__main__':
233+
if __name__ == "__main__":
213234
tests = [
214-
('Basic metaclass super().__init__', test_basic_metaclass_super),
215-
('Metaclass multiple inheritance', test_metaclass_multiple_inheritance),
216-
('type.__init__ signature', test_type_init_signature),
217-
('Regular class no type.__init__', test_regular_class_no_type_init),
218-
('Explicit type base', test_explicit_type_base),
219-
('Metaclass without __init__', test_metaclass_no_init),
220-
('Deep metaclass hierarchy', test_deep_metaclass_hierarchy),
221-
('super() in other methods', test_super_in_other_metaclass_method),
222-
('type.__init__ is no-op', test_type_init_is_noop),
223-
('Instance __init__ still works', test_instance_init_still_works),
235+
("Basic metaclass super().__init__", test_basic_metaclass_super),
236+
("Metaclass multiple inheritance", test_metaclass_multiple_inheritance),
237+
("type.__init__ signature", test_type_init_signature),
238+
("Regular class no type.__init__", test_regular_class_no_type_init),
239+
("Explicit type base", test_explicit_type_base),
240+
("Metaclass without __init__", test_metaclass_no_init),
241+
("Deep metaclass hierarchy", test_deep_metaclass_hierarchy),
242+
("super() in other methods", test_super_in_other_metaclass_method),
243+
("type.__init__ is no-op", test_type_init_is_noop),
244+
("Instance __init__ still works", test_instance_init_still_works),
224245
]
225246

226247
passed = 0
@@ -232,9 +253,9 @@ def __init__(self):
232253
else:
233254
failed += 1
234255

235-
print(f"\n{'='*50}")
256+
print(f"\n{'=' * 50}")
236257
print(f"Results: {passed} passed, {failed} failed")
237258
if failed == 0:
238259
print("All tests PASSED!")
239260
else:
240-
print(f"Some tests FAILED!")
261+
print(f"Some tests FAILED!")

test_metaclass_inheritance.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@
33
# Test inherited metaclass __init__
44
init_calls = []
55

6+
67
class BaseMeta(type):
78
def __init__(cls, name, bases, attrs):
89
super().__init__(name, bases, attrs)
910
init_calls.append(f"BaseMeta.__init__({name})")
1011
print(f"BaseMeta.__init__({name})")
1112

13+
1214
class DerivedMeta(BaseMeta):
1315
pass # No __init__ in this class's dict
1416

17+
1518
class TestClass(metaclass=DerivedMeta):
1619
pass
1720

21+
1822
print(f"\nInit calls: {init_calls}")
1923
print(f"Expected: ['BaseMeta.__init__(TestClass)']")
2024
print(f"Pass: {init_calls == ['BaseMeta.__init__(TestClass)']}")

0 commit comments

Comments
 (0)