Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 94a2d5f5fa3e
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 51c591f01aa4
Choose a head ref
  • 3 commits
  • 11 files changed
  • 1 contributor

Commits on Aug 18, 2015

  1. Copy the full SHA
    1040a40 View commit details

Commits on Aug 19, 2015

  1. Implement methods.

    whitequark committed Aug 19, 2015
    Copy the full SHA
    6c8de9b View commit details
  2. Unbreak tests.

    whitequark committed Aug 19, 2015
    Copy the full SHA
    51c591f View commit details
10 changes: 5 additions & 5 deletions artiq/compiler/builtins.py
Original file line number Diff line number Diff line change
@@ -201,8 +201,8 @@ def is_collection(typ):
types.is_mono(typ, "list")

def is_allocated(typ):
return typ.fold(False, lambda accum, 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)))
return 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_method(typ) or types.is_tuple(typ) or
types.is_value(typ))
64 changes: 43 additions & 21 deletions artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -711,13 +711,22 @@ def visit_AttributeT(self, node):
finally:
self.current_assign = old_assign

if node.attr not in node.type.find().attributes:
if node.attr not in obj.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))
print(node)
print(obj)
constr_type = obj.type.constructor
constr = self.append(ir.GetConstructor(self._env_for(constr_type.name),
constr_type.name, constr_type,
name="constructor." + constr_type.name))

if types.is_function(constr.type.attributes[node.attr]):
# A method. Construct a method object instead.
func = self.append(ir.GetAttr(constr, node.attr))
return self.append(ir.Alloc([func, obj], node.type))
else:
obj = constr

if self.current_assign is None:
return self.append(ir.GetAttr(obj, node.attr,
@@ -1402,47 +1411,60 @@ def body_gen(index):
return ir.Constant(None, builtins.TNone())
elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ):
return self.append(ir.Alloc([], typ.instance))
else:
assert False

def visit_CallT(self, node):
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):
if types.is_builtin(typ):
return self.visit_builtin_call(node)
else:
func = self.visit(node.func)
args = [None] * (len(typ.args) + len(typ.optargs))
if types.is_function(typ):
func = self.visit(node.func)
self_arg = None
fn_typ = typ
elif types.is_method(typ):
method = self.visit(node.func)
func = self.append(ir.GetAttr(method, "__func__"))
self_arg = self.append(ir.GetAttr(method, "__self__"))
fn_typ = types.get_method_function(typ)

args = [None] * (len(fn_typ.args) + len(fn_typ.optargs))

for index, arg_node in enumerate(node.args):
arg = self.visit(arg_node)
if index < len(typ.args):
if index < len(fn_typ.args):
args[index] = arg
else:
args[index] = self.append(ir.Alloc([arg], ir.TOption(arg.type)))

for keyword in node.keywords:
arg = self.visit(keyword.value)
if keyword.arg in typ.args:
for index, arg_name in enumerate(typ.args):
if keyword.arg in fn_typ.args:
for index, arg_name in enumerate(fn_typ.args):
if keyword.arg == arg_name:
assert args[index] is None
args[index] = arg
break
elif keyword.arg in typ.optargs:
for index, optarg_name in enumerate(typ.optargs):
elif keyword.arg in fn_typ.optargs:
for index, optarg_name in enumerate(fn_typ.optargs):
if keyword.arg == optarg_name:
assert args[len(typ.args) + index] is None
args[len(typ.args) + index] = \
assert args[len(fn_typ.args) + index] is None
args[len(fn_typ.args) + index] = \
self.append(ir.Alloc([arg], ir.TOption(arg.type)))
break

for index, optarg_name in enumerate(typ.optargs):
if args[len(typ.args) + index] is None:
args[len(typ.args) + index] = \
self.append(ir.Alloc([], ir.TOption(typ.optargs[optarg_name])))
for index, optarg_name in enumerate(fn_typ.optargs):
if args[len(fn_typ.args) + index] is None:
args[len(fn_typ.args) + index] = \
self.append(ir.Alloc([], ir.TOption(fn_typ.optargs[optarg_name])))

if self_arg is not None:
assert args[0] is None
args[0] = self_arg

assert None not in args

