diff --git a/compiler/decls.go b/compiler/decls.go index 91f6b15fc..b6427a697 100644 --- a/compiler/decls.go +++ b/compiler/decls.go @@ -339,7 +339,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance) } fc.pkgCtx.CollectDCEDeps(d, func() { - d.DeclCode = fc.translateTopLevelFunction(fun, inst) + d.DeclCode = fc.namedFuncContext(inst).translateTopLevelFunction(fun) }) return d } diff --git a/compiler/internal/dce/collector.go b/compiler/internal/dce/collector.go index 7d251029b..7b2342eeb 100644 --- a/compiler/internal/dce/collector.go +++ b/compiler/internal/dce/collector.go @@ -14,7 +14,7 @@ type Decl interface { // Collector is a tool to collect dependencies for a declaration // that'll be used in dead-code elimination (DCE). type Collector struct { - dependencies map[types.Object]struct{} + dce *Info } // CollectDCEDeps captures a list of Go objects (types, functions, etc.) @@ -24,23 +24,21 @@ type Collector struct { // Only one CollectDCEDeps call can be active at a time. // This will overwrite any previous dependencies collected for the given DCE. func (c *Collector) CollectDCEDeps(decl Decl, f func()) { - if c.dependencies != nil { + if c.dce != nil { panic(errors.New(`called CollectDCEDeps inside another CollectDCEDeps call`)) } - c.dependencies = make(map[types.Object]struct{}) - defer func() { c.dependencies = nil }() + c.dce = decl.Dce() + defer func() { c.dce = nil }() f() - - decl.Dce().setDeps(c.dependencies) } // DeclareDCEDep records that the code that is currently being transpiled // depends on a given Go object. func (c *Collector) DeclareDCEDep(o types.Object) { - if c.dependencies == nil { + if c.dce == nil { return // Dependencies are not being collected. } - c.dependencies[o] = struct{}{} + c.dce.addDep(o) } diff --git a/compiler/internal/dce/dce_test.go b/compiler/internal/dce/dce_test.go index c46a7f03c..f60a986ba 100644 --- a/compiler/internal/dce/dce_test.go +++ b/compiler/internal/dce/dce_test.go @@ -75,43 +75,35 @@ func Test_Collector_Collecting(t *testing.T) { func Test_Info_SetNameAndDep(t *testing.T) { tests := []struct { - name string - obj types.Object - want Info // expected Info after SetName - wantDep string // expected dep after addDep + name string + obj types.Object + wantObjFilter string // expected Info after SetName + wantMetFilter string // expected Info after SetName + wantDep string // expected dep after addDep }{ { name: `package`, obj: parseObject(t, `Sarah`, `package jim import Sarah "fmt"`), - want: Info{ - importPath: `jim`, - objectFilter: `Sarah`, - }, - wantDep: `jim.Sarah`, + wantObjFilter: `jim.Sarah`, + wantDep: `jim.Sarah`, }, { name: `exposed var`, obj: parseObject(t, `Toby`, `package jim var Toby float64`), - want: Info{ - importPath: `jim`, - objectFilter: `Toby`, - }, - wantDep: `jim.Toby`, + wantObjFilter: `jim.Toby`, + wantDep: `jim.Toby`, }, { name: `exposed const`, obj: parseObject(t, `Ludo`, `package jim const Ludo int = 42`), - want: Info{ - importPath: `jim`, - objectFilter: `Ludo`, - }, - wantDep: `jim.Ludo`, + wantObjFilter: `jim.Ludo`, + wantDep: `jim.Ludo`, }, { name: `label`, @@ -125,55 +117,40 @@ func Test_Info_SetNameAndDep(t *testing.T) { goto Gobo } }`), - want: Info{ - importPath: `jim`, - objectFilter: `Gobo`, - }, - wantDep: `jim.Gobo`, + wantObjFilter: `jim.Gobo`, + wantDep: `jim.Gobo`, }, { name: `exposed specific type`, obj: parseObject(t, `Jen`, `package jim type Jen struct{}`), - want: Info{ - importPath: `jim`, - objectFilter: `Jen`, - }, - wantDep: `jim.Jen`, + wantObjFilter: `jim.Jen`, + wantDep: `jim.Jen`, }, { name: `exposed generic type`, obj: parseObject(t, `Henson`, `package jim type Henson[T comparable] struct{}`), - want: Info{ - importPath: `jim`, - objectFilter: `Henson`, - }, - wantDep: `jim.Henson`, + wantObjFilter: `jim.Henson`, + wantDep: `jim.Henson`, }, { name: `exposed specific function`, obj: parseObject(t, `Jareth`, `package jim func Jareth() {}`), - want: Info{ - importPath: `jim`, - objectFilter: `Jareth`, - }, - wantDep: `jim.Jareth`, + wantObjFilter: `jim.Jareth`, + wantDep: `jim.Jareth`, }, { name: `exposed generic function`, obj: parseObject(t, `Didymus`, `package jim func Didymus[T comparable]() {}`), - want: Info{ - importPath: `jim`, - objectFilter: `Didymus`, - }, - wantDep: `jim.Didymus`, + wantObjFilter: `jim.Didymus`, + wantDep: `jim.Didymus`, }, { name: `exposed specific method`, @@ -181,11 +158,8 @@ func Test_Info_SetNameAndDep(t *testing.T) { `package jim type Fizzgig string func (f Fizzgig) Kira() {}`), - want: Info{ - importPath: `jim`, - objectFilter: `Fizzgig`, - }, - wantDep: `jim.Kira~`, + wantObjFilter: `jim.Fizzgig`, + wantDep: `jim.Kira~`, }, { name: `unexposed specific method`, @@ -193,12 +167,9 @@ func Test_Info_SetNameAndDep(t *testing.T) { `package jim type Aughra int func (a Aughra) frank() {}`), - want: Info{ - importPath: `jim`, - objectFilter: `Aughra`, - methodFilter: `frank~`, - }, - wantDep: `jim.frank~`, + wantObjFilter: `jim.Aughra`, + wantMetFilter: `jim.frank~`, + wantDep: `jim.frank~`, }, { name: `specific method on unexposed type`, @@ -206,11 +177,8 @@ func Test_Info_SetNameAndDep(t *testing.T) { `package jim type wembley struct{} func (w wembley) Red() {}`), - want: Info{ - importPath: `jim`, - objectFilter: `wembley`, - }, - wantDep: `jim.Red~`, + wantObjFilter: `jim.wembley`, + wantDep: `jim.Red~`, }, } @@ -223,11 +191,10 @@ func Test_Info_SetNameAndDep(t *testing.T) { t.Log(`object:`, types.ObjectString(tt.obj, nil)) d.Dce().SetName(tt.obj) - equal(t, d.Dce().unnamed(), tt.want.unnamed()) - equal(t, d.Dce().importPath, tt.want.importPath) - equal(t, d.Dce().objectFilter, tt.want.objectFilter) - equal(t, d.Dce().methodFilter, tt.want.methodFilter) - equal(t, d.Dce().String(), tt.want.String()) + equal(t, d.Dce().unnamed(), false) + objectFilter, methodFilter := d.Dce().getInfoNames() + equal(t, objectFilter, tt.wantObjFilter) + equal(t, methodFilter, tt.wantMetFilter) }) } }) @@ -235,14 +202,14 @@ func Test_Info_SetNameAndDep(t *testing.T) { t.Run(`addDep`, func(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d := &testDecl{} + d1 := &testDecl{} t.Log(`object:`, types.ObjectString(tt.obj, nil)) - d.Dce().setDeps(map[types.Object]struct{}{ - tt.obj: {}, - }) - equal(t, len(d.Dce().deps), 1) - equal(t, d.Dce().deps[0], tt.wantDep) + d1.Dce().addDep(tt.obj) + equal(t, len(d1.Dce().deps), 1) + depNames := d1.Dce().getDepNames() + equal(t, len(depNames), 1) + equal(t, depNames[0], tt.wantDep) }) } }) diff --git a/compiler/internal/dce/info.go b/compiler/internal/dce/info.go index f25344552..b8a770739 100644 --- a/compiler/internal/dce/info.go +++ b/compiler/internal/dce/info.go @@ -20,20 +20,8 @@ type Info struct { // obj is the Go object the declaration this DCE is for. obj types.Object - // importPath is the package path of the package the declaration is in. - importPath string - - // Symbol's identifier used by the dead-code elimination logic, not including - // package path. If empty, the symbol is assumed to be alive and will not be - // eliminated. For methods it is the same as its receiver type identifier. - objectFilter string - - // The second part of the identified used by dead-code elimination for methods. - // Empty for other types of symbols. - methodFilter string - - // deps is the set of DCE info objects that this DCE depends on. - deps map[*Info]struct{} + // deps is the set of Go objects that this DCE depends on. + deps map[types.Object]struct{} } // String gets a human-readable representation of the DCE info. @@ -45,17 +33,19 @@ func (d *Info) String() string { if d.unnamed() { tags += `[unnamed] ` } - fullName := d.importPath + `.` + d.objectFilter - if len(d.methodFilter) > 0 { - fullName += `.` + d.methodFilter + objectName, methodName := d.getInfoNames() + fullName := objectName + if len(methodName) > 0 { + objectName += ` &` + methodName } - return tags + fullName + ` -> [` + strings.Join(d.deps, `, `) + `]` + depNames := `[` + strings.Join(d.getDepNames(), `, `) + `]` + return tags + fullName + ` -> ` + depNames } // unnamed returns true if SetName has not been called for this declaration. // This indicates that the DCE is not initialized. func (d *Info) unnamed() bool { - return d.objectFilter == `` && d.methodFilter == `` + return d.obj == nil } // isAlive returns true if the declaration is marked as alive. @@ -80,23 +70,33 @@ func (d *Info) SetName(o types.Object) { if !d.unnamed() { panic(fmt.Errorf(`may only set the name once for %s`, d.String())) } + d.obj = o +} + +// addDep adds a declaration dependency for the declaration this +// DCE info is attached to. +func (d *Info) addDep(dep types.Object) { + d.deps[dep] = struct{}{} +} - d.importPath = o.Pkg().Path() +func (d *Info) getInfoNames() (objectFilter, methodFilter string) { + o := d.obj + importPath := o.Pkg().Path() if typesutil.IsMethod(o) { recv := typesutil.RecvType(o.Type().(*types.Signature)).Obj() - d.objectFilter = recv.Name() + objectFilter = importPath + `.` + recv.Name() if !o.Exported() { - d.methodFilter = o.Name() + `~` + methodFilter = importPath + `.` + o.Name() + `~` } } else { - d.objectFilter = o.Name() + objectFilter = importPath + `.` + o.Name() } + return } func (d *Info) getDepNames() []string { depNames := make([]string, 0, len(d.deps)) - for dep := range d.deps { - o := dep.obj + for o := range d.deps { qualifiedName := o.Pkg().Path() + "." + o.Name() if typesutil.IsMethod(o) { qualifiedName += "~" @@ -106,9 +106,3 @@ func (d *Info) getDepNames() []string { sort.Strings(depNames) return depNames } - -// addDep adds a declaration dependency for the declaration this -// DCE info is attached to. -func (d *Info) addDep(dep *Info) { - d.deps[dep] = struct{}{} -} diff --git a/compiler/internal/dce/selector.go b/compiler/internal/dce/selector.go index 66f202bef..7616d22c6 100644 --- a/compiler/internal/dce/selector.go +++ b/compiler/internal/dce/selector.go @@ -41,13 +41,14 @@ func (s *Selector[D]) Include(decl D, implementsLink bool) { info := &declInfo[D]{decl: decl} - if dce.objectFilter != `` { - info.objectFilter = dce.importPath + `.` + dce.objectFilter + objectFilter, methodFilter := dce.getInfoNames() + if objectFilter != `` { + info.objectFilter = objectFilter s.byFilter[info.objectFilter] = append(s.byFilter[info.objectFilter], info) } - if dce.methodFilter != `` { - info.methodFilter = dce.importPath + `.` + dce.methodFilter + if methodFilter != `` { + info.methodFilter = methodFilter s.byFilter[info.methodFilter] = append(s.byFilter[info.methodFilter], info) } }