lang: Switch over to the new PolyFunc interface

This isn't perfect yet, but we're trying to do this incrementally, and
merge whatever we can as early as possible.

During this work, I realized that the Simplify method of the exclusive
could probably be improved, and possibly receive a better signature.
This work will have to happen later.
This commit is contained in:
James Shubin
2021-05-19 04:00:26 -04:00
parent f4eb54b835
commit 310e26dda9
3 changed files with 51 additions and 44 deletions

View File

@@ -66,23 +66,21 @@ type Func interface {
Close() error Close() error
} }
// UnifiedPolyFunc is an interface for functions which are statically // PolyFunc is an interface for functions which are statically polymorphic. In
// polymorphic. In other words, they are functions which before compile time are // other words, they are functions which before compile time are polymorphic,
// polymorphic, but after a successful compilation have a fixed static // but after a successful compilation have a fixed static signature. This makes
// signature. This makes implementing what would appear to be generic or // implementing what would appear to be generic or polymorphic instead something
// polymorphic instead something that is actually static and that still has the // that is actually static and that still has the language safety properties.
// language safety properties. Our engine requires that by the end of // Our engine requires that by the end of compilation, everything is static.
// compilation, everything is static. This is needed so that values can flow // This is needed so that values can flow safely along the DAG that represents
// safely along the DAG that represents their execution. If the types could // their execution. If the types could change, then we wouldn't be able to
// change, then we wouldn't be able to safely pass values around. // safely pass values around.
// //
// XXX: This interface is similar to PolyFunc, except that it uses a Unify // NOTE: This interface is similar to OldPolyFunc, except that it uses a Unify
// method that works differently than the original Polymorphisms method. This // method that works differently than the original Polymorphisms method. This
// allows us to build invariants that are used directly by the type unification // allows us to build invariants that are used directly by the type unification
// solver. If this new approach is more successful, then we will rename the // solver.
// UnifiedPolyFunc to PolyFunc. This interface is subject to change because this type PolyFunc interface {
// is currently not properly tested.
type UnifiedPolyFunc interface { // XXX: name this "PolyFunc" and remove or wrap the old interface?
Func // implement everything in Func but add the additional requirements Func // implement everything in Func but add the additional requirements
// Unify returns the list of invariants that this func produces. It is a // Unify returns the list of invariants that this func produces. It is a
@@ -109,12 +107,12 @@ type UnifiedPolyFunc interface { // XXX: name this "PolyFunc" and remove or wrap
Build(*types.Type) error // then, you can get argNames from Info() Build(*types.Type) error // then, you can get argNames from Info()
} }
// PolyFunc is an interface for functions which are statically polymorphic. In // OldPolyFunc is an interface for functions which are statically polymorphic.
// other words, they are functions which before compile time are polymorphic, // In other words, they are functions which before compile time are polymorphic,
// but after a successful compilation have a fixed static signature. This makes // but after a successful compilation have a fixed static signature. This makes
// implementing what would appear to be generic or polymorphic instead something // implementing what would appear to be generic or polymorphic instead something
// that is actually static and that still has the language safety properties. // that is actually static and that still has the language safety properties.
type PolyFunc interface { type OldPolyFunc interface {
Func // implement everything in Func but add the additional requirements Func // implement everything in Func but add the additional requirements
// Polymorphisms returns a list of possible function type signatures. It // Polymorphisms returns a list of possible function type signatures. It

View File

@@ -1 +1 @@
# err: errUnify: polymorphic signatures for func `fmt.printf` could not be found: could not determine type from format string # err: errUnify: only recursive solutions left

View File

@@ -7088,37 +7088,36 @@ func (obj *ExprFunc) Unify() ([]interfaces.Invariant, error) {
// TODO: should we try and add invariants for obj.Args? // TODO: should we try and add invariants for obj.Args?
// We don't want to Unify against the *original* ExprFunc pointer, since
// we want the copy of it which is what ExprCall points to. We don't
// call this Unify() method from anywhere except from when it's within
// an ExprCall. We don't call it from StmtFunc which is just a container
// for it. This is basically used as a helper function! By the time this
// is called, we've already made an obj.function which is the copied,
// instantiated version of obj.Function that we are going to use.
if obj.Function != nil { if obj.Function != nil {
// XXX: can we add anything here, perhaps this? fn := obj.function // instantiated copy of obj.Function
fn := obj.Function() polyFn, ok := fn.(interfaces.PolyFunc) // is it statically polymorphic?
_, ok1 := fn.(interfaces.PolyFunc) // is it statically polymorphic? if ok {
unifiedPolyFn, ok2 := fn.(interfaces.UnifiedPolyFunc) // is it statically polymorphic?
if !ok1 && !ok2 {
sig := fn.Info().Sig
if sig != nil && !sig.HasVariant() {
invar := &interfaces.EqualsInvariant{
Expr: obj,
Type: sig,
}
invariants = append(invariants, invar)
}
} else if ok2 { // XXX: try the new interface for now
// We just run the Unify() method of the ExprFunc if it // We just run the Unify() method of the ExprFunc if it
// happens to have one. Get the list of Invariants, and // happens to have one. Get the list of Invariants, and
// return them directly. // return them directly.
invars, err := unifiedPolyFn.Unify(obj) invars, err := polyFn.Unify(obj)
if err != nil { if err != nil {
return nil, err return nil, err
} }
invariants = append(invariants, invars...) invariants = append(invariants, invars...)
}
} else { // It's okay to attempt to get a static signature too, if it's
// XXX: ignore this part for now... plan on deprecating it // nil or has a variant (polymorphic funcs) then it's ignored.
//results, err := polyFn.Polymorphisms(nil, nil) // TODO: is this okay? sig := fn.Info().Sig
//if err == nil { if sig != nil && !sig.HasVariant() {
// // TODO: build an exclusive here... invar := &interfaces.EqualsInvariant{
//} Expr: obj,
Type: sig,
}
invariants = append(invariants, invar)
} }
} }
@@ -7974,20 +7973,30 @@ func (obj *ExprCall) Unify() ([]interfaces.Invariant, error) {
var polyFn interfaces.PolyFunc var polyFn interfaces.PolyFunc
var ok bool var ok bool
// do we have a special case like the operator or template function?
if fn.Function != nil { if fn.Function != nil {
polyFn, ok = fn.function.(interfaces.PolyFunc) // is it statically polymorphic? polyFn, ok = fn.function.(interfaces.PolyFunc) // is it statically polymorphic?
} }
if fn.Function != nil && ok { if fn.Function != nil && ok {
var err error // We just run the Unify() method of the ExprFunc if it happens
results, err = polyFn.Polymorphisms(partialType, partialValues) // to have one. Get the list of Invariants, and return them
// directly. We want to run this unification inside of ExprCall
// and not in ExprFunc, because it's only in ExprCall that we
// have the instantiated copy of the ExprFunc that we actually
// build and unify against and which has the correct pointer
// now, where as the ExprFunc pointer isn't what we unify with!
invars, err := polyFn.Unify(obj.expr)
if err != nil { if err != nil {
return nil, errwrap.Wrapf(err, "polymorphic signatures for func `%s` could not be found", obj.Name) return nil, errwrap.Wrapf(err, "polymorphic unification for func `%s` could not be done", obj.Name)
} }
invariants = append(invariants, invars...)
} else if fn.Function != nil && !ok { } else if fn.Function != nil && !ok {
sig := fn.function.Info().Sig sig := fn.function.Info().Sig
if sig == nil {
// this can happen if it's incorrectly implemented
return nil, errwrap.Wrapf(err, "unification for func `%s` returned nil signature", obj.Name)
}
results = []*types.Type{sig} // only one (non-polymorphic) results = []*types.Type{sig} // only one (non-polymorphic)
} }