forked from M-Labs/artiq
compiler: fix quoting of methods (fixes #423).
This commit is contained in:
parent
4c78bb4950
commit
4e5d752951
@ -105,13 +105,24 @@ class ASTSynthesizer:
|
||||
loc=begin_loc.join(end_loc))
|
||||
elif inspect.isfunction(value) or inspect.ismethod(value) or \
|
||||
isinstance(value, pytypes.BuiltinFunctionType):
|
||||
quote_loc = self._add('`')
|
||||
repr_loc = self._add(repr(value))
|
||||
unquote_loc = self._add('`')
|
||||
loc = quote_loc.join(unquote_loc)
|
||||
if inspect.ismethod(value):
|
||||
quoted_self = self.quote(value.__self__)
|
||||
function_type = self.quote_function(value.__func__, self.expanded_from)
|
||||
method_type = types.TMethod(quoted_self.type, function_type)
|
||||
|
||||
function_type = self.quote_function(value, self.expanded_from)
|
||||
return asttyped.QuoteT(value=value, type=function_type, loc=loc)
|
||||
dot_loc = self._add('.')
|
||||
name_loc = self._add(value.__func__.__name__)
|
||||
loc = quoted_self.loc.join(name_loc)
|
||||
return asttyped.QuoteT(value=value, type=method_type,
|
||||
self_loc=quoted_self.loc, loc=loc)
|
||||
else:
|
||||
function_type = self.quote_function(value, self.expanded_from)
|
||||
|
||||
quote_loc = self._add('`')
|
||||
repr_loc = self._add(repr(value))
|
||||
unquote_loc = self._add('`')
|
||||
loc = quote_loc.join(unquote_loc)
|
||||
return asttyped.QuoteT(value=value, type=function_type, loc=loc)
|
||||
else:
|
||||
quote_loc = self._add('`')
|
||||
repr_loc = self._add(repr(value))
|
||||
@ -476,6 +487,16 @@ class StitchingInferencer(Inferencer):
|
||||
|
||||
super()._unify_attribute(result_type, value_node, attr_name, attr_loc, loc)
|
||||
|
||||
def visit_QuoteT(self, node):
|
||||
if inspect.ismethod(node.value):
|
||||
if types.is_rpc(types.get_method_function(node.type)):
|
||||
return
|
||||
self._unify_method_self(method_type=node.type,
|
||||
attr_name=node.value.__func__.__name__,
|
||||
attr_loc=None,
|
||||
loc=node.loc,
|
||||
self_loc=node.self_loc)
|
||||
|
||||
class TypedtreeHasher(algorithm.Visitor):
|
||||
def generic_visit(self, node):
|
||||
def freeze(obj):
|
||||
@ -770,8 +791,12 @@ class Stitcher:
|
||||
|
||||
if isinstance(callee, pytypes.BuiltinFunctionType):
|
||||
pass
|
||||
elif isinstance(callee, pytypes.FunctionType):
|
||||
signature = inspect.signature(callee)
|
||||
elif isinstance(callee, pytypes.FunctionType) or isinstance(callee, pytypes.MethodType):
|
||||
if isinstance(callee, pytypes.FunctionType):
|
||||
signature = inspect.signature(callee)
|
||||
else:
|
||||
# inspect bug?
|
||||
signature = inspect.signature(callee.__func__)
|
||||
if signature.return_annotation is not inspect.Signature.empty:
|
||||
ret_type = self._extract_annot(callee, signature.return_annotation,
|
||||
"return type", loc, is_syscall=False)
|
||||
|
@ -92,6 +92,41 @@ class Inferencer(algorithm.Visitor):
|
||||
attr_name=node.attr, attr_loc=node.attr_loc,
|
||||
loc=node.loc)
|
||||
|
||||
def _unify_method_self(self, method_type, attr_name, attr_loc, loc, self_loc):
|
||||
self_type = types.get_method_self(method_type)
|
||||
function_type = types.get_method_function(method_type)
|
||||
|
||||
if len(function_type.args) < 1:
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"function '{attr}{type}' of class '{class}' cannot accept a self argument",
|
||||
{"attr": attr_name, "type": types.TypePrinter().name(function_type),
|
||||
"class": self_type.name},
|
||||
loc)
|
||||
self.engine.process(diag)
|
||||
else:
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
if attr_loc is None:
|
||||
msgb = "reference to an instance with a method '{attr}{typeb}'"
|
||||
else:
|
||||
msgb = "reference to a method '{attr}{typeb}'"
|
||||
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
msgb,
|
||||
{"attr": attr_name,
|
||||
"typeb": printer.name(function_type)},
|
||||
locb)
|
||||
]
|
||||
|
||||
self._unify(self_type, list(function_type.args.values())[0],
|
||||
self_loc, loc,
|
||||
makenotes=makenotes,
|
||||
when=" while inferring the type for self argument")
|
||||
|
||||
def _unify_attribute(self, result_type, value_node, attr_name, attr_loc, loc):
|
||||
object_type = value_node.type.find()
|
||||
if not types.is_var(object_type):
|
||||
@ -116,39 +151,8 @@ class Inferencer(algorithm.Visitor):
|
||||
attr_type = object_type.constructor.attributes[attr_name].find()
|
||||
if types.is_function(attr_type):
|
||||
# Convert to a method.
|
||||
if len(attr_type.args) < 1:
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"function '{attr}{type}' of class '{class}' cannot accept a self argument",
|
||||
{"attr": attr_name, "type": types.TypePrinter().name(attr_type),
|
||||
"class": object_type.name},
|
||||
loc)
|
||||
self.engine.process(diag)
|
||||
return
|
||||
else:
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
if attr_loc is None:
|
||||
msgb = "reference to an instance with a method '{attr}{typeb}'"
|
||||
else:
|
||||
msgb = "reference to a method '{attr}{typeb}'"
|
||||
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
msgb,
|
||||
{"attr": attr_name,
|
||||
"typeb": printer.name(attr_type)},
|
||||
locb)
|
||||
]
|
||||
|
||||
self._unify(object_type, list(attr_type.args.values())[0],
|
||||
value_node.loc, loc,
|
||||
makenotes=makenotes,
|
||||
when=" while inferring the type for self argument")
|
||||
|
||||
attr_type = types.TMethod(object_type, attr_type)
|
||||
self._unify_method_self(attr_type, attr_name, attr_loc, loc, value_node.loc)
|
||||
elif types.is_rpc(attr_type):
|
||||
# Convert to a method. We don't have to bother typechecking
|
||||
# the self argument, since for RPCs anything goes.
|
||||
@ -894,6 +898,8 @@ class Inferencer(algorithm.Visitor):
|
||||
self._unify(node.type, typ.ret,
|
||||
node.loc, None)
|
||||
return
|
||||
elif typ.arity() == 0:
|
||||
return # error elsewhere
|
||||
|
||||
typ_arity = typ.arity() - 1
|
||||
typ_args = OrderedDict(list(typ.args.items())[1:])
|
||||
|
@ -1395,18 +1395,28 @@ class LLVMIRGenerator:
|
||||
lleltsptr = llglobal.bitcast(lleltsary.type.element.as_pointer())
|
||||
llconst = ll.Constant(llty, [ll.Constant(lli32, len(llelts)), lleltsptr])
|
||||
return llconst
|
||||
elif types.is_rpc(typ) or types.is_function(typ):
|
||||
elif types.is_rpc(typ) or types.is_c_function(typ):
|
||||
# RPC and C functions have no runtime representation.
|
||||
# We only get down this codepath for ARTIQ Python functions when they're
|
||||
# referenced from a constructor, and the value inside the constructor
|
||||
# is never used.
|
||||
return ll.Constant(llty, ll.Undefined)
|
||||
elif types.is_function(typ):
|
||||
llfun = self.get_function(typ, self.function_map[value])
|
||||
llclosure = ll.Constant(self.llty_of_type(typ), [
|
||||
ll.Constant(llptr, ll.Undefined),
|
||||
llfun
|
||||
])
|
||||
return llclosure
|
||||
elif types.is_method(typ):
|
||||
llclosure = self._quote(value.__func__, types.get_method_function(typ),
|
||||
lambda: path() + ['__func__'])
|
||||
llself = self._quote(value.__self__, types.get_method_self(typ),
|
||||
lambda: path() + ['__self__'])
|
||||
return ll.Constant(llty, [llclosure, llself])
|
||||
else:
|
||||
print(typ)
|
||||
assert False
|
||||
|
||||
def process_Quote(self, insn):
|
||||
if insn.value in self.function_map:
|
||||
if insn.value in self.function_map and types.is_function(insn.type):
|
||||
llfun = self.get_function(insn.type.find(), self.function_map[insn.value])
|
||||
llclosure = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
|
||||
llclosure = self.llbuilder.insert_value(llclosure, llfun, 1, name=insn.name)
|
||||
|
19
artiq/test/lit/embedding/error_kernel_method_no_self.py
Normal file
19
artiq/test/lit/embedding/error_kernel_method_no_self.py
Normal file
@ -0,0 +1,19 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
class c:
|
||||
pass
|
||||
@kernel
|
||||
def f():
|
||||
pass
|
||||
c.f = f
|
||||
x = c().f
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
# CHECK-L: <synthesized>:1: error: function 'f()->NoneType delay('a)' of class 'testbench.c' cannot accept a self argument
|
||||
# CHECK-L: ${LINE:+1}: note: expanded from here
|
||||
x
|
24
artiq/test/lit/embedding/error_kernel_method_self_unify.py
Normal file
24
artiq/test/lit/embedding/error_kernel_method_self_unify.py
Normal file
@ -0,0 +1,24 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
@kernel
|
||||
def f(self):
|
||||
core_log(self.x)
|
||||
class c:
|
||||
a = f
|
||||
x = 1
|
||||
class d:
|
||||
b = f
|
||||
x = 2
|
||||
xa = c().a
|
||||
xb = d().b
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
xa()
|
||||
# CHECK-L: <synthesized>:1: error: cannot unify <instance testbench.d> with <instance testbench.c
|
||||
# CHECK-L: ${LINE:+1}: note: expanded from here
|
||||
xb()
|
Loading…
Reference in New Issue
Block a user