model: new metamodel
[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 know 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 = once ("NIT_PATH".to_symbol).environ
71 if not path_env.is_empty then
72 paths.append(path_env.split_with(':'))
73 end
74
75 path_env = once ("NIT_DIR".to_symbol).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 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop)
1004 else
1005 if n_kwredef == null then
1006 if self isa AMainMethPropdef then
1007 # no warning
1008 else
1009 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop)
1010 end
1011 end
1012 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1013 end
1014
1015 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
1016
1017 self.mpropdef = mpropdef
1018 modelbuilder.mpropdef2npropdef[mpropdef] = self
1019 if mpropdef.is_intro then
1020 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
1021 else
1022 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
1023 end
1024 end
1025
1026 redef fun build_signature(modelbuilder, nclassdef)
1027 do
1028 var mpropdef = self.mpropdef
1029 if mpropdef == null then return # Error thus skiped
1030 var mmodule = mpropdef.mclassdef.mmodule
1031 var nsig = self.n_signature
1032
1033 # Retrieve info from the signature AST
1034 var param_names = new Array[String] # Names of parameters from the AST
1035 var param_types = new Array[MType] # Types of parameters from the AST
1036 var vararg_rank = -1
1037 var ret_type: nullable MType = null # Return type from the AST
1038 if nsig != null then
1039 for np in nsig.n_params do
1040 param_names.add(np.n_id.text)
1041 var ntype = np.n_type
1042 if ntype != null then
1043 var mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1044 if mtype == null then return # Skip error
1045 for i in [0..param_names.length-param_types.length[ do
1046 param_types.add(mtype)
1047 end
1048 if np.n_dotdotdot != null then
1049 if vararg_rank != -1 then
1050 modelbuilder.error(np, "Error: {param_names[vararg_rank]} is already a vararg")
1051 else
1052 vararg_rank = param_names.length - 1
1053 end
1054 end
1055 end
1056 end
1057 var ntype = nsig.n_type
1058 if ntype != null then
1059 ret_type = modelbuilder.resolve_mtype(nclassdef, ntype)
1060 if ret_type == null then return # Skip errir
1061 end
1062 end
1063
1064 # Look for some signature to inherit
1065 # FIXME: do not inherit from the intro, but from the most specific
1066 var msignature: nullable MSignature = null
1067 if not mpropdef.is_intro then
1068 msignature = mpropdef.mproperty.intro.msignature
1069 if msignature == null then return # Skip error
1070 else if mpropdef.mproperty.is_init then
1071 # FIXME UGLY: inherit signature from a super-constructor
1072 for msupertype in nclassdef.mclassdef.supertypes do
1073 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
1074 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
1075 if candidate != null then
1076 if msignature == null then
1077 msignature = candidate.intro.as(MMethodDef).msignature
1078 end
1079 end
1080 end
1081 end
1082
1083 # Inherit the signature
1084 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
1085 # Parameters are untyped, thus inherit them
1086 param_types = msignature.parameter_mtypes
1087 vararg_rank = msignature.vararg_rank
1088 end
1089 if msignature != null and ret_type == null then
1090 ret_type = msignature.return_mtype
1091 end
1092
1093 if param_names.length != param_types.length then
1094 # Some parameters are typed, other parameters are not typed.
1095 modelbuilder.warning(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1096 return
1097 end
1098
1099 msignature = new MSignature(param_names, param_types, ret_type, vararg_rank)
1100 mpropdef.msignature = msignature
1101 end
1102
1103 redef fun check_signature(modelbuilder, nclassdef)
1104 do
1105 var mpropdef = self.mpropdef
1106 if mpropdef == null then return # Error thus skiped
1107 var mmodule = mpropdef.mclassdef.mmodule
1108 var nsig = self.n_signature
1109 var mysignature = self.mpropdef.msignature
1110 if mysignature == null then return # Error thus skiped
1111
1112 # Lookup for signature in the precursor
1113 # FIXME all precursors should be considered
1114 if not mpropdef.is_intro then
1115 var msignature = mpropdef.mproperty.intro.msignature
1116 if msignature == null then return
1117
1118 if mysignature.arity != msignature.arity then
1119 var node: ANode
1120 if nsig != null then node = nsig else node = self
1121 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1122 return
1123 end
1124 var precursor_ret_type = msignature.return_mtype
1125 var ret_type = mysignature.return_mtype
1126 if ret_type != null and precursor_ret_type == null then
1127 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1128 return
1129 end
1130
1131 if mysignature.arity > 0 then
1132 # Check parameters types
1133 for i in [0..mysignature.arity[ do
1134 var myt = mysignature.parameter_mtypes[i]
1135 var prt = msignature.parameter_mtypes[i]
1136 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1137 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1138 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.parameter_names[i]}'. found {myt}, expected {prt}.")
1139 end
1140 end
1141 end
1142 if precursor_ret_type != null then
1143 if ret_type == null then
1144 # Inherit the return type
1145 ret_type = precursor_ret_type
1146 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1147 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1148 end
1149 end
1150 end
1151 end
1152 end
1153
1154 redef class AAttrPropdef
1155 # The associated MAttributeDef once build by a `ModelBuilder'
1156 var mpropdef: nullable MAttributeDef
1157 # The associated getter (read accessor) if any
1158 var mreadpropdef: nullable MMethodDef
1159 # The associated setter (write accessor) if any
1160 var mwritepropdef: nullable MMethodDef
1161 redef fun build_property(modelbuilder, nclassdef)
1162 do
1163 var mclassdef = nclassdef.mclassdef.as(not null)
1164 var mclass = mclassdef.mclass
1165
1166 var name: String
1167 if self.n_id != null then
1168 name = self.n_id.text
1169 else
1170 name = self.n_id2.text
1171 end
1172
1173 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
1174 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
1175 else if mclass.kind == enum_kind then
1176 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
1177 end
1178
1179 var nid = self.n_id
1180 if nid != null then
1181 # Old attribute style
1182 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
1183 if mprop == null then
1184 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1185 mprop = new MAttribute(mclassdef, name, mvisibility)
1186 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1187 else
1188 assert mprop isa MAttribute
1189 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1190 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1191 end
1192 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1193 self.mpropdef = mpropdef
1194 modelbuilder.mpropdef2npropdef[mpropdef] = self
1195
1196 var nreadable = self.n_readable
1197 if nreadable != null then
1198 var readname = name.substring_from(1)
1199 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
1200 if mreadprop == null then
1201 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
1202 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1203 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop)
1204 else
1205 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop)
1206 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
1207 end
1208 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1209 self.mreadpropdef = mreadpropdef
1210 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1211 end
1212
1213 var nwritable = self.n_writable
1214 if nwritable != null then
1215 var writename = name.substring_from(1) + "="
1216 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
1217 if mwriteprop == null then
1218 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1219 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1220 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop)
1221 else
1222 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop)
1223 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1224 end
1225 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1226 self.mwritepropdef = mwritepropdef
1227 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1228 end
1229 else
1230 # New attribute style
1231 var nid2 = self.n_id2.as(not null)
1232 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
1233 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1234 self.mpropdef = mpropdef
1235 modelbuilder.mpropdef2npropdef[mpropdef] = self
1236
1237 var readname = name
1238 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
1239 if mreadprop == null then
1240 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1241 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1242 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop)
1243 else
1244 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop)
1245 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
1246 end
1247 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1248 self.mreadpropdef = mreadpropdef
1249 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1250
1251 var writename = name + "="
1252 var nwritable = self.n_writable
1253 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
1254 var nwkwredef: nullable Token = null
1255 if nwritable != null then nwkwredef = nwritable.n_kwredef
1256 if mwriteprop == null then
1257 var mvisibility
1258 if nwritable != null then
1259 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1260 else
1261 mvisibility = private_visibility
1262 end
1263 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1264 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop)
1265 else
1266 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop)
1267 if nwritable != null then
1268 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1269 end
1270 end
1271 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1272 self.mwritepropdef = mwritepropdef
1273 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1274 end
1275 end
1276
1277 redef fun build_signature(modelbuilder, nclassdef)
1278 do
1279 var mpropdef = self.mpropdef
1280 if mpropdef == null then return # Error thus skiped
1281 var mmodule = mpropdef.mclassdef.mmodule
1282 var mtype: nullable MType = null
1283
1284 var ntype = self.n_type
1285 if ntype != null then
1286 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1287 if mtype == null then return
1288 end
1289
1290 if mtype == null then
1291 modelbuilder.warning(self, "Error: Untyped attribute {mpropdef}")
1292 return
1293 end
1294
1295 mpropdef.static_mtype = mtype
1296
1297 var mreadpropdef = self.mreadpropdef
1298 if mreadpropdef != null then
1299 var msignature = new MSignature(new Array[String], new Array[MType], mtype, -1)
1300 mreadpropdef.msignature = msignature
1301 end
1302
1303 var msritepropdef = self.mwritepropdef
1304 if mwritepropdef != null then
1305 var name: String
1306 if n_id != null then
1307 name = n_id.text.substring_from(1)
1308 else
1309 name = n_id2.text
1310 end
1311 var msignature = new MSignature([name], [mtype], null, -1)
1312 mwritepropdef.msignature = msignature
1313 end
1314 end
1315
1316 redef fun check_signature(modelbuilder, nclassdef)
1317 do
1318 var mpropdef = self.mpropdef
1319 if mpropdef == null then return # Error thus skiped
1320 var mmodule = mpropdef.mclassdef.mmodule
1321 var ntype = self.n_type
1322 var mtype = self.mpropdef.static_mtype
1323 if mtype == null then return # Error thus skiped
1324
1325 # Lookup for signature in the precursor
1326 # FIXME all precursors should be considered
1327 if not mpropdef.is_intro then
1328 var precursor_type = mpropdef.mproperty.intro.static_mtype
1329 if precursor_type == null then return
1330
1331 if mtype != precursor_type then
1332 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1333 return
1334 end
1335 end
1336
1337 # FIXME: Check getter ans setter
1338 end
1339 end
1340
1341 redef class ATypePropdef
1342 # The associated MVirtualTypeDef once build by a `ModelBuilder'
1343 var mpropdef: nullable MVirtualTypeDef
1344 redef fun build_property(modelbuilder, nclassdef)
1345 do
1346 var mclassdef = nclassdef.mclassdef.as(not null)
1347 var name = self.n_id.text
1348 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1349 if mprop == null then
1350 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1351 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1352 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1353 else
1354 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1355 assert mprop isa MVirtualTypeProp
1356 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1357 end
1358 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1359 self.mpropdef = mpropdef
1360 end
1361
1362 redef fun build_signature(modelbuilder, nclassdef)
1363 do
1364 var mpropdef = self.mpropdef
1365 if mpropdef == null then return # Error thus skiped
1366 var mmodule = mpropdef.mclassdef.mmodule
1367 var mtype: nullable MType = null
1368
1369 var ntype = self.n_type
1370 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1371 if mtype == null then return
1372
1373 mpropdef.bound = mtype
1374 # print "{mpropdef}: {mtype}"
1375 end
1376
1377 redef fun check_signature(modelbuilder, nclassdef)
1378 do
1379 var bound = self.mpropdef.bound
1380
1381 # Fast case: the bound is not a formal type
1382 if not bound isa MVirtualType then return
1383
1384 var mmodule = nclassdef.mclassdef.mmodule
1385 var anchor = nclassdef.mclassdef.bound_mtype
1386
1387 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1388 var seen = [self.mpropdef.mproperty.mvirtualtype]
1389 loop
1390 if seen.has(bound) then
1391 seen.add(bound)
1392 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1393 return
1394 end
1395 seen.add(bound)
1396 var next = bound.lookup_bound(mmodule, anchor)
1397 if not next isa MVirtualType then return
1398 bound = next
1399 end
1400 end
1401 end