Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7cd601198124
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e07057c224f9
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Jun 26, 2015

  1. Assignment rhs is typed before lhs.

    whitequark committed Jun 26, 2015
    Copy the full SHA
    71256a7 View commit details
  2. Add range types.

    whitequark committed Jun 26, 2015
    Copy the full SHA
    e07057c View commit details
Showing with 103 additions and 28 deletions.
  1. +24 −3 artiq/py2llvm/builtins.py
  2. +1 −1 artiq/py2llvm/prelude.py
  3. +61 −24 artiq/py2llvm/typing.py
  4. +12 −0 lit-test/py2llvm/typing/error_builtin_calls.py
  5. +5 −0 lit-test/py2llvm/typing/error_iterable.py
27 changes: 24 additions & 3 deletions artiq/py2llvm/builtins.py
Original file line number Diff line number Diff line change
@@ -31,6 +31,12 @@ def __init__(self, elt=None):
elt = types.TVar()
super().__init__("list", {"elt": elt})

class TRange(types.TMono):
def __init__(self, elt=None):
if elt is None:
elt = types.TVar()
super().__init__("range", {"elt": elt})

def fn_bool():
return types.TBuiltin("class bool")

@@ -43,15 +49,15 @@ def fn_float():
def fn_list():
return types.TBuiltin("class list")

def fn_range():
return types.TBuiltin("function range")

def fn_len():
return types.TBuiltin("function len")

def fn_round():
return types.TBuiltin("function round")

def fn_range():
return types.TBuiltin("function range")

def fn_syscall():
return types.TBuiltin("function syscall")

@@ -87,6 +93,21 @@ def is_list(typ, elt=None):
else:
return types.is_mono(typ, "list")

def is_range(typ, elt=None):
if elt:
return types.is_mono(typ, "range", {"elt": elt})
else:
return types.is_mono(typ, "range")

def is_iterable(typ):
typ = typ.find()
return isinstance(typ, types.TMono) and \
typ.name in ('list', 'range')

def get_iterable_elt(typ):
if is_iterable(typ):
return typ.find()["elt"]

def is_collection(typ):
typ = typ.find()
return isinstance(typ, types.TTuple) or \
2 changes: 1 addition & 1 deletion artiq/py2llvm/prelude.py
Original file line number Diff line number Diff line change
@@ -11,8 +11,8 @@ def globals():
"int": builtins.fn_int(),
"float": builtins.fn_float(),
"list": builtins.fn_list(),
"range": builtins.fn_range(),
"len": builtins.fn_len(),
"round": builtins.fn_round(),
"range": builtins.fn_range(),
"syscall": builtins.fn_syscall(),
}
85 changes: 61 additions & 24 deletions artiq/py2llvm/typing.py
Original file line number Diff line number Diff line change
@@ -35,13 +35,13 @@ def visit_in_assign(self, node):
self.in_assign = False

def visit_Assign(self, node):
self.visit(node.value)
for target in node.targets:
self.visit_in_assign(target)
self.visit(node.value)

def visit_For(self, node):
self.visit_in_assign(node.target)
self.visit(node.iter)
self.visit_in_assign(node.target)
self.visit(node.body)
self.visit(node.orelse)

@@ -51,8 +51,8 @@ def visit_withitem(self, node):
self.visit_in_assign(node.optional_vars)

def visit_comprehension(self, node):
self.visit_in_assign(node.target)
self.visit(node.iter)
self.visit_in_assign(node.target)
for if_ in node.ifs:
self.visit(node.ifs)

@@ -454,14 +454,22 @@ def visit_AttributeT(self, node):
node.attr_loc, [node.value.loc])
self.engine.process(diag)

def _unify_collection(self, element, collection):
# TODO: support more than just lists
self._unify(builtins.TList(element.type), collection.type,
element.loc, collection.loc)
def _unify_iterable(self, element, collection):
if builtins.is_iterable(collection.type):
rhs_type = collection.type.find()
rhs_wrapped_lhs_type = types.TMono(rhs_type.name, {"elt": element.type})
self._unify(rhs_wrapped_lhs_type, rhs_type,
element.loc, collection.loc)
elif not types.is_var(collection.type):
diag = diagnostic.Diagnostic("error",
"type {type} is not iterable",
{"type": types.TypePrinter().name(collection.type)},
collection.loc, [])
self.engine.process(diag)

def visit_SubscriptT(self, node):
self.generic_visit(node)
self._unify_collection(element=node, collection=node.value)
self._unify_iterable(element=node, collection=node.value)

def visit_IfExpT(self, node):
self.generic_visit(node)
@@ -678,7 +686,7 @@ def visit_CompareT(self, node):
left.loc, right.loc)
elif all(map(lambda op: isinstance(op, (ast.In, ast.NotIn)), node.ops)):
for left, right in pairs:
self._unify_collection(element=left, collection=right)
self._unify_iterable(element=left, collection=right)
else: # Eq, NotEq, Lt, LtE, Gt, GtE
operands = [node.left] + node.comparators
operand_types = [operand.type for operand in operands]
@@ -713,7 +721,7 @@ def visit_ListCompT(self, node):

