Implement class definitions and class attribute access.

This commit is contained in:
whitequark 2015-08-15 09:45:16 -04:00
parent fd3c8a2830
commit 00efc8c636
16 changed files with 235 additions and 88 deletions

View File

@ -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):

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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",

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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())