Skip to content

Commit 41c00a5

Browse files
committed
add multiverse lookup to Type
1 parent 85fd79d commit 41c00a5

File tree

4 files changed

+168
-15
lines changed

4 files changed

+168
-15
lines changed

v2/parser/parse.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ 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+
out = out.NewMultiVerse(u)
643+
}()
640644
// Most of the cases are underlying types of the named type.
641645
name := goNameToName(in.String())
642646
if useName != nil {
@@ -645,13 +649,13 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
645649

646650
// Handle alias types conditionally on go1.22+.
647651
// Inline this once the minimum supported version is go1.22
648-
if out := p.walkAliasType(u, in); out != nil {
652+
if out = p.walkAliasType(u, in); out != nil {
649653
return out
650654
}
651655

652656
switch t := in.(type) {
653657
case *gotypes.Struct:
654-
out := u.Type(name)
658+
out = u.Type(name)
655659
out.GoType = in
656660
if out.Kind != types.Unknown {
657661
return out
@@ -670,7 +674,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
670674
}
671675
return out
672676
case *gotypes.Map:
673-
out := u.Type(name)
677+
out = u.Type(name)
674678
out.GoType = in
675679
if out.Kind != types.Unknown {
676680
return out
@@ -680,7 +684,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
680684
out.Key = p.walkType(u, nil, t.Key())
681685
return out
682686
case *gotypes.Pointer:
683-
out := u.Type(name)
687+
out = u.Type(name)
684688
out.GoType = in
685689
if out.Kind != types.Unknown {
686690
return out
@@ -689,7 +693,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
689693
out.Elem = p.walkType(u, nil, t.Elem())
690694
return out
691695
case *gotypes.Slice:
692-
out := u.Type(name)
696+
out = u.Type(name)
693697
out.GoType = in
694698
if out.Kind != types.Unknown {
695699
return out
@@ -698,7 +702,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
698702
out.Elem = p.walkType(u, nil, t.Elem())
699703
return out
700704
case *gotypes.Array:
701-
out := u.Type(name)
705+
out = u.Type(name)
702706
out.GoType = in
703707
if out.Kind != types.Unknown {
704708
return out
@@ -708,7 +712,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
708712
out.Len = in.(*gotypes.Array).Len()
709713
return out
710714
case *gotypes.Chan:
711-
out := u.Type(name)
715+
out = u.Type(name)
712716
out.GoType = in
713717
if out.Kind != types.Unknown {
714718
return out
@@ -719,7 +723,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
719723
// cannot be properly written.
720724
return out
721725
case *gotypes.Basic:
722-
out := u.Type(types.Name{
726+
out = u.Type(types.Name{
723727
Package: "", // This is a magic package name in the Universe.
724728
Name: t.Name(),
725729
})
@@ -730,7 +734,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
730734
out.Kind = types.Unsupported
731735
return out
732736
case *gotypes.Signature:
733-
out := u.Type(name)
737+
out = u.Type(name)
734738
out.GoType = in
735739
if out.Kind != types.Unknown {
736740
return out
@@ -739,7 +743,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
739743
out.Signature = p.convertSignature(u, t)
740744
return out
741745
case *gotypes.Interface:
742-
out := u.Type(name)
746+
out = u.Type(name)
743747
out.GoType = in
744748
if out.Kind != types.Unknown {
745749
return out
@@ -758,7 +762,6 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
758762
}
759763
return out
760764
case *gotypes.Named:
761-
var out *types.Type
762765
switch t.Underlying().(type) {
763766
case *gotypes.Named, *gotypes.Basic, *gotypes.Map, *gotypes.Slice:
764767
name := goNameToName(t.String())
@@ -785,7 +788,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
785788
name.Name = fmt.Sprintf("%s[%s]", strings.SplitN(name.Name, "[", 2)[0], strings.Join(tpNames, ","))
786789
}
787790

788-
if out := u.Type(name); out.Kind != types.Unknown {
791+
if out = u.Type(name); out.Kind != types.Unknown {
789792
out.GoType = in
790793
return out // short circuit if we've already made this.
791794
}
@@ -797,7 +800,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
797800
// "feature" for users. This flattens those types
798801
// together.
799802
name := goNameToName(t.String())
800-
if out := u.Type(name); out.Kind != types.Unknown {
803+
if out = u.Type(name); out.Kind != types.Unknown {
801804
return out // short circuit if we've already made this.
802805
}
803806
out = p.walkType(u, &name, t.Underlying())
@@ -827,7 +830,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
827830
Kind: types.TypeParam,
828831
}
829832
default:
830-
out := u.Type(name)
833+
out = u.Type(name)
831834
out.GoType = in
832835
if out.Kind != types.Unknown {
833836
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: 53 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,56 @@ 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+
func (t *Type) NewMultiVerse(u Universe) *Type {
385+
if t == nil {
386+
return t
387+
}
388+
t.multiverse = &multiverse{
389+
real: u,
390+
mu: sync.Mutex{},
391+
synthetic: map[string]*Type{},
392+
}
393+
return t
394+
}
395+
396+
func (t *Type) GetMultiVerse() *multiverse {
397+
return t.multiverse
398+
}
399+
400+
// GetOrAddType searches a Type in the Universe and synthetic map.
401+
// If there is a matching name, return the Type, otherwise, create the Type.
402+
func (m *multiverse) GetOrAddType(t *Type) *Type {
403+
if m == nil {
404+
return t
405+
}
406+
if p, ok := m.real[t.Name.Package]; ok {
407+
if t, ok := p.Types[t.Name.Name]; ok {
408+
return t
409+
}
410+
}
411+
m.mu.Lock()
412+
defer m.mu.Unlock()
413+
if t, ok := m.synthetic[t.Name.Name]; ok {
414+
return t
415+
}
416+
m.synthetic[t.Name.Name] = t
417+
return t
367418
}
368419

369420
// String returns the name of the type.
@@ -556,13 +607,14 @@ var (
556607
)
557608

558609
func PointerTo(t *Type) *Type {
559-
return &Type{
610+
pt := &Type{
560611
Name: Name{
561612
Name: "*" + t.Name.String(),
562613
},
563614
Kind: Pointer,
564615
Elem: t,
565616
}
617+
return t.multiverse.GetOrAddType(pt)
566618
}
567619

568620
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)