forked from M-Labs/artiq
compiler.embedding: test all diagnostics.
Also, unify and improve diagnostic messages.
This commit is contained in:
parent
d80be482fc
commit
7c1abb25ec
@ -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()
|
||||||
|
|
||||||
|
25
artiq/compiler/testbench/embedding.py
Normal file
25
artiq/compiler/testbench/embedding.py
Normal 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()
|
@ -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))
|
|
||||||
|
@ -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):
|
||||||
|
8
lit-test/test/embedding/ddb.pyon
Normal file
8
lit-test/test/embedding/ddb.pyon
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"comm": {
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.comm_dummy",
|
||||||
|
"class": "Comm",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
}
|
16
lit-test/test/embedding/error_attr_absent.py
Normal file
16
lit-test/test/embedding/error_attr_absent.py
Normal 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
|
21
lit-test/test/embedding/error_attr_conflict.py
Normal file
21
lit-test/test/embedding/error_attr_conflict.py
Normal 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
|
17
lit-test/test/embedding/error_attr_unify.py
Normal file
17
lit-test/test/embedding/error_attr_unify.py
Normal 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
|
14
lit-test/test/embedding/error_rpc_annot_return.py
Normal file
14
lit-test/test/embedding/error_rpc_annot_return.py
Normal 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()
|
15
lit-test/test/embedding/error_rpc_default_unify.py
Normal file
15
lit-test/test/embedding/error_rpc_default_unify.py
Normal 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()
|
14
lit-test/test/embedding/error_rpc_return.py
Normal file
14
lit-test/test/embedding/error_rpc_return.py
Normal 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()
|
15
lit-test/test/embedding/error_syscall_annot.py
Normal file
15
lit-test/test/embedding/error_syscall_annot.py
Normal 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()
|
15
lit-test/test/embedding/error_syscall_annot_return.py
Normal file
15
lit-test/test/embedding/error_syscall_annot_return.py
Normal 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()
|
15
lit-test/test/embedding/error_syscall_arg.py
Normal file
15
lit-test/test/embedding/error_syscall_arg.py
Normal 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()
|
14
lit-test/test/embedding/error_syscall_default_arg.py
Normal file
14
lit-test/test/embedding/error_syscall_default_arg.py
Normal 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()
|
14
lit-test/test/embedding/error_syscall_return.py
Normal file
14
lit-test/test/embedding/error_syscall_return.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user