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