go/callgraph/cha: fix bug computing correspondence of abstract/concrete methods

In a dynamic interface method call x.f() where x has type I, it is not sound
to assume that the receiver of the types.Signature for types.Func f has type I, as
Func objects for abstract methods may be shared by multiple interfaces.

Fixes golang/go#23925

Change-Id: I755e3010d1310480c46855e072946346626b3e59
Reviewed-on: https://go-review.googlesource.com/95697
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/go/callgraph/cha/cha.go b/go/callgraph/cha/cha.go
index 68ffffe..215ff17 100644
--- a/go/callgraph/cha/cha.go
+++ b/go/callgraph/cha/cha.go
@@ -47,23 +47,36 @@
 
 	// methodsByName contains all methods,
 	// grouped by name for efficient lookup.
+	// (methodsById would be better but not every SSA method has a go/types ID.)
 	methodsByName := make(map[string][]*ssa.Function)
 
-	// methodsMemo records, for every abstract method call call I.f on
-	// interface type I, the set of concrete methods C.f of all
+	// An imethod represents an interface method I.m.
+	// (There's no go/types object for it;
+	// a *types.Func may be shared by many interfaces due to interface embedding.)
+	type imethod struct {
+		I  *types.Interface
+		id string
+	}
+	// methodsMemo records, for every abstract method call I.m on
+	// interface type I, the set of concrete methods C.m of all
 	// types C that satisfy interface I.
-	methodsMemo := make(map[*types.Func][]*ssa.Function)
-	lookupMethods := func(m *types.Func) []*ssa.Function {
-		methods, ok := methodsMemo[m]
+	//
+	// Abstract methods may be shared by several interfaces,
+	// hence we must pass I explicitly, not guess from m.
+	//
+	// methodsMemo is just a cache, so it needn't be a typeutil.Map.
+	methodsMemo := make(map[imethod][]*ssa.Function)
+	lookupMethods := func(I *types.Interface, m *types.Func) []*ssa.Function {
+		id := m.Id()
+		methods, ok := methodsMemo[imethod{I, id}]
 		if !ok {
-			I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface)
 			for _, f := range methodsByName[m.Name()] {
 				C := f.Signature.Recv().Type() // named or *named
 				if types.Implements(C, I) {
 					methods = append(methods, f)
 				}
 			}
-			methodsMemo[m] = methods
+			methodsMemo[imethod{I, id}] = methods
 		}
 		return methods
 	}
@@ -109,7 +122,8 @@
 				if site, ok := instr.(ssa.CallInstruction); ok {
 					call := site.Common()
 					if call.IsInvoke() {
-						addEdges(fnode, site, lookupMethods(call.Method))
+						tiface := call.Value.Type().Underlying().(*types.Interface)
+						addEdges(fnode, site, lookupMethods(tiface, call.Method))
 					} else if g := call.StaticCallee(); g != nil {
 						addEdge(fnode, site, g)
 					} else if _, ok := call.Value.(*ssa.Builtin); !ok {
diff --git a/go/callgraph/cha/cha_test.go b/go/callgraph/cha/cha_test.go
index 7e56c06..cb2d585 100644
--- a/go/callgraph/cha/cha_test.go
+++ b/go/callgraph/cha/cha_test.go
@@ -30,6 +30,7 @@
 	"testdata/func.go",
 	"testdata/iface.go",
 	"testdata/recv.go",
+	"testdata/issue23925.go",
 }
 
 func expectation(f *ast.File) (string, token.Pos) {
diff --git a/go/callgraph/cha/testdata/issue23925.go b/go/callgraph/cha/testdata/issue23925.go
new file mode 100644
index 0000000..59eaf18
--- /dev/null
+++ b/go/callgraph/cha/testdata/issue23925.go
@@ -0,0 +1,38 @@
+package main
+
+// Regression test for https://github.com/golang/go/issues/23925
+
+type stringFlagImpl string
+
+func (*stringFlagImpl) Set(s string) error { return nil }
+
+type boolFlagImpl bool
+
+func (*boolFlagImpl) Set(s string) error { return nil }
+func (*boolFlagImpl) extra()             {}
+
+// A copy of flag.boolFlag interface, without a dependency.
+// Must appear first, so that it becomes the owner of the Set methods.
+type boolFlag interface {
+	flagValue
+	extra()
+}
+
+// A copy of flag.Value, without adding a dependency.
+type flagValue interface {
+	Set(string) error
+}
+
+func main() {
+	var x flagValue = new(stringFlagImpl)
+	x.Set("")
+
+	var y boolFlag = new(boolFlagImpl)
+	y.Set("")
+}
+
+// WANT:
+// Dynamic calls
+//   main --> (*boolFlagImpl).Set
+//   main --> (*boolFlagImpl).Set
+//   main --> (*stringFlagImpl).Set