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: 5baf18ba0d94
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: 2955f9a1e0d1
Choose a head ref
  • 2 commits
  • 10 files changed
  • 1 contributor

Commits on Jan 4, 2016

  1. Copy the full SHA
    dfbf55f View commit details
  2. Commit missing parts of 5baf18b.

    whitequark committed Jan 4, 2016
    Copy the full SHA
    2955f9a View commit details
2 changes: 1 addition & 1 deletion artiq/compiler/analyses/devirtualization.py
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ def visit_ForT(self, node):
self.visit(node.body)
self.visit(node.orelse)

def visit_withitem(self, node):
def visit_withitemT(self, node):
self.visit(node.context_expr)
self.visit_in_assign(node.optional_vars)

3 changes: 3 additions & 0 deletions artiq/compiler/asttyped.py
Original file line number Diff line number Diff line change
@@ -42,6 +42,9 @@ class ForT(ast.For):
:ivar trip_interval: (:class:`iodelay.Expr`)
"""

class withitemT(ast.withitem):
_types = ("enter_type", "exit_type")

class SliceT(ast.Slice, commontyped):
pass

4 changes: 2 additions & 2 deletions artiq/compiler/embedding.py
Original file line number Diff line number Diff line change
@@ -303,8 +303,8 @@ def _unify_attribute(self, result_type, value_node, attr_name, attr_loc, loc):
# which would be the optimal solution.

object_type = value_node.type.find()
attr_value_type = None
for object_value, object_loc in self.value_map[object_type]:
attr_value_type = None
if not hasattr(object_value, attr_name):
if attr_name.startswith('_'):
names = set(filter(lambda name: not name.startswith('_'),
@@ -412,7 +412,7 @@ def proxy_diagnostic(diag):
" different from previously inferred type {typeb} for the same attribute",
{"typea": printer.name(attr_value_type),
"typeb": printer.name(attributes[attr_name]),
"attr": node.attr},
"attr": attr_name},
object_loc)
self.engine.process(diag)

8 changes: 8 additions & 0 deletions artiq/compiler/transforms/asttyped_rewriter.py
Original file line number Diff line number Diff line change
@@ -480,6 +480,14 @@ def visit_For(self, node):
else_loc=node.else_loc, else_colon_loc=node.else_colon_loc)
return node

def visit_withitem(self, node):
node = self.generic_visit(node)
node = asttyped.withitemT(
context_expr=node.context_expr, optional_vars=node.optional_vars,
enter_type=types.TVar(), exit_type=types.TVar(),
as_loc=node.as_loc, loc=node.loc)
return node

# Unsupported visitors
#
def visit_unsupported(self, node):
95 changes: 88 additions & 7 deletions artiq/compiler/transforms/inferencer.py
Original file line number Diff line number Diff line change
@@ -160,7 +160,7 @@ def makenotes(printer, typea, typeb, loca, locb):
self._unify(result_type, attr_type,
loc, None)
else:
if attr_name_loc.source_buffer == value_node.loc.source_buffer:
if attr_loc.source_buffer == value_node.loc.source_buffer:
highlights, notes = [value_node.loc], []
else:
# This happens when the object being accessed is embedded
@@ -173,7 +173,7 @@ def makenotes(printer, typea, typeb, loca, locb):
diag = diagnostic.Diagnostic("error",
"type {type} does not have an attribute '{attr}'",
{"type": types.TypePrinter().name(object_type), "attr": attr_name},
node.attr_loc, highlights, notes)
attr_loc, highlights, notes)
self.engine.process(diag)

def _unify_iterable(self, element, collection):
@@ -970,23 +970,104 @@ def visit_Continue(self, node):
node.keyword_loc)
self.engine.process(diag)

def visit_withitem(self, node):
def visit_withitemT(self, node):
self.generic_visit(node)

typ = node.context_expr.type
if (types.is_builtin(typ, "parallel") or types.is_builtin(typ, "sequential") or
(isinstance(node.context_expr, asttyped.CallT) and
types.is_builtin(node.context_expr.func.type, "watchdog"))):
# builtin context managers
if node.optional_vars is not None:
self._unify(node.optional_vars.type, builtins.TNone(),
node.optional_vars.loc, None)
elif types.is_instance(typ) or types.is_constructor(typ):
# user-defined context managers
self._unify_attribute(result_type=node.enter_type, value_node=node.context_expr,
attr_name='__enter__', attr_loc=None, loc=node.loc)
self._unify_attribute(result_type=node.exit_type, value_node=node.context_expr,
attr_name='__exit__', attr_loc=None, loc=node.loc)

printer = types.TypePrinter()

def check_callback(attr_name, typ, arity):
if types.is_var(typ):
return

if not (types.is_method(typ) or types.is_function(typ)):
diag = diagnostic.Diagnostic("error",
"attribute '{attr}' of type {manager_type} must be a function",
{"attr": attr_name,
"manager_type": printer.name(node.context_expr.type)},
node.context_expr.loc)
self.engine.process(diag)
return

if types.is_method(typ):
typ = types.get_method_function(typ).find()
else:
typ = typ.find()

if not (len(typ.args) == arity and len(typ.optargs) == 0):
diag = diagnostic.Diagnostic("error",
"function '{attr}{attr_type}' must accept "
"{arity} positional argument{s} and no optional arguments",
{"attr": attr_name,
"attr_type": printer.name(typ),
"arity": arity, "s": "s" if arity > 1 else ""},
node.context_expr.loc)
self.engine.process(diag)

for formal_arg_name in list(typ.args)[1:]:
formal_arg_type = typ.args[formal_arg_name]
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"exception handling via context managers is not supported; "
"the argument '{arg}' of function '{attr}{attr_type}' "
"will always be None",
{"arg": formal_arg_name,
"attr": attr_name,
"attr_type": printer.name(typ)},
loca),
]

self._unify(formal_arg_type, builtins.TNone(),
node.context_expr.loc, None,
makenotes=makenotes)

check_callback('__enter__', node.enter_type, 1)
check_callback('__exit__', node.exit_type, 4)

if node.optional_vars is not None:
if types.is_method(node.exit_type):
var_type = types.get_method_function(node.exit_type).find().ret
else:
var_type = node.exit_type.find().ret

def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"expression of type {typea}",
{"typea": printer.name(typea)},
loca),
diagnostic.Diagnostic("note",
"context manager with an '__enter__' method returning {typeb}",
{"typeb": printer.name(typeb)},
locb)
]

self._unify(node.optional_vars.type, var_type,
node.optional_vars.loc, node.context_expr.loc,
makenotes=makenotes)

else:
diag = diagnostic.Diagnostic("error",
"value of type {type} cannot act as a context manager",
{"type": types.TypePrinter().name(typ)},
node.context_expr.loc)
self.engine.process(diag)

if node.optional_vars is not None:
self._unify(node.optional_vars.type, node.context_expr.type,
node.optional_vars.loc, node.context_expr.loc)

def visit_With(self, node):
self.generic_visit(node)

15 changes: 15 additions & 0 deletions lit-test/test/inferencer/error_with_arity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

class contextmgr:
def __enter__(self, n1):
pass

def __exit__(self, n1, n2):
pass

def foo():
# CHECK-L: ${LINE:+2}: error: function '__enter__(self:<instance contextmgr {}>, n1:'a)->NoneType delay('b)' must accept 1 positional argument and no optional arguments
# CHECK-L: ${LINE:+1}: error: function '__exit__(self:<instance contextmgr>, n1:'c, n2:'d)->NoneType delay('e)' must accept 4 positional arguments and no optional arguments
with contextmgr():
pass
16 changes: 16 additions & 0 deletions lit-test/test/inferencer/error_with_exn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

class contextmgr:
def __enter__(self):
pass

def __exit__(self, n1, n2, n3):
n3 = 1
pass

def foo():
# CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with NoneType
# CHECK-L: ${LINE:+1}: note: exception handling via context managers is not supported; the argument 'n3' of function '__exit__(self:<instance contextmgr {}>, n1:NoneType, n2:NoneType, n3:int(width='a))->NoneType delay('b)' will always be None
with contextmgr():
pass
17 changes: 17 additions & 0 deletions lit-test/test/inferencer/error_with_self.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

class contextmgr:
def __enter__(self):
pass

def __exit__(self, n1, n2, n3):
pass

def foo():
contextmgr.__enter__(1)
# CHECK-L: ${LINE:+3}: error: cannot unify <instance contextmgr> with int(width='a) while inferring the type for self argument
# CHECK-L: ${LINE:+2}: note: expression of type <instance contextmgr {}>
# CHECK-L: ${LINE:+1}: note: reference to an instance with a method '__enter__(self:int(width='a))->NoneType delay('b)'
with contextmgr():
pass
17 changes: 17 additions & 0 deletions lit-test/test/inferencer/error_with_var.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

class contextmgr:
def __enter__(self):
return 1

def __exit__(self, n1, n2, n3):
pass

def foo():
x = "x"
# CHECK-L: ${LINE:+3}: error: cannot unify str with NoneType
# CHECK-L: ${LINE:+2}: note: expression of type str
# CHECK-L: ${LINE:+1}: note: context manager with an '__enter__' method returning NoneType
with contextmgr() as x:
pass
2 changes: 1 addition & 1 deletion lit-test/test/inferencer/with.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
# RUN: OutputCheck %s --file-to-check=%t

# CHECK-L: as x:<builtin parallel>
# CHECK-L: as x:NoneType
with parallel as x: pass