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