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