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