Skip to content

Commit 79a034b

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

File tree

3 files changed

+82
-97
lines changed

3 files changed

+82
-97
lines changed

compiler/internal/typeparams/collect.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Resolver struct {
2020
// entries with the same index.
2121
func NewResolver(tc *types.Context, tParams *types.TypeParamList, tArgs []types.Type) *Resolver {
2222
r := &Resolver{
23-
subster: subst.New(tc, nil, tParams, tArgs),
23+
subster: subst.New(tc, tParams, tArgs),
2424
selMemo: map[typesutil.Selection]typesutil.Selection{},
2525
}
2626
return r
@@ -83,8 +83,8 @@ func (r *Resolver) SubstituteSelection(sel typesutil.Selection) typesutil.Select
8383
}
8484

8585
func (r *Resolver) String() string {
86-
if r == nil || r.subster == nil {
87-
return "nil"
86+
if r == nil {
87+
return `{}`
8888
}
8989
return r.subster.String()
9090
}

internal/govendor/subst/export.go

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"fmt"
88
"go/token"
99
"go/types"
10+
"sort"
11+
"strings"
1012
)
1113

1214
// To simplify future updates of the borrowed code, we minimize modifications
@@ -15,24 +17,38 @@ import (
1517

1618
// Subster performs type parameter substitution.
1719
type Subster struct {
18-
impl *subster
19-
tParams []*types.TypeParam
20-
tArgs []types.Type
20+
impl *subster
2121
}
2222

2323
// New creates a new Subster with a given list of type parameters and matching args.
2424
//
2525
// - This may return a nil if there is no substitution to be done.
2626
// Using a nil Subster will always return the original type.
27+
// - Given type arguments should not contain any types in the type parameters.
28+
// - 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.
2738
// - The given context must be non-nil to cache types.
2839
// - The given function may be nil for any package level types.
29-
// It must be non-nil for any types nested within a generic function.
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.
3043
// - Given type arguments should not contain any types in the type parameters.
3144
// - The internal implementation is not concurrency-safe.
32-
func New(tc *types.Context, fn *types.Func, tParams *types.TypeParamList, tArgs []types.Type) *Subster {
33-
assert(tParams.Len() == len(tArgs), "New() argument count must match")
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")
3450

35-
if tParams.Len() == 0 && len(tArgs) == 0 {
51+
if safeLen(tParams) == 0 && len(tArgs) == 0 && safeLen(fnParams) == 0 && len(fnArgs) == 0 {
3652
return nil
3753
}
3854

@@ -42,37 +58,59 @@ func New(tc *types.Context, fn *types.Func, tParams *types.TypeParamList, tArgs
4258
}
4359

4460
subst := makeSubster(tc, fn, tParams, tArgs, false)
45-
return &Subster{
46-
impl: subst,
61+
for i := 0; i < fnParams.Len(); i++ {
62+
subst.replacements[fnParams.At(i)] = fnArgs[i]
4763
}
64+
return &Subster{impl: subst}
4865
}
4966

50-
// Type returns a version of typ with all references to type parameters replaced
51-
// with the corresponding type arguments.
52-
func (s *Subster) Type(typ types.Type) types.Type {
53-
if s == nil {
54-
return typ
67+
func safeLen(tp interface{ Len() int }) int {
68+
if tp == nil {
69+
return 0
5570
}
56-
return s.impl.typ(typ)
71+
return tp.Len()
5772
}
5873

59-
// Params returns the type parameters being substituted.
60-
func (s *Subster) Params() []*types.TypeParam {
61-
if s == nil {
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 {
6283
return nil
6384
}
64-
return s.tParams
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
6592
}
6693

67-
// Args returns the type arguments to substitute for the
68-
// corresponding type parameter at the same index.
69-
func (s *Subster) Args() []types.Type {
94+
// Type returns a version of typ with all references to type parameters replaced
95+
// with the corresponding type arguments.
96+
func (s *Subster) Type(typ types.Type) types.Type {
7097
if s == nil {
71-
return nil
98+
return typ
7299
}
73-
return s.tArgs
100+
return s.impl.typ(typ)
74101
}
75102

103+
// String gets a strings representation of the replacement for debugging.
104+
// The parameters are sorted by name not by the order in the type parameter list.
76105
func (s *Subster) String() string {
77-
return fmt.Sprintf("%v->%v", s.Params(), s.Args())
106+
if s == nil || s.impl == nil {
107+
return `{}`
108+
}
109+
110+
parts := make([]string, 0, len(s.impl.replacements))
111+
for tp, ta := range s.impl.replacements {
112+
parts = append(parts, fmt.Sprintf("%s->%s", tp, ta))
113+
}
114+
sort.Strings(parts)
115+
return `{` + strings.Join(parts, `, `) + `}`
78116
}

internal/govendor/subst/export_test.go

Lines changed: 16 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ func TestNestedSubstInGenericFunction(t *testing.T) {
1515
func A[T any](){
1616
type B struct{X T}
1717
type C[U any] struct{X T; Y U}
18+
}
19+
20+
func D(){
21+
type E[V any] struct{X V}
1822
}`
1923

2024
fSet := token.NewFileSet()
@@ -37,70 +41,18 @@ func TestNestedSubstInGenericFunction(t *testing.T) {
3741
want string // expected underlying value after substitution
3842
}{
3943
{
40-
fnName: "A", fnArgs: []string{"int"},
41-
stName: "B", stArgs: []string{},
42-
want: "struct{X int}",
44+
fnName: `A`, fnArgs: []string{`int`},
45+
stName: `B`, stArgs: []string{},
46+
want: `struct{X int}`,
4347
},
44-
} {
45-
ctxt := types.NewContext()
46-
47-
fnGen, _ := pkg.Scope().Lookup(test.fnName).(*types.Func)
48-
if fnGen == nil {
49-
t.Fatal("Failed to find the function " + test.fnName)
50-
}
51-
fnType := fnGen.Type().(*types.Signature)
52-
fnArgs := evalTypeList(t, fSet, pkg, test.fnArgs)
53-
fnInst, err := types.Instantiate(ctxt, fnType, fnArgs, true)
54-
if err != nil {
55-
t.Fatalf("Failed to instantiate %s: %v", fnType, err)
56-
}
57-
fnFunc := types.NewFunc(fnGen.Pos(), pkg, fnGen.Name(), fnInst.(*types.Signature))
58-
59-
stType, _ := fnFunc.Scope().Lookup(test.stName).Type().(*types.Named)
60-
if stType == nil {
61-
t.Fatal("Failed to find the object " + test.fnName + " in function " + test.fnName)
62-
}
63-
stArgs := evalTypeList(t, fSet, pkg, test.stArgs)
64-
65-
stSubst := New(types.NewContext(), fnFunc, stType.TypeParams(), stArgs)
66-
stInst := stSubst.Type(stType.Underlying())
67-
68-
if got := stInst.String(); got != test.want {
69-
t.Errorf("subst{%v->%v}.typ(%s) = %v, want %v", test.stName, test.stArgs, stType.Underlying(), got, test.want)
70-
}
71-
}
72-
}
73-
74-
func TestNestedSubstInConcreteFunction(t *testing.T) {
75-
const source = `
76-
package P
77-
78-
func A(){
79-
type C[U any] struct{X T; Y U}
80-
}`
81-
82-
fSet := token.NewFileSet()
83-
f, err := parser.ParseFile(fSet, "hello.go", source, 0)
84-
if err != nil {
85-
t.Fatal(err)
86-
}
87-
88-
var conf types.Config
89-
pkg, err := conf.Check("P", fSet, []*ast.File{f}, nil)
90-
if err != nil {
91-
t.Fatal(err)
92-
}
93-
94-
for _, test := range []struct {
95-
fnName string // the name of the nesting function
96-
fnArgs []string // type expressions of args for the nesting function
97-
stName string // the name of the named type
98-
stArgs []string // type expressions of args for the named type
99-
want string // expected underlying value after substitution
100-
}{
10148
{
102-
fnName: "A", fnArgs: []string{"int"},
103-
stName: "B", stArgs: []string{},
49+
fnName: `A`, fnArgs: []string{`int`},
50+
stName: `C`, stArgs: []string{`bool`},
51+
want: "struct{X int; Y bool}",
52+
},
53+
{
54+
fnName: `D`, fnArgs: []string{},
55+
stName: `E`, stArgs: []string{`int`},
10456
want: "struct{X int}",
10557
},
10658
} {
@@ -110,21 +62,16 @@ func TestNestedSubstInConcreteFunction(t *testing.T) {
11062
if fnGen == nil {
11163
t.Fatal("Failed to find the function " + test.fnName)
11264
}
113-
fnType := fnGen.Type().(*types.Signature)
11465
fnArgs := evalTypeList(t, fSet, pkg, test.fnArgs)
115-
fnInst, err := types.Instantiate(ctxt, fnType, fnArgs, true)
116-
if err != nil {
117-
t.Fatalf("Failed to instantiate %s: %v", fnType, err)
118-
}
119-
fnFunc := types.NewFunc(fnGen.Pos(), pkg, fnGen.Name(), fnInst.(*types.Signature))
66+
fnFunc := types.NewFunc(fnGen.Pos(), pkg, fnGen.Name(), fnGen.Type().(*types.Signature))
12067

12168
stType, _ := fnFunc.Scope().Lookup(test.stName).Type().(*types.Named)
12269
if stType == nil {
12370
t.Fatal("Failed to find the object " + test.fnName + " in function " + test.fnName)
12471
}
12572
stArgs := evalTypeList(t, fSet, pkg, test.stArgs)
12673

127-
stSubst := New(types.NewContext(), fnFunc, stType.TypeParams(), stArgs)
74+
stSubst := NewNested(ctxt, fnFunc, fnArgs, stType.TypeParams(), stArgs)
12875
stInst := stSubst.Type(stType.Underlying())
12976

13077
if got := stInst.String(); got != test.want {

0 commit comments

Comments
 (0)