modelize_property: improve error message for property already defined
[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 MPropDef
215 # Does the MPropDef contains a call to super or a call of a super-constructor?
216 # Subsequent phases of the frontend (esp. typing) set it if required
217 var has_supercall: Bool writable = false
218 end
219
220 redef class AClassdef
221 var build_properties_is_done: Bool = false
222 # The list of super-constructor to call at the start of the free constructor
223 # 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
224 var super_inits: nullable Collection[MMethod] = null
225
226 # The free init (implicitely constructed by the class if required)
227 var mfree_init: nullable MMethodDef = null
228
229 # What is the `APropdef` associated to a `MProperty`?
230 # Used to check multiple definition of a property.
231 var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
232 end
233
234 redef class Prod
235 # Join the text of all tokens
236 # Used to get the 'real name' of method definitions.
237 fun collect_text: String
238 do
239 var v = new TextCollectorVisitor
240 v.enter_visit(self)
241 assert v.text != ""
242 return v.text
243 end
244 end
245
246 private class TextCollectorVisitor
247 super Visitor
248 var text: String = ""
249 redef fun visit(n)
250 do
251 if n isa Token then text += n.text
252 n.visit_all(self)
253 end
254 end
255
256 redef class APropdef
257 # The associated main model entity
258 type MPROPDEF: MPropDef
259
260 # The associated propdef once build by a `ModelBuilder`
261 var mpropdef: nullable MPROPDEF writable
262
263 private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract
264 private fun build_signature(modelbuilder: ModelBuilder) is abstract
265 private fun check_signature(modelbuilder: ModelBuilder) is abstract
266 private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility
267 do
268 var mvisibility = public_visibility
269 if nvisibility != null then
270 mvisibility = nvisibility.mvisibility
271 if mvisibility == intrude_visibility then
272 modelbuilder.error(nvisibility, "Error: intrude is not a legal visibility for properties.")
273 mvisibility = public_visibility
274 end
275 end
276 if mclassdef.mclass.visibility == private_visibility then
277 if mvisibility == protected_visibility then
278 assert nvisibility != null
279 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
280 else if mvisibility == private_visibility then
281 assert nvisibility != null
282 # Not yet
283 # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.")
284 end
285 mvisibility = private_visibility
286 end
287 return mvisibility
288 end
289
290 private fun set_doc(mpropdef: MPropDef)
291 do
292 var ndoc = self.n_doc
293 if ndoc != null then
294 var mdoc = ndoc.to_mdoc
295 mpropdef.mdoc = mdoc
296 mdoc.original_mentity = mpropdef
297 end
298 end
299
300 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nvisibility: nullable AVisibility, mprop: MProperty)
301 do
302 if nvisibility == null then return
303 var mvisibility = nvisibility.mvisibility
304 if mvisibility != mprop.visibility and mvisibility != public_visibility then
305 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
306 end
307 end
308
309 private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
310 do
311 if nclassdef.mprop2npropdef.has_key(mprop) then
312 modelbuilder.error(self, "Error: A property {mprop} is already defined in class {nclassdef.mclassdef.mclass} at line {nclassdef.mprop2npropdef[mprop].location.line_start}.")
313 return false
314 end
315 if kwredef == null then
316 if need_redef then
317 modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
318 return false
319 end
320 else
321 if not need_redef then
322 modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
323 return false
324 end
325 end
326 return true
327 end
328
329 end
330
331 redef class ASignature
332 # Is the model builder has correctly visited the signature
333 var is_visited = false
334 # Names of parameters from the AST
335 # REQUIRE: is_visited
336 var param_names = new Array[String]
337 # Types of parameters from the AST
338 # REQUIRE: is_visited
339 var param_types = new Array[MType]
340 # Rank of the vararg (of -1 if none)
341 # REQUIRE: is_visited
342 var vararg_rank: Int = -1
343 # Return type
344 var ret_type: nullable MType = null
345
346 # Visit and fill information about a signature
347 private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
348 do
349 var mmodule = mclassdef.mmodule
350 var param_names = self.param_names
351 var param_types = self.param_types
352 for np in self.n_params do
353 param_names.add(np.n_id.text)
354 var ntype = np.n_type
355 if ntype != null then
356 var mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
357 if mtype == null then return false # Skip error
358 for i in [0..param_names.length-param_types.length[ do
359 param_types.add(mtype)
360 end
361 if np.n_dotdotdot != null then
362 if self.vararg_rank != -1 then
363 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
364 return false
365 else
366 self.vararg_rank = param_names.length - 1
367 end
368 end
369 end
370 end
371 var ntype = self.n_type
372 if ntype != null then
373 self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
374 if self.ret_type == null then return false # Skip errir
375 end
376
377 self.is_visited = true
378 return true
379 end
380
381 # Build a visited signature
382 fun build_signature(modelbuilder: ModelBuilder): nullable MSignature
383 do
384 if param_names.length != param_types.length then
385 # Some parameters are typed, other parameters are not typed.
386 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
387 return null
388 end
389
390 var mparameters = new Array[MParameter]
391 for i in [0..param_names.length[ do
392 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
393 self.n_params[i].mparameter = mparameter
394 mparameters.add(mparameter)
395 end
396
397 var msignature = new MSignature(mparameters, ret_type)
398 return msignature
399 end
400 end
401
402 redef class AParam
403 # The associated mparameter if any
404 var mparameter: nullable MParameter = null
405 end
406
407 redef class AMethPropdef
408 redef type MPROPDEF: MMethodDef
409
410 redef fun build_property(modelbuilder, nclassdef)
411 do
412 var n_kwinit = n_kwinit
413 var n_kwnew = n_kwnew
414 var is_init = n_kwinit != null or n_kwnew != null
415 var mclassdef = nclassdef.mclassdef.as(not null)
416 var name: String
417 var amethodid = self.n_methid
418 var name_node: ANode
419 if amethodid == null then
420 if not is_init then
421 name = "main"
422 name_node = self
423 else if n_kwinit != null then
424 name = "init"
425 name_node = n_kwinit
426 else if n_kwnew != null then
427 name = "init"
428 name_node = n_kwnew
429 else
430 abort
431 end
432 else if amethodid isa AIdMethid then
433 name = amethodid.n_id.text
434 name_node = amethodid
435 else
436 # operator, bracket or assign
437 name = amethodid.collect_text
438 name_node = amethodid
439
440 if name == "-" and self.n_signature.n_params.length == 0 then
441 name = "unary -"
442 end
443 end
444
445 var mprop: nullable MMethod = null
446 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
447 if mprop == null then
448 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
449 mprop = new MMethod(mclassdef, name, mvisibility)
450 mprop.is_init = is_init
451 mprop.is_new = n_kwnew != null
452 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return
453 else
454 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
455 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
456 end
457 nclassdef.mprop2npropdef[mprop] = self
458
459 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
460
461 set_doc(mpropdef)
462
463 self.mpropdef = mpropdef
464 modelbuilder.mpropdef2npropdef[mpropdef] = self
465 if mpropdef.is_intro then
466 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
467 else
468 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
469 end
470 end
471
472 redef fun build_signature(modelbuilder)
473 do
474 var mpropdef = self.mpropdef
475 if mpropdef == null then return # Error thus skiped
476 var mclassdef = mpropdef.mclassdef
477 var mmodule = mclassdef.mmodule
478 var nsig = self.n_signature
479
480 # Retrieve info from the signature AST
481 var param_names = new Array[String] # Names of parameters from the AST
482 var param_types = new Array[MType] # Types of parameters from the AST
483 var vararg_rank = -1
484 var ret_type: nullable MType = null # Return type from the AST
485 if nsig != null then
486 if not nsig.visit_signature(modelbuilder, mclassdef) then return
487 param_names = nsig.param_names
488 param_types = nsig.param_types
489 vararg_rank = nsig.vararg_rank
490 ret_type = nsig.ret_type
491 end
492
493 # Look for some signature to inherit
494 # FIXME: do not inherit from the intro, but from the most specific
495 var msignature: nullable MSignature = null
496 if not mpropdef.is_intro then
497 msignature = mpropdef.mproperty.intro.msignature
498 if msignature == null then return # Skip error
499
500 # Check inherited signature arity
501 if param_names.length != msignature.arity then
502 var node: ANode
503 if nsig != null then node = nsig else node = self
504 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
505 return
506 end
507 else if mpropdef.mproperty.is_init then
508 # FIXME UGLY: inherit signature from a super-constructor
509 for msupertype in mclassdef.supertypes do
510 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
511 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
512 if candidate != null then
513 if msignature == null then
514 msignature = candidate.intro.as(MMethodDef).msignature
515 end
516 end
517 end
518 end
519
520
521 # Inherit the signature
522 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
523 # Parameters are untyped, thus inherit them
524 param_types = new Array[MType]
525 for mparameter in msignature.mparameters do
526 param_types.add(mparameter.mtype)
527 end
528 vararg_rank = msignature.vararg_rank
529 end
530 if msignature != null and ret_type == null then
531 ret_type = msignature.return_mtype
532 end
533
534 if param_names.length != param_types.length then
535 # Some parameters are typed, other parameters are not typed.
536 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
537 return
538 end
539
540 var mparameters = new Array[MParameter]
541 for i in [0..param_names.length[ do
542 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
543 if nsig != null then nsig.n_params[i].mparameter = mparameter
544 mparameters.add(mparameter)
545 end
546
547 msignature = new MSignature(mparameters, ret_type)
548 mpropdef.msignature = msignature
549 mpropdef.is_abstract = self isa ADeferredMethPropdef
550 mpropdef.is_intern = self isa AInternMethPropdef
551 mpropdef.is_extern = self isa AExternPropdef
552 end
553
554 redef fun check_signature(modelbuilder)
555 do
556 var mpropdef = self.mpropdef
557 if mpropdef == null then return # Error thus skiped
558 var mclassdef = mpropdef.mclassdef
559 var mmodule = mclassdef.mmodule
560 var nsig = self.n_signature
561 var mysignature = self.mpropdef.msignature
562 if mysignature == null then return # Error thus skiped
563
564 # Lookup for signature in the precursor
565 # FIXME all precursors should be considered
566 if not mpropdef.is_intro then
567 var msignature = mpropdef.mproperty.intro.msignature
568 if msignature == null then return
569
570 var precursor_ret_type = msignature.return_mtype
571 var ret_type = mysignature.return_mtype
572 if ret_type != null and precursor_ret_type == null then
573 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
574 return
575 end
576
577 if mysignature.arity > 0 then
578 # Check parameters types
579 for i in [0..mysignature.arity[ do
580 var myt = mysignature.mparameters[i].mtype
581 var prt = msignature.mparameters[i].mtype
582 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
583 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
584 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}.")
585 end
586 end
587 end
588 if precursor_ret_type != null then
589 if ret_type == null then
590 # Inherit the return type
591 ret_type = precursor_ret_type
592 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
593 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}.")
594 end
595 end
596 end
597
598 if mysignature.arity > 0 then
599 # Check parameters visibility
600 for i in [0..mysignature.arity[ do
601 var nt = nsig.n_params[i].n_type
602 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
603 end
604 var nt = nsig.n_type
605 if nt != null then modelbuilder.check_visibility(nt, nt.mtype.as(not null), mpropdef)
606 end
607 end
608 end
609
610 redef class AAttrPropdef
611 redef type MPROPDEF: MAttributeDef
612
613 # The associated getter (read accessor) if any
614 var mreadpropdef: nullable MMethodDef writable
615 # The associated setter (write accessor) if any
616 var mwritepropdef: nullable MMethodDef writable
617 redef fun build_property(modelbuilder, nclassdef)
618 do
619 var mclassdef = nclassdef.mclassdef.as(not null)
620 var mclass = mclassdef.mclass
621
622 var name: String
623 if self.n_id != null then
624 name = self.n_id.text
625 else
626 name = self.n_id2.text
627 end
628
629 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
630 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
631 else if mclass.kind == enum_kind then
632 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
633 else if mclass.kind == extern_kind then
634 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
635 end
636
637 var nid = self.n_id
638 if nid != null then
639 # Old attribute style
640 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
641 if mprop == null then
642 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
643 mprop = new MAttribute(mclassdef, name, mvisibility)
644 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
645 else
646 assert mprop isa MAttribute
647 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
648 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
649 end
650 nclassdef.mprop2npropdef[mprop] = self
651
652 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
653 self.mpropdef = mpropdef
654 modelbuilder.mpropdef2npropdef[mpropdef] = self
655 set_doc(mpropdef)
656
657 var nreadable = self.n_readable
658 if nreadable != null then
659 var readname = name.substring_from(1)
660 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
661 if mreadprop == null then
662 var mvisibility = new_property_visibility(modelbuilder, mclassdef, nreadable.n_visibility)
663 mreadprop = new MMethod(mclassdef, readname, mvisibility)
664 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return
665 else
666 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return
667 check_redef_property_visibility(modelbuilder, nreadable.n_visibility, mreadprop)
668 end
669 nclassdef.mprop2npropdef[mreadprop] = self
670
671 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
672 self.mreadpropdef = mreadpropdef
673 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
674 mreadpropdef.mdoc = mpropdef.mdoc
675 end
676
677 var nwritable = self.n_writable
678 if nwritable != null then
679 var writename = name.substring_from(1) + "="
680 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
681 if mwriteprop == null then
682 var mvisibility = new_property_visibility(modelbuilder, mclassdef, nwritable.n_visibility)
683 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
684 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return
685 else
686 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return
687 check_redef_property_visibility(modelbuilder, nwritable.n_visibility, mwriteprop)
688 end
689 nclassdef.mprop2npropdef[mwriteprop] = self
690
691 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
692 self.mwritepropdef = mwritepropdef
693 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
694 mwritepropdef.mdoc = mpropdef.mdoc
695 end
696 else
697 # New attribute style
698 var nid2 = self.n_id2.as(not null)
699 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
700 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
701 self.mpropdef = mpropdef
702 modelbuilder.mpropdef2npropdef[mpropdef] = self
703 set_doc(mpropdef)
704
705 var readname = name
706 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
707 if mreadprop == null then
708 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
709 mreadprop = new MMethod(mclassdef, readname, mvisibility)
710 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return
711 else
712 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return
713 check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
714 end
715 nclassdef.mprop2npropdef[mreadprop] = self
716
717 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
718 self.mreadpropdef = mreadpropdef
719 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
720 mreadpropdef.mdoc = mpropdef.mdoc
721
722 var writename = name + "="
723 var nwritable = self.n_writable
724 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
725 var nwkwredef: nullable Token = null
726 if nwritable != null then nwkwredef = nwritable.n_kwredef
727 if mwriteprop == null then
728 var mvisibility
729 if nwritable != null then
730 mvisibility = new_property_visibility(modelbuilder, mclassdef, nwritable.n_visibility)
731 else
732 mvisibility = private_visibility
733 end
734 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
735 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return
736 else
737 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return
738 if nwritable != null then
739 check_redef_property_visibility(modelbuilder, nwritable.n_visibility, mwriteprop)
740 end
741 end
742 nclassdef.mprop2npropdef[mwriteprop] = self
743
744 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
745 self.mwritepropdef = mwritepropdef
746 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
747 mwritepropdef.mdoc = mpropdef.mdoc
748 end
749 end
750
751 redef fun build_signature(modelbuilder)
752 do
753 var mpropdef = self.mpropdef
754 if mpropdef == null then return # Error thus skiped
755 var mclassdef = mpropdef.mclassdef
756 var mmodule = 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(mmodule, mclassdef, 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(mmodule, mclassdef, 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(mmodule, mclassdef, 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)
830 do
831 var mpropdef = self.mpropdef
832 if mpropdef == null then return # Error thus skiped
833 var mclassdef = mpropdef.mclassdef
834 var mmodule = mclassdef.mmodule
835 var ntype = self.n_type
836 var mtype = self.mpropdef.static_mtype
837 if mtype == null then return # Error thus skiped
838
839 # Lookup for signature in the precursor
840 # FIXME all precursors should be considered
841 if not mpropdef.is_intro then
842 var precursor_type = mpropdef.mproperty.intro.static_mtype
843 if precursor_type == null then return
844
845 if mtype != precursor_type then
846 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
847 return
848 end
849 end
850
851 # Check getter and setter
852 var meth = self.mreadpropdef
853 if meth != null then
854 self.check_method_signature(modelbuilder, meth)
855 var node: nullable ANode = ntype
856 if node == null then node = self
857 modelbuilder.check_visibility(node, mtype, meth)
858 end
859 meth = self.mwritepropdef
860 if meth != null then
861 self.check_method_signature(modelbuilder, meth)
862 var node: nullable ANode = ntype
863 if node == null then node = self
864 modelbuilder.check_visibility(node, mtype, meth)
865 end
866 end
867
868 private fun check_method_signature(modelbuilder: ModelBuilder, mpropdef: MMethodDef)
869 do
870 var mclassdef = mpropdef.mclassdef
871 var mmodule = mclassdef.mmodule
872 var nsig = self.n_type
873 var mysignature = mpropdef.msignature
874 if mysignature == null then return # Error thus skiped
875
876 # Lookup for signature in the precursor
877 # FIXME all precursors should be considered
878 if not mpropdef.is_intro then
879 var msignature = mpropdef.mproperty.intro.msignature
880 if msignature == null then return
881
882 if mysignature.arity != msignature.arity then
883 var node: ANode
884 if nsig != null then node = nsig else node = self
885 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
886 return
887 end
888 var precursor_ret_type = msignature.return_mtype
889 var ret_type = mysignature.return_mtype
890 if ret_type != null and precursor_ret_type == null then
891 var node: ANode
892 if nsig != null then node = nsig else node = self
893 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
894 return
895 end
896
897 if mysignature.arity > 0 then
898 # Check parameters types
899 for i in [0..mysignature.arity[ do
900 var myt = mysignature.mparameters[i].mtype
901 var prt = msignature.mparameters[i].mtype
902 if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) and
903 not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
904 var node: ANode
905 if nsig != null then node = nsig else node = self
906 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
907 end
908 end
909 end
910 if precursor_ret_type != null then
911 if ret_type == null then
912 # Inherit the return type
913 ret_type = precursor_ret_type
914 else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
915 var node: ANode
916 if nsig != null then node = nsig else node = self
917 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
918 end
919 end
920 end
921 end
922 end
923
924 redef class ATypePropdef
925 redef type MPROPDEF: MVirtualTypeDef
926
927 redef fun build_property(modelbuilder, nclassdef)
928 do
929 var mclassdef = nclassdef.mclassdef.as(not null)
930 var name = self.n_id.text
931 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
932 if mprop == null then
933 var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
934 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
935 for c in name.chars do if c >= 'a' and c<= 'z' then
936 modelbuilder.warning(n_id, "Warning: lowercase in the virtual type {name}")
937 break
938 end
939 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
940 else
941 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
942 assert mprop isa MVirtualTypeProp
943 check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
944 end
945 nclassdef.mprop2npropdef[mprop] = self
946
947 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
948 self.mpropdef = mpropdef
949 modelbuilder.mpropdef2npropdef[mpropdef] = self
950 set_doc(mpropdef)
951 end
952
953 redef fun build_signature(modelbuilder)
954 do
955 var mpropdef = self.mpropdef
956 if mpropdef == null then return # Error thus skiped
957 var mclassdef = mpropdef.mclassdef
958 var mmodule = mclassdef.mmodule
959 var mtype: nullable MType = null
960
961 var ntype = self.n_type
962 mtype = modelbuilder.resolve_mtype(mmodule, mclassdef, 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)
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 mclassdef = mpropdef.mclassdef
983 var mmodule = mclassdef.mmodule
984 var anchor = mclassdef.bound_mtype
985
986 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
987 var seen = [self.mpropdef.mproperty.mvirtualtype]
988 loop
989 if seen.has(bound) then
990 seen.add(bound)
991 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
992 return
993 end
994 seen.add(bound)
995 var next = bound.lookup_bound(mmodule, anchor)
996 if not next isa MVirtualType then break
997 bound = next
998 end
999 end
1000 end