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{})