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