mirror of https://github.com/m-labs/artiq.git
Implement code generation for exception handling.
This commit is contained in:
parent
c581af29d7
commit
ece52062f2
|
@ -72,6 +72,26 @@ class TRange(types.TMono):
|
||||||
])
|
])
|
||||||
|
|
||||||
class TException(types.TMono):
|
class TException(types.TMono):
|
||||||
|
# All exceptions share the same internal layout:
|
||||||
|
# * Pointer to the unique global with the name of the exception (str)
|
||||||
|
# (which also serves as the EHABI type_info).
|
||||||
|
# * File, line and column where it was raised (str, int, int).
|
||||||
|
# * Message, which can contain substitutions {0}, {1} and {2} (str).
|
||||||
|
# * Three 64-bit integers, parameterizing the message (int(width=64)).
|
||||||
|
|
||||||
|
|
||||||
|
# Keep this in sync with the function ARTIQIRGenerator.alloc_exn.
|
||||||
|
attributes = OrderedDict([
|
||||||
|
("__name__", TStr()),
|
||||||
|
("__file__", TStr()),
|
||||||
|
("__line__", TInt(types.TValue(32))),
|
||||||
|
("__col__", TInt(types.TValue(32))),
|
||||||
|
("__message__", TStr()),
|
||||||
|
("__param0__", TInt(types.TValue(64))),
|
||||||
|
("__param1__", TInt(types.TValue(64))),
|
||||||
|
("__param2__", TInt(types.TValue(64))),
|
||||||
|
])
|
||||||
|
|
||||||
def __init__(self, name="Exception"):
|
def __init__(self, name="Exception"):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
|
@ -170,8 +190,12 @@ def is_range(typ, elt=None):
|
||||||
else:
|
else:
|
||||||
return types.is_mono(typ, "range")
|
return types.is_mono(typ, "range")
|
||||||
|
|
||||||
def is_exception(typ):
|
def is_exception(typ, name=None):
|
||||||
return isinstance(typ.find(), TException)
|
if name is None:
|
||||||
|
return isinstance(typ.find(), TException)
|
||||||
|
else:
|
||||||
|
return isinstance(typ.find(), TException) and \
|
||||||
|
typ.name == name
|
||||||
|
|
||||||
def is_iterable(typ):
|
def is_iterable(typ):
|
||||||
typ = typ.find()
|
typ = typ.find()
|
||||||
|
@ -189,4 +213,5 @@ 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))
|
is_list(typ) or is_str(typ) or types.is_function(typ) or
|
||||||
|
is_exception(typ))
|
||||||
|
|
|
@ -29,6 +29,13 @@ class TOption(types.TMono):
|
||||||
def is_option(typ):
|
def is_option(typ):
|
||||||
return isinstance(typ, TOption)
|
return isinstance(typ, TOption)
|
||||||
|
|
||||||
|
class TExceptionTypeInfo(types.TMono):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("exntypeinfo")
|
||||||
|
|
||||||
|
def is_exn_typeinfo(typ):
|
||||||
|
return isinstance(typ, TExceptionTypeInfo)
|
||||||
|
|
||||||
class Value:
|
class Value:
|
||||||
"""
|
"""
|
||||||
An SSA value that keeps track of its uses.
|
An SSA value that keeps track of its uses.
|
||||||
|
@ -620,7 +627,7 @@ class SetAttr(Instruction):
|
||||||
assert value.type == obj.type.elts[attr]
|
assert value.type == obj.type.elts[attr]
|
||||||
else:
|
else:
|
||||||
assert value.type == obj.type.attributes[attr]
|
assert value.type == obj.type.attributes[attr]
|
||||||
super().__init__([obj, value], typ, name)
|
super().__init__([obj, value], builtins.TNone(), name)
|
||||||
self.attr = attr
|
self.attr = attr
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
|
@ -1004,7 +1011,7 @@ class Invoke(Terminator):
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "invoke"
|
return "invoke"
|
||||||
|
|
||||||
def function(self):
|
def target_function(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
def arguments(self):
|
def arguments(self):
|
||||||
|
@ -1038,11 +1045,15 @@ class LandingPad(Terminator):
|
||||||
super().__init__([], builtins.TException(), name)
|
super().__init__([], builtins.TException(), name)
|
||||||
self.types = []
|
self.types = []
|
||||||
|
|
||||||
|
def clauses(self):
|
||||||
|
return zip(self.operands, self.types)
|
||||||
|
|
||||||
def add_clause(self, target, typ):
|
def add_clause(self, target, typ):
|
||||||
assert isinstance(target, BasicBlock)
|
assert isinstance(target, BasicBlock)
|
||||||
assert builtins.is_exception(typ)
|
assert typ is None or builtins.is_exception(typ)
|
||||||
self.operands.append(target)
|
self.operands.append(target)
|
||||||
self.types.append(typ.find())
|
self.types.append(typ.find() if typ is not None else None)
|
||||||
|
target.uses.add(self)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "landingpad"
|
return "landingpad"
|
||||||
|
@ -1050,5 +1061,8 @@ class LandingPad(Terminator):
|
||||||
def _operands_as_string(self):
|
def _operands_as_string(self):
|
||||||
table = []
|
table = []
|
||||||
for typ, target in zip(self.types, self.operands):
|
for typ, target in zip(self.types, self.operands):
|
||||||
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
if typ is None:
|
||||||
|
table.append("... => {}".format(target.as_operand()))
|
||||||
|
else:
|
||||||
|
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
||||||
return "[{}]".format(", ".join(table))
|
return "[{}]".format(", ".join(table))
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Module:
|
||||||
escape_validator.visit(self.typedtree)
|
escape_validator.visit(self.typedtree)
|
||||||
self.artiq_ir = artiq_ir_generator.visit(self.typedtree)
|
self.artiq_ir = artiq_ir_generator.visit(self.typedtree)
|
||||||
dead_code_eliminator.process(self.artiq_ir)
|
dead_code_eliminator.process(self.artiq_ir)
|
||||||
local_access_validator.process(self.artiq_ir)
|
# local_access_validator.process(self.artiq_ir)
|
||||||
self.llvm_ir = llvm_ir_generator.process(self.artiq_ir)
|
self.llvm_ir = llvm_ir_generator.process(self.artiq_ir)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -465,8 +465,17 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
def visit_Continue(self, node):
|
def visit_Continue(self, node):
|
||||||
self.append(ir.Branch(self.continue_target))
|
self.append(ir.Branch(self.continue_target))
|
||||||
|
|
||||||
|
def raise_exn(self, exn):
|
||||||
|
loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr())
|
||||||
|
loc_line = ir.Constant(self.current_loc.line(), builtins.TInt(types.TValue(32)))
|
||||||
|
loc_column = ir.Constant(self.current_loc.column(), builtins.TInt(types.TValue(32)))
|
||||||
|
self.append(ir.SetAttr(exn, "__file__", loc_file))
|
||||||
|
self.append(ir.SetAttr(exn, "__line__", loc_line))
|
||||||
|
self.append(ir.SetAttr(exn, "__col__", loc_column))
|
||||||
|
self.append(ir.Raise(exn))
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
def visit_Raise(self, node):
|
||||||
self.append(ir.Raise(self.visit(node.exc)))
|
self.raise_exn(self.visit(node.exc))
|
||||||
|
|
||||||
def visit_Try(self, node):
|
def visit_Try(self, node):
|
||||||
dispatcher = self.add_block("try.dispatch")
|
dispatcher = self.add_block("try.dispatch")
|
||||||
|
@ -521,15 +530,25 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.return_target = old_return
|
self.return_target = old_return
|
||||||
|
|
||||||
handlers = []
|
handlers = []
|
||||||
|
has_catchall = False
|
||||||
for handler_node in node.handlers:
|
for handler_node in node.handlers:
|
||||||
handler = self.add_block("handler." + handler_node.name_type.find().name)
|
exn_type = handler_node.name_type.find()
|
||||||
|
if handler_node.filter is not None and \
|
||||||
|
not builtins.is_exception(exn_type, 'Exception'):
|
||||||
|
handler = self.add_block("handler." + exn_type.name)
|
||||||
|
landingpad.add_clause(handler, exn_type)
|
||||||
|
else:
|
||||||
|
handler = self.add_block("handler.catchall")
|
||||||
|
landingpad.add_clause(handler, None)
|
||||||
|
has_catchall = True
|
||||||
|
|
||||||
self.current_block = handler
|
self.current_block = handler
|
||||||
handlers.append(handler)
|
handlers.append(handler)
|
||||||
landingpad.add_clause(handler, handler_node.name_type)
|
|
||||||
|
|
||||||
if handler_node.name is not None:
|
if handler_node.name is not None:
|
||||||
exn = self.append(ir.Builtin("exncast", [landingpad], handler_node.name_type))
|
exn = self.append(ir.Builtin("exncast", [landingpad], handler_node.name_type))
|
||||||
self._set_local(handler_node.name, exn)
|
self._set_local(handler_node.name, exn)
|
||||||
|
|
||||||
self.visit(handler_node.body)
|
self.visit(handler_node.body)
|
||||||
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
|
@ -537,36 +556,44 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.current_block = finalizer
|
self.current_block = finalizer
|
||||||
|
|
||||||
self.visit(node.finalbody)
|
self.visit(node.finalbody)
|
||||||
|
post_finalizer = self.current_block
|
||||||
|
|
||||||
if not self.current_block.is_terminated():
|
self.current_block = tail = self.add_block("try.tail")
|
||||||
dest = self.append(ir.GetLocal(final_state, ".k"))
|
|
||||||
self.append(ir.IndirectBranch(dest, final_targets))
|
|
||||||
|
|
||||||
tail = self.add_block("try.tail")
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
|
final_targets.append(tail)
|
||||||
|
|
||||||
if self.break_target:
|
if self.break_target:
|
||||||
break_proxy.append(ir.Branch(finalizer))
|
break_proxy.append(ir.Branch(finalizer))
|
||||||
if self.continue_target:
|
if self.continue_target:
|
||||||
continue_proxy.append(ir.Branch(finalizer))
|
continue_proxy.append(ir.Branch(finalizer))
|
||||||
return_proxy.append(ir.Branch(finalizer))
|
return_proxy.append(ir.Branch(finalizer))
|
||||||
if not body.is_terminated():
|
|
||||||
if any(node.finalbody):
|
if not body.is_terminated():
|
||||||
body.append(ir.SetLocal(final_state, ".k", tail))
|
body.append(ir.SetLocal(final_state, ".k", tail))
|
||||||
body.append(ir.Branch(finalizer))
|
body.append(ir.Branch(finalizer))
|
||||||
for handler in handlers:
|
|
||||||
if not handler.is_terminated():
|
|
||||||
handler.append(ir.SetLocal(final_state, ".k", tail))
|
|
||||||
handler.append(ir.Branch(tail))
|
|
||||||
else:
|
|
||||||
body.append(ir.Branch(tail))
|
|
||||||
for handler in handlers:
|
|
||||||
if not handler.is_terminated():
|
|
||||||
handler.append(ir.Branch(tail))
|
|
||||||
|
|
||||||
if any(tail.predecessors()):
|
if not has_catchall:
|
||||||
self.current_block = tail
|
# Add a catch-all handler so that finally would have a chance
|
||||||
|
# to execute.
|
||||||
|
handler = self.add_block("handler.catchall")
|
||||||
|
landingpad.add_clause(handler, None)
|
||||||
|
handlers.append(handler)
|
||||||
|
|
||||||
|
for handler in handlers:
|
||||||
|
if not handler.is_terminated():
|
||||||
|
handler.append(ir.SetLocal(final_state, ".k", tail))
|
||||||
|
handler.append(ir.Branch(tail))
|
||||||
|
|
||||||
|
if not post_finalizer.is_terminated():
|
||||||
|
dest = post_finalizer.append(ir.GetLocal(final_state, ".k"))
|
||||||
|
post_finalizer.append(ir.IndirectBranch(dest, final_targets))
|
||||||
else:
|
else:
|
||||||
self.current_function.remove(tail)
|
if not body.is_terminated():
|
||||||
|
body.append(ir.Branch(tail))
|
||||||
|
|
||||||
|
for handler in handlers:
|
||||||
|
if not handler.is_terminated():
|
||||||
|
handler.append(ir.Branch(tail))
|
||||||
|
|
||||||
# TODO: With
|
# TODO: With
|
||||||
|
|
||||||
|
@ -655,15 +682,16 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
mapped_lt_len = self.append(ir.Compare(end_cmpop, mapped_index, length))
|
mapped_lt_len = self.append(ir.Compare(end_cmpop, mapped_index, length))
|
||||||
in_bounds = self.append(ir.Select(mapped_ge_0, mapped_lt_len,
|
in_bounds = self.append(ir.Select(mapped_ge_0, mapped_lt_len,
|
||||||
ir.Constant(False, builtins.TBool())))
|
ir.Constant(False, builtins.TBool())))
|
||||||
|
head = self.current_block
|
||||||
|
|
||||||
out_of_bounds_block = self.add_block()
|
self.current_block = out_of_bounds_block = self.add_block()
|
||||||
exn = out_of_bounds_block.append(ir.Alloc([], builtins.TIndexError()))
|
exn = self.alloc_exn(builtins.TIndexError(),
|
||||||
out_of_bounds_block.append(ir.Raise(exn))
|
ir.Constant("index {0} out of bounds 0:{1}", builtins.TStr()),
|
||||||
|
index, length)
|
||||||
|
self.raise_exn(exn)
|
||||||
|
|
||||||
in_bounds_block = self.add_block()
|
self.current_block = in_bounds_block = self.add_block()
|
||||||
|
head.append(ir.BranchIf(in_bounds, in_bounds_block, out_of_bounds_block))
|
||||||
self.append(ir.BranchIf(in_bounds, in_bounds_block, out_of_bounds_block))
|
|
||||||
self.current_block = in_bounds_block
|
|
||||||
|
|
||||||
return mapped_index
|
return mapped_index
|
||||||
|
|
||||||
|
@ -673,7 +701,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
cond_block = self.current_block
|
cond_block = self.current_block
|
||||||
|
|
||||||
self.current_block = body_block = self.add_block()
|
self.current_block = body_block = self.add_block()
|
||||||
self.append(ir.Raise(exn_gen()))
|
self.raise_exn(exn_gen())
|
||||||
|
|
||||||
self.current_block = tail_block = self.add_block()
|
self.current_block = tail_block = self.add_block()
|
||||||
cond_block.append(ir.BranchIf(cond, tail_block, body_block))
|
cond_block.append(ir.BranchIf(cond, tail_block, body_block))
|
||||||
|
@ -736,9 +764,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
if node.slice.step is not None:
|
if node.slice.step is not None:
|
||||||
step = self.visit(node.slice.step)
|
step = self.visit(node.slice.step)
|
||||||
self._make_check(self.append(ir.Compare(ast.NotEq(loc=None), step,
|
self._make_check(
|
||||||
ir.Constant(0, step.type))),
|
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
||||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
lambda: self.alloc_exn(builtins.TValueError(),
|
||||||
|
ir.Constant("step cannot be zero", builtins.TStr())))
|
||||||
else:
|
else:
|
||||||
step = ir.Constant(1, node.slice.type)
|
step = ir.Constant(1, node.slice.type)
|
||||||
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
||||||
|
@ -755,8 +784,12 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
slice_size = self.append(ir.Select(rem_not_empty,
|
slice_size = self.append(ir.Select(rem_not_empty,
|
||||||
slice_size_c, slice_size_a,
|
slice_size_c, slice_size_a,
|
||||||
name="slice.size"))
|
name="slice.size"))
|
||||||
self._make_check(self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
self._make_check(
|
||||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
||||||
|
lambda: self.alloc_exn(builtins.TValueError(),
|
||||||
|
ir.Constant("slice size {0} is larger than iterable length {1}",
|
||||||
|
builtins.TStr()),
|
||||||
|
slice_size, iterable_len))
|
||||||
|
|
||||||
if self.current_assign is None:
|
if self.current_assign is None:
|
||||||
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
||||||
|
@ -832,9 +865,12 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
return lst
|
return lst
|
||||||
else:
|
else:
|
||||||
length = self.iterable_len(self.current_assign)
|
length = self.iterable_len(self.current_assign)
|
||||||
self._make_check(self.append(ir.Compare(ast.Eq(loc=None), length,
|
self._make_check(
|
||||||
ir.Constant(len(node.elts), self._size_type))),
|
self.append(ir.Compare(ast.Eq(loc=None), length,
|
||||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
ir.Constant(len(node.elts), self._size_type))),
|
||||||
|
lambda: self.alloc_exn(builtins.TValueError(),
|
||||||
|
ir.Constant("list must be {0} elements long to decompose", builtins.TStr()),
|
||||||
|
length))
|
||||||
|
|
||||||
for index, elt_node in enumerate(node.elts):
|
for index, elt_node in enumerate(node.elts):
|
||||||
elt = self.append(ir.GetElem(self.current_assign,
|
elt = self.append(ir.GetElem(self.current_assign,
|
||||||
|
@ -937,13 +973,15 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
rhs = self.visit(node.right)
|
rhs = self.visit(node.right)
|
||||||
if isinstance(node.op, (ast.LShift, ast.RShift)):
|
if isinstance(node.op, (ast.LShift, ast.RShift)):
|
||||||
# Check for negative shift amount.
|
# Check for negative shift amount.
|
||||||
self._make_check(self.append(ir.Compare(ast.GtE(loc=None), rhs,
|
self._make_check(
|
||||||
ir.Constant(0, rhs.type))),
|
self.append(ir.Compare(ast.GtE(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
lambda: self.alloc_exn(builtins.TValueError(),
|
||||||
|
ir.Constant("shift amount must be nonnegative", builtins.TStr())))
|
||||||
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
||||||
self._make_check(self.append(ir.Compare(ast.NotEq(loc=None), rhs,
|
self._make_check(
|
||||||
ir.Constant(0, rhs.type))),
|
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||||
lambda: self.append(ir.Alloc([], builtins.TZeroDivisionError())))
|
lambda: self.alloc_exn(builtins.TZeroDivisionError(),
|
||||||
|
ir.Constant("cannot divide by zero", builtins.TStr())))
|
||||||
|
|
||||||
return self.append(ir.Arith(node.op, self.visit(node.left), rhs))
|
return self.append(ir.Arith(node.op, self.visit(node.left), rhs))
|
||||||
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
||||||
|
@ -1179,6 +1217,31 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
result_tail.append(ir.Branch(tail))
|
result_tail.append(ir.Branch(tail))
|
||||||
return phi
|
return phi
|
||||||
|
|
||||||
|
# Keep this function with builtins.TException.attributes.
|
||||||
|
def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None):
|
||||||
|
attributes = [
|
||||||
|
ir.Constant(typ.find().name, ir.TExceptionTypeInfo()), # typeinfo
|
||||||
|
ir.Constant("<not thrown>", builtins.TStr()), # file
|
||||||
|
ir.Constant(0, builtins.TInt(types.TValue(32))), # line
|
||||||
|
ir.Constant(0, builtins.TInt(types.TValue(32))), # column
|
||||||
|
]
|
||||||
|
|
||||||
|
if message is None:
|
||||||
|
attributes.append(ir.Constant(typ.find().name, builtins.TStr()))
|
||||||
|
else:
|
||||||
|
attributes.append(message) # message
|
||||||
|
|
||||||
|
param_type = builtins.TInt(types.TValue(64))
|
||||||
|
for param in [param0, param1, param2]:
|
||||||
|
if param is None:
|
||||||
|
attributes.append(ir.Constant(0, builtins.TInt(types.TValue(64))))
|
||||||
|
else:
|
||||||
|
if param.type != param_type:
|
||||||
|
param = self.append(ir.Coerce(param, param_type))
|
||||||
|
attributes.append(param) # paramN, N=0:2
|
||||||
|
|
||||||
|
return self.append(ir.Alloc(attributes, typ))
|
||||||
|
|
||||||
def visit_builtin_call(self, node):
|
def visit_builtin_call(self, node):
|
||||||
# A builtin by any other name... Ignore node.func, just use the type.
|
# A builtin by any other name... Ignore node.func, just use the type.
|
||||||
typ = node.func.type
|
typ = node.func.type
|
||||||
|
@ -1275,7 +1338,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
separator=" ", suffix="\n")
|
separator=" ", suffix="\n")
|
||||||
return ir.Constant(None, builtins.TNone())
|
return ir.Constant(None, builtins.TNone())
|
||||||
elif types.is_exn_constructor(typ):
|
elif types.is_exn_constructor(typ):
|
||||||
return self.append(ir.Alloc([self.visit(arg) for args in node.args], node.type))
|
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
@ -1485,8 +1548,14 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
format_string += ")"
|
format_string += ")"
|
||||||
elif builtins.is_exception(value.type):
|
elif builtins.is_exception(value.type):
|
||||||
# TODO: print exceptions
|
name = self.append(ir.GetAttr(value, "__name__"))
|
||||||
assert False
|
message = self.append(ir.GetAttr(value, "__message__"))
|
||||||
|
param1 = self.append(ir.GetAttr(value, "__param0__"))
|
||||||
|
param2 = self.append(ir.GetAttr(value, "__param1__"))
|
||||||
|
param3 = self.append(ir.GetAttr(value, "__param2__"))
|
||||||
|
|
||||||
|
format_string += "%s(%s, %lld, %lld, %lld)"
|
||||||
|
args += [name, message, param1, param2, param3]
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
|
@ -440,23 +440,32 @@ class Inferencer(algorithm.Visitor):
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
||||||
if types.is_exn_constructor(typ):
|
if types.is_exn_constructor(typ):
|
||||||
exns = {
|
valid_forms = lambda: [
|
||||||
"IndexError": builtins.TIndexError,
|
valid_form("{exn}() -> {exn}".format(exn=typ.name)),
|
||||||
"ValueError": builtins.TValueError,
|
valid_form("{exn}(message:str) -> {exn}".format(exn=typ.name)),
|
||||||
}
|
valid_form("{exn}(message:str, param1:int(width=64)) -> {exn}".format(exn=typ.name)),
|
||||||
for exn in exns:
|
valid_form("{exn}(message:str, param1:int(width=64), "
|
||||||
if types.is_exn_constructor(typ, exn):
|
"param2:int(width=64)) -> {exn}".format(exn=typ.name)),
|
||||||
valid_forms = lambda: [
|
valid_form("{exn}(message:str, param1:int(width=64), "
|
||||||
valid_form("{exn}() -> {exn}".format(exn=exn))
|
"param2:int(width=64), param3:int(width=64)) "
|
||||||
]
|
"-> {exn}".format(exn=typ.name)),
|
||||||
|
]
|
||||||
|
|
||||||
if len(node.args) == 0 and len(node.keywords) == 0:
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
pass # False
|
pass # Default message, zeroes as parameters
|
||||||
else:
|
elif len(node.args) >= 1 and len(node.args) <= 4 and len(node.keywords) == 0:
|
||||||
diagnose(valid_forms())
|
message, *params = node.args
|
||||||
|
|
||||||
self._unify(node.type, exns[exn](),
|
self._unify(message.type, builtins.TStr(),
|
||||||
node.loc, None)
|
message.loc, None)
|
||||||
|
for param in params:
|
||||||
|
self._unify(param.type, builtins.TInt(types.TValue(64)),
|
||||||
|
param.loc, None)
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
|
||||||
|
self._unify(node.type, getattr(builtins, "T" + typ.name)(),
|
||||||
|
node.loc, None)
|
||||||
elif types.is_builtin(typ, "bool"):
|
elif types.is_builtin(typ, "bool"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("bool() -> bool"),
|
valid_form("bool() -> bool"),
|
||||||
|
@ -829,26 +838,27 @@ class Inferencer(algorithm.Visitor):
|
||||||
def visit_ExceptHandlerT(self, node):
|
def visit_ExceptHandlerT(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
|
||||||
if not types.is_exn_constructor(node.filter.type):
|
if node.filter is not None:
|
||||||
diag = diagnostic.Diagnostic("error",
|
if not types.is_exn_constructor(node.filter.type):
|
||||||
"this expression must refer to an exception constructor",
|
diag = diagnostic.Diagnostic("error",
|
||||||
{"type": types.TypePrinter().name(node.filter.type)},
|
"this expression must refer to an exception constructor",
|
||||||
node.filter.loc)
|
{"type": types.TypePrinter().name(node.filter.type)},
|
||||||
self.engine.process(diag)
|
node.filter.loc)
|
||||||
else:
|
self.engine.process(diag)
|
||||||
def makenotes(printer, typea, typeb, loca, locb):
|
else:
|
||||||
return [
|
def makenotes(printer, typea, typeb, loca, locb):
|
||||||
diagnostic.Diagnostic("note",
|
return [
|
||||||
"expression of type {typea}",
|
diagnostic.Diagnostic("note",
|
||||||
{"typea": printer.name(typea)},
|
"expression of type {typea}",
|
||||||
loca),
|
{"typea": printer.name(typea)},
|
||||||
diagnostic.Diagnostic("note",
|
loca),
|
||||||
"constructor of an exception of type {typeb}",
|
diagnostic.Diagnostic("note",
|
||||||
{"typeb": printer.name(typeb)},
|
"constructor of an exception of type {typeb}",
|
||||||
locb)
|
{"typeb": printer.name(typeb)},
|
||||||
]
|
locb)
|
||||||
self._unify(node.name_type, builtins.TException(node.filter.type.name),
|
]
|
||||||
node.name_loc, node.filter.loc, makenotes)
|
self._unify(node.name_type, builtins.TException(node.filter.type.name),
|
||||||
|
node.name_loc, node.filter.loc, makenotes)
|
||||||
|
|
||||||
def _type_from_arguments(self, node, ret):
|
def _type_from_arguments(self, node, ret):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
@ -943,6 +953,17 @@ class Inferencer(algorithm.Visitor):
|
||||||
self._unify(self.function.return_type, node.value.type,
|
self._unify(self.function.return_type, node.value.type,
|
||||||
self.function.name_loc, node.value.loc, makenotes)
|
self.function.name_loc, node.value.loc, makenotes)
|
||||||
|
|
||||||
|
def visit_Raise(self, node):
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
||||||
|
exc_type = node.exc.type
|
||||||
|
if not types.is_var(exc_type) and not builtins.is_exception(exc_type):
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"cannot raise a value of type {type}, which is not an exception",
|
||||||
|
{"type": types.TypePrinter().name(exc_type)},
|
||||||
|
node.exc.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
def visit_Assert(self, node):
|
def visit_Assert(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
self._unify(node.test.type, builtins.TBool(),
|
self._unify(node.test.type, builtins.TBool(),
|
||||||
|
|
|
@ -43,7 +43,7 @@ class LLVMIRGenerator:
|
||||||
return ll.IntType(builtins.get_int_width(typ))
|
return ll.IntType(builtins.get_int_width(typ))
|
||||||
elif builtins.is_float(typ):
|
elif builtins.is_float(typ):
|
||||||
return ll.DoubleType()
|
return ll.DoubleType()
|
||||||
elif builtins.is_str(typ):
|
elif builtins.is_str(typ) or ir.is_exn_typeinfo(typ):
|
||||||
return ll.IntType(8).as_pointer()
|
return ll.IntType(8).as_pointer()
|
||||||
elif builtins.is_list(typ):
|
elif builtins.is_list(typ):
|
||||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||||
|
@ -51,11 +51,8 @@ class LLVMIRGenerator:
|
||||||
elif builtins.is_range(typ):
|
elif builtins.is_range(typ):
|
||||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||||
return ll.LiteralStructType([lleltty, lleltty, lleltty])
|
return ll.LiteralStructType([lleltty, lleltty, lleltty])
|
||||||
elif builtins.is_exception(typ):
|
|
||||||
# TODO: hack before EH is working
|
|
||||||
return ll.LiteralStructType([])
|
|
||||||
elif ir.is_basic_block(typ):
|
elif ir.is_basic_block(typ):
|
||||||
return ll.LabelType()
|
return ll.IntType(8).as_pointer()
|
||||||
elif ir.is_option(typ):
|
elif ir.is_option(typ):
|
||||||
return ll.LiteralStructType([ll.IntType(1), self.llty_of_type(typ.params["inner"])])
|
return ll.LiteralStructType([ll.IntType(1), self.llty_of_type(typ.params["inner"])])
|
||||||
elif ir.is_environment(typ):
|
elif ir.is_environment(typ):
|
||||||
|
@ -65,8 +62,21 @@ class LLVMIRGenerator:
|
||||||
return llty
|
return llty
|
||||||
else:
|
else:
|
||||||
return llty.as_pointer()
|
return llty.as_pointer()
|
||||||
else:
|
else: # Catch-all for exceptions and custom classes
|
||||||
assert False
|
if builtins.is_exception(typ):
|
||||||
|
name = 'Exception' # they all share layout
|
||||||
|
else:
|
||||||
|
name = typ.name
|
||||||
|
|
||||||
|
llty = self.llcontext.get_identified_type(name)
|
||||||
|
if llty.elements is None:
|
||||||
|
llty.elements = [self.llty_of_type(attrtyp)
|
||||||
|
for attrtyp in typ.attributes.values()]
|
||||||
|
|
||||||
|
if bare or not builtins.is_allocated(typ):
|
||||||
|
return llty
|
||||||
|
else:
|
||||||
|
return llty.as_pointer()
|
||||||
|
|
||||||
def llconst_of_const(self, const):
|
def llconst_of_const(self, const):
|
||||||
llty = self.llty_of_type(const.type)
|
llty = self.llty_of_type(const.type)
|
||||||
|
@ -79,14 +89,27 @@ class LLVMIRGenerator:
|
||||||
elif isinstance(const.value, (int, float)):
|
elif isinstance(const.value, (int, float)):
|
||||||
return ll.Constant(llty, const.value)
|
return ll.Constant(llty, const.value)
|
||||||
elif isinstance(const.value, str):
|
elif isinstance(const.value, str):
|
||||||
as_bytes = (const.value + '\0').encode('utf-8')
|
assert "\0" not in const.value
|
||||||
|
|
||||||
|
as_bytes = (const.value + "\0").encode("utf-8")
|
||||||
|
if ir.is_exn_typeinfo(const.type):
|
||||||
|
# Exception typeinfo; should be merged with identical others
|
||||||
|
name = "__artiq_exn_" + const.value
|
||||||
|
linkage = "linkonce"
|
||||||
|
unnamed_addr = False
|
||||||
|
else:
|
||||||
|
# Just a string
|
||||||
|
name = self.llmodule.get_unique_name("str")
|
||||||
|
linkage = "private"
|
||||||
|
unnamed_addr = True
|
||||||
|
|
||||||
llstrty = ll.ArrayType(ll.IntType(8), len(as_bytes))
|
llstrty = ll.ArrayType(ll.IntType(8), len(as_bytes))
|
||||||
llconst = ll.GlobalVariable(self.llmodule, llstrty,
|
llconst = ll.GlobalVariable(self.llmodule, llstrty, name)
|
||||||
name=self.llmodule.get_unique_name("str"))
|
|
||||||
llconst.global_constant = True
|
llconst.global_constant = True
|
||||||
llconst.unnamed_addr = True
|
|
||||||
llconst.linkage = 'internal'
|
|
||||||
llconst.initializer = ll.Constant(llstrty, bytearray(as_bytes))
|
llconst.initializer = ll.Constant(llstrty, bytearray(as_bytes))
|
||||||
|
llconst.linkage = linkage
|
||||||
|
llconst.unnamed_addr = unnamed_addr
|
||||||
|
|
||||||
return llconst.bitcast(ll.IntType(8).as_pointer())
|
return llconst.bitcast(ll.IntType(8).as_pointer())
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
@ -112,6 +135,10 @@ class LLVMIRGenerator:
|
||||||
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
||||||
elif name == "printf":
|
elif name == "printf":
|
||||||
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()], var_arg=True)
|
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()], var_arg=True)
|
||||||
|
elif name == "__artiq_raise":
|
||||||
|
llty = ll.FunctionType(ll.VoidType(), [self.llty_of_type(builtins.TException())])
|
||||||
|
elif name == "__artiq_personality":
|
||||||
|
llty = ll.FunctionType(ll.IntType(32), [], var_arg=True)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
return ll.Function(self.llmodule, llty, name)
|
return ll.Function(self.llmodule, llty, name)
|
||||||
|
@ -124,8 +151,10 @@ class LLVMIRGenerator:
|
||||||
elif isinstance(value, ir.Function):
|
elif isinstance(value, ir.Function):
|
||||||
llfun = self.llmodule.get_global(value.name)
|
llfun = self.llmodule.get_global(value.name)
|
||||||
if llfun is None:
|
if llfun is None:
|
||||||
return ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
|
llfun = ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
|
||||||
value.name)
|
value.name)
|
||||||
|
llfun.linkage = 'internal'
|
||||||
|
return llfun
|
||||||
else:
|
else:
|
||||||
return llfun
|
return llfun
|
||||||
else:
|
else:
|
||||||
|
@ -186,6 +215,9 @@ class LLVMIRGenerator:
|
||||||
self.fixups.append(fixup)
|
self.fixups.append(fixup)
|
||||||
return llinsn
|
return llinsn
|
||||||
|
|
||||||
|
def llindex(self, index):
|
||||||
|
return ll.Constant(ll.IntType(32), index)
|
||||||
|
|
||||||
def process_Alloc(self, insn):
|
def process_Alloc(self, insn):
|
||||||
if ir.is_environment(insn.type):
|
if ir.is_environment(insn.type):
|
||||||
return self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True),
|
return self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True),
|
||||||
|
@ -210,6 +242,13 @@ 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):
|
||||||
|
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):
|
elif builtins.is_allocated(insn.type):
|
||||||
assert False
|
assert False
|
||||||
else: # immutable
|
else: # immutable
|
||||||
|
@ -219,9 +258,6 @@ class LLVMIRGenerator:
|
||||||
llvalue.name = insn.name
|
llvalue.name = insn.name
|
||||||
return llvalue
|
return llvalue
|
||||||
|
|
||||||
def llindex(self, index):
|
|
||||||
return ll.Constant(ll.IntType(32), index)
|
|
||||||
|
|
||||||
def llptr_to_var(self, llenv, env_ty, var_name):
|
def llptr_to_var(self, llenv, env_ty, var_name):
|
||||||
if var_name in env_ty.params:
|
if var_name in env_ty.params:
|
||||||
var_index = list(env_ty.params.keys()).index(var_name)
|
var_index = list(env_ty.params.keys()).index(var_name)
|
||||||
|
@ -241,6 +277,8 @@ class LLVMIRGenerator:
|
||||||
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)
|
||||||
llvalue = self.map(insn.value())
|
llvalue = self.map(insn.value())
|
||||||
|
if isinstance(llvalue, ll.Block):
|
||||||
|
llvalue = ll.BlockAddress(self.llfunction, llvalue)
|
||||||
if llptr.type.pointee != llvalue.type:
|
if llptr.type.pointee != llvalue.type:
|
||||||
# The environment argument is an i8*, so that all closures can
|
# The environment argument is an i8*, so that all closures can
|
||||||
# unify with each other regardless of environment type or size.
|
# unify with each other regardless of environment type or size.
|
||||||
|
@ -266,11 +304,11 @@ class LLVMIRGenerator:
|
||||||
return self.llbuilder.load(llptr)
|
return self.llbuilder.load(llptr)
|
||||||
|
|
||||||
def process_SetAttr(self, insn):
|
def process_SetAttr(self, insn):
|
||||||
assert builtins.is_allocated(insns.object().type)
|
assert builtins.is_allocated(insn.object().type)
|
||||||
llptr = self.llbuilder.gep(self.map(insn.object()),
|
llptr = self.llbuilder.gep(self.map(insn.object()),
|
||||||
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
return self.llbuilder.store(llptr, self.map(insn.value()))
|
return self.llbuilder.store(self.map(insn.value()), llptr)
|
||||||
|
|
||||||
def process_GetElem(self, insn):
|
def process_GetElem(self, insn):
|
||||||
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
|
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
|
||||||
|
@ -483,7 +521,9 @@ class LLVMIRGenerator:
|
||||||
llargs = map(self.map, insn.operands)
|
llargs = map(self.map, insn.operands)
|
||||||
return self.llbuilder.call(self.llbuiltin("printf"), llargs,
|
return self.llbuilder.call(self.llbuiltin("printf"), llargs,
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
# elif insn.op == "exncast":
|
elif insn.op == "exncast":
|
||||||
|
# This is an identity cast at LLVM IR level.
|
||||||
|
return self.map(insn.operands[0])
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
@ -495,13 +535,24 @@ class LLVMIRGenerator:
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
return llvalue
|
return llvalue
|
||||||
|
|
||||||
def process_Call(self, insn):
|
def prepare_call(self, insn):
|
||||||
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
|
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
|
||||||
llenv = self.llbuilder.extract_value(llclosure, 0)
|
llenv = self.llbuilder.extract_value(llclosure, 0)
|
||||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||||
return self.llbuilder.call(llfun, [llenv] + list(llargs),
|
return llfun, [llenv] + list(llargs)
|
||||||
|
|
||||||
|
def process_Call(self, insn):
|
||||||
|
llfun, llargs = self.prepare_call(insn)
|
||||||
|
return self.llbuilder.call(llfun, llargs,
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
|
|
||||||
|
def process_Invoke(self, insn):
|
||||||
|
llfun, llargs = self.prepare_call(insn)
|
||||||
|
llnormalblock = self.map(insn.normal_target())
|
||||||
|
llunwindblock = self.map(insn.exception_target())
|
||||||
|
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||||
|
name=insn.name)
|
||||||
|
|
||||||
def process_Select(self, insn):
|
def process_Select(self, insn):
|
||||||
return self.llbuilder.select(self.map(insn.condition()),
|
return self.llbuilder.select(self.map(insn.condition()),
|
||||||
self.map(insn.if_true()), self.map(insn.if_false()))
|
self.map(insn.if_true()), self.map(insn.if_false()))
|
||||||
|
@ -513,8 +564,11 @@ class LLVMIRGenerator:
|
||||||
return self.llbuilder.cbranch(self.map(insn.condition()),
|
return self.llbuilder.cbranch(self.map(insn.condition()),
|
||||||
self.map(insn.if_true()), self.map(insn.if_false()))
|
self.map(insn.if_true()), self.map(insn.if_false()))
|
||||||
|
|
||||||
# def process_IndirectBranch(self, insn):
|
def process_IndirectBranch(self, insn):
|
||||||
# pass
|
llinsn = self.llbuilder.branch_indirect(self.map(insn.target()))
|
||||||
|
for dest in insn.destinations():
|
||||||
|
llinsn.add_destination(self.map(dest))
|
||||||
|
return llinsn
|
||||||
|
|
||||||
def process_Return(self, insn):
|
def process_Return(self, insn):
|
||||||
if builtins.is_none(insn.value().type):
|
if builtins.is_none(insn.value().type):
|
||||||
|
@ -526,15 +580,33 @@ class LLVMIRGenerator:
|
||||||
return self.llbuilder.unreachable()
|
return self.llbuilder.unreachable()
|
||||||
|
|
||||||
def process_Raise(self, insn):
|
def process_Raise(self, insn):
|
||||||
# TODO: hack before EH is working
|
arg = self.map(insn.operands[0])
|
||||||
llinsn = self.llbuilder.call(self.llbuiltin("llvm.trap"), [],
|
llinsn = self.llbuilder.call(self.llbuiltin("__artiq_raise"), [arg],
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
|
llinsn.attributes.add('noreturn')
|
||||||
self.llbuilder.unreachable()
|
self.llbuilder.unreachable()
|
||||||
return llinsn
|
return llinsn
|
||||||
|
|
||||||
# def process_Invoke(self, insn):
|
def process_LandingPad(self, insn):
|
||||||
# pass
|
lllandingpad = self.llbuilder.landingpad(self.llty_of_type(insn.type),
|
||||||
|
self.llbuiltin("__artiq_personality"))
|
||||||
|
llexnnameptr = self.llbuilder.gep(lllandingpad, [self.llindex(0), self.llindex(0)])
|
||||||
|
llexnname = self.llbuilder.load(llexnnameptr)
|
||||||
|
|
||||||
# def process_LandingPad(self, insn):
|
for target, typ in insn.clauses():
|
||||||
# pass
|
if typ is None:
|
||||||
|
llclauseexnname = ll.Constant(
|
||||||
|
self.llty_of_type(ir.TExceptionTypeInfo()), None)
|
||||||
|
else:
|
||||||
|
llclauseexnname = self.llconst_of_const(
|
||||||
|
ir.Constant(typ.name, ir.TExceptionTypeInfo()))
|
||||||
|
lllandingpad.add_clause(ll.CatchClause(llclauseexnname))
|
||||||
|
|
||||||
|
llmatchingclause = self.llbuilder.icmp_unsigned('==', llexnname, llclauseexnname)
|
||||||
|
with self.llbuilder.if_then(llmatchingclause):
|
||||||
|
self.llbuilder.branch(self.map(target))
|
||||||
|
|
||||||
|
self.llbuilder.resume(lllandingpad)
|
||||||
|
|
||||||
|
return lllandingpad
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,9 @@ class TMono(Type):
|
||||||
A monomorphic type, possibly parametric.
|
A monomorphic type, possibly parametric.
|
||||||
|
|
||||||
:class:`TMono` is supposed to be subclassed by builtin types,
|
:class:`TMono` is supposed to be subclassed by builtin types,
|
||||||
unlike all other :class:`Type` descendants.
|
unlike all other :class:`Type` descendants. Similarly,
|
||||||
|
instances of :class:`TMono` should never be allocated directly,
|
||||||
|
as that will break the type-sniffing code in :mod:`builtins`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
attributes = OrderedDict()
|
attributes = OrderedDict()
|
||||||
|
|
|
@ -239,13 +239,17 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
# Only three ways for a pointer to escape:
|
# Only three ways for a pointer to escape:
|
||||||
# * Assigning or op-assigning it (we ensure an outlives relationship)
|
# * Assigning or op-assigning it (we ensure an outlives relationship)
|
||||||
# * Returning it (we only allow returning values that live forever)
|
# * Returning it (we only allow returning values that live forever)
|
||||||
# * Raising it (we forbid raising mutable data)
|
# * Raising it (we forbid allocating exceptions that refer to mutable data)¹
|
||||||
#
|
#
|
||||||
# Literals doesn't count: a constructed object is always
|
# Literals doesn't count: a constructed object is always
|
||||||
# outlived by all its constituents.
|
# outlived by all its constituents.
|
||||||
# Closures don't count: see above.
|
# Closures don't count: see above.
|
||||||
# Calling functions doesn't count: arguments never outlive
|
# Calling functions doesn't count: arguments never outlive
|
||||||
# the function body.
|
# the function body.
|
||||||
|
#
|
||||||
|
# ¹Strings are currently never allocated with a limited lifetime,
|
||||||
|
# and exceptions can only refer to strings, so we don't actually check
|
||||||
|
# this property. But we will need to, if string operations are ever added.
|
||||||
|
|
||||||
def visit_assignment(self, target, value, is_aug_assign=False):
|
def visit_assignment(self, target, value, is_aug_assign=False):
|
||||||
target_region = self._region_of(target)
|
target_region = self._region_of(target)
|
||||||
|
@ -298,14 +302,3 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
"cannot return a mutable value that does not live forever", {},
|
"cannot return a mutable value that does not live forever", {},
|
||||||
node.value.loc, notes=self._diagnostics_for(region, node.value.loc) + [note])
|
node.value.loc, notes=self._diagnostics_for(region, node.value.loc) + [note])
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
|
||||||
if builtins.is_allocated(node.exc.type):
|
|
||||||
note = diagnostic.Diagnostic("note",
|
|
||||||
"this expression has type {type}",
|
|
||||||
{"type": types.TypePrinter().name(node.exc.type)},
|
|
||||||
node.exc.loc)
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
|
||||||
"cannot raise a mutable value", {},
|
|
||||||
node.exc.loc, notes=[note])
|
|
||||||
self.engine.process(diag)
|
|
||||||
|
|
|
@ -56,3 +56,18 @@ lambda x, y=1: x
|
||||||
|
|
||||||
k = "x"
|
k = "x"
|
||||||
# CHECK-L: k:str
|
# CHECK-L: k:str
|
||||||
|
|
||||||
|
IndexError()
|
||||||
|
# CHECK-L: IndexError:<constructor IndexError>():IndexError
|
||||||
|
|
||||||
|
IndexError("x")
|
||||||
|
# CHECK-L: IndexError:<constructor IndexError>("x":str):IndexError
|
||||||
|
|
||||||
|
IndexError("x", 1)
|
||||||
|
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64)):IndexError
|
||||||
|
|
||||||
|
IndexError("x", 1, 1)
|
||||||
|
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64)):IndexError
|
||||||
|
|
||||||
|
IndexError("x", 1, 1, 1)
|
||||||
|
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64), 1:int(width=64)):IndexError
|
||||||
|
|
Loading…
Reference in New Issue