Implement class attribute access through instances.

This commit is contained in:
whitequark 2015-08-15 11:04:12 -04:00
parent 00efc8c636
commit 94a2d5f5fa
8 changed files with 130 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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