Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b2ca984

Browse files
msullivanJukkaL
authored andcommitted
Implement initial support for single inheritance (mypyc/mypyc#139)
This doesn't handle subclasses changing method signatures (which will require generating compatibility methods, and using the compatibility method in the base class part of the vtable and the real one in the subclass part). * Make ir.attributes be a dictionary * Centralize vtable computation into ClassIR * Support basic class inheritance
1 parent ebd7af5 commit b2ca984

8 files changed

Lines changed: 244 additions & 41 deletions

File tree

mypyc/emitclass.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
2626
getseters_name = '{}_getseters'.format(name)
2727
methods_name = '{}_methods'.format(name)
2828
vtable_name = '{}_vtable'.format(name)
29+
base_arg = "&{}".format(type_struct_name(cl.base.name)) if cl.base else "0"
2930

3031
def emit_line() -> None:
3132
emitter.emit_line()
3233

3334
emit_line()
3435
generate_object_struct(cl, emitter)
36+
emit_line()
3537

3638
# If there is a __init__ method, generate a function for tp_init and
3739
# extract the args (which we'll use for the native constructor)
@@ -100,7 +102,7 @@ def emit_line() -> None:
100102
{methods_name}, /* tp_methods */
101103
0, /* tp_members */
102104
{getseters_name}, /* tp_getset */
103-
0, /* tp_base */
105+
{base_arg}, /* tp_base */
104106
0, /* tp_dict */
105107
0, /* tp_descr_get */
106108
0, /* tp_descr_set */
@@ -119,6 +121,7 @@ def emit_line() -> None:
119121
methods_name=methods_name,
120122
getseters_name=getseters_name,
121123
init_name=init_name,
124+
base_arg=base_arg,
122125
))
123126
emitter.emit_line()
124127
generate_setup_for_class(cl, setup_name, vtable_name, emitter)
@@ -148,14 +151,15 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
148151
emitter.emit_lines('typedef struct {',
149152
'PyObject_HEAD',
150153
'CPyVTableItem *vtable;')
151-
for attr, rtype in cl.attributes:
152-
emitter.emit_line('{}{};'.format(rtype.ctype_spaced(), attr))
154+
for base in reversed(cl.mro):
155+
for attr, rtype in base.attributes.items():
156+
emitter.emit_line('{}{};'.format(rtype.ctype_spaced(), attr))
153157
emitter.emit_line('}} {};'.format(cl.struct_name()))
154158

155159

