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