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