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