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