forked from M-Labs/artiq
Implement class attribute access through instances.
This commit is contained in:
parent
00efc8c636
commit
94a2d5f5fa
@ -202,5 +202,7 @@ def is_collection(typ):
|
||||
|
||||
def is_allocated(typ):
|
||||
return typ.fold(False, lambda accum, typ:
|
||||
is_list(typ) or is_str(typ) or types.is_function(typ) or
|
||||
is_exception(typ) or types.is_constructor(typ))
|
||||
accum or not (is_none(typ) or is_bool(typ) or is_int(typ) or
|
||||
is_float(typ) or is_range(typ) or
|
||||
types.is_c_function(typ) or types.is_rpc_function(typ) or
|
||||
types.is_value(typ)))
|
||||
|
@ -554,7 +554,7 @@ class GetLocal(Instruction):
|
||||
self.var_name = var_name
|
||||
|
||||
def opcode(self):
|
||||
return "getlocal({})".format(self.var_name)
|
||||
return "getlocal({})".format(repr(self.var_name))
|
||||
|
||||
def environment(self):
|
||||
return self.operands[0]
|
||||
@ -582,7 +582,7 @@ class SetLocal(Instruction):
|
||||
self.var_name = var_name
|
||||
|
||||
def opcode(self):
|
||||
return "setlocal({})".format(self.var_name)
|
||||
return "setlocal({})".format(repr(self.var_name))
|
||||
|
||||
def environment(self):
|
||||
return self.operands[0]
|
||||
@ -590,6 +590,33 @@ class SetLocal(Instruction):
|
||||
def value(self):
|
||||
return self.operands[1]
|
||||
|
||||
class GetConstructor(Instruction):
|
||||
"""
|
||||
An intruction that loads a local variable with the given type
|
||||
from an environment, possibly going through multiple levels of indirection.
|
||||
|
||||
:ivar var_name: (string) variable name
|
||||
"""
|
||||
|
||||
"""
|
||||
:param env: (:class:`Value`) local environment
|
||||
:param var_name: (string) local variable name
|
||||
:param var_type: (:class:`types.Type`) local variable type
|
||||
"""
|
||||
def __init__(self, env, var_name, var_type, name=""):
|
||||
assert isinstance(env, Value)
|
||||
assert isinstance(env.type, TEnvironment)
|
||||
assert isinstance(var_name, str)
|
||||
assert isinstance(var_type, types.Type)
|
||||
super().__init__([env], var_type, name)
|
||||
self.var_name = var_name
|
||||
|
||||
def opcode(self):
|
||||
return "getconstructor({})".format(repr(self.var_name))
|
||||
|
||||
def environment(self):
|
||||
return self.operands[0]
|
||||
|
||||
class GetAttr(Instruction):
|
||||
"""
|
||||
An intruction that loads an attribute from an object,
|
||||
|
@ -711,6 +711,14 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
finally:
|
||||
self.current_assign = old_assign
|
||||
|
||||
if node.attr not in node.type.find().attributes:
|
||||
# A class attribute. Get the constructor (class object) and
|
||||
# extract the attribute from it.
|
||||
constructor = obj.type.constructor
|
||||
obj = self.append(ir.GetConstructor(self._env_for(constructor.name),
|
||||
constructor.name, constructor,
|
||||
name="constructor." + constructor.name))
|
||||
|
||||
if self.current_assign is None:
|
||||
return self.append(ir.GetAttr(obj, node.attr,
|
||||
name="{}.{}".format(_readable_name(obj), node.attr)))
|
||||
@ -1398,10 +1406,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
assert False
|
||||
|
||||
def visit_CallT(self, node):
|
||||
if types.is_builtin(node.func.type):
|
||||
typ = node.func.type.find()
|
||||
|
||||
if types.is_constructor(typ) and not types.is_exn_constructor(typ):
|
||||
return self.append(ir.Alloc([], typ.instance))
|
||||
elif types.is_builtin(typ):
|
||||
return self.visit_builtin_call(node)
|
||||
else:
|
||||
typ = node.func.type.find()
|
||||
func = self.visit(node.func)
|
||||
args = [None] * (len(typ.args) + len(typ.optargs))
|
||||
|
||||
|
@ -277,14 +277,14 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||
# 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
|
||||
instance_type = types.TInstance(node.name)
|
||||
|
||||
# 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
|
||||
instance_type.constructor = constructor_type
|
||||
|
||||
self.env_stack[-1][node.name] = constructor_type
|
||||
|
||||
|
@ -91,6 +91,11 @@ class Inferencer(algorithm.Visitor):
|
||||
# assumes no free type variables in .attributes
|
||||
self._unify(node.type, object_type.attributes[node.attr],
|
||||
node.loc, None)
|
||||
elif types.is_instance(object_type) and \
|
||||
node.attr in object_type.constructor.attributes:
|
||||
# assumes no free type variables in .attributes
|
||||
self._unify(node.type, object_type.constructor.attributes[node.attr],
|
||||
node.loc, None)
|
||||
else:
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"type {type} does not have an attribute '{attr}'",
|
||||
@ -680,19 +685,24 @@ class Inferencer(algorithm.Visitor):
|
||||
self.engine.process(diag)
|
||||
return
|
||||
|
||||
if types.is_var(node.func.type):
|
||||
typ = node.func.type.find()
|
||||
|
||||
if types.is_var(typ):
|
||||
return # not enough info yet
|
||||
elif types.is_builtin(node.func.type):
|
||||
elif types.is_constructor(typ) and not types.is_exn_constructor(typ):
|
||||
self._unify(node.type, typ.find().instance,
|
||||
node.loc, None)
|
||||
return
|
||||
elif types.is_builtin(typ):
|
||||
return self.visit_builtin_call(node)
|
||||
elif not types.is_function(node.func.type):
|
||||
elif not types.is_function(typ):
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"cannot call this expression of type {type}",
|
||||
{"type": types.TypePrinter().name(node.func.type)},
|
||||
{"type": types.TypePrinter().name(typ)},
|
||||
node.func.loc, [])
|
||||
self.engine.process(diag)
|
||||
return
|
||||
|
||||
typ = node.func.type.find()
|
||||
passed_args = dict()
|
||||
|
||||
if len(node.args) > typ.arity():
|
||||
|
@ -224,7 +224,9 @@ class LLVMIRGenerator:
|
||||
return llty.as_pointer()
|
||||
else: # Catch-all for exceptions and custom classes
|
||||
if builtins.is_exception(typ):
|
||||
name = 'Exception' # they all share layout
|
||||
name = "class.Exception" # they all share layout
|
||||
elif types.is_constructor(typ):
|
||||
name = "class.{}".format(typ.name)
|
||||
else:
|
||||
name = typ.name
|
||||
|
||||
@ -437,24 +439,23 @@ class LLVMIRGenerator:
|
||||
size=llsize)
|
||||
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name)
|
||||
return llvalue
|
||||
elif builtins.is_exception(insn.type) or types.is_constructor(insn.type):
|
||||
elif not builtins.is_allocated(insn.type):
|
||||
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
|
||||
for index, elt in enumerate(insn.operands):
|
||||
llvalue = self.llbuilder.insert_value(llvalue, self.map(elt), index)
|
||||
llvalue.name = insn.name
|
||||
return llvalue
|
||||
else: # catchall for exceptions and custom (allocated) classes
|
||||
llalloc = self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True))
|
||||
for index, operand in enumerate(insn.operands):
|
||||
lloperand = self.map(operand)
|
||||
llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)])
|
||||
self.llbuilder.store(lloperand, llfieldptr)
|
||||
return llalloc
|
||||
elif builtins.is_allocated(insn.type):
|
||||
assert False
|
||||
else: # immutable
|
||||
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
|
||||
for index, elt in enumerate(insn.operands):
|
||||
llvalue = self.llbuilder.insert_value(llvalue, self.map(elt), index)
|
||||
llvalue.name = insn.name
|
||||
return llvalue
|
||||
|
||||
def llptr_to_var(self, llenv, env_ty, var_name):
|
||||
if var_name in env_ty.params:
|
||||
def llptr_to_var(self, llenv, env_ty, var_name, var_type=None):
|
||||
if var_name in env_ty.params and (var_type is None or
|
||||
env_ty.params[var_name] == var_type):
|
||||
var_index = list(env_ty.params.keys()).index(var_name)
|
||||
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)])
|
||||
else:
|
||||
@ -468,6 +469,11 @@ class LLVMIRGenerator:
|
||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||
return self.llbuilder.load(llptr)
|
||||
|
||||
def process_GetConstructor(self, insn):
|
||||
env = insn.environment()
|
||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name, insn.type)
|
||||
return self.llbuilder.load(llptr)
|
||||
|
||||
def process_SetLocal(self, insn):
|
||||
env = insn.environment()
|
||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||
|
@ -300,7 +300,7 @@ class TBuiltin(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
return "py2llvm.types.TBuiltin(%s)" % repr(self.name)
|
||||
return "py2llvm.types.{}({})".format(type(self).__name__, repr(self.name))
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, TBuiltin) and \
|
||||
@ -316,9 +316,9 @@ class TBuiltinFunction(TBuiltin):
|
||||
|
||||
class TConstructor(TBuiltin):
|
||||
"""
|
||||
A type of a constructor of a builtin class, e.g. ``list``.
|
||||
A type of a constructor of a class, e.g. ``list``.
|
||||
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", ...)`` (or a descendant).
|
||||
|
||||
:ivar instance: (:class:`Type`)
|
||||
the type of the instance created by this constructor
|
||||
@ -331,11 +331,29 @@ class TConstructor(TBuiltin):
|
||||
|
||||
class TExceptionConstructor(TConstructor):
|
||||
"""
|
||||
A type of a constructor of a builtin exception, e.g. ``Exception``.
|
||||
A type of a constructor of an exception, e.g. ``Exception``.
|
||||
Note that this is not the same as the type of an instance of
|
||||
the class, which is ``TMono("Exception", ...)``.
|
||||
"""
|
||||
|
||||
class TInstance(TMono):
|
||||
"""
|
||||
A type of an instance of a user-defined class.
|
||||
|
||||
:ivar constructor: (:class:`TConstructor`)
|
||||
the type of the constructor with which this instance
|
||||
was created
|
||||
"""
|
||||
|
||||
def __init__(self, name, attributes=OrderedDict()):
|
||||
super().__init__(name)
|
||||
self.attributes = attributes
|
||||
|
||||
def __repr__(self):
|
||||
return "py2llvm.types.TInstance({}, {]})".format(
|
||||
repr(self.name), repr(self.attributes))
|
||||
|
||||
|
||||
class TValue(Type):
|
||||
"""
|
||||
A type-level value (such as the integer denoting width of
|
||||
@ -426,6 +444,17 @@ def is_exn_constructor(typ, name=None):
|
||||
else:
|
||||
return isinstance(typ, TExceptionConstructor)
|
||||
|
||||
def is_instance(typ, name=None):
|
||||
typ = typ.find()
|
||||
if name is not None:
|
||||
return isinstance(typ, TInstance) and \
|
||||
typ.name == name
|
||||
else:
|
||||
return isinstance(typ, TInstance)
|
||||
|
||||
def is_value(typ):
|
||||
return isinstance(typ.find(), TValue)
|
||||
|
||||
def get_value(typ):
|
||||
typ = typ.find()
|
||||
if isinstance(typ, TVar):
|
||||
|
16
lit-test/test/integration/instance.py
Normal file
16
lit-test/test/integration/instance.py
Normal file
@ -0,0 +1,16 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
||||
# RUN: %python %s
|
||||
|
||||
class c:
|
||||
a = 1
|
||||
|
||||
i = c()
|
||||
|
||||
# CHECK-L: a 1
|
||||
print("a", i.a)
|
||||
|
||||
def f():
|
||||
c = None
|
||||
# CHECK-L: shadow a 1
|
||||
print("shadow a", i.a)
|
||||
f()
|
Loading…
Reference in New Issue
Block a user