95 changes: 71 additions & 24 deletions artiq/compiler/transforms/inferencer.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ def __init__(self, engine):
self.in_loop = False
self.has_return = False

def _unify(self, typea, typeb, loca, locb, makenotes=None):
def _unify(self, typea, typeb, loca, locb, makenotes=None, when=""):
try:
typea.unify(typeb)
except types.UnificationError as e:
@@ -45,16 +45,19 @@ def _unify(self, typea, typeb, loca, locb, makenotes=None):
locb))

highlights = [locb] if locb else []
if e.typea.find() == typea.find() and e.typeb.find() == typeb.find():
if e.typea.find() == typea.find() and e.typeb.find() == typeb.find() or \
e.typeb.find() == typea.find() and e.typea.find() == typeb.find():
diag = diagnostic.Diagnostic("error",
"cannot unify {typea} with {typeb}",
{"typea": printer.name(typea), "typeb": printer.name(typeb)},
"cannot unify {typea} with {typeb}{when}",
{"typea": printer.name(typea), "typeb": printer.name(typeb),
"when": when},
loca, highlights, notes)
else: # give more detail
diag = diagnostic.Diagnostic("error",
"cannot unify {typea} with {typeb}: {fraga} is incompatible with {fragb}",
"cannot unify {typea} with {typeb}{when}: {fraga} is incompatible with {fragb}",
{"typea": printer.name(typea), "typeb": printer.name(typeb),
"fraga": printer.name(e.typea), "fragb": printer.name(e.typeb)},
"fraga": printer.name(e.typea), "fragb": printer.name(e.typeb),
"when": when},
loca, highlights, notes)
self.engine.process(diag)

@@ -88,13 +91,43 @@ def visit_AttributeT(self, node):
object_type = node.value.type.find()
if not types.is_var(object_type):
if node.attr in object_type.attributes:
# assumes no free type variables in .attributes
# 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],
# Assumes no free type variables in .attributes.
attr_type = object_type.constructor.attributes[node.attr].find()
if types.is_function(attr_type):
# Convert to a method.
if len(attr_type.args) < 1:
diag = diagnostic.Diagnostic("error",
"function '{attr}{type}' of class '{class}' cannot accept a self argument",
{"attr": node.attr, "type": types.TypePrinter().name(attr_type),
"class": object_type.name},
node.loc)
self.engine.process(diag)
return
else:
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"expression of type {typea}",
{"typea": printer.name(typea)},
loca),
diagnostic.Diagnostic("note",
"reference to a class function of type {typeb}",
{"typeb": printer.name(attr_type)},
locb)
]

self._unify(object_type, list(attr_type.args.values())[0],
node.value.loc, node.loc,
makenotes=makenotes,
when=" while inferring the type for self argument")

attr_type = types.TMethod(object_type, attr_type)
self._unify(node.type, attr_type,
node.loc, None)
else:
diag = diagnostic.Diagnostic("error",
@@ -672,6 +705,12 @@ def makenotes(printer, typea, typeb, loca, locb):
pass
else:
diagnose(valid_forms())
elif types.is_constructor(typ):
# An user-defined class.
self._unify(node.type, typ.find().instance,
node.loc, None)
else:
assert False

def visit_CallT(self, node):
self.generic_visit(node)
@@ -689,36 +728,44 @@ def visit_CallT(self, node):

if types.is_var(typ):
return # not enough info yet
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(typ):
elif not (types.is_function(typ) or types.is_method(typ)):
diag = diagnostic.Diagnostic("error",
"cannot call this expression of type {type}",
{"type": types.TypePrinter().name(typ)},
node.func.loc, [])
self.engine.process(diag)
return

if types.is_function(typ):
typ_arity = typ.arity()
typ_args = typ.args
typ_optargs = typ.optargs
typ_ret = typ.ret
else:
typ = types.get_method_function(typ)
typ_arity = typ.arity() - 1
typ_args = OrderedDict(list(typ.args.items())[1:])
typ_optargs = typ.optargs
typ_ret = typ.ret

passed_args = dict()

if len(node.args) > typ.arity():
if len(node.args) > typ_arity:
note = diagnostic.Diagnostic("note",
"extraneous argument(s)", {},
node.args[typ.arity()].loc.join(node.args[-1].loc))
node.args[typ_arity].loc.join(node.args[-1].loc))
diag = diagnostic.Diagnostic("error",
"this function of type {type} accepts at most {num} arguments",
{"type": types.TypePrinter().name(node.func.type),
"num": typ.arity()},
"num": typ_arity},
node.func.loc, [], [note])
self.engine.process(diag)
return

