cmd/compile: make sure methods are type checked when used in inference When comparing a type against an interface during type inference, some methods may not yet have been encountered in the source. Type-check on demand as we do elsewhere. Fixes #77905. Change-Id: Ib87e625c112e734ae361275364d93add79d544ee Reviewed-on: https://go-review.googlesource.com/c/go/+/789940 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com>
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 8f4336f..1529d11 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go
@@ -111,7 +111,7 @@ // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(tparams, targs, check.allowVersion(go1_21)) + u := newUnifier(check, tparams, targs, check.allowVersion(go1_21)) errorf := func(tpar, targ Type, arg *operand) { // provide a better error message if we can
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index 9e90c5f..e554e77 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go
@@ -67,6 +67,7 @@ // corresponding types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { + check *Checker // handles maps each type parameter to its inferred type through // an indirection *Type called (inferred type) "handle". // Initially, each type parameter has its own, separate handle, @@ -85,7 +86,7 @@ // and corresponding type argument lists. The type argument list may be shorter // than the type parameter list, and it may contain nil types. Matching type // parameters and arguments must have the same index. -func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { +func newUnifier(check *Checker, tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { assert(len(tparams) >= len(targs)) handles := make(map[*TypeParam]*Type, len(tparams)) // Allocate all handles up-front: in a correct program, all type parameters @@ -99,7 +100,7 @@ } handles[x] = &t } - return &unifier{handles, 0, enableInterfaceInference} + return &unifier{check, handles, 0, enableInterfaceInference} } // unifyMode controls the behavior of the unifier. @@ -142,6 +143,7 @@ } func (u *unifier) tracef(format string, args ...any) { + // TODO(gri) consider adjusting this to use Checker.trace fmt.Println(strings.Repeat(". ", u.depth) + sprintf(nil, true, format, args...)) } @@ -539,7 +541,12 @@ xmethods := xi.typeSet().methods for _, xm := range xmethods { obj, _, _ := LookupFieldOrMethod(y, false, xm.pkg, xm.name) - if ym, _ := obj.(*Func); ym == nil || ym.Signature().TypeParams().Len() > 0 || !u.nify(xm.typ, ym.typ, exact, p) { + ym, _ := obj.(*Func) + if ym == nil { + return false + } + u.check.objDecl(ym) // ensure fully set-up signature + if ym.Signature().TypeParams() != nil || !u.nify(xm.typ, ym.typ, exact, p) { return false } }
diff --git a/src/go/types/infer.go b/src/go/types/infer.go index d18e19e..de873cf 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go
@@ -114,7 +114,7 @@ // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(tparams, targs, check.allowVersion(go1_21)) + u := newUnifier(check, tparams, targs, check.allowVersion(go1_21)) errorf := func(tpar, targ Type, arg *operand) { // provide a better error message if we can
diff --git a/src/go/types/unify.go b/src/go/types/unify.go index 070f9d4..00c93a2 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go
@@ -70,6 +70,7 @@ // corresponding types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { + check *Checker // handles maps each type parameter to its inferred type through // an indirection *Type called (inferred type) "handle". // Initially, each type parameter has its own, separate handle, @@ -88,7 +89,7 @@ // and corresponding type argument lists. The type argument list may be shorter // than the type parameter list, and it may contain nil types. Matching type // parameters and arguments must have the same index. -func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { +func newUnifier(check *Checker, tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { assert(len(tparams) >= len(targs)) handles := make(map[*TypeParam]*Type, len(tparams)) // Allocate all handles up-front: in a correct program, all type parameters @@ -102,7 +103,7 @@ } handles[x] = &t } - return &unifier{handles, 0, enableInterfaceInference} + return &unifier{check, handles, 0, enableInterfaceInference} } // unifyMode controls the behavior of the unifier. @@ -145,6 +146,7 @@ } func (u *unifier) tracef(format string, args ...any) { + // TODO(gri) consider adjusting this to use Checker.trace fmt.Println(strings.Repeat(". ", u.depth) + sprintf(nil, nil, true, format, args...)) } @@ -542,7 +544,12 @@ xmethods := xi.typeSet().methods for _, xm := range xmethods { obj, _, _ := LookupFieldOrMethod(y, false, xm.pkg, xm.name) - if ym, _ := obj.(*Func); ym == nil || ym.Signature().TypeParams().Len() > 0 || !u.nify(xm.typ, ym.typ, exact, p) { + ym, _ := obj.(*Func) + if ym == nil { + return false + } + u.check.objDecl(ym) // ensure fully set-up signature + if ym.Signature().TypeParams() != nil || !u.nify(xm.typ, ym.typ, exact, p) { return false } }
diff --git a/src/internal/types/testdata/fixedbugs/issue77905.go b/src/internal/types/testdata/fixedbugs/issue77905.go new file mode 100644 index 0000000..6288a7c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue77905.go
@@ -0,0 +1,30 @@ +// Copyright 2026 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 + +type M[T any] interface { + m() T +} + +func f[T any](x interface{ m() T }) T { return x.m() } +func g[T any](x M[T]) T { return x.m() } + +type S struct{} + +// inference must work here even though m is declared only afterwards +// (inference must type-check m as needed) +var _ = f(S{}) +var _ = g(S{}) + +func _() { + var s S + var _ = f(s) + var _ = g(s) +} + +func (S) m() int { return 0 } + +var _ = f(S{}) +var _ = g(S{})