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