Skip to content

Commit

Permalink
go/types: return an error from Instantiate on incorrect len(targs)
Browse files Browse the repository at this point in the history
Instantiate already returns an error when validation fails. Panicking on
an incorrect number of type arguments means that callers must both
pre-validate the number of type arguments and handle resulting errors.
Returning an error rather than panicking allows eliminating
pre-validation at the call-site.

Also update the Instantiate docstring to correct some stale/inaccurate
information, and to clarify its behavior more precisely.

Updates #47916

Change-Id: I997ef30b3486760a90b0db4c3ea7111280d74a81
Reviewed-on: https://go-review.googlesource.com/c/go/+/363635
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
findleyr committed Nov 13, 2021
1 parent e658c42 commit c78a267
Showing 1 changed file with 30 additions and 20 deletions.
50 changes: 30 additions & 20 deletions src/go/types/instantiate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,50 @@ import (
"go/token"
)

// Instantiate instantiates the type typ with the given type arguments targs.
// typ must be a *Named or a *Signature type, and its number of type parameters
// must match the number of provided type arguments. The result is a new,
// instantiated (not parameterized) type of the same kind (either a *Named or a
// *Signature). Any methods attached to a *Named are simply copied; they are
// not instantiated.
// Instantiate instantiates the type orig with the given type arguments targs.
// orig must be a *Named or a *Signature type. If there is no error, the
// resulting Type is a new, instantiated (not parameterized) type of the same
// kind (either a *Named or a *Signature). Methods attached to a *Named type
// are also instantiated, and associated with a new *Func that has the same
// position as the original method, but nil function scope.
//
// If ctxt is non-nil, it may be used to de-dupe the instance against previous
// instances with the same identity.
// If ctxt is non-nil, it may be used to de-duplicate the instance against
// previous instances with the same identity. As a special case, generic
// *Signature origin types are only considered identical if they are pointer
// equivalent, so that instantiating distinct (but possibly identical)
// signatures will yield different instances.
//
// If verify is set and constraint satisfaction fails, the returned error may
// wrap an *ArgumentError indicating which type argument did not satisfy its
// corresponding type parameter constraint, and why.
// If validate is set, Instantiate verifies that the number of type arguments
// and parameters match, and that the type arguments satisfy their
// corresponding type constraints. If verification fails, the resulting error
// may wrap an *ArgumentError indicating which type argument did not satisfy
// its corresponding type parameter constraint, and why.
//
// TODO(rfindley): change this function to also return an error if lengths of
// tparams and targs do not match.
func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, error) {
inst := (*Checker)(nil).instance(token.NoPos, typ, targs, ctxt)

var err error
// If validate is not set, Instantiate does not verify the type argument count
// or whether the type arguments satisfy their constraints. Instantiate is
// guaranteed to not return an error, but may panic. Specifically, for
// *Signature types, Instantiate will panic immediately if the type argument
// count is incorrect; for *Named types, a panic may occur later inside the
// *Named API.
func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
if validate {
var tparams []*TypeParam
switch t := typ.(type) {
switch t := orig.(type) {
case *Named:
tparams = t.TypeParams().list()
case *Signature:
tparams = t.TypeParams().list()
}
if len(targs) != len(tparams) {
return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams))
}
if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs); err != nil {
return inst, &ArgumentError{i, err}
return nil, &ArgumentError{i, err}
}
}

return inst, err
inst := (*Checker)(nil).instance(token.NoPos, orig, targs, ctxt)
return inst, nil
}

// instance creates a type or function instance using the given original type
Expand Down

0 comments on commit c78a267

Please sign in to comment.