Add better type inference and encapsulate it in its own file (#110)
* Add more rigorous type inference and encapsulate the type inferece code in its own file (ti.py). Add constraints accumulation during type inference, to represent constraints that cannot be expressed using bijective derivation functions between typevars. Add testing for new type inference code. * Additional annotations to appease mypy
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
e63c581859
commit
e35398842d
@@ -100,8 +100,8 @@ class Var(Expr):
|
||||
# TypeVar representing the type of this variable.
|
||||
self.typevar = None # type: TypeVar
|
||||
# The original 'typeof(x)' type variable that was created for this Var.
|
||||
# This one doesn't change. `self.typevar` above may be joined with
|
||||
# other typevars.
|
||||
# This one doesn't change. `self.typevar` above may be changed to
|
||||
# another typevar by type inference.
|
||||
self.original_typevar = None # type: TypeVar
|
||||
|
||||
def __str__(self):
|
||||
@@ -180,16 +180,9 @@ class Var(Expr):
|
||||
self.typevar = tv
|
||||
return self.typevar
|
||||
|
||||
def link_typevar(self, base, derived_func):
|
||||
# type: (TypeVar, str) -> None
|
||||
"""
|
||||
Link the type variable on this Var to the type variable `base` using
|
||||
`derived_func`.
|
||||
"""
|
||||
self.original_typevar = None
|
||||
self.typevar.change_to_derived(base, derived_func)
|
||||
# Possibly eliminate redundant SAMEAS links.
|
||||
self.typevar = self.typevar.strip_sameas()
|
||||
def set_typevar(self, tv):
|
||||
# type: (TypeVar) -> None
|
||||
self.typevar = tv
|
||||
|
||||
def has_free_typevar(self):
|
||||
# type: () -> bool
|
||||
@@ -213,136 +206,6 @@ class Var(Expr):
|
||||
"""
|
||||
return self.typevar.rust_expr()
|
||||
|
||||
def constrain_typevar(self, sym_typevar, sym_ctrl, ctrl_var):
|
||||
# type: (TypeVar, TypeVar, Var) -> None
|
||||
"""
|
||||
Constrain the set of allowed types for this variable.
|
||||
|
||||
Merge type variables for the involved variables to minimize the set for
|
||||
free type variables.
|
||||
|
||||
Suppose we're looking at an instruction defined like this:
|
||||
|
||||
c = Operand('c', TxN.as_bool())
|
||||
x = Operand('x', TxN)
|
||||
y = Operand('y', TxN)
|
||||
a = Operand('a', TxN)
|
||||
vselect = Instruction('vselect', ins=(c, x, y), outs=a)
|
||||
|
||||
And suppose the instruction is used in a pattern like this:
|
||||
|
||||
v0 << vselect(v1, v2, v3)
|
||||
|
||||
We want to reconcile the types of the variables v0-v3 with the
|
||||
constraints from the definition of vselect. This means that v0, v2, and
|
||||
v3 must all have the same type, and v1 must have the type
|
||||
`typeof(v2).as_bool()`.
|
||||
|
||||
The types are reconciled by calling this function once for each
|
||||
input/output operand on the instruction in the pattern with these
|
||||
arguments.
|
||||
|
||||
:param sym_typevar: Symbolic type variable constraining this variable
|
||||
in the definition of the instruction.
|
||||
:param sym_ctrl: Controlling type variable of `sym_typevar` in the
|
||||
definition of the instruction.
|
||||
:param ctrl_var: Variable determining the type of `sym_ctrl`.
|
||||
|
||||
When processing `v1` as used in the pattern above, we would get:
|
||||
|
||||
- self: v1
|
||||
- sym_typevar: TxN.as_bool()
|
||||
- sym_ctrl: TxN
|
||||
- ctrl_var: v2
|
||||
|
||||
Here, 'v2' represents the controlling variable because of how the
|
||||
`Ternary` instruction format is defined with `typevar_operand=1`.
|
||||
"""
|
||||
# First check if sym_typevar is tied to the controlling type variable
|
||||
# in the instruction definition. We also allow free type variables on
|
||||
# instruction inputs that can't be tied to anything else.
|
||||
#
|
||||
# This also covers non-polymorphic instructions and other cases where
|
||||
# we don't have a Var representing the controlling type variable.
|
||||
sym_free_var = sym_typevar.free_typevar()
|
||||
if not sym_free_var or sym_free_var is not sym_ctrl or not ctrl_var:
|
||||
# Just constrain our type to be compatible with the required
|
||||
# typeset.
|
||||
self.get_typevar().constrain_types(sym_typevar)
|
||||
return
|
||||
|
||||
# Now sym_typevar is known to be tied to (or identical to) the
|
||||
# controlling type variable.
|
||||
|
||||
if not self.typevar:
|
||||
# If this variable is not yet constrained, just infer its type and
|
||||
# link it to the controlling type variable.
|
||||
if not sym_typevar.is_derived:
|
||||
assert sym_typevar is sym_ctrl
|
||||
# Identity mapping.
|
||||
# Note that `self == ctrl_var` is both possible and common.
|
||||
self.typevar = ctrl_var.get_typevar()
|
||||
else:
|
||||
assert self is not ctrl_var, (
|
||||
'Impossible type constraints for {}: {}'
|
||||
.format(self, sym_typevar))
|
||||
# Create a derived type variable identical to sym_typevar, but
|
||||
# with a different base.
|
||||
self.typevar = TypeVar.derived(
|
||||
ctrl_var.get_typevar(),
|
||||
sym_typevar.derived_func)
|
||||
# Match the type set constraints of the instruction.
|
||||
self.typevar.constrain_types(sym_typevar)
|
||||
return
|
||||
|
||||
# We already have a self.typevar describing our constraints. We need to
|
||||
# reconcile with the additional constraints.
|
||||
|
||||
# It's likely that ctrl_var and self already share a type
|
||||
# variable. (Often because `ctrl_var == self`).
|
||||
if ctrl_var.typevar == self.typevar:
|
||||
return
|
||||
|
||||
if not sym_typevar.is_derived:
|
||||
assert sym_typevar is sym_ctrl
|
||||
# sym_typevar is a direct use of sym_ctrl, so we need to reconcile
|
||||
# self with ctrl_var.
|
||||
assert not sym_typevar.is_derived
|
||||
self.typevar.constrain_types(sym_typevar)
|
||||
|
||||
# It's possible that ctrl_var has not yet been assigned a type
|
||||
# variable.
|
||||
if not ctrl_var.typevar:
|
||||
ctrl_var.typevar = self.typevar
|
||||
return
|
||||
|
||||
# We can also bind variables with a free type variable to another
|
||||
# variable. Prefer to do this to temps because they aren't allowed
|
||||
# to be free,
|
||||
if self.is_temp() and self.has_free_typevar():
|
||||
self.link_typevar(ctrl_var.typevar, TypeVar.SAMEAS)
|
||||
return
|
||||
if ctrl_var.is_temp() and ctrl_var.has_free_typevar():
|
||||
ctrl_var.link_typevar(self.typevar, TypeVar.SAMEAS)
|
||||
return
|
||||
if self.has_free_typevar():
|
||||
self.link_typevar(ctrl_var.typevar, TypeVar.SAMEAS)
|
||||
return
|
||||
if ctrl_var.has_free_typevar():
|
||||
ctrl_var.link_typevar(self.typevar, TypeVar.SAMEAS)
|
||||
return
|
||||
|
||||
# TODO: Other cases are harder to handle.
|
||||
#
|
||||
# - If either variable is an independent free type variable, it
|
||||
# should be changed to be linked to the other.
|
||||
# - If both variable are free, we should pick one to link to the
|
||||
# other. In particular, if one is a temp, it should be linked.
|
||||
else:
|
||||
# sym_typevar is derived from sym_ctrl.
|
||||
# TODO: Other cases are harder to handle.
|
||||
pass
|
||||
|
||||
|
||||
class Apply(Expr):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user