Separate inference and asttyped transformation.

This allows to run inference several times on the same tree,
as would be necessary when coercion nodes are added.
This commit is contained in:
whitequark 2015-06-11 06:34:22 +03:00
parent e18ea0daae
commit df686136f1

View File

@ -166,6 +166,22 @@ class Inferencer(algorithm.Transformer):
loca, highlights, notes)
self.engine.process(diag)
# makenotes for the case where types of multiple elements are unified
# with the type of parent expression
def _makenotes_elts(self, elts, kind):
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"{kind} of type {typea}",
{"kind": kind, "typea": printer.name(elts[0].type)},
elts[0].loc),
diagnostic.Diagnostic("note",
"{kind} of type {typeb}",
{"kind": kind, "typeb": printer.name(typeb)},
locb)
]
return makenotes
def _find_name(self, name, loc):
for typing_env in reversed(self.env_stack):
if name in typing_env:
@ -212,26 +228,6 @@ class Inferencer(algorithm.Transformer):
return node
def visit_Return(self, node):
node = self.generic_visit(node)
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"function with return type {typea}",
{"typea": printer.name(typea)},
self.function.name_loc),
diagnostic.Diagnostic("note",
"a statement returning {typeb}",
{"typeb": printer.name(typeb)},
node.loc)
]
if node.value is None:
self._unify(self.function.return_type, types.TNone(),
self.function.name_loc, node.loc, makenotes)
else:
self._unify(self.function.return_type, node.value.type,
self.function.name_loc, node.value.loc, makenotes)
def visit_Num(self, node):
if isinstance(node.n, int):
typ = types.TInt()
@ -265,63 +261,55 @@ class Inferencer(algorithm.Transformer):
node = self.generic_visit(node)
node = asttyped.ListT(type=types.TList(),
elts=node.elts, ctx=node.ctx, loc=node.loc)
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"a list element of type {typea}",
{"typea": printer.name(node.elts[0].type)},
node.elts[0].loc),
diagnostic.Diagnostic("note",
"a list element of type {typeb}",
{"typeb": printer.name(typeb)},
locb)
]
for elt in node.elts:
self._unify(node.type["elt"], elt.type,
node.loc, elt.loc, makenotes)
return node
return self.visit(node)
def visit_Subscript(self, node):
node = self.generic_visit(node)
node = asttyped.SubscriptT(type=types.TVar(),
value=node.value, slice=node.slice, ctx=node.ctx,
loc=node.loc)
# TODO: support more than just lists
self._unify(types.TList(node.type), node.value.type,
node.loc, node.value.loc)
return node
return self.visit(node)
def visit_IfExp(self, node):
node = self.generic_visit(node)
self._unify(node.body.type, node.orelse.type,
node.body.loc, node.orelse.loc)
return asttyped.IfExpT(type=node.body.type,
node = asttyped.IfExpT(type=types.TVar(),
test=node.test, body=node.body, orelse=node.orelse,
if_loc=node.if_loc, else_loc=node.else_loc, loc=node.loc)
return self.visit(node)
def visit_BoolOp(self, node):
node = self.generic_visit(node)
node = asttyped.BoolOpT(type=types.TVar(),
op=node.op, values=node.values,
op_locs=node.op_locs, loc=node.loc)
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"an operand of type {typea}",
{"typea": printer.name(node.values[0].type)},
node.values[0].loc),
diagnostic.Diagnostic("note",
"an operand of type {typeb}",
{"typeb": printer.name(typeb)},
locb)
]
for value in node.values:
self._unify(node.type, value.type,
node.loc, value.loc, makenotes)
return node
return self.visit(node)
# Visitors that just unify types
#
def visit_ListT(self, node):
for elt in node.elts:
self._unify(node.type["elt"], elt.type,
node.loc, elt.loc, self._makenotes_elts(node.elts, "a list element"))
return node
def visit_SubscriptT(self, node):
# TODO: support more than just lists
self._unify(types.TList(node.type), node.value.type,
node.loc, node.value.loc)
return node
def visit_IfExpT(self, node):
self._unify(node.body.type, node.orelse.type,
node.body.loc, node.orelse.loc)
node.type = node.body.type
return node
def visit_BoolOpT(self, node):
for value in node.values:
self._unify(node.type, value.type,
node.loc, value.loc, self._makenotes_elts(node.values, "an operand"))
return node
def visit_Assign(self, node):
node = self.generic_visit(node)
if len(node.targets) > 1:
@ -345,6 +333,26 @@ class Inferencer(algorithm.Transformer):
node.target.loc, node.iter.loc)
return node
def visit_Return(self, node):
node = self.generic_visit(node)
def makenotes(printer, typea, typeb, loca, locb):
return [
diagnostic.Diagnostic("note",
"function with return type {typea}",
{"typea": printer.name(typea)},
self.function.name_loc),
diagnostic.Diagnostic("note",
"a statement returning {typeb}",
{"typeb": printer.name(typeb)},
node.loc)
]
if node.value is None:
self._unify(self.function.return_type, types.TNone(),
self.function.name_loc, node.loc, makenotes)
else:
self._unify(self.function.return_type, node.value.type,
self.function.name_loc, node.value.loc, makenotes)
# Unsupported visitors
#
def visit_unsupported(self, node):