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