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