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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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