def visit_comprehension(self, node):
self.generic_visit(node)
self._unify_collection(element=node.target, collection=node.iter)
self._unify_iterable(element=node.target, collection=node.iter)

def visit_builtin_call(self, node):
typ = node.func.type.find()
@@ -770,6 +778,8 @@ def diagnose(valid_forms):
diag = diagnostic.Diagnostic("error",
"the width argument of int() must be an integer literal", {},
node.keywords[0].loc)
self.engine.process(diag)
return

self._unify(node.type, builtins.TInt(types.TValue(width.n)),
node.loc, None)
@@ -805,9 +815,36 @@ def diagnose(valid_forms):
pass # []
else:
diagnose(valid_forms())
elif builtins.is_builtin(typ, "function range"):
valid_forms = lambda: [
valid_form("range(max:'a) -> range(elt='a)"),
valid_form("range(min:'a, max:'a) -> range(elt='a)"),
valid_form("range(min:'a, max:'a, step:'a) -> range(elt='a)"),
]

range_tvar = types.TVar()
self._unify(node.type, builtins.TRange(range_tvar),
node.loc, None)

if len(node.args) in (1, 2, 3) and len(node.keywords) == 0:
for arg in node.args:
self._unify(arg.type, range_tvar,
arg.loc, None)

if not builtins.is_numeric(arg.type):
note = diagnostic.Diagnostic("note",
"this expression has type {type}",
{"type": types.TypePrinter().name(arg.type)},
arg.loc)
diag = diagnostic.Diagnostic("error",
"an argument of range() must be of a numeric type", {},
node.func.loc, notes=[note])
self.engine.process(diag)
else:
diagnose(valid_forms())
elif builtins.is_builtin(typ, "function len"):
valid_forms = lambda: [
valid_form("len(x:list(elt='a)) -> int(width='b)"),
valid_form("len(x:'a) -> int(width='b) where 'a is iterable"),
]

# TODO: should be ssize_t-sized
@@ -817,8 +854,17 @@ def diagnose(valid_forms):
if len(node.args) == 1 and len(node.keywords) == 0:
arg, = node.args

self._unify(arg.type, builtins.TList(),
arg.loc, None)
if builtins.is_list(arg.type) or builtins.is_range(arg.type):
pass
else:
note = diagnostic.Diagnostic("note",
"this expression has type {type}",
{"type": types.TypePrinter().name(arg.type)},
arg.loc)
diag = diagnostic.Diagnostic("error",
"the argument of len() must be of an iterable type", {},
node.func.loc, notes=[note])
self.engine.process(diag)
else:
diagnose(valid_forms())
elif builtins.is_builtin(typ, "function round"):
@@ -836,13 +882,6 @@ def diagnose(valid_forms):
arg.loc, None)
else:
diagnose(valid_forms())
# TODO: add when there are range types
# elif builtins.is_builtin(typ, "function range"):
# valid_forms = lambda: [
# valid_form("range(max:'a) -> range(elt='a)"),
# valid_form("range(min:'a, max:'a) -> range(elt='a)"),
# valid_form("range(min:'a, max:'a, step:'a) -> range(elt='a)"),
# ]
# TODO: add when it is clear what interface syscall() has
# elif builtins.is_builtin(typ, "function syscall"):
# valid_Forms = lambda: [
@@ -862,7 +901,7 @@ def visit_CallT(self, node):

if types.is_var(node.func.type):
return # not enough info yet
elif types.is_mono(node.func.type) or types.is_builtin(node.func.type):
elif types.is_builtin(node.func.type):
return self.visit_builtin_call(node)
elif not types.is_function(node.func.type):
diag = diagnostic.Diagnostic("error",
@@ -988,9 +1027,7 @@ def visit_For(self, node):
old_in_loop, self.in_loop = self.in_loop, True
self.generic_visit(node)
self.in_loop = old_in_loop
# TODO: support more than just lists
self._unify(builtins.TList(node.target.type), node.iter.type,
node.target.loc, node.iter.loc)
self._unify_iterable(node.target, node.iter)

def visit_While(self, node):
old_in_loop, self.in_loop = self.in_loop, True
12 changes: 12 additions & 0 deletions lit-test/py2llvm/typing/error_builtin_calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# RUN: %python -m artiq.py2llvm.typing +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

a = 1
# CHECK-L: ${LINE:+1}: error: the width argument of int() must be an integer literal
int(1.0, width=a)

# CHECK-L: ${LINE:+1}: error: the argument of len() must be of an iterable type
len(1)

# CHECK-L: ${LINE:+1}: error: an argument of range() must be of a numeric type
range([])
5 changes: 5 additions & 0 deletions lit-test/py2llvm/typing/error_iterable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# RUN: %python -m artiq.py2llvm.typing +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

# CHECK-L: ${LINE:+1}: error: type int(width='a) is not iterable
for x in 1: pass