Skip to content

Commit 7c1013b

Browse files
committed
add multiverse lookup to Type
1 parent 85fd79d commit 7c1013b

File tree

4 files changed

+174
-15
lines changed

4 files changed

+174
-15
lines changed

v2/parser/parse.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,12 @@ func (p *Parser) convertSignature(u types.Universe, t *gotypes.Signature) *types
637637

638638
// walkType adds the type, and any necessary child types.
639639
func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type) *types.Type {
640+
var out *types.Type
641+
defer func() {
642+
if out.GetMultiverse() == nil {
643+
out.NewMultiverse(u)
644+
}
645+
}()
640646
// Most of the cases are underlying types of the named type.
641647
name := goNameToName(in.String())
642648
if useName != nil {
@@ -645,13 +651,13 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
645651

646652
// Handle alias types conditionally on go1.22+.
647653
// Inline this once the minimum supported version is go1.22
648-
if out := p.walkAliasType(u, in); out != nil {
654+
if out = p.walkAliasType(u, in); out != nil {
649655
return out
650656
}
651657

652658
switch t := in.(type) {
653659
case *gotypes.Struct:
654-
out := u.Type(name)
660+
out = u.Type(name)
655661
out.GoType = in
656662
if out.Kind != types.Unknown {
657663
return out
@@ -670,7 +676,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
670676
}
671677
return out
672678
case *gotypes.Map:
673-
out := u.Type(name)
679+
out = u.Type(name)
674680
out.GoType = in
675681
if out.Kind != types.Unknown {
676682
return out
@@ -680,7 +686,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
680686
out.Key = p.walkType(u, nil, t.Key())
681687
return out
682688
case *gotypes.Pointer:
683-
out := u.Type(name)
689+
out = u.Type(name)
684690
out.GoType = in
685691
if out.Kind != types.Unknown {
686692
return out
@@ -689,7 +695,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
689695
out.Elem = p.walkType(u, nil, t.Elem())
690696
return out
691697
case *gotypes.Slice:
692-
out := u.Type(name)
698+
out = u.Type(name)
693699
out.GoType = in
694700
if out.Kind != types.Unknown {
695701
return out
@@ -698,7 +704,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
698704
out.Elem = p.walkType(u, nil, t.Elem())
699705
return out
700706
case *gotypes.Array:
701-
out := u.Type(name)
707+
out = u.Type(name)
702708
out.GoType = in
703709
if out.Kind != types.Unknown {
704710
return out
@@ -708,7 +714,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
708714
out.Len = in.(*gotypes.Array).Len()
709715
return out
710716
case *gotypes.Chan:
711-
out := u.Type(name)
717+
out = u.Type(name)
712718
out.GoType = in
713719
if out.Kind != types.Unknown {
714720
return out
@@ -719,7 +725,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
719725
// cannot be properly written.
720726
return out
721727
case *gotypes.Basic:
722-
out := u.Type(types.Name{
728+
out = u.Type(types.Name{
723729
Package: "", // This is a magic package name in the Universe.
724730
Name: t.Name(),
725731
})
@@ -730,7 +736,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
730736
out.Kind = types.Unsupported
731737
return out
732738
case *gotypes.Signature:
733-
out := u.Type(name)
739+
out = u.Type(name)
734740
out.GoType = in
735741
if out.Kind != types.Unknown {
736742
return out
@@ -739,7 +745,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
739745
out.Signature = p.convertSignature(u, t)
740746
return out
741747
case *gotypes.Interface:
742-
out := u.Type(name)
748+
out = u.Type(name)
743749
out.GoType = in
744750
if out.Kind != types.Unknown {
745751
return out
@@ -758,7 +764,6 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
758764
}
759765
return out
760766
case *gotypes.Named:
761-
var out *types.Type
762767
switch t.Underlying().(type) {
763768
case *gotypes.Named, *gotypes.Basic, *gotypes.Map, *gotypes.Slice:
764769
name := goNameToName(t.String())
@@ -785,7 +790,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
785790
name.Name = fmt.Sprintf("%s[%s]", strings.SplitN(name.Name, "[", 2)[0], strings.Join(tpNames, ","))
786791
}
787792

788-
if out := u.Type(name); out.Kind != types.Unknown {
793+
if out = u.Type(name); out.Kind != types.Unknown {
789794
out.GoType = in
790795
return out // short circuit if we've already made this.
791796
}
@@ -797,7 +802,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
797802
// "feature" for users. This flattens those types
798803
// together.
799804
name := goNameToName(t.String())
800-
if out := u.Type(name); out.Kind != types.Unknown {
805+
if out = u.Type(name); out.Kind != types.Unknown {
801806
return out // short circuit if we've already made this.
802807
}
803808
out = p.walkType(u, &name, t.Underlying())
@@ -827,7 +832,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
827832
Kind: types.TypeParam,
828833
}
829834
default:
830-
out := u.Type(name)
835+
out = u.Type(name)
831836
out.GoType = in
832837
if out.Kind != types.Unknown {
833838
return out

v2/parser/parse_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,12 @@ func TestAddOnePkgToUniverse(t *testing.T) {
548548
if len(ud.Types) == 0 {
549549
t.Errorf("expected non-zero types in direct package")
550550
} else {
551+
// verify that multiverse has been populated in type
552+
for _, tp := range ud.Types {
553+
if tp.GetMultiverse() == nil {
554+
t.Errorf("expected no nil multiverse in type %v", tp)
555+
}
556+
}
551557
type testcase struct {
552558
kind types.Kind
553559
elem string // just the type name
@@ -1124,6 +1130,7 @@ func TestStructParse(t *testing.T) {
11241130
}
11251131
opts := []cmp.Option{
11261132
cmpopts.IgnoreFields(types.Type{}, "GoType"),
1133+
cmpopts.IgnoreFields(types.Type{}, "multiverse"),
11271134
}
11281135
if e, a := expected, st; !cmp.Equal(e, a, opts...) {
11291136
t.Errorf("wanted, got:\n%#v\n%#v\n%s", e, a, cmp.Diff(e, a, opts...))

v2/types/types.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package types
1919
import (
2020
gotypes "go/types"
2121
"strings"
22+
"sync"
2223
)
2324

2425
// Ref makes a reference to the given type. It can only be used for e.g.
@@ -364,6 +365,60 @@ type Type struct {
364365

365366
// The underlying Go type.
366367
GoType gotypes.Type
368+
369+
// The reference to Multiverse
370+
multiverse *multiverse
371+
}
372+
373+
// multiverse holds Type definitions that were not found in the imported code but are needed during
374+
// generation. For example the imported code my have T but not *T, while the generated code needs *T. This
375+
// can't be part of the main Universe because that is a simple map, with no locking, and we all know what happens
376+
// when you modify a map while it is being iterated. Sharing this multiverse across Types ensures that there's
377+
// a single *Type for every type, and maintains the invariant that PointerTo(String) == PointerTo(String).
378+
type multiverse struct {
379+
real Universe
380+
mu sync.Mutex
381+
synthetic map[string]*Type
382+
}
383+
384+
// NeMultiverse creates a multiverse.
385+
func (t *Type) NewMultiverse(u Universe) {
386+
if t == nil {
387+
return
388+
}
389+
t.multiverse = &multiverse{
390+
real: u,
391+
mu: sync.Mutex{},
392+
synthetic: map[string]*Type{},
393+
}
394+
}
395+
396+
// GetMultiverse returns a Type.multiverse.
397+
func (t *Type) GetMultiverse() *multiverse {
398+
if t == nil {
399+
return nil
400+
}
401+
return t.multiverse
402+
}
403+
404+
// GetOrAddType searches a Type in the Universe and synthetic map.
405+
// If there is a matching name, return the Type, otherwise, create the Type.
406+
func (m *multiverse) GetOrAddType(t *Type) *Type {
407+
if m == nil {
408+
return t
409+
}
410+
if p, ok := m.real[t.Name.Package]; ok {
411+
if t, ok := p.Types[t.Name.Name]; ok {
412+
return t
413+
}
414+
}
415+
m.mu.Lock()
416+
defer m.mu.Unlock()
417+
if t, ok := m.synthetic[t.Name.Name]; ok {
418+
return t
419+
}
420+
m.synthetic[t.Name.Name] = t
421+
return t
367422
}
368423

369424
// String returns the name of the type.
@@ -556,13 +611,14 @@ var (
556611
)
557612

558613
func PointerTo(t *Type) *Type {
559-
return &Type{
614+
pt := &Type{
560615
Name: Name{
561616
Name: "*" + t.Name.String(),
562617
},
563618
Kind: Pointer,
564619
Elem: t,
565620
}
621+
return t.multiverse.GetOrAddType(pt)
566622
}
567623

568624
func IsInteger(t *Type) bool {

v2/types/types_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package types
1818

1919
import (
20+
"reflect"
21+
"sync"
2022
"testing"
2123
)
2224

@@ -37,6 +39,95 @@ func TestGetBuiltin(t *testing.T) {
3739
}
3840
}
3941

42+
func TestPointerTo(t *testing.T) {
43+
type1 := &Type{
44+
Name: Name{Package: "pkgname", Name: "structname"},
45+
Kind: Struct,
46+
}
47+
type2 := &Type{
48+
Name: Name{Package: "pkgname", Name: "secondstructname"},
49+
Kind: Struct,
50+
}
51+
52+
u := Universe{
53+
"pkgname": &Package{
54+
Types: map[string]*Type{
55+
"structname": type1,
56+
"secondstructname": type2,
57+
},
58+
},
59+
"": &Package{
60+
Types: map[string]*Type{
61+
"*pkgname.structname": &Type{
62+
Name: Name{Name: "*pkgname.structname"},
63+
Kind: Pointer,
64+
},
65+
},
66+
},
67+
}
68+
69+
type3 := &Type{
70+
Name: Name{Package: "pkgname", Name: "thridstructname"},
71+
Kind: Struct,
72+
multiverse: &multiverse{
73+
real: u,
74+
synthetic: map[string]*Type{
75+
"*pkgname.thridstructname": &Type{
76+
Name: Name{Name: "*pkgname.thridstructname"},
77+
Kind: Pointer,
78+
},
79+
},
80+
mu: sync.Mutex{},
81+
},
82+
}
83+
84+
testCases := []struct {
85+
name string
86+
tp *Type
87+
expected *Type
88+
expectCreation bool
89+
}{
90+
{
91+
name: "universe has the pointer type",
92+
tp: type1,
93+
expected: u[""].Types["*pkgname.structname"],
94+
expectCreation: false,
95+
},
96+
{
97+
name: "neither universe or cache has the pointer type",
98+
tp: type2,
99+
expected: &Type{
100+
Name: Name{Name: "*pkgname.secondstructname"},
101+
Kind: Pointer,
102+
Elem: type2,
103+
},
104+
expectCreation: true,
105+
},
106+
{
107+
name: "cache has the pointer type",
108+
tp: type3,
109+
expected: type3.multiverse.synthetic["*pkgname.thridstructname"],
110+
expectCreation: false,
111+
},
112+
}
113+
for _, tc := range testCases {
114+
if tc.tp.multiverse == nil {
115+
tc.tp.multiverse = &multiverse{
116+
real: u,
117+
synthetic: map[string]*Type{},
118+
mu: sync.Mutex{},
119+
}
120+
}
121+
tp := PointerTo(tc.tp)
122+
if tc.expectCreation && !reflect.DeepEqual(tp, tc.expected) {
123+
t.Errorf("PointerTo failed, expected %v, got : %v", tc.expected, tp)
124+
}
125+
if !tc.expectCreation && tp != tc.expected {
126+
t.Errorf("PointerTo should not create a new pointer type, expected %v, got : %v", tc.expected, tp)
127+
}
128+
}
129+
}
130+
40131
func TestGetMarker(t *testing.T) {
41132
u := Universe{}
42133
n := Name{Package: "path/to/package", Name: "Foo"}

0 commit comments

Comments
 (0)