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