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