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