Skip to content

Commit 51379e2

Browse files
committed
Value types tests working
1 parent 6868d34 commit 51379e2

File tree

11 files changed

+332
-104
lines changed

11 files changed

+332
-104
lines changed

mypyc/codegen/emit.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -361,14 +361,22 @@ def bitmap_field(self, index: int) -> str:
361361
return "bitmap"
362362
return f"bitmap{n + 1}"
363363

364-
def attr_bitmap_expr(self, obj: str, cl: ClassIR, index: int) -> str:
365-
"""Return reference to the attribute definedness bitmap."""
366-
cast = f"({cl.struct_name(self.names)} *)"
364+
def attr_bitmap_expr(self, obj: str, cl: RInstance, index: int) -> str:
365+
"""
366+
Return reference to the attribute definedness bitmap.
367+
368+
If a_ref is True, assume object's type is a reference.
369+
Otherwise, the object type is indicated by the class IR.
370+
"""
367371
attr = self.bitmap_field(index)
368-
return f"({cast}{obj})->{attr}"
372+
if cl.is_unboxed:
373+
return f"{obj}.{attr}"
374+
else:
375+
cast = f"({cl.struct_name(self.names)} *)"
376+
return f"({cast}{obj})->{attr}"
369377

370378
def emit_attr_bitmap_set(
371-
self, value: str, obj: str, rtype: RType, cl: ClassIR, attr: str
379+
self, value: str, obj: str, rtype: RType, cl: RInstance, attr: str
372380
) -> None:
373381
"""Mark an attribute as defined in the attribute bitmap.
374382
@@ -377,20 +385,20 @@ def emit_attr_bitmap_set(
377385
"""
378386
self._emit_attr_bitmap_update(value, obj, rtype, cl, attr, clear=False)
379387

380-
def emit_attr_bitmap_clear(self, obj: str, rtype: RType, cl: ClassIR, attr: str) -> None:
388+
def emit_attr_bitmap_clear(self, obj: str, rtype: RType, cl: RInstance, attr: str) -> None:
381389
"""Mark an attribute as undefined in the attribute bitmap.
382390
383391
Unlike emit_attr_bitmap_set, clear unconditionally.
384392
"""
385393
self._emit_attr_bitmap_update("", obj, rtype, cl, attr, clear=True)
386394

387395
def _emit_attr_bitmap_update(
388-
self, value: str, obj: str, rtype: RType, cl: ClassIR, attr: str, clear: bool
396+
self, value: str, obj: str, rtype: RType, cl: RInstance, attr: str, clear: bool
389397
) -> None:
390398
if value:
391399
check = self.error_value_check(rtype, value, "==")
392400
self.emit_line(f"if (unlikely({check})) {{")
393-
index = cl.bitmap_attrs.index(attr)
401+
index = cl.class_ir.bitmap_attrs.index(attr)
394402
mask = 1 << (index & (BITMAP_BITS - 1))
395403
bitmap = self.attr_bitmap_expr(obj, cl, index)
396404
if clear:
@@ -410,19 +418,19 @@ def emit_undefined_attr_check(
410418
compare: str,
411419
obj: str,
412420
attr: str,
413-
cl: ClassIR,
421+
cl: RInstance,
414422
*,
415423
unlikely: bool = False,
416424
) -> None:
417425
check = self.error_value_check(rtype, attr_expr, compare)
418426
if unlikely:
419427
check = f"unlikely({check})"
420428
if rtype.error_overlap:
421-
index = cl.bitmap_attrs.index(attr)
429+
index = cl.class_ir.bitmap_attrs.index(attr)
430+
attr_expr = self.attr_bitmap_expr(obj, cl, index)
422431
bit = 1 << (index & (BITMAP_BITS - 1))
423-
attr = self.bitmap_field(index)
424-
obj_expr = f"({cl.struct_name(self.names)} *){obj}"
425-
check = f"{check} && !(({obj_expr})->{attr} & {bit})"
432+
check = f"{check} && !({attr_expr} & {bit})"
433+
426434
self.emit_line(f"if ({check}) {{")
427435

428436
def error_value_check(self, rtype: RType, value: str, compare: str) -> str:
@@ -500,6 +508,8 @@ def declare_tuple_struct(self, tuple_type: RTuple) -> None:
500508
# XXX other types might eventually need similar behavior
501509
if isinstance(typ, RTuple):
502510
dependencies.add(typ.struct_name)
511+
if isinstance(typ, RInstanceValue):
512+
dependencies.add(typ.struct_name(self.names))
503513

