Source file
src/go/types/union.go
1
2
3
4
5 package types
6
7 import (
8 "go/ast"
9 "go/token"
10 )
11
12
13
14
15
16 type Union struct {
17 terms []*Term
18 }
19
20
21
22 func NewUnion(terms []*Term) *Union {
23 if len(terms) == 0 {
24 panic("empty union")
25 }
26 return &Union{terms}
27 }
28
29 func (u *Union) Len() int { return len(u.terms) }
30 func (u *Union) Term(i int) *Term { return u.terms[i] }
31
32 func (u *Union) Underlying() Type { return u }
33 func (u *Union) String() string { return TypeString(u, nil) }
34
35
36 type Term term
37
38
39 func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} }
40
41 func (t *Term) Tilde() bool { return t.tilde }
42 func (t *Term) Type() Type { return t.typ }
43 func (t *Term) String() string { return (*term)(t).String() }
44
45
46
47
48
49 const maxTermCount = 100
50
51
52
53 func parseUnion(check *Checker, uexpr ast.Expr) Type {
54 blist, tlist := flattenUnion(nil, uexpr)
55 assert(len(blist) == len(tlist)-1)
56
57 var terms []*Term
58
59 var u Type
60 for i, x := range tlist {
61 term := parseTilde(check, x)
62 if len(tlist) == 1 && !term.tilde {
63
64
65
66 return term.typ
67 }
68 if len(terms) >= maxTermCount {
69 if u != Typ[Invalid] {
70 check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
71 u = Typ[Invalid]
72 }
73 } else {
74 terms = append(terms, term)
75 u = &Union{terms}
76 }
77
78 if i > 0 {
79 check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
80 }
81 }
82
83 if u == Typ[Invalid] {
84 return u
85 }
86
87
88
89
90 check.later(func() {
91 for i, t := range terms {
92 if t.typ == Typ[Invalid] {
93 continue
94 }
95
96 u := under(t.typ)
97 f, _ := u.(*Interface)
98 if t.tilde {
99 if f != nil {
100 check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (%s is an interface)", t.typ)
101 continue
102 }
103
104 if !Identical(u, t.typ) {
105 check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
106 continue
107 }
108 }
109
110
111
112
113
114 if f != nil {
115 tset := f.typeSet()
116 switch {
117 case tset.NumMethods() != 0:
118 check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t)
119 case t.typ == universeComparable.Type():
120 check.error(tlist[i], _InvalidUnion, "cannot use comparable in union")
121 case tset.comparable:
122 check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t)
123 }
124 continue
125 }
126
127
128
129 if j := overlappingTerm(terms[:i], t); j >= 0 {
130 check.softErrorf(tlist[i], _InvalidUnion, "overlapping terms %s and %s", t, terms[j])
131 }
132 }
133 })
134
135 return u
136 }
137
138 func parseTilde(check *Checker, tx ast.Expr) *Term {
139 x := tx
140 var tilde bool
141 if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
142 x = op.X
143 tilde = true
144 }
145 typ := check.typ(x)
146
147
148
149
150
151 if isTypeParam(typ) {
152 check.error(x, _MisplacedTypeParam, "cannot embed a type parameter")
153 typ = Typ[Invalid]
154 }
155 term := NewTerm(tilde, typ)
156 if tilde {
157 check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil)
158 }
159 return term
160 }
161
162
163
164
165
166 func overlappingTerm(terms []*Term, y *Term) int {
167 assert(!IsInterface(y.typ))
168 for i, x := range terms {
169 if IsInterface(x.typ) {
170 continue
171 }
172
173
174 if debug {
175 if x == nil || x.typ == nil || y == nil || y.typ == nil {
176 panic("empty or top union term")
177 }
178 }
179 if !(*term)(x).disjoint((*term)(y)) {
180 return i
181 }
182 }
183 return -1
184 }
185
186
187
188 func flattenUnion(list []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) {
189 if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
190 blist, tlist = flattenUnion(list, o.X)
191 blist = append(blist, o)
192 x = o.Y
193 }
194 return blist, append(tlist, x)
195 }
196
View as plain text