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