504514
self.context.declarations[tuple_type.struct_name] = HeaderDeclaration(
505515
self.tuple_c_declaration(tuple_type), dependencies=dependencies, is_type=True
@@ -1075,7 +1085,7 @@ def emit_box(
10751085
)
10761086
self.emit_line(f"if (unlikely({temp_dest} == NULL))")
10771087
self.emit_line(" CPyError_OutOfMemory();")
1078-
for attr, attr_type in cl.attributes.items():
1088+
for attr, attr_type in cl.all_attributes().items():
10791089
attr_name = self.attr(attr)
10801090
self.emit_line(f"{temp_dest}->{attr_name} = {src}.{attr_name};", ann="box")
10811091

mypyc/codegen/emitclass.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from mypyc.common import BITMAP_BITS, BITMAP_TYPE, NATIVE_PREFIX, PREFIX, REG_PREFIX
2222
from mypyc.ir.class_ir import ClassIR, VTableEntries
2323
from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR
24-
from mypyc.ir.rtypes import RTuple, RType, object_rprimitive
24+
from mypyc.ir.rtypes import RTuple, RType, object_rprimitive, RInstanceValue, RInstance
2525
from mypyc.namegen import NameGenerator
2626
from mypyc.sametype import is_same_type
2727

@@ -294,9 +294,14 @@ def emit_line() -> None:
294294
# Declare setup method that allocates and initializes an object. type is the
295295
# type of the class being initialized, which could be another class if there
296296
# is an interpreted subclass.
297-
emitter.context.declarations[setup_name] = HeaderDeclaration(
298-
f"PyObject *{setup_name}(PyTypeObject *type);", needs_export=True
299-
)
297+
if cl.is_value_type:
298+
# Value types will need this method be exported because it will be required
299+
# when boxing the value type instance.
300+
emitter.context.declarations[setup_name] = HeaderDeclaration(
301+
f"PyObject *{setup_name}(PyTypeObject *type);", needs_export=True
302+
)
303+
else:
304+
emitter.emit_line(f"static PyObject *{setup_name}(PyTypeObject *type);")
300305

301306
assert cl.ctor is not None
302307
emitter.emit_line(native_function_header(cl.ctor, emitter) + ";")
@@ -949,11 +954,13 @@ def generate_getter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N
949954
always_defined = cl.is_always_defined(attr) and not rtype.is_refcounted
950955

951956
if not always_defined:
952-
emitter.emit_undefined_attr_check(rtype, attr_expr, "==", "self", attr, cl, unlikely=True)
957+
clt = RInstance(cl)
958+
emitter.emit_undefined_attr_check(rtype, attr_expr, "==", "self", attr, clt, unlikely=True)
953959
emitter.emit_line("PyErr_SetString(PyExc_AttributeError,")
954960
emitter.emit_line(f' "attribute {repr(attr)} of {repr(cl.name)} undefined");')
955961
emitter.emit_line("return NULL;")
956962
emitter.emit_line("}")
963+
957964
emitter.emit_inc_ref(f"self->{attr_field}", rtype)
958965
emitter.emit_box(f"self->{attr_field}", "retval", rtype, declare_dest=True)
959966
emitter.emit_line("return retval;")
@@ -988,7 +995,8 @@ def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N
988995
if rtype.is_refcounted:
989996
attr_expr = f"self->{attr_field}"
990997
if not always_defined:
991-
emitter.emit_undefined_attr_check(rtype, attr_expr, "!=", "self", attr, cl)
998+
clt = RInstance(cl)
999+
emitter.emit_undefined_attr_check(rtype, attr_expr, "!=", "self", attr, clt)
9921000
emitter.emit_dec_ref(f"self->{attr_field}", rtype)
9931001
if not always_defined:
9941002
emitter.emit_line("}")
@@ -1006,13 +1014,13 @@ def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N
10061014
emitter.emit_inc_ref("tmp", rtype)
10071015
emitter.emit_line(f"self->{attr_field} = tmp;")
10081016
if rtype.error_overlap and not always_defined:
1009-
emitter.emit_attr_bitmap_set("tmp", "self", rtype, cl, attr)
1017+
emitter.emit_attr_bitmap_set("tmp", "self", rtype, RInstance(cl), attr)
10101018

10111019
if deletable:
10121020
emitter.emit_line("} else")
10131021
emitter.emit_line(f" self->{attr_field} = {emitter.c_undefined_value(rtype)};")
10141022
if rtype.error_overlap:
1015-
emitter.emit_attr_bitmap_clear("self", rtype, cl, attr)
1023+
emitter.emit_attr_bitmap_clear("self", rtype, RInstance(cl), attr)
10161024
emitter.emit_line("return 0;")
10171025
emitter.emit_line("}")
10181026

@@ -1027,19 +1035,22 @@ def generate_readonly_getter(
10271035
)
10281036
)
10291037
emitter.emit_line("{")
1038+
1039+
arg0 = func_ir.args[0].type
1040+
obj = "*self" if isinstance(arg0, RInstanceValue) else "(PyObject *)self"
1041+
10301042
if rtype.is_unboxed:
10311043
emitter.emit_line(
1032-
"{}retval = {}{}((PyObject *) self);".format(
1033-
emitter.ctype_spaced(rtype), NATIVE_PREFIX, func_ir.cname(emitter.names)
1044+
"{}retval = {}{}({});".format(
1045+
emitter.ctype_spaced(rtype), NATIVE_PREFIX, func_ir.cname(emitter.names), obj
10341046
)
10351047
)
10361048
emitter.emit_error_check("retval", rtype, "return NULL;")
10371049
emitter.emit_box("retval", "retbox", rtype, declare_dest=True)
10381050
emitter.emit_line("return retbox;")
10391051
else:
1040-
emitter.emit_line(
1041-
f"return {NATIVE_PREFIX}{func_ir.cname(emitter.names)}((PyObject *) self);"
1042-
)
1052+
emitter.emit_line(f"return {NATIVE_PREFIX}{func_ir.cname(emitter.names)}({obj});")
1053+
10431054
emitter.emit_line("}")
10441055

10451056

mypyc/codegen/emitfunc.py

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
TYPE_VAR_PREFIX,
1616
)
1717
from mypyc.ir.class_ir import ClassIR
18-
from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values
18+
from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values, FUNC_NORMAL
1919
from mypyc.ir.ops import (
2020
ERR_FALSE,
2121
NAMESPACE_MODULE,
@@ -71,14 +71,12 @@
7171
)
7272
from mypyc.ir.pprint import generate_names_for_ir
7373
from mypyc.ir.rtypes import (
74-
PyObject,
7574
RArray,
7675
RInstance,
7776
RInstanceValue,
7877
RStruct,
7978
RTuple,
8079
RType,
81-
c_pyssize_t_rprimitive,
8280
is_int32_rprimitive,
8381
is_int64_rprimitive,
8482
is_int_rprimitive,
@@ -87,18 +85,6 @@
8785
)
8886

