Skip to content

Commit 0a6a48c

Browse files
[mypyc] Add 'range' primitive type (#10307)
Closes mypyc/mypyc#770.
1 parent 0af616e commit 0a6a48c

File tree

9 files changed

+134
-10
lines changed

9 files changed

+134
-10
lines changed

mypyc/codegen/emit.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
is_list_rprimitive, is_dict_rprimitive, is_set_rprimitive, is_tuple_rprimitive,
1616
is_none_rprimitive, is_object_rprimitive, object_rprimitive, is_str_rprimitive,
1717
int_rprimitive, is_optional_type, optional_value_type, is_int32_rprimitive,
18-
is_int64_rprimitive, is_bit_rprimitive
18+
is_int64_rprimitive, is_bit_rprimitive, is_range_rprimitive
1919
)
2020
from mypyc.ir.func_ir import FuncDecl
2121
from mypyc.ir.class_ir import ClassIR, all_concrete_classes
@@ -410,8 +410,8 @@ def emit_cast(self, src: str, dest: str, typ: RType, declare_dest: bool = False,
410410

411411
# TODO: Verify refcount handling.
412412
if (is_list_rprimitive(typ) or is_dict_rprimitive(typ) or is_set_rprimitive(typ)
413-
or is_float_rprimitive(typ) or is_str_rprimitive(typ) or is_int_rprimitive(typ)
414-
or is_bool_rprimitive(typ)):
413+
or is_str_rprimitive(typ) or is_range_rprimitive(typ) or is_float_rprimitive(typ)
414+
or is_int_rprimitive(typ) or is_bool_rprimitive(typ)):
415415
if declare_dest:
416416
self.emit_line('PyObject *{};'.format(dest))
417417
if is_list_rprimitive(typ):
@@ -420,10 +420,12 @@ def emit_cast(self, src: str, dest: str, typ: RType, declare_dest: bool = False,
420420
prefix = 'PyDict'
421421
elif is_set_rprimitive(typ):
422422
prefix = 'PySet'
423-
elif is_float_rprimitive(typ):
424-
prefix = 'CPyFloat'
425423
elif is_str_rprimitive(typ):
426424
prefix = 'PyUnicode'
425+
elif is_range_rprimitive(typ):
426+
prefix = 'PyRange'
427+
elif is_float_rprimitive(typ):
428+
prefix = 'CPyFloat'
427429
elif is_int_rprimitive(typ):
428430
prefix = 'PyLong'
429431
elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):

mypyc/ir/rtypes.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ def __hash__(self) -> int:
326326
tuple_rprimitive = RPrimitive('builtins.tuple', is_unboxed=False,
327327
is_refcounted=True) # type: Final
328328

329+
# Python range object.
330+
range_rprimitive = RPrimitive('builtins.range', is_unboxed=False,
331+
is_refcounted=True) # type: Final
332+
329333

330334
def is_tagged(rtype: RType) -> bool:
331335
return rtype is int_rprimitive or rtype is short_int_rprimitive
@@ -405,6 +409,10 @@ def is_tuple_rprimitive(rtype: RType) -> bool:
405409
return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.tuple'
406410

407411

412+
def is_range_rprimitive(rtype: RType) -> bool:
413+
return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.range'
414+
415+
408416
def is_sequence_rprimitive(rtype: RType) -> bool:
409417
return isinstance(rtype, RPrimitive) and (
410418
is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype)
@@ -805,5 +813,5 @@ def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RArray':
805813
PyListObject = RStruct(
806814
name='PyListObject',
807815
names=['ob_base', 'ob_item', 'allocated'],
808-
types=[PyObject, pointer_rprimitive, c_pyssize_t_rprimitive]
816+
types=[PyVarObject, pointer_rprimitive, c_pyssize_t_rprimitive]
809817
)

mypyc/irbuild/mapper.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from mypyc.ir.rtypes import (
1313
RType, RUnion, RTuple, RInstance, object_rprimitive, dict_rprimitive, tuple_rprimitive,
1414
none_rprimitive, int_rprimitive, float_rprimitive, str_rprimitive, bool_rprimitive,
15-
list_rprimitive, set_rprimitive
15+
list_rprimitive, set_rprimitive, range_rprimitive
1616
)
1717
from mypyc.ir.func_ir import FuncSignature, FuncDecl, RuntimeArg
1818
from mypyc.ir.class_ir import ClassIR
@@ -58,6 +58,8 @@ def type_to_rtype(self, typ: Optional[Type]) -> RType:
5858
return set_rprimitive
5959
elif typ.type.fullname == 'builtins.tuple':
6060
return tuple_rprimitive # Varying-length tuple
61+
elif typ.type.fullname == 'builtins.range':
62+
return range_rprimitive
6163
elif typ.type in self.type_to_ir:
6264
inst = RInstance(self.type_to_ir[typ.type])
6365
# Treat protocols as Union[protocol, object], so that we can do fast

mypyc/primitives/misc_ops.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
type=object_rprimitive,
1616
src='PyBool_Type')
1717

