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