8987

90-
def struct_type(class_ir: ClassIR, emitter: Emitter) -> RStruct:
91-
"""Return the struct type for this instance type."""
92-
python_fields: list[tuple[str, RType]] = [
93-
("head", PyObject),
94-
("vtable", c_pyssize_t_rprimitive),
95-
]
96-
class_fields = list(class_ir.attributes.items())
97-
attr_names = [emitter.attr(name) for name, _ in python_fields + class_fields]
98-
attr_types = [rtype for _, rtype in python_fields + class_fields]
99-
return RStruct(class_ir.struct_name(emitter.names), attr_names, attr_types)
100-
101-
10288
def native_function_type(fn: FuncIR, emitter: Emitter) -> str:
10389
args = ", ".join(emitter.ctype(arg.type) for arg in fn.args) or "void"
10490
ret = emitter.ctype(fn.ret_type)
@@ -295,7 +281,7 @@ def visit_assign(self, op: Assign) -> None:
295281
# clang whines about self assignment (which we might generate
296282
# for some casts), so don't emit it.
297283
if dest != src:
298-
# We sometimes assign from an integer prepresentation of a pointer
284+
# We sometimes assign from an integer representation of a pointer
299285
# to a real pointer, and C compilers insist on a cast.
300286
if op.src.type.is_unboxed and not op.dest.type.is_unboxed:
301287
src = f"(void *){src}"
@@ -406,20 +392,12 @@ def visit_get_attr(self, op: GetAttr) -> None:
406392
attr_expr = self.get_attr_expr(obj, op, decl_cl)
407393
always_defined = cl.is_always_defined(op.attr)
408394
# This steals the reference to src, so we don't need to increment the arg
409-
if isinstance(attr_rtype, RInstance) and attr_rtype.class_ir.is_value_type:
410-
# special case for value types, it is unboxed in the struct
411-
struct_name = attr_rtype.class_ir.struct_name(self.names)
412-
temp = self.emitter.temp_name()
413-
self.emitter.emit_line(f"{struct_name} {temp} = {attr_expr};")
414-
self.emitter.emit_line(f"{dest} = (PyObject *)&{temp};")
415-
always_defined = True
416-
else:
417-
self.emitter.emit_line(f"{dest} = {attr_expr};")
395+
self.emitter.emit_line(f"{dest} = {attr_expr};")
418396