156160
def generate_native_getters_and_setters(cl: ClassIR,
157161
emitter: Emitter) -> None:
158-
for attr, rtype in cl.attributes:
162+
for attr, rtype in cl.attributes.items():
159163
# Native getter
160164
emitter.emit_line('{}{}({} *self)'.format(rtype.ctype_spaced(),
161165
native_getter_name(cl.name, attr),
@@ -189,15 +193,23 @@ def generate_native_getters_and_setters(cl: ClassIR,
189193
emitter.emit_line()
190194

191195

192-
def generate_vtable(cl: ClassIR,
196+
def generate_vtable(base: ClassIR,
193197
vtable_name: str,
194198
emitter: Emitter) -> None:
195199
emitter.emit_line('static CPyVTableItem {}[] = {{'.format(vtable_name))
196-
for attr, rtype in cl.attributes:
197-
emitter.emit_line('(CPyVTableItem){},'.format(native_getter_name(cl.name, attr)))
198-
emitter.emit_line('(CPyVTableItem){},'.format(native_setter_name(cl.name, attr)))
199-
for fn in cl.methods:
200-
emitter.emit_line('(CPyVTableItem){}{},'.format(NATIVE_PREFIX, fn.cname))
200+
for cl in reversed(base.mro):
201+
for attr in cl.attributes:
202+
emitter.emit_line('(CPyVTableItem){},'.format(native_getter_name(cl.name, attr)))
203+
emitter.emit_line('(CPyVTableItem){},'.format(native_setter_name(cl.name, attr)))
204+
for fn in cl.methods:
205+
# TODO: This is gross, and inefficient, and wrong if the type changes.
206+
# This logic should all live on the genops side, I think
207+
search = base.mro if fn.name != '__init__' else [cl]
208+
for cl2 in search:
209+
m = cl2.get_method(fn.name)
210+
if m:
211+
emitter.emit_line('(CPyVTableItem){}{},'.format(NATIVE_PREFIX, m.cname))
212+
break
201213
emitter.emit_line('};')
202214

203215

@@ -216,7 +228,7 @@ def generate_setup_for_class(cl: ClassIR,
216228
emitter.emit_line('if (self == NULL)')
217229
emitter.emit_line(' return NULL;')
218230
emitter.emit_line('self->vtable = {};'.format(vtable_name))
219-
for attr, rtype in cl.attributes:
231+
for attr, rtype in cl.attributes.items():
220232
emitter.emit_line('self->{} = {};'.format(attr, rtype.c_undefined_value()))
221233
emitter.emit_line('return (PyObject *)self;')
222234
emitter.emit_line('}')
@@ -282,7 +294,7 @@ def generate_traverse_for_class(cl: ClassIR,
282294
emitter.emit_line('{}({} *self, visitproc visit, void *arg)'.format(func_name,
283295
cl.struct_name()))
284296
emitter.emit_line('{')
285-
for attr, rtype in cl.attributes:
297+
for attr, rtype in cl.attributes.items():
286298
emitter.emit_gc_visit('self->{}'.format(attr), rtype)
287299
emitter.emit_line('return 0;')
288300
emitter.emit_line('}')
@@ -294,7 +306,7 @@ def generate_clear_for_class(cl: ClassIR,
294306
emitter.emit_line('static int')
295307
emitter.emit_line('{}({} *self)'.format(func_name, cl.struct_name()))
296308
emitter.emit_line('{')
297-
for attr, rtype in cl.attributes:
309+
for attr, rtype in cl.attributes.items():
298310
emitter.emit_gc_clear('self->{}'.format(attr), rtype)
299311
emitter.emit_line('return 0;')
300312
emitter.emit_line('}')
@@ -327,7 +339,7 @@ def generate_methods_table(cl: ClassIR,
327339

328340

329341
def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None:
330-
for attr, rtype in cl.attributes:
342+
for attr in cl.attributes:
331343
emitter.emit_line('static PyObject *')
332344
emitter.emit_line('{}({} *self, void *closure);'.format(getter_name(cl.name, attr),
333345
cl.struct_name()))
@@ -342,7 +354,7 @@ def generate_getseters_table(cl: ClassIR,
342354
emitter: Emitter) -> None:
343355

344356
emitter.emit_line('static PyGetSetDef {}[] = {{'.format(name))
345-
for attr, rtype in cl.attributes:
357+
for attr in cl.attributes:
346358
emitter.emit_line('{{"{}",'.format(attr))
347359
emitter.emit_line(' (getter){}, (setter){},'.format(getter_name(cl.name, attr),
348360
setter_name(cl.name, attr)))
@@ -352,7 +364,7 @@ def generate_getseters_table(cl: ClassIR,
352364

353365

354366
def generate_getseters(cl: ClassIR, emitter: Emitter) -> None:
355-
for i, (attr, rtype) in enumerate(cl.attributes):
367+
for i, (attr, rtype) in enumerate(cl.attributes.items()):
356368
generate_getter(cl, attr, rtype, emitter)
357369
emitter.emit_line('')
358370
generate_setter(cl, attr, rtype, emitter)

mypyc/genops.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,29 +183,52 @@ def visit_mypy_file(self, mypyfile: MypyFile) -> Value:
183183
# built-in primitives.
184184
return INVALID_VALUE
185185

186-
# First pass: Build ClassIRs and TypeInfo-to-ClassIR mapping.
187-
for node in mypyfile.defs:
188-
if isinstance(node, ClassDef):
189-
self.prepare_class_def(node)
186+
classes = [node for node in mypyfile.defs if isinstance(node, ClassDef)]
187+
188+
# Build ClassIRs and TypeInfo-to-ClassIR mapping.
189+
for cls in classes:
190+
self.create_class_def(cls)
190191

191-
# Second pass: Generate ops.
192+
# Do class def setup
193+
for cls in classes:
194+
self.prepare_class_def(cls)
195+
196+
# Generate ops.
192197
self.current_module_name = mypyfile.fullname()
193198
for node in mypyfile.defs:
194199
node.accept(self)
195200

201+
# Compute vtables.
202+
for cls in classes:
203+
self.mapper.type_to_ir[cls.info].compute_vtable()
204+
196205
return INVALID_VALUE
197206

198-
def prepare_class_def(self, cdef: ClassDef) -> None:
207+
def create_class_def(self, cdef: ClassDef) -> None:
199208
# We want to collect the attributes first so they are available
200209
# while generating the methods
201210
ir = ClassIR(cdef.name)
202211
self.classes.append(ir)
203212
self.mapper.type_to_ir[cdef.info] = ir
204213

205-
for name, node in cdef.info.names.items():
214+
def prepare_class_def(self, cdef: ClassDef) -> None:
215+
ir = self.mapper.type_to_ir[cdef.info]
216+
info = cdef.info
217+
for name, node in info.names.items():
206218
if isinstance(node.node, Var):
207219
assert node.node.type, "Class member missing type"
208-
ir.attributes.append((name, self.type_to_rtype(node.node.type)))
220+
ir.attributes[name] = self.type_to_rtype(node.node.type)
221+
222+
# Set up the parent class
223+
assert len(info.bases) == 1, "Only single inheritance is supported"
224+
mro = []
225+
for cls in info.mro:
226+
if cls.fullname() == 'builtins.object': continue
227+
assert cls in self.mapper.type_to_ir, "Can't subclass cpython types yet"
228+
mro.append(self.mapper.type_to_ir[cls])
229+
if len(mro) > 1:
230+
ir.base = mro[1]
231+
ir.mro = mro
209232

210233
def visit_class_def(self, cdef: ClassDef) -> Value:
211234
ir = self.mapper.type_to_ir[cdef.info]

mypyc/ops.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -273,26 +273,16 @@ def struct_name(self) -> str:
273273
return self.class_ir.struct_name()
274274

275275
def getter_index(self, name: str) -> int:
276-
for i, (attr, _) in enumerate(self.class_ir.attributes):
277-
if attr == name:
278-
return i * 2
279-
assert False, '%r has no attribute %r' % (self.name, name)
276+
return self.class_ir.vtable_entry(name)
280277

281278
def setter_index(self, name: str) -> int:
282279
return self.getter_index(name) + 1
283280

284281
def method_index(self, name: str) -> int:
285-
base = len(self.class_ir.attributes) * 2
286-
for i, fn in enumerate(self.class_ir.methods):
287-
if fn.name == name:
288-
return base + i
289-
assert False, '%r has no attribute %r' % (self.name, name)
282+
return self.class_ir.vtable_entry(name)
290283

291284
def attr_type(self, name: str) -> RType:
292-
for i, (attr, rtype) in enumerate(self.class_ir.attributes):
293-
if attr == name:
294-
return rtype
295-
assert False, '%r has no attribute %r' % (self.name, name)
285+
return self.class_ir.attr_type(name)
296286

297287
def __repr__(self) -> str:
298288
return '<RInstance %s>' % self.name
@@ -1232,8 +1222,39 @@ class ClassIR:
12321222

12331223
def __init__(self, name: str) -> None:
12341224
self.name = name
1235-
self.attributes = [] # type: List[Tuple[str, RType]]
1225+
self.attributes = OrderedDict() # type: OrderedDict[str, RType]
12361226
self.methods = [] # type: List[FuncIR]
1227+
self.vtable = None # type: Optional[Dict[str, int]]
1228+
self.vtable_size = 0
1229+
self.base = None # type: Optional[ClassIR]
1230+
self.mro = [] # type: List[ClassIR]
1231+
1232+
def compute_vtable(self) -> None:
1233+
if self.vtable is not None: return
1234+
self.vtable = {}
1235+
base = 0
1236+
if self.base:
1237+
self.base.compute_vtable()
1238+
assert self.base.vtable is not None
1239+
self.vtable.update(self.base.vtable)
1240+
base = self.base.vtable_size
1241+
1242+
for i, attr in enumerate(self.attributes):
1243+
self.vtable[attr] = base + i * 2
1244+
base += len(self.attributes) * 2
1245+
for i, fn in enumerate(self.methods):
1246+
self.vtable[fn.name] = base + i
1247+
self.vtable_size = base + len(self.methods)
1248+
1249+
def vtable_entry(self, name: str) -> int:
1250+
assert self.vtable is not None, "vtable not computed yet"
1251+
assert name in self.vtable, '%r has no attribute %r' % (self.name, name)
1252+
return self.vtable[name]
1253+
1254+
def attr_type(self, name: str) -> RType:
1255+
for ir in self.mro:
1256+
if name in ir.attributes: return ir.attributes[name]
1257+
assert False, '%r has no attribute %r' % (self.name, name)
12371258

12381259
def struct_name(self) -> str:
12391260
return '{}Object'.format(self.name)

mypyc/subtype.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ def __init__(self, right: RType) -> None:
2727
self.right = right
2828

2929
def visit_rinstance(self, left: RInstance) -> bool:
30-
# TODO: Inheritance
31-
return isinstance(self.right, RInstance) and self.right.name == left.name
30+
return isinstance(self.right, RInstance) and self.right.class_ir in left.class_ir.mro
3231

3332
def visit_roptional(self, left: ROptional) -> bool:
3433
return isinstance(self.right, ROptional) and is_subtype(left.value_type,

mypyc/test/test_emitfunc.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import unittest
22

3+
from collections import OrderedDict
4+
35
from mypy.nodes import Var
46
from mypy.test.helpers import assert_string_arrays_equal
57

@@ -38,7 +40,9 @@ def setUp(self) -> None:
3840
self.tt = self.env.add_local(
3941
Var('tt'), RTuple([RTuple([int_rprimitive, bool_rprimitive]), bool_rprimitive]))
4042
ir = ClassIR('A')
41-
ir.attributes = [('x', bool_rprimitive), ('y', int_rprimitive)]
43+
ir.attributes = OrderedDict([('x', bool_rprimitive), ('y', int_rprimitive)])
44+
ir.compute_vtable()
45+
ir.mro = [ir]
4246
self.r = self.env.add_local(Var('r'), RInstance(ir))
4347

4448
self.context = EmitterContext()

test-data/genops-classes.test

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,38 @@ L1:
141141
L2:
142142
r6 = 1
143143
return r6
144+
145+
[case testSubclass]
146+
class A:
147+
def __init__(self) -> None:
148+
self.x = 10
149+
class B(A):
150+
def __init__(self) -> None:
151+
self.x = 20
152+
self.y = 30
153+
[out]
154+
-- This is totally useless because we don't print any class info!!
155+
def __init__(self):
156+
self :: A
157+
r0 :: int
158+
r1 :: bool
159+
r2 :: None
160+
L0:
161+
r0 = 10
162+
self.x = r0; r1 = is_error
163+
r2 = None
164+
return r2
165+
def __init__(self):
166+
self :: B
167+
r0 :: int
168+
r1 :: bool
169+
r2 :: int
170+
r3 :: bool
171+
r4 :: None
172+
L0:
173+
r0 = 20
174+
self.x = r0; r1 = is_error
175+
r2 = 30
176+
self.y = r2; r3 = is_error
177+
r4 = None
178+
return r4

0 commit comments

Comments
 (0)