Skip to content

Commit 732a8f6

Browse files
authored
Merge pull request #1276 from nevkontakte/gng2
Support collecting instances across packages in typeparams.Collector.
2 parents 4d56d3f + 9ecdb18 commit 732a8f6

File tree

5 files changed

+170
-16
lines changed

5 files changed

+170
-16
lines changed

compiler/internal/typeparams/collect.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func ToSlice(tpl *types.TypeParamList) []*types.TypeParam {
9999
// function, Resolver must be provided mapping the type parameters into concrete
100100
// types.
101101
type visitor struct {
102-
instances *InstanceSet
102+
instances *PackageInstanceSets
103103
resolver *Resolver
104104
info *types.Info
105105
}
@@ -216,11 +216,11 @@ func (c *seedVisitor) Visit(n ast.Node) ast.Visitor {
216216
type Collector struct {
217217
TContext *types.Context
218218
Info *types.Info
219-
Instances *InstanceSet
219+
Instances *PackageInstanceSets
220220
}
221221

222222
// Scan package files for generic instances.
223-
func (c *Collector) Scan(files ...*ast.File) {
223+
func (c *Collector) Scan(pkg *types.Package, files ...*ast.File) {
224224
if c.Info.Instances == nil || c.Info.Defs == nil {
225225
panic(fmt.Errorf("types.Info must have Instances and Defs populated"))
226226
}
@@ -240,8 +240,8 @@ func (c *Collector) Scan(files ...*ast.File) {
240240
ast.Walk(&sc, file)
241241
}
242242

243-
for !c.Instances.exhausted() {
244-
inst, _ := c.Instances.next()
243+
for iset := c.Instances.Pkg(pkg); !iset.exhausted(); {
244+
inst, _ := iset.next()
245245
switch typ := inst.Object.Type().(type) {
246246
case *types.Signature:
247247
v := visitor{

compiler/internal/typeparams/collect_test.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,12 @@ func TestVisitor(t *testing.T) {
252252
for _, test := range tests {
253253
t.Run(test.descr, func(t *testing.T) {
254254
v := visitor{
255-
instances: &InstanceSet{},
255+
instances: &PackageInstanceSets{},
256256
resolver: test.resolver,
257257
info: info,
258258
}
259259
ast.Walk(&v, test.node)
260-
got := v.instances.Values()
260+
got := v.instances.Pkg(pkg).Values()
261261
if diff := cmp.Diff(test.want, got, instanceOpts()); diff != "" {
262262
t.Errorf("Discovered instance diff (-want,+got):\n%s", diff)
263263
}
@@ -285,7 +285,7 @@ func TestSeedVisitor(t *testing.T) {
285285

286286
sv := seedVisitor{
287287
visitor: visitor{
288-
instances: &InstanceSet{},
288+
instances: &PackageInstanceSets{},
289289
resolver: nil,
290290
info: info,
291291
},
@@ -317,7 +317,7 @@ func TestSeedVisitor(t *testing.T) {
317317
tInst(types.Typ[types.Int64]),
318318
mInst(types.Typ[types.Int64]),
319319
}
320-
got := sv.instances.Values()
320+
got := sv.instances.Pkg(pkg).Values()
321321
if diff := cmp.Diff(want, got, instanceOpts()); diff != "" {
322322
t.Errorf("Instances from initialSeeder contain diff (-want,+got):\n%s", diff)
323323
}
@@ -349,9 +349,9 @@ func TestCollector(t *testing.T) {
349349
c := Collector{
350350
TContext: types.NewContext(),
351351
Info: info,
352-
Instances: &InstanceSet{},
352+
Instances: &PackageInstanceSets{},
353353
}
354-
c.Scan(file)
354+
c.Scan(pkg, file)
355355

356356
inst := func(name string, tArg types.Type) Instance {
357357
return Instance{
@@ -371,12 +371,76 @@ func TestCollector(t *testing.T) {
371371
inst("fun", types.Typ[types.Int64]),
372372
inst("fun.nested", types.Typ[types.Int64]),
373373
}
374-
got := c.Instances.Values()
374+
got := c.Instances.Pkg(pkg).Values()
375375
if diff := cmp.Diff(want, got, instanceOpts()); diff != "" {
376376
t.Errorf("Instances from initialSeeder contain diff (-want,+got):\n%s", diff)
377377
}
378378
}
379379

380+
func TestCollector_CrossPackage(t *testing.T) {
381+
f := srctesting.New(t)
382+
const src = `package foo
383+
type X[T any] struct {Value T}
384+
385+
func F[G any](g G) {
386+
x := X[G]{}
387+
println(x)
388+
}
389+
390+
func DoFoo() {
391+
F(int8(8))
392+
}
393+
`
394+
fooFile := f.Parse("foo.go", src)
395+
_, fooPkg := f.Check("pkg/foo", fooFile)
396+
397+
const src2 = `package bar
398+
import "pkg/foo"
399+
func FProxy[T any](t T) {
400+
foo.F[T](t)
401+
}
402+
func DoBar() {
403+
FProxy(int16(16))
404+
}
405+
`
406+
barFile := f.Parse("bar.go", src2)
407+
_, barPkg := f.Check("pkg/bar", barFile)
408+
409+
c := Collector{
410+
TContext: types.NewContext(),
411+
Info: f.Info,
412+
Instances: &PackageInstanceSets{},
413+
}
414+
c.Scan(barPkg, barFile)
415+
c.Scan(fooPkg, fooFile)
416+
417+
inst := func(pkg *types.Package, name string, tArg types.BasicKind) Instance {
418+
return Instance{
419+
Object: srctesting.LookupObj(pkg, name),
420+
TArgs: []types.Type{types.Typ[tArg]},
421+
}
422+
}
423+
424+
wantFooInstances := []Instance{
425+
inst(fooPkg, "F", types.Int16), // Found in "pkg/foo".
426+
inst(fooPkg, "F", types.Int8),
427+
inst(fooPkg, "X", types.Int16), // Found due to F[int16] found in "pkg/foo".
428+
inst(fooPkg, "X", types.Int8),
429+
}
430+
gotFooInstances := c.Instances.Pkg(fooPkg).Values()
431+
if diff := cmp.Diff(wantFooInstances, gotFooInstances, instanceOpts()); diff != "" {
432+
t.Errorf("Instances from pkg/foo contain diff (-want,+got):\n%s", diff)
433+
}
434+
435+
wantBarInstances := []Instance{
436+
inst(barPkg, "FProxy", types.Int16),
437+
}
438+
gotBarInstances := c.Instances.Pkg(barPkg).Values()
439+
if diff := cmp.Diff(wantBarInstances, gotBarInstances, instanceOpts()); diff != "" {
440+
t.Errorf("Instances from pkg/foo contain diff (-want,+got):\n%s", diff)
441+
}
442+
}
443+
380444
func TestResolver_SubstituteSelection(t *testing.T) {
381445
tests := []struct {
382446
descr string

compiler/internal/typeparams/instance.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,33 @@ func (iset *InstanceSet) exhausted() bool { return len(iset.values) <= iset.unpr
137137
func (iset *InstanceSet) Values() []Instance {
138138
return iset.values
139139
}
140+
141+
// PackageInstanceSets stores an InstanceSet for each package in a program, keyed
142+
// by import path.
143+
type PackageInstanceSets map[string]*InstanceSet
144+
145+
// Pkg returns InstanceSet for objects defined in the given package.
146+
func (i PackageInstanceSets) Pkg(pkg *types.Package) *InstanceSet {
147+
path := pkg.Path()
148+
iset, ok := i[path]
149+
if !ok {
150+
iset = &InstanceSet{}
151+
i[path] = iset
152+
}
153+
return iset
154+
}
155+
156+
// Add instances to the appropriate package's set. Automatically initialized
157+
// new per-package sets upon a first encounter.
158+
func (i PackageInstanceSets) Add(instances ...Instance) {
159+
for _, inst := range instances {
160+
i.Pkg(inst.Object.Pkg()).Add(inst)
161+
}
162+
}
163+
164+
// ID returns a unique numeric identifier assigned to an instance in the set.
165+
//
166+
// See: InstanceSet.ID().
167+
func (i PackageInstanceSets) ID(inst Instance) int {
168+
return i.Pkg(inst.Object.Pkg()).ID(inst)
169+
}

compiler/internal/typeparams/instance_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,63 @@ func TestInstanceQueue(t *testing.T) {
204204
t.Errorf("set.Values() returned diff (-want,+got):\n%s", diff)
205205
}
206206
}
207+
208+
func TestInstancesByPackage(t *testing.T) {
209+
f := srctesting.New(t)
210+
211+
const src1 = `package foo
212+
type Typ[T any, V any] []T
213+
`
214+
_, foo := f.Check("pkg/foo", f.Parse("foo.go", src1))
215+
216+
const src2 = `package bar
217+
func Fun[U any, W any](x, y U) {}
218+
`
219+
_, bar := f.Check("pkg/bar", f.Parse("bar.go", src2))
220+
221+
i1 := Instance{
222+
Object: foo.Scope().Lookup("Typ"),
223+
TArgs: []types.Type{types.Typ[types.String], types.Typ[types.String]},
224+
}
225+
i2 := Instance{
226+
Object: foo.Scope().Lookup("Typ"),
227+
TArgs: []types.Type{types.Typ[types.Int], types.Typ[types.Int]},
228+
}
229+
i3 := Instance{
230+
Object: bar.Scope().Lookup("Fun"),
231+
TArgs: []types.Type{types.Typ[types.String], types.Typ[types.String]},
232+
}
233+
234+
t.Run("Add", func(t *testing.T) {
235+
instByPkg := PackageInstanceSets{}
236+
instByPkg.Add(i1, i2, i3)
237+
238+
gotFooInstances := instByPkg.Pkg(foo).Values()
239+
wantFooInstances := []Instance{i1, i2}
240+
if diff := cmp.Diff(wantFooInstances, gotFooInstances, instanceOpts()); diff != "" {
241+
t.Errorf("instByPkg.Pkg(foo).Values() returned diff (-want,+got):\n%s", diff)
242+
}
243+
244+
gotValues := instByPkg.Pkg(bar).Values()
245+
wantValues := []Instance{i3}
246+
if diff := cmp.Diff(wantValues, gotValues, instanceOpts()); diff != "" {
247+
t.Errorf("instByPkg.Pkg(bar).Values() returned diff (-want,+got):\n%s", diff)
248+
}
249+
})
250+
251+
t.Run("ID", func(t *testing.T) {
252+
instByPkg := PackageInstanceSets{}
253+
instByPkg.Add(i1, i2, i3)
254+
255+
got := []int{
256+
instByPkg.ID(i1),
257+
instByPkg.ID(i2),
258+
instByPkg.ID(i3),
259+
}
260+
want := []int{0, 1, 0}
261+
262+
if diff := cmp.Diff(want, got); diff != "" {
263+
t.Errorf("unexpected instance IDs assigned (-want,+got):\n%s", diff)
264+
}
265+
})
266+
}

compiler/package.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type pkgContext struct {
3939
minify bool
4040
fileSet *token.FileSet
4141
errList ErrorList
42-
instanceSet *typeparams.InstanceSet
42+
instanceSet *typeparams.PackageInstanceSets
4343
}
4444

4545
// funcContext maintains compiler context for a specific function (lexical scope?).
@@ -215,11 +215,11 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor
215215
tc := typeparams.Collector{
216216
TContext: config.Context,
217217
Info: typesInfo,
218-
Instances: &typeparams.InstanceSet{},
218+
Instances: &typeparams.PackageInstanceSets{},
219219
}
220-
tc.Scan(simplifiedFiles...)
220+
tc.Scan(typesPkg, simplifiedFiles...)
221221
instancesByObj := map[types.Object][]typeparams.Instance{}
222-
for _, inst := range tc.Instances.Values() {
222+
for _, inst := range tc.Instances.Pkg(typesPkg).Values() {
223223
instancesByObj[inst.Object] = append(instancesByObj[inst.Object], inst)
224224
}
225225

0 commit comments

Comments
 (0)