Skip to content

Commit

Permalink
go/ssa: Adds datastructures for function instantiation.
Browse files Browse the repository at this point in the history
Adds [unexported] fields to Function for Origin, TypeParams, and TypeArguments. Populates TypeParameters for package level functions and methods.

Adds datastructures for creating function instantiations. Tracking unique instantiations on Program.

Adds map for canonicalizing lists of types.

Updates golang/go#48525

Change-Id: I9cb01f2ed24a9cacf3a515444d0cc0474333e417
Reviewed-on: https://go-review.googlesource.com/c/tools/+/397714
Reviewed-by: Robert Findley <rfindley@google.com>
Trust: Tim King <taking@google.com>
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
  • Loading branch information
timothy-king committed Apr 8, 2022
1 parent ee2bc8b commit 7dd9f20
Show file tree
Hide file tree
Showing 13 changed files with 596 additions and 32 deletions.
37 changes: 37 additions & 0 deletions go/analysis/passes/buildssa/buildssa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/buildssa"
"golang.org/x/tools/internal/typeparams"
)

func Test(t *testing.T) {
Expand All @@ -27,3 +28,39 @@ func Test(t *testing.T) {
}
}
}

func TestGenericDecls(t *testing.T) {
if !typeparams.Enabled {
t.Skip("TestGenericDecls requires type parameters.")
}
testdata := analysistest.TestData()
result := analysistest.Run(t, testdata, buildssa.Analyzer, "b")[0].Result

ssainfo := result.(*buildssa.SSA)
got := fmt.Sprint(ssainfo.SrcFuncs)
want := `[(*b.Pointer[T]).Load b.Load b.LoadPointer]`
if got != want {
t.Errorf("SSA.SrcFuncs = %s, want %s", got, want)
for _, f := range ssainfo.SrcFuncs {
f.WriteTo(os.Stderr)
}
}
}

func TestImporting(t *testing.T) {
if !typeparams.Enabled {
t.Skip("TestImporting depends on testdata/b/b/go which uses type parameters.")
}
testdata := analysistest.TestData()
result := analysistest.Run(t, testdata, buildssa.Analyzer, "c")[0].Result

ssainfo := result.(*buildssa.SSA)
got := fmt.Sprint(ssainfo.SrcFuncs)
want := `[c.A c.B]`
if got != want {
t.Errorf("SSA.SrcFuncs = %s, want %s", got, want)
for _, f := range ssainfo.SrcFuncs {
f.WriteTo(os.Stderr)
}
}
}
21 changes: 21 additions & 0 deletions go/analysis/passes/buildssa/testdata/src/b/b.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Package b contains declarations of generic functions.
package b

import "unsafe"

type Pointer[T any] struct {
v unsafe.Pointer
}

func (x *Pointer[T]) Load() *T {
return (*T)(LoadPointer(&x.v))
}

func Load[T any](x *Pointer[T]) *T {
return x.Load()
}

func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)

// TODO(taking): Add calls from non-generic functions to instantiations of generic functions.
// TODO(taking): Add globals with types that are instantiations of generic functions.
18 changes: 18 additions & 0 deletions go/analysis/passes/buildssa/testdata/src/c/c.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Package c is to test buildssa importing packages.
package c

import (
"a"
"b"
"unsafe"
)

func A() {
_ = a.Fib(10)
}

