modelize_property: disable old-style setter
[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 = false is writable
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 is 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)
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 end
424
425 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nvisibility: nullable AVisibility, mprop: MProperty)
426 do
427 if nvisibility == null then return
428 var mvisibility = nvisibility.mvisibility
429 if mvisibility != mprop.visibility and mvisibility != public_visibility then
430 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
431 end
432 end
433
434 private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
435 do
436 if mclassdef.mprop2npropdef.has_key(mprop) then
437 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.")
438 return false
439 end
440 if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then
441 if mprop.is_toplevel then
442 modelbuilder.error(self, "Error: {mprop} is a top level method.")
443 else
444 modelbuilder.error(self, "Error: {mprop} is not a top level method.")
445 end
446 return false
447
448 end
449 if kwredef == null then
450 if need_redef then
451 modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
452 return false
453 end
454 else
455 if not need_redef then
456 modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
457 return false
458 end
459 end
460 return true
461 end
462
463 end
464
465 redef class ASignature
466 # Is the model builder has correctly visited the signature
467 var is_visited = false
468 # Names of parameters from the AST
469 # REQUIRE: is_visited
470 var param_names = new Array[String]
471 # Types of parameters from the AST
472 # REQUIRE: is_visited
473 var param_types = new Array[MType]
474 # Rank of the vararg (of -1 if none)
475 # REQUIRE: is_visited
476 var vararg_rank: Int = -1
477 # Return type
478 var ret_type: nullable MType = null
479
480 # Visit and fill information about a signature
481 private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
482 do
483 var mmodule = mclassdef.mmodule
484 var param_names = self.param_names
485 var param_types = self.param_types
486 for np in self.n_params do
487 param_names.add(np.n_id.text)
488 var ntype = np.n_type
489 if ntype != null then
490 var mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
491 if mtype == null then return false # Skip error
492 for i in [0..param_names.length-param_types.length[ do
493 param_types.add(mtype)
494 end
495 if np.n_dotdotdot != null then
496 if self.vararg_rank != -1 then
497 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
498 return false
499 else
500 self.vararg_rank = param_names.length - 1
501 end
502 end
503 end
504 end
505 var ntype = self.n_type
506 if ntype != null then
507 self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
508 if self.ret_type == null then return false # Skip errir
509 end
510
511 self.is_visited = true
512 return true
513 end
514
515 # Build a visited signature
516 fun build_signature(modelbuilder: ModelBuilder): nullable MSignature
517 do
518 if param_names.length != param_types.length then
519 # Some parameters are typed, other parameters are not typed.
520 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
521 return null
522 end
523
524 var mparameters = new Array[MParameter]
525 for i in [0..param_names.length[ do
526 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
527 self.n_params[i].mparameter = mparameter
528 mparameters.add(mparameter)
529 end
530
531 var msignature = new MSignature(mparameters, ret_type)
532 return msignature
533 end
534 end
535
536 redef class AParam
537 # The associated mparameter if any
538 var mparameter: nullable MParameter = null
539 end
540
541 redef class AMethPropdef
542 redef type MPROPDEF: MMethodDef
543
544
545 # Can self be used as a root init?
546 private fun look_like_a_root_init(modelbuilder: ModelBuilder): Bool
547 do
548 # Need the `init` keyword
549 if n_kwinit == null then return false
550 # Need to by anonymous
551 if self.n_methid != null then return false
552 # No parameters
553 if self.n_signature.n_params.length > 0 then return false
554 # Cannot be private or something
555 if not self.n_visibility isa APublicVisibility then return false
556 # No annotation on itself
557 if get_single_annotation("old_style_init", modelbuilder) != null then return false
558 # Nor on its module
559 var amod = self.parent.parent.as(AModule)
560 var amoddecl = amod.n_moduledecl
561 if amoddecl != null then
562 var old = amoddecl.get_single_annotation("old_style_init", modelbuilder)
563 if old != null then return false
564 end
565
566 return true
567 end
568
569 redef fun build_property(modelbuilder, mclassdef)
570 do
571 var n_kwinit = n_kwinit
572 var n_kwnew = n_kwnew
573 var is_init = n_kwinit != null or n_kwnew != null
574 var name: String
575 var amethodid = self.n_methid
576 var name_node: ANode
577 if amethodid == null then
578 if not is_init then
579 name = "main"
580 name_node = self
581 else if n_kwinit != null then
582 name = "init"
583 name_node = n_kwinit
584 else if n_kwnew != null then
585 name = "init"
586 name_node = n_kwnew
587 else
588 abort
589 end
590 else if amethodid isa AIdMethid then
591 name = amethodid.n_id.text
592 name_node = amethodid
593 else
594 # operator, bracket or assign
595 name = amethodid.collect_text
596 name_node = amethodid
597
598 if name == "-" and self.n_signature.n_params.length == 0 then
599 name = "unary -"
600 end
601 end
602
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(modelbuilder) then
606 mprop = modelbuilder.the_root_init_mmethod
607 end
608 if mprop == null then
609 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
610 mprop = new MMethod(mclassdef, name, mvisibility)
611 if look_like_a_root_init(modelbuilder) and modelbuilder.the_root_init_mmethod == null then
612 modelbuilder.the_root_init_mmethod = mprop
613 mprop.is_root_init = true
614 end
615 mprop.is_init = is_init
616 mprop.is_new = n_kwnew != null
617 if parent isa ATopClassdef then mprop.is_toplevel = true
618 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return
619 else
620 if not mprop.is_root_init and not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
621 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
622 end
623 mclassdef.mprop2npropdef[mprop] = self
624
625 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
626
627 set_doc(mpropdef)
628
629 self.mpropdef = mpropdef
630 modelbuilder.mpropdef2npropdef[mpropdef] = self
631 if mpropdef.is_intro then
632 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
633 else
634 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
635 end
636 end
637
638 redef fun build_signature(modelbuilder)
639 do
640 var mpropdef = self.mpropdef
641 if mpropdef == null then return # Error thus skiped
642 var mclassdef = mpropdef.mclassdef
643 var mmodule = mclassdef.mmodule
644 var nsig = self.n_signature
645
646 # Retrieve info from the signature AST
647 var param_names = new Array[String] # Names of parameters from the AST
648 var param_types = new Array[MType] # Types of parameters from the AST
649 var vararg_rank = -1
650 var ret_type: nullable MType = null # Return type from the AST
651 if nsig != null then
652 if not nsig.visit_signature(modelbuilder, mclassdef) then return
653 param_names = nsig.param_names
654 param_types = nsig.param_types
655 vararg_rank = nsig.vararg_rank
656 ret_type = nsig.ret_type
657 end
658
659 # Look for some signature to inherit
660 # FIXME: do not inherit from the intro, but from the most specific
661 var msignature: nullable MSignature = null
662 if not mpropdef.is_intro then
663 msignature = mpropdef.mproperty.intro.msignature
664 if msignature == null then return # Skip error
665
666 # Check inherited signature arity
667 if param_names.length != msignature.arity then
668 var node: ANode
669 if nsig != null then node = nsig else node = self
670 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
671 return
672 end
673 else if mpropdef.mproperty.is_init then
674 # FIXME UGLY: inherit signature from a super-constructor
675 for msupertype in mclassdef.supertypes do
676 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
677 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
678 if candidate != null then
679 if msignature == null then
680 msignature = candidate.intro.as(MMethodDef).msignature
681 end
682 end
683 end
684 end
685
686
687 # Inherit the signature
688 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
689 # Parameters are untyped, thus inherit them
690 param_types = new Array[MType]
691 for mparameter in msignature.mparameters do
692 param_types.add(mparameter.mtype)
693 end
694 vararg_rank = msignature.vararg_rank
695 end
696 if msignature != null and ret_type == null then
697 ret_type = msignature.return_mtype
698 end
699
700 if param_names.length != param_types.length then
701 # Some parameters are typed, other parameters are not typed.
702 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
703 return
704 end
705
706 var mparameters = new Array[MParameter]
707 for i in [0..param_names.length[ do
708 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
709 if nsig != null then nsig.n_params[i].mparameter = mparameter
710 mparameters.add(mparameter)
711 end
712
713 msignature = new MSignature(mparameters, ret_type)
714 mpropdef.msignature = msignature
715 mpropdef.is_abstract = self isa ADeferredMethPropdef
716 mpropdef.is_intern = self isa AInternMethPropdef
717 mpropdef.is_extern = self isa AExternPropdef
718 end
719
720 redef fun check_signature(modelbuilder)
721 do
722 var mpropdef = self.mpropdef
723 if mpropdef == null then return # Error thus skiped
724 var mclassdef = mpropdef.mclassdef
725 var mmodule = mclassdef.mmodule
726 var nsig = self.n_signature
727 var mysignature = self.mpropdef.msignature
728 if mysignature == null then return # Error thus skiped
729
730 # Lookup for signature in the precursor
731 # FIXME all precursors should be considered
732 if not mpropdef.is_intro then
733 var msignature = mpropdef.mproperty.intro.msignature
734 if msignature == null then return
735
736 var precursor_ret_type = msignature.return_mtype
737 var ret_type = mysignature.return_mtype
738 if ret_type != null and precursor_ret_type == null then
739 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
740 return
741 end
742
743 if mysignature.arity > 0 then
744 # Check parameters types
745 for i in [0..mysignature.arity[ do
746 var myt = mysignature.mparameters[i].mtype
747 var prt = msignature.mparameters[i].mtype
748 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
749 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
750 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}.")
751 end
752 end
753 end
754 if precursor_ret_type != null then
755 if ret_type == null then
756 # Inherit the return type
757 ret_type = precursor_ret_type
758 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
759 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}.")
760 end
761 end
762 end
763
764 if mysignature.arity > 0 then
765 # Check parameters visibility
766 for i in [0..mysignature.arity[ do
767 var nt = nsig.n_params[i].n_type
768 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
769 end
770 var nt = nsig.n_type
771 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
772 end
773 end
774 end
775
776 redef class AAttrPropdef
777 redef type MPROPDEF: MAttributeDef
778
779 # Is the node tagged `noinit`?
780 var noinit = false
781
782 # Is the node taggeg lazy?
783 var is_lazy = false
784
785 # The guard associated to a lasy attribute.
786 # Because some engines does not have a working `isset`,
787 # this additionnal attribute is used to guard the lazy initialization.
788 # TODO: to remove once isset is correctly implemented
789 var mlazypropdef: nullable MAttributeDef
790
791 # The associated getter (read accessor) if any
792 var mreadpropdef: nullable MMethodDef is writable
793 # The associated setter (write accessor) if any
794 var mwritepropdef: nullable MMethodDef is writable
795
796 redef fun build_property(modelbuilder, mclassdef)
797 do
798 var mclass = mclassdef.mclass
799
800 var name: String
801 if self.n_id != null then
802 name = self.n_id.text
803 else
804 name = self.n_id2.text
805 end
806
807 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
808 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
809 else if mclass.kind == enum_kind then
810 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
811 else if mclass.kind == extern_kind then
812 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
813 end
814
815 var nid = self.n_id
816 if nid != null then
817 # Old attribute style
818 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
819 if mprop == null then
820 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
821 mprop = new MAttribute(mclassdef, name, mvisibility)
822 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
823 else
824 assert mprop isa MAttribute
825 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
826 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
827 end
828 mclassdef.mprop2npropdef[mprop] = self
829
830 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
831 self.mpropdef = mpropdef
832 modelbuilder.mpropdef2npropdef[mpropdef] = self
833 set_doc(mpropdef)
834
835 var nreadable = self.n_readable
836 if nreadable != null then modelbuilder.error(nreadable, "Error: old-style getter no more supported")
837 var nwritable = self.n_writable
838 if nwritable != null then modelbuilder.error(nwritable, "Error: old-style setter no more supported")
839 else
840 # New attribute style
841 var nid2 = self.n_id2.as(not null)
842 var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
843 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
844 self.mpropdef = mpropdef
845 modelbuilder.mpropdef2npropdef[mpropdef] = self
846 set_doc(mpropdef)
847
848 var readname = name
849 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
850 if mreadprop == null then
851 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
852 mreadprop = new MMethod(mclassdef, readname, mvisibility)
853 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
854 else
855 if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
856 check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
857 end
858 mclassdef.mprop2npropdef[mreadprop] = self
859
860 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
861 self.mreadpropdef = mreadpropdef
862 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
863 mreadpropdef.mdoc = mpropdef.mdoc
864
865 var atlazy = self.get_single_annotation("lazy", modelbuilder)
866 if atlazy != null then
867 if n_expr == null then
868 modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
869 end
870 is_lazy = true
871 var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
872 var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
873 self.mlazypropdef = mlazypropdef
874 end
875
876 var atreadonly = self.get_single_annotation("readonly", modelbuilder)
877 if atreadonly != null then
878 if n_expr == null then
879 modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value")
880 end
881 # No setter, so just leave
882 return
883 end
884
885 var writename = name + "="
886 var nwritable = self.n_writable
887 if nwritable != null then modelbuilder.error(nwritable, "Error: old-style setter no more supported")
888 var atwritable = self.get_single_annotation("writable", modelbuilder)
889 if atwritable != null then
890 if not atwritable.n_args.is_empty then
891 writename = atwritable.arg_as_id(modelbuilder) or else writename
892 end
893 end
894 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
895 var nwkwredef: nullable Token = null
896 if atwritable != null then nwkwredef = atwritable.n_kwredef
897 if mwriteprop == null then
898 var mvisibility
899 if atwritable != null then
900 mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility)
901 else
902 mvisibility = private_visibility
903 end
904 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
905 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
906 else
907 if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
908 if atwritable != null then
909 check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
910 end
911 end
912 mclassdef.mprop2npropdef[mwriteprop] = self
913
914 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
915 self.mwritepropdef = mwritepropdef
916 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
917 mwritepropdef.mdoc = mpropdef.mdoc
918 end
919 end
920
921 redef fun build_signature(modelbuilder)
922 do
923 var mpropdef = self.mpropdef
924 if mpropdef == null then return # Error thus skiped
925 var mclassdef = mpropdef.mclassdef
926 var mmodule = mclassdef.mmodule
927 var mtype: nullable MType = null
928
929 var mreadpropdef = self.mreadpropdef
930
931 var ntype = self.n_type
932 if ntype != null then
933 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
934 if mtype == null then return
935 end
936
937 # Inherit the type from the getter (usually an abstact getter)
938 if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then
939 var msignature = mreadpropdef.mproperty.intro.msignature
940 if msignature == null then return # Error, thus skiped
941 mtype = msignature.return_mtype
942 end
943
944 var nexpr = self.n_expr
945 if mtype == null then
946 if nexpr != null then
947 if nexpr isa ANewExpr then
948 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
949 else if nexpr isa AIntExpr then
950 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
951 if cla != null then mtype = cla.mclass_type
952 else if nexpr isa AFloatExpr then
953 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
954 if cla != null then mtype = cla.mclass_type
955 else if nexpr isa ACharExpr then
956 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
957 if cla != null then mtype = cla.mclass_type
958 else if nexpr isa ABoolExpr then
959 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
960 if cla != null then mtype = cla.mclass_type
961 else if nexpr isa ASuperstringExpr then
962 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
963 if cla != null then mtype = cla.mclass_type
964 else if nexpr isa AStringFormExpr then
965 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
966 if cla != null then mtype = cla.mclass_type
967 else
968 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
969 end
970
971 if mtype == null then return
972 end
973 else if ntype != null then
974 if nexpr isa ANewExpr then
975 var xmtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
976 if xmtype == mtype and modelbuilder.toolcontext.opt_warn.value >= 2 then
977 modelbuilder.warning(ntype, "Warning: useless type definition")
978 end
979 end
980 end
981
982 if mtype == null then
983 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
984 return
985 end
986
987 mpropdef.static_mtype = mtype
988
989 if mreadpropdef != null then
990 var msignature = new MSignature(new Array[MParameter], mtype)
991 mreadpropdef.msignature = msignature
992 end
993
994 var mwritepropdef = self.mwritepropdef
995 if mwritepropdef != null then
996 var name: String
997 if n_id != null then
998 name = n_id.text.substring_from(1)
999 else
1000 name = n_id2.text
1001 end
1002 var mparameter = new MParameter(name, mtype, false)
1003 var msignature = new MSignature([mparameter], null)
1004 mwritepropdef.msignature = msignature
1005 end
1006
1007 var mlazypropdef = self.mlazypropdef
1008 if mlazypropdef != null then
1009 mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
1010 end
1011 end
1012
1013 redef fun check_signature(modelbuilder)
1014 do
1015 var mpropdef = self.mpropdef
1016 if mpropdef == null then return # Error thus skiped
1017 var mclassdef = mpropdef.mclassdef
1018 var mmodule = mclassdef.mmodule
1019 var ntype = self.n_type
1020 var mtype = self.mpropdef.static_mtype
1021 if mtype == null then return # Error thus skiped
1022
1023 # Lookup for signature in the precursor
1024 # FIXME all precursors should be considered
1025 if not mpropdef.is_intro then
1026 var precursor_type = mpropdef.mproperty.intro.static_mtype
1027 if precursor_type == null then return
1028
1029 if mtype != precursor_type then
1030 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1031 return
1032 end
1033 end
1034
1035 # Check getter and setter
1036 var meth = self.mreadpropdef
1037 if meth != null then
1038 self.check_method_signature(modelbuilder, meth)
1039 var node: nullable ANode = ntype
1040 if node == null then node = self
1041 modelbuilder.check_visibility(node, mtype, meth)
1042 end
1043 meth = self.mwritepropdef
1044 if meth != null then
1045 self.check_method_signature(modelbuilder, meth)
1046 var node: nullable ANode = ntype
1047 if node == null then node = self
1048 modelbuilder.check_visibility(node, mtype, meth)
1049 end
1050 end
1051
1052 private fun check_method_signature(modelbuilder: ModelBuilder, mpropdef: MMethodDef)
1053 do
1054 var mclassdef = mpropdef.mclassdef
1055 var mmodule = mclassdef.mmodule
1056 var nsig = self.n_type
1057 var mysignature = mpropdef.msignature
1058 if mysignature == null then return # Error thus skiped
1059
1060 # Lookup for signature in the precursor
1061 # FIXME all precursors should be considered
1062 if not mpropdef.is_intro then
1063 var msignature = mpropdef.mproperty.intro.msignature
1064 if msignature == null then return
1065
1066 if mysignature.arity != msignature.arity then
1067 var node: ANode
1068 if nsig != null then node = nsig else node = self
1069 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1070 return
1071 end
1072 var precursor_ret_type = msignature.return_mtype
1073 var ret_type = mysignature.return_mtype
1074 if ret_type != null and precursor_ret_type == null then
1075 var node: ANode
1076 if nsig != null then node = nsig else node = self
1077 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1078 return
1079 end
1080
1081 if mysignature.arity > 0 then
1082 # Check parameters types
1083 for i in [0..mysignature.arity[ do
1084 var myt = mysignature.mparameters[i].mtype
1085 var prt = msignature.mparameters[i].mtype
1086 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
1087 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
1088 var node: ANode
1089 if nsig != null then node = nsig else node = self
1090 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1091 end
1092 end
1093 end
1094 if precursor_ret_type != null then
1095 if ret_type == null then
1096 # Inherit the return type
1097 ret_type = precursor_ret_type
1098 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
1099 var node: ANode
1100 if nsig != null then node = nsig else node = self
1101 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1102 end
1103 end
1104 end
1105 end
1106 end
1107
1108 redef class ATypePropdef
1109 redef type MPROPDEF: MVirtualTypeDef
1110
1111 redef fun build_property(modelbuilder, mclassdef)
1112 do
1113 var name = self.n_id.text
1114 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1115 if mprop == null then
1116 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
1117 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1118 for c in name.chars do if c >= 'a' and c<= 'z' then
1119 modelbuilder.warning(n_id, "Warning: lowercase in the virtual type {name}")
1120 break
1121 end
1122 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
1123 else
1124 if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
1125 assert mprop isa MVirtualTypeProp
1126 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
1127 end
1128 mclassdef.mprop2npropdef[mprop] = self
1129
1130 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1131 self.mpropdef = mpropdef
1132 modelbuilder.mpropdef2npropdef[mpropdef] = self
1133 set_doc(mpropdef)
1134
1135 var atfixed = get_single_annotation("fixed", modelbuilder)
1136 if atfixed != null then
1137 mpropdef.is_fixed = true
1138 end
1139 end
1140
1141 redef fun build_signature(modelbuilder)
1142 do
1143 var mpropdef = self.mpropdef
1144 if mpropdef == null then return # Error thus skiped
1145 var mclassdef = mpropdef.mclassdef
1146 var mmodule = mclassdef.mmodule
1147 var mtype: nullable MType = null
1148
1149 var ntype = self.n_type
1150 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
1151 if mtype == null then return
1152
1153 mpropdef.bound = mtype
1154 # print "{mpropdef}: {mtype}"
1155 end
1156
1157 redef fun check_signature(modelbuilder)
1158 do
1159 var mpropdef = self.mpropdef
1160 if mpropdef == null then return # Error thus skiped
1161
1162 var bound = self.mpropdef.bound
1163 if bound == null then return # Error thus skiped
1164
1165 modelbuilder.check_visibility(n_type, bound, mpropdef)
1166
1167 var mclassdef = mpropdef.mclassdef
1168 var mmodule = mclassdef.mmodule
1169 var anchor = mclassdef.bound_mtype
1170
1171 # Check circularity
1172 if bound isa MVirtualType then
1173 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1174 var seen = [self.mpropdef.mproperty.mvirtualtype]
1175 loop
1176 if seen.has(bound) then
1177 seen.add(bound)
1178 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1179 return
1180 end
1181 seen.add(bound)
1182 var next = bound.lookup_bound(mmodule, anchor)
1183 if not next isa MVirtualType then break
1184 bound = next
1185 end
1186 end
1187
1188 # Check redefinitions
1189 bound = mpropdef.bound.as(not null)
1190 for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
1191 var supbound = p.bound.as(not null)
1192 if p.is_fixed then
1193 modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
1194 break
1195 end
1196 if p.mclassdef.mclass == mclassdef.mclass then
1197 # Still a warning to pass existing bad code
1198 modelbuilder.warning(n_type, "Redef Error: a virtual type cannot be refined.")
1199 break
1200 end
1201 if not bound.is_subtype(mmodule, anchor, supbound) then
1202 modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
1203 break
1204 end
1205 end
1206 end
1207 end