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