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