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