lang: unification: Add equiv matching and resultant equalities
The set of initial invariants that we see might include: ?8 = func(inputs []int, function ?4) ?3 ?8 = func(inputs []int, function ?10) ?9 ?8 = func arg0 []int, arg1 ?6) ?7 From this we can infer that since they are all equal, that we also know that ?4, ?10 and ?6 must also be equal. The same is true of ?3, ?9 and ?10. Those new equalities are sometimes necessary in order to complete the full unification. The second interesting aspect is when we have dissimilar equalities: ?2 = func(x ?1) str ?4 = func(a int) ?5 ?10 = func(a int) ?11 In this example we also have an additional equality: ?6 = ?2 From this and the above we can determine that ?2, ?4, ?6 and ?10 are all equal. We only know about ?4, ?6, and ?10 from the direct relationship, and we add in ?2 from the indirect (graph) relationship. These relationships let us determine new information that ?5 and ?11 are both str and that ?1 is an int. Two important reminders: 1) Arg names don't have to match. It would impossible to build such a system where this was both possible, but also let us name our functions sanely. 2) None of this guarantees we won't find an inconsistency in our solution. If this is found, it simply means that someone wrote code which does not type check.
This commit is contained in:
@@ -52,3 +52,73 @@ func UniqueExprList(exprList []interfaces.Expr) []interfaces.Expr {
|
||||
exprMap := ExprListToExprMap(exprList)
|
||||
return ExprMapToExprList(exprMap)
|
||||
}
|
||||
|
||||
// ExprContains is an "in array" function to test for an expr in a slice of
|
||||
// expressions.
|
||||
func ExprContains(needle interfaces.Expr, haystack []interfaces.Expr) bool {
|
||||
for _, v := range haystack {
|
||||
if needle == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// pairs is a simple list of pairs of expressions which can be used as a simple
|
||||
// undirected graph structure, or as a simple list of equalities.
|
||||
type pairs []*interfaces.EqualityInvariant
|
||||
|
||||
// Vertices returns the list of vertices that the input expr is directly
|
||||
// connected to.
|
||||
func (obj pairs) Vertices(expr interfaces.Expr) []interfaces.Expr {
|
||||
m := make(map[interfaces.Expr]struct{})
|
||||
for _, x := range obj {
|
||||
if x.Expr1 == x.Expr2 { // skip circular
|
||||
continue
|
||||
}
|
||||
if x.Expr1 == expr {
|
||||
m[x.Expr2] = struct{}{}
|
||||
}
|
||||
if x.Expr2 == expr {
|
||||
m[x.Expr1] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
out := []interfaces.Expr{}
|
||||
// FIXME: can we do this loop in a deterministic, sorted way?
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// DFS returns a depth first search for the graph, starting at the input vertex.
|
||||
func (obj pairs) DFS(start interfaces.Expr) []interfaces.Expr {
|
||||
var d []interfaces.Expr // discovered
|
||||
var s []interfaces.Expr // stack
|
||||
found := false
|
||||
for _, x := range obj { // does the start exist?
|
||||
if x.Expr1 == start || x.Expr2 == start {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil // TODO: error
|
||||
}
|
||||
v := start
|
||||
s = append(s, v)
|
||||
for len(s) > 0 {
|
||||
v, s = s[len(s)-1], s[:len(s)-1] // s.pop()
|
||||
|
||||
if !ExprContains(v, d) { // if not discovered
|
||||
d = append(d, v) // label as discovered
|
||||
|
||||
for _, w := range obj.Vertices(v) {
|
||||
s = append(s, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user