Skip to content

Commit 1e55371

Browse files
committed
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 <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Tim King <[email protected]> Reviewed-by: Zvonimir Pavlinovic <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 29d48d6 commit 1e55371

File tree

3 files changed

+79
-8
lines changed

3 files changed

+79
-8
lines changed

go/ssa/builder.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -840,16 +840,15 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
840840
panic("unexpected expression-relative selector")
841841

842842
case *typeparams.IndexListExpr:
843-
if ident, ok := e.X.(*ast.Ident); ok {
844-
// IndexListExpr is an instantiation. It will be handled by the *Ident case.
845-
return b.expr(fn, ident)
843+
// f[X, Y] must be a generic function
844+
if !instance(fn.info, e.X) {
845+
panic("unexpected expression-could not match index list to instantiation")
846846
}
847+
return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
848+
847849
case *ast.IndexExpr:
848-
if ident, ok := e.X.(*ast.Ident); ok {
849-
if _, ok := typeparams.GetInstances(fn.info)[ident]; ok {
850-
// If the IndexExpr is an instantiation, it will be handled by the *Ident case.
851-
return b.expr(fn, ident)
852-
}
850+
if instance(fn.info, e.X) {
851+
return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
853852
}
854853
// not a generic instantiation.
855854
switch t := fn.typeOf(e.X).Underlying().(type) {

go/ssa/builder_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strings"
2121
"testing"
2222

23+
"golang.org/x/tools/go/buildutil"
2324
"golang.org/x/tools/go/loader"
2425
"golang.org/x/tools/go/ssa"
2526
"golang.org/x/tools/go/ssa/ssautil"
@@ -823,3 +824,56 @@ func sliceMax(s []int) []int { return s[a():b():c()] }
823824
})
824825
}
825826
}
827+
828+
// TestGenericFunctionSelector ensures generic functions from other packages can be selected.
829+
func TestGenericFunctionSelector(t *testing.T) {
830+
if !typeparams.Enabled {
831+
t.Skip("TestGenericFunctionSelector uses type parameters.")
832+
}
833+
834+
pkgs := map[string]map[string]string{
835+
"main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`},
836+
"a": {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `},
837+
}
838+
839+
for _, mode := range []ssa.BuilderMode{
840+
ssa.SanityCheckFunctions,
841+
ssa.SanityCheckFunctions | ssa.InstantiateGenerics,
842+
} {
843+
conf := loader.Config{
844+
Build: buildutil.FakeContext(pkgs),
845+
}
846+
conf.Import("main")
847+
848+
lprog, err := conf.Load()
849+
if err != nil {
850+
t.Errorf("Load failed: %s", err)
851+
}
852+
if lprog == nil {
853+
t.Fatalf("Load returned nil *Program")
854+
}
855+
// Create and build SSA
856+
prog := ssautil.CreateProgram(lprog, mode)
857+
p := prog.Package(lprog.Package("main").Pkg)
858+
p.Build()
859+
860+
var callees []string // callees of the CallInstruction.String() in main().
861+
for _, b := range p.Func("main").Blocks {
862+
for _, i := range b.Instrs {
863+
if call, ok := i.(ssa.CallInstruction); ok {
864+
if callee := call.Common().StaticCallee(); call != nil {
865+
callees = append(callees, callee.String())
866+
} else {
867+
t.Errorf("CallInstruction without StaticCallee() %q", call)
868+
}
869+
}
870+
}
871+
}
872+
sort.Strings(callees) // ignore the order in the code.
873+
874+
want := "[a.F[[int]] a.G[[int string]] a.H[[int]]]"
875+
if got := fmt.Sprint(callees); got != want {
876+
t.Errorf("Expected main() to contain calls %v. got %v", want, got)
877+
}
878+
}
879+
}

go/ssa/util.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ func recvAsFirstArg(sig *types.Signature) *types.Signature {
175175
return typeparams.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
176176
}
177177

178+
// instance returns whether an expression is a simple or qualified identifier
179+
// that is a generic instantiation.
180+
func instance(info *types.Info, expr ast.Expr) bool {
181+
// Compare the logic here against go/types.instantiatedIdent,
182+
// which also handles *IndexExpr and *IndexListExpr.
183+
var id *ast.Ident
184+
switch x := expr.(type) {
185+
case *ast.Ident:
186+
id = x
187+
case *ast.SelectorExpr:
188+
id = x.Sel
189+
default:
190+
return false
191+
}
192+
_, ok := typeparams.GetInstances(info)[id]
193+
return ok
194+
}
195+
178196
// instanceArgs returns the Instance[id].TypeArgs as a slice.
179197
func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
180198
targList := typeparams.GetInstances(info)[id].TypeArgs

0 commit comments

Comments
 (0)