forked from M-Labs/artiq
1
0
Fork 0

compiler.embedding: test all diagnostics.

Also, unify and improve diagnostic messages.
This commit is contained in:
whitequark 2015-08-28 00:47:28 -05:00
parent d80be482fc
commit 7c1abb25ec
16 changed files with 236 additions and 46 deletions

View File

@ -269,12 +269,11 @@ class StitchingInferencer(Inferencer):
else: else:
attributes = object_type.attributes attributes = object_type.attributes
ast = self.quote(attr_value, None) ast = self.quote(attr_value, object_loc.expanded_from)
def proxy_diagnostic(diag): def proxy_diagnostic(diag):
note = diagnostic.Diagnostic("note", note = diagnostic.Diagnostic("note",
"expanded from here while trying to infer a type for an" "while inferring a type for an attribute '{attr}' of a host object",
" attribute '{attr}' of a host object",
{"attr": node.attr}, {"attr": node.attr},
node.loc) node.loc)
diag.notes.append(note) diag.notes.append(note)
@ -293,10 +292,11 @@ class StitchingInferencer(Inferencer):
# Does this conflict with an earlier guess? # Does this conflict with an earlier guess?
printer = types.TypePrinter() printer = types.TypePrinter()
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"host object has an attribute of type {typea}, which is" "host object has an attribute '{attr}' of type {typea}, which is"
" different from previously inferred type {typeb}", " different from previously inferred type {typeb} for the same attribute",
{"typea": printer.name(ast.type), {"typea": printer.name(ast.type),
"typeb": printer.name(attributes[node.attr])}, "typeb": printer.name(attributes[node.attr]),
"attr": node.attr},
object_loc) object_loc)
self.engine.process(diag) self.engine.process(diag)
@ -465,27 +465,23 @@ class Stitcher:
source_buffer = source.Buffer(source_line, filename, line) source_buffer = source.Buffer(source_line, filename, line)
return source.Range(source_buffer, column, column) return source.Range(source_buffer, column, column)
def _function_def_note(self, function): def _call_site_note(self, call_loc, is_syscall):
return diagnostic.Diagnostic("note",
"definition of function '{function}'",
{"function": function.__name__},
self._function_loc(function))
def _extract_annot(self, function, annot, kind, call_loc, is_syscall):
if not isinstance(annot, types.Type):
if is_syscall: if is_syscall:
note = diagnostic.Diagnostic("note", return diagnostic.Diagnostic("note",
"in system call here", {}, "in system call here", {},
call_loc) call_loc)
else: else:
note = diagnostic.Diagnostic("note", return diagnostic.Diagnostic("note",
"in function called remotely here", {}, "in function called remotely here", {},
call_loc) call_loc)
def _extract_annot(self, function, annot, kind, call_loc, is_syscall):
if not isinstance(annot, types.Type):
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"type annotation for {kind}, '{annot}', is not an ARTIQ type", "type annotation for {kind}, '{annot}', is not an ARTIQ type",
{"kind": kind, "annot": repr(annot)}, {"kind": kind, "annot": repr(annot)},
self._function_loc(function), self._function_loc(function),
notes=[note]) notes=[self._call_site_note(call_loc, is_syscall)])
self.engine.process(diag) self.engine.process(diag)
return types.TVar() return types.TVar()
@ -503,7 +499,8 @@ class Stitcher:
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"system call argument '{argument}' must have a type annotation", "system call argument '{argument}' must have a type annotation",
{"argument": param.name}, {"argument": param.name},
self._function_loc(function)) self._function_loc(function),
notes=[self._call_site_note(loc, is_syscall)])
self.engine.process(diag) self.engine.process(diag)
elif param.default is not inspect.Parameter.empty: elif param.default is not inspect.Parameter.empty:
# Try and infer the type from the default value. # Try and infer the type from the default value.
@ -517,10 +514,10 @@ class Stitcher:
"expanded from here while trying to infer a type for an" "expanded from here while trying to infer a type for an"
" unannotated optional argument '{argument}' from its default value", " unannotated optional argument '{argument}' from its default value",
{"argument": param.name}, {"argument": param.name},
loc) self._function_loc(function))
diag.notes.append(note) diag.notes.append(note)
diag.notes.append(self._function_def_note(function)) diag.notes.append(self._call_site_note(loc, is_syscall))
self.engine.process(diag) self.engine.process(diag)
@ -556,12 +553,13 @@ class Stitcher:
is_syscall=syscall is not None) is_syscall=syscall is not None)
elif syscall is None: elif syscall is None:
optarg_types[param.name] = self._type_of_param(function, loc, param, optarg_types[param.name] = self._type_of_param(function, loc, param,
is_syscall=syscall is not None) is_syscall=False)
else: else:
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"system call argument '{argument}' must not have a default value", "system call argument '{argument}' must not have a default value",
{"argument": param.name}, {"argument": param.name},
self._function_loc(function)) self._function_loc(function),
notes=[self._call_site_note(loc, is_syscall=True)])
self.engine.process(diag) self.engine.process(diag)
if signature.return_annotation is not inspect.Signature.empty: if signature.return_annotation is not inspect.Signature.empty:
@ -570,13 +568,15 @@ class Stitcher:
elif syscall is None: elif syscall is None:
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"function must have a return type annotation to be called remotely", {}, "function must have a return type annotation to be called remotely", {},
self._function_loc(function)) self._function_loc(function),
notes=[self._call_site_note(loc, is_syscall=False)])
self.engine.process(diag) self.engine.process(diag)
ret_type = types.TVar() ret_type = types.TVar()
else: # syscall is not None else: # syscall is not None
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"system call must have a return type annotation", {}, "system call must have a return type annotation", {},
self._function_loc(function)) self._function_loc(function),
notes=[self._call_site_note(loc, is_syscall=True)])
self.engine.process(diag) self.engine.process(diag)
ret_type = types.TVar() ret_type = types.TVar()

