src: finish documenting some module
[nit.git] / src / modelize / modelize_property.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Analysis and verification of property definitions to instantiate model element
18 module modelize_property
19
20 intrude import modelize_class
21 private import annotation
22
23 redef class ToolContext
24 # Run `AClassdef::build_property` on the classdefs of each module
25 var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])
26 end
27
28 private class ModelizePropertyPhase
29 super Phase
30 redef fun process_nmodule(nmodule)
31 do
32 for nclassdef in nmodule.n_classdefs do
33 if nclassdef.all_defs == null then continue # skip non principal classdef
34 toolcontext.modelbuilder.build_properties(nclassdef)
35 end
36 end
37 end
38
39 redef class ModelBuilder
40 # Registration of the npropdef associated to each mpropdef.
41 #
42 # Public clients need to use `mpropdef2node` to access stuff.
43 private var mpropdef2npropdef = new HashMap[MPropDef, APropdef]
44
45 # Retrieve the associated AST node of a mpropertydef.
46 # This method is used to associate model entity with syntactic entities.
47 #
48 # If the property definition is not associated with a node, returns node.
49 fun mpropdef2node(mpropdef: MPropDef): nullable ANode
50 do
51 var res: nullable ANode = mpropdef2npropdef.get_or_null(mpropdef)
52 if res != null then return res
53 if mpropdef isa MMethodDef and mpropdef.mproperty.is_root_init then
54 res = mclassdef2nclassdef.get_or_null(mpropdef.mclassdef)
55 if res != null then return res
56 end
57 return null
58 end
59
60 # Retrieve all the attributes nodes localy definied
61 # FIXME think more about this method and how the separations separate/global and ast/model should be done.
62 fun collect_attr_propdef(mclassdef: MClassDef): Array[AAttrPropdef]
63 do
64 var res = new Array[AAttrPropdef]
65 var n = mclassdef2nclassdef.get_or_null(mclassdef)
66 if n == null then return res
67 for npropdef in n.n_propdefs do
68 if npropdef isa AAttrPropdef then
69 res.add(npropdef)
70 end
71 end
72 return res
73 end
74
75 # Build the properties of `nclassdef`.
76 # REQUIRE: all superclasses are built.
77 private fun build_properties(nclassdef: AClassdef)
78 do
79 # Force building recursively
80 if nclassdef.build_properties_is_done then return
81 nclassdef.build_properties_is_done = true
82 var mclassdef = nclassdef.mclassdef.as(not null)
83 if mclassdef.in_hierarchy == null then return # Skip error
84 for superclassdef in mclassdef.in_hierarchy.direct_greaters do
85 if not mclassdef2nclassdef.has_key(superclassdef) then continue
86 build_properties(mclassdef2nclassdef[superclassdef])
87 end
88
89 mclassdef.build_self_type(self, nclassdef)
90 for nclassdef2 in nclassdef.all_defs do
91 for npropdef in nclassdef2.n_propdefs do
92 npropdef.build_property(self, mclassdef)
93 end
94 for npropdef in nclassdef2.n_propdefs do
95 npropdef.build_signature(self)
96 end
97 for npropdef in nclassdef2.n_propdefs do
98 npropdef.check_signature(self)
99 end
100 end
101 process_default_constructors(nclassdef)
102 end
103
104 # the root init of the Object class
105 # Is usually implicitly defined
106 # Then explicit or implicit definitions of root-init are attached to it
107 var the_root_init_mmethod: nullable MMethod
108
109 # Introduce or inherit default constructor
110 # This is the last part of `build_properties`.
111 private fun process_default_constructors(nclassdef: AClassdef)
112 do
113 var mclassdef = nclassdef.mclassdef.as(not null)
114
115 # Are we a refinement
116 if not mclassdef.is_intro then return
117
118 # Look for the init in Object, or create it
119 if mclassdef.mclass.name == "Object" and the_root_init_mmethod == null then
120 # Create the implicit root-init method
121 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
122 mprop.is_root_init = true
123 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
124 var mparameters = new Array[MParameter]
125 var msignature = new MSignature(mparameters, null)
126 mpropdef.msignature = msignature
127 mpropdef.new_msignature = msignature
128 mprop.is_init = true
129 nclassdef.mfree_init = mpropdef
130 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
131 the_root_init_mmethod = mprop
132 return
133 end
134
135 # Is the class forbid constructors?
136 if not mclassdef.mclass.kind.need_init then return
137
138 # Is there already a constructor defined?
139 var defined_init: nullable MMethodDef = null
140 for mpropdef in mclassdef.mpropdefs do
141 if not mpropdef isa MMethodDef then continue
142 if not mpropdef.mproperty.is_init then continue
143 if mpropdef.mproperty.is_root_init then
144 assert defined_init == null
145 defined_init = mpropdef
146 else if mpropdef.mproperty.name == "init" then
147 # An explicit old-style init named "init", so return
148 return
149 end
150 end
151
152 if not nclassdef isa AStdClassdef then return
153
154 # Collect undefined attributes
155 var mparameters = new Array[MParameter]
156 var initializers = new Array[MProperty]
157 for npropdef in nclassdef.n_propdefs do
158 if npropdef isa AMethPropdef then
159 if npropdef.mpropdef == null then return # Skip broken attribute
160 var at = npropdef.get_single_annotation("autoinit", self)
161 if at == null then continue # Skip non tagged init
162
163 var sig = npropdef.mpropdef.msignature
164 if sig == null then continue # Skip broken method
165
166 if not npropdef.mpropdef.is_intro then
167 self.error(at, "Error: `autoinit` cannot be set on redefinitions")
168 continue
169 end
170
171 for param in sig.mparameters do
172 var ret_type = param.mtype
173 var mparameter = new MParameter(param.name, ret_type, false)
174 mparameters.add(mparameter)
175 end
176 initializers.add(npropdef.mpropdef.mproperty)
177 end
178 if npropdef isa AAttrPropdef then
179 if npropdef.mpropdef == null then return # Skip broken attribute
180 if npropdef.noinit then continue # Skip noinit attribute
181 var atautoinit = npropdef.get_single_annotation("autoinit", self)
182 if atautoinit != null then
183 # For autoinit attributes, call the reader to force
184 # the lazy initialization of the attribute.
185 initializers.add(npropdef.mreadpropdef.mproperty)
186 continue
187 end
188 if npropdef.has_value then continue
189 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
190 var ret_type = npropdef.mpropdef.static_mtype
191 if ret_type == null then return
192 var mparameter = new MParameter(paramname, ret_type, false)
193 mparameters.add(mparameter)
194 var msetter = npropdef.mwritepropdef
195 if msetter == null then
196 # No setter, it is a old-style attribute, so just add it
197 initializers.add(npropdef.mpropdef.mproperty)
198 else
199 # Add the setter to the list
200 initializers.add(msetter.mproperty)
201 end
202 end
203 end
204
205 if the_root_init_mmethod == null then return
206
207 # Look for most-specific new-stype init definitions
208 var spropdefs = the_root_init_mmethod.lookup_super_definitions(mclassdef.mmodule, mclassdef.bound_mtype)
209 if spropdefs.is_empty then
210 toolcontext.error(nclassdef.location, "Error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?")
211 return
212 end
213
214 # Search the longest-one and checks for conflict
215 var longest = spropdefs.first
216 if spropdefs.length > 1 then
217 # Check for conflict in the order of initializers
218 # Each initializer list must me a prefix of the longest list
219 # part 1. find the longest list
220 for spd in spropdefs do
221 if spd.initializers.length > longest.initializers.length then longest = spd
222 end
223 # part 2. compare
224 for spd in spropdefs do
225 var i = 0
226 for p in spd.initializers do
227 if p != longest.initializers[i] then
228 self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
229 return
230 end
231 i += 1
232 end
233 end
234 end
235
236 # Can we just inherit?
237 if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then
238 self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3)
239 mclassdef.mclass.root_init = longest
240 return
241 end
242
243 # Combine the inherited list to what is collected
244 if longest.initializers.length > 0 then
245 mparameters.prepend longest.new_msignature.mparameters
246 initializers.prepend longest.initializers
247 end
248
249 # If we already have a basic init definition, then setup its initializers
250 if defined_init != null then
251 defined_init.initializers.add_all(initializers)
252 var msignature = new MSignature(mparameters, null)
253 defined_init.new_msignature = msignature
254 self.toolcontext.info("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3)
255 mclassdef.mclass.root_init = defined_init
256 return
257 end
258
259 # Else create the local implicit basic init definition
260 var mprop = the_root_init_mmethod.as(not null)
261 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
262 mpropdef.has_supercall = true
263 mpropdef.initializers.add_all(initializers)
264 var msignature = new MSignature(mparameters, null)
265 mpropdef.new_msignature = msignature
266 mpropdef.msignature = new MSignature(new Array[MParameter], null) # always an empty real signature
267 nclassdef.mfree_init = mpropdef
268 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
269 mclassdef.mclass.root_init = mpropdef
270 end
271
272 # Check the visibility of `mtype` as an element of the signature of `mpropdef`.
273 fun check_visibility(node: ANode, mtype: MType, mpropdef: MPropDef)
274 do
275 var mmodule = mpropdef.mclassdef.mmodule
276 var mproperty = mpropdef.mproperty
277
278 # Extract visibility information of the main part of `mtype`
279 # It is a case-by case
280 var vis_type: nullable MVisibility = null # The own visibility of the type
281 var mmodule_type: nullable MModule = null # The original module of the type
282 mtype = mtype.as_notnullable
283 if mtype isa MClassType then
284 vis_type = mtype.mclass.visibility
285 mmodule_type = mtype.mclass.intro.mmodule
286 else if mtype isa MVirtualType then
287 vis_type = mtype.mproperty.visibility
288 mmodule_type = mtype.mproperty.intro_mclassdef.mmodule
289 else if mtype isa MParameterType then
290 # nothing, always visible
291 else
292 node.debug "Unexpected type {mtype}"
293 abort
294 end
295
296 if vis_type != null then
297 assert mmodule_type != null
298 var vis_module_type = mmodule.visibility_for(mmodule_type) # the visibility of the original module
299 if mproperty.visibility > vis_type then
300 error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the {vis_type} type `{mtype}`")
301 return
302 else if mproperty.visibility > vis_module_type then
303 error(node, "Error: The {mproperty.visibility} property `{mproperty}` cannot contain the type `{mtype}` from the {vis_module_type} module `{mmodule_type}`")
304 return
305 end
306 end
307
308 # No error, try to go deeper in generic types
309 if node isa AType then
310 for a in node.n_types do
311 var t = a.mtype
312 if t == null then continue # Error, thus skipped
313 check_visibility(a, t, mpropdef)
314 end
315 else if mtype isa MGenericType then
316 for t in mtype.arguments do check_visibility(node, t, mpropdef)
317 end
318 end
319 end
320
321 redef class MPropDef
322 # Does the MPropDef contains a call to super or a call of a super-constructor?
323 # Subsequent phases of the frontend (esp. typing) set it if required
324 var has_supercall: Bool = false is writable
325 end
326
327 redef class AClassdef
328 # Marker used in `ModelBuilder::build_properties`
329 private var build_properties_is_done = false
330
331 # The free init (implicitely constructed by the class if required)
332 var mfree_init: nullable MMethodDef = null
333 end
334
335 redef class MClass
336 # The base init of the class.
337 # Used to get the common new_msignature and initializers
338 #
339 # TODO: Where to put this information is not clear because unlike other
340 # informations, the initialisers are stable in a same class.
341 var root_init: nullable MMethodDef = null
342 end
343
344 redef class MClassDef
345 # What is the `APropdef` associated to a `MProperty`?
346 # Used to check multiple definition of a property.
347 var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
348
349 # Build the virtual type `SELF` only for introduction `MClassDef`
350 fun build_self_type(modelbuilder: ModelBuilder, nclassdef: AClassdef)
351 do
352 if not is_intro then return
353
354 var name = "SELF"
355 var mprop = modelbuilder.try_get_mproperty_by_name(nclassdef, self, name)
356
357 # If SELF type is declared nowherer?
358 if mprop == null then return
359
360 # SELF is not a virtual type? it is weird but we ignore it
361 if not mprop isa MVirtualTypeProp then return
362
363 # Is this the intro of SELF in the library?
364 var intro = mprop.intro
365 var intro_mclassdef = intro.mclassdef
366 if intro_mclassdef == self then
367 var nintro = modelbuilder.mpropdef2npropdef[intro]
368
369 # SELF must be declared in Object, otherwise this will create conflicts
370 if intro_mclassdef.mclass.name != "Object" then
371 modelbuilder.error(nintro, "Error: the virtual type SELF must be declared in Object.")
372 end
373
374 # SELF must be public
375 if mprop.visibility != public_visibility then
376 modelbuilder.error(nintro, "Error: the virtual type SELF must be public.")
377 end
378
379 # SELF must not be fixed
380 if intro.is_fixed then
381 modelbuilder.error(nintro, "Error: the virtual type SELF cannot be fixed.")
382 end
383
384 return
385 end
386
387 # This class introduction inherits a SELF
388 # We insert an artificial property to update it
389 var mpropdef = new MVirtualTypeDef(self, mprop, self.location)
390 mpropdef.bound = mclass.mclass_type
391 end
392 end
393
394 redef class APropdef
395 # The associated main model entity
396 type MPROPDEF: MPropDef
397
398 # The associated propdef once build by a `ModelBuilder`
399 var mpropdef: nullable MPROPDEF is writable
400
401 private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract
402 private fun build_signature(modelbuilder: ModelBuilder) is abstract
403 private fun check_signature(modelbuilder: ModelBuilder) is abstract
404 private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility
405 do
406 var mvisibility = public_visibility
407 if nvisibility != null then
408 mvisibility = nvisibility.mvisibility
409 if mvisibility == intrude_visibility then
410 modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.")
411 mvisibility = public_visibility
412 end
413 end
414 if mclassdef.mclass.visibility == private_visibility then
415 if mvisibility == protected_visibility then
416 assert nvisibility != null
417 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
418 else if mvisibility == private_visibility then
419 assert nvisibility != null
420 modelbuilder.advice(nvisibility, "useless-visibility", "Warning: private is superfluous since the only legal visibility for properties in a private class is private.")
421 end
422 mvisibility = private_visibility
423 end
424 return mvisibility
425 end
426
427 private fun set_doc(mpropdef: MPropDef, modelbuilder: ModelBuilder)
428 do
429 var ndoc = self.n_doc
430 if ndoc != null then
431 var mdoc = ndoc.to_mdoc
432 mpropdef.mdoc = mdoc
433 mdoc.original_mentity = mpropdef
434 else if mpropdef.is_intro and mpropdef.mproperty.visibility >= protected_visibility then
435 modelbuilder.advice(self, "missing-doc", "Documentation warning: Undocumented property `{mpropdef.mproperty}`")
436 end
437
438 var at_deprecated = get_single_annotation("deprecated", modelbuilder)
439 if at_deprecated != null then
440 if not mpropdef.is_intro then
441 modelbuilder.error(self, "Error: method redefinition cannot be deprecated.")
442 else
443 var info = new MDeprecationInfo
444 ndoc = at_deprecated.n_doc
445 if ndoc != null then info.mdoc = ndoc.to_mdoc
446 mpropdef.mproperty.deprecation = info
447 end
448 end
449 end
450
451 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nvisibility: nullable AVisibility, mprop: MProperty)
452 do
453 if nvisibility == null then return
454 var mvisibility = nvisibility.mvisibility
455 if mvisibility != mprop.visibility and mvisibility != public_visibility then
456 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
457 end
458 end
459
460 private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
461 do
462 if mclassdef.mprop2npropdef.has_key(mprop) then
463 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.")
464 return false
465 end
466 if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then
467 if mprop.is_toplevel then
468 modelbuilder.error(self, "Error: {mprop} is a top level method.")
469 else
470 modelbuilder.error(self, "Error: {mprop} is not a top level method.")
471 end
472 return false
473
474 end
475 if mprop isa MMethod and mprop.is_root_init then return true
476 if kwredef == null then
477 if need_redef then
478 modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
479 return false
480 end
481 else
482 if not need_redef then
483 modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
484 return false
485 end
486 end
487 return true
488 end
489
490 end
491
492 redef class ASignature
493 # Is the model builder has correctly visited the signature
494 var is_visited = false
495 # Names of parameters from the AST
496 # REQUIRE: is_visited
497 var param_names = new Array[String]
498 # Types of parameters from the AST
499 # REQUIRE: is_visited
500 var param_types = new Array[MType]
501 # Rank of the vararg (of -1 if none)
502 # REQUIRE: is_visited
503 var vararg_rank: Int = -1
504 # Return type
505 var ret_type: nullable MType = null
506
507 # Visit and fill information about a signature
508 private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
509 do
510 var mmodule = mclassdef.mmodule
511 var param_names = self.param_names
512 var param_types = self.param_types
513 for np in self.n_params do
514 param_names.add(np.n_id.text)
515 var ntype = np.n_type
516 if ntype != null then
517 var mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
518 if mtype == null then return false # Skip error
519 for i in [0..param_names.length-param_types.length[ do
520 param_types.add(mtype)
521 end
522 if np.n_dotdotdot != null then
523 if self.vararg_rank != -1 then
524 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
525 return false
526 else
527 self.vararg_rank = param_names.length - 1
528 end
529 end
530 end
531 end
532 var ntype = self.n_type
533 if ntype != null then
534 self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
535 if self.ret_type == null then return false # Skip error
536 end
537
538 self.is_visited = true
539 return true
540 end
541
542 # Build a visited signature
543 fun build_signature(modelbuilder: ModelBuilder): nullable MSignature
544 do
545 if param_names.length != param_types.length then
546 # Some parameters are typed, other parameters are not typed.
547 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
548 return null
549 end
550
551 var mparameters = new Array[MParameter]
552 for i in [0..param_names.length[ do
553 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
554 self.n_params[i].mparameter = mparameter
555 mparameters.add(mparameter)
556 end
557
558 var msignature = new MSignature(mparameters, ret_type)
559 return msignature
560 end
561 end
562
563 redef class AParam
564 # The associated mparameter if any
565 var mparameter: nullable MParameter = null
566 end
567
568 redef class AMethPropdef
569 redef type MPROPDEF: MMethodDef
570
571
572 # Can self be used as a root init?
573 private fun look_like_a_root_init(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
574 do
575 # Need the `init` keyword
576 if n_kwinit == null then return false
577 # Need to by anonymous
578 if self.n_methid != null then return false
579 # No annotation on itself
580 if get_single_annotation("old_style_init", modelbuilder) != null then return false
581 # Nor on its module
582 var amod = self.parent.parent.as(AModule)
583 var amoddecl = amod.n_moduledecl
584 if amoddecl != null then
585 var old = amoddecl.get_single_annotation("old_style_init", modelbuilder)
586 if old != null then return false
587 end
588 # No parameters
589 if self.n_signature.n_params.length > 0 then
590 modelbuilder.advice(self, "old-init", "Warning: init with signature in {mclassdef}")
591 return false
592 end
593 # Cannot be private or something
594 if not self.n_visibility isa APublicVisibility then
595 modelbuilder.advice(self, "old-init", "Warning: non-public init in {mclassdef}")
596 return false
597 end
598
599 return true
600 end
601
602 redef fun build_property(modelbuilder, mclassdef)
603 do
604 var n_kwinit = n_kwinit
605 var n_kwnew = n_kwnew
606 var is_init = n_kwinit != null or n_kwnew != null
607 var name: String
608 var amethodid = self.n_methid
609 var name_node: ANode
610 if amethodid == null then
611 if not is_init then
612 name = "main"
613 name_node = self
614 else if n_kwinit != null then
615 name = "init"
616 name_node = n_kwinit
617 else if n_kwnew != null then
618 name = "new"
619 name_node = n_kwnew
620 else
621 abort
622 end
623 else if amethodid isa AIdMethid then
624 name = amethodid.n_id.text
625 name_node = amethodid
626 else
627 # operator, bracket or assign
628 name = amethodid.collect_text
629 name_node = amethodid
630
631 if name == "-" and self.n_signature.n_params.length == 0 then
632 name = "unary -"
633 end
634 end
635
636 var look_like_a_root_init = look_like_a_root_init(modelbuilder, mclassdef)
637 var mprop: nullable MMethod = null
638 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
639 if mprop == null and look_like_a_root_init then
640 mprop = modelbuilder.the_root_init_mmethod
641 var nb = n_block
642 if nb isa ABlockExpr and nb.n_expr.is_empty and n_doc == null then
643 modelbuilder.advice(self, "useless-init", "Warning: useless empty init in {mclassdef}")
644 end
645 end
646 if mprop == null then
647 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
648 mprop = new MMethod(mclassdef, name, mvisibility)
649 if look_like_a_root_init and modelbuilder.the_root_init_mmethod == null then
650 modelbuilder.the_root_init_mmethod = mprop
651 mprop.is_root_init = true
652 end
653 mprop.is_init = is_init
654 mprop.is_new = n_kwnew != null
655 if parent isa ATopClassdef then mprop.is_toplevel = true
656 self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop)
657 else
658 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
659 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
660 end
661
662 # Check name conflicts in the local class for constructors.
663 if is_init then
664 for p, n in mclassdef.mprop2npropdef do
665 if p != mprop and p isa MMethod and p.name == name then
666 check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p)
667 break
668 end
669 end
670 end
671
672 mclassdef.mprop2npropdef[mprop] = self
673
674 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
675
676 set_doc(mpropdef, modelbuilder)
677
678 self.mpropdef = mpropdef
679 modelbuilder.mpropdef2npropdef[mpropdef] = self
680 if mpropdef.is_intro then
681 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 4)
682 else
683 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 4)
684 end
685 end
686
687 redef fun build_signature(modelbuilder)
688 do
689 var mpropdef = self.mpropdef
690 if mpropdef == null then return # Error thus skiped
691 var mclassdef = mpropdef.mclassdef
692 var mmodule = mclassdef.mmodule
693 var nsig = self.n_signature
694
695 if mpropdef.mproperty.is_root_init and not mclassdef.is_intro then
696 var root_init = mclassdef.mclass.root_init
697 if root_init != null then
698 # Inherit the initializers by refinement
699 mpropdef.new_msignature = root_init.new_msignature
700 assert mpropdef.initializers.is_empty
701 mpropdef.initializers.add_all root_init.initializers
702 end
703 end
704
705 # Retrieve info from the signature AST
706 var param_names = new Array[String] # Names of parameters from the AST
707 var param_types = new Array[MType] # Types of parameters from the AST
708 var vararg_rank = -1
709 var ret_type: nullable MType = null # Return type from the AST
710 if nsig != null then
711 if not nsig.visit_signature(modelbuilder, mclassdef) then return
712 param_names = nsig.param_names
713 param_types = nsig.param_types
714 vararg_rank = nsig.vararg_rank
715 ret_type = nsig.ret_type
716 end
717
718 # Look for some signature to inherit
719 # FIXME: do not inherit from the intro, but from the most specific
720 var msignature: nullable MSignature = null
721 if not mpropdef.is_intro then
722 msignature = mpropdef.mproperty.intro.msignature
723 if msignature == null then return # Skip error
724
725 # The local signature is adapted to use the local formal types, if any.
726 msignature = msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
727
728 # Check inherited signature arity
729 if param_names.length != msignature.arity then
730 var node: ANode
731 if nsig != null then node = nsig else node = self
732 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
733 return
734 end
735 else if mpropdef.mproperty.is_init and not mpropdef.mproperty.is_new then
736 # FIXME UGLY: inherit signature from a super-constructor
737 for msupertype in mclassdef.supertypes do
738 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
739 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
740 if candidate != null then
741 if msignature == null then
742 msignature = candidate.intro.as(MMethodDef).msignature
743 end
744 end
745 end
746 end
747
748
749 # Inherit the signature
750 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
751 # Parameters are untyped, thus inherit them
752 param_types = new Array[MType]
753 for mparameter in msignature.mparameters do
754 param_types.add(mparameter.mtype)
755 end
756 vararg_rank = msignature.vararg_rank
757 end
758 if msignature != null and ret_type == null then
759 ret_type = msignature.return_mtype
760 end
761
762 if param_names.length != param_types.length then
763 # Some parameters are typed, other parameters are not typed.
764 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
765 return
766 end
767
768 var mparameters = new Array[MParameter]
769 for i in [0..param_names.length[ do
770 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
771 if nsig != null then nsig.n_params[i].mparameter = mparameter
772 mparameters.add(mparameter)
773 end
774
775 # In `new`-factories, the return type is by default the classtype.
776 if ret_type == null and mpropdef.mproperty.is_new then ret_type = mclassdef.mclass.mclass_type
777
778 msignature = new MSignature(mparameters, ret_type)
779 mpropdef.msignature = msignature
780 mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null
781 mpropdef.is_intern = self.get_single_annotation("intern", modelbuilder) != null
782 mpropdef.is_extern = self.n_extern_code_block != null or self.get_single_annotation("extern", modelbuilder) != null
783 end
784
785 redef fun check_signature(modelbuilder)
786 do
787 var mpropdef = self.mpropdef
788 if mpropdef == null then return # Error thus skiped
789 var mclassdef = mpropdef.mclassdef
790 var mmodule = mclassdef.mmodule
791 var nsig = self.n_signature
792 var mysignature = self.mpropdef.msignature
793 if mysignature == null then return # Error thus skiped
794
795 # Lookup for signature in the precursor
796 # FIXME all precursors should be considered
797 if not mpropdef.is_intro then
798 var msignature = mpropdef.mproperty.intro.msignature
799 if msignature == null then return
800
801 var precursor_ret_type = msignature.return_mtype
802 var ret_type = mysignature.return_mtype
803 if ret_type != null and precursor_ret_type == null then
804 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
805 return
806 end
807
808 if mysignature.arity > 0 then
809 # Check parameters types
810 for i in [0..mysignature.arity[ do
811 var myt = mysignature.mparameters[i].mtype
812 var prt = msignature.mparameters[i].mtype
813 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
814 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
815 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
816 end
817 end
818 end
819 if precursor_ret_type != null then
820 if ret_type == null then
821 # Inherit the return type
822 ret_type = precursor_ret_type
823 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
824 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
825 end
826 end
827 end
828
829 if mysignature.arity > 0 then
830 # Check parameters visibility
831 for i in [0..mysignature.arity[ do
832 var nt = nsig.n_params[i].n_type
833 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
834 end
835 var nt = nsig.n_type
836 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
837 end
838 end
839 end
840
841 redef class AAttrPropdef
842 redef type MPROPDEF: MAttributeDef
843
844 # Is the node tagged `noinit`?
845 var noinit = false
846
847 # Is the node tagged lazy?
848 var is_lazy = false
849
850 # Has the node a default value?
851 # Could be through `n_expr` or `n_block`
852 var has_value = false
853
854 # The guard associated to a lazy attribute.
855 # Because some engines does not have a working `isset`,
856 # this additional attribute is used to guard the lazy initialization.
857 # TODO: to remove once isset is correctly implemented
858 var mlazypropdef: nullable MAttributeDef
859
860 # The associated getter (read accessor) if any
861 var mreadpropdef: nullable MMethodDef is writable
862 # The associated setter (write accessor) if any
863 var mwritepropdef: nullable MMethodDef is writable
864
865 redef fun build_property(modelbuilder, mclassdef)
866 do
867 var mclass = mclassdef.mclass
868
869 var name: String
870 name = self.n_id2.text
871
872 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
873 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
874 else if mclass.kind == enum_kind then
875 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
876 else if mclass.kind == extern_kind then
877 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
878 end
879
880 # New attribute style
881 var nid2 = self.n_id2
882 var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
883 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
884 self.mpropdef = mpropdef
885 modelbuilder.mpropdef2npropdef[mpropdef] = self
886
887 var readname = name
888 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
889 if mreadprop == null then
890 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
891 mreadprop = new MMethod(mclassdef, readname, mvisibility)
892 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
893 mreadprop.deprecation = mprop.deprecation
894 else
895 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
896 check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
897 end
898 mclassdef.mprop2npropdef[mreadprop] = self
899
900 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
901 self.mreadpropdef = mreadpropdef
902 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
903 set_doc(mreadpropdef, modelbuilder)
904 mpropdef.mdoc = mreadpropdef.mdoc
905
906 has_value = n_expr != null or n_block != null
907
908 var atnoinit = self.get_single_annotation("noinit", modelbuilder)
909 if atnoinit != null then
910 noinit = true
911 if has_value then
912 modelbuilder.error(atnoinit, "Error: `noinit` attributes cannot have an initial value")
913 return
914 end
915 end
916
917 var atlazy = self.get_single_annotation("lazy", modelbuilder)
918 var atautoinit = self.get_single_annotation("autoinit", modelbuilder)
919 if atlazy != null or atautoinit != null then
920 if atlazy != null and atautoinit != null then
921 modelbuilder.error(atlazy, "Error: lazy incompatible with autoinit")
922 return
923 end
924 if not has_value then
925 if atlazy != null then
926 modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
927 else if atautoinit != null then
928 modelbuilder.error(atautoinit, "Error: a autoinit attribute needs a value")
929 end
930 return
931 end
932 is_lazy = true
933 var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
934 var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
935 self.mlazypropdef = mlazypropdef
936 end
937
938 var atreadonly = self.get_single_annotation("readonly", modelbuilder)
939 if atreadonly != null then
940 if not has_value then
941 modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value")
942 end
943 # No setter, so just leave
944 return
945 end
946
947 var writename = name + "="
948 var atwritable = self.get_single_annotation("writable", modelbuilder)
949 if atwritable != null then
950 if not atwritable.n_args.is_empty then
951 writename = atwritable.arg_as_id(modelbuilder) or else writename
952 end
953 end
954 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
955 var nwkwredef: nullable Token = null
956 if atwritable != null then nwkwredef = atwritable.n_kwredef
957 if mwriteprop == null then
958 var mvisibility
959 if atwritable != null then
960 mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility)
961 else
962 mvisibility = private_visibility
963 end
964 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
965 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
966 mwriteprop.deprecation = mprop.deprecation
967 else
968 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
969 if atwritable != null then
970 check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
971 end
972 end
973 mclassdef.mprop2npropdef[mwriteprop] = self
974
975 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
976 self.mwritepropdef = mwritepropdef
977 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
978 mwritepropdef.mdoc = mpropdef.mdoc
979 end
980
981 redef fun build_signature(modelbuilder)
982 do
983 var mpropdef = self.mpropdef
984 if mpropdef == null then return # Error thus skipped
985 var mclassdef = mpropdef.mclassdef
986 var mmodule = mclassdef.mmodule
987 var mtype: nullable MType = null
988
989 var mreadpropdef = self.mreadpropdef
990
991 var ntype = self.n_type
992 if ntype != null then
993 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
994 if mtype == null then return
995 end
996
997 var inherited_type: nullable MType = null
998 # Inherit the type from the getter (usually an abstract getter)
999 if mreadpropdef != null and not mreadpropdef.is_intro then
1000 var msignature = mreadpropdef.mproperty.intro.msignature
1001 if msignature == null then return # Error, thus skipped
1002 inherited_type = msignature.return_mtype
1003 if inherited_type != null then
1004 # The inherited type is adapted to use the local formal types, if any.
1005 inherited_type = inherited_type.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
1006 if mtype == null then mtype = inherited_type
1007 end
1008 end
1009
1010 var nexpr = self.n_expr
1011 if mtype == null then
1012 if nexpr != null then
1013 if nexpr isa ANewExpr then
1014 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
1015 else if nexpr isa AIntExpr then
1016 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
1017 if cla != null then mtype = cla.mclass_type
1018 else if nexpr isa AFloatExpr then
1019 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
1020 if cla != null then mtype = cla.mclass_type
1021 else if nexpr isa ACharExpr then
1022 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
1023 if cla != null then mtype = cla.mclass_type
1024 else if nexpr isa ABoolExpr then
1025 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
1026 if cla != null then mtype = cla.mclass_type
1027 else if nexpr isa ASuperstringExpr then
1028 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1029 if cla != null then mtype = cla.mclass_type
1030 else if nexpr isa AStringFormExpr then
1031 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1032 if cla != null then mtype = cla.mclass_type
1033 else
1034 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
1035 end
1036
1037 if mtype == null then return
1038 end
1039 else if ntype != null and inherited_type == mtype then
1040 if nexpr isa ANewExpr then
1041 var xmtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
1042 if xmtype == mtype then
1043 modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition")
1044 end
1045 end
1046 end
1047
1048 if mtype == null then
1049 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
1050 return
1051 end
1052
1053 mpropdef.static_mtype = mtype
1054
1055 if mreadpropdef != null then
1056 var msignature = new MSignature(new Array[MParameter], mtype)
1057 mreadpropdef.msignature = msignature
1058 end
1059
1060 var mwritepropdef = self.mwritepropdef
1061 if mwritepropdef != null then
1062 var name: String
1063 name = n_id2.text
1064 var mparameter = new MParameter(name, mtype, false)
1065 var msignature = new MSignature([mparameter], null)
1066 mwritepropdef.msignature = msignature
1067 end
1068
1069 var mlazypropdef = self.mlazypropdef
1070 if mlazypropdef != null then
1071 mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
1072 end
1073 end
1074
1075 redef fun check_signature(modelbuilder)
1076 do
1077 var mpropdef = self.mpropdef
1078 if mpropdef == null then return # Error thus skipped
1079 var ntype = self.n_type
1080 var mtype = self.mpropdef.static_mtype
1081 if mtype == null then return # Error thus skipped
1082
1083 # Lookup for signature in the precursor
1084 # FIXME all precursors should be considered
1085 if not mpropdef.is_intro then
1086 var precursor_type = mpropdef.mproperty.intro.static_mtype
1087 if precursor_type == null then return
1088
1089 if mtype != precursor_type then
1090 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1091 return
1092 end
1093 end
1094
1095 # Check getter and setter
1096 var meth = self.mreadpropdef
1097 if meth != null then
1098 self.check_method_signature(modelbuilder, meth)
1099 var node: nullable ANode = ntype
1100 if node == null then node = self
1101 modelbuilder.check_visibility(node, mtype, meth)
1102 end
1103 meth = self.mwritepropdef
1104 if meth != null then
1105 self.check_method_signature(modelbuilder, meth)
1106 var node: nullable ANode = ntype
1107 if node == null then node = self
1108 modelbuilder.check_visibility(node, mtype, meth)
1109 end
1110 end
1111
1112 private fun check_method_signature(modelbuilder: ModelBuilder, mpropdef: MMethodDef)
1113 do
1114 var mclassdef = mpropdef.mclassdef
1115 var mmodule = mclassdef.mmodule
1116 var nsig = self.n_type
1117 var mysignature = mpropdef.msignature
1118 if mysignature == null then return # Error thus skiped
1119
1120 # Lookup for signature in the precursor
1121 # FIXME all precursors should be considered
1122 if not mpropdef.is_intro then
1123 var msignature = mpropdef.mproperty.intro.msignature
1124 if msignature == null then return
1125
1126 if mysignature.arity != msignature.arity then
1127 var node: ANode
1128 if nsig != null then node = nsig else node = self
1129 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1130 return
1131 end
1132 var precursor_ret_type = msignature.return_mtype
1133 var ret_type = mysignature.return_mtype
1134 if ret_type != null and precursor_ret_type == null then
1135 var node: ANode
1136 if nsig != null then node = nsig else node = self
1137 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1138 return
1139 end
1140
1141 if mysignature.arity > 0 then
1142 # Check parameters types
1143 for i in [0..mysignature.arity[ do
1144 var myt = mysignature.mparameters[i].mtype
1145 var prt = msignature.mparameters[i].mtype
1146 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
1147 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
1148 var node: ANode
1149 if nsig != null then node = nsig else node = self
1150 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1151 end
1152 end
1153 end
1154 if precursor_ret_type != null then
1155 if ret_type == null then
1156 # Inherit the return type
1157 ret_type = precursor_ret_type
1158 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
1159 var node: ANode
1160 if nsig != null then node = nsig else node = self
1161 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1162 end
1163 end
1164 end
1165 end
1166 end
1167
1168 redef class ATypePropdef
1169 redef type MPROPDEF: MVirtualTypeDef
1170
1171 redef fun build_property(modelbuilder, mclassdef)
1172 do
1173 var name = self.n_id.text
1174 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1175 if mprop == null then
1176 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
1177 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1178 for c in name.chars do if c >= 'a' and c<= 'z' then
1179 modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type {name}")
1180 break
1181 end
1182 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
1183 else
1184 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
1185 assert mprop isa MVirtualTypeProp
1186 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
1187 end
1188 mclassdef.mprop2npropdef[mprop] = self
1189
1190 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1191 self.mpropdef = mpropdef
1192 modelbuilder.mpropdef2npropdef[mpropdef] = self
1193 if mpropdef.is_intro then
1194 modelbuilder.toolcontext.info("{mpropdef} introduces new type {mprop.full_name}", 4)
1195 else
1196 modelbuilder.toolcontext.info("{mpropdef} redefines type {mprop.full_name}", 4)
1197 end
1198 set_doc(mpropdef, modelbuilder)
1199
1200 var atfixed = get_single_annotation("fixed", modelbuilder)
1201 if atfixed != null then
1202 mpropdef.is_fixed = true
1203 end
1204 end
1205
1206 redef fun build_signature(modelbuilder)
1207 do
1208 var mpropdef = self.mpropdef
1209 if mpropdef == null then return # Error thus skipped
1210 var mclassdef = mpropdef.mclassdef
1211 var mmodule = mclassdef.mmodule
1212 var mtype: nullable MType = null
1213
1214 var ntype = self.n_type
1215 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
1216 if mtype == null then return
1217
1218 mpropdef.bound = mtype
1219 # print "{mpropdef}: {mtype}"
1220 end
1221
1222 redef fun check_signature(modelbuilder)
1223 do
1224 var mpropdef = self.mpropdef
1225 if mpropdef == null then return # Error thus skipped
1226
1227 var bound = self.mpropdef.bound
1228 if bound == null then return # Error thus skipped
1229
1230 modelbuilder.check_visibility(n_type, bound, mpropdef)
1231
1232 var mclassdef = mpropdef.mclassdef
1233 var mmodule = mclassdef.mmodule
1234 var anchor = mclassdef.bound_mtype
1235
1236 # Check circularity
1237 if bound isa MVirtualType then
1238 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1239 var seen = [self.mpropdef.mproperty.mvirtualtype]
1240 loop
1241 if seen.has(bound) then
1242 seen.add(bound)
1243 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1244 return
1245 end
1246 seen.add(bound)
1247 var next = bound.lookup_bound(mmodule, anchor)
1248 if not next isa MVirtualType then break
1249 bound = next
1250 end
1251 end
1252
1253 # Check redefinitions
1254 bound = mpropdef.bound.as(not null)
1255 for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
1256 var supbound = p.bound
1257 if supbound == null then break # broken super bound, skip error
1258 if p.is_fixed then
1259 modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
1260 break
1261 end
1262 if p.mclassdef.mclass == mclassdef.mclass then
1263 # Still a warning to pass existing bad code
1264 modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
1265 break
1266 end
1267 if not bound.is_subtype(mmodule, anchor, supbound) then
1268 modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
1269 break
1270 end
1271 end
1272 end
1273 end