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