mirror of https://github.com/m-labs/artiq.git
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):
|
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
|
accum or not (is_none(typ) or is_bool(typ) or is_int(typ) or
|
||||||
is_exception(typ) or types.is_constructor(typ))
|
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
|
self.var_name = var_name
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "getlocal({})".format(self.var_name)
|
return "getlocal({})".format(repr(self.var_name))
|
||||||
|
|
||||||
def environment(self):
|
def environment(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
@ -582,7 +582,7 @@ class SetLocal(Instruction):
|
||||||
self.var_name = var_name
|
self.var_name = var_name
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "setlocal({})".format(self.var_name)
|
return "setlocal({})".format(repr(self.var_name))
|
||||||
|
|
||||||
def environment(self):
|
def environment(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
@ -590,6 +590,33 @@ class SetLocal(Instruction):
|
||||||
def value(self):
|
def value(self):
|
||||||
return self.operands[1]
|
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):
|
class GetAttr(Instruction):
|
||||||
"""
|
"""
|
||||||
An intruction that loads an attribute from an object,
|
An intruction that loads an attribute from an object,
|
||||||
|
|
|
@ -711,6 +711,14 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
finally:
|
finally:
|
||||||
self.current_assign = old_assign
|
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:
|
if self.current_assign is None:
|
||||||
return self.append(ir.GetAttr(obj, node.attr,
|
return self.append(ir.GetAttr(obj, node.attr,
|
||||||
name="{}.{}".format(_readable_name(obj), node.attr)))
|
name="{}.{}".format(_readable_name(obj), node.attr)))
|
||||||
|
@ -1398,10 +1406,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def visit_CallT(self, node):
|
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)
|
return self.visit_builtin_call(node)
|
||||||
else:
|
else:
|
||||||
typ = node.func.type.find()
|
|
||||||
func = self.visit(node.func)
|
func = self.visit(node.func)
|
||||||
args = [None] * (len(typ.args) + len(typ.optargs))
|
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
|
# Its attributes are those of the class environment, but wrapped
|
||||||
# appropriately so that they are linked to the class from which they
|
# appropriately so that they are linked to the class from which they
|
||||||
# originate.
|
# originate.
|
||||||
instance_type = types.TMono(node.name)
|
instance_type = types.TInstance(node.name)
|
||||||
instance_type.attributes = OrderedDict({}) # TODO
|
|
||||||
|
|
||||||
# The second type is the type of the constructor itself (in other words,
|
# 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
|
# the class object): it is simply a singleton type that has the class
|
||||||
# environment as attributes.
|
# environment as attributes.
|
||||||
constructor_type = types.TConstructor(instance_type)
|
constructor_type = types.TConstructor(instance_type)
|
||||||
constructor_type.attributes = extractor.typing_env
|
constructor_type.attributes = extractor.typing_env
|
||||||
|
instance_type.constructor = constructor_type
|
||||||
|
|
||||||
self.env_stack[-1][node.name] = 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
|
# assumes no free type variables in .attributes
|
||||||
self._unify(node.type, object_type.attributes[node.attr],
|
self._unify(node.type, object_type.attributes[node.attr],
|
||||||
node.loc, None)
|
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:
|
else:
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"type {type} does not have an attribute '{attr}'",
|
"type {type} does not have an attribute '{attr}'",
|
||||||
|
@ -680,19 +685,24 @@ class Inferencer(algorithm.Visitor):
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
return
|
return
|
||||||
|
|
||||||
if types.is_var(node.func.type):
|
typ = node.func.type.find()
|
||||||
|
|
||||||
|
if types.is_var(typ):
|
||||||
return # not enough info yet
|
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)
|
return self.visit_builtin_call(node)
|
||||||
elif not types.is_function(node.func.type):
|
elif not types.is_function(typ):
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"cannot call this expression of type {type}",
|
"cannot call this expression of type {type}",
|
||||||
{"type": types.TypePrinter().name(node.func.type)},
|
{"type": types.TypePrinter().name(typ)},
|
||||||
node.func.loc, [])
|
node.func.loc, [])
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
return
|
return
|
||||||
|
|
||||||
typ = node.func.type.find()
|
|
||||||
passed_args = dict()
|
passed_args = dict()
|
||||||
|
|
||||||
if len(node.args) > typ.arity():
|
if len(node.args) > typ.arity():
|
||||||
|
|
|
@ -224,7 +224,9 @@ class LLVMIRGenerator:
|
||||||
return llty.as_pointer()
|
return llty.as_pointer()
|
||||||
else: # Catch-all for exceptions and custom classes
|
else: # Catch-all for exceptions and custom classes
|
||||||
if builtins.is_exception(typ):
|
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:
|
else:
|
||||||
name = typ.name
|
name = typ.name
|
||||||
|
|
||||||
|
@ -437,24 +439,23 @@ 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) 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))
|
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)
|
||||||
llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)])
|
llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)])
|
||||||
self.llbuilder.store(lloperand, llfieldptr)
|
self.llbuilder.store(lloperand, llfieldptr)
|
||||||
return llalloc
|
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):
|
def llptr_to_var(self, llenv, env_ty, var_name, var_type=None):
|
||||||
if var_name in env_ty.params:
|
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)
|
var_index = list(env_ty.params.keys()).index(var_name)
|
||||||
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)])
|
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)])
|
||||||
else:
|
else:
|
||||||
|
@ -468,6 +469,11 @@ class LLVMIRGenerator:
|
||||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||||
return self.llbuilder.load(llptr)
|
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):
|
def process_SetLocal(self, insn):
|
||||||
env = insn.environment()
|
env = insn.environment()
|
||||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||||
|
|
|
@ -300,7 +300,7 @@ class TBuiltin(Type):
|
||||||
return fn(accum, self)
|
return fn(accum, self)
|
||||||
|
|
||||||
def __repr__(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):
|
def __eq__(self, other):
|
||||||
return isinstance(other, TBuiltin) and \
|
return isinstance(other, TBuiltin) and \
|
||||||
|
@ -316,9 +316,9 @@ class TBuiltinFunction(TBuiltin):
|
||||||
|
|
||||||
class TConstructor(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
|
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`)
|
:ivar instance: (:class:`Type`)
|
||||||
the type of the instance created by this constructor
|
the type of the instance created by this constructor
|
||||||
|
@ -331,11 +331,29 @@ class TConstructor(TBuiltin):
|
||||||
|
|
||||||
class TExceptionConstructor(TConstructor):
|
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
|
Note that this is not the same as the type of an instance of
|
||||||
the class, which is ``TMono("Exception", ...)``.
|
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):
|
class TValue(Type):
|
||||||
"""
|
"""
|
||||||
A type-level value (such as the integer denoting width of
|
A type-level value (such as the integer denoting width of
|
||||||
|
@ -426,6 +444,17 @@ def is_exn_constructor(typ, name=None):
|
||||||
else:
|
else:
|
||||||
return isinstance(typ, TExceptionConstructor)
|
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):
|
def get_value(typ):
|
||||||
typ = typ.find()
|
typ = typ.find()
|
||||||
if isinstance(typ, TVar):
|
if isinstance(typ, TVar):
|
||||||
|
|
|
@ -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