Skip to content

Commit ddd9816

Browse files
Merge branch 'master' into patch-11
2 parents 3e5b054 + 6adb7a4 commit ddd9816

File tree

9 files changed

+86
-25
lines changed

9 files changed

+86
-25
lines changed

mypy/stubtest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,7 @@ def verify_typealias(
16531653
"__loader__",
16541654
"__spec__",
16551655
"__annotations__",
1656+
"__conditional_annotations__", # 3.14+
16561657
"__annotate__",
16571658
"__path__", # mypy adds __path__ to packages, but C packages don't have it
16581659
"__getattr__", # resulting behaviour might be typed explicitly

mypyc/codegen/emit.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
TYPE_PREFIX,
2424
)
2525
from mypyc.ir.class_ir import ClassIR, all_concrete_classes
26-
from mypyc.ir.func_ir import FuncDecl, FuncIR, get_text_signature
26+
from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncDecl, FuncIR, get_text_signature
2727
from mypyc.ir.ops import BasicBlock, Value
2828
from mypyc.ir.rtypes import (
2929
RInstance,
@@ -1222,10 +1222,11 @@ def emit_cpyfunction_instance(
12221222
cfunc = f"(PyCFunction){cname}"
12231223
func_flags = "METH_FASTCALL | METH_KEYWORDS"
12241224
doc = f"PyDoc_STR({native_function_doc_initializer(fn)})"
1225+
has_self_arg = "true" if fn.class_name and fn.decl.kind != FUNC_STATICMETHOD else "false"
12251226

12261227
code_flags = "CO_COROUTINE"
12271228
self.emit_line(
1228-
f'PyObject* {wrapper_name} = CPyFunction_New({module}, "{filepath}", "{name}", {cfunc}, {func_flags}, {doc}, {fn.line}, {code_flags});'
1229+
f'PyObject* {wrapper_name} = CPyFunction_New({module}, "{filepath}", "{name}", {cfunc}, {func_flags}, {doc}, {fn.line}, {code_flags}, {has_self_arg});'
12291230
)
12301231
self.emit_line(f"if (unlikely(!{wrapper_name}))")
12311232
self.emit_line(error_stmt)

mypyc/irbuild/for_helpers.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from mypy.checkexpr import try_getting_literal
1414
from mypy.nodes import (
1515
ARG_POS,
16-
BytesExpr,
1716
CallExpr,
1817
DictionaryComprehension,
1918
Expression,
@@ -25,10 +24,8 @@
2524
RefExpr,
2625
SetExpr,
2726
StarExpr,
28-
StrExpr,
2927
TupleExpr,
3028
TypeAlias,
31-
Var,
3229
)
3330
from mypy.types import LiteralType, TupleType, get_proper_type, get_proper_types
3431
from mypyc.ir.ops import (
@@ -69,6 +66,7 @@
6966
short_int_rprimitive,
7067
)
7168
from mypyc.irbuild.builder import IRBuilder
69+
from mypyc.irbuild.constant_fold import constant_fold_expr
7270
from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
7371
from mypyc.irbuild.targets import AssignmentTarget, AssignmentTargetTuple
7472
from mypyc.primitives.dict_ops import (
@@ -1227,8 +1225,9 @@ def gen_cleanup(self) -> None:
12271225

12281226

12291227
def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None:
1230-
if isinstance(expr, (StrExpr, BytesExpr)):
1231-
return len(expr.value)
1228+
folded = constant_fold_expr(builder, expr)
1229+
if isinstance(folded, (str, bytes)):
1230+
return len(folded)
12321231
elif isinstance(expr, (ListExpr, TupleExpr)):
12331232
# if there are no star expressions, or we know the length of them,
12341233
# we know the length of the expression
@@ -1238,14 +1237,6 @@ def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None:
12381237
return other + sum(stars) # type: ignore [arg-type]
12391238
elif isinstance(expr, StarExpr):
12401239
return get_expr_length(builder, expr.expr)
1241-
elif (
1242-
isinstance(expr, RefExpr)
1243-
and isinstance(expr.node, Var)
1244-
and expr.node.is_final
1245-
and isinstance(expr.node.final_value, str)
1246-
and expr.node.has_explicit_value
1247-
):
1248-
return len(expr.node.final_value)
12491240
# TODO: extend this, passing length of listcomp and genexp should have worthwhile
12501241
# performance boost and can be (sometimes) figured out pretty easily. set and dict
12511242
# comps *can* be done as well but will need special logic to consider the possibility

mypyc/lib-rt/CPy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ typedef struct {
971971

972972
PyObject* CPyFunction_New(PyObject *module, const char *filename, const char *funcname,
973973
PyCFunction func, int func_flags, const char *func_doc,
974-
int first_line, int code_flags);
974+
int first_line, int code_flags, bool has_self_arg);
975975
PyObject* CPyFunction_get_name(PyObject *op, void *context);
976976
int CPyFunction_set_name(PyObject *op, PyObject *value, void *context);
977977
PyObject* CPyFunction_get_code(PyObject *op, void *context);

mypyc/lib-rt/function_wrapper.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,21 @@ static PyObject* CPyFunction_Vectorcall(PyObject *func, PyObject *const *args, s
177177
PyCFunction meth = ((PyCFunctionObject *)f)->m_ml->ml_meth;
178178

179179
self = ((PyCFunctionObject *)f)->m_self;
180+
if (!self) {
181+
self = args[0];
182+
args += 1;
183+
nargs -= 1;
184+
}
180185
return ((_PyCFunctionFastWithKeywords)(void(*)(void))meth)(self, args, nargs, kwnames);
181186
}
182187

183188

184189
static CPyFunction* CPyFunction_Init(CPyFunction *op, PyMethodDef *ml, PyObject* name,
185-
PyObject *module, PyObject* code) {
190+
PyObject *module, PyObject* code, bool set_self) {
186191
PyCFunctionObject *cf = (PyCFunctionObject *)op;
187192
CPyFunction_weakreflist(op) = NULL;
188193
cf->m_ml = ml;
189-
cf->m_self = (PyObject *) op;
194+
cf->m_self = set_self ? (PyObject *) op : NULL;
190195

191196
Py_XINCREF(module);
192197
cf->m_module = module;
@@ -226,9 +231,10 @@ static PyMethodDef* CPyMethodDef_New(const char *name, PyCFunction func, int fla
226231

227232
PyObject* CPyFunction_New(PyObject *module, const char *filename, const char *funcname,
228233
PyCFunction func, int func_flags, const char *func_doc,
229-
int first_line, int code_flags) {
234+
int first_line, int code_flags, bool has_self_arg) {
230235
PyMethodDef *method = NULL;
231236
PyObject *code = NULL, *op = NULL;
237+
bool set_self = false;
232238

233239
if (!CPyFunctionType) {
234240
CPyFunctionType = (PyTypeObject *)PyType_FromSpec(&CPyFunction_spec);
@@ -245,8 +251,15 @@ PyObject* CPyFunction_New(PyObject *module, const char *filename, const char *fu
245251
if (unlikely(!code)) {
246252
goto err;
247253
}
254+
255+
// Set m_self inside the function wrapper only if the wrapped function has no self arg
256+
// to pass m_self as the self arg when the function is called.
257+
// When the function has a self arg, it will come in the args vector passed to the
258+
// vectorcall handler.
259+
set_self = !has_self_arg;
248260
op = (PyObject *)CPyFunction_Init(PyObject_GC_New(CPyFunction, CPyFunctionType),
249-
method, PyUnicode_FromString(funcname), module, code);
261+
method, PyUnicode_FromString(funcname), module,
262+
code, set_self);
250263
if (unlikely(!op)) {
251264
goto err;
252265
}

mypyc/test-data/irbuild-tuple.test

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,54 @@ L4:
434434
a = r1
435435
return 1
436436

437+
[case testTupleBuiltFromConstantFolding]
438+
from typing import Final
439+
440+
c: Final = "c"
441+
442+
def f2(val: str) -> str:
443+
return val + "f2"
444+
445+
def test() -> None:
446+
# `"ab" + c` should constant fold to "abc"
447+
a = tuple(f2(x) for x in "ab" + c)
448+
449+
[out]
450+
def f2(val):
451+
val, r0, r1 :: str
452+
L0:
453+
r0 = 'f2'
454+
r1 = PyUnicode_Concat(val, r0)
455+
return r1
456+
def test():
457+
r0 :: str
458+
r1 :: tuple
459+
r2 :: native_int
460+
r3 :: bit
461+
r4, x, r5 :: str
462+
r6 :: native_int
463+
a :: tuple
464+
L0:
465+
r0 = 'abc'
466+
r1 = PyTuple_New(3)
467+
r2 = 0
468+
goto L2
469+
L1:
470+
r3 = r2 < 3 :: signed
471+
if r3 goto L2 else goto L4 :: bool
472+
L2:
473+
r4 = CPyStr_GetItemUnsafe(r0, r2)
474+
x = r4
475+
r5 = f2(x)
476+
CPySequenceTuple_SetItemUnsafe(r1, r2, r5)
477+
L3:
478+
r6 = r2 + 1
479+
r2 = r6
480+
goto L1
481+
L4:
482+
a = r1
483+
return 1
484+
437485
[case testTupleBuiltFromFinalStr]
438486
from typing import Final
439487

mypyc/test-data/run-async.test

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ async def sleep(t: float) -> None: ...
228228

229229
[case testAsyncWith]
230230
from testutil import async_val
231+
from typing import Any
231232

232233
class async_ctx:
233234
async def __aenter__(self) -> str:
@@ -242,15 +243,22 @@ async def async_with() -> str:
242243
async with async_ctx() as x:
243244
return await async_val("body")
244245

246+
async def async_with_vectorcall() -> str:
247+
ctx: Any = async_ctx()
248+
async with ctx:
249+
return await async_val("vc")
245250

246251
[file driver.py]
247-
from native import async_with
252+
from native import async_with, async_with_vectorcall
248253
from testutil import run_generator
249254

250255
yields, val = run_generator(async_with(), [None, 'x', None])
251256
assert yields == ('enter', 'body', 'exit'), yields
252257
assert val == 'x', val
253258

259+
yields, val = run_generator(async_with_vectorcall(), [None, 'x', None])
260+
assert yields == ('enter', 'vc', 'exit'), yields
261+
assert val == 'x', val
254262

255263
[case testAsyncReturn]
256264
from testutil import async_val
@@ -1516,7 +1524,9 @@ def test_method() -> None:
15161524
assert str(T.returns_one_async).startswith("<function T.returns_one_async"), str(T.returns_one_async)
15171525

15181526
t = T()
1519-
assert asyncio.run(t.returns_one_async()) == 1
1527+
# Call through variable to make sure the call is through vectorcall and not optimized to a native call.
1528+
f: Any = t.returns_one_async
1529+
assert asyncio.run(f()) == 1
15201530

15211531
assert not is_coroutine(T.returns_two)
15221532
assert is_coroutine(T.returns_two_async)

test-data/unit/check-incremental.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2336,7 +2336,6 @@ tmp/c.py:1: error: Module "d" has no attribute "x"
23362336
[out]
23372337
[out2]
23382338
mypy: can't read file 'tmp/nonexistent.py': No such file or directory
2339-
-- '
23402339

23412340
[case testSerializeAbstractPropertyIncremental]
23422341
from abc import abstractmethod

test-data/unit/cmdline.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ sub.pkg is not a valid Python package name
117117
mypy: can't decode file 'a.py': unknown encoding: uft-8
118118
== Return code: 2
119119

120-
-- '
121120
[case testCannotIgnoreDuplicateModule]
122121
# cmd: mypy one/mod/__init__.py two/mod/__init__.py
123122
[file one/mod/__init__.py]
@@ -546,7 +545,6 @@ a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None";
546545
[out]
547546
mypy: can't read file 'nope.py': No such file or directory
548547
== Return code: 2
549-
--'
550548

551549
[case testModulesAndPackages]
552550
# cmd: mypy --package p.a --package p.b --module c

0 commit comments

Comments
 (0)