func B() {
var x int
ptr := unsafe.Pointer(&x)
_ = b.LoadPointer(&ptr)
}
3 changes: 2 additions & 1 deletion go/ssa/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,7 @@ func (b *builder) buildFunctionBody(fn *Function) {
var functype *ast.FuncType
switch n := fn.syntax.(type) {
case nil:
// TODO(taking): Temporarily this can be the body of a generic function.
return // not a Go source function. (Synthetic, or from object file.)
case *ast.FuncDecl:
functype = n.Type
Expand Down Expand Up @@ -2351,7 +2352,7 @@ func (p *Package) build() {
// TODO(adonovan): ideally belongs in memberFromObject, but
// that would require package creation in topological order.
for name, mem := range p.Members {
if ast.IsExported(name) {
if ast.IsExported(name) && !isGeneric(mem) {
p.Prog.needMethodsOf(mem.Type(), &p.created)
}
}
Expand Down
63 changes: 63 additions & 0 deletions go/ssa/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/internal/typeparams"
)

func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
Expand Down Expand Up @@ -498,3 +499,65 @@ func h(error)
t.Errorf("expected a single Phi (for the range index), got %d", phis)
}
}

// TestGenericDecls ensures that *unused* generic types, methods and functions
// signatures can be built.
//
// TODO(taking): Add calls from non-generic functions to instantiations of generic functions.
// TODO(taking): Add globals with types that are instantiations of generic functions.
func TestGenericDecls(t *testing.T) {
if !typeparams.Enabled {
t.Skip("TestGenericDecls only works with type parameters enabled.")
}
const input = `
package p
import "unsafe"
type Pointer[T any] struct {
v unsafe.Pointer
}
func (x *Pointer[T]) Load() *T {
return (*T)(LoadPointer(&x.v))
}
func Load[T any](x *Pointer[T]) *T {
return x.Load()
}
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
`
// The SSA members for this package should look something like this:
// func LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer)
// type Pointer struct{v unsafe.Pointer}
// method (*Pointer[T any]) Load() *T
// func init func()
// var init$guard bool

// Parse
var conf loader.Config
f, err := conf.ParseFile("<input>", input)
if err != nil {
t.Fatalf("parse: %v", err)
}
conf.CreateFromFiles("p", f)

// Load
lprog, err := conf.Load()
if err != nil {
t.Fatalf("Load: %v", err)
}

// Create and build SSA
prog := ssautil.CreateProgram(lprog, 0)
p := prog.Package(lprog.Package("p").Pkg)
p.Build()

if load := p.Func("Load"); typeparams.ForSignature(load.Signature).Len() != 1 {
t.Errorf("expected a single type param T for Load got %q", load.Signature)
}
if ptr := p.Type("Pointer"); typeparams.ForNamed(ptr.Type().(*types.Named)).Len() != 1 {
t.Errorf("expected a single type param T for Pointer got %q", ptr.Type())
}
}
51 changes: 37 additions & 14 deletions go/ssa/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"sync"

"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
)

// NewProgram returns a new SSA Program.
Expand All @@ -24,12 +25,15 @@ import (
//
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[*types.Func]*Function),
mode: mode,
Fset: fset,
imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
thunks: make(map[selectionKey]*Function),
bounds: make(map[*types.Func]*Function),
mode: mode,
canon: newCanonizer(),
ctxt: typeparams.NewContext(),
instances: make(map[*Function]*instanceSet),
}

h := typeutil.MakeHasher() // protected by methodsMu, in effect
Expand Down Expand Up @@ -85,20 +89,39 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}

// Collect type parameters if this is a generic function/method.
var tparams []*typeparams.TypeParam
for i, rtparams := 0, typeparams.RecvTypeParams(sig); i < rtparams.Len(); i++ {
tparams = append(tparams, rtparams.At(i))
}
for i, sigparams := 0, typeparams.ForSignature(sig); i < sigparams.Len(); i++ {
tparams = append(tparams, sigparams.At(i))
}

fn := &Function{
name: name,
object: obj,
Signature: sig,
syntax: syntax,
pos: obj.Pos(),
Pkg: pkg,
Prog: pkg.Prog,
info: pkg.info,
name: name,
object: obj,
Signature: sig,
syntax: syntax,
pos: obj.Pos(),
Pkg: pkg,
Prog: pkg.Prog,
info: pkg.info,
_TypeParams: tparams,
}
pkg.created.Add(fn)
if syntax == nil {
fn.Synthetic = "loaded from gc object file"
}
if len(tparams) > 0 {
fn.Prog.createInstanceSet(fn)
}
if len(tparams) > 0 && syntax != nil {
fn.Synthetic = "generic function"
// TODO(taking): Allow for the function to be built once type params are supported.
fn.syntax = nil // Treating as an external function temporarily.
}

pkg.objects[obj] = fn
if sig.Recv() == nil {
Expand Down
1 change: 1 addition & 0 deletions go/ssa/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ func (f *Function) finishBody() {
// clear remaining stateful variables
f.namedResults = nil // (used by lifting)
f.info = nil
f.subst = nil

numberRegisters(f)
}
Expand Down
Loading

0 comments on commit 7dd9f20

Please sign in to comment.