diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 9933e90d2ab55..05975d7b2bf5e 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -186,27 +186,29 @@ def translate_set_from_generator_call( @specialize_function('builtins.min') -def faster_min(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: +@specialize_function('builtins.max') +def faster_min_max(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: if expr.arg_kinds == [ARG_POS, ARG_POS]: x, y = builder.accept(expr.args[0]), builder.accept(expr.args[1]) result = Register(builder.node_type(expr)) - comparison = builder.binary_op(y, x, '<', expr.line) - true = BasicBlock() - false = BasicBlock() - next_block = BasicBlock() + # CPython evaluates arguments reversely when calling min(...) or max(...) + if callee.fullname == 'builtins.min': + comparison = builder.binary_op(y, x, '<', expr.line) + else: + comparison = builder.binary_op(y, x, '>', expr.line) - builder.add_bool_branch(comparison, true, false) + true_block, false_block, next_block = BasicBlock(), BasicBlock(), BasicBlock() + builder.add_bool_branch(comparison, true_block, false_block) - builder.activate_block(true) + builder.activate_block(true_block) builder.assign(result, builder.coerce(y, result.type, expr.line), expr.line) builder.goto(next_block) - builder.activate_block(false) + builder.activate_block(false_block) builder.assign(result, builder.coerce(x, result.type, expr.line), expr.line) builder.goto(next_block) builder.activate_block(next_block) - return result return None diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index f57cc2818a8aa..786f77143e9ee 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -303,6 +303,7 @@ def eval(e: str) -> Any: ... def abs(x: float) -> float: ... def exit() -> None: ... def min(x: T, y: T) -> T: ... +def max(x: T, y: T) -> T: ... def repr(o: object) -> str: ... def ascii(o: object) -> str: ... def ord(o: object) -> int: ... diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 6abd1105bbad1..6ec8eb58fe7d1 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -122,3 +122,29 @@ L0: r6 = C(r5) x = r6 return 1 + +[case testMax] +from typing import TypeVar +T = TypeVar('T') +def f(x: T, y: T) -> T: + return max(x, y) +[out] +def f(x, y): + x, y, r0 :: object + r1 :: int32 + r2 :: bit + r3 :: bool + r4 :: object +L0: + r0 = PyObject_RichCompare(y, x, 4) + r1 = PyObject_IsTrue(r0) + r2 = r1 >= 0 :: signed + r3 = truncate r1: int32 to builtins.bool + if r3 goto L1 else goto L2 :: bool +L1: + r4 = y + goto L3 +L2: + r4 = x +L3: + return r4 diff --git a/mypyc/test-data/run-dunders.test b/mypyc/test-data/run-dunders.test index 500421099bf68..aee2a956c47f2 100644 --- a/mypyc/test-data/run-dunders.test +++ b/mypyc/test-data/run-dunders.test @@ -758,7 +758,7 @@ def test_in_place_operator_returns_none() -> None: with assertRaises(TypeError, "native.BadInplaceAdd object expected; got None"): o += 5 -[case testDunderMin] +[case testDunderMinMax] class SomeItem: def __init__(self, val: int) -> None: self.val = val @@ -766,6 +766,9 @@ class SomeItem: def __lt__(self, x: 'SomeItem') -> bool: return self.val < x.val + def __gt__(self, x: 'SomeItem') -> bool: + return self.val > x.val + class AnotherItem: def __init__(self, val: str) -> None: self.val = val @@ -773,14 +776,23 @@ class AnotherItem: def __lt__(self, x: 'AnotherItem') -> bool: return True + def __gt__(self, x: 'AnotherItem') -> bool: + return True + def test_dunder_min() -> None: x = SomeItem(5) y = SomeItem(10) z = SomeItem(15) assert min(x, y).val == 5 assert min(y, z).val == 10 + assert max(x, y).val == 10 + assert max(y, z).val == 15 x2 = AnotherItem('xxx') y2 = AnotherItem('yyy') z2 = AnotherItem('zzz') assert min(x2, y2).val == 'yyy' assert min(y2, x2).val == 'xxx' + assert max(x2, y2).val == 'yyy' + assert max(y2, x2).val == 'xxx' + assert min(y2, z2).val == 'zzz' + assert max(x2, z2).val == 'zzz' diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index 6050ee3e6285c..1b67a1190cd8b 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -21,8 +21,10 @@ def test_abs() -> None: assert abs(-23.4) == 23.4 assert abs(-43.44e-4) == 43.44e-4 -def test_float_min() -> None: +def test_float_min_max() -> None: x: float = 20.0 y: float = 30.0 assert min(x, y) == 20.0 assert min(y, x) == 20.0 + assert max(x, y) == 30.0 + assert max(y, x) == 30.0 diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index 70950ce803285..b9668d6dec9f7 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -428,43 +428,51 @@ def test_constant_fold() -> None: n64 = -(1 << 64) + int() assert n64 == -(1 << 64) -[case testIntMin] -def test_int_min() -> None: +[case testIntMinMax] +def test_int_min_max() -> None: x: int = 200 y: int = 30 assert min(x, y) == 30 + assert max(x, y) == 200 + assert min(y, x) == 30 + assert max(y, x) == 200 -def test_int_min_2() -> None: - x: int = 30 - y: int = 200 - assert min(x, y) == 30 - -def test_int_hybrid_min() -> None: +def test_int_hybrid_min_max() -> None: from typing import Any x: object = 30 y: Any = 20.0 assert min(x, y) == 20.0 + assert max(x, y) == 30 u: object = 20 v: float = 30.0 assert min(u, v) == 20 + assert max(u, v) == 30.0 -def test_int_incompatible_min() -> None: +def test_int_incompatible_min_max() -> None: x: int = 2 y: str = 'aaa' try: print(min(x, y)) except TypeError as e: assert str(e) == "'<' not supported between instances of 'str' and 'int'" + try: + print(max(x, y)) + except TypeError as e: + assert str(e) == "'>' not supported between instances of 'str' and 'int'" -def test_int_bool_min() -> None: +def test_int_bool_min_max() -> None: x: int = 2 y: bool = False z: bool = True assert min(x, y) == False assert min(x, z) == True + assert max(x, y) == 2 + assert max(x, z) == 2 u: int = -10 assert min(u, y) == -10 assert min(u, z) == -10 + assert max(u, y) == False + assert max(u, z) == True diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index d7afce458f90b..c2b010bdb2bdc 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -149,12 +149,14 @@ def test_str_to_bool() -> None: assert is_true(x) assert not is_false(x) -def test_str_min() -> None: +def test_str_min_max() -> None: x: str = 'aaa' y: str = 'bbb' z: str = 'aa' assert min(x, y) == 'aaa' assert min(x, z) == 'aa' + assert max(x, y) == 'bbb' + assert max(x, z) == 'aaa' [case testStringFormattingCStyle] [typing fixtures/typing-full.pyi]