230160ba217f0c3147fe8da88b21f469bb556f3b
[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 self.n_params[i].mparameter = mparameter
328 mparameters.add(mparameter)
329 end
330
331 var msignature = new MSignature(mparameters, ret_type)
332 return msignature
333 end
334 end
335
336 redef class AParam
337 # The associated mparameter if any
338 var mparameter: nullable MParameter = null
339 end
340
341 redef class AMethPropdef
342 redef type MPROPDEF: MMethodDef
343
344 # The associated super init if any
345 var super_init: nullable MMethod
346 redef fun build_property(modelbuilder, nclassdef)
347 do
348 var is_init = self isa AInitPropdef
349 var mclassdef = nclassdef.mclassdef.as(not null)
350 var name: String
351 var amethodid = self.n_methid
352 var name_node: ANode
353 if amethodid == null then
354 if self isa AMainMethPropdef then
355 name = "main"
356 name_node = self
357 else if self isa AConcreteInitPropdef then
358 name = "init"
359 name_node = self.n_kwinit
360 else if self isa AExternInitPropdef then
361 name = "init"
362 name_node = self.n_kwnew
363 else
364 abort
365 end
366 else if amethodid isa AIdMethid then
367 name = amethodid.n_id.text
368 name_node = amethodid
369 else
370 # operator, bracket or assign
371 name = amethodid.collect_text
372 name_node = amethodid
373
374 if name == "-" and self.n_signature.n_params.length == 0 then
375 name = "unary -"
376 end
377 end
378
379 var mprop: nullable MMethod = null
380 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
381 if mprop == null then
382 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
383 mprop = new MMethod(mclassdef, name, mvisibility)
384 mprop.is_init = is_init
385 mprop.is_new = self isa AExternInitPropdef
386 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return
387 else
388 if n_kwredef == null then
389 if self isa AMainMethPropdef then
390 # no warning
391 else
392 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop) then return
393 end
394 end
395 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
396 end
397 nclassdef.mprop2npropdef[mprop] = self
398
399 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
400
401 self.mpropdef = mpropdef
402 modelbuilder.mpropdef2npropdef[mpropdef] = self
403 if mpropdef.is_intro then
404 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
405 else
406 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
407 end
408 end
409
410 redef fun build_signature(modelbuilder, nclassdef)
411 do
412 var mpropdef = self.mpropdef
413 if mpropdef == null then return # Error thus skiped
414 var mmodule = mpropdef.mclassdef.mmodule
415 var nsig = self.n_signature
416
417 # Retrieve info from the signature AST
418 var param_names = new Array[String] # Names of parameters from the AST
419 var param_types = new Array[MType] # Types of parameters from the AST
420 var vararg_rank = -1
421 var ret_type: nullable MType = null # Return type from the AST
422 if nsig != null then
423 if not nsig.visit_signature(modelbuilder, nclassdef) then return
424 param_names = nsig.param_names
425 param_types = nsig.param_types
426 vararg_rank = nsig.vararg_rank
427 ret_type = nsig.ret_type
428 end
429
430 # Look for some signature to inherit
431 # FIXME: do not inherit from the intro, but from the most specific
432 var msignature: nullable MSignature = null
433 if not mpropdef.is_intro then
434 msignature = mpropdef.mproperty.intro.msignature
435 if msignature == null then return # Skip error
436
437 # Check inherited signature arity
438 if param_names.length != msignature.arity then
439 var node: ANode
440 if nsig != null then node = nsig else node = self
441 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
442 return
443 end
444 else if mpropdef.mproperty.is_init then
445 # FIXME UGLY: inherit signature from a super-constructor
446 for msupertype in nclassdef.mclassdef.supertypes do
447 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
448 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
449 if candidate != null then
450 if msignature == null then
451 msignature = candidate.intro.as(MMethodDef).msignature
452 end
453 end
454 end
455 end
456
457
458 # Inherit the signature
459 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
460 # Parameters are untyped, thus inherit them
461 param_types = new Array[MType]
462 for mparameter in msignature.mparameters do
463 param_types.add(mparameter.mtype)
464 end
465 vararg_rank = msignature.vararg_rank
466 end
467 if msignature != null and ret_type == null then
468 ret_type = msignature.return_mtype
469 end
470
471 if param_names.length != param_types.length then
472 # Some parameters are typed, other parameters are not typed.
473 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
474 return
475 end
476
477 var mparameters = new Array[MParameter]
478 for i in [0..param_names.length[ do
479 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
480 if nsig != null then nsig.n_params[i].mparameter = mparameter
481 mparameters.add(mparameter)
482 end
483
484 msignature = new MSignature(mparameters, ret_type)
485 mpropdef.msignature = msignature
486 mpropdef.is_abstract = self isa ADeferredMethPropdef
487
488 if nsig != null then
489 for nclosure in nsig.n_closure_decls do
490 var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef)
491 if clos_signature == null then return
492 var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false)
493 msignature.mclosures.add(mparameter)
494 end
495 end
496
497 end
498
499 redef fun check_signature(modelbuilder, nclassdef)
500 do
501 var mpropdef = self.mpropdef
502 if mpropdef == null then return # Error thus skiped
503 var mmodule = mpropdef.mclassdef.mmodule
504 var nsig = self.n_signature
505 var mysignature = self.mpropdef.msignature
506 if mysignature == null then return # Error thus skiped
507
508 # Lookup for signature in the precursor
509 # FIXME all precursors should be considered
510 if not mpropdef.is_intro then
511 var msignature = mpropdef.mproperty.intro.msignature
512 if msignature == null then return
513
514 var precursor_ret_type = msignature.return_mtype
515 var ret_type = mysignature.return_mtype
516 if ret_type != null and precursor_ret_type == null then
517 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
518 return
519 end
520
521 if mysignature.arity > 0 then
522 # Check parameters types
523 for i in [0..mysignature.arity[ do
524 var myt = mysignature.mparameters[i].mtype
525 var prt = msignature.mparameters[i].mtype
526 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
527 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
528 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
529 end
530 end
531 end
532 if precursor_ret_type != null then
533 if ret_type == null then
534 # Inherit the return type
535 ret_type = precursor_ret_type
536 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
537 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
538 end
539 end
540 end
541 end
542 end
543
544 redef class AAttrPropdef
545 redef type MPROPDEF: MAttributeDef
546
547 # The associated getter (read accessor) if any
548 var mreadpropdef: nullable MMethodDef writable
549 # The associated setter (write accessor) if any
550 var mwritepropdef: nullable MMethodDef writable
551 redef fun build_property(modelbuilder, nclassdef)
552 do
553 var mclassdef = nclassdef.mclassdef.as(not null)
554 var mclass = mclassdef.mclass
555
556 var name: String
557 if self.n_id != null then
558 name = self.n_id.text
559 else
560 name = self.n_id2.text
561 end
562
563 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
564 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
565 else if mclass.kind == enum_kind then
566 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
567 else if mclass.kind == extern_kind then
568 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
569 end
570
571 var nid = self.n_id
572 if nid != null then
573 # Old attribute style
574 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
575 if mprop == null then
576 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
577 mprop = new MAttribute(mclassdef, name, mvisibility)
578 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
579 else
580 assert mprop isa MAttribute
581 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
582 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
583 end
584 nclassdef.mprop2npropdef[mprop] = self
585
586 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
587 self.mpropdef = mpropdef
588 modelbuilder.mpropdef2npropdef[mpropdef] = self
589
590 var nreadable = self.n_readable
591 if nreadable != null then
592 var readname = name.substring_from(1)
593 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
594 if mreadprop == null then
595 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
596 mreadprop = new MMethod(mclassdef, readname, mvisibility)
597 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return
598 else
599 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return
600 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
601 end
602 nclassdef.mprop2npropdef[mreadprop] = self
603
604 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
605 self.mreadpropdef = mreadpropdef
606 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
607 end
608
609 var nwritable = self.n_writable
610 if nwritable != null then
611 var writename = name.substring_from(1) + "="
612 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
613 if mwriteprop == null then
614 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
615 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
616 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return
617 else
618 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return
619 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
620 end
621 nclassdef.mprop2npropdef[mwriteprop] = self
622
623 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
624 self.mwritepropdef = mwritepropdef
625 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
626 end
627 else
628 # New attribute style
629 var nid2 = self.n_id2.as(not null)
630 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
631 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
632 self.mpropdef = mpropdef
633 modelbuilder.mpropdef2npropdef[mpropdef] = self
634
635 var readname = name
636 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
637 if mreadprop == null then
638 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
639 mreadprop = new MMethod(mclassdef, readname, mvisibility)
640 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return
641 else
642 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return
643 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
644 end
645 nclassdef.mprop2npropdef[mreadprop] = self
646
647 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
648 self.mreadpropdef = mreadpropdef
649 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
650
651 var writename = name + "="
652 var nwritable = self.n_writable
653 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
654 var nwkwredef: nullable Token = null
655 if nwritable != null then nwkwredef = nwritable.n_kwredef
656 if mwriteprop == null then
657 var mvisibility
658 if nwritable != null then
659 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
660 else
661 mvisibility = private_visibility
662 end
663 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
664 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return
665 else
666 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return
667 if nwritable != null then
668 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
669 end
670 end
671 nclassdef.mprop2npropdef[mwriteprop] = self
672
673 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
674 self.mwritepropdef = mwritepropdef
675 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
676 end
677 end
678
679 redef fun build_signature(modelbuilder, nclassdef)
680 do
681 var mpropdef = self.mpropdef
682 if mpropdef == null then return # Error thus skiped
683 var mmodule = mpropdef.mclassdef.mmodule
684 var mtype: nullable MType = null
685
686 var ntype = self.n_type
687 if ntype != null then
688 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
689 if mtype == null then return
690 end
691
692 if mtype == null then
693 var nexpr = self.n_expr
694 if nexpr != null then
695 if nexpr isa ANewExpr then
696 mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
697 else if nexpr isa AIntExpr then
698 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
699 if cla != null then mtype = cla.mclass_type
700 else if nexpr isa AFloatExpr then
701 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
702 if cla != null then mtype = cla.mclass_type
703 else if nexpr isa ACharExpr then
704 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
705 if cla != null then mtype = cla.mclass_type
706 else if nexpr isa ABoolExpr then
707 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
708 if cla != null then mtype = cla.mclass_type
709 else if nexpr isa ASuperstringExpr then
710 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
711 if cla != null then mtype = cla.mclass_type
712 else if nexpr isa AStringFormExpr then
713 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
714 if cla != null then mtype = cla.mclass_type
715 else
716 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
717 end
718
719 else
720 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
721 end
722 end
723
724 if mtype == null then return
725
726 mpropdef.static_mtype = mtype
727
728 var mreadpropdef = self.mreadpropdef
729 if mreadpropdef != null then
730 var msignature = new MSignature(new Array[MParameter], mtype)
731 mreadpropdef.msignature = msignature
732 end
733
734 var msritepropdef = self.mwritepropdef
735 if mwritepropdef != null then
736 var name: String
737 if n_id != null then
738 name = n_id.text.substring_from(1)
739 else
740 name = n_id2.text
741 end
742 var mparameter = new MParameter(name, mtype, false)
743 var msignature = new MSignature([mparameter], null)
744 mwritepropdef.msignature = msignature
745 end
746 end
747
748 redef fun check_signature(modelbuilder, nclassdef)
749 do
750 var mpropdef = self.mpropdef
751 if mpropdef == null then return # Error thus skiped
752 var mmodule = mpropdef.mclassdef.mmodule
753 var ntype = self.n_type
754 var mtype = self.mpropdef.static_mtype
755 if mtype == null then return # Error thus skiped
756
757 # Lookup for signature in the precursor
758 # FIXME all precursors should be considered
759 if not mpropdef.is_intro then
760 var precursor_type = mpropdef.mproperty.intro.static_mtype
761 if precursor_type == null then return
762
763 if mtype != precursor_type then
764 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
765 return
766 end
767 end
768
769 # Check getter and setter
770 var meth = self.mreadpropdef
771 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
772 meth = self.mwritepropdef
773 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
774 end
775
776 private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef)
777 do
778 var mmodule = mpropdef.mclassdef.mmodule
779 var nsig = self.n_type
780 var mysignature = mpropdef.msignature
781 if mysignature == null then return # Error thus skiped
782
783 # Lookup for signature in the precursor
784 # FIXME all precursors should be considered
785 if not mpropdef.is_intro then
786 var msignature = mpropdef.mproperty.intro.msignature
787 if msignature == null then return
788
789 if mysignature.arity != msignature.arity then
790 var node: ANode
791 if nsig != null then node = nsig else node = self
792 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
793 return
794 end
795 var precursor_ret_type = msignature.return_mtype
796 var ret_type = mysignature.return_mtype
797 if ret_type != null and precursor_ret_type == null then
798 var node: ANode
799 if nsig != null then node = nsig else node = self
800 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
801 return
802 end
803
804 if mysignature.arity > 0 then
805 # Check parameters types
806 for i in [0..mysignature.arity[ do
807 var myt = mysignature.mparameters[i].mtype
808 var prt = msignature.mparameters[i].mtype
809 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
810 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
811 var node: ANode
812 if nsig != null then node = nsig else node = self
813 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
814 end
815 end
816 end
817 if precursor_ret_type != null then
818 if ret_type == null then
819 # Inherit the return type
820 ret_type = precursor_ret_type
821 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
822 var node: ANode
823 if nsig != null then node = nsig else node = self
824 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
825 end
826 end
827 end
828 end
829 end
830
831 redef class ATypePropdef
832 redef type MPROPDEF: MVirtualTypeDef
833
834 redef fun build_property(modelbuilder, nclassdef)
835 do
836 var mclassdef = nclassdef.mclassdef.as(not null)
837 var name = self.n_id.text
838 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
839 if mprop == null then
840 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
841 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
842 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
843 else
844 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
845 assert mprop isa MVirtualTypeProp
846 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
847 end
848 nclassdef.mprop2npropdef[mprop] = self
849
850 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
851 self.mpropdef = mpropdef
852 end
853
854 redef fun build_signature(modelbuilder, nclassdef)
855 do
856 var mpropdef = self.mpropdef
857 if mpropdef == null then return # Error thus skiped
858 var mmodule = mpropdef.mclassdef.mmodule
859 var mtype: nullable MType = null
860
861 var ntype = self.n_type
862 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
863 if mtype == null then return
864
865 mpropdef.bound = mtype
866 # print "{mpropdef}: {mtype}"
867 end
868
869 redef fun check_signature(modelbuilder, nclassdef)
870 do
871 var bound = self.mpropdef.bound
872
873 # Fast case: the bound is not a formal type
874 if not bound isa MVirtualType then return
875
876 var mmodule = nclassdef.mclassdef.mmodule
877 var anchor = nclassdef.mclassdef.bound_mtype
878
879 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
880 var seen = [self.mpropdef.mproperty.mvirtualtype]
881 loop
882 if seen.has(bound) then
883 seen.add(bound)
884 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
885 return
886 end
887 seen.add(bound)
888 var next = bound.lookup_bound(mmodule, anchor)
889 if not next isa MVirtualType then return
890 bound = next
891 end
892 end
893 end