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:
@@ -66,23 +66,21 @@ type Func interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// UnifiedPolyFunc is an interface for functions which are statically
|
||||
// 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 implementing what would appear to be generic or
|
||||
// polymorphic instead something that is actually static and that still has the
|
||||
// language safety properties. Our engine requires that by the end of
|
||||
// compilation, everything is static. This is needed so that values can flow
|
||||
// safely along the DAG that represents their execution. If the types could
|
||||
// change, then we wouldn't be able to safely pass values around.
|
||||
// PolyFunc is an interface for functions which are statically 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
|
||||
// implementing what would appear to be generic or polymorphic instead something
|
||||
// that is actually static and that still has the language safety properties.
|
||||
// Our engine requires that by the end of compilation, everything is static.
|
||||
// This is needed so that values can flow safely along the DAG that represents
|
||||
// their execution. If the types could change, then we wouldn't be able to
|
||||
// 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
|
||||
// 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
|
||||
// UnifiedPolyFunc to PolyFunc. This interface is subject to change because this
|
||||
// is currently not properly tested.
|
||||
type UnifiedPolyFunc interface { // XXX: name this "PolyFunc" and remove or wrap the old interface?
|
||||
// solver.
|
||||
type PolyFunc interface {
|
||||
Func // implement everything in Func but add the additional requirements
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// PolyFunc is an interface for functions which are statically polymorphic. In
|
||||
// other words, they are functions which before compile time are polymorphic,
|
||||
// OldPolyFunc is an interface for functions which are statically 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
|
||||
// implementing what would appear to be generic or polymorphic instead something
|
||||
// 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
|
||||
|
||||
// Polymorphisms returns a list of possible function type signatures. It
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7088,12 +7088,29 @@ func (obj *ExprFunc) Unify() ([]interfaces.Invariant, error) {
|
||||
|
||||
// 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 {
|
||||
// XXX: can we add anything here, perhaps this?
|
||||
fn := obj.Function()
|
||||
_, ok1 := fn.(interfaces.PolyFunc) // is it statically polymorphic?
|
||||
unifiedPolyFn, ok2 := fn.(interfaces.UnifiedPolyFunc) // is it statically polymorphic?
|
||||
if !ok1 && !ok2 {
|
||||
fn := obj.function // instantiated copy of obj.Function
|
||||
polyFn, ok := fn.(interfaces.PolyFunc) // is it statically polymorphic?
|
||||
if ok {
|
||||
// We just run the Unify() method of the ExprFunc if it
|
||||
// happens to have one. Get the list of Invariants, and
|
||||
// return them directly.
|
||||
invars, err := polyFn.Unify(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invariants = append(invariants, invars...)
|
||||
}
|
||||
|
||||
// It's okay to attempt to get a static signature too, if it's
|
||||
// nil or has a variant (polymorphic funcs) then it's ignored.
|
||||
sig := fn.Info().Sig
|
||||
if sig != nil && !sig.HasVariant() {
|
||||
invar := &interfaces.EqualsInvariant{
|
||||
@@ -7102,24 +7119,6 @@ func (obj *ExprFunc) Unify() ([]interfaces.Invariant, error) {
|
||||
}
|
||||
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
|
||||
// happens to have one. Get the list of Invariants, and
|
||||
// return them directly.
|
||||
invars, err := unifiedPolyFn.Unify(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invariants = append(invariants, invars...)
|
||||
|
||||
} else {
|
||||
// XXX: ignore this part for now... plan on deprecating it
|
||||
//results, err := polyFn.Polymorphisms(nil, nil) // TODO: is this okay?
|
||||
//if err == nil {
|
||||
// // TODO: build an exclusive here...
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
//if len(obj.Values) > 0
|
||||
@@ -7974,20 +7973,30 @@ func (obj *ExprCall) Unify() ([]interfaces.Invariant, error) {
|
||||
|
||||
var polyFn interfaces.PolyFunc
|
||||
var ok bool
|
||||
// do we have a special case like the operator or template function?
|
||||
if fn.Function != nil {
|
||||
polyFn, ok = fn.function.(interfaces.PolyFunc) // is it statically polymorphic?
|
||||
}
|
||||
|
||||
if fn.Function != nil && ok {
|
||||
var err error
|
||||
results, err = polyFn.Polymorphisms(partialType, partialValues)
|
||||
// We just run the Unify() method of the ExprFunc if it happens
|
||||
// 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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user