tc_assign.mx raw
1 package types
2
3 // Identical reports whether T and U are identical types under Moxie rules:
4 // - string = []byte (Moxie text unification)
5 // - int32 = int32, uint32 = uint32 (enforced via universe scope, but checked here too)
6 func Identical(T, U Type) (ok bool) {
7 if T == U {
8 return true
9 }
10 if T == nil || U == nil {
11 return false
12 }
13 switch t := T.(type) {
14 case *Basic:
15 var u *Basic
16 u, ok = U.(*Basic)
17 if !ok {
18 if t.Info&IsString != 0 {
19 sl, slOk := U.(*Slice)
20 if slOk {
21 eb, ebOk := sl.Elem.(*Basic)
22 if ebOk && eb.Kind == Uint8 {
23 return true
24 }
25 }
26 }
27 return false
28 }
29 tk := normaliseKind(t.Kind)
30 uk := normaliseKind(u.Kind)
31 if tk == uk {
32 return true
33 }
34 if t.Info&IsString != 0 && u.Info&IsString != 0 {
35 return true
36 }
37 return false
38 case *Pointer:
39 var u *Pointer
40 u, ok = U.(*Pointer)
41 return ok && Identical(t.Base, u.Base)
42 case *Array:
43 var u *Array
44 u, ok = U.(*Array)
45 return ok && t.Len == u.Len && Identical(t.Elem, u.Elem)
46 case *Slice:
47 var u *Slice
48 u, ok = U.(*Slice)
49 if !ok {
50 // []byte = string in Moxie (including untyped string)
51 b, bOk := U.(*Basic)
52 if bOk && b.Info&IsString != 0 {
53 eb, ebOk := t.Elem.(*Basic)
54 if ebOk && eb.Kind == Uint8 {
55 return true
56 }
57 }
58 return false
59 }
60 return Identical(t.Elem, u.Elem)
61 case *TCMap:
62 var u *TCMap
63 u, ok = U.(*TCMap)
64 return ok && Identical(t.Key, u.Key) && Identical(t.Elem, u.Elem)
65 case *TCChan:
66 var u *TCChan
67 u, ok = U.(*TCChan)
68 return ok && t.Dir == u.Dir && Identical(t.Elem, u.Elem)
69 case *TCStruct:
70 var u *TCStruct
71 u, ok = U.(*TCStruct)
72 if !ok || t.NumFields() != u.NumFields() {
73 return false
74 }
75 for i := range t.Fields {
76 tf := t.Fields[i]
77 uf := u.Fields[i]
78 if tf.Name != uf.Name || tf.Anonymous != uf.Anonymous {
79 return false
80 }
81 if !Identical(tf.Typ, uf.Typ) {
82 return false
83 }
84 }
85 return true
86 case *TCInterface:
87 var u *TCInterface
88 u, ok = U.(*TCInterface)
89 if !ok {
90 return false
91 }
92 if t.NumMethods() != u.NumMethods() {
93 return false
94 }
95 for i, tm := range t.AllMethods {
96 um := u.AllMethods[i]
97 if tm.Name != um.Name || !Identical(tm.Sig, um.Sig) {
98 return false
99 }
100 }
101 return true
102 case *Signature:
103 var u *Signature
104 u, ok = U.(*Signature)
105 if !ok {
106 return false
107 }
108 if t.Variadic != u.Variadic {
109 return false
110 }
111 if !identicalTuples(t.Params, u.Params) || !identicalTuples(t.Results, u.Results) {
112 return false
113 }
114 return true
115 case *Named:
116 var u *Named
117 u, ok = U.(*Named)
118 return ok && t.Obj == u.Obj
119 case *TypeParam:
120 var u *TypeParam
121 u, ok = U.(*TypeParam)
122 return ok && t.Id == u.Id
123 case *Tuple:
124 var u *Tuple
125 u, ok = U.(*Tuple)
126 return ok && identicalTuples(t, u)
127 }
128 return false
129 }
130
131 func identicalTuples(a, b *Tuple) (ok bool) {
132 la, lb := tupleLen(a), tupleLen(b)
133 if la != lb {
134 return false
135 }
136 for i := 0; i < la; i++ {
137 if !Identical(a.Vars[i].Typ, b.Vars[i].Typ) {
138 return false
139 }
140 }
141 return true
142 }
143
144 func tupleLen(t *Tuple) (n int32) {
145 if t == nil {
146 return 0
147 }
148 return t.Len()
149 }
150
151 // normaliseKind maps int32/uint32 to their 32-bit equivalents for identity checks.
152 func normaliseKind(k BasicKind) (b BasicKind) {
153 return k // int32=int32 is handled by the universe scope; no extra mapping needed
154 }
155
156 // Assignable reports whether a value of type V is assignable to type T
157 // under Moxie's type rules (spec Assignability, plus Moxie extensions).
158 func Assignable(V, T Type) (ok bool) {
159 if V == nil || T == nil {
160 return V == T
161 }
162 // Identical types are always assignable.
163 if Identical(V, T) {
164 return true
165 }
166 // Moxie extension: string = []byte anywhere.
167 if isTextType(V) && isTextType(T) {
168 return true
169 }
170 // Untyped constants are assignable to any numeric/bool/string type they fit in.
171 bv, bvOk := V.(*Basic)
172 if bvOk && bv.Info&IsUntyped != 0 {
173 return untypedAssignable(bv, T)
174 }
175 // nil is assignable to pointer, slice, map, chan, func, interface.
176 if V == Typ[UntypedNil] {
177 return isNilable(T)
178 }
179 // V's underlying type identical to T's underlying type, and at least one is unnamed.
180 vu, tu := SafeUnderlying(V), SafeUnderlying(T)
181 if Identical(vu, tu) {
182 _, vNamed := V.(*Named)
183 _, tNamed := T.(*Named)
184 if !vNamed || !tNamed {
185 return true
186 }
187 }
188 // T is an interface and V implements it.
189 iface, ifOk := SafeUnderlying(T).(*TCInterface)
190 if ifOk {
191 return Implements(V, iface)
192 }
193 // Directional channel: bidirectional chan is assignable to directed chan.
194 tc, tcOk := T.(*TCChan)
195 if tcOk {
196 vc, vcOk := V.(*TCChan)
197 if vcOk {
198 if Identical(tc.Elem, vc.Elem) {
199 return vc.Dir == TCSendRecv || vc.Dir == tc.Dir
200 }
201 }
202 }
203 return false
204 }
205
206 func isNilable(T Type) (ok bool) {
207 switch SafeUnderlying(T).(type) {
208 case *Pointer, *Slice, *TCMap, *TCChan, *Signature, *TCInterface:
209 return true
210 }
211 return false
212 }
213
214 func untypedAssignable(V *Basic, T Type) (ok bool) {
215 var b *Basic
216 b, ok = SafeUnderlying(T).(*Basic)
217 if !ok {
218 return false
219 }
220 switch V.Kind {
221 case UntypedBool:
222 return b.Info&IsBoolean != 0
223 case UntypedInt, UntypedRune:
224 return b.Info&IsInteger != 0 || b.Info&IsFloat != 0
225 case UntypedFloat:
226 return b.Info&IsFloat != 0
227 case UntypedString:
228 return b.Info&IsString != 0
229 }
230 return false
231 }
232
233 // isTextType returns true if T is string or []byte under Moxie's unified model.
234 func isTextType(T Type) (ok bool) {
235 b, bOk := SafeUnderlying(T).(*Basic)
236 if bOk && b.Info&IsString != 0 {
237 return true
238 }
239 sl, slOk := SafeUnderlying(T).(*Slice)
240 if slOk {
241 eb, ebOk := SafeUnderlying(sl.Elem).(*Basic)
242 if ebOk && eb.Kind == Uint8 {
243 return true
244 }
245 }
246 return false
247 }
248
249 // Implements reports whether type T implements interface I.
250 func Implements(T Type, I *TCInterface) (ok bool) {
251 if I.IsEmpty() {
252 return true
253 }
254 // Collect T's method set.
255 ms := methodSet(T)
256 for _, im := range I.AllMethods {
257 var m *TCFunc
258 m, ok = ms[im.Name]
259 if !ok {
260 return false
261 }
262 if !Identical(m.Signature(), im.Sig) {
263 return false
264 }
265 }
266 return true
267 }
268
269 // methodSet returns the named method set of T as a name->Func map.
270 // Includes methods from *T if T is a named type (pointer receiver methods).
271 func methodSet(T Type) (m map[string]*TCFunc) {
272 ms := map[string]*TCFunc{}
273 collectMethods(T, ms, false)
274 return ms
275 }
276
277 func collectMethods(T Type, ms map[string]*TCFunc, ptrOk bool) {
278 switch t := T.(type) {
279 case *Named:
280 for _, m := range t.Methods {
281 if _, dup := ms[m.Name]; !dup {
282 ms[m.Name] = m
283 }
284 }
285 // Also collect pointer receiver methods if T is addressable.
286 if ptrOk {
287 collectMethods(NewPointer(T), ms, false)
288 }
289 collectMethods(t.Under, ms, ptrOk)
290 case *Pointer:
291 if named, ok := t.Base.(*Named); ok {
292 for _, m := range named.Methods {
293 if _, dup := ms[m.Name]; !dup {
294 ms[m.Name] = m
295 }
296 }
297 }
298 case *TCInterface:
299 for _, m := range t.AllMethods {
300 if _, dup := ms[m.Name]; !dup {
301 ms[m.Name] = NewTCFunc(nil, m.Name, m.Sig)
302 }
303 }
304 }
305 }
306