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