Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions mypyc/codegen/emitclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,11 @@ def generate_getter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N
emitter.emit_line("}")
return

if IS_FREE_THREADED and is_int_rprimitive(rtype) and attr not in cl.final_attributes:
if (
emitter.target_python.free_threaded
and is_int_rprimitive(rtype)
and attr not in cl.final_attributes
):
# In free-threaded builds, CPyTagged_GetAttr takes a new reference to a long
# value without racing with a concurrent setter (see CPyTagged_SetAttr). It
# returns CPY_INT_TAG if the attribute is undefined.
Expand Down Expand Up @@ -1479,7 +1483,7 @@ def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N
emitter.emit_line("}")
return

if IS_FREE_THREADED and is_int_rprimitive(rtype):
if emitter.target_python.free_threaded and is_int_rprimitive(rtype):
# In free-threaded builds, CPyTagged_SetAttr steals the new value and safely
# reclaims the old one (see CPyTagged_GetAttr). Unbox (borrowed) and take a
# new reference to hand over; a failed unbox leaves the attribute intact.
Expand Down
4 changes: 2 additions & 2 deletions mypyc/codegen/emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ def emit_load_attr_take_ref(
ClassIR.attrs_are_thread_confined.
"""
if (
IS_FREE_THREADED
self.emitter.target_python.free_threaded
and is_int_rprimitive(attr_rtype)
and not op.is_borrowed
and not cl.attrs_are_thread_confined()
Expand Down Expand Up @@ -659,7 +659,7 @@ def visit_set_attr(self, op: SetAttr) -> None:
if op.error_kind == ERR_FALSE:
self.emitter.emit_line(f"{dest} = 1;")
elif (
IS_FREE_THREADED
self.emitter.target_python.free_threaded
and is_int_rprimitive(attr_rtype)
and not cl.attrs_are_thread_confined()
):
Expand Down
7 changes: 5 additions & 2 deletions mypyc/test/test_emitclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@ def test_free_threaded_ref_attribute_getter_and_setter_use_owner(self) -> None:
assert "CPy_SetAttrRef((PyObject *)self, (PyObject **)&self->_o, tmp)" in generated
assert "CPy_SetAttrRef((PyObject *)self, (PyObject **)&self->_o, NULL)" in generated

@unittest.skipUnless(IS_FREE_THREADED, "requires a free threaded build")
def test_free_threaded_int_attribute_getter_and_setter(self) -> None:
cl = ClassIR("A", "mod")
cl.attributes = {"n": int_rprimitive}
cl.deletable = ["n"]
cl.mro = cl.base_mro = [cl]
emitter = Emitter(EmitterContext(NameGenerator([["mod"]]), True))
emitter = Emitter(
EmitterContext(
NameGenerator([["mod"]]), True, target_python=TargetPython((3, 14), True)
)
)

generate_getter(cl, "n", int_rprimitive, emitter)
generate_setter(cl, "n", int_rprimitive, emitter)
Expand Down
21 changes: 13 additions & 8 deletions mypyc/test/test_emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ def use_free_threaded_target(self) -> None:
NameGenerator([["mod"]]), True, target_python=TargetPython((3, 14), True)
)

def use_gil_target(self) -> None:
self.context = EmitterContext(
NameGenerator([["mod"]]), True, target_python=TargetPython((3, 14), False)
)

def test_goto(self) -> None:
self.assert_emit(Goto(BasicBlock(2)), "goto CPyL2;")

Expand Down Expand Up @@ -523,8 +528,8 @@ def test_list_append(self) -> None:
"""cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o);""",
)

@unittest.skipIf(IS_FREE_THREADED, "free threaded builds use CPyTagged_GetAttr")
def test_get_attr(self) -> None:
self.use_gil_target()
self.assert_emit(
GetAttr(self.r, "y", 1),
"""cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_y;
Expand All @@ -536,8 +541,8 @@ def test_get_attr(self) -> None:
""",
)

@unittest.skipUnless(IS_FREE_THREADED, "requires a free threaded build")
def test_get_attr_int_free_threaded(self) -> None:
self.use_free_threaded_target()
self.assert_emit(
GetAttr(self.r, "y", 1),
"""\
Expand All @@ -548,8 +553,8 @@ def test_get_attr_int_free_threaded(self) -> None:
""",
)

@unittest.skipUnless(IS_FREE_THREADED, "requires a free threaded build")
def test_get_attr_int_merged_free_threaded(self) -> None:
self.use_free_threaded_target()
op = GetAttr(self.r, "y", 1)
branch = Branch(op, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
branch.traceback_entry = ("foobar", 123)
Expand Down Expand Up @@ -589,8 +594,8 @@ def test_get_attr_ref_free_threaded(self) -> None:
""",
)

@unittest.skipIf(IS_FREE_THREADED, "free threaded builds use CPyTagged_GetAttr")
def test_get_attr_merged(self) -> None:
self.use_gil_target()
op = GetAttr(self.r, "y", 1)
branch = Branch(op, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
branch.traceback_entry = ("foobar", 123)
Expand Down Expand Up @@ -630,8 +635,8 @@ def test_get_attr_nullable_with_tuple(self) -> None:
""",
)

@unittest.skipIf(IS_FREE_THREADED, "free threaded builds use CPyTagged_SetAttr")
def test_set_attr(self) -> None:
self.use_gil_target()
self.assert_emit(
SetAttr(self.r, "y", self.m, 1),
"""if (((mod___AObject *)cpy_r_r)->_y != CPY_INT_TAG) {
Expand All @@ -652,8 +657,8 @@ def test_set_attr_ref_free_threaded(self) -> None:
""",
)

@unittest.skipUnless(IS_FREE_THREADED, "requires a free threaded build")
def test_set_attr_int_free_threaded(self) -> None:
self.use_free_threaded_target()
self.assert_emit(
SetAttr(self.r, "y", self.m, 1),
"""\
Expand All @@ -662,8 +667,8 @@ def test_set_attr_int_free_threaded(self) -> None:
""",
)

@unittest.skipUnless(IS_FREE_THREADED, "requires a free threaded build")
def test_set_attr_int_init_free_threaded(self) -> None:
self.use_free_threaded_target()
op = SetAttr(self.r, "y", self.m, 1)
op.mark_as_initializer()
self.assert_emit(op, "CPyTagged_InitAttr(&((mod___AObject *)cpy_r_r)->_y, cpy_r_m);")
Expand All @@ -676,8 +681,8 @@ def test_set_attr_non_refcounted(self) -> None:
""",
)

@unittest.skipIf(IS_FREE_THREADED, "free threaded builds use CPyTagged_SetAttr")
def test_set_attr_no_error(self) -> None:
self.use_gil_target()
op = SetAttr(self.r, "y", self.m, 1)
op.error_kind = ERR_NEVER
self.assert_emit(
Expand Down
Loading