modelbuilder: check redef signature in getter/setter
[nit.git] / src / modelbuilder.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 # Load nit source files and build the associated model
18 #
19 # FIXME better doc
20 #
21 # FIXME split this module into submodules
22 # FIXME add missing error checks
23 module modelbuilder
24
25 import parser
26 import model
27 import poset
28 import opts
29 import toolcontext
30
31 ###
32
33 redef class ToolContext
34 # Option --path
35 readable var _opt_path: OptionArray = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path")
36
37 # Option --only-metamodel
38 readable var _opt_only_metamodel: OptionBool = new OptionBool("Stop after meta-model processing", "--only-metamodel")
39
40 # Option --only-parse
41 readable var _opt_only_parse: OptionBool = new OptionBool("Only proceed to parse step of loaders", "--only-parse")
42
43 redef init
44 do
45 super
46 option_context.add_option(opt_path, opt_only_parse, opt_only_metamodel)
47 end
48 end
49
50 # A model builder knows how to load nit source files and build the associated model
51 # The important function is `parse_and_build' that does all the job.
52 # The others function can be used for specific tasks
53 class ModelBuilder
54 # The model where new modules, classes and properties are added
55 var model: Model
56
57 # The toolcontext used to control the interaction with the user (getting options and displaying messages)
58 var toolcontext: ToolContext
59
60 # Instantiate a modelbuilder for a model and a toolcontext
61 # Important, the options of the toolcontext must be correctly set (parse_option already called)
62 init(model: Model, toolcontext: ToolContext)
63 do
64 self.model = model
65 self.toolcontext = toolcontext
66
67 # Setup the paths value
68 paths.append(toolcontext.opt_path.value)
69
70 var path_env = "NIT_PATH".environ
71 if not path_env.is_empty then
72 paths.append(path_env.split_with(':'))
73 end
74
75 path_env = "NIT_DIR".environ
76 if not path_env.is_empty then
77 var libname = "{path_env}/lib"
78 if libname.file_exists then paths.add(libname)
79 end
80
81 var libname = "{sys.program_name.dirname}/../lib"
82 if libname.file_exists then paths.add(libname.simplify_path)
83 end
84
85 # Load and analyze a bunch of modules.
86 # `modules' can contains filenames or module names.
87 # Imported modules are automatically loaded, builds and analysed.
88 # The result is the corresponding built modules.
89 # Errors and warnings are printed with the toolcontext.
90 #
91 # FIXME: Maybe just let the client do the loop (instead of playing with Sequences)
92 fun parse_and_build(modules: Sequence[String]): Array[MModule]
93 do
94 var time0 = get_time
95 # Parse and recursively load
96 self.toolcontext.info("*** PARSE ***", 1)
97 var mmodules = new Array[MModule]
98 for a in modules do
99 var nmodule = self.load_module(null, a)
100 if nmodule == null then continue # Skip error
101 mmodules.add(nmodule.mmodule.as(not null))
102 end
103 var time1 = get_time
104 self.toolcontext.info("*** END PARSE: {time1-time0} ***", 2)
105
106 self.toolcontext.check_errors
107
108 if self.toolcontext.opt_only_parse.value then
109 self.toolcontext.info("--only-parse: stop processing", 2)
110 return new Array[MModule]
111 end
112
113 # Build the model
114 self.toolcontext.info("*** BUILD MODEL ***", 1)
115 self.build_all_classes
116 var time2 = get_time
117 self.toolcontext.info("*** END BUILD MODEL: {time2-time1} ***", 2)
118
119 self.toolcontext.check_errors
120
121 return mmodules
122 end
123
124 # Return a class named `name' visible by the module `mmodule'.
125 # Visibility in modules is correctly handled.
126 # If no such a class exists, then null is returned.
127 # If more than one class exists, then an error on `anode' is displayed and null is returned.
128 # FIXME: add a way to handle class name conflict
129 fun try_get_mclass_by_name(anode: ANode, mmodule: MModule, name: String): nullable MClass
130 do
131 var classes = model.get_mclasses_by_name(name)
132 if classes == null then
133 return null
134 end
135
136 var res: nullable MClass = null
137 for mclass in classes do
138 if not mmodule.in_importation <= mclass.intro_mmodule then continue
139 if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then continue
140 if res == null then
141 res = mclass
142 else
143 error(anode, "Ambigous class name '{name}'; conflict between {mclass.full_name} and {res.full_name}")
144 return null
145 end
146 end
147 return res
148 end
149
150 # Return a property named `name' on the type `mtype' visible in the module `mmodule'.
151 # Visibility in modules is correctly handled.
152 # Protected properties are returned (it is up to the caller to check and reject protected properties).
153 # If no such a property exists, then null is returned.
154 # If more than one property exists, then an error on `anode' is displayed and null is returned.
155 # FIXME: add a way to handle property name conflict
156 fun try_get_mproperty_by_name2(anode: ANode, mmodule: MModule, mtype: MType, name: String): nullable MProperty
157 do
158 var props = self.model.get_mproperties_by_name(name)
159 if props == null then
160 return null
161 end
162
163 var cache = self.try_get_mproperty_by_name2_cache[mmodule, mtype, name]
164 if cache != null then return cache
165
166 var res: nullable MProperty = null
167 var ress: nullable Array[MProperty] = null
168 for mprop in props do
169 if not mtype.has_mproperty(mmodule, mprop) then continue
170 if not mmodule.is_visible(mprop.intro_mclassdef.mmodule, mprop.visibility) then continue
171 if res == null then
172 res = mprop
173 else
174 var restype = res.intro_mclassdef.bound_mtype
175 var mproptype = mprop.intro_mclassdef.bound_mtype
176 if restype.is_subtype(mmodule, null, mproptype) then
177 # we keep res
178 else if mproptype.is_subtype(mmodule, null, restype) then
179 res = mprop
180 else
181 if ress == null then ress = new Array[MProperty]
182 ress.add(mprop)
183 end
184 end
185 end
186 if ress != null then
187 var restype = res.intro_mclassdef.bound_mtype
188 for mprop in ress do
189 var mproptype = mprop.intro_mclassdef.bound_mtype
190 if not restype.is_subtype(mmodule, null, mproptype) then
191 self.error(anode, "Ambigous property name '{name}' for {mtype}; conflict between {mprop.full_name} and {res.full_name}")
192 return null
193 end
194 end
195 end
196
197 self.try_get_mproperty_by_name2_cache[mmodule, mtype, name] = res
198 return res
199 end
200
201 private var try_get_mproperty_by_name2_cache: HashMap3[MModule, MType, String, nullable MProperty] = new HashMap3[MModule, MType, String, nullable MProperty]
202
203
204 # Alias for try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.mtype, name)
205 fun try_get_mproperty_by_name(anode: ANode, mclassdef: MClassDef, name: String): nullable MProperty
206 do
207 return try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.bound_mtype, name)
208 end
209
210 # The list of directories to search for top level modules
211 # The list is initially set with :
212 # * the toolcontext --path option
213 # * the NIT_PATH environment variable
214 # * some heuristics including the NIT_DIR environment variable and the progname of the process
215 # Path can be added (or removed) by the client
216 var paths: Array[String] = new Array[String]
217
218 # Get a module by its short name; if required, the module is loaded, parsed and its hierarchies computed.
219 # If `mmodule' is set, then the module search starts from it up to the top level (see `paths');
220 # if `mmodule' is null then the module is searched in the top level only.
221 # If no module exists or there is a name conflict, then an error on `anode' is displayed and null is returned.
222 # FIXME: add a way to handle module name conflict
223 fun get_mmodule_by_name(anode: ANode, mmodule: nullable MModule, name: String): nullable MModule
224 do
225 var origmmodule = mmodule
226 var modules = model.get_mmodules_by_name(name)
227
228 var tries = new Array[String]
229
230 var lastmodule = mmodule
231 while mmodule != null do
232 var dirname = mmodule.location.file.filename.dirname
233
234 # Determine the owner
235 var owner: nullable MModule
236 if dirname.basename("") != mmodule.name then
237 owner = mmodule.direct_owner
238 else
239 owner = mmodule
240 end
241
242 # First, try the already known nested modules
243 if modules != null then
244 for candidate in modules do
245 if candidate.direct_owner == owner then
246 return candidate
247 end
248 end
249 end
250
251 # Second, try the directory to find a file
252 var try_file = dirname + "/" + name + ".nit"
253 tries.add try_file
254 if try_file.file_exists then
255 var res = self.load_module(owner, try_file.simplify_path)
256 if res == null then return null # Forward error
257 return res.mmodule.as(not null)
258 end
259
260 # Third, try if the requested module is itself an owner
261 try_file = dirname + "/" + name + "/" + name + ".nit"
262 if try_file.file_exists then
263 var res = self.load_module(owner, try_file.simplify_path)
264 if res == null then return null # Forward error
265 return res.mmodule.as(not null)
266 end
267
268 lastmodule = mmodule
269 mmodule = mmodule.direct_owner
270 end
271
272 if modules != null then
273 for candidate in modules do
274 if candidate.direct_owner == null then
275 return candidate
276 end
277 end
278 end
279
280 # Look at some known directories
281 var lookpaths = self.paths
282
283 # Look in the directory of the last module also (event if not in the path)
284 if lastmodule != null then
285 var dirname = lastmodule.location.file.filename.dirname
286 if dirname.basename("") == lastmodule.name then
287 dirname = dirname.dirname
288 end
289 if not lookpaths.has(dirname) then
290 lookpaths = lookpaths.to_a
291 lookpaths.add(dirname)
292 end
293 end
294
295 var candidate: nullable String = null
296 for dirname in lookpaths do
297 var try_file = (dirname + "/" + name + ".nit").simplify_path
298 tries.add try_file
299 if try_file.file_exists then
300 if candidate == null then
301 candidate = try_file
302 else if candidate != try_file then
303 error(anode, "Error: conflicting module file for {name}: {candidate} {try_file}")
304 end
305 end
306 try_file = (dirname + "/" + name + "/" + name + ".nit").simplify_path
307 if try_file.file_exists then
308 if candidate == null then
309 candidate = try_file
310 else if candidate != try_file then
311 error(anode, "Error: conflicting module file for {name}: {candidate} {try_file}")
312 end
313 end
314 end
315 if candidate == null then
316 if origmmodule != null then
317 error(anode, "Error: cannot find module {name} from {origmmodule}. tried {tries.join(", ")}")
318 else
319 error(anode, "Error: cannot find module {name}. tried {tries.join(", ")}")
320 end
321 return null
322 end
323 var res = self.load_module(mmodule, candidate)
324 if res == null then return null # Forward error
325 return res.mmodule.as(not null)
326 end
327
328 # Try to load a module using a path.
329 # Display an error if there is a problem (IO / lexer / parser) and return null
330 # Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
331 fun load_module(owner: nullable MModule, filename: String): nullable AModule
332 do
333 if not filename.file_exists then
334 self.toolcontext.error(null, "Error: file {filename} not found.")
335 return null
336 end
337
338 var x = if owner != null then owner.to_s else "."
339 self.toolcontext.info("load module {filename} in {x}", 2)
340
341 # Load the file
342 var file = new IFStream.open(filename)
343 var lexer = new Lexer(new SourceFile(filename, file))
344 var parser = new Parser(lexer)
345 var tree = parser.parse
346 file.close
347
348 # Handle lexer and parser error
349 var nmodule = tree.n_base
350 if nmodule == null then
351 var neof = tree.n_eof
352 assert neof isa AError
353 error(neof, neof.message)
354 return null
355 end
356
357 # Check the module name
358 var mod_name = filename.basename(".nit")
359 var decl = nmodule.n_moduledecl
360 if decl == null then
361 #warning(nmodule, "Warning: Missing 'module' keyword") #FIXME: NOT YET FOR COMPATIBILITY
362 else
363 var decl_name = decl.n_name.n_id.text
364 if decl_name != mod_name then
365 error(decl.n_name, "Error: module name missmatch; declared {decl_name} file named {mod_name}")
366 end
367 end
368
369 # Create the module
370 var mmodule = new MModule(model, owner, mod_name, nmodule.location)
371 nmodule.mmodule = mmodule
372 nmodules.add(nmodule)
373 self.mmodule2nmodule[mmodule] = nmodule
374
375 build_module_importation(nmodule)
376
377 return nmodule
378 end
379
380 # Analysis the module importation and fill the module_importation_hierarchy
381 private fun build_module_importation(nmodule: AModule)
382 do
383 if nmodule.is_importation_done then return
384 var mmodule = nmodule.mmodule.as(not null)
385 var stdimport = true
386 var imported_modules = new Array[MModule]
387 for aimport in nmodule.n_imports do
388 stdimport = false
389 if not aimport isa AStdImport then
390 continue
391 end
392 var mod_name = aimport.n_name.n_id.text
393 var sup = self.get_mmodule_by_name(aimport.n_name, mmodule, mod_name)
394 if sup == null then continue # Skip error
395 imported_modules.add(sup)
396 var mvisibility = aimport.n_visibility.mvisibility
397 mmodule.set_visibility_for(sup, mvisibility)
398 end
399 if stdimport then
400 var mod_name = "standard"
401 var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
402 if sup != null then # Skip error
403 imported_modules.add(sup)
404 mmodule.set_visibility_for(sup, public_visibility)
405 end
406 end
407 self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
408 mmodule.set_imported_mmodules(imported_modules)
409 nmodule.is_importation_done = true
410 end
411
412 # All the loaded modules
413 var nmodules: Array[AModule] = new Array[AModule]
414
415 # Build the classes of all modules `nmodules'.
416 private fun build_all_classes
417 do
418 for nmodule in self.nmodules do
419 build_classes(nmodule)
420 end
421 end
422
423 # Visit the AST and create the MClass objects
424 private fun build_a_mclass(nmodule: AModule, nclassdef: AClassdef)
425 do
426 var mmodule = nmodule.mmodule.as(not null)
427
428 var name: String
429 var nkind: nullable AClasskind
430 var mkind: MClassKind
431 var nvisibility: nullable AVisibility
432 var mvisibility: nullable MVisibility
433 var arity = 0
434 if nclassdef isa AStdClassdef then
435 name = nclassdef.n_id.text
436 nkind = nclassdef.n_classkind
437 mkind = nkind.mkind
438 nvisibility = nclassdef.n_visibility
439 mvisibility = nvisibility.mvisibility
440 arity = nclassdef.n_formaldefs.length
441 else if nclassdef isa ATopClassdef then
442 name = "Object"
443 nkind = null
444 mkind = interface_kind
445 nvisibility = null
446 mvisibility = public_visibility
447 else if nclassdef isa AMainClassdef then
448 name = "Sys"
449 nkind = null
450 mkind = concrete_kind
451 nvisibility = null
452 mvisibility = public_visibility
453 else
454 abort
455 end
456
457 var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
458 if mclass == null then
459 mclass = new MClass(mmodule, name, arity, mkind, mvisibility)
460 #print "new class {mclass}"
461 else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then
462 error(nclassdef, "Redef error: {name} is an imported class. Add the redef keyword to refine it.")
463 return
464 else if mclass.arity != arity then
465 error(nclassdef, "Redef error: Formal parameter arity missmatch; got {arity}, expected {mclass.arity}.")
466 return
467 else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then
468 error(nkind, "Error: refinement changed the kind from a {mclass.kind} to a {mkind}")
469 else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
470 error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
471 end
472 nclassdef.mclass = mclass
473 end
474
475 # Visit the AST and create the MClassDef objects
476 private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
477 do
478 var mmodule = nmodule.mmodule.as(not null)
479 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
480 var mclass = nclassdef.mclass.as(not null)
481 #var mclassdef = nclassdef.mclassdef.as(not null)
482
483 var names = new Array[String]
484 var bounds = new Array[MType]
485 if nclassdef isa AStdClassdef and mclass.arity > 0 then
486 # Collect formal parameter names
487 for i in [0..mclass.arity[ do
488 var nfd = nclassdef.n_formaldefs[i]
489 var ptname = nfd.n_id.text
490 if names.has(ptname) then
491 error(nfd, "Error: A formal parameter type `{ptname}' already exists")
492 return
493 end
494 names.add(ptname)
495 end
496
497 # Revolve bound for formal parameter names
498 for i in [0..mclass.arity[ do
499 var nfd = nclassdef.n_formaldefs[i]
500 var nfdt = nfd.n_type
501 if nfdt != null then
502 var bound = resolve_mtype(nclassdef, nfdt)
503 if bound == null then return # Forward error
504 if bound.need_anchor then
505 # No F-bounds!
506 error(nfd, "Error: Formal parameter type `{names[i]}' bounded with a formal parameter type")
507 else
508 bounds.add(bound)
509 end
510 else if mclass.mclassdefs.is_empty then
511 # No bound, then implicitely bound by nullable Object
512 bounds.add(objectclass.mclass_type.as_nullable)
513 else
514 # Inherit the bound
515 bounds.add(mclass.mclassdefs.first.bound_mtype.as(MGenericType).arguments[i])
516 end
517 end
518 end
519
520 var bound_mtype = mclass.get_mtype(bounds)
521 var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names)
522 nclassdef.mclassdef = mclassdef
523 self.mclassdef2nclassdef[mclassdef] = nclassdef
524
525 if mclassdef.is_intro then
526 self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
527 else
528 self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
529 end
530 end
531
532 # Visit the AST and set the super-types of the MClass objects (ie compute the inheritance)
533 private fun build_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
534 do
535 var mmodule = nmodule.mmodule.as(not null)
536 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
537 var mclass = nclassdef.mclass.as(not null)
538 var mclassdef = nclassdef.mclassdef.as(not null)
539
540 var specobject = true
541 var supertypes = new Array[MClassType]
542 if nclassdef isa AStdClassdef then
543 for nsc in nclassdef.n_superclasses do
544 specobject = false
545 var ntype = nsc.n_type
546 var mtype = resolve_mtype(nclassdef, ntype)
547 if mtype == null then continue # Skip because of error
548 if not mtype isa MClassType then
549 error(ntype, "Error: supertypes cannot be a formal type")
550 return
551 end
552 supertypes.add mtype
553 #print "new super : {mclass} < {mtype}"
554 end
555 end
556 if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
557 supertypes.add objectclass.mclass_type
558 end
559
560 mclassdef.set_supertypes(supertypes)
561 if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
562 end
563
564 # Check the validity of the specialization heirarchy
565 # FIXME Stub implementation
566 private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef)
567 do
568 var mmodule = nmodule.mmodule.as(not null)
569 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
570 var mclass = nclassdef.mclass.as(not null)
571 var mclassdef = nclassdef.mclassdef.as(not null)
572 end
573
574 # Build the classes of the module `nmodule'.
575 # REQUIRE: classes of imported modules are already build. (let `build_all_classes' do the job)
576 private fun build_classes(nmodule: AModule)
577 do
578 # Force building recursively
579 if nmodule.build_classes_is_done then return
580 var mmodule = nmodule.mmodule.as(not null)
581 for imp in mmodule.in_importation.direct_greaters do
582 build_classes(mmodule2nmodule[imp])
583 end
584
585 # Create all classes
586 for nclassdef in nmodule.n_classdefs do
587 self.build_a_mclass(nmodule, nclassdef)
588 end
589
590 # Create all classdefs
591 for nclassdef in nmodule.n_classdefs do
592 self.build_a_mclassdef(nmodule, nclassdef)
593 end
594
595 # Create inheritance on all classdefs
596 for nclassdef in nmodule.n_classdefs do
597 self.build_a_mclassdef_inheritance(nmodule, nclassdef)
598 end
599
600 # TODO: Check that the super-class is not intrusive
601
602 # TODO: Check that the super-class is not already known (by transitivity)
603
604 for nclassdef in nmodule.n_classdefs do
605 self.build_properties(nclassdef)
606 end
607
608 nmodule.build_classes_is_done = true
609 end
610
611 # Register the nmodule associated to each mmodule
612 # FIXME: why not refine the MModule class with a nullable attribute?
613 var mmodule2nmodule: HashMap[MModule, AModule] = new HashMap[MModule, AModule]
614 # Register the nclassdef associated to each mclassdef
615 # FIXME: why not refine the MClassDef class with a nullable attribute?
616 var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef]
617 # Register the npropdef associated to each mpropdef
618 # FIXME: why not refine the MPropDef class with a nullable attribute?
619 var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
620
621 # Build the properties of `nclassdef'.
622 # REQUIRE: all superclasses are built.
623 private fun build_properties(nclassdef: AClassdef)
624 do
625 # Force building recursively
626 if nclassdef.build_properties_is_done then return
627 var mclassdef = nclassdef.mclassdef.as(not null)
628 if mclassdef.in_hierarchy == null then return # Skip error
629 for superclassdef in mclassdef.in_hierarchy.direct_greaters do
630 build_properties(mclassdef2nclassdef[superclassdef])
631 end
632
633 for npropdef in nclassdef.n_propdefs do
634 npropdef.build_property(self, nclassdef)
635 end
636 for npropdef in nclassdef.n_propdefs do
637 npropdef.build_signature(self, nclassdef)
638 end
639 for npropdef in nclassdef.n_propdefs do
640 npropdef.check_signature(self, nclassdef)
641 end
642 process_default_constructors(nclassdef)
643 nclassdef.build_properties_is_done = true
644 end
645
646 # Introduce or inherit default constructor
647 # This is the last part of `build_properties'.
648 private fun process_default_constructors(nclassdef: AClassdef)
649 do
650 var mclassdef = nclassdef.mclassdef.as(not null)
651
652 # Are we a refinement
653 if not mclassdef.is_intro then return
654
655 # Is the class forbid constructors?
656 if not mclassdef.mclass.kind.need_init then return
657
658 # Is there already a constructor defined?
659 for mpropdef in mclassdef.mpropdefs do
660 if not mpropdef isa MMethodDef then continue
661 if mpropdef.mproperty.is_init then return
662 end
663
664 if not nclassdef isa AStdClassdef then return
665
666 var mmodule = nclassdef.mclassdef.mmodule
667 # Do we inherit for a constructor?
668 var combine = new Array[MMethod]
669 var inhc: nullable MClass = null
670 for st in mclassdef.supertypes do
671 var c = st.mclass
672 if not c.kind.need_init then continue
673 st = st.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
674 var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod)
675 if candidate != null and candidate.intro.msignature.arity == 0 then
676 combine.add(candidate)
677 continue
678 end
679 var inhc2 = c.inherit_init_from
680 if inhc2 == null then inhc2 = c
681 if inhc2 == inhc then continue
682 if inhc != null then
683 self.error(nclassdef, "Cannot provide a defaut constructor: conflict for {inhc} and {c}")
684 else
685 inhc = inhc2
686 end
687 end
688 if combine.is_empty and inhc != null then
689 # TODO: actively inherit the consturctor
690 self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3)
691 mclassdef.mclass.inherit_init_from = inhc
692 return
693 end
694 if not combine.is_empty and inhc != null then
695 self.error(nclassdef, "Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}")
696 return
697 end
698
699 if not combine.is_empty then
700 nclassdef.super_inits = combine
701 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
702 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
703 var mparameters = new Array[MParameter]
704 var msignature = new MSignature(mparameters, null)
705 mpropdef.msignature = msignature
706 mprop.is_init = true
707 nclassdef.mfree_init = mpropdef
708 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
709 return
710 end
711
712 # Collect undefined attributes
713 var mparameters = new Array[MParameter]
714 for npropdef in nclassdef.n_propdefs do
715 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
716 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
717 var ret_type = npropdef.mpropdef.static_mtype
718 if ret_type == null then return
719 var mparameter = new MParameter(paramname, ret_type, false)
720 mparameters.add(mparameter)
721 end
722 end
723
724 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
725 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
726 var msignature = new MSignature(mparameters, null)
727 mpropdef.msignature = msignature
728 mprop.is_init = true
729 nclassdef.mfree_init = mpropdef
730 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
731 end
732
733 # Return the static type associated to the node `ntype'.
734 # `classdef' is the context where the call is made (used to understand formal types)
735 # The mmodule used as context is `nclassdef.mmodule'
736 # In case of problem, an error is displayed on `ntype' and null is returned.
737 # FIXME: the name "resolve_mtype" is awful
738 fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
739 do
740 var name = ntype.n_id.text
741 var mclassdef = nclassdef.mclassdef
742 var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
743 var res: MType
744
745 # Check virtual type
746 if mclassdef != null then
747 var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp)
748 if prop != null then
749 if not ntype.n_types.is_empty then
750 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
751 end
752 res = prop.mvirtualtype
753 if ntype.n_kwnullable != null then res = res.as_nullable
754 return res
755 end
756 end
757
758 # Check parameter type
759 if mclassdef != null and mclassdef.parameter_names.has(name) then
760 if not ntype.n_types.is_empty then
761 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
762 end
763 for i in [0..mclassdef.parameter_names.length[ do
764 if mclassdef.parameter_names[i] == name then
765 res = mclassdef.mclass.mclass_type.as(MGenericType).arguments[i]
766 if ntype.n_kwnullable != null then res = res.as_nullable
767 return res
768 end
769 end
770 abort
771 end
772
773 # Check class
774 var mclass = try_get_mclass_by_name(ntype, mmodule, name)
775 if mclass != null then
776 var arity = ntype.n_types.length
777 if arity != mclass.arity then
778 if arity == 0 then
779 error(ntype, "Type error: '{name}' is a generic class.")
780 else if mclass.arity == 0 then
781 error(ntype, "Type error: '{name}' is not a generic class.")
782 else
783 error(ntype, "Type error: '{name}' has {mclass.arity} parameters ({arity} are provided).")
784 end
785 return null
786 end
787 if arity == 0 then
788 res = mclass.mclass_type
789 if ntype.n_kwnullable != null then res = res.as_nullable
790 return res
791 else
792 var mtypes = new Array[MType]
793 for nt in ntype.n_types do
794 var mt = resolve_mtype(nclassdef, nt)
795 if mt == null then return null # Forward error
796 mtypes.add(mt)
797 end
798 res = mclass.get_mtype(mtypes)
799 if ntype.n_kwnullable != null then res = res.as_nullable
800 return res
801 end
802 end
803
804 # If everything fail, then give up :(
805 error(ntype, "Type error: class {name} not found in module {mmodule}.")
806 return null
807 end
808
809 # Helper function to display an error on a node.
810 # Alias for `self.toolcontext.error(n.hot_location, text)'
811 fun error(n: ANode, text: String)
812 do
813 self.toolcontext.error(n.hot_location, text)
814 end
815
816 # Helper function to display a warning on a node.
817 # Alias for: `self.toolcontext.warning(n.hot_location, text)'
818 fun warning(n: ANode, text: String)
819 do
820 self.toolcontext.warning(n.hot_location, text)
821 end
822 end
823
824 redef class AModule
825 # The associated MModule once build by a `ModelBuilder'
826 var mmodule: nullable MModule
827 # Flag that indicate if the importation is already completed
828 var is_importation_done: Bool = false
829 # Flag that indicate if the class and prop building is already completed
830 var build_classes_is_done: Bool = false
831 end
832
833 redef class MClass
834 # The class whose self inherit all the constructors.
835 # 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
836 var inherit_init_from: nullable MClass = null
837 end
838
839 redef class AClassdef
840 # The associated MClass once build by a `ModelBuilder'
841 var mclass: nullable MClass
842 # The associated MClassDef once build by a `ModelBuilder'
843 var mclassdef: nullable MClassDef
844 var build_properties_is_done: Bool = false
845 # The list of super-constructor to call at the start of the free constructor
846 # 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
847 var super_inits: nullable Collection[MMethod] = null
848
849 # The free init (implicitely constructed by the class if required)
850 var mfree_init: nullable MMethodDef = null
851 end
852
853 redef class AClasskind
854 # The class kind associated with the AST node class
855 private fun mkind: MClassKind is abstract
856 end
857 redef class AConcreteClasskind
858 redef fun mkind do return concrete_kind
859 end
860 redef class AAbstractClasskind
861 redef fun mkind do return abstract_kind
862 end
863 redef class AInterfaceClasskind
864 redef fun mkind do return interface_kind
865 end
866 redef class AEnumClasskind
867 redef fun mkind do return enum_kind
868 end
869 redef class AExternClasskind
870 redef fun mkind do return extern_kind
871 end
872
873 redef class AVisibility
874 # The visibility level associated with the AST node class
875 private fun mvisibility: MVisibility is abstract
876 end
877 redef class AIntrudeVisibility
878 redef fun mvisibility do return intrude_visibility
879 end
880 redef class APublicVisibility
881 redef fun mvisibility do return public_visibility
882 end
883 redef class AProtectedVisibility
884 redef fun mvisibility do return protected_visibility
885 end
886 redef class APrivateVisibility
887 redef fun mvisibility do return private_visibility
888 end
889
890
891 #
892
893 redef class Prod
894 # Join the text of all tokens
895 # Used to get the 'real name' of method definitions.
896 fun collect_text: String
897 do
898 var v = new TextCollectorVisitor
899 v.enter_visit(self)
900 assert v.text != ""
901 return v.text
902 end
903 end
904
905 private class TextCollectorVisitor
906 super Visitor
907 var text: String = ""
908 redef fun visit(n)
909 do
910 if n isa Token then text += n.text
911 n.visit_all(self)
912 end
913 end
914
915 redef class APropdef
916 private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef)
917 do
918 end
919 private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
920 do
921 end
922 private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
923 do
924 end
925 private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility
926 do
927 var mvisibility = public_visibility
928 if nvisibility != null then mvisibility = nvisibility.mvisibility
929 if nclassdef.mclassdef.mclass.visibility == private_visibility then
930 if mvisibility == protected_visibility then
931 assert nvisibility != null
932 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
933 else if mvisibility == private_visibility then
934 assert nvisibility != null
935 # Not yet
936 # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.")
937 end
938 mvisibility = private_visibility
939 end
940 return mvisibility
941 end
942
943 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty)
944 do
945 if nvisibility == null then return
946 var mvisibility = nvisibility.mvisibility
947 if mvisibility != mprop.visibility and mvisibility != public_visibility then
948 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
949 end
950 end
951
952 private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty)
953 do
954 if kwredef == null then
955 if need_redef then
956 modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
957 end
958 else
959 if not need_redef then
960 modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
961 end
962 end
963 end
964
965 end
966
967 redef class ASignature
968 # Is the model builder has correctly visited the signature
969 var is_visited = false
970 # Names of parameters from the AST
971 # REQUIRE: is_visited
972 var param_names = new Array[String]
973 # Types of parameters from the AST
974 # REQUIRE: is_visited
975 var param_types = new Array[MType]
976 # Rank of the vararg (of -1 if none)
977 # REQUIRE: is_visited
978 var vararg_rank: Int = -1
979 # Return type
980 var ret_type: nullable MType = null
981
982 # Visit and fill information about a signature
983 private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool
984 do
985 var param_names = self.param_names
986 var param_types = self.param_types
987 for np in self.n_params do
988 param_names.add(np.n_id.text)
989 var ntype = np.n_type
990 if ntype != null then
991 var mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
992 if mtype == null then return false # Skip error
993 for i in [0..param_names.length-param_types.length[ do
994 param_types.add(mtype)
995 end
996 if np.n_dotdotdot != null then
997 if self.vararg_rank != -1 then
998 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
999 return false
1000 else
1001 self.vararg_rank = param_names.length - 1
1002 end
1003 end
1004 end
1005 end
1006 var ntype = self.n_type
1007 if ntype != null then
1008 self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype)
1009 if self.ret_type == null then return false # Skip errir
1010 end
1011
1012 for nclosure in self.n_closure_decls do
1013 if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false
1014 end
1015
1016 self.is_visited = true
1017 return true
1018 end
1019
1020 # Build a visited signature
1021 fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature
1022 do
1023 if param_names.length != param_types.length then
1024 # Some parameters are typed, other parameters are not typed.
1025 modelbuilder.warning(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1026 return null
1027 end
1028
1029 var mparameters = new Array[MParameter]
1030 for i in [0..param_names.length[ do
1031 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1032 mparameters.add(mparameter)
1033 end
1034
1035 var msignature = new MSignature(mparameters, ret_type)
1036 return msignature
1037 end
1038 end
1039
1040 redef class AMethPropdef
1041 # The associated MMethodDef once build by a `ModelBuilder'
1042 var mpropdef: nullable MMethodDef
1043
1044 # The associated super init if any
1045 var super_init: nullable MMethod
1046 redef fun build_property(modelbuilder, nclassdef)
1047 do
1048 var is_init = self isa AInitPropdef
1049 var mclassdef = nclassdef.mclassdef.as(not null)
1050 var name: String
1051 var amethodid = self.n_methid
1052 var name_node: ANode
1053 if amethodid == null then
1054 if self isa AMainMethPropdef then
1055 name = "main"
1056 name_node = self
1057 else if self isa AConcreteInitPropdef then
1058 name = "init"
1059 name_node = self.n_kwinit
1060 else if self isa AExternInitPropdef then
1061 name = "new"
1062 name_node = self.n_kwnew
1063 else
1064 abort
1065 end
1066 else if amethodid isa AIdMethid then
1067 name = amethodid.n_id.text
1068 name_node = amethodid
1069 else
1070 # operator, bracket or assign
1071 name = amethodid.collect_text
1072 name_node = amethodid
1073
1074 if name == "-" and self.n_signature.n_params.length == 0 then
1075 name = "unary -"
1076 end
1077 end
1078
1079 var mprop: nullable MMethod = null
1080 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
1081 if mprop == null then
1082 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1083 mprop = new MMethod(mclassdef, name, mvisibility)
1084 mprop.is_init = is_init
1085 mprop.is_new = self isa AExternInitPropdef
1086 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop)
1087 else
1088 if n_kwredef == null then
1089 if self isa AMainMethPropdef then
1090 # no warning
1091 else
1092 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop)
1093 end
1094 end
1095 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1096 end
1097
1098 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
1099
1100 self.mpropdef = mpropdef
1101 modelbuilder.mpropdef2npropdef[mpropdef] = self
1102 if mpropdef.is_intro then
1103 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
1104 else
1105 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
1106 end
1107 end
1108
1109 redef fun build_signature(modelbuilder, nclassdef)
1110 do
1111 var mpropdef = self.mpropdef
1112 if mpropdef == null then return # Error thus skiped
1113 var mmodule = mpropdef.mclassdef.mmodule
1114 var nsig = self.n_signature
1115
1116 # Retrieve info from the signature AST
1117 var param_names = new Array[String] # Names of parameters from the AST
1118 var param_types = new Array[MType] # Types of parameters from the AST
1119 var vararg_rank = -1
1120 var ret_type: nullable MType = null # Return type from the AST
1121 if nsig != null then
1122 if not nsig.visit_signature(modelbuilder, nclassdef) then return
1123 param_names = nsig.param_names
1124 param_types = nsig.param_types
1125 vararg_rank = nsig.vararg_rank
1126 ret_type = nsig.ret_type
1127 end
1128
1129 # Look for some signature to inherit
1130 # FIXME: do not inherit from the intro, but from the most specific
1131 var msignature: nullable MSignature = null
1132 if not mpropdef.is_intro then
1133 msignature = mpropdef.mproperty.intro.msignature
1134 if msignature == null then return # Skip error
1135 else if mpropdef.mproperty.is_init then
1136 # FIXME UGLY: inherit signature from a super-constructor
1137 for msupertype in nclassdef.mclassdef.supertypes do
1138 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
1139 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
1140 if candidate != null then
1141 if msignature == null then
1142 msignature = candidate.intro.as(MMethodDef).msignature
1143 end
1144 end
1145 end
1146 end
1147
1148 # Inherit the signature
1149 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
1150 # Parameters are untyped, thus inherit them
1151 param_types = new Array[MType]
1152 for mparameter in msignature.mparameters do
1153 param_types.add(mparameter.mtype)
1154 end
1155 vararg_rank = msignature.vararg_rank
1156 end
1157 if msignature != null and ret_type == null then
1158 ret_type = msignature.return_mtype
1159 end
1160
1161 if param_names.length != param_types.length then
1162 # Some parameters are typed, other parameters are not typed.
1163 modelbuilder.warning(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1164 return
1165 end
1166
1167 var mparameters = new Array[MParameter]
1168 for i in [0..param_names.length[ do
1169 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1170 mparameters.add(mparameter)
1171 end
1172
1173 msignature = new MSignature(mparameters, ret_type)
1174 mpropdef.msignature = msignature
1175
1176 if nsig != null then
1177 for nclosure in nsig.n_closure_decls do
1178 var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef)
1179 if clos_signature == null then return
1180 var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false)
1181 msignature.mclosures.add(mparameter)
1182 end
1183 end
1184
1185 end
1186
1187 redef fun check_signature(modelbuilder, nclassdef)
1188 do
1189 var mpropdef = self.mpropdef
1190 if mpropdef == null then return # Error thus skiped
1191 var mmodule = mpropdef.mclassdef.mmodule
1192 var nsig = self.n_signature
1193 var mysignature = self.mpropdef.msignature
1194 if mysignature == null then return # Error thus skiped
1195
1196 # Lookup for signature in the precursor
1197 # FIXME all precursors should be considered
1198 if not mpropdef.is_intro then
1199 var msignature = mpropdef.mproperty.intro.msignature
1200 if msignature == null then return
1201
1202 if mysignature.arity != msignature.arity then
1203 var node: ANode
1204 if nsig != null then node = nsig else node = self
1205 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1206 return
1207 end
1208 var precursor_ret_type = msignature.return_mtype
1209 var ret_type = mysignature.return_mtype
1210 if ret_type != null and precursor_ret_type == null then
1211 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1212 return
1213 end
1214
1215 if mysignature.arity > 0 then
1216 # Check parameters types
1217 for i in [0..mysignature.arity[ do
1218 var myt = mysignature.mparameters[i].mtype
1219 var prt = msignature.mparameters[i].mtype
1220 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1221 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1222 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1223 end
1224 end
1225 end
1226 if precursor_ret_type != null then
1227 if ret_type == null then
1228 # Inherit the return type
1229 ret_type = precursor_ret_type
1230 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1231 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1232 end
1233 end
1234 end
1235 end
1236 end
1237
1238 redef class AAttrPropdef
1239 # The associated MAttributeDef once build by a `ModelBuilder'
1240 var mpropdef: nullable MAttributeDef
1241 # The associated getter (read accessor) if any
1242 var mreadpropdef: nullable MMethodDef
1243 # The associated setter (write accessor) if any
1244 var mwritepropdef: nullable MMethodDef
1245 redef fun build_property(modelbuilder, nclassdef)
1246 do
1247 var mclassdef = nclassdef.mclassdef.as(not null)
1248 var mclass = mclassdef.mclass
1249
1250 var name: String
1251 if self.n_id != null then
1252 name = self.n_id.text
1253 else
1254 name = self.n_id2.text
1255 end
1256
1257 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
1258 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
1259 else if mclass.kind == enum_kind then
1260 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
1261 end
1262
1263 var nid = self.n_id
1264 if nid != null then
1265 # Old attribute style
1266 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
1267 if mprop == null then
1268 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1269 mprop = new MAttribute(mclassdef, name, mvisibility)
1270 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1271 else
1272 assert mprop isa MAttribute
1273 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1274 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1275 end
1276 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1277 self.mpropdef = mpropdef
1278 modelbuilder.mpropdef2npropdef[mpropdef] = self
1279
1280 var nreadable = self.n_readable
1281 if nreadable != null then
1282 var readname = name.substring_from(1)
1283 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
1284 if mreadprop == null then
1285 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
1286 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1287 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop)
1288 else
1289 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop)
1290 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
1291 end
1292 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1293 self.mreadpropdef = mreadpropdef
1294 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1295 end
1296
1297 var nwritable = self.n_writable
1298 if nwritable != null then
1299 var writename = name.substring_from(1) + "="
1300 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
1301 if mwriteprop == null then
1302 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1303 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1304 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop)
1305 else
1306 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop)
1307 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1308 end
1309 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1310 self.mwritepropdef = mwritepropdef
1311 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1312 end
1313 else
1314 # New attribute style
1315 var nid2 = self.n_id2.as(not null)
1316 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
1317 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1318 self.mpropdef = mpropdef
1319 modelbuilder.mpropdef2npropdef[mpropdef] = self
1320
1321 var readname = name
1322 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
1323 if mreadprop == null then
1324 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1325 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1326 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop)
1327 else
1328 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop)
1329 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
1330 end
1331 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1332 self.mreadpropdef = mreadpropdef
1333 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1334
1335 var writename = name + "="
1336 var nwritable = self.n_writable
1337 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
1338 var nwkwredef: nullable Token = null
1339 if nwritable != null then nwkwredef = nwritable.n_kwredef
1340 if mwriteprop == null then
1341 var mvisibility
1342 if nwritable != null then
1343 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1344 else
1345 mvisibility = private_visibility
1346 end
1347 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1348 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop)
1349 else
1350 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop)
1351 if nwritable != null then
1352 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1353 end
1354 end
1355 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1356 self.mwritepropdef = mwritepropdef
1357 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1358 end
1359 end
1360
1361 redef fun build_signature(modelbuilder, nclassdef)
1362 do
1363 var mpropdef = self.mpropdef
1364 if mpropdef == null then return # Error thus skiped
1365 var mmodule = mpropdef.mclassdef.mmodule
1366 var mtype: nullable MType = null
1367
1368 var ntype = self.n_type
1369 if ntype != null then
1370 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1371 if mtype == null then return
1372 end
1373
1374 if mtype == null then
1375 var nexpr = self.n_expr
1376 if nexpr != null then
1377 if nexpr isa ANewExpr then
1378 mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
1379 else if nexpr isa AIntExpr then
1380 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
1381 if cla != null then mtype = cla.mclass_type
1382 else if nexpr isa AFloatExpr then
1383 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
1384 if cla != null then mtype = cla.mclass_type
1385 else if nexpr isa ACharExpr then
1386 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
1387 if cla != null then mtype = cla.mclass_type
1388 else if nexpr isa ABoolExpr then
1389 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
1390 if cla != null then mtype = cla.mclass_type
1391 else if nexpr isa ASuperstringExpr then
1392 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1393 if cla != null then mtype = cla.mclass_type
1394 else if nexpr isa AStringFormExpr then
1395 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1396 if cla != null then mtype = cla.mclass_type
1397 else
1398 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
1399 end
1400
1401 else
1402 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
1403 end
1404 end
1405
1406 if mtype == null then return
1407
1408 mpropdef.static_mtype = mtype
1409
1410 var mreadpropdef = self.mreadpropdef
1411 if mreadpropdef != null then
1412 var msignature = new MSignature(new Array[MParameter], mtype)
1413 mreadpropdef.msignature = msignature
1414 end
1415
1416 var msritepropdef = self.mwritepropdef
1417 if mwritepropdef != null then
1418 var name: String
1419 if n_id != null then
1420 name = n_id.text.substring_from(1)
1421 else
1422 name = n_id2.text
1423 end
1424 var mparameter = new MParameter(name, mtype, false)
1425 var msignature = new MSignature([mparameter], null)
1426 mwritepropdef.msignature = msignature
1427 end
1428 end
1429
1430 redef fun check_signature(modelbuilder, nclassdef)
1431 do
1432 var mpropdef = self.mpropdef
1433 if mpropdef == null then return # Error thus skiped
1434 var mmodule = mpropdef.mclassdef.mmodule
1435 var ntype = self.n_type
1436 var mtype = self.mpropdef.static_mtype
1437 if mtype == null then return # Error thus skiped
1438
1439 # Lookup for signature in the precursor
1440 # FIXME all precursors should be considered
1441 if not mpropdef.is_intro then
1442 var precursor_type = mpropdef.mproperty.intro.static_mtype
1443 if precursor_type == null then return
1444
1445 if mtype != precursor_type then
1446 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1447 return
1448 end
1449 end
1450
1451 # Check getter and setter
1452 var meth = self.mreadpropdef
1453 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1454 meth = self.mwritepropdef
1455 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1456 end
1457
1458 private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef)
1459 do
1460 var mmodule = mpropdef.mclassdef.mmodule
1461 var nsig = self.n_type
1462 var mysignature = mpropdef.msignature
1463 if mysignature == null then return # Error thus skiped
1464
1465 # Lookup for signature in the precursor
1466 # FIXME all precursors should be considered
1467 if not mpropdef.is_intro then
1468 var msignature = mpropdef.mproperty.intro.msignature
1469 if msignature == null then return
1470
1471 if mysignature.arity != msignature.arity then
1472 var node: ANode
1473 if nsig != null then node = nsig else node = self
1474 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1475 return
1476 end
1477 var precursor_ret_type = msignature.return_mtype
1478 var ret_type = mysignature.return_mtype
1479 if ret_type != null and precursor_ret_type == null then
1480 var node: ANode
1481 if nsig != null then node = nsig else node = self
1482 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1483 return
1484 end
1485
1486 if mysignature.arity > 0 then
1487 # Check parameters types
1488 for i in [0..mysignature.arity[ do
1489 var myt = mysignature.mparameters[i].mtype
1490 var prt = msignature.mparameters[i].mtype
1491 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1492 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1493 var node: ANode
1494 if nsig != null then node = nsig else node = self
1495 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1496 end
1497 end
1498 end
1499 if precursor_ret_type != null then
1500 if ret_type == null then
1501 # Inherit the return type
1502 ret_type = precursor_ret_type
1503 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1504 var node: ANode
1505 if nsig != null then node = nsig else node = self
1506 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1507 end
1508 end
1509 end
1510 end
1511 end
1512
1513 redef class ATypePropdef
1514 # The associated MVirtualTypeDef once build by a `ModelBuilder'
1515 var mpropdef: nullable MVirtualTypeDef
1516 redef fun build_property(modelbuilder, nclassdef)
1517 do
1518 var mclassdef = nclassdef.mclassdef.as(not null)
1519 var name = self.n_id.text
1520 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1521 if mprop == null then
1522 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1523 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1524 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1525 else
1526 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1527 assert mprop isa MVirtualTypeProp
1528 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1529 end
1530 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1531 self.mpropdef = mpropdef
1532 end
1533
1534 redef fun build_signature(modelbuilder, nclassdef)
1535 do
1536 var mpropdef = self.mpropdef
1537 if mpropdef == null then return # Error thus skiped
1538 var mmodule = mpropdef.mclassdef.mmodule
1539 var mtype: nullable MType = null
1540
1541 var ntype = self.n_type
1542 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1543 if mtype == null then return
1544
1545 mpropdef.bound = mtype
1546 # print "{mpropdef}: {mtype}"
1547 end
1548
1549 redef fun check_signature(modelbuilder, nclassdef)
1550 do
1551 var bound = self.mpropdef.bound
1552
1553 # Fast case: the bound is not a formal type
1554 if not bound isa MVirtualType then return
1555
1556 var mmodule = nclassdef.mclassdef.mmodule
1557 var anchor = nclassdef.mclassdef.bound_mtype
1558
1559 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1560 var seen = [self.mpropdef.mproperty.mvirtualtype]
1561 loop
1562 if seen.has(bound) then
1563 seen.add(bound)
1564 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1565 return
1566 end
1567 seen.add(bound)
1568 var next = bound.lookup_bound(mmodule, anchor)
1569 if not next isa MVirtualType then return
1570 bound = next
1571 end
1572 end
1573 end