18+
# Get the 'range' type object.
19+
load_address_op(
20+
name='builtins.range',
21+
type=object_rprimitive,
22+
src='PyRange_Type')
23+
1824
# Get the boxed Python 'None' object
1925
none_object_op = load_address_op(
2026
name='Py_None',

mypyc/test-data/fixtures/ir.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ def __or__(self, s: Set[S]) -> Set[Union[T, S]]: ...
193193

194194
class slice: pass
195195

196+
class range(Iterable[int]):
197+
def __init__(self, x: int, y: int = ..., z: int = ...) -> None: pass
198+
def __iter__(self) -> Iterator[int]: pass
199+
def __len__(self) -> int: pass
200+
def __next__(self) -> int: pass
201+
196202
class property:
197203
def __init__(self, fget: Optional[Callable[[Any], Any]] = ...,
198204
fset: Optional[Callable[[Any, Any], None]] = ...,
@@ -245,7 +251,6 @@ def id(o: object) -> int: pass
245251
# This type is obviously wrong but the test stubs don't have Sized anymore
246252
def len(o: object) -> int: pass
247253
def print(*object) -> None: pass
248-
def range(x: int, y: int = ..., z: int = ...) -> Iterator[int]: pass
249254
def isinstance(x: object, t: object) -> bool: pass
250255
def iter(i: Iterable[T]) -> Iterator[T]: pass
251256
@overload

mypyc/test-data/irbuild-basic.test

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3649,3 +3649,71 @@ L0:
36493649
r2 = r1 >= 0 :: signed
36503650
r3 = truncate r1: int32 to builtins.bool
36513651
return r3
3652+
3653+
[case testRangeObject]
3654+
def range_object() -> None:
3655+
r = range(4, 12, 2)
3656+
sum = 0
3657+
for i in r:
3658+
sum += i
3659+
3660+
def range_in_loop() -> None:
3661+
sum = 0
3662+
for i in range(4, 12, 2):
3663+
sum += i
3664+
[out]
3665+
def range_object():
3666+
r0, r1, r2, r3, r4 :: object
3667+
r5, r :: range
3668+
sum :: int
3669+
r6, r7 :: object
3670+
r8, i, r9 :: int
3671+
r10 :: bit
3672+
L0:
3673+
r0 = load_address PyRange_Type
3674+
r1 = box(short_int, 8)
3675+
r2 = box(short_int, 24)
3676+
r3 = box(short_int, 4)
3677+
r4 = PyObject_CallFunctionObjArgs(r0, r1, r2, r3, 0)
3678+
r5 = cast(range, r4)
3679+
r = r5
3680+
sum = 0
3681+
r6 = PyObject_GetIter(r)
3682+
L1:
3683+
r7 = PyIter_Next(r6)
3684+
if is_error(r7) goto L4 else goto L2
3685+
L2:
3686+
r8 = unbox(int, r7)
3687+
i = r8
3688+
r9 = CPyTagged_Add(sum, i)
3689+
sum = r9
3690+
L3:
3691+
goto L1
3692+
L4:
3693+
r10 = CPy_NoErrOccured()
3694+
L5:
3695+
return 1
3696+
def range_in_loop():
3697+
sum :: int
3698+
r0 :: short_int
3699+
i :: int
3700+
r1 :: bit
3701+
r2 :: int
3702+
r3 :: short_int
3703+
L0:
3704+
sum = 0
3705+
r0 = 8
3706+
i = r0
3707+
L1:
3708+
r1 = r0 < 24 :: signed
3709+
if r1 goto L2 else goto L4 :: bool
3710+
L2:
3711+
r2 = CPyTagged_Add(sum, i)
3712+
sum = r2
3713+
L3:
3714+
r3 = r0 + 4
3715+
r0 = r3
3716+
i = r3
3717+
goto L1
3718+
L4:
3719+
return 1

mypyc/test-data/run-dicts.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Dict test cases (compile and run)
1+
# Test cases for dicts (compile and run)
22

33
[case testDictStuff]
44
from typing import Dict, Any, List, Set, Tuple

mypyc/test-data/run-floats.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Test cases for floats (compile and run)
2+
13
[case testStrToFloat]
24
def str_to_float(x: str) -> float:
35
return float(x)

mypyc/test-data/run-loops.test

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Test cases for "for" and "while" loops (compile and run)
1+
# Test cases for "range" objects, "for" and "while" loops (compile and run)
22

33
[case testFor]
44
from typing import List, Tuple
@@ -452,3 +452,34 @@ def bar(x: Optional[str]) -> None:
452452
[file driver.py]
453453
from native import bar
454454
bar(None)
455+
456+
[case testRangeObject]
457+
from typing import Any
458+
459+
def f(x: range) -> int:
460+
sum = 0
461+
for i in x:
462+
sum += i
463+
return sum
464+
465+
def test_range_object() -> None:
466+
r1 = range(4, 12, 2)
467+
tmp_list = [x for x in r1]
468+
assert tmp_list == [4, 6, 8, 10]
469+
assert f(r1) == 28
470+
r2: Any = range(10)
471+
assert f(r2) == 45
472+
r3: Any = 'x'
473+
try:
474+
f(r3)
475+
except TypeError as e:
476+
assert "range object expected; got str" in str(e)
477+
try:
478+
ff: Any = f
479+
ff(r3)
480+
except TypeError as e:
481+
assert "range object expected; got str" in str(e)
482+
try:
483+
r4 = range(4, 12, 0)
484+
except ValueError as e:
485+
assert "range() arg 3 must not be zero" in str(e)

0 commit comments

Comments
 (0)