Skip to content

Commit e671c77

Browse files
working on nesting types
1 parent 79a034b commit e671c77

File tree

3 files changed

+111
-88
lines changed

3 files changed

+111
-88
lines changed

internal/govendor/subst/export.go

Lines changed: 20 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package subst
55

66
import (
77
"fmt"
8-
"go/token"
98
"go/types"
109
"sort"
1110
"strings"
@@ -17,6 +16,7 @@ import (
1716

1817
// Subster performs type parameter substitution.
1918
type Subster struct {
19+
nest *Subster
2020
impl *subster
2121
}
2222

@@ -26,69 +26,22 @@ type Subster struct {
2626
// Using a nil Subster will always return the original type.
2727
// - Given type arguments should not contain any types in the type parameters.
2828
// - The internal implementation is not concurrency-safe.
29-
func New(tc *types.Context, tParams *types.TypeParamList, tArgs []types.Type) *Subster {
30-
return NewNested(tc, nil, nil, tParams, tArgs)
31-
}
32-
33-
// NewNested creates a new Subster with a given list of type parameters and
34-
// matching args for types nested within a function.
35-
//
36-
// - This may return a nil if there is no substitution to be done.
37-
// Using a nil Subster will always return the original type.
38-
// - The given context must be non-nil to cache types.
39-
// - The given function may be nil for any package level types.
40-
// It must be non-nil for any types nested within a function.
41-
// - The given function type arguments will instantiate the function
42-
// and be used on any nested types.
43-
// - Given type arguments should not contain any types in the type parameters.
44-
// - The internal implementation is not concurrency-safe.
45-
func NewNested(tc *types.Context, fn *types.Func, fnArgs []types.Type, tParams *types.TypeParamList, tArgs []types.Type) *Subster {
46-
assert(safeLen(tParams) == len(tArgs), "New() argument count must match")
47-
48-
fnParams := getFuncTypeParams(fn)
49-
assert(fnParams.Len() == len(fnArgs), "New() function argument count must match")
50-
51-
if safeLen(tParams) == 0 && len(tArgs) == 0 && safeLen(fnParams) == 0 && len(fnArgs) == 0 {
52-
return nil
53-
}
54-
55-
if fn == nil {
56-
fn = types.NewFunc(token.NoPos, nil, "$substPseudoFunc",
57-
types.NewSignatureType(nil, nil, nil, nil, nil, false))
29+
// - If a non-nil nest is given, this subster will use that nest to
30+
// perform substitution as well to allow for nested types.
31+
func New(tc *types.Context, tParams *types.TypeParamList, tArgs []types.Type, nest *Subster) *Subster {
32+
tpLen := 0
33+
if tParams != nil {
34+
tpLen = tParams.Len()
5835
}
5936

60-
subst := makeSubster(tc, fn, tParams, tArgs, false)
61-
for i := 0; i < fnParams.Len(); i++ {
62-
subst.replacements[fnParams.At(i)] = fnArgs[i]
63-
}
64-
return &Subster{impl: subst}
65-
}
37+
assert(tpLen == len(tArgs), "New() argument count must match")
6638

67-
func safeLen(tp interface{ Len() int }) int {
68-
if tp == nil {
69-
return 0
39+
if tpLen == 0 && len(tArgs) == 0 {
40+
return nest
7041
}
71-
return tp.Len()
72-
}
7342

74-
// getFuncTypeParams gets the type parameters of the given function.
75-
// It will return either the receiver type parameters,
76-
// the function type parameters, or nil if none found.
77-
func getFuncTypeParams(fn *types.Func) *types.TypeParamList {
78-
if fn == nil {
79-
return nil
80-
}
81-
sig := fn.Type().(*types.Signature)
82-
if sig == nil {
83-
return nil
84-
}
85-
if tps := sig.RecvTypeParams(); tps != nil && tps.Len() > 0 {
86-
return tps
87-
}
88-
if tps := sig.TypeParams(); tps != nil && tps.Len() > 0 {
89-
return tps
90-
}
91-
return nil
43+
subst := makeSubster(tc, nil, tParams, tArgs, false)
44+
return &Subster{nest: nest, impl: subst}
9245
}
9346

9447
// Type returns a version of typ with all references to type parameters replaced
@@ -97,6 +50,9 @@ func (s *Subster) Type(typ types.Type) types.Type {
9750
if s == nil {
9851
return typ
9952
}
53+
if s.nest != nil {
54+
typ = s.nest.Type(typ)
55+
}
10056
return s.impl.typ(typ)
10157
}
10258

@@ -112,5 +68,9 @@ func (s *Subster) String() string {
11268
parts = append(parts, fmt.Sprintf("%s->%s", tp, ta))
11369
}
11470
sort.Strings(parts)
115-
return `{` + strings.Join(parts, `, `) + `}`
71+
nestStr := ``
72+
if s.nest != nil {
73+
nestStr = s.nest.String() + `:`
74+
}
75+
return nestStr + `{` + strings.Join(parts, `, `) + `}`
11676
}

internal/govendor/subst/export_test.go

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ func TestNestedSubstInGenericFunction(t *testing.T) {
1919
2020
func D(){
2121
type E[V any] struct{X V}
22-
}`
22+
}
23+
24+
type F[W any] struct{X W}
25+
`
2326

2427
fSet := token.NewFileSet()
2528
f, err := parser.ParseFile(fSet, "hello.go", source, 0)
@@ -33,51 +36,105 @@ func TestNestedSubstInGenericFunction(t *testing.T) {
3336
t.Fatal(err)
3437
}
3538

39+
type namedType struct {
40+
name string // the name of the named type
41+
args []string // type expressions of args for the named type
42+
}
43+
3644
for _, test := range []struct {
37-
fnName string // the name of the nesting function
38-
fnArgs []string // type expressions of args for the nesting function
39-
stName string // the name of the named type
40-
stArgs []string // type expressions of args for the named type
41-
want string // expected underlying value after substitution
45+
nesting []namedType
46+
want string // expected underlying value after substitution
4247
}{
4348
{
44-
fnName: `A`, fnArgs: []string{`int`},
45-
stName: `B`, stArgs: []string{},
49+
nesting: []namedType{
50+
{name: `A`, args: []string{`int`}},
51+
},
4652
want: `struct{X int}`,
4753
},
4854
{
49-
fnName: `A`, fnArgs: []string{`int`},
50-
stName: `C`, stArgs: []string{`bool`},
55+
nesting: []namedType{
56+
{name: `A`, args: []string{`int`}},
57+
{name: `B`},
58+
},
59+
want: `struct{X int}`,
60+
},
61+
{
62+
nesting: []namedType{
63+
{name: `A`, args: []string{`int`}},
64+
{name: `C`, args: []string{`bool`}},
65+
},
5166
want: "struct{X int; Y bool}",
5267
},
5368
{
54-
fnName: `D`, fnArgs: []string{},
55-
stName: `E`, stArgs: []string{`int`},
69+
nesting: []namedType{
70+
{name: `D`},
71+
{name: `E`, args: []string{`int`}},
72+
},
5673
want: "struct{X int}",
5774
},
75+
{
76+
nesting: []namedType{
77+
{name: `F`, args: []string{`int`}},
78+
},
79+
want: `struct{X int}`,
80+
},
5881
} {
59-
ctxt := types.NewContext()
60-
61-
fnGen, _ := pkg.Scope().Lookup(test.fnName).(*types.Func)
62-
if fnGen == nil {
63-
t.Fatal("Failed to find the function " + test.fnName)
82+
if len(test.nesting) == 0 {
83+
t.Fatal(`Must have at least one names type to instantiate`)
6484
}
65-
fnArgs := evalTypeList(t, fSet, pkg, test.fnArgs)
66-
fnFunc := types.NewFunc(fnGen.Pos(), pkg, fnGen.Name(), fnGen.Type().(*types.Signature))
6785

68-
stType, _ := fnFunc.Scope().Lookup(test.stName).Type().(*types.Named)
69-
if stType == nil {
70-
t.Fatal("Failed to find the object " + test.fnName + " in function " + test.fnName)
86+
ctxt := types.NewContext()
87+
var subst *Subster
88+
var obj types.Object
89+
scope := pkg.Scope()
90+
for _, nt := range test.nesting {
91+
obj = scope.Lookup(nt.name)
92+
if obj == nil {
93+
t.Fatalf(`Failed to find %s in package scope`, nt.name)
94+
}
95+
if fn, ok := obj.(*types.Func); ok {
96+
scope = fn.Scope()
97+
}
98+
args := evalTypeList(t, fSet, pkg, nt.args)
99+
tp := getTypeParams(t, obj.Type())
100+
subst = New(ctxt, tp, args, subst)
71101
}
72-
stArgs := evalTypeList(t, fSet, pkg, test.stArgs)
73102

74-
stSubst := NewNested(ctxt, fnFunc, fnArgs, stType.TypeParams(), stArgs)
75-
stInst := stSubst.Type(stType.Underlying())
103+
shouldNotPanic(t, func() {
104+
stInst := subst.Type(obj.Type().Underlying())
105+
if got := stInst.String(); got != test.want {
106+
t.Errorf("%s.typ(%s) = %v, want %v", subst, obj.Type().Underlying(), got, test.want)
107+
}
108+
})
109+
}
110+
}
111+
112+
func shouldNotPanic(t *testing.T, f func()) {
113+
t.Helper()
114+
defer func() {
115+
if r := recover(); r != nil {
116+
t.Errorf(`panicked: %v`, r)
117+
}
118+
}()
119+
f()
120+
}
76121

77-
if got := stInst.String(); got != test.want {
78-
t.Errorf("subst{%v->%v}.typ(%s) = %v, want %v", test.stName, test.stArgs, stType.Underlying(), got, test.want)
122+
func getTypeParams(t *testing.T, typ types.Type) *types.TypeParamList {
123+
switch typ := typ.(type) {
124+
case *types.Named:
125+
return typ.TypeParams()
126+
case *types.Signature:
127+
if tp := typ.RecvTypeParams(); tp != nil && tp.Len() > 0 {
128+
return tp
79129
}
130+
return typ.TypeParams()
131+
case interface{ Elem() types.Type }:
132+
// Pointer, slice, array, map, and channel types.
133+
return getTypeParams(t, typ.Elem())
134+
default:
135+
t.Fatalf(`getTypeParams(%v) hit unexpected type`, typ)
80136
}
137+
return nil
81138
}
82139

83140
func evalType(t *testing.T, fSet *token.FileSet, pkg *types.Package, expr string) types.Type {

internal/govendor/subst/subst.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,16 @@ type subster struct {
6666
func makeSubster(ctxt *types.Context, fn *types.Func, tparams *types.TypeParamList, targs []types.Type, debug bool) *subster {
6767
assert(tparams.Len() == len(targs), "makeSubster argument count must match")
6868

69+
// GOPHERJS change: Made the fn nil-able.
70+
var origin *types.Func
71+
if fn != nil {
72+
origin = fn.Origin()
73+
}
74+
6975
subst := &subster{
7076
replacements: make(map[*types.TypeParam]types.Type, tparams.Len()),
7177
cache: make(map[types.Type]types.Type),
72-
origin: fn.Origin(),
78+
origin: origin,
7379
ctxt: ctxt,
7480
}
7581
for i := 0; i < tparams.Len(); i++ {

0 commit comments

Comments
 (0)