lang: unification: Improve our simple solver

This removed a bug in the InvariantCall stuff, and also hopefully made
it more robust to actually solving when it had a solution.
This commit is contained in:
James Shubin
2021-05-11 02:47:24 -04:00
parent 2bed668d31
commit 2865ba7632

View File

@@ -135,17 +135,20 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa
// optimization: if we have zero generator invariants, we can
// discard the value invariants!
if len(generators) == 0 {
// NOTE: if exclusives do *not* contain nested generators, then
// we don't need to check for exclusives here, and the logic is
// much faster and simpler and can possibly solve more cases...
if len(generators) == 0 && len(exclusives) == 0 {
used := []int{}
for i, x := range equalities {
if _, ok := x.(*interfaces.ValueInvariant); !ok {
continue
}
if _, ok := x.(*interfaces.CallFuncArgsValueInvariant); !ok {
_, ok1 := x.(*interfaces.ValueInvariant)
_, ok2 := x.(*interfaces.CallFuncArgsValueInvariant)
if !ok1 && !ok2 {
continue
}
used = append(used, i) // mark equality as used up
}
logf("%s: got %d equalities left after %d used up", Name, len(equalities)-len(used), len(used))
// delete used equalities, in reverse order to preserve indexing!
for i := len(used) - 1; i >= 0; i-- {
ix := used[i] // delete index that was marked as used!
@@ -180,13 +183,16 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa
funcPartials := make(map[interfaces.Expr]map[interfaces.Expr]*types.Type)
callPartials := make(map[interfaces.Expr]map[interfaces.Expr]*types.Type)
isSolved := func(solved map[interfaces.Expr]*types.Type) bool {
isSolvedFn := func(solved map[interfaces.Expr]*types.Type) (map[interfaces.Expr]struct{}, bool) {
unsolved := make(map[interfaces.Expr]struct{})
result := true
for _, x := range expected {
if typ, exists := solved[x]; !exists || typ == nil {
return false
result = false
unsolved[x] = struct{}{}
}
}
return true
return unsolved, result
}
logf("%s: starting loop with %d equalities", Name, len(equalities))
@@ -676,7 +682,8 @@ Loop:
// These two optimizations turn out to use the exact
// same algorithm and code, so they're combined here...
if isSolved(solved) {
_, isSolved := isSolvedFn(solved)
if isSolved {
logf("%s: solved early with %d exclusives left!", Name, len(exclusives))
} else {
logf("%s: unsolved with %d exclusives left!", Name, len(exclusives))
@@ -715,6 +722,37 @@ Loop:
continue Loop
}
// If we don't have any exclusives left, then we don't
// need the Value invariants... This logic is the same
// as in process() but it's duplicated here because we
// want it to happen at this stage as well. We can try
// and clean up the duplication and improve the logic.
// NOTE: We should probably check that there aren't any
// generators left in the equalities, but since we have
// already tried to use them up, it is probably safe to
// unblock the solver if it's only ValueInvatiant left.
if len(exclusives) == 0 || isSolved { // either is okay
used := []int{}
for i, x := range equalities {
_, ok1 := x.(*interfaces.ValueInvariant)
_, ok2 := x.(*interfaces.CallFuncArgsValueInvariant)
if !ok1 && !ok2 {
continue
}
used = append(used, i) // mark equality as used up
}
logf("%s: got %d equalities left after %d used up", Name, len(equalities)-len(used), len(used))
// delete used equalities, in reverse order to preserve indexing!
for i := len(used) - 1; i >= 0; i-- {
ix := used[i] // delete index that was marked as used!
equalities = append(equalities[:ix], equalities[ix+1:]...)
}
if len(used) > 0 {
continue Loop
}
}
// what have we learned for sure so far?
partialSolutions := []interfaces.Invariant{}
logf("%s: %d solved, %d unsolved, and %d exclusives left", Name, len(solved), len(equalities), len(exclusives))
@@ -822,6 +860,14 @@ Loop:
}
// TODO: print ambiguity
logf("%s: ================ ambiguity ================", Name)
for _, x := range equalities {
logf("%s: unsolved equality: %+v", Name, x)
}
unsolved, _ := isSolvedFn(solved)
for x := range unsolved {
logf("%s: unsolved expected: %+v", Name, x)
}
return nil, ErrAmbiguous
}
// delete used equalities, in reverse order to preserve indexing!