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