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