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