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