diff --git a/artiq/py2llvm/typing.py b/artiq/py2llvm/typing.py index 20ee758ad..7624a562b 100644 --- a/artiq/py2llvm/typing.py +++ b/artiq/py2llvm/typing.py @@ -84,21 +84,25 @@ class LocalExtractor(algorithm.Visitor): 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): @@ -106,11 +110,12 @@ class LocalExtractor(algorithm.Visitor): 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) @@ -355,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 + 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) - 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))) if __name__ == "__main__": main() diff --git a/lit-test/py2llvm/typing/error_local_unbound.py b/lit-test/py2llvm/typing/error_local_unbound.py new file mode 100644 index 000000000..ceb61da83 --- /dev/null +++ b/lit-test/py2llvm/typing/error_local_unbound.py @@ -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 diff --git a/lit-test/py2llvm/typing/error_nonlocal_global.py b/lit-test/py2llvm/typing/error_nonlocal_global.py new file mode 100644 index 000000000..8586d418d --- /dev/null +++ b/lit-test/py2llvm/typing/error_nonlocal_global.py @@ -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 diff --git a/lit-test/py2llvm/typing/unify.py b/lit-test/py2llvm/typing/unify.py index 879c55d0f..60f9dba11 100644 --- a/lit-test/py2llvm/typing/unify.py +++ b/lit-test/py2llvm/typing/unify.py @@ -24,3 +24,11 @@ g = [] 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)