summaryrefslogtreecommitdiff
path: root/src/go/types/call.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/types/call.go')
-rw-r--r--src/go/types/call.go125
1 files changed, 101 insertions, 24 deletions
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 979de2338f..02b6038ccc 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -25,14 +25,16 @@ import (
func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *typeparams.IndexExpr) {
assert(tsig != nil || ix != nil)
+ var versionErr bool // set if version error was reported
+ var instErrPos positioner // position for instantion error
+ if ix != nil {
+ instErrPos = inNode(ix.Orig, ix.Lbrack)
+ } else {
+ instErrPos = atPos(pos)
+ }
if !check.allowVersion(check.pkg, pos, 1, 18) {
- var posn positioner
- if ix != nil {
- posn = inNode(ix.Orig, ix.Lbrack)
- } else {
- posn = atPos(pos)
- }
- check.softErrorf(posn, UnsupportedFeature, "function instantiation requires go1.18 or later")
+ check.softErrorf(instErrPos, UnsupportedFeature, "function instantiation requires go1.18 or later")
+ versionErr = true
}
// targs and xlist are the type arguments and corresponding type expressions, or nil.
@@ -74,6 +76,13 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t
// of a synthetic function f where f's parameters are the parameters and results
// of x and where the arguments to the call of f are values of the parameter and
// result types of x.
+ if !versionErr && !check.allowVersion(check.pkg, pos, 1, 21) {
+ if ix != nil {
+ check.softErrorf(instErrPos, UnsupportedFeature, "partially instantiated function in assignment requires go1.21 or later")
+ } else {
+ check.softErrorf(instErrPos, UnsupportedFeature, "implicitly instantiated function in assignment requires go1.21 or later")
+ }
+ }
n := tsig.params.Len()
m := tsig.results.Len()
args = make([]*operand, n+m)
@@ -308,7 +317,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
}
// evaluate arguments
- args := check.exprList(call.Args)
+ args := check.genericExprList(call.Args)
sig = check.arguments(call, sig, targs, args, xlist)
if wasGeneric && sig.TypeParams().Len() == 0 {
@@ -343,6 +352,8 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
return statement
}
+// exprList evaluates a list of expressions and returns the corresponding operands.
+// A single-element expression list may evaluate to multiple operands.
func (check *Checker) exprList(elist []ast.Expr) (xlist []*operand) {
switch len(elist) {
case 0:
@@ -361,6 +372,25 @@ func (check *Checker) exprList(elist []ast.Expr) (xlist []*operand) {
return
}
+// genericExprList is like exprList but result operands may be generic (not fully instantiated).
+func (check *Checker) genericExprList(elist []ast.Expr) (xlist []*operand) {
+ switch len(elist) {
+ case 0:
+ // nothing to do
+ case 1:
+ xlist = check.genericMultiExpr(elist[0])
+ default:
+ // multiple (possibly invalid) values
+ xlist = make([]*operand, len(elist))
+ for i, e := range elist {
+ var x operand
+ check.genericExpr(&x, e)
+ xlist[i] = &x
+ }
+ }
+ return
+}
+
// xlist is the list of type argument expressions supplied in the source code.
func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type, args []*operand, xlist []ast.Expr) (rsig *Signature) {
rsig = sig
@@ -391,7 +421,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
// set up parameters
sigParams := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
- adjusted := false // indicates if sigParams is different from t.params
+ adjusted := false // indicates if sigParams is different from sig.params
if sig.variadic {
if ddd {
// variadic_func(a, b, c...)
@@ -452,8 +482,12 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
return
}
- // infer type arguments and instantiate signature if necessary
- if sig.TypeParams().Len() > 0 {
+ // collect type parameters of callee and generic function arguments
+ var tparams []*TypeParam
+
+ // collect type parameters of callee
+ n := sig.TypeParams().Len()
+ if n > 0 {
if !check.allowVersion(check.pkg, call.Pos(), 1, 18) {
switch call.Fun.(type) {
case *ast.IndexExpr, *ast.IndexListExpr:
@@ -463,29 +497,72 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
check.softErrorf(inNode(call, call.Lparen), UnsupportedFeature, "implicit function instantiation requires go1.18 or later")
}
}
-
- // Rename type parameters to avoid problems with recursive calls.
- var tparams []*TypeParam
+ // rename type parameters to avoid problems with recursive calls
tparams, sigParams = check.renameTParams(call.Pos(), sig.TypeParams().list(), sigParams)
+ }
- targs := check.infer(call, tparams, targs, sigParams, args)
+ // collect type parameters from generic function arguments
+ var genericArgs []int // indices of generic function arguments
+ if check.conf._EnableReverseTypeInference {
+ for i, arg := range args {
+ // generic arguments cannot have a defined (*Named) type - no need for underlying type below
+ if asig, _ := arg.typ.(*Signature); asig != nil && asig.TypeParams().Len() > 0 {
+ // TODO(gri) need to also rename type parameters for cases like f(g, g)
+ tparams = append(tparams, asig.TypeParams().list()...)
+ genericArgs = append(genericArgs, i)
+ }
+ }
+ }
+ if len(genericArgs) > 0 && !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
+ // at the moment we only support implicit instantiations of argument functions
+ check.softErrorf(inNode(call, call.Lparen), UnsupportedFeature, "implicitly instantiated function as argument requires go1.21 or later")
+ }
+
+ // tparams holds the type parameters of the callee and generic function arguments, if any:
+ // the first n type parameters belong to the callee, followed by mi type parameters for each
+ // of the generic function arguments, where mi = args[i].typ.(*Signature).TypeParams().Len().
+
+ // infer missing type arguments of callee and function arguments
+ if len(tparams) > 0 {
+ targs = check.infer(call, tparams, targs, sigParams, args)
if targs == nil {
+ // TODO(gri) If infer inferred the first targs[:n], consider instantiating
+ // the call signature for better error messages/gopls behavior.
+ // Perhaps instantiate as much as we can, also for arguments.
+ // This will require changes to how infer returns its results.
return // error already reported
}
- // compute result signature
- rsig = check.instantiateSignature(call.Pos(), sig, targs, xlist)
- assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
- check.recordInstance(call.Fun, targs, rsig)
+ // compute result signature: instantiate if needed
+ rsig = sig
+ if n > 0 {
+ rsig = check.instantiateSignature(call.Pos(), sig, targs[:n], xlist)
+ assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
+ check.recordInstance(call.Fun, targs[:n], rsig)
+ }
- // Optimization: Only if the parameter list was adjusted do we
- // need to compute it from the adjusted list; otherwise we can
- // simply use the result signature's parameter list.
- if adjusted {
- sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(tparams, targs), nil, check.context()).(*Tuple)
+ // Optimization: Only if the callee's parameter list was adjusted do we need to
+ // compute it from the adjusted list; otherwise we can simply use the result
+ // signature's parameter list. We only need the n type parameters and arguments
+ // of the callee.
+ if n > 0 && adjusted {
+ sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(tparams[:n], targs[:n]), nil, check.context()).(*Tuple)
} else {
sigParams = rsig.params
}
+
+ // compute argument signatures: instantiate if needed
+ j := n
+ for _, i := range genericArgs {
+ asig := args[i].typ.(*Signature)
+ k := j + asig.TypeParams().Len()
+ // targs[j:k] are the inferred type arguments for asig
+ asig = check.instantiateSignature(call.Pos(), asig, targs[j:k], nil) // TODO(gri) provide xlist if possible (partial instantiations)
+ assert(asig.TypeParams().Len() == 0) // signature is not generic anymore
+ args[i].typ = asig
+ check.recordInstance(args[i].expr, targs[j:k], asig)
+ j = k
+ }
}
// check arguments