Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3e4a720

Browse files
committed
Start adding support for Optional[...]
Also fix a fix bugs that were exposed.
1 parent 0174dac commit 3e4a720

9 files changed

Lines changed: 143 additions & 22 deletions

File tree

doc/dev-intro.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ Here are some hints about how to add support for a new primitive type
182182
`supports_unbox`, `ctype` and various other properties work
183183
correctly for the new type.
184184

185+
* Update `mypyc.genops.Mapper.type_to_rtype()`.
186+
185187
* Update `emit_box` in `mypyc.emit`.
186188

187189
* Update `emit_unbox` or `emit_cast` in `mypyc.emit`.
@@ -193,8 +195,6 @@ Here are some hints about how to add support for a new primitive type
193195

194196
* Update `emit_error_check` in `mypyc.emit` for unboxed types.
195197

196-
* Update `mypyc.genops.Mapper.type_to_rtype()`.
197-
198198
The above may be enough to allow you to declare variables with the
199199
type and pass values around. You likely also want to add support for
200200
some primitive operations for the type (see Built-in Operation for an

mypyc/analysis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def analyze_undefined_regs(blocks: List[BasicBlock],
254254

255255
class LivenessVisitor(BaseAnalysisVisitor):
256256
def visit_branch(self, op: Branch) -> GenAndKill:
257-
return {op.left, op.right}, set()
257+
return set(op.sources()), set()
258258

259259
def visit_return(self, op: Return) -> GenAndKill:
260260
return {op.reg}, set()

mypyc/emit.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from mypyc.common import REG_PREFIX
66
from mypyc.ops import (
7-
Environment, Label, Register, RType, ObjectRType, TupleRType, UserRType, type_struct_name
7+
Environment, Label, Register, RType, ObjectRType, TupleRType, UserRType, OptionalRType,
8+
type_struct_name
89
)
910

1011

@@ -162,7 +163,20 @@ def emit_cast(self, src: str, dest: str, typ: RType, failure: str,
162163
elif typ.name == 'None':
163164
if declare_dest:
164165
self.emit_line('PyObject *{};'.format(dest))
165-
self.emit_line('{} = {};'.format(dest, src))
166+
self.emit_lines(
167+
'if ({} == Py_None)'.format(src),
168+
' {} = {};'.format(dest, src),
169+
'else',
170+
failure)
171+
elif isinstance(typ, OptionalRType):
172+
if declare_dest:
173+
self.emit_line('PyObject *{};'.format(dest))
174+
self.emit_lines(
175+
'if ({} == Py_None)'.format(src),
176+
' {} = {};'.format(dest, src),
177+
'else {')
178+
self.emit_cast(src, dest, typ.value_type, failure.lstrip())
179+
self.emit_line('}')
166180
else:
167181
assert False, 'Cast not implemented: %s' % typ
168182

@@ -263,6 +277,7 @@ def emit_box(self, src: str, dest: str, typ: RType, failure: str,
263277
declare_dest=True)
264278
self.emit_line('PyTuple_SetItem({}, {}, {});'.format(dest, i, inner_name, i))
265279
else:
280+
assert not typ.supports_unbox
266281
# Type is boxed -- trivially just assign.
267282
self.emit_line('{}{} = {};'.format(declaration, dest, src))
268283

mypyc/emitfunc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ def visit_branch(self, op: Branch) -> None:
7373
if op.op == Branch.BOOL_EXPR:
7474
expr_result = self.reg(op.left) # right isn't used
7575
self.emit_line('if ({}({}))'.format(neg, expr_result))
76+
elif op.op == Branch.IS_NONE:
77+
compare = '!=' if op.negated else '=='
78+
self.emit_line('if ({} {} Py_None)'.format(self.reg(op.left), compare))
7679
else:
7780
left = self.reg(op.left)
7881
right = self.reg(op.right)

mypyc/genops.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def f(x: int) -> int:
2323
ContinueStmt, ConditionalExpr, OperatorAssignmentStmt, TupleExpr, ClassDef, TypeInfo,
2424
Import, ImportFrom, ImportAll, ARG_POS, MODULE_REF
2525
)
26-
from mypy.types import Type, Instance, CallableType, NoneTyp, TupleType
26+
from mypy.types import Type, Instance, CallableType, NoneTyp, TupleType, UnionType
2727
from mypy.visitor import NodeVisitor
2828
from mypy.subtypes import is_named_instance
2929

@@ -32,7 +32,7 @@ def f(x: int) -> int:
3232
PrimitiveOp, Branch, Goto, RuntimeArg, Call, Box, Unbox, Cast, TupleRType,
3333
Unreachable, TupleGet, ClassIR, UserRType, ModuleIR, GetAttr, SetAttr, LoadStatic,
3434
PyGetAttr, PyCall, IntRType, BoolRType, ListRType, SequenceTupleRType, ObjectRType, NoneRType,
35-
c_module_name, INVALID_REGISTER, INVALID_LABEL
35+
OptionalRType, c_module_name, INVALID_REGISTER, INVALID_LABEL
3636
)
3737

3838

@@ -69,6 +69,13 @@ def type_to_rtype(self, typ: Type) -> RType:
6969
return ObjectRType()
7070
elif isinstance(typ, NoneTyp):
7171
return NoneRType()
72+
elif isinstance(typ, UnionType):
73+
assert len(typ.items) == 2 and any(isinstance(it, NoneTyp) for it in typ.items)
74+
if isinstance(typ.items[0], NoneTyp):
75+
value_type = typ.items[1]
76+
else:
77+
value_type = typ.items[0]
78+
return OptionalRType(self.type_to_rtype(value_type))
7279
assert False, '%s unsupported' % type(typ)
7380

7481

@@ -788,9 +795,16 @@ def process_conditional(self, e: Node) -> List[Branch]:
788795
right = self.accept(e.operands[1])
789796
opcode = self.int_relative_ops[op]
790797
branch = Branch(left, right, INVALID_LABEL, INVALID_LABEL, opcode)
791-
self.add(branch)
792-
return [branch]
793-
assert False, "unsupported comparison epxression"
798+
elif op in ['is', 'is not']:
799+
left = self.accept(e.operands[0])
800+
branch = Branch(left, INVALID_REGISTER, INVALID_LABEL, INVALID_LABEL,
801+
Branch.IS_NONE)
802+
if op == 'is not':
803+
branch.negated = True
804+
else:
805+
assert False, "unsupported comparison epxression"
806+
self.add(branch)
807+
return [branch]
794808
elif isinstance(e, OpExpr) and e.op in ['and', 'or']:
795809
if e.op == 'and':
796810
# Short circuit 'and' in a conditional context.

mypyc/ops.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
# Eventually we may want to separate expression visitors and statement-like visitors at
3030
# the type level but until then returning INVALID_REGISTER from a statement-like visitor
3131
# seems acceptable.
32-
INVALID_REGISTER = Register(-1)
32+
INVALID_REGISTER = Register(-99999)
3333

3434

3535
# Similarly this is used for placeholder labels which aren't assigned yet (but will
3636
# be eventually. Its kind of a hack.
37-
INVALID_LABEL = Label(-1)
37+
INVALID_LABEL = Label(-88888)
3838

3939

4040
def c_module_name(module_name: str):
@@ -255,6 +255,23 @@ def __repr__(self) -> str:
255255
return '<UserRType %s>' % self.name
256256

257257

258+
class OptionalRType(PyObjectRType):
259+
"""Optional[x]"""
260+
261+
def __init__(self, value_type: RType) -> None:
262+
self.name = 'optional'
263+
self.value_type = value_type
264+
265+
def __repr__(self) -> str:
266+
return '<OptionalRType %s>' % self.value_type
267+
268+
def __eq__(self, other: object) -> bool:
269+
return isinstance(other, OptionalRType) and other.value_type == self.value_type
270+
271+
def __hash__(self) -> int:
272+
return hash(('optional', self.value_type))
273+
274+
258275
class Environment:
259276
"""Keep track of names and types of registers."""
260277

@@ -361,9 +378,10 @@ class Branch(Op):
361378
INT_GT = 14
362379
INT_GE = 15
363380

364-
# Unlike the above, a unary operation so it only uses the "left" register
381+
# Unlike the above, these are unary operations so they only uses the "left" register
365382
# ("right" should be INVALID_REGISTER).
366-
BOOL_EXPR = 16
383+
BOOL_EXPR = 100
384+
IS_NONE = 101
367385

368386
op_names = {
369387
INT_EQ: ('==', 'int'),
@@ -374,6 +392,11 @@ class Branch(Op):
374392
INT_GE: ('>=', 'int'),
375393
}
376394

395+
unary_op_names = {
396+
BOOL_EXPR: ('%r', 'bool'),
397+
IS_NONE: ('%r is None', 'object'),
398+
}
399+
377400
def __init__(self, left: Register, right: Register, true_label: Label,
378401
false_label: Label, op: int) -> None:
379402
self.left = left
@@ -384,24 +407,24 @@ def __init__(self, left: Register, right: Register, true_label: Label,
384407
self.negated = False
385408

386409
def sources(self) -> List[Register]:
387-
return [self.left, self.right]
410+
if self.right != INVALID_REGISTER:
411+
return [self.left, self.right]
412+
else:
413+
return [self.left]
388414

389415
def to_str(self, env: Environment) -> str:
390416
# Right not used for BOOL_EXPR
391-
if self.op != Branch.BOOL_EXPR:
417+
if self.op in self.op_names:
392418
if self.negated:
393419
fmt = 'not %r {} %r'
394420
else:
395421
fmt = '%r {} %r'
396422
op, typ = self.op_names[self.op]
397423
fmt = fmt.format(op)
398-
399424
else:
425+
fmt, typ = self.unary_op_names[self.op]
400426
if self.negated:
401-
fmt = 'not %r'
402-
else:
403-
fmt = '%r'
404-
typ = 'bool'
427+
fmt = 'not {}'.format(fmt)
405428

406429
cond = env.format(fmt, self.left, self.right)
407430
fmt = 'if {} goto %l else goto %l :: {}'.format(cond, typ)
@@ -674,7 +697,7 @@ def sources(self) -> List[Register]:
674697

675698
def to_str(self, env: Environment) -> str:
676699
params = {} # type: Dict[str, Any]
677-
if self.dest is not None:
700+
if self.dest is not None and self.dest != INVALID_REGISTER:
678701
params['dest'] = env.format('%r', self.dest)
679702
args = [env.format('%r', arg) for arg in self.args]
680703
params['args'] = args

test-data/genops-basic.test

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,32 @@ L0:
578578
r5 = cast(None, r2)
579579
r6 = None
580580
return r6
581+
582+
[case testIsNone]
583+
from typing import Optional
584+
585+
class A: pass
586+
587+
def f(x: Optional[A]) -> int:
588+
if x is None:
589+
return 1
590+
if x is not None:
591+
return 2
592+
return 3
593+
[out]
594+
def f(x):
595+
x :: optional
596+
r0, r1, r2 :: int
597+
L0:
598+
if x is None goto L1 else goto L2 :: object
599+
L1:
600+
r0 = 1
601+
return r0
602+
L2:
603+
if not x is None goto L3 else goto L4 :: object
604+
L3:
605+
r1 = 2
606+
return r1
607+
L4:
608+
r2 = 3
609+
return r2

test-data/refcount.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,3 +544,18 @@ L0:
544544
dec_ref d
545545
r3 = None
546546
return r3
547+
548+
[case testUnaryBranchSpecialCase]
549+
def f(x: bool) -> int:
550+
if x:
551+
return 1
552+
return 2
553+
[out]
554+
L0:
555+
if x goto L1 else goto L2 :: bool
556+
L1:
557+
r0 = 1
558+
return r0
559+
L2:
560+
r1 = 2
561+
return r1

test-data/run.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,25 @@ from native import f
257257
f(5)
258258
[out]
259259
5
260+
261+
[case testOptional]
262+
from typing import Optional
263+
264+
class A: pass
265+
266+
def f(x: Optional[A]) -> Optional[A]:
267+
return x
268+
269+
def g(x: Optional[A]) -> int:
270+
if x is None:
271+
return 1
272+
if x is not None:
273+
return 2
274+
return 3
275+
[file driver.py]
276+
from native import f, g, A
277+
a = A()
278+
assert f(None) is None
279+
assert f(a) is a
280+
assert g(None) == 1
281+
assert g(a) == 2

0 commit comments

Comments
 (0)