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: 98fe15215982
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: 159692339d5f
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Jun 6, 2015

  1. AugAssign does not create a binding.

    whitequark committed Jun 6, 2015
    Copy the full SHA
    a8ff9d0 View commit details
  2. Add tests for all diagnostics and unifications.

    whitequark committed Jun 6, 2015
    Copy the full SHA
    1596923 View commit details
Showing with 71 additions and 31 deletions.
  1. +33 −31 artiq/py2llvm/typing.py
  2. +5 −0 lit-test/py2llvm/typing/error_local_unbound.py
  3. +25 −0 lit-test/py2llvm/typing/error_nonlocal_global.py
  4. +8 −0 lit-test/py2llvm/typing/unify.py
64 changes: 33 additions & 31 deletions artiq/py2llvm/typing.py
Original file line number Diff line number Diff line change
@@ -35,11 +35,6 @@ def visit_Assign(self, node):
self.visit_in_assign(target)
self.visit(node.value)

def visit_AugAssign(self, node):
self.visit_in_assign(node.target)
self.visit(node.op)
self.visit(node.value)

def visit_For(self, node):
self.visit_in_assign(node.target)
self.visit(node.iter)
@@ -89,33 +84,38 @@ def visit_Name(self, node):

def _check_not_in(self, name, names, curkind, newkind, loc):
if name in names:
diag = diagnostic.Diagnostic("fatal",
diag = diagnostic.Diagnostic("error",
"name '{name}' cannot be {curkind} and {newkind} simultaneously",
{"name": name, "curkind": curkind, "newkind": newkind}, loc)
self.engine.process(diag)
return True
return False

def visit_Global(self, node):
for name, loc in zip(node.names, node.name_locs):
self._check_not_in(name, self.nonlocal_, "nonlocal", "global", loc)
self._check_not_in(name, self.params, "a parameter", "global", loc)
if self._check_not_in(name, self.nonlocal_, "nonlocal", "global", loc) or \
self._check_not_in(name, self.params, "a parameter", "global", loc):
continue
self.global_.add(name)

def visit_Nonlocal(self, node):
for name, loc in zip(node.names, node.name_locs):
self._check_not_in(name, self.global_, "global", "nonlocal", loc)
self._check_not_in(name, self.params, "a parameter", "nonlocal", loc)
if self._check_not_in(name, self.global_, "global", "nonlocal", loc) or \
self._check_not_in(name, self.params, "a parameter", "nonlocal", loc):
continue

found = False
for outer_env in reversed(self.env_stack):
if name in outer_env:
found = True
break
if not found:
diag = diagnostic.Diagnostic("fatal",
diag = diagnostic.Diagnostic("error",
"can't declare name '{name}' as nonlocal: it is not bound in any outer scope",
{"name": name},
loc, [node.keyword_loc])
self.engine.process(diag)
continue

self.nonlocal_.add(name)

@@ -360,27 +360,29 @@ def main():

if sys.argv[1] == '+diag':
del sys.argv[1]
inference_mode = False
def process_diagnostic(diag):
print("\n".join(diag.render(only_line=True)))
if diag.level == 'fatal':
exit()
else:
inference_mode = True

engine = diagnostic.Engine(all_errors_are_fatal=True)
try:
buf = source.Buffer("".join(fileinput.input()), os.path.basename(fileinput.filename()))
parsed, comments = parse_buffer(buf, engine=engine)
typed = Inferencer(engine=engine).visit_root(parsed)
printer = Printer(buf)
printer.visit(typed)
for comment in comments:
if comment.text.find("CHECK") >= 0:
printer.rewriter.remove(comment.loc)
print(printer.rewrite().source)
except diagnostic.Error as e:
if inference_mode:
print("\n".join(e.diagnostic.render()), file=sys.stderr)
exit(1)
else:
print("\n".join(e.diagnostic.render(only_line=True)))
def process_diagnostic(diag):
print("\n".join(diag.render()))
if diag.level == 'fatal':
exit(1)

engine = diagnostic.Engine()
engine.process = process_diagnostic

buf = source.Buffer("".join(fileinput.input()), os.path.basename(fileinput.filename()))
parsed, comments = parse_buffer(buf, engine=engine)
typed = Inferencer(engine=engine).visit_root(parsed)
printer = Printer(buf)
printer.visit(typed)
for comment in comments:
if comment.text.find("CHECK") >= 0:
printer.rewriter.remove(comment.loc)
print(printer.rewrite().source)


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions lit-test/py2llvm/typing/error_local_unbound.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}: fatal: name 'x' is not bound to anything
x
25 changes: 25 additions & 0 deletions lit-test/py2llvm/typing/error_nonlocal_global.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# RUN: %python -m artiq.py2llvm.typing +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t

def a():
# CHECK-L: ${LINE:+1}: error: can't declare name 'x' as nonlocal: it is not bound in any outer scope
nonlocal x

x = 1
def b():
nonlocal x
# CHECK-L: ${LINE:+1}: error: name 'x' cannot be nonlocal and global simultaneously
global x

def c():
global x
# CHECK-L: ${LINE:+1}: error: name 'x' cannot be global and nonlocal simultaneously
nonlocal x

def d(x):
# CHECK-L: ${LINE:+1}: error: name 'x' cannot be a parameter and global simultaneously
global x

def d(x):
# CHECK-L: ${LINE:+1}: error: name 'x' cannot be a parameter and nonlocal simultaneously
nonlocal x
8 changes: 8 additions & 0 deletions lit-test/py2llvm/typing/unify.py
Original file line number Diff line number Diff line change
@@ -24,3 +24,11 @@

h = [1]
# CHECK-L: h:list(elt=int(width='c))

i = []
i[0] = 1
# CHECK-L: i:list(elt=int(width='d))

j = []
j += [1.0]
# CHECK-L: j:list(elt=float)