View File

@ -0,0 +1,25 @@
import sys, os
from artiq.protocols.file_db import FlatFileDB
from artiq.master.worker_db import DeviceManager
from artiq.coredevice.core import Core, CompileError
def main():
with open(sys.argv[1]) as f:
testcase_code = compile(f.read(), f.name, "exec")
testcase_vars = {}
exec(testcase_code, testcase_vars)
ddb_path = os.path.join(os.path.dirname(sys.argv[1]), "ddb.pyon")
try:
core = Core(dmgr=DeviceManager(FlatFileDB(ddb_path)))
core.run(testcase_vars["entrypoint"], (), {})
print(core.comm.get_log())
core.comm.clear_log()
except CompileError as error:
print("\n".join(error.__cause__.diagnostic.render(only_line=True)))
if __name__ == "__main__":
main()

View File

@ -8,23 +8,11 @@ class Comm:
def switch_clock(self, external): def switch_clock(self, external):
pass pass
def load(self, kcode): def load(self, kernel_library):
print("================") pass
print(" LLVM IR")
print("================")
print(kcode)
def run(self, kname): def run(self):
print("RUN: "+kname) pass
def serve(self, rpc_map, exception_map): def serve(self, object_map, symbolizer):
print("================") pass
print(" RPC map")
print("================")
for k, v in sorted(rpc_map.items(), key=itemgetter(0)):
print(str(k)+" -> "+str(v))
print("================")
print(" Exception map")
print("================")
for k, v in sorted(exception_map.items(), key=itemgetter(0)):
print(str(k)+" -> "+str(v))

View File

@ -48,7 +48,6 @@ class Core:
return stitcher.object_map, stripped_library, \ return stitcher.object_map, stripped_library, \
lambda addresses: target.symbolize(library, addresses) lambda addresses: target.symbolize(library, addresses)
except diagnostic.Error as error: except diagnostic.Error as error:
print("\n".join(error.diagnostic.render(colored=True)), file=sys.stderr)
raise CompileError() from error raise CompileError() from error
def run(self, function, args, kwargs): def run(self, function, args, kwargs):

View File

@ -0,0 +1,8 @@
{
"comm": {
"type": "local",
"module": "artiq.coredevice.comm_dummy",
"class": "Comm",
"arguments": {}
}
}

View File

@ -0,0 +1,16 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
class c:
pass
@kernel
def entrypoint():
# CHECK-L: <synthesized>:1: error: host object does not have an attribute 'x'
# CHECK-L: ${LINE:+1}: note: expanded from here
a = c
# CHECK-L: ${LINE:+1}: note: attribute accessed here
a.x

View File

@ -0,0 +1,21 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
class c:
pass
i1 = c()
i1.x = 1
i2 = c()
i2.x = 1.0
@kernel
def entrypoint():
# CHECK-L: <synthesized>:1: error: host object has an attribute 'x' of type float, which is different from previously inferred type int(width=32) for the same attribute
i1.x
# CHECK-L: ${LINE:+1}: note: expanded from here
i2.x

View File

@ -0,0 +1,17 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
class c:
x = [1, "x"]
@kernel
def entrypoint():
# CHECK-L: <synthesized>:1: error: cannot unify int(width='a) with str
# CHECK-NEXT-L: [1, 'x']
# CHECK-L: ${LINE:+1}: note: expanded from here
a = c
# CHECK-L: ${LINE:+1}: note: while inferring a type for an attribute 'x' of a host object
a.x

View File

@ -0,0 +1,14 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+1}: error: type annotation for return type, '1', is not an ARTIQ type
def foo() -> 1:
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in function called remotely here
foo()

View File

@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: <synthesized>:1: error: cannot unify int(width='a) with str
# CHECK-L: ${LINE:+1}: note: expanded from here while trying to infer a type for an unannotated optional argument 'x' from its default value
def foo(x=[1,"x"]):
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in function called remotely here
foo()

View File

@ -0,0 +1,14 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+1}: error: function must have a return type annotation to be called remotely
def foo():
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in function called remotely here
foo()

View File

@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+2}: error: type annotation for argument 'x', '1', is not an ARTIQ type
@syscall
def foo(x: 1) -> TNone:
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in system call here
foo()

View File

@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+2}: error: type annotation for return type, '1', is not an ARTIQ type
@syscall
def foo() -> 1:
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in system call here
foo()

View File

@ -0,0 +1,15 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+2}: error: system call argument 'x' must have a type annotation
@syscall
def foo(x) -> TNone:
pass
@kernel
def entrypoint():
# CHECK-L: ${LINE:+1}: note: in system call here
foo()

View File

@ -0,0 +1,14 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+2}: error: system call argument 'x' must not have a default value
@syscall
def foo(x=1) -> TNone:
pass
@kernel
def entrypoint():
foo()

View File

@ -0,0 +1,14 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s >%t
# RUN: OutputCheck %s --file-to-check=%t
from artiq.language.core import *
from artiq.language.types import *
# CHECK-L: ${LINE:+2}: error: system call must have a return type annotation
@syscall
def foo():
pass
@kernel
def entrypoint():
foo()