diff options
Diffstat (limited to 'src/go/types/call.go')
-rw-r--r-- | src/go/types/call.go | 125 |
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 |