v2 / vlib / v / checker / interface.v
397 lines · 393 sloc · 14.45 KB · fd3b3f4c53739e35cf115f1abc85e669f2cf8907
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module checker
5
6import v.ast
7import v.token
8
9fn (mut c Checker) interface_decl(mut node ast.InterfaceDecl) {
10 c.check_valid_pascal_case(node.name, 'interface name', node.pos)
11 mut decl_sym := c.table.sym(node.typ)
12 is_js := node.language == .js
13 if mut decl_sym.info is ast.Interface {
14 mut has_generic_types := false
15 if node.embeds.len > 0 {
16 all_embeds := c.expand_iface_embeds(node, 0, node.embeds)
17 // eprintln('> node.name: ${node.name} | node.embeds.len: ${node.embeds.len} | all_embeds: ${all_embeds.len}')
18 node.embeds = all_embeds
19 mut emnames := map[string]int{}
20 mut emnames_ds := map[string]bool{}
21 mut emnames_ds_info := map[string]bool{}
22 mut efnames := map[string]int{}
23 mut efnames_ds_info := map[string]bool{}
24 for i, m in node.methods {
25 emnames[m.name] = i
26 emnames_ds[m.name] = true
27 emnames_ds_info[m.name] = true
28 }
29 for i, f in node.fields {
30 efnames[f.name] = i
31 efnames_ds_info[f.name] = true
32 }
33 for embed in all_embeds {
34 isym := c.table.sym(embed.typ)
35 if embed.typ.has_flag(.generic) {
36 has_generic_types = true
37 }
38 if isym.kind != .interface {
39 c.error('interface `${node.name}` tries to embed `${isym.name}`, but `${isym.name}` is not an interface, but `${isym.kind}`',
40 embed.pos)
41 continue
42 }
43 // Ensure each generic type of the embed was declared in the interface's definition
44 if node.generic_types.len > 0 && embed.typ.has_flag(.generic) {
45 embed_generic_names := c.table.generic_type_names(embed.typ)
46 node_generic_names := node.generic_types.map(c.table.type_to_str(it))
47 for name in embed_generic_names {
48 if name !in node_generic_names {
49 interface_generic_names := node_generic_names.join(', ')
50 c.error('generic type name `${name}` is not mentioned in interface `${node.name}<${interface_generic_names}>`',
51 embed.pos)
52 }
53 }
54 }
55 isym_info := isym.info as ast.Interface
56 for f in isym_info.fields {
57 if !efnames_ds_info[f.name] {
58 efnames_ds_info[f.name] = true
59 decl_sym.info.fields << f
60 }
61 }
62 for m in isym_info.methods {
63 if !emnames_ds_info[m.name] {
64 emnames_ds_info[m.name] = true
65 decl_sym.info.methods << m.new_method_with_receiver_type(node.typ)
66 }
67 }
68 for m in isym.methods {
69 if !emnames_ds[m.name] {
70 emnames_ds[m.name] = true
71 decl_sym.methods << m.new_method_with_receiver_type(node.typ)
72 }
73 }
74 if embed_decl := c.table.interfaces[embed.typ] {
75 for f in embed_decl.fields {
76 if f.name in efnames {
77 // already existing field name, check for conflicts
78 ifield := node.fields[efnames[f.name]]
79 if field := c.table.find_field_with_embeds(isym, f.name) {
80 if ifield.typ != field.typ {
81 exp := c.table.type_to_str(ifield.typ)
82 got := c.table.type_to_str(field.typ)
83 c.error('embedded interface `${embed_decl.name}` conflicts existing field: `${ifield.name}`, expecting type: `${exp}`, got type: `${got}`',
84 ifield.pos)
85 }
86 }
87 } else {
88 efnames[f.name] = node.fields.len
89 node.fields << f
90 }
91 }
92 for m in embed_decl.methods {
93 if m.name in emnames {
94 // already existing method name, check for conflicts
95 imethod := node.methods[emnames[m.name]]
96 if em_fn := decl_sym.find_method(imethod.name) {
97 if m_fn := isym.find_method(m.name) {
98 msg := c.table.is_same_method(m_fn, em_fn)
99 if msg.len > 0 {
100 em_sig := c.table.fn_signature(em_fn, skip_receiver: true)
101 m_sig := c.table.fn_signature(m_fn, skip_receiver: true)
102 c.error('embedded interface `${embed_decl.name}` causes conflict: ${msg}, for interface method `${em_sig}` vs `${m_sig}`',
103 imethod.pos)
104 }
105 }
106 }
107 } else {
108 emnames[m.name] = node.methods.len
109 mut new_method := m.new_method_with_receiver_type(node.typ)
110 new_method.pos = embed.pos
111 node.methods << new_method
112 }
113 }
114 }
115 }
116 }
117 for i, method in node.methods {
118 if node.language == .v {
119 c.check_valid_snake_case(method.name, 'method name', method.pos)
120 }
121 if !c.ensure_type_exists(method.return_type, method.return_type_pos) {
122 continue
123 }
124 if is_js {
125 mrsym := c.table.sym(method.return_type)
126 if !mrsym.is_js_compatible() {
127 c.error('method ${method.name} returns non JS type', method.pos)
128 }
129 }
130 if method.return_type.has_flag(.generic) {
131 has_generic_types = true
132 // Ensure each generic type of the method was declared in the interface's definition
133 if node.generic_types.len > 0 {
134 method_generic_names := c.table.generic_type_names(method.return_type)
135 node_generic_names := node.generic_types.map(c.table.type_to_str(it))
136 for name in method_generic_names {
137 if name !in node_generic_names {
138 interface_generic_names := node_generic_names.join(', ')
139 c.error('generic type name `${name}` is not mentioned in interface `${node.name}<${interface_generic_names}>`',
140 method.return_type_pos)
141 }
142 }
143 }
144 } else if !method.return_type.has_option_or_result() {
145 ret_sym := c.table.sym(method.return_type)
146 if ret_sym.info is ast.ArrayFixed && !ret_sym.info.is_fn_ret {
147 c.cast_to_fixed_array_ret(method.return_type, ret_sym)
148 } else if ret_sym.info is ast.Alias {
149 parent_sym := c.table.sym(ret_sym.info.parent_type)
150 if parent_sym.info is ast.ArrayFixed && !parent_sym.info.is_fn_ret {
151 c.cast_to_fixed_array_ret(ret_sym.info.parent_type, parent_sym)
152 }
153 }
154 }
155 for j, param in method.params {
156 if j == 0 && is_js {
157 continue // no need to check first param
158 }
159 if param.typ.has_flag(.generic) {
160 has_generic_types = true
161 }
162 if !c.ensure_type_exists(param.typ, param.pos) {
163 continue
164 }
165 if reserved_type_names_chk.matches(param.name) {
166 c.error('invalid use of reserved type `${param.name}` as a parameter name',
167 param.pos)
168 }
169 // Ensure each generic type of the method was declared in the interface's definition
170 if node.generic_types.len > 0 && param.typ.has_flag(.generic) {
171 method_generic_names := c.table.generic_type_names(param.typ)
172 node_generic_names := node.generic_types.map(c.table.type_to_str(it))
173 for name in method_generic_names {
174 if name !in node_generic_names {
175 interface_generic_names := node_generic_names.join(', ')
176 c.error('generic type name `${name}` is not mentioned in interface `${node.name}<${interface_generic_names}>`',
177 param.type_pos)
178 }
179 }
180 }
181 if is_js {
182 psym := c.table.sym(param.typ)
183 if !psym.is_js_compatible() && !(j == method.params.len - 1
184 && method.is_variadic) {
185 c.error('method `${method.name}` accepts non JS type as parameter',
186 method.pos)
187 }
188 }
189 }
190 for field in node.fields {
191 field_sym := c.table.sym(field.typ)
192 if field.name == method.name && field_sym.kind == .function {
193 c.error('type `${decl_sym.name}` has both field and method named `${method.name}`',
194 method.pos)
195 }
196 }
197 for j in 0 .. i {
198 if method.name == node.methods[j].name {
199 c.error('duplicate method name `${method.name}`', method.pos)
200 }
201 }
202 }
203 for i, field in node.fields {
204 if node.language == .v {
205 c.check_valid_snake_case(field.name, 'field name', field.pos)
206 }
207 if !c.ensure_type_exists(field.typ, field.pos) {
208 continue
209 }
210 if field.typ.has_flag(.generic) {
211 has_generic_types = true
212 }
213 if is_js {
214 tsym := c.table.sym(field.typ)
215 if !tsym.is_js_compatible() {
216 c.error('field `${field.name}` uses non JS type', field.pos)
217 }
218 }
219 if field.typ == node.typ && node.language != .js {
220 c.error('recursive interface fields are not allowed because they cannot be initialised',
221 field.type_pos)
222 }
223 for j in 0 .. i {
224 if field.name == node.fields[j].name {
225 c.error('field name `${field.name}` duplicate', field.pos)
226 }
227 }
228 }
229 if node.generic_types.len == 0 && has_generic_types {
230 c.error('generic interface `${node.name}` declaration must specify the generic type names, e.g. ${node.name}[T]',
231 node.pos)
232 }
233 }
234}
235
236fn (mut c Checker) unwrap_generic_interface(typ ast.Type, interface_type ast.Type, pos token.Pos) ast.Type {
237 utyp := c.unwrap_generic(typ)
238 resolved_interface_type := c.unwrap_generic(interface_type)
239 if resolved_interface_type != interface_type && !resolved_interface_type.has_flag(.generic) {
240 mut resolved_sym := c.table.sym(resolved_interface_type)
241 if mut resolved_sym.info is ast.Interface {
242 if resolved_sym.info.concrete_types.len == 0
243 && resolved_sym.generic_types.len == resolved_sym.info.generic_types.len {
244 resolved_sym.info.concrete_types = resolved_sym.generic_types.clone()
245 }
246 }
247 return resolved_interface_type
248 }
249 typ_sym := c.table.sym(utyp)
250 mut inter_sym := c.table.sym(interface_type)
251
252 if mut inter_sym.info is ast.Interface {
253 if inter_sym.info.is_generic {
254 mut inferred_types := []ast.Type{}
255 generic_names := inter_sym.info.generic_types.map(c.table.get_type_name(it))
256 mut interface_concrete_types := inter_sym.generic_types.clone()
257 if interface_concrete_types.len == 0
258 && inter_sym.info.generic_types.len == inter_sym.info.concrete_types.len {
259 interface_concrete_types = inter_sym.info.concrete_types.clone()
260 }
261 // inferring interface generic types
262 for gt_name in generic_names {
263 mut inferred_type := ast.void_type
264 for ifield in inter_sym.info.fields {
265 if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
266 mut ifield_typ := ifield.typ
267 if interface_concrete_types.len == generic_names.len {
268 if resolved_field_type := c.table.convert_generic_type(ifield.typ,
269 generic_names, interface_concrete_types)
270 {
271 ifield_typ = resolved_field_type
272 }
273 }
274 inferred_field_type := c.infer_composite_generic_type(gt_name, ifield_typ,
275 field.typ)
276 if inferred_field_type != ast.void_type {
277 inferred_type = inferred_field_type
278 }
279 }
280 }
281 for imethod in inter_sym.info.methods {
282 mut method := typ_sym.find_method_with_generic_parent(imethod.name) or {
283 if pos.file_idx != -1 {
284 c.error('can not find method `${imethod.name}` on `${typ_sym.name}`, needed for interface: `${inter_sym.name}`', pos)
285 }
286 return 0
287 }
288 method = c.resolve_method_for_concrete_type(method, typ_sym)
289 mut imethod_return_type := imethod.return_type
290 if interface_concrete_types.len == generic_names.len {
291 if resolved_return_type := c.table.convert_generic_type(imethod.return_type,
292 generic_names, interface_concrete_types)
293 {
294 imethod_return_type = resolved_return_type
295 }
296 }
297 if imethod_return_type.has_flag(.generic)
298 || c.type_has_unresolved_generic_parts(imethod_return_type) {
299 imret_sym := c.table.sym(imethod_return_type)
300 mret_sym := c.table.sym(method.return_type)
301 if method.return_type == ast.void_type
302 && imethod_return_type != method.return_type {
303 if pos.file_idx != -1 {
304 c.error('interface method `${imethod.name}` returns `${imret_sym.name}`, but implementation method `${method.name}` returns no value',
305 pos)
306 }
307 return 0
308 }
309 if imethod_return_type == ast.void_type
310 && imethod_return_type != method.return_type {
311 if pos.file_idx != -1 {
312 c.error('interface method `${imethod.name}` returns no value, but implementation method `${method.name}` returns `${mret_sym.name}`',
313 pos)
314 }
315 return 0
316 }
317 inferred_return_type := c.infer_composite_generic_type(gt_name,
318 imethod_return_type, method.return_type)
319 if inferred_return_type != ast.void_type {
320 inferred_type = inferred_return_type
321 }
322 }
323 for i, iparam in imethod.params {
324 if i == 0 {
325 continue
326 }
327 param := method.params[i] or { ast.Param{} }
328 mut iparam_typ := iparam.typ
329 if interface_concrete_types.len == generic_names.len {
330 if resolved_param_type := c.table.convert_generic_type(iparam.typ,
331 generic_names, interface_concrete_types)
332 {
333 iparam_typ = resolved_param_type
334 }
335 }
336 mut need_inferred_type := false
337 if !iparam.typ.has_flag(.generic) && iparam.typ == param.typ
338 && imethod.return_type == method.return_type
339 && imethod.name == method.name {
340 need_inferred_type = true
341 }
342 if iparam_typ.has_flag(.generic)
343 || c.type_has_unresolved_generic_parts(iparam_typ) || need_inferred_type {
344 inferred_param_type := c.infer_composite_generic_type(gt_name,
345 iparam_typ, param.typ)
346 if inferred_param_type != ast.void_type {
347 inferred_type = inferred_param_type
348 } else if need_inferred_type && inferred_type == ast.void_type {
349 inferred_type = param.typ
350 }
351 }
352 }
353 }
354 if inferred_type == ast.void_type {
355 if interface_concrete_types.any(c.type_has_unresolved_generic_parts(it)) {
356 return interface_type
357 }
358 if pos.file_idx != -1 {
359 c.error('could not infer generic type `${gt_name}` in interface `${inter_sym.name}`',
360 pos)
361 }
362 return interface_type
363 }
364 inferred_types << inferred_type
365 }
366 // add concrete types to method, but only if at least one
367 // inferred type is actually concrete (not still generic).
368 // When all inferred types are generic (e.g. T -> T from
369 // `implements`), registering them would cause a circular
370 // recheck that fails to resolve field types.
371 if !inferred_types.all(it.has_flag(.generic)) {
372 for imethod in inter_sym.info.methods {
373 im_fkey := imethod.fkey()
374 if c.table.register_fn_concrete_types(im_fkey, inferred_types) {
375 c.need_recheck_generic_fns = true
376 }
377 if method := typ_sym.find_method_with_generic_parent(imethod.name) {
378 method_concrete_types := c.concrete_types_for_type_symbol(typ_sym)
379 if method_concrete_types.len > 0
380 && c.table.register_fn_concrete_types(method.fkey(), method_concrete_types) {
381 c.need_recheck_generic_fns = true
382 }
383 }
384 }
385 }
386 result_type := c.table.unwrap_generic_type(interface_type, generic_names,
387 inferred_types)
388 // Set concrete types on the instantiated interface symbol
389 mut result_sym := c.table.sym(result_type)
390 if mut result_sym.info is ast.Interface {
391 result_sym.info.concrete_types = inferred_types
392 }
393 return result_type
394 }
395 }
396 return interface_type
397}
398