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