mirror of https://github.com/m-labs/artiq.git
Implement class definitions and class attribute access.
This commit is contained in:
parent
fd3c8a2830
commit
00efc8c636
|
@ -25,15 +25,15 @@ class scoped(object):
|
||||||
class argT(ast.arg, commontyped):
|
class argT(ast.arg, commontyped):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ClassDefT(ast.ClassDef, scoped):
|
class ClassDefT(ast.ClassDef):
|
||||||
pass
|
_types = ("constructor_type",)
|
||||||
class FunctionDefT(ast.FunctionDef, scoped):
|
class FunctionDefT(ast.FunctionDef, scoped):
|
||||||
_types = ("signature_type",)
|
_types = ("signature_type",)
|
||||||
class ModuleT(ast.Module, scoped):
|
class ModuleT(ast.Module, scoped):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ExceptHandlerT(ast.ExceptHandler):
|
class ExceptHandlerT(ast.ExceptHandler):
|
||||||
_fields = ("filter", "name", "body") # rename ast.ExceptHandler.type
|
_fields = ("filter", "name", "body") # rename ast.ExceptHandler.type to filter
|
||||||
_types = ("name_type",)
|
_types = ("name_type",)
|
||||||
|
|
||||||
class SliceT(ast.Slice, commontyped):
|
class SliceT(ast.Slice, commontyped):
|
||||||
|
|
|
@ -96,44 +96,32 @@ class TException(types.TMono):
|
||||||
def __init__(self, name="Exception"):
|
def __init__(self, name="Exception"):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
class TIndexError(TException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__("IndexError")
|
|
||||||
|
|
||||||
class TValueError(TException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__("ValueError")
|
|
||||||
|
|
||||||
class TZeroDivisionError(TException):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__("ZeroDivisionError")
|
|
||||||
|
|
||||||
def fn_bool():
|
def fn_bool():
|
||||||
return types.TConstructor("bool")
|
return types.TConstructor(TBool())
|
||||||
|
|
||||||
def fn_int():
|
def fn_int():
|
||||||
return types.TConstructor("int")
|
return types.TConstructor(TInt())
|
||||||
|
|
||||||
def fn_float():
|
def fn_float():
|
||||||
return types.TConstructor("float")
|
return types.TConstructor(TFloat())
|
||||||
|
|
||||||
def fn_str():
|
def fn_str():
|
||||||
return types.TConstructor("str")
|
return types.TConstructor(TStr())
|
||||||
|
|
||||||
def fn_list():
|
def fn_list():
|
||||||
return types.TConstructor("list")
|
return types.TConstructor(TList())
|
||||||
|
|
||||||
def fn_Exception():
|
def fn_Exception():
|
||||||
return types.TExceptionConstructor("Exception")
|
return types.TExceptionConstructor(TException("Exception"))
|
||||||
|
|
||||||
def fn_IndexError():
|
def fn_IndexError():
|
||||||
return types.TExceptionConstructor("IndexError")
|
return types.TExceptionConstructor(TException("IndexError"))
|
||||||
|
|
||||||
def fn_ValueError():
|
def fn_ValueError():
|
||||||
return types.TExceptionConstructor("ValueError")
|
return types.TExceptionConstructor(TException("ValueError"))
|
||||||
|
|
||||||
def fn_ZeroDivisionError():
|
def fn_ZeroDivisionError():
|
||||||
return types.TExceptionConstructor("ZeroDivisionError")
|
return types.TExceptionConstructor(TException("ZeroDivisionError"))
|
||||||
|
|
||||||
def fn_range():
|
def fn_range():
|
||||||
return types.TBuiltinFunction("range")
|
return types.TBuiltinFunction("range")
|
||||||
|
@ -215,4 +203,4 @@ def is_collection(typ):
|
||||||
def is_allocated(typ):
|
def is_allocated(typ):
|
||||||
return typ.fold(False, lambda accum, typ:
|
return typ.fold(False, lambda accum, typ:
|
||||||
is_list(typ) or is_str(typ) or types.is_function(typ) or
|
is_list(typ) or is_str(typ) or types.is_function(typ) or
|
||||||
is_exception(typ))
|
is_exception(typ) or types.is_constructor(typ))
|
||||||
|
|
|
@ -636,9 +636,9 @@ class SetAttr(Instruction):
|
||||||
assert isinstance(attr, (str, int))
|
assert isinstance(attr, (str, int))
|
||||||
assert isinstance(value, Value)
|
assert isinstance(value, Value)
|
||||||
if isinstance(attr, int):
|
if isinstance(attr, int):
|
||||||
assert value.type == obj.type.elts[attr]
|
assert value.type == obj.type.elts[attr].find()
|
||||||
else:
|
else:
|
||||||
assert value.type == obj.type.attributes[attr]
|
assert value.type == obj.type.attributes[attr].find()
|
||||||
super().__init__([obj, value], builtins.TNone(), name)
|
super().__init__([obj, value], builtins.TNone(), name)
|
||||||
self.attr = attr
|
self.attr = attr
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.name = [module_name] if module_name != "" else []
|
self.name = [module_name] if module_name != "" else []
|
||||||
self.current_loc = None
|
self.current_loc = None
|
||||||
self.current_function = None
|
self.current_function = None
|
||||||
|
self.current_class = None
|
||||||
self.current_globals = set()
|
self.current_globals = set()
|
||||||
self.current_block = None
|
self.current_block = None
|
||||||
self.current_env = None
|
self.current_env = None
|
||||||
|
@ -156,6 +157,17 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
# Statement visitors
|
# Statement visitors
|
||||||
|
|
||||||
|
def visit_ClassDefT(self, node):
|
||||||
|
klass = self.append(ir.Alloc([], node.constructor_type,
|
||||||
|
name="class.{}".format(node.name)))
|
||||||
|
self._set_local(node.name, klass)
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_class, self.current_class = self.current_class, klass
|
||||||
|
self.visit(node.body)
|
||||||
|
finally:
|
||||||
|
self.current_class = old_class
|
||||||
|
|
||||||
def visit_function(self, node, is_lambda, is_internal):
|
def visit_function(self, node, is_lambda, is_internal):
|
||||||
if is_lambda:
|
if is_lambda:
|
||||||
name = "lambda@{}:{}".format(node.loc.line(), node.loc.column())
|
name = "lambda@{}:{}".format(node.loc.line(), node.loc.column())
|
||||||
|
@ -239,9 +251,12 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
return self.append(ir.Closure(func, self.current_env))
|
return self.append(ir.Closure(func, self.current_env))
|
||||||
|
|
||||||
def visit_FunctionDefT(self, node):
|
def visit_FunctionDefT(self, node, in_class=None):
|
||||||
func = self.visit_function(node, is_lambda=False, is_internal=len(self.name) > 2)
|
func = self.visit_function(node, is_lambda=False, is_internal=len(self.name) > 2)
|
||||||
|
if in_class is None:
|
||||||
self._set_local(node.name, func)
|
self._set_local(node.name, func)
|
||||||
|
else:
|
||||||
|
self.append(ir.SetAttr(in_class, node.name, func))
|
||||||
|
|
||||||
def visit_Return(self, node):
|
def visit_Return(self, node):
|
||||||
if node.value is None:
|
if node.value is None:
|
||||||
|
@ -668,9 +683,19 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
return self.current_env
|
return self.current_env
|
||||||
|
|
||||||
def _get_local(self, name):
|
def _get_local(self, name):
|
||||||
return self.append(ir.GetLocal(self._env_for(name), name, name="local." + name))
|
if self.current_class is not None and \
|
||||||
|
name in self.current_class.type.attributes:
|
||||||
|
return self.append(ir.GetAttr(self.current_class, name,
|
||||||
|
name="local." + name))
|
||||||
|
|
||||||
|
return self.append(ir.GetLocal(self._env_for(name), name,
|
||||||
|
name="local." + name))
|
||||||
|
|
||||||
def _set_local(self, name, value):
|
def _set_local(self, name, value):
|
||||||
|
if self.current_class is not None and \
|
||||||
|
name in self.current_class.type.attributes:
|
||||||
|
return self.append(ir.SetAttr(self.current_class, name, value))
|
||||||
|
|
||||||
self.append(ir.SetLocal(self._env_for(name), name, value))
|
self.append(ir.SetLocal(self._env_for(name), name, value))
|
||||||
|
|
||||||
def visit_NameT(self, node):
|
def visit_NameT(self, node):
|
||||||
|
@ -706,7 +731,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
head = self.current_block
|
head = self.current_block
|
||||||
|
|
||||||
self.current_block = out_of_bounds_block = self.add_block()
|
self.current_block = out_of_bounds_block = self.add_block()
|
||||||
exn = self.alloc_exn(builtins.TIndexError(),
|
exn = self.alloc_exn(builtins.TException("IndexError"),
|
||||||
ir.Constant("index {0} out of bounds 0:{1}", builtins.TStr()),
|
ir.Constant("index {0} out of bounds 0:{1}", builtins.TStr()),
|
||||||
index, length)
|
index, length)
|
||||||
self.raise_exn(exn, loc=loc)
|
self.raise_exn(exn, loc=loc)
|
||||||
|
@ -790,7 +815,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
step = self.visit(node.slice.step)
|
step = self.visit(node.slice.step)
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
||||||
lambda: self.alloc_exn(builtins.TValueError(),
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("step cannot be zero", builtins.TStr())),
|
ir.Constant("step cannot be zero", builtins.TStr())),
|
||||||
loc=node.slice.step.loc)
|
loc=node.slice.step.loc)
|
||||||
else:
|
else:
|
||||||
|
@ -811,7 +836,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
name="slice.size"))
|
name="slice.size"))
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
||||||
lambda: self.alloc_exn(builtins.TValueError(),
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("slice size {0} is larger than iterable length {1}",
|
ir.Constant("slice size {0} is larger than iterable length {1}",
|
||||||
builtins.TStr()),
|
builtins.TStr()),
|
||||||
slice_size, length),
|
slice_size, length),
|
||||||
|
@ -894,7 +919,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.Eq(loc=None), length,
|
self.append(ir.Compare(ast.Eq(loc=None), length,
|
||||||
ir.Constant(len(node.elts), self._size_type))),
|
ir.Constant(len(node.elts), self._size_type))),
|
||||||
lambda: self.alloc_exn(builtins.TValueError(),
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("list must be {0} elements long to decompose", builtins.TStr()),
|
ir.Constant("list must be {0} elements long to decompose", builtins.TStr()),
|
||||||
length))
|
length))
|
||||||
|
|
||||||
|
@ -1002,13 +1027,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
# Check for negative shift amount.
|
# Check for negative shift amount.
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.GtE(loc=None), rhs, ir.Constant(0, rhs.type))),
|
self.append(ir.Compare(ast.GtE(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||||
lambda: self.alloc_exn(builtins.TValueError(),
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("shift amount must be nonnegative", builtins.TStr())),
|
ir.Constant("shift amount must be nonnegative", builtins.TStr())),
|
||||||
loc=node.right.loc)
|
loc=node.right.loc)
|
||||||
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||||
lambda: self.alloc_exn(builtins.TZeroDivisionError(),
|
lambda: self.alloc_exn(builtins.TException("ZeroDivisionError"),
|
||||||
ir.Constant("cannot divide by zero", builtins.TStr())),
|
ir.Constant("cannot divide by zero", builtins.TStr())),
|
||||||
loc=node.right.loc)
|
loc=node.right.loc)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
to a typedtree (:mod:`..asttyped`).
|
to a typedtree (:mod:`..asttyped`).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pythonparser import algorithm, diagnostic
|
from collections import OrderedDict
|
||||||
|
from pythonparser import ast, algorithm, diagnostic
|
||||||
from .. import asttyped, types, builtins
|
from .. import asttyped, types, builtins
|
||||||
|
|
||||||
# This visitor will be called for every node with a scope,
|
# This visitor will be called for every node with a scope,
|
||||||
|
@ -16,7 +17,7 @@ class LocalExtractor(algorithm.Visitor):
|
||||||
|
|
||||||
self.in_root = False
|
self.in_root = False
|
||||||
self.in_assign = False
|
self.in_assign = False
|
||||||
self.typing_env = {}
|
self.typing_env = OrderedDict()
|
||||||
|
|
||||||
# which names are global have to be recorded in the current scope
|
# which names are global have to be recorded in the current scope
|
||||||
self.global_ = set()
|
self.global_ = set()
|
||||||
|
@ -189,6 +190,7 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
self.globals = None
|
self.globals = None
|
||||||
self.env_stack = [prelude]
|
self.env_stack = [prelude]
|
||||||
|
self.in_class = None
|
||||||
|
|
||||||
def _try_find_name(self, name):
|
def _try_find_name(self, name):
|
||||||
for typing_env in reversed(self.env_stack):
|
for typing_env in reversed(self.env_stack):
|
||||||
|
@ -196,11 +198,17 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||||
return typing_env[name]
|
return typing_env[name]
|
||||||
|
|
||||||
def _find_name(self, name, loc):
|
def _find_name(self, name, loc):
|
||||||
|
if self.in_class is not None:
|
||||||
|
typ = self.in_class.constructor_type.attributes.get(name)
|
||||||
|
if typ is not None:
|
||||||
|
return typ
|
||||||
|
|
||||||
typ = self._try_find_name(name)
|
typ = self._try_find_name(name)
|
||||||
if typ is not None:
|
if typ is not None:
|
||||||
return typ
|
return typ
|
||||||
|
|
||||||
diag = diagnostic.Diagnostic("fatal",
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
"name '{name}' is not bound to anything", {"name":name}, loc)
|
"undefined variable '{name}'", {"name":name}, loc)
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
||||||
# Visitors that replace node with a typed node
|
# Visitors that replace node with a typed node
|
||||||
|
@ -239,6 +247,66 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||||
finally:
|
finally:
|
||||||
self.env_stack.pop()
|
self.env_stack.pop()
|
||||||
|
|
||||||
|
def visit_ClassDef(self, node):
|
||||||
|
if any(node.bases) or any(node.keywords) or \
|
||||||
|
node.starargs is not None or node.kwargs is not None:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"inheritance is not supported", {},
|
||||||
|
node.lparen_loc.join(node.rparen_loc))
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
for child in node.body:
|
||||||
|
if isinstance(child, (ast.Assign, ast.FunctionDef, ast.Pass)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
|
"class body must contain only assignments and function definitions", {},
|
||||||
|
child.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
if node.name in self.env_stack[-1]:
|
||||||
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
|
"variable '{name}' is already defined", {"name":name}, loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
||||||
|
extractor.visit(node)
|
||||||
|
|
||||||
|
# Now we create two types.
|
||||||
|
# The first type is the type of instances created by the constructor.
|
||||||
|
# Its attributes are those of the class environment, but wrapped
|
||||||
|
# appropriately so that they are linked to the class from which they
|
||||||
|
# originate.
|
||||||
|
instance_type = types.TMono(node.name)
|
||||||
|
instance_type.attributes = OrderedDict({}) # TODO
|
||||||
|
|
||||||
|
# The second type is the type of the constructor itself (in other words,
|
||||||
|
# the class object): it is simply a singleton type that has the class
|
||||||
|
# environment as attributes.
|
||||||
|
constructor_type = types.TConstructor(instance_type)
|
||||||
|
constructor_type.attributes = extractor.typing_env
|
||||||
|
|
||||||
|
self.env_stack[-1][node.name] = constructor_type
|
||||||
|
|
||||||
|
node = asttyped.ClassDefT(
|
||||||
|
constructor_type=constructor_type,
|
||||||
|
name=node.name,
|
||||||
|
bases=self.visit(node.bases), keywords=self.visit(node.keywords),
|
||||||
|
starargs=self.visit(node.starargs), kwargs=self.visit(node.kwargs),
|
||||||
|
body=node.body,
|
||||||
|
decorator_list=self.visit(node.decorator_list),
|
||||||
|
keyword_loc=node.keyword_loc, name_loc=node.name_loc,
|
||||||
|
lparen_loc=node.lparen_loc, star_loc=node.star_loc,
|
||||||
|
dstar_loc=node.dstar_loc, rparen_loc=node.rparen_loc,
|
||||||
|
colon_loc=node.colon_loc, at_locs=node.at_locs,
|
||||||
|
loc=node.loc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_in_class, self.in_class = self.in_class, node
|
||||||
|
return self.generic_visit(node)
|
||||||
|
finally:
|
||||||
|
self.in_class = old_in_class
|
||||||
|
|
||||||
def visit_arg(self, node):
|
def visit_arg(self, node):
|
||||||
return asttyped.argT(type=self._find_name(node.arg, node.loc),
|
return asttyped.argT(type=self._find_name(node.arg, node.loc),
|
||||||
arg=node.arg, annotation=self.visit(node.annotation),
|
arg=node.arg, annotation=self.visit(node.annotation),
|
||||||
|
@ -426,7 +494,6 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||||
visit_YieldFrom = visit_unsupported
|
visit_YieldFrom = visit_unsupported
|
||||||
|
|
||||||
# stmt
|
# stmt
|
||||||
visit_ClassDef = visit_unsupported
|
|
||||||
visit_Delete = visit_unsupported
|
visit_Delete = visit_unsupported
|
||||||
visit_Import = visit_unsupported
|
visit_Import = visit_unsupported
|
||||||
visit_ImportFrom = visit_unsupported
|
visit_ImportFrom = visit_unsupported
|
||||||
|
|
|
@ -467,7 +467,7 @@ class Inferencer(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
diagnose(valid_forms())
|
diagnose(valid_forms())
|
||||||
|
|
||||||
self._unify(node.type, getattr(builtins, "T" + typ.name)(),
|
self._unify(node.type, typ.instance,
|
||||||
node.loc, None)
|
node.loc, None)
|
||||||
elif types.is_builtin(typ, "bool"):
|
elif types.is_builtin(typ, "bool"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
|
@ -895,8 +895,8 @@ class Inferencer(algorithm.Visitor):
|
||||||
"decorators are not supported", {},
|
"decorators are not supported", {},
|
||||||
node.at_locs[index], [decorator.loc])
|
node.at_locs[index], [decorator.loc])
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
return
|
|
||||||
|
|
||||||
|
try:
|
||||||
old_function, self.function = self.function, node
|
old_function, self.function = self.function, node
|
||||||
old_in_loop, self.in_loop = self.in_loop, False
|
old_in_loop, self.in_loop = self.in_loop, False
|
||||||
old_has_return, self.has_return = self.has_return, False
|
old_has_return, self.has_return = self.has_return, False
|
||||||
|
@ -917,7 +917,7 @@ class Inferencer(algorithm.Visitor):
|
||||||
]
|
]
|
||||||
self._unify(node.return_type, builtins.TNone(),
|
self._unify(node.return_type, builtins.TNone(),
|
||||||
node.name_loc, None, makenotes)
|
node.name_loc, None, makenotes)
|
||||||
|
finally:
|
||||||
self.function = old_function
|
self.function = old_function
|
||||||
self.in_loop = old_in_loop
|
self.in_loop = old_in_loop
|
||||||
self.has_return = old_has_return
|
self.has_return = old_has_return
|
||||||
|
@ -927,6 +927,15 @@ class Inferencer(algorithm.Visitor):
|
||||||
self._unify(node.signature_type, signature_type,
|
self._unify(node.signature_type, signature_type,
|
||||||
node.name_loc, None)
|
node.name_loc, None)
|
||||||
|
|
||||||
|
def visit_ClassDefT(self, node):
|
||||||
|
if any(node.decorator_list):
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"decorators are not supported", {},
|
||||||
|
node.at_locs[0], [node.decorator_list[0].loc])
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
||||||
def visit_Return(self, node):
|
def visit_Return(self, node):
|
||||||
if not self.function:
|
if not self.function:
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
|
|
@ -437,7 +437,7 @@ class LLVMIRGenerator:
|
||||||
size=llsize)
|
size=llsize)
|
||||||
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name)
|
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name)
|
||||||
return llvalue
|
return llvalue
|
||||||
elif builtins.is_exception(insn.type):
|
elif builtins.is_exception(insn.type) or types.is_constructor(insn.type):
|
||||||
llalloc = self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True))
|
llalloc = self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True))
|
||||||
for index, operand in enumerate(insn.operands):
|
for index, operand in enumerate(insn.operands):
|
||||||
lloperand = self.map(operand)
|
lloperand = self.map(operand)
|
||||||
|
|
|
@ -319,9 +319,17 @@ class TConstructor(TBuiltin):
|
||||||
A type of a constructor of a builtin class, e.g. ``list``.
|
A type of a constructor of a builtin class, e.g. ``list``.
|
||||||
Note that this is not the same as the type of an instance of
|
Note that this is not the same as the type of an instance of
|
||||||
the class, which is ``TMono("list", ...)``.
|
the class, which is ``TMono("list", ...)``.
|
||||||
|
|
||||||
|
:ivar instance: (:class:`Type`)
|
||||||
|
the type of the instance created by this constructor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class TExceptionConstructor(TBuiltin):
|
def __init__(self, instance):
|
||||||
|
assert isinstance(instance, TMono)
|
||||||
|
super().__init__(instance.name)
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
class TExceptionConstructor(TConstructor):
|
||||||
"""
|
"""
|
||||||
A type of a constructor of a builtin exception, e.g. ``Exception``.
|
A type of a constructor of a builtin exception, e.g. ``Exception``.
|
||||||
Note that this is not the same as the type of an instance of
|
Note that this is not the same as the type of an instance of
|
||||||
|
@ -402,6 +410,14 @@ def is_builtin(typ, name=None):
|
||||||
return isinstance(typ, TBuiltin) and \
|
return isinstance(typ, TBuiltin) and \
|
||||||
typ.name == name
|
typ.name == name
|
||||||
|
|
||||||
|
def is_constructor(typ, name=None):
|
||||||
|
typ = typ.find()
|
||||||
|
if name is not None:
|
||||||
|
return isinstance(typ, TConstructor) and \
|
||||||
|
typ.name == name
|
||||||
|
else:
|
||||||
|
return isinstance(typ, TConstructor)
|
||||||
|
|
||||||
def is_exn_constructor(typ, name=None):
|
def is_exn_constructor(typ, name=None):
|
||||||
typ = typ.find()
|
typ = typ.find()
|
||||||
if name is not None:
|
if name is not None:
|
||||||
|
@ -459,9 +475,11 @@ class TypePrinter(object):
|
||||||
elif isinstance(typ, TFunction):
|
elif isinstance(typ, TFunction):
|
||||||
return signature
|
return signature
|
||||||
elif isinstance(typ, TBuiltinFunction):
|
elif isinstance(typ, TBuiltinFunction):
|
||||||
return "<function %s>" % typ.name
|
return "<function {}>".format(typ.name)
|
||||||
elif isinstance(typ, (TConstructor, TExceptionConstructor)):
|
elif isinstance(typ, (TConstructor, TExceptionConstructor)):
|
||||||
return "<constructor %s>" % typ.name
|
attrs = ", ".join(["{}: {}".format(attr, self.name(typ.attributes[attr]))
|
||||||
|
for attr in typ.attributes])
|
||||||
|
return "<constructor {} {{{}}}>".format(typ.name, attrs)
|
||||||
elif isinstance(typ, TValue):
|
elif isinstance(typ, TValue):
|
||||||
return repr(typ.value)
|
return repr(typ.value)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -208,7 +208,7 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
loc)
|
loc)
|
||||||
]
|
]
|
||||||
|
|
||||||
def visit_in_region(self, node, region):
|
def visit_in_region(self, node, region, typing_env):
|
||||||
try:
|
try:
|
||||||
old_youngest_region = self.youngest_region
|
old_youngest_region = self.youngest_region
|
||||||
self.youngest_region = region
|
self.youngest_region = region
|
||||||
|
@ -216,8 +216,8 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
old_youngest_env = self.youngest_env
|
old_youngest_env = self.youngest_env
|
||||||
self.youngest_env = {}
|
self.youngest_env = {}
|
||||||
|
|
||||||
for name in node.typing_env:
|
for name in typing_env:
|
||||||
if builtins.is_allocated(node.typing_env[name]):
|
if builtins.is_allocated(typing_env[name]):
|
||||||
self.youngest_env[name] = Region(None) # not yet known
|
self.youngest_env[name] = Region(None) # not yet known
|
||||||
else:
|
else:
|
||||||
self.youngest_env[name] = None # lives forever
|
self.youngest_env[name] = None # lives forever
|
||||||
|
@ -230,11 +230,15 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
self.youngest_region = old_youngest_region
|
self.youngest_region = old_youngest_region
|
||||||
|
|
||||||
def visit_ModuleT(self, node):
|
def visit_ModuleT(self, node):
|
||||||
self.visit_in_region(node, None)
|
self.visit_in_region(node, None, node.typing_env)
|
||||||
|
|
||||||
def visit_FunctionDefT(self, node):
|
def visit_FunctionDefT(self, node):
|
||||||
self.youngest_env[node.name] = self.youngest_region
|
self.youngest_env[node.name] = self.youngest_region
|
||||||
self.visit_in_region(node, Region(node.loc))
|
self.visit_in_region(node, Region(node.loc), node.typing_env)
|
||||||
|
|
||||||
|
def visit_ClassDefT(self, node):
|
||||||
|
self.youngest_env[node.name] = self.youngest_region
|
||||||
|
self.visit_in_region(node, Region(node.loc), node.constructor_type.attributes)
|
||||||
|
|
||||||
# Only three ways for a pointer to escape:
|
# Only three ways for a pointer to escape:
|
||||||
# * Assigning or op-assigning it (we ensure an outlives relationship)
|
# * Assigning or op-assigning it (we ensure an outlives relationship)
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: bool:<constructor bool>():bool
|
# CHECK-L: bool:<constructor bool {}>():bool
|
||||||
bool()
|
bool()
|
||||||
|
|
||||||
# CHECK-L: bool:<constructor bool>([]:list(elt='a)):bool
|
# CHECK-L: bool:<constructor bool {}>([]:list(elt='a)):bool
|
||||||
bool([])
|
bool([])
|
||||||
|
|
||||||
# CHECK-L: int:<constructor int>():int(width='b)
|
# CHECK-L: int:<constructor int {}>():int(width='b)
|
||||||
int()
|
int()
|
||||||
|
|
||||||
# CHECK-L: int:<constructor int>(1.0:float):int(width='c)
|
# CHECK-L: int:<constructor int {}>(1.0:float):int(width='c)
|
||||||
int(1.0)
|
int(1.0)
|
||||||
|
|
||||||
# CHECK-L: int:<constructor int>(1.0:float, width=64:int(width='d)):int(width=64)
|
# CHECK-L: int:<constructor int {}>(1.0:float, width=64:int(width='d)):int(width=64)
|
||||||
int(1.0, width=64)
|
int(1.0, width=64)
|
||||||
|
|
||||||
# CHECK-L: float:<constructor float>():float
|
# CHECK-L: float:<constructor float {}>():float
|
||||||
float()
|
float()
|
||||||
|
|
||||||
# CHECK-L: float:<constructor float>(1:int(width='e)):float
|
# CHECK-L: float:<constructor float {}>(1:int(width='e)):float
|
||||||
float(1)
|
float(1)
|
||||||
|
|
||||||
# CHECK-L: list:<constructor list>():list(elt='f)
|
# CHECK-L: list:<constructor list {}>():list(elt='f)
|
||||||
list()
|
list()
|
||||||
|
|
||||||
# CHECK-L: len:<function len>([]:list(elt='g)):int(width=32)
|
# CHECK-L: len:<function len>([]:list(elt='g)):int(width=32)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
class c:
|
||||||
|
a = 1
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# CHECK-L: c:<constructor c {a: int(width='a), f: ()->NoneType}>
|
||||||
|
c
|
||||||
|
# CHECK-L: .a:int(width='a)
|
||||||
|
c.a
|
||||||
|
# CHECK-L: .f:()->NoneType
|
||||||
|
c.f
|
|
@ -0,0 +1,10 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: ${LINE:+1}: error: inheritance is not supported
|
||||||
|
class a(1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class b:
|
||||||
|
# CHECK-L: ${LINE:+1}: fatal: class body must contain only assignments and function definitions
|
||||||
|
x += 1
|
|
@ -1,5 +1,5 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: fatal: name 'x' is not bound to anything
|
# CHECK-L: ${LINE:+1}: fatal: undefined variable 'x'
|
||||||
x
|
x
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: Exception:<constructor Exception>
|
# CHECK-L: Exception:<constructor Exception {}>
|
||||||
Exception
|
Exception
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -58,16 +58,16 @@ k = "x"
|
||||||
# CHECK-L: k:str
|
# CHECK-L: k:str
|
||||||
|
|
||||||
IndexError()
|
IndexError()
|
||||||
# CHECK-L: IndexError:<constructor IndexError>():IndexError
|
# CHECK-L: IndexError:<constructor IndexError {}>():IndexError
|
||||||
|
|
||||||
IndexError("x")
|
IndexError("x")
|
||||||
# CHECK-L: IndexError:<constructor IndexError>("x":str):IndexError
|
# CHECK-L: IndexError:<constructor IndexError {}>("x":str):IndexError
|
||||||
|
|
||||||
IndexError("x", 1)
|
IndexError("x", 1)
|
||||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64)):IndexError
|
# CHECK-L: IndexError:<constructor IndexError {}>("x":str, 1:int(width=64)):IndexError
|
||||||
|
|
||||||
IndexError("x", 1, 1)
|
IndexError("x", 1, 1)
|
||||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64)):IndexError
|
# CHECK-L: IndexError:<constructor IndexError {}>("x":str, 1:int(width=64), 1:int(width=64)):IndexError
|
||||||
|
|
||||||
IndexError("x", 1, 1, 1)
|
IndexError("x", 1, 1, 1)
|
||||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64), 1:int(width=64)):IndexError
|
# CHECK-L: IndexError:<constructor IndexError {}>("x":str, 1:int(width=64), 1:int(width=64), 1:int(width=64)):IndexError
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.jit %s
|
||||||
|
# RUN: %python %s
|
||||||
|
|
||||||
|
class c:
|
||||||
|
a = 1
|
||||||
|
def f():
|
||||||
|
return 2
|
||||||
|
|
||||||
|
# CHECK-L: a 1
|
||||||
|
print("a", c.a)
|
||||||
|
# CHECK-L: f() 2
|
||||||
|
print("f()", c.f())
|
Loading…
Reference in New Issue