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