419397
merged_branch = None
420398
if not always_defined:
421399
self.emitter.emit_undefined_attr_check(
422-
attr_rtype, dest, "==", obj, op.attr, cl, unlikely=True
400+
attr_rtype, dest, "==", obj, op.attr, rtype, unlikely=True
423401
)
424402
branch = self.next_branch()
425403
if branch is not None:
@@ -466,7 +444,8 @@ def visit_set_attr(self, op: SetAttr) -> None:
466444
dest = self.reg(op)
467445
obj = self.reg(op.obj)
468446
src = self.reg(op.src)
469-
rtype = op.class_type
447+
rtype = op.obj.type
448+
assert isinstance(rtype, RInstance)
470449
cl = rtype.class_ir
471450
attr_rtype, decl_cl = cl.attr_details(op.attr)
472451
if cl.get_method(op.attr):
@@ -501,23 +480,18 @@ def visit_set_attr(self, op: SetAttr) -> None:
501480
always_defined = cl.is_always_defined(op.attr)
502481
if not always_defined:
503482
self.emitter.emit_undefined_attr_check(
504-
attr_rtype, attr_expr, "!=", obj, op.attr, cl
483+
attr_rtype, attr_expr, "!=", obj, op.attr, rtype
505484
)
506485
self.emitter.emit_dec_ref(attr_expr, attr_rtype)
507486
if not always_defined:
508487
self.emitter.emit_line("}")
509488
elif attr_rtype.error_overlap and not cl.is_always_defined(op.attr):
510489
# If there is overlap with the error value, update bitmap to mark
511490
# attribute as defined.
512-
self.emitter.emit_attr_bitmap_set(src, obj, attr_rtype, cl, op.attr)
491+
self.emitter.emit_attr_bitmap_set(src, obj, attr_rtype, rtype, op.attr)
513492

514493
# This steals the reference to src, so we don't need to increment the arg
515-
if isinstance(attr_rtype, RInstance) and attr_rtype.class_ir.is_value_type:
516-
# special case for value types, it is unboxed in the struct
517-
struct_name = attr_rtype.class_ir.struct_name(self.names)
518-
self.emitter.emit_line(f"{attr_expr} = *({struct_name} *)({src});")
519-
else:
520-
self.emitter.emit_line(f"{attr_expr} = {src};")
494+
self.emitter.emit_line(f"{attr_expr} = {src};")
521495
if op.error_kind == ERR_FALSE:
522496
self.emitter.emit_line(f"{dest} = 1;")
523497

@@ -589,6 +563,20 @@ def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Va
589563
if method.decl.kind == FUNC_STATICMETHOD
590564
else [f"(PyObject *)Py_TYPE({obj})"] if method.decl.kind == FUNC_CLASSMETHOD else [obj]
591565
)
566+
need_box_obj = (
567+
method.decl.kind == FUNC_NORMAL
568+
and rtype.is_unboxed
569+
and not method.args[0].type.is_unboxed
570+
)
571+
obj_boxed = ""
572+
if need_box_obj:
573+
# for cases where obj.method(...) is called and obj is unboxed, but method
574+
# expects a boxed due inheritance or trait. e.g. obj is a value type
575+
# but method comes from a parent which not
576+
obj_boxed = self.temp_name()
577+
self.emitter.emit_box(obj, obj_boxed, rtype, declare_dest=True)
578+
obj_args = [obj_boxed]
579+
592580
args = ", ".join(obj_args + [self.reg(arg) for arg in op_args])
593581
mtype = native_function_type(method, self.emitter)
594582
version = "_TRAIT" if rtype.class_ir.is_trait else ""
@@ -613,6 +601,9 @@ def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Va
613601
)
614602
)
615603

604+
if need_box_obj:
605+
self.emitter.emit_dec_ref(obj_boxed, RInstance(rtype.class_ir))
606+
616607
def visit_inc_ref(self, op: IncRef) -> None:
617608
src = self.reg(op.src)
618609
self.emit_inc_ref(src, op.src.type)

0 commit comments

Comments
 (0)