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