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