Skip to content

Commit 5bcfc68

Browse files
Updating DCE to support nested types
1 parent d4d3cf7 commit 5bcfc68

File tree

13 files changed

+749
-134
lines changed

13 files changed

+749
-134
lines changed

compiler/compiler_test.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,179 @@ func TestDeclSelection_RemoveUnusedTypeConstraint(t *testing.T) {
364364
sel.IsDead(`var:command-line-arguments.ghost`)
365365
}
366366

367+
func TestDeclSelection_RemoveUnusedNestedTypesInFunction(t *testing.T) {
368+
src := `
369+
package main
370+
func Foo[T any](u T) any {
371+
type Bar struct { v T }
372+
return Bar{v: u}
373+
}
374+
func deadCode() {
375+
println(Foo[int](42))
376+
}
377+
func main() {
378+
println(Foo[string]("cat"))
379+
}`
380+
381+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
382+
sel := declSelection(t, srcFiles, nil)
383+
sel.IsAlive(`func:command-line-arguments.main`)
384+
385+
sel.IsAlive(`funcVar:command-line-arguments.Foo`)
386+
sel.IsAlive(`func:command-line-arguments.Foo<string>`)
387+
sel.IsDead(`func:command-line-arguments.Foo<int>`)
388+
389+
sel.IsAlive(`typeVar:command-line-arguments.Bar`)
390+
sel.IsAlive(`type:command-line-arguments.Bar<string;>`)
391+
sel.IsDead(`type:command-line-arguments.Bar<int;>`)
392+
393+
sel.IsDead(`funcVar:command-line-arguments.deadCode`)
394+
sel.IsDead(`func:command-line-arguments.deadCode`)
395+
}
396+
397+
func TestDeclSelection_RemoveUnusedNestedTypesInMethod(t *testing.T) {
398+
src := `
399+
package main
400+
type Baz[T any] struct{}
401+
func (b *Baz[T]) Foo(u T) any {
402+
type Bar struct { v T }
403+
return Bar{v: u}
404+
}
405+
func deadCode() {
406+
b := Baz[int]{}
407+
println(b.Foo(42))
408+
}
409+
func main() {
410+
b := Baz[string]{}
411+
println(b.Foo("cat"))
412+
}`
413+
414+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
415+
sel := declSelection(t, srcFiles, nil)
416+
sel.IsAlive(`func:command-line-arguments.main`)
417+
418+
sel.IsAlive(`typeVar:command-line-arguments.Baz`)
419+
sel.IsDead(`type:command-line-arguments.Baz<int>`)
420+
sel.IsAlive(`type:command-line-arguments.Baz<string>`)
421+
422+
sel.IsDead(`func:command-line-arguments.(*Baz).Foo<int>`)
423+
sel.IsAlive(`func:command-line-arguments.(*Baz).Foo<string>`)
424+
425+
sel.IsAlive(`typeVar:command-line-arguments.Bar`)
426+
sel.IsDead(`type:command-line-arguments.Bar<int;>`)
427+
sel.IsAlive(`type:command-line-arguments.Bar<string;>`)
428+
429+
sel.IsDead(`funcVar:command-line-arguments.deadCode`)
430+
sel.IsDead(`func:command-line-arguments.deadCode`)
431+
}
432+
433+
func TestDeclSelection_RemoveAllUnusedNestedTypes(t *testing.T) {
434+
src := `
435+
package main
436+
func Foo[T any](u T) any {
437+
type Bar struct { v T }
438+
return Bar{v: u}
439+
}
440+
func deadCode() {
441+
println(Foo[int](42))
442+
println(Foo[string]("cat"))
443+
}
444+
func main() {}`
445+
446+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
447+
sel := declSelection(t, srcFiles, nil)
448+
sel.IsAlive(`func:command-line-arguments.main`)
449+
450+
sel.IsDead(`funcVar:command-line-arguments.Foo`)
451+
sel.IsDead(`func:command-line-arguments.Foo<string>`)
452+
sel.IsDead(`func:command-line-arguments.Foo<int>`)
453+
454+
sel.IsDead(`typeVar:command-line-arguments.Bar`)
455+
sel.IsDead(`type:command-line-arguments.Bar<string;>`)
456+
sel.IsDead(`type:command-line-arguments.Bar<int;>`)
457+
458+
sel.IsDead(`funcVar:command-line-arguments.deadCode`)
459+
sel.IsDead(`func:command-line-arguments.deadCode`)
460+
}
461+
462+
func TestDeclSelection_CompletelyRemoveNestedType(t *testing.T) {
463+
src := `
464+
package main
465+
func Foo[T any](u T) any {
466+
type Bar struct { v T }
467+
return Bar{v: u}
468+
}
469+
func deadCode() {
470+
println(Foo[int](42))
471+
}
472+
func main() {}`
473+
474+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
475+
sel := declSelection(t, srcFiles, nil)
476+
477+
sel.IsAlive(`func:command-line-arguments.main`)
478+
479+
sel.IsDead(`funcVar:command-line-arguments.Foo`)
480+
sel.IsDead(`func:command-line-arguments.Foo<int>`)
481+
482+
sel.IsDead(`typeVar:command-line-arguments.Bar`)
483+
sel.IsDead(`type:command-line-arguments.Bar<int;>`)
484+
485+
sel.IsDead(`funcVar:command-line-arguments.deadCode`)
486+
sel.IsDead(`func:command-line-arguments.deadCode`)
487+
}
488+
489+
func TestDeclSelection_RemoveAnonNestedTypes(t *testing.T) {
490+
// Based on test/fixedbugs/issue53635.go
491+
// This checks that if an anon type (e.g. []T) is used in a function
492+
// that is not used, the type is removed, otherwise it is kept.
493+
494+
src := `
495+
package main
496+
func Foo[T any](u T) any {
497+
return []T(nil)
498+
}
499+
func deadCode() {
500+
println(Foo[string]("cat"))
501+
}
502+
func main() {
503+
println(Foo[int](42))
504+
}`
505+
506+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
507+
sel := declSelection(t, srcFiles, nil)
508+
sel.IsDead(`anonType:command-line-arguments.sliceType`) // []string
509+
sel.IsAlive(`anonType:command-line-arguments.sliceType$1`) // []int
510+
}
511+
512+
func TestDeclSelection_NoNestAppliedToFuncCallInMethod(t *testing.T) {
513+
// Checks that a function call to a non-local function isn't
514+
// being labeled as a nested function call.
515+
src := `
516+
package main
517+
func foo(a any) {
518+
println(a)
519+
}
520+
type Bar[T any] struct { u T }
521+
func (b *Bar[T]) Baz() {
522+
foo(b.u)
523+
}
524+
func main() {
525+
b := &Bar[int]{u: 42}
526+
b.Baz()
527+
}`
528+
529+
srcFiles := []srctesting.Source{{Name: `main.go`, Contents: []byte(src)}}
530+
sel := declSelection(t, srcFiles, nil)
531+
sel.IsAlive(`init:main`)
532+
533+
sel.IsAlive(`typeVar:command-line-arguments.Bar`)
534+
sel.IsAlive(`type:command-line-arguments.Bar<int>`)
535+
sel.IsAlive(`func:command-line-arguments.(*Bar).Baz<int>`)
536+
537+
sel.IsAlive(`func:command-line-arguments.foo`)
538+
}
539+
367540
func TestLengthParenthesizingIssue841(t *testing.T) {
368541
// See issue https://github.com/gopherjs/gopherjs/issues/841
369542
//

compiler/declNames.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,17 @@ func funcDeclFullName(inst typeparams.Instance) string {
4949
}
5050

5151
// typeVarDeclFullName returns a unique name for a package-level variable
52-
// that is used for a named type declaration.
52+
// that is used for a named type declaration or a named nested type declaration.
5353
// If the type is generic, this declaration name is also for the list
5454
// of instantiations of the type.
5555
func typeVarDeclFullName(o *types.TypeName) string {
5656
return `typeVar:` + symbol.New(o).String()
5757
}
5858

5959
// typeDeclFullName returns a unique name for a package-level type declaration
60-
// for the given instance of a type. Names are only unique per package.
60+
// for the given instance of a type. Names are only unique per scope package
61+
// unless the type is a nested type then the name is only unique per the
62+
// function or method it is declared in.
6163
func typeDeclFullName(inst typeparams.Instance) string {
6264
return `type:` + inst.String()
6365
}

compiler/decls.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func (fc *funcContext) newVarDecl(init *types.Initializer) *Decl {
264264
fc.localVars = nil // Clean up after ourselves.
265265
})
266266

267-
d.Dce().SetName(init.Lhs[0])
267+
d.Dce().SetName(init.Lhs[0], nil, nil)
268268
if len(init.Lhs) != 1 || analysis.HasSideEffect(init.Rhs, fc.pkgCtx.Info.Info) {
269269
d.Dce().SetAsAlive()
270270
}
@@ -291,7 +291,7 @@ func (fc *funcContext) funcDecls(functions []*ast.FuncDecl) ([]*Decl, error) {
291291
FullName: funcVarDeclFullName(o),
292292
Vars: []string{objName},
293293
}
294-
varDecl.Dce().SetName(o)
294+
varDecl.Dce().SetName(o, nil, nil)
295295
if o.Type().(*types.Signature).TypeParams().Len() != 0 {
296296
varDecl.DeclCode = fc.CatchOutput(0, func() {
297297
fc.Printf("%s = {};", objName)
@@ -331,7 +331,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance)
331331
Blocking: fc.pkgCtx.IsBlocking(inst),
332332
LinkingName: symbol.New(o),
333333
}
334-
d.Dce().SetName(o, inst.TArgs...)
334+
d.Dce().SetName(o, inst.TNest, inst.TArgs)
335335

336336
if typesutil.IsMethod(o) {
337337
recv := typesutil.RecvType(o.Type().(*types.Signature)).Obj()
@@ -433,6 +433,7 @@ func (fc *funcContext) newNamedTypeVarDecl(obj *types.TypeName) *Decl {
433433
FullName: typeVarDeclFullName(obj),
434434
Vars: []string{name},
435435
}
436+
varDecl.Dce().SetName(obj, nil, nil)
436437
if fc.pkgCtx.instanceSet.Pkg(obj.Pkg()).ObjHasInstances(obj) {
437438
varDecl.DeclCode = fc.CatchOutput(0, func() {
438439
fc.Printf("%s = {};", name)
@@ -470,7 +471,7 @@ func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, er
470471
d := &Decl{
471472
FullName: typeDeclFullName(inst),
472473
}
473-
d.Dce().SetName(inst.Object, inst.TArgs...)
474+
d.Dce().SetName(inst.Object, inst.TNest, inst.TArgs)
474475
fc.pkgCtx.CollectDCEDeps(d, func() {
475476
// Code that declares a JS type (i.e. prototype) for each Go type.
476477
d.DeclCode = fc.CatchOutput(0, func() {
@@ -595,7 +596,7 @@ func (fc *funcContext) anonTypeDecls(anonTypes []*types.TypeName) []*Decl {
595596
FullName: anonTypeDeclFullName(t),
596597
Vars: []string{t.Name()},
597598
}
598-
d.Dce().SetName(t)
599+
d.Dce().SetName(t, nil, nil)
599600
fc.pkgCtx.CollectDCEDeps(d, func() {
600601
d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), fc.initArgs(t.Type())))
601602
})

compiler/expressions.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression {
591591
case types.MethodVal:
592592
return fc.formatExpr(`$methodVal(%s, "%s")`, fc.makeReceiver(e), sel.Obj().(*types.Func).Name())
593593
case types.MethodExpr:
594-
fc.pkgCtx.DeclareDCEDep(sel.Obj(), inst.TArgs...)
594+
fc.pkgCtx.DeclareDCEDep(sel.Obj(), inst.TNest, inst.TArgs)
595595
if _, ok := sel.Recv().Underlying().(*types.Interface); ok {
596596
return fc.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name())
597597
}
@@ -906,7 +906,7 @@ func (fc *funcContext) delegatedCall(expr *ast.CallExpr) (callable *expression,
906906
func (fc *funcContext) makeReceiver(e *ast.SelectorExpr) *expression {
907907
sel, _ := fc.selectionOf(e)
908908
if !sel.Obj().Exported() {
909-
fc.pkgCtx.DeclareDCEDep(sel.Obj())
909+
fc.pkgCtx.DeclareDCEDep(sel.Obj(), nil, nil)
910910
}
911911

912912
x := e.X

compiler/internal/dce/README.md

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -265,17 +265,32 @@ of either will indicate a use of both. This will cause slightly more unexported
265265
methods to be alive while reducing the complication of type checking which object
266266
or type of object is performing the call.
267267

268-
| Declaration | exported | unexported | non-generic | generic | object name | method name |
269-
|:------------|:--------:|:----------:|:-----------:|:-------:|:------------|:------------|
270-
| variables |||| n/a | `<package>.<var name>` | |
271-
| functions |||| | `<package>.<func name>` | |
272-
| functions ||| || `<package>.<func name>[<type args>]` | |
273-
| named type |||| | `<package>.<type name>` | |
274-
| named type ||| || `<package>.<type name>[<type args>]` | |
275-
| method || || | `<package>.<receiver name>` | |
276-
| method || | || `<package>.<receiver name>[<type args>]` | |
277-
| method | ||| | `<package>.<receiver name>` | `<package>.<method name>(<parameter types>)(<result types>)` |
278-
| method | || || `<package>.<receiver name>[<type args>]` | `<package>.<method name>(<parameter types>)(<result types>)` |
268+
| Declaration | exported | unexported | generic | object name | method name |
269+
|:------------|:--------:|:----------:|:-------:|:------------|:------------|
270+
| variables ||| | `<package>.<var name>` | |
271+
| functions ||| | `<package>.<func name>` | |
272+
| functions |||| `<package>.<func name>[<type args>]` | |
273+
| named type ||| | `<package>.<type name>` | |
274+
| named type |||| `<package>.<type name>[<type args>]` | |
275+
| method || | | `<package>.<receiver name>` | |
276+
| method || || `<package>.<receiver name>[<type args>]` | |
277+
| method | || | `<package>.<receiver name>` | `<package>.<method name>(<parameter types>)(<result types>)` |
278+
| method | ||| `<package>.<receiver name>[<type args>]` | `<package>.<method name>(<parameter types>)(<result types>)` |
279+
280+
For nested types, the nest (the function or method in which the type is
281+
declared) needs to be taken into consideration to differentiate types with
282+
the same name but different nests.
283+
284+
| Nest type | generic nest | generic object | object name |
285+
|:----------|:------------:|:--------------:|:------------|
286+
| function | | | `<package>.<func name>:<type name>` |
287+
| function || | `<package>.<func name>:<type name>[<nest args>;]` |
288+
| function | || `<package>.<func name>:<type name>[<type args>]` |
289+
| function ||| `<package>.<func name>:<type name>[<nest args>;<type args>]` |
290+
| method | | | `<package>.<receiver name>:<method name>:<type name>` |
291+
| method || | `<package>.<receiver name>:<method name>:<type name>[<nest args>;]` |
292+
| method | || `<package>.<receiver name>:<method name>:<type name>[<type args>]` |
293+
| method ||| `<package>.<receiver name>:<method name>:<type name>[<nest args>;<type args>]` |
279294

280295
#### Name Specifics
281296

compiler/internal/dce/collector.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func (c *Collector) CollectDCEDeps(decl Decl, f func()) {
3939
// The given optional type arguments are used to when the object is a
4040
// function with type parameters or anytime the object doesn't carry them.
4141
// If not given, this attempts to get the type arguments from the object.
42-
func (c *Collector) DeclareDCEDep(o types.Object, tArgs ...types.Type) {
42+
func (c *Collector) DeclareDCEDep(o types.Object, tNest, tArgs []types.Type) {
4343
if c.dce != nil {
44-
c.dce.addDep(o, tArgs)
44+
c.dce.addDep(o, tNest, tArgs)
4545
}
4646
}

0 commit comments

Comments
 (0)