go/ssa: fix *SelectorExpr within *IndexExpr handling
When a *IndexExpr or *IndexListExpr expr is over a *SelectorExpr and
expr denotes an instantiation, build expr as the *SelectorExpr.
Fixes golang/go#52834
Change-Id: I9a69ac28a6e8fb0ee9eb45db8675872b75d69a0f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/405555
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Tim King <taking@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 5c2a2ef..b36775a 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -840,16 +840,15 @@
panic("unexpected expression-relative selector")
case *typeparams.IndexListExpr:
- if ident, ok := e.X.(*ast.Ident); ok {
- // IndexListExpr is an instantiation. It will be handled by the *Ident case.
- return b.expr(fn, ident)
+ // f[X, Y] must be a generic function
+ if !instance(fn.info, e.X) {
+ panic("unexpected expression-could not match index list to instantiation")
}
+ return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
+
case *ast.IndexExpr:
- if ident, ok := e.X.(*ast.Ident); ok {
- if _, ok := typeparams.GetInstances(fn.info)[ident]; ok {
- // If the IndexExpr is an instantiation, it will be handled by the *Ident case.
- return b.expr(fn, ident)
- }
+ if instance(fn.info, e.X) {
+ return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
}
// not a generic instantiation.
switch t := fn.typeOf(e.X).Underlying().(type) {
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index 6b9c798..ba9aaf7 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -20,6 +20,7 @@
"strings"
"testing"
+ "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
@@ -823,3 +824,56 @@
})
}
}
+
+// TestGenericFunctionSelector ensures generic functions from other packages can be selected.
+func TestGenericFunctionSelector(t *testing.T) {
+ if !typeparams.Enabled {
+ t.Skip("TestGenericFunctionSelector uses type parameters.")
+ }
+
+ pkgs := map[string]map[string]string{
+ "main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`},
+ "a": {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `},
+ }
+
+ for _, mode := range []ssa.BuilderMode{
+ ssa.SanityCheckFunctions,
+ ssa.SanityCheckFunctions | ssa.InstantiateGenerics,
+ } {
+ conf := loader.Config{
+ Build: buildutil.FakeContext(pkgs),
+ }
+ conf.Import("main")
+
+ lprog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed: %s", err)
+ }
+ if lprog == nil {
+ t.Fatalf("Load returned nil *Program")
+ }
+ // Create and build SSA
+ prog := ssautil.CreateProgram(lprog, mode)
+ p := prog.Package(lprog.Package("main").Pkg)
+ p.Build()
+
+ var callees []string // callees of the CallInstruction.String() in main().
+ for _, b := range p.Func("main").Blocks {
+ for _, i := range b.Instrs {
+ if call, ok := i.(ssa.CallInstruction); ok {
+ if callee := call.Common().StaticCallee(); call != nil {
+ callees = append(callees, callee.String())
+ } else {
+ t.Errorf("CallInstruction without StaticCallee() %q", call)
+ }
+ }
+ }
+ }
+ sort.Strings(callees) // ignore the order in the code.
+
+ want := "[a.F[[int]] a.G[[int string]] a.H[[int]]]"
+ if got := fmt.Sprint(callees); got != want {
+ t.Errorf("Expected main() to contain calls %v. got %v", want, got)
+ }
+ }
+}
diff --git a/go/ssa/util.go b/go/ssa/util.go
index dfeaeeb..80c7d5c 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -175,6 +175,24 @@
return typeparams.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
}
+// instance returns whether an expression is a simple or qualified identifier
+// that is a generic instantiation.
+func instance(info *types.Info, expr ast.Expr) bool {
+ // Compare the logic here against go/types.instantiatedIdent,
+ // which also handles *IndexExpr and *IndexListExpr.
+ var id *ast.Ident
+ switch x := expr.(type) {
+ case *ast.Ident:
+ id = x
+ case *ast.SelectorExpr:
+ id = x.Sel
+ default:
+ return false
+ }
+ _, ok := typeparams.GetInstances(info)[id]
+ return ok
+}
+
// instanceArgs returns the Instance[id].TypeArgs as a slice.
func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
targList := typeparams.GetInstances(info)[id].TypeArgs