diff --git a/lang/interfaces/unification.go b/lang/interfaces/unification.go index e7c0a23d..06d149c7 100644 --- a/lang/interfaces/unification.go +++ b/lang/interfaces/unification.go @@ -28,6 +28,8 @@ import ( // 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 // the AST. +// XXX: add the extended methods into sub-interfaces since not each invariant +// uses them... type Invariant interface { // TODO: should we add any other methods to this type? fmt.Stringer @@ -1049,3 +1051,34 @@ func (obj *CallFuncArgsValueInvariant) Possible(partials []Invariant) error { // XXX: not implemented 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") +} diff --git a/lang/unification/simplesolver.go b/lang/unification/simplesolver.go index 6753bc4d..34926b73 100644 --- a/lang/unification/simplesolver.go +++ b/lang/unification/simplesolver.go @@ -170,6 +170,9 @@ func DebugSolverState(solved map[interfaces.Expr]*types.Type, equalities []inter case *interfaces.AnyInvariant: // skip, not used in the examples I care about + case *interfaces.SkipInvariant: + // we don't care about this one + default: s += fmt.Sprintf("%v\n", equality) } @@ -249,6 +252,10 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa case *interfaces.CallFuncArgsValueInvariant: equalities = append(equalities, invariant) + case *interfaces.SkipInvariant: + // drop it for now + //equalities = append(equalities, invariant) + default: return nil, nil, fmt.Errorf("unknown invariant type: %T", x) } @@ -296,6 +303,8 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa return nil, err } + //skipExprs := make(map[interfaces.Expr]struct{}) + // XXX: if these partials all shared the same variable definition, would // it all work??? Maybe we don't even need the first map prefix... listPartials := make(map[interfaces.Expr]map[interfaces.Expr]*types.Type) @@ -1074,6 +1083,11 @@ Loop: // a generator invariant wants to read them... continue + case *interfaces.SkipInvariant: + //skipExprs[eq.Expr] = struct{}{} // save + used = append(used, eqi) // mark equality as used up + continue + default: return nil, fmt.Errorf("unknown invariant type: %T", eqx) } @@ -1334,6 +1348,12 @@ Loop: solutions := []*interfaces.EqualsInvariant{} // FIXME: can we do this loop in a deterministic, sorted way? 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{ Expr: expr, Type: typ, diff --git a/lang/unification/unification.go b/lang/unification/unification.go index befd10c7..cc79310e 100644 --- a/lang/unification/unification.go +++ b/lang/unification/unification.go @@ -72,7 +72,13 @@ func (obj *Unifier) Unify() error { // build a list of what we think we need to solve for to succeed exprs := []interfaces.Expr{} + skips := make(map[interfaces.Expr]struct{}) for _, x := range invariants { + if si, ok := x.(*interfaces.SkipInvariant); ok { + skips[si.Expr] = struct{}{} + continue + } + exprs = append(exprs, x.ExprList()...) } exprMap := ExprListToExprMap(exprs) // makes searching faster @@ -137,6 +143,10 @@ func (obj *Unifier) Unify() error { // programming error ? return fmt.Errorf("unexpected invalid solution at: %p", x) } + if _, exists := skips[x.Expr]; exists { + continue + } + if obj.Debug { obj.Logf("solution: %p => %+v\t(%+v)", x.Expr, x.Type, x.Expr.String()) }