for actualarg, (formalname, formaltyp) in \
zip(node.args, list(typ.args.items()) + list(typ.optargs.items())):
zip(node.args, list(typ_args.items()) + list(typ_optargs.items())):
self._unify(actualarg.type, formaltyp,
actualarg.loc, None)
passed_args[formalname] = actualarg.loc
@@ -732,15 +779,15 @@ def visit_CallT(self, node):
self.engine.process(diag)
return

if keyword.arg in typ.args:
self._unify(keyword.value.type, typ.args[keyword.arg],
if keyword.arg in typ_args:
self._unify(keyword.value.type, typ_args[keyword.arg],
keyword.value.loc, None)
elif keyword.arg in typ.optargs:
self._unify(keyword.value.type, typ.optargs[keyword.arg],
elif keyword.arg in typ_optargs:
self._unify(keyword.value.type, typ_optargs[keyword.arg],
keyword.value.loc, None)
passed_args[keyword.arg] = keyword.arg_loc

for formalname in typ.args:
for formalname in typ_args:
if formalname not in passed_args:
note = diagnostic.Diagnostic("note",
"the called function is of type {type}",
@@ -753,7 +800,7 @@ def visit_CallT(self, node):
self.engine.process(diag)
return

self._unify(node.type, typ.ret,
self._unify(node.type, typ_ret,
node.loc, None)

def visit_LambdaT(self, node):
6 changes: 5 additions & 1 deletion artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -192,6 +192,10 @@ def llty_of_type(self, typ, bare=False, for_return=False):
return llty
else:
return ll.LiteralStructType([envarg, llty.as_pointer()])
elif types.is_method(typ):
llfuncty = self.llty_of_type(types.get_method_function(typ))
llselfty = self.llty_of_type(types.get_method_self(typ))
return ll.LiteralStructType([llfuncty, llselfty])
elif builtins.is_none(typ):
if for_return:
return llvoid
@@ -387,7 +391,7 @@ def process_function(self, func):
assert llinsn is not None
self.llmap[insn] = llinsn

if insn.loc is not None:
if insn.loc is not None and not isinstance(llinsn, ll.Constant):
diloc = self.debug_info_emitter.emit_loc(insn.loc, disubprogram)
llinsn.set_metadata('dbg', diloc)

28 changes: 25 additions & 3 deletions artiq/compiler/types.py
Original file line number Diff line number Diff line change
@@ -99,8 +99,8 @@ class TMono(Type):
attributes = OrderedDict()

def __init__(self, name, params={}):
assert isinstance(params, dict)
self.name, self.params = name, params
assert isinstance(params, (dict, OrderedDict))
self.name, self.params = name, OrderedDict(sorted(params.items()))

def find(self):
return self
@@ -350,9 +350,20 @@ def __init__(self, name, attributes=OrderedDict()):
self.attributes = attributes

def __repr__(self):
return "py2llvm.types.TInstance({}, {]})".format(
return "py2llvm.types.TInstance({}, {})".format(
repr(self.name), repr(self.attributes))

class TMethod(TMono):
"""
A type of a method.
"""

def __init__(self, self_type, function_type):
super().__init__("method", {"self": self_type, "fn": function_type})
self.attributes = OrderedDict([
("__func__", function_type),
("__self__", self_type),
])

class TValue(Type):
"""
@@ -452,6 +463,17 @@ def is_instance(typ, name=None):
else:
return isinstance(typ, TInstance)

def is_method(typ):
return isinstance(typ.find(), TMethod)

def get_method_self(typ):
if is_method(typ):
return typ.find().params["self"]

def get_method_function(typ):
if is_method(typ):
return typ.find().params["fn"]

def is_value(typ):
return isinstance(typ.find(), TValue)

Loading