Skip to content

[WIP] Generic type nesting #1368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
72498bf
Adding test to demonstrate issue in nesting
grantnelson-wf Apr 15, 2025
e82fa78
working on identifying where nesting types should get injected
grantnelson-wf Apr 23, 2025
d81a16a
Merge branch 'master' of github.com:gopherjs/gopherjs into genNestedT…
grantnelson-wf Apr 24, 2025
7f949b7
trying to inject nesting types into visitor
grantnelson-wf Apr 25, 2025
624f159
trying to inject nesting types into visitor
grantnelson-wf Apr 25, 2025
9783473
Updating subst
grantnelson-wf May 1, 2025
6581339
Using TypeParamLists
grantnelson-wf May 2, 2025
79a034b
working on nesting types
grantnelson-wf May 5, 2025
e671c77
working on nesting types
grantnelson-wf May 5, 2025
c642f18
working on nesting types
grantnelson-wf May 5, 2025
7e10671
working on nesting types
grantnelson-wf May 6, 2025
0991c16
working on nesting types
grantnelson-wf May 6, 2025
b706b60
working on nesting types
grantnelson-wf May 6, 2025
329e497
working on nesting types
grantnelson-wf May 6, 2025
a94000c
working on nesting types
grantnelson-wf May 6, 2025
c477608
working on nesting types
grantnelson-wf May 6, 2025
16089bc
working on nesting types
grantnelson-wf May 6, 2025
963fe43
working on nesting types
grantnelson-wf May 6, 2025
8073c31
working on nesting types
grantnelson-wf May 7, 2025
c70f619
working on nesting types
grantnelson-wf May 7, 2025
d280c4e
working on nesting types
grantnelson-wf May 7, 2025
d17b6d2
working on nesting types
grantnelson-wf May 7, 2025
925c2bc
working on nesting types
grantnelson-wf May 7, 2025
9cf91d8
working on nesting types
grantnelson-wf May 8, 2025
adcc226
working on nesting types
grantnelson-wf May 8, 2025
23bf10f
working on nesting types
grantnelson-wf May 8, 2025
65600a4
working on nesting types
grantnelson-wf May 9, 2025
2774069
working on nesting types
grantnelson-wf May 9, 2025
f838cea
working on nesting types
grantnelson-wf May 9, 2025
fc32f21
working on nesting types
grantnelson-wf May 9, 2025
f554343
working on nesting types
grantnelson-wf May 9, 2025
79ea77f
working on nesting types
grantnelson-wf May 9, 2025
e8387aa
working on nesting types
grantnelson-wf May 9, 2025
acc62b7
working on nesting types
grantnelson-wf May 9, 2025
9ffd9e4
working on nesting types
grantnelson-wf May 9, 2025
0a8fb71
working on nesting types
grantnelson-wf May 12, 2025
79abcdd
fixing issues in nesting
grantnelson-wf May 12, 2025
b20cb21
fixing some issues in nesting
grantnelson-wf May 14, 2025
7982109
Merge branch 'nestedTypes' of github.com:Workiva/gopherjs into genNes…
grantnelson-wf May 14, 2025
f2aa738
fixing some issues in nesting
grantnelson-wf May 14, 2025
479ff41
Working on resolver improvements
grantnelson-wf May 20, 2025
b00dc85
Working on resolver improvements
grantnelson-wf May 20, 2025
b788e19
Working on resolver improvements
grantnelson-wf May 20, 2025
6a4f2ca
Working on resolver improvements
grantnelson-wf May 20, 2025
51c6a03
Working on resolver improvements
grantnelson-wf May 21, 2025
576e45d
Working on resolver improvements
grantnelson-wf May 21, 2025
67f9293
Merge branch 'master' of github.com:gopherjs/gopherjs into genNestedT…
grantnelson-wf May 22, 2025
7fad33d
Working on resolver improvements
grantnelson-wf May 22, 2025
5ad861b
Working on resolver improvements
grantnelson-wf May 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 8 additions & 23 deletions compiler/decls.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ func (fc *funcContext) newVarDecl(init *types.Initializer) *Decl {
fc.localVars = nil // Clean up after ourselves.
})

d.Dce().SetName(init.Lhs[0])
d.Dce().SetName(init.Lhs[0], nil, nil)
if len(init.Lhs) != 1 || analysis.HasSideEffect(init.Rhs, fc.pkgCtx.Info.Info) {
d.Dce().SetAsAlive()
}
Expand All @@ -291,7 +291,7 @@ func (fc *funcContext) funcDecls(functions []*ast.FuncDecl) ([]*Decl, error) {
FullName: funcVarDeclFullName(o),
Vars: []string{objName},
}
varDecl.Dce().SetName(o)
varDecl.Dce().SetName(o, nil, nil)
if o.Type().(*types.Signature).TypeParams().Len() != 0 {
varDecl.DeclCode = fc.CatchOutput(0, func() {
fc.Printf("%s = {};", objName)
Expand Down Expand Up @@ -331,7 +331,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance)
Blocking: fc.pkgCtx.IsBlocking(inst),
LinkingName: symbol.New(o),
}
d.Dce().SetName(o, inst.TArgs...)
d.Dce().SetName(o, inst.TNest, inst.TArgs)

if typesutil.IsMethod(o) {
recv := typesutil.RecvType(o.Type().(*types.Signature)).Obj()
Expand Down Expand Up @@ -451,35 +451,20 @@ func (fc *funcContext) newNamedTypeVarDecl(obj *types.TypeName) *Decl {
func (fc *funcContext) newNamedTypeInstDecl(inst typeparams.Instance) (*Decl, error) {
originType := inst.Object.Type().(*types.Named)

var nestResolver *typeparams.Resolver
if len(inst.TNest) > 0 {
fn := typeparams.FindNestingFunc(inst.Object)
tp := typeparams.SignatureTypeParams(fn.Type().(*types.Signature))
nestResolver = typeparams.NewResolver(fc.pkgCtx.typesCtx, tp, inst.TNest, nil)
}
fc.typeResolver = typeparams.NewResolver(fc.pkgCtx.typesCtx, originType.TypeParams(), inst.TArgs, nestResolver)
fc.typeResolver = typeparams.NewResolver(fc.pkgCtx.typesCtx, inst)
defer func() { fc.typeResolver = nil }()

instanceType := originType
if !inst.IsTrivial() {
if len(inst.TArgs) > 0 {
instantiated, err := types.Instantiate(fc.pkgCtx.typesCtx, originType, inst.TArgs, true)
if err != nil {
return nil, fmt.Errorf("failed to instantiate type %v with args %v: %w", originType, inst.TArgs, err)
}
instanceType = instantiated.(*types.Named)
}
if len(inst.TNest) > 0 {
instantiated := nestResolver.Substitute(instanceType)
instanceType = instantiated.(*types.Named)
}
instantiated := fc.typeResolver.Substitute(instanceType)
instanceType = instantiated.(*types.Named)
}

underlying := instanceType.Underlying()
d := &Decl{
FullName: typeDeclFullName(inst),
}
d.Dce().SetName(inst.Object, inst.TArgs...)
d.Dce().SetName(inst.Object, inst.TNest, inst.TArgs)
fc.pkgCtx.CollectDCEDeps(d, func() {
// Code that declares a JS type (i.e. prototype) for each Go type.
d.DeclCode = fc.CatchOutput(0, func() {
Expand Down Expand Up @@ -604,7 +589,7 @@ func (fc *funcContext) anonTypeDecls(anonTypes []*types.TypeName) []*Decl {
FullName: anonTypeDeclFullName(t),
Vars: []string{t.Name()},
}
d.Dce().SetName(t)
d.Dce().SetName(t, nil, nil)
fc.pkgCtx.CollectDCEDeps(d, func() {
d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), fc.initArgs(t.Type())))
})
Expand Down
4 changes: 2 additions & 2 deletions compiler/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression {
case types.MethodVal:
return fc.formatExpr(`$methodVal(%s, "%s")`, fc.makeReceiver(e), sel.Obj().(*types.Func).Name())
case types.MethodExpr:
fc.pkgCtx.DeclareDCEDep(sel.Obj(), inst.TArgs...)
fc.pkgCtx.DeclareDCEDep(sel.Obj(), inst.TNest, inst.TArgs)
if _, ok := sel.Recv().Underlying().(*types.Interface); ok {
return fc.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name())
}
Expand Down Expand Up @@ -906,7 +906,7 @@ func (fc *funcContext) delegatedCall(expr *ast.CallExpr) (callable *expression,
func (fc *funcContext) makeReceiver(e *ast.SelectorExpr) *expression {
sel, _ := fc.selectionOf(e)
if !sel.Obj().Exported() {
fc.pkgCtx.DeclareDCEDep(sel.Obj())
fc.pkgCtx.DeclareDCEDep(sel.Obj(), nil, nil)
}

x := e.X
Expand Down
11 changes: 1 addition & 10 deletions compiler/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,14 @@ func (fc *funcContext) nestedFunctionContext(info *analysis.FuncInfo, inst typep
flowDatas: map[*types.Label]*flowData{nil: {}},
caseCounter: 1,
labelCases: make(map[*types.Label]int),
typeResolver: fc.typeResolver,
typeResolver: typeparams.NewResolver(fc.pkgCtx.typesCtx, inst),
objectNames: map[types.Object]string{},
sig: &typesutil.Signature{Sig: sig},
}
for k, v := range fc.allVars {
c.allVars[k] = v
}

if sig.TypeParams().Len() > 0 {
c.typeResolver = typeparams.NewResolver(c.pkgCtx.typesCtx, sig.TypeParams(), inst.TArgs, nil)
} else if sig.RecvTypeParams().Len() > 0 {
c.typeResolver = typeparams.NewResolver(c.pkgCtx.typesCtx, sig.RecvTypeParams(), inst.TArgs, nil)
}
if c.objectNames == nil {
c.objectNames = map[types.Object]string{}
}

// Synthesize an identifier by which the function may reference itself. Since
// it appears in the stack trace, it's useful to include the receiver type in
// it.
Expand Down
6 changes: 1 addition & 5 deletions compiler/internal/analysis/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,7 @@ func (info *Info) newFuncInfoInstances(fd *ast.FuncDecl) []*FuncInfo {

funcInfos := make([]*FuncInfo, 0, len(instances))
for _, inst := range instances {
var resolver *typeparams.Resolver
if sig, ok := obj.Type().(*types.Signature); ok {
tp := typeparams.SignatureTypeParams(sig)
resolver = typeparams.NewResolver(info.typeCtx, tp, inst.TArgs, nil)
}
resolver := typeparams.NewResolver(info.typeCtx, inst)
fi := info.newFuncInfo(fd, inst.Object, inst.TArgs, resolver)
funcInfos = append(funcInfos, fi)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/internal/dce/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func (c *Collector) CollectDCEDeps(decl Decl, f func()) {
// The given optional type arguments are used to when the object is a
// function with type parameters or anytime the object doesn't carry them.
// If not given, this attempts to get the type arguments from the object.
func (c *Collector) DeclareDCEDep(o types.Object, tArgs ...types.Type) {
func (c *Collector) DeclareDCEDep(o types.Object, tNest, tArgs []types.Type) {
if c.dce != nil {
c.dce.addDep(o, tArgs)
c.dce.addDep(o, tNest, tArgs)
}
}
105 changes: 69 additions & 36 deletions compiler/internal/dce/dce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,34 @@ func Test_Collector_Collecting(t *testing.T) {
decl2 := quickTestDecl(obj2)
var c Collector

c.DeclareDCEDep(obj1) // no effect since a collection isn't running.
c.DeclareDCEDep(obj1, nil, nil) // no effect since a collection isn't running.
depCount(t, decl1, 0)
depCount(t, decl2, 0)

c.CollectDCEDeps(decl1, func() {
c.DeclareDCEDep(obj2)
c.DeclareDCEDep(obj3)
c.DeclareDCEDep(obj3) // already added so has no effect.
c.DeclareDCEDep(obj2, nil, nil)
c.DeclareDCEDep(obj3, nil, nil)
c.DeclareDCEDep(obj3, nil, nil) // already added so has no effect.
})
depCount(t, decl1, 2)
depCount(t, decl2, 0)

c.DeclareDCEDep(obj4) // no effect since a collection isn't running.
c.DeclareDCEDep(obj4, nil, nil) // no effect since a collection isn't running.
depCount(t, decl1, 2)
depCount(t, decl2, 0)

c.CollectDCEDeps(decl2, func() {
c.DeclareDCEDep(obj5)
c.DeclareDCEDep(obj6)
c.DeclareDCEDep(obj7)
c.DeclareDCEDep(obj5, nil, nil)
c.DeclareDCEDep(obj6, nil, nil)
c.DeclareDCEDep(obj7, nil, nil)
})
depCount(t, decl1, 2)
depCount(t, decl2, 3)

// The second collection adds to existing dependencies.
c.CollectDCEDeps(decl2, func() {
c.DeclareDCEDep(obj4)
c.DeclareDCEDep(obj5)
c.DeclareDCEDep(obj4, nil, nil)
c.DeclareDCEDep(obj5, nil, nil)
})
depCount(t, decl1, 2)
depCount(t, decl2, 4)
Expand Down Expand Up @@ -541,7 +541,7 @@ func Test_Info_SetNameAndDep(t *testing.T) {
equal(t, d.Dce().String(), `[unnamed] -> []`)
t.Log(`object:`, types.ObjectString(tt.obj, nil))

d.Dce().SetName(tt.obj)
d.Dce().SetName(tt.obj, nil, nil)
equal(t, d.Dce().unnamed(), tt.want.unnamed())
equal(t, d.Dce().objectFilter, tt.want.objectFilter)
equal(t, d.Dce().methodFilter, tt.want.methodFilter)
Expand All @@ -568,7 +568,7 @@ func Test_Info_SetNameAndDep(t *testing.T) {

c := Collector{}
c.CollectDCEDeps(d, func() {
c.DeclareDCEDep(tt.obj)
c.DeclareDCEDep(tt.obj, nil, nil)
})
equalSlices(t, d.Dce().getDeps(), wantDeps)
})
Expand All @@ -582,10 +582,10 @@ func Test_Info_SetNameOnlyOnce(t *testing.T) {
obj2 := quickVar(pkg, `Stripe`)

decl := &testDecl{}
decl.Dce().SetName(obj1)
decl.Dce().SetName(obj1, nil, nil)

err := capturePanic(t, func() {
decl.Dce().SetName(obj2)
decl.Dce().SetName(obj2, nil, nil)
})
errorMatches(t, err, `^may only set the name once for path/to/mogwai\.Gizmo .*$`)
}
Expand Down Expand Up @@ -743,7 +743,7 @@ func Test_Info_UsesDeps(t *testing.T) {

c := Collector{}
c.CollectDCEDeps(d, func() {
c.DeclareDCEDep(uses, tArgs...)
c.DeclareDCEDep(uses, nil, tArgs)
})
equalSlices(t, d.Dce().getDeps(), tt.wantDeps)
})
Expand All @@ -754,6 +754,7 @@ func Test_Info_SpecificCasesDeps(t *testing.T) {
tests := []struct {
name string
obj types.Object
tNest []types.Type
tArgs []types.Type
wantDeps []string
}{
Expand Down Expand Up @@ -808,16 +809,48 @@ func Test_Info_SpecificCasesDeps(t *testing.T) {
`astoria.shuffle(string) int`,
},
},
{
name: `a generic method with a nested concrete type instance`,
obj: parseObject(t, `quan`,
`package astoria
func data[T any](v T) any {
type quan struct { V T }
return quan{ V: v }
}`),
tNest: []types.Type{types.Typ[types.Int]},
// TODO(grantnelson-wf): This should take into account the nested type.
//wantDeps: []string{`astoria.quan[int;]`},
wantDeps: []string{`astoria.quan`},
},
{
name: `a generic method with a nested generic type instance`,
obj: parseObject(t, `matuszak`,
`package astoria
func sloth[T any]() any {
type matuszak[U any] struct { X T; Y U }
return matuszak[T]{}
}`),
tNest: []types.Type{types.Typ[types.String]},
tArgs: []types.Type{types.Typ[types.Bool]},
// TODO(grantnelson-wf): This should take into account the nested type.
//wantDeps: []string{`astoria.matuszak[string;bool]`},
wantDeps: []string{`astoria.matuszak[bool]`},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &testDecl{}
t.Logf(`object: %s with [%s]`, types.ObjectString(tt.obj, nil), (typesutil.TypeList)(tt.tArgs).String())
tail := ``
if len(tt.tNest) > 0 {
tail += (typesutil.TypeList)(tt.tNest).String() + `;`
}
tail += (typesutil.TypeList)(tt.tArgs).String()
t.Logf(`object: %s with [%s]`, types.ObjectString(tt.obj, nil), tail)

c := Collector{}
c.CollectDCEDeps(d, func() {
c.DeclareDCEDep(tt.obj, tt.tArgs...)
c.DeclareDCEDep(tt.obj, tt.tNest, tt.tArgs)
})
equalSlices(t, d.Dce().getDeps(), tt.wantDeps)
})
Expand All @@ -837,7 +870,7 @@ func Test_Info_SetAsAlive(t *testing.T) {
equal(t, decl.Dce().isAlive(), true) // still alive but now explicitly alive
equal(t, decl.Dce().String(), `[alive] [unnamed] -> []`)

decl.Dce().SetName(obj)
decl.Dce().SetName(obj, nil, nil)
equal(t, decl.Dce().isAlive(), true) // alive because SetAsAlive was called
equal(t, decl.Dce().String(), `[alive] path/to/fantasia.Falkor -> []`)
})
Expand All @@ -848,7 +881,7 @@ func Test_Info_SetAsAlive(t *testing.T) {
equal(t, decl.Dce().isAlive(), true) // unnamed is automatically alive
equal(t, decl.Dce().String(), `[unnamed] -> []`)

decl.Dce().SetName(obj)
decl.Dce().SetName(obj, nil, nil)
equal(t, decl.Dce().isAlive(), false) // named so no longer automatically alive
equal(t, decl.Dce().String(), `path/to/fantasia.Artax -> []`)

Expand Down Expand Up @@ -876,27 +909,27 @@ func Test_Selector_JustVars(t *testing.T) {

c := Collector{}
c.CollectDCEDeps(frodo, func() {
c.DeclareDCEDep(samwise.obj)
c.DeclareDCEDep(meri.obj)
c.DeclareDCEDep(pippin.obj)
c.DeclareDCEDep(samwise.obj, nil, nil)
c.DeclareDCEDep(meri.obj, nil, nil)
c.DeclareDCEDep(pippin.obj, nil, nil)
})
c.CollectDCEDeps(pippin, func() {
c.DeclareDCEDep(meri.obj)
c.DeclareDCEDep(meri.obj, nil, nil)
})
c.CollectDCEDeps(aragorn, func() {
c.DeclareDCEDep(boromir.obj)
c.DeclareDCEDep(boromir.obj, nil, nil)
})
c.CollectDCEDeps(gimli, func() {
c.DeclareDCEDep(legolas.obj)
c.DeclareDCEDep(legolas.obj, nil, nil)
})
c.CollectDCEDeps(legolas, func() {
c.DeclareDCEDep(gimli.obj)
c.DeclareDCEDep(gimli.obj, nil, nil)
})
c.CollectDCEDeps(gandalf, func() {
c.DeclareDCEDep(frodo.obj)
c.DeclareDCEDep(aragorn.obj)
c.DeclareDCEDep(gimli.obj)
c.DeclareDCEDep(legolas.obj)
c.DeclareDCEDep(frodo.obj, nil, nil)
c.DeclareDCEDep(aragorn.obj, nil, nil)
c.DeclareDCEDep(gimli.obj, nil, nil)
c.DeclareDCEDep(legolas.obj, nil, nil)
})

for _, decl := range fellowship {
Expand Down Expand Up @@ -1012,16 +1045,16 @@ func Test_Selector_SpecificMethods(t *testing.T) {

c := Collector{}
c.CollectDCEDeps(rincewindRun, func() {
c.DeclareDCEDep(rincewind.obj)
c.DeclareDCEDep(rincewind.obj, nil, nil)
})
c.CollectDCEDeps(rincewindHide, func() {
c.DeclareDCEDep(rincewind.obj)
c.DeclareDCEDep(rincewind.obj, nil, nil)
})
c.CollectDCEDeps(vimesRun, func() {
c.DeclareDCEDep(vimes.obj)
c.DeclareDCEDep(vimes.obj, nil, nil)
})
c.CollectDCEDeps(vimesRead, func() {
c.DeclareDCEDep(vimes.obj)
c.DeclareDCEDep(vimes.obj, nil, nil)
})
vetinari.Dce().SetAsAlive()

Expand Down Expand Up @@ -1058,7 +1091,7 @@ func Test_Selector_SpecificMethods(t *testing.T) {
vetinari.Dce().deps = nil // reset deps
c.CollectDCEDeps(vetinari, func() {
for _, decl := range tt.deps {
c.DeclareDCEDep(decl.obj)
c.DeclareDCEDep(decl.obj, nil, nil)
}
})

Expand Down Expand Up @@ -1095,7 +1128,7 @@ func testPackage(name string) *types.Package {

func quickTestDecl(o types.Object) *testDecl {
d := &testDecl{obj: o}
d.Dce().SetName(o)
d.Dce().SetName(o, nil, nil)
return d
}

Expand Down
Loading
Loading