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