go/ssa: return nil on parameterized types on MethodValue.

MethodValue now checks if a type is parameterized types and returns nil instead of proceeding. Parameterized types are not runtime types so they should not have method sets created or be added to Prog.methodSet. This is similar to what MethodValue previously did for interfaces.

Updates golang/go#48525

Change-Id: Ib9026ddc0167fd71afd3e5c194aadf20411b9cdf
Reviewed-on: https://go-review.googlesource.com/c/tools/+/400515
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/go/ssa/create.go b/go/ssa/create.go
index 0ab2fe1..403baae 100644
--- a/go/ssa/create.go
+++ b/go/ssa/create.go
@@ -24,15 +24,16 @@
 // mode controls diagnostics and checking during SSA construction.
 func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
 	prog := &Program{
-		Fset:      fset,
-		imported:  make(map[string]*Package),
-		packages:  make(map[*types.Package]*Package),
-		thunks:    make(map[selectionKey]*Function),
-		bounds:    make(map[boundsKey]*Function),
-		mode:      mode,
-		canon:     newCanonizer(),
-		ctxt:      typeparams.NewContext(),
-		instances: make(map[*Function]*instanceSet),
+		Fset:          fset,
+		imported:      make(map[string]*Package),
+		packages:      make(map[*types.Package]*Package),
+		thunks:        make(map[selectionKey]*Function),
+		bounds:        make(map[boundsKey]*Function),
+		mode:          mode,
+		canon:         newCanonizer(),
+		ctxt:          typeparams.NewContext(),
+		instances:     make(map[*Function]*instanceSet),
+		parameterized: tpWalker{seen: make(map[types.Type]bool)},
 	}
 
 	h := typeutil.MakeHasher() // protected by methodsMu, in effect
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index 7955ed3..aedb41f 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -9,11 +9,13 @@
 import (
 	"fmt"
 	"go/types"
+
+	"golang.org/x/tools/internal/typeparams"
 )
 
 // MethodValue returns the Function implementing method sel, building
 // wrapper methods on demand.  It returns nil if sel denotes an
-// abstract (interface) method.
+// abstract (interface or parameterized) method.
 //
 // Precondition: sel.Kind() == MethodVal.
 //
@@ -26,17 +28,26 @@
 	}
 	T := sel.Recv()
 	if isInterface(T) {
-		return nil // abstract method
+		return nil // abstract method (interface)
 	}
 	if prog.mode&LogSource != 0 {
 		defer logStack("MethodValue %s %v", T, sel)()
 	}
 
+	var m *Function
 	b := builder{created: &creator{}}
+
 	prog.methodsMu.Lock()
-	m := prog.addMethod(prog.createMethodSet(T), sel, b.created)
+	// Checks whether a type param is reachable from T.
+	// This is an expensive check. May need to be optimized later.
+	if !prog.parameterized.isParameterized(T) {
+		m = prog.addMethod(prog.createMethodSet(T), sel, b.created)
+	}
 	prog.methodsMu.Unlock()
 
+	if m == nil {
+		return nil // abstract method (generic)
+	}
 	for !b.done() {
 		b.buildCreated()
 		b.needsRuntimeTypes()
@@ -64,6 +75,11 @@
 // Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
 // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
 func (prog *Program) createMethodSet(T types.Type) *methodSet {
+	if prog.mode&SanityCheckFunctions != 0 {
+		if isInterface(T) || prog.parameterized.isParameterized(T) {
+			panic("type is interface or parameterized")
+		}
+	}
 	mset, ok := prog.methodSets.At(T).(*methodSet)
 	if !ok {
 		mset = &methodSet{mapping: make(map[string]*Function)}
@@ -73,6 +89,7 @@
 }
 
 // Adds any created functions to cr.
+// Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
 // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
 func (prog *Program) addMethod(mset *methodSet, sel *types.Selection, cr *creator) *Function {
 	if sel.Kind() == types.MethodExpr {
@@ -144,7 +161,7 @@
 // Precondition: T is not a method signature (*Signature with Recv()!=nil).
 // Precondition: T is not parameterized.
 //
-// Thread-safe.  (Called via emitConv from multiple builder goroutines.)
+// Thread-safe.  (Called via Package.build from multiple builder goroutines.)
 //
 // TODO(adonovan): make this faster.  It accounts for 20% of SSA build time.
 //
@@ -156,6 +173,7 @@
 }
 
 // Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Precondition: T is not parameterized.
 // Recursive case: skip => don't create methods for T.
 //
 // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
@@ -241,6 +259,12 @@
 			prog.needMethods(t.At(i).Type(), false, cr)
 		}
 
+	case *typeparams.TypeParam:
+		panic(T) // type parameters are always abstract.
+
+	case *typeparams.Union:
+		// nop
+
 	default:
 		panic(T)
 	}
diff --git a/go/ssa/methods_test.go b/go/ssa/methods_test.go
new file mode 100644
index 0000000..8391cf6
--- /dev/null
+++ b/go/ssa/methods_test.go
@@ -0,0 +1,96 @@
+// Copyright 2022 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 ssa_test
+
+import (
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"testing"
+
+	"golang.org/x/tools/go/ssa"
+	"golang.org/x/tools/go/ssa/ssautil"
+	"golang.org/x/tools/internal/typeparams"
+)
+
+// Tests that MethodValue returns the expected method.
+func TestMethodValue(t *testing.T) {
+	if !typeparams.Enabled {
+		t.Skip("TestMethodValue requires type parameters")
+	}
+	input := `
+package p
+
+type I interface{ M() }
+
+type S int
+func (S) M() {}
+type R[T any] struct{ S }
+
+var i I
+var s S
+var r R[string]
+
+func selections[T any]() {
+	_ = i.M
+	_ = s.M
+	_ = r.M
+
+	var v R[T]
+	_ = v.M
+}
+`
+
+	// Parse the file.
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "input.go", input, 0)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	// Build an SSA program from the parsed file.
+	p, info, err := ssautil.BuildPackage(&types.Config{}, fset,
+		types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	// Collect all of the *types.Selection in the function "selections".
+	var selections []*types.Selection
+	for _, decl := range f.Decls {
+		if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == "selections" {
+			for _, stmt := range fn.Body.List {
+				if assign, ok := stmt.(*ast.AssignStmt); ok {
+					sel := assign.Rhs[0].(*ast.SelectorExpr)
+					selections = append(selections, info.Selections[sel])
+				}
+			}
+		}
+	}
+
+	wants := map[string]string{
+		"method (p.S) M()":         "(p.S).M",
+		"method (p.R[string]) M()": "(p.R[string]).M",
+		"method (p.I) M()":         "nil", // interface
+		"method (p.R[T]) M()":      "nil", // parameterized
+	}
+	if len(wants) != len(selections) {
+		t.Fatalf("Wanted %d selections. got %d", len(wants), len(selections))
+	}
+	for _, selection := range selections {
+		var got string
+		if m := p.Prog.MethodValue(selection); m != nil {
+			got = m.String()
+		} else {
+			got = "nil"
+		}
+		if want := wants[selection.String()]; want != got {
+			t.Errorf("p.Prog.MethodValue(%s) expected %q. got %q", selection, want, got)
+		}
+	}
+}
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 97c745b..fe3f497 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -30,12 +30,13 @@
 	canon *canonizer          // type canonicalization map
 	ctxt  *typeparams.Context // cache for type checking instantiations
 
-	methodsMu    sync.Mutex                 // guards the following maps:
-	methodSets   typeutil.Map               // maps type to its concrete methodSet
-	runtimeTypes typeutil.Map               // types for which rtypes are needed
-	bounds       map[boundsKey]*Function    // bounds for curried x.Method closures
-	thunks       map[selectionKey]*Function // thunks for T.Method expressions
-	instances    map[*Function]*instanceSet // instances of generic functions
+	methodsMu     sync.Mutex                 // guards the following maps:
+	methodSets    typeutil.Map               // maps type to its concrete methodSet
+	runtimeTypes  typeutil.Map               // types for which rtypes are needed
+	bounds        map[boundsKey]*Function    // bounds for curried x.Method closures
+	thunks        map[selectionKey]*Function // thunks for T.Method expressions
+	instances     map[*Function]*instanceSet // instances of generic functions
+	parameterized tpWalker                   // determines whether a type is parameterized.
 }
 
 // A Package is a single analyzed Go package containing Members for