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