}
if x == nil {
+ var target *target
+ // avoid calling syntax.String if not needed
+ if T != nil {
+ if _, ok := under(T).(*Signature); ok {
+ target = newTarget(T, syntax.String(lhs))
+ }
+ }
x = new(operand)
- check.expr(T, x, rhs)
+ check.expr(target, x, rhs)
}
context := "assignment"
if l == r && !isCall {
var x operand
for i, lhs := range lhs {
- check.expr(lhs.typ, &x, orig_rhs[i])
+ desc := lhs.name
+ if returnStmt != nil && desc == "" {
+ desc = "result variable"
+ }
+ check.expr(newTarget(lhs.typ, desc), &x, orig_rhs[i])
check.initVar(lhs, &x, context)
}
return
// funcInst type-checks a function instantiation.
// The incoming x must be a generic function.
// If inst != nil, it provides some or all of the type arguments (inst.Index).
-// If target type tsig != nil, the signature may be used to infer missing type
-// arguments of x, if any. At least one of tsig or inst must be provided.
+// If target != nil, it may be used to infer missing type arguments of x, if any.
+// At least one of T or inst must be provided.
//
// There are two modes of operation:
//
//
// If an error (other than a version error) occurs in any case, it is reported
// and x.mode is set to invalid.
-func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) {
- assert(tsig != nil || inst != nil)
+func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) {
+ assert(T != nil || inst != nil)
var instErrPos poser
if inst != nil {
//
var args []*operand
var params []*Var
- if tsig != nil && sig.tparams != nil {
+ if T != nil && sig.tparams != nil {
if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
if inst != nil {
check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
// The type of the argument operand is tsig, which is the type of the LHS in an assignment
// or the result type in a return statement. Create a pseudo-expression for that operand
// that makes sense when reported in error messages from infer, below.
- expr := syntax.NewName(x.Pos(), "variable in assignment")
- args = []*operand{{mode: value, expr: expr, typ: tsig}}
+ expr := syntax.NewName(x.Pos(), T.desc)
+ args = []*operand{{mode: value, expr: expr, typ: T.sig}}
}
// Rename type parameters to avoid problems with recursive instantiations.
if lhs == nil || len(lhs) == 1 {
assert(lhs == nil || lhs[0] == obj)
var x operand
- check.expr(obj.typ, &x, init)
+ check.expr(newTarget(obj.typ, obj.name), &x, init)
check.initVar(obj, &x, "variable declaration")
return
}
statement
)
-// TODO(gri) In rawExpr below, consider using T instead of hint and
-// some sort of "operation mode" instead of allowGeneric.
-// May be clearer and less error-prone.
+// target represent the (signature) type and description of the LHS
+// variable of an assignment, or of a function result variable.
+type target struct {
+ sig *Signature
+ desc string
+}
+
+// newTarget creates a new target for the given type and description.
+// The result is nil if typ is not a signature.
+func newTarget(typ Type, desc string) *target {
+ if typ != nil {
+ if sig, _ := under(typ).(*Signature); sig != nil {
+ return &target{sig, desc}
+ }
+ }
+ return nil
+}
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occurred, x.mode is set to invalid.
-// If a non-nil target type T is given and e is a generic function
-// or function call, T is used to infer the type arguments for e.
+// If a non-nil target T is given and e is a generic function,
+// T is used to infer the type arguments for e.
// If hint != nil, it is the type of a composite literal element.
// If allowGeneric is set, the operand type may be an uninstantiated
// parameterized type or function value.
-func (check *Checker) rawExpr(T Type, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind {
+func (check *Checker) rawExpr(T *target, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind {
if check.conf.Trace {
check.trace(e.Pos(), "-- expr %s", e)
check.indent++
}
// If x is a generic type, or a generic function whose type arguments cannot be inferred
-// from a non-nil target type T, nonGeneric reports an error and invalidates x.mode and x.typ.
+// from a non-nil target T, nonGeneric reports an error and invalidates x.mode and x.typ.
// Otherwise it leaves x alone.
-func (check *Checker) nonGeneric(T Type, x *operand) {
+func (check *Checker) nonGeneric(T *target, x *operand) {
if x.mode == invalid || x.mode == novalue {
return
}
case *Signature:
if t.tparams != nil {
if enableReverseTypeInference && T != nil {
- if tsig, _ := under(T).(*Signature); tsig != nil {
- check.funcInst(tsig, x.Pos(), x, nil, true)
- return
- }
+ check.funcInst(T, x.Pos(), x, nil, true)
+ return
}
what = "function"
}
// exprInternal contains the core of type checking of expressions.
// Must only be called by rawExpr.
// (See rawExpr for an explanation of the parameters.)
-func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) exprKind {
+func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Type) exprKind {
// make sure x has a valid state in case of bailout
// (was go.dev/issue/5770)
x.mode = invalid
case *syntax.IndexExpr:
if check.indexExpr(x, e) {
- var tsig *Signature
- if enableReverseTypeInference && T != nil {
- tsig, _ = under(T).(*Signature)
+ if !enableReverseTypeInference {
+ T = nil
}
- check.funcInst(tsig, e.Pos(), x, e, true)
+ check.funcInst(T, e.Pos(), x, e, true)
}
if x.mode == invalid {
goto Error
}
// expr typechecks expression e and initializes x with the expression value.
-// If a non-nil target type T is given and e is a generic function
-// or function call, T is used to infer the type arguments for e.
+// If a non-nil target T is given and e is a generic function or
+// a function call, T is used to infer the type arguments for e.
// The result must be a single value.
// If an error occurred, x.mode is set to invalid.
-func (check *Checker) expr(T Type, x *operand, e syntax.Expr) {
+func (check *Checker) expr(T *target, x *operand, e syntax.Expr) {
check.rawExpr(T, x, e, nil, false)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
// Terminology: generic parameter = function parameter with a type-parameterized type
u := newUnifier(tparams, targs, check.allowVersion(check.pkg, pos, go1_21))
- errorf := func(kind string, tpar, targ Type, arg *operand) {
+ errorf := func(tpar, targ Type, arg *operand) {
// provide a better error message if we can
targs := u.inferred(tparams)
if targs[0] == nil {
}
}
if allFailed {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams))
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
return
}
}
// InvalidTypeArg). We can't differentiate these cases, so fall back on
// the more general CannotInferTypeArgs.
if inferred != tpar {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
} else {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s", targ, arg.expr, tpar)
}
}
// Collect the indices of untyped arguments and handle them later.
if isTyped(arg.typ) {
if !u.unify(par.typ, arg.typ, assign) {
- errorf("type", par.typ, arg.typ, arg)
+ errorf(par.typ, arg.typ, arg)
return nil
}
} else if _, ok := par.typ.(*TypeParam); ok && !arg.isNil() {
}
if x == nil {
+ var target *target
+ // avoid calling ExprString if not needed
+ if T != nil {
+ if _, ok := under(T).(*Signature); ok {
+ target = newTarget(T, ExprString(lhs))
+ }
+ }
x = new(operand)
- check.expr(T, x, rhs)
+ check.expr(target, x, rhs)
}
context := "assignment"
if l == r && !isCall {
var x operand
for i, lhs := range lhs {
- check.expr(lhs.typ, &x, orig_rhs[i])
+ desc := lhs.name
+ if returnStmt != nil && desc == "" {
+ desc = "result variable"
+ }
+ check.expr(newTarget(lhs.typ, desc), &x, orig_rhs[i])
check.initVar(lhs, &x, context)
}
return
// funcInst type-checks a function instantiation.
// The incoming x must be a generic function.
// If ix != nil, it provides some or all of the type arguments (ix.Indices).
-// If target type tsig != nil, the signature may be used to infer missing type
-// arguments of x, if any. At least one of tsig or inst must be provided.
+// If target != nil, it may be used to infer missing type arguments of x, if any.
+// At least one of T or ix must be provided.
//
// There are two modes of operation:
//
//
// If an error (other than a version error) occurs in any case, it is reported
// and x.mode is set to invalid.
-func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *typeparams.IndexExpr, infer bool) ([]Type, []ast.Expr) {
- assert(tsig != nil || ix != nil)
+func (check *Checker) funcInst(T *target, pos token.Pos, x *operand, ix *typeparams.IndexExpr, infer bool) ([]Type, []ast.Expr) {
+ assert(T != nil || ix != nil)
var instErrPos positioner
if ix != nil {
//
var args []*operand
var params []*Var
- if tsig != nil && sig.tparams != nil {
+ if T != nil && sig.tparams != nil {
if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
if ix != nil {
check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
// The type of the argument operand is tsig, which is the type of the LHS in an assignment
// or the result type in a return statement. Create a pseudo-expression for that operand
// that makes sense when reported in error messages from infer, below.
- expr := ast.NewIdent("variable in assignment")
+ expr := ast.NewIdent(T.desc)
expr.NamePos = x.Pos() // correct position
- args = []*operand{{mode: value, expr: expr, typ: tsig}}
+ args = []*operand{{mode: value, expr: expr, typ: T.sig}}
}
// Rename type parameters to avoid problems with recursive instantiations.
if lhs == nil || len(lhs) == 1 {
assert(lhs == nil || lhs[0] == obj)
var x operand
- check.expr(obj.typ, &x, init)
+ check.expr(newTarget(obj.typ, obj.name), &x, init)
check.initVar(obj, &x, "variable declaration")
return
}
statement
)
-// TODO(gri) In rawExpr below, consider using T instead of hint and
-// some sort of "operation mode" instead of allowGeneric.
-// May be clearer and less error-prone.
+// target represent the (signature) type and description of the LHS
+// variable of an assignment, or of a function result variable.
+type target struct {
+ sig *Signature
+ desc string
+}
+
+// newTarget creates a new target for the given type and description.
+// The result is nil if typ is not a signature.
+func newTarget(typ Type, desc string) *target {
+ if typ != nil {
+ if sig, _ := under(typ).(*Signature); sig != nil {
+ return &target{sig, desc}
+ }
+ }
+ return nil
+}
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occurred, x.mode is set to invalid.
-// If a non-nil target type T is given and e is a generic function
-// or function call, T is used to infer the type arguments for e.
+// If a non-nil target T is given and e is a generic function,
+// T is used to infer the type arguments for e.
// If hint != nil, it is the type of a composite literal element.
// If allowGeneric is set, the operand type may be an uninstantiated
// parameterized type or function value.
-func (check *Checker) rawExpr(T Type, x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind {
+func (check *Checker) rawExpr(T *target, x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind {
if check.conf._Trace {
check.trace(e.Pos(), "-- expr %s", e)
check.indent++
}
// If x is a generic type, or a generic function whose type arguments cannot be inferred
-// from a non-nil target type T, nonGeneric reports an error and invalidates x.mode and x.typ.
+// from a non-nil target T, nonGeneric reports an error and invalidates x.mode and x.typ.
// Otherwise it leaves x alone.
-func (check *Checker) nonGeneric(T Type, x *operand) {
+func (check *Checker) nonGeneric(T *target, x *operand) {
if x.mode == invalid || x.mode == novalue {
return
}
case *Signature:
if t.tparams != nil {
if enableReverseTypeInference && T != nil {
- if tsig, _ := under(T).(*Signature); tsig != nil {
- check.funcInst(tsig, x.Pos(), x, nil, true)
- return
- }
+ check.funcInst(T, x.Pos(), x, nil, true)
+ return
}
what = "function"
}
// exprInternal contains the core of type checking of expressions.
// Must only be called by rawExpr.
// (See rawExpr for an explanation of the parameters.)
-func (check *Checker) exprInternal(T Type, x *operand, e ast.Expr, hint Type) exprKind {
+func (check *Checker) exprInternal(T *target, x *operand, e ast.Expr, hint Type) exprKind {
// make sure x has a valid state in case of bailout
// (was go.dev/issue/5770)
x.mode = invalid
case *ast.IndexExpr, *ast.IndexListExpr:
ix := typeparams.UnpackIndexExpr(e)
if check.indexExpr(x, ix) {
- var tsig *Signature
- if enableReverseTypeInference && T != nil {
- tsig, _ = under(T).(*Signature)
+ if !enableReverseTypeInference {
+ T = nil
}
- check.funcInst(tsig, e.Pos(), x, ix, true)
+ check.funcInst(T, e.Pos(), x, ix, true)
}
if x.mode == invalid {
goto Error
}
// expr typechecks expression e and initializes x with the expression value.
-// If a non-nil target type T is given and e is a generic function
-// or function call, T is used to infer the type arguments for e.
+// If a non-nil target T is given and e is a generic function or
+// a function call, T is used to infer the type arguments for e.
// The result must be a single value.
// If an error occurred, x.mode is set to invalid.
-func (check *Checker) expr(T Type, x *operand, e ast.Expr) {
+func (check *Checker) expr(T *target, x *operand, e ast.Expr) {
check.rawExpr(T, x, e, nil, false)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
// Terminology: generic parameter = function parameter with a type-parameterized type
u := newUnifier(tparams, targs, check.allowVersion(check.pkg, posn, go1_21))
- errorf := func(kind string, tpar, targ Type, arg *operand) {
+ errorf := func(tpar, targ Type, arg *operand) {
// provide a better error message if we can
targs := u.inferred(tparams)
if targs[0] == nil {
}
}
if allFailed {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams))
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))
return
}
}
// InvalidTypeArg). We can't differentiate these cases, so fall back on
// the more general CannotInferTypeArgs.
if inferred != tpar {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)
} else {
- check.errorf(arg, CannotInferTypeArgs, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+ check.errorf(arg, CannotInferTypeArgs, "type %s of %s does not match %s", targ, arg.expr, tpar)
}
}
// Collect the indices of untyped arguments and handle them later.
if isTyped(arg.typ) {
if !u.unify(par.typ, arg.typ, assign) {
- errorf("type", par.typ, arg.typ, arg)
+ errorf(par.typ, arg.typ, arg)
return nil
}
} else if _, ok := par.typ.(*TypeParam); ok && !arg.isNil() {
_ func(int) int = f3[int]
v6 func(int, int) = f4
- v7 func(int, string) = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)"
+ v7 func(int, string) = f4 // ERROR "type func(int, string) of v7 does not match inferred type func(int, int) for func(P, P)"
v8 func(int) []int = f5
- v9 func(string) []int = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P"
+ v9 func(string) []int = f5 // ERROR "type func(string) []int of v9 does not match inferred type func(string) []string for func(P) []P"
_, _ func(int) = f1, f1
_, _ func(int) = f1, f2 // ERROR "cannot infer P"
v5 = f3[int]
v6 = f4
- v7 = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)"
+ v7 = f4 // ERROR "type func(int, string) of v7 does not match inferred type func(int, int) for func(P, P)"
v8 = f5
- v9 = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P"
+ v9 = f5 // ERROR "type func(string) []int of v9 does not match inferred type func(string) []string for func(P) []P"
+
+ // non-trivial LHS
+ var a [2]func(string) []int
+ a[0] = f5 // ERROR "type func(string) []int of a[0] does not match inferred type func(string) []string for func(P) []P"
}
// Return statements
func _() func(int, int) { return f4 }
func _() func(int, string) {
- return f4 /* ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)" */
+ return f4 /* ERROR "type func(int, string) of result variable does not match inferred type func(int, int) for func(P, P)" */
}
func _() func(int) []int { return f5 }
func _() func(string) []int {
- return f5 /* ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P" */
+ return f5 /* ERROR "type func(string) []int of result variable does not match inferred type func(string) []string for func(P) []P" */
}
func _() (_, _ func(int)) { return f1, f1 }
// be identical to match).
// The result is an error from type inference, rather than an
// error from an assignment mismatch.
-var f func(int, String) = g // ERROR "type func(int, String) of variable in assignment does not match inferred type func(int, string) for func(P, string)"
+var f func(int, String) = g // ERROR "type func(int, String) of f does not match inferred type func(int, string) for func(P, string)"
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f[P any](P) P { panic(0) }
+
+var v func(string) int = f // ERROR "type func(string) int of v does not match inferred type func(string) string for func(P) P"
+
+func _() func(string) int {
+ return f // ERROR "type func(string) int of result variable does not match inferred type func(string) string for func(P) P"
+}