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