lang: unification, interfaces: Add a skip invariant

This is a cleaner way of telling the type unifier that we don't want
this particular expression in the solution set at the end.
This commit is contained in:
James Shubin
2023-12-17 21:41:20 -05:00
parent c2bf4ef7d4
commit 7cc231e8b9
3 changed files with 63 additions and 0 deletions

View File

@@ -28,6 +28,8 @@ import (
// Invariant represents a constraint that is described by the Expr's and Stmt's, // Invariant represents a constraint that is described by the Expr's and Stmt's,
// and which is passed into the unification solver to describe what is known by // and which is passed into the unification solver to describe what is known by
// the AST. // the AST.
// XXX: add the extended methods into sub-interfaces since not each invariant
// uses them...
type Invariant interface { type Invariant interface {
// TODO: should we add any other methods to this type? // TODO: should we add any other methods to this type?
fmt.Stringer fmt.Stringer
@@ -1049,3 +1051,34 @@ func (obj *CallFuncArgsValueInvariant) Possible(partials []Invariant) error {
// XXX: not implemented // XXX: not implemented
return nil // safer to return nil than error return nil // safer to return nil than error
} }
// SkipInvariant expresses that a particular expression does must not be part of
// the final solution, and should be skipped. It can be part of the solving
// process though.
type SkipInvariant struct {
Expr Expr
}
// String returns a representation of this invariant.
func (obj *SkipInvariant) String() string {
return fmt.Sprintf("skip(%p)", obj.Expr)
}
// ExprList returns the list of valid expressions in this invariant. It is not
// used for this invariant.
func (obj *SkipInvariant) ExprList() []Expr {
// XXX: not used
return []Expr{obj.Expr}
}
// Matches is not used for this invariant.
func (obj *SkipInvariant) Matches(solved map[Expr]*types.Type) (bool, error) {
// XXX: not used
panic("not used")
}
// Possible is not used for this invariant.
func (obj *SkipInvariant) Possible(partials []Invariant) error {
// XXX: not used
panic("not used")
}

View File

@@ -170,6 +170,9 @@ func DebugSolverState(solved map[interfaces.Expr]*types.Type, equalities []inter
case *interfaces.AnyInvariant: case *interfaces.AnyInvariant:
// skip, not used in the examples I care about // skip, not used in the examples I care about
case *interfaces.SkipInvariant:
// we don't care about this one
default: default:
s += fmt.Sprintf("%v\n", equality) s += fmt.Sprintf("%v\n", equality)
} }
@@ -249,6 +252,10 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa
case *interfaces.CallFuncArgsValueInvariant: case *interfaces.CallFuncArgsValueInvariant:
equalities = append(equalities, invariant) equalities = append(equalities, invariant)
case *interfaces.SkipInvariant:
// drop it for now
//equalities = append(equalities, invariant)
default: default:
return nil, nil, fmt.Errorf("unknown invariant type: %T", x) return nil, nil, fmt.Errorf("unknown invariant type: %T", x)
} }
@@ -296,6 +303,8 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa
return nil, err return nil, err
} }
//skipExprs := make(map[interfaces.Expr]struct{})
// XXX: if these partials all shared the same variable definition, would // XXX: if these partials all shared the same variable definition, would
// it all work??? Maybe we don't even need the first map prefix... // it all work??? Maybe we don't even need the first map prefix...
listPartials := make(map[interfaces.Expr]map[interfaces.Expr]*types.Type) listPartials := make(map[interfaces.Expr]map[interfaces.Expr]*types.Type)
@@ -1074,6 +1083,11 @@ Loop:
// a generator invariant wants to read them... // a generator invariant wants to read them...
continue continue
case *interfaces.SkipInvariant:
//skipExprs[eq.Expr] = struct{}{} // save
used = append(used, eqi) // mark equality as used up
continue
default: default:
return nil, fmt.Errorf("unknown invariant type: %T", eqx) return nil, fmt.Errorf("unknown invariant type: %T", eqx)
} }
@@ -1334,6 +1348,12 @@ Loop:
solutions := []*interfaces.EqualsInvariant{} solutions := []*interfaces.EqualsInvariant{}
// FIXME: can we do this loop in a deterministic, sorted way? // FIXME: can we do this loop in a deterministic, sorted way?
for expr, typ := range solved { for expr, typ := range solved {
// Don't do this here, or the current Unifier struct machinery
// will see it as a bug. Do it there until we change the API.
//if _, exists := skipExprs[expr]; exists {
// continue
//}
invar := &interfaces.EqualsInvariant{ invar := &interfaces.EqualsInvariant{
Expr: expr, Expr: expr,
Type: typ, Type: typ,

View File

@@ -72,7 +72,13 @@ func (obj *Unifier) Unify() error {
// build a list of what we think we need to solve for to succeed // build a list of what we think we need to solve for to succeed
exprs := []interfaces.Expr{} exprs := []interfaces.Expr{}
skips := make(map[interfaces.Expr]struct{})
for _, x := range invariants { for _, x := range invariants {
if si, ok := x.(*interfaces.SkipInvariant); ok {
skips[si.Expr] = struct{}{}
continue
}
exprs = append(exprs, x.ExprList()...) exprs = append(exprs, x.ExprList()...)
} }
exprMap := ExprListToExprMap(exprs) // makes searching faster exprMap := ExprListToExprMap(exprs) // makes searching faster
@@ -137,6 +143,10 @@ func (obj *Unifier) Unify() error {
// programming error ? // programming error ?
return fmt.Errorf("unexpected invalid solution at: %p", x) return fmt.Errorf("unexpected invalid solution at: %p", x)
} }
if _, exists := skips[x.Expr]; exists {
continue
}
if obj.Debug { if obj.Debug {
obj.Logf("solution: %p => %+v\t(%+v)", x.Expr, x.Type, x.Expr.String()) obj.Logf("solution: %p => %+v\t(%+v)", x.Expr, x.Type, x.Expr.String())
} }