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