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