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