|
6 | 6 | from libclinic.function import ( |
7 | 7 | Function, Parameter, ParamTuple, |
8 | 8 | count_required, group_to_variable_name, permute_optional_groups, |
9 | | - GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT, |
| 9 | + GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT, METHOD_NEW, |
10 | 10 | ACCESSORS, SETTERS) |
11 | 11 | from libclinic.converter import CConverter |
12 | 12 | from libclinic.converters import ( |
@@ -1537,19 +1537,22 @@ def create_template_dict(self) -> dict[str, str]: |
1537 | 1537 | return d2 |
1538 | 1538 |
|
1539 | 1539 | def _vectorcall_type_check(self) -> list[str]: |
1540 | | - """Assert `type` is the one type this vectorcall was generated for. |
| 1540 | + """Check dispatch function hasn't changed. |
1541 | 1541 |
|
1542 | | - The generated code is only correct for that type: __init__ calls |
1543 | | - tp_new with no arguments, then the impl. tp_vectorcall is not |
1544 | | - inherited, so subclasses never reach it; the assert catches C code |
1545 | | - installing the function on a second type. |
| 1542 | + The generated code is only correct for a type whose tp_new / tp_init is |
| 1543 | + the parser this vectorcall shadows. |
1546 | 1544 | """ |
1547 | 1545 | func = self.func |
1548 | | - # The DSL parser rejects @vectorcall without a class and type object. |
1549 | | - assert func.cls is not None |
1550 | | - assert func.cls.type_object |
| 1546 | + if func.kind is METHOD_INIT: |
| 1547 | + check = f"_PyType_CAST(type)->tp_init == {func.c_basename}" |
| 1548 | + elif func.kind is METHOD_NEW: |
| 1549 | + check = f"_PyType_CAST(type)->tp_new == {func.c_basename}" |
| 1550 | + else: |
| 1551 | + raise AssertionError( |
| 1552 | + f"Unhandled function kind for vectorcall: {func.kind!r}" |
| 1553 | + ) |
1551 | 1554 | return [libclinic.normalize_snippet(f""" |
1552 | | - assert(Py_Is(_PyType_CAST(type), {func.cls.type_object})); |
| 1555 | + assert({check}); |
1553 | 1556 | /* Make sure the type object is immutable: the generated |
1554 | 1557 | * vectorcall doesn't deal e.g. with users reassigning __init__. */ |
1555 | 1558 | assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE)); |
@@ -1689,18 +1692,35 @@ def parse_vectorcall_pos_or_kw(self) -> None: |
1689 | 1692 | parser_code.extend(self._vectorcall_positional(arity_checked=True)) |
1690 | 1693 | self.vectorcall_body(*parser_code) |
1691 | 1694 |
|
| 1695 | + def parse_vectorcall_no_args(self) -> None: |
| 1696 | + """No keyword or positional arguments.""" |
| 1697 | + parser_code = self._vectorcall_type_check() |
| 1698 | + self.codegen.add_include('pycore_modsupport.h', |
| 1699 | + '_PyArg_NoKwnames()') |
| 1700 | + parser_code.append(libclinic.normalize_snippet(""" |
| 1701 | + if (nargs) {{ |
| 1702 | + PyErr_SetString(PyExc_TypeError, |
| 1703 | + "{name}() takes no positional arguments"); |
| 1704 | + goto exit; |
| 1705 | + }} |
| 1706 | + if (!_PyArg_NoKwnames("{name}", kwnames)) {{ |
| 1707 | + goto exit; |
| 1708 | + }} |
| 1709 | + """, indent=4)) |
| 1710 | + self.vectorcall_body(*parser_code) |
| 1711 | + |
1692 | 1712 | def parse_vectorcall(self) -> None: |
1693 | 1713 | """Generate the vectorcall entry point for __new__ / __init__. |
1694 | 1714 |
|
1695 | 1715 | Dispatch to specific parser-code builders based on parameter shape. |
1696 | 1716 | """ |
1697 | 1717 | # Branches ordered to mirror parse_args(). The DSL parser rejects |
1698 | 1718 | # @vectorcall with optional groups, and METH_O never applies to |
1699 | | - # __new__/__init__. They always have arguments. |
| 1719 | + # __new__/__init__. |
1700 | 1720 | assert not self.has_option_groups() |
1701 | 1721 | assert not self.use_meth_o() |
1702 | 1722 | if not self.parameters and not self.varpos and not self.var_keyword: |
1703 | | - raise NotImplementedError("No argument vectorcall") |
| 1723 | + self.parse_vectorcall_no_args() |
1704 | 1724 | elif self.var_keyword is not None: |
1705 | 1725 | self.parse_vectorcall_kw_required() |
1706 | 1726 | elif self.pos_only == len(self.parameters): |
|
0 commit comments