modelbuilder: error when a module defines a class more than once
[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 nmodule.is_importation_done = true
385 var mmodule = nmodule.mmodule.as(not null)
386 var stdimport = true
387 var imported_modules = new Array[MModule]
388 for aimport in nmodule.n_imports do
389 stdimport = false
390 if not aimport isa AStdImport then
391 continue
392 end
393 var mod_name = aimport.n_name.n_id.text
394 var sup = self.get_mmodule_by_name(aimport.n_name, mmodule, mod_name)
395 if sup == null then continue # Skip error
396 imported_modules.add(sup)
397 var mvisibility = aimport.n_visibility.mvisibility
398 mmodule.set_visibility_for(sup, mvisibility)
399 end
400 if stdimport then
401 var mod_name = "standard"
402 var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
403 if sup != null then # Skip error
404 imported_modules.add(sup)
405 mmodule.set_visibility_for(sup, public_visibility)
406 end
407 end
408 self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
409 mmodule.set_imported_mmodules(imported_modules)
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 nmodule.mclass2nclassdef.has_key(mclass) then
462 error(nclassdef, "Error: A class {name} is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.")
463 return
464 else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then
465 error(nclassdef, "Redef error: {name} is an imported class. Add the redef keyword to refine it.")
466 return
467 else if mclass.arity != arity then
468 error(nclassdef, "Redef error: Formal parameter arity missmatch; got {arity}, expected {mclass.arity}.")
469 return
470 else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then
471 error(nkind, "Error: refinement changed the kind from a {mclass.kind} to a {mkind}")
472 else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
473 error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
474 end
475 nclassdef.mclass = mclass
476 nmodule.mclass2nclassdef[mclass] = nclassdef
477 end
478
479 # Visit the AST and create the MClassDef objects
480 private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
481 do
482 var mmodule = nmodule.mmodule.as(not null)
483 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
484 var mclass = nclassdef.mclass
485 if mclass == null then return # Skip error
486 #var mclassdef = nclassdef.mclassdef.as(not null)
487
488 var names = new Array[String]
489 var bounds = new Array[MType]
490 if nclassdef isa AStdClassdef and mclass.arity > 0 then
491 # Collect formal parameter names
492 for i in [0..mclass.arity[ do
493 var nfd = nclassdef.n_formaldefs[i]
494 var ptname = nfd.n_id.text
495 if names.has(ptname) then
496 error(nfd, "Error: A formal parameter type `{ptname}' already exists")
497 return
498 end
499 names.add(ptname)
500 end
501
502 # Revolve bound for formal parameter names
503 for i in [0..mclass.arity[ do
504 var nfd = nclassdef.n_formaldefs[i]
505 var nfdt = nfd.n_type
506 if nfdt != null then
507 var bound = resolve_mtype(nclassdef, nfdt)
508 if bound == null then return # Forward error
509 if bound.need_anchor then
510 # No F-bounds!
511 error(nfd, "Error: Formal parameter type `{names[i]}' bounded with a formal parameter type")
512 else
513 bounds.add(bound)
514 end
515 else if mclass.mclassdefs.is_empty then
516 # No bound, then implicitely bound by nullable Object
517 bounds.add(objectclass.mclass_type.as_nullable)
518 else
519 # Inherit the bound
520 bounds.add(mclass.mclassdefs.first.bound_mtype.arguments[i])
521 end
522 end
523 end
524
525 var bound_mtype = mclass.get_mtype(bounds)
526 var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names)
527 nclassdef.mclassdef = mclassdef
528 self.mclassdef2nclassdef[mclassdef] = nclassdef
529
530 if mclassdef.is_intro then
531 self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
532 else
533 self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
534 end
535 end
536
537 # Visit the AST and set the super-types of the MClassdef objects
538 private fun collect_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
539 do
540 var mmodule = nmodule.mmodule.as(not null)
541 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
542 var mclass = nclassdef.mclass.as(not null)
543 var mclassdef = nclassdef.mclassdef.as(not null)
544
545 var specobject = true
546 var supertypes = new Array[MClassType]
547 if nclassdef isa AStdClassdef then
548 for nsc in nclassdef.n_superclasses do
549 specobject = false
550 var ntype = nsc.n_type
551 var mtype = resolve_mtype(nclassdef, ntype)
552 if mtype == null then continue # Skip because of error
553 if not mtype isa MClassType then
554 error(ntype, "Error: supertypes cannot be a formal type")
555 return
556 end
557 supertypes.add mtype
558 #print "new super : {mclass} < {mtype}"
559 end
560 end
561 if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
562 supertypes.add objectclass.mclass_type
563 end
564
565 mclassdef.set_supertypes(supertypes)
566 if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
567 end
568
569 # Check the validity of the specialization heirarchy
570 # FIXME Stub implementation
571 private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef)
572 do
573 var mmodule = nmodule.mmodule.as(not null)
574 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
575 var mclass = nclassdef.mclass.as(not null)
576 var mclassdef = nclassdef.mclassdef.as(not null)
577 end
578
579 # Build the classes of the module `nmodule'.
580 # REQUIRE: classes of imported modules are already build. (let `build_all_classes' do the job)
581 private fun build_classes(nmodule: AModule)
582 do
583 # Force building recursively
584 if nmodule.build_classes_is_done then return
585 nmodule.build_classes_is_done = true
586 var mmodule = nmodule.mmodule.as(not null)
587 for imp in mmodule.in_importation.direct_greaters do
588
589 build_classes(mmodule2nmodule[imp])
590 end
591
592 # Create all classes
593 for nclassdef in nmodule.n_classdefs do
594 self.build_a_mclass(nmodule, nclassdef)
595 end
596
597 # Create all classdefs
598 for nclassdef in nmodule.n_classdefs do
599 self.build_a_mclassdef(nmodule, nclassdef)
600 end
601
602 for nclassdef in nmodule.n_classdefs do
603 if nclassdef.mclassdef == null then return # forward error
604 end
605
606 # Create inheritance on all classdefs
607 for nclassdef in nmodule.n_classdefs do
608 self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
609 end
610
611 # Create the mclassdef hierarchy
612 for nclassdef in nmodule.n_classdefs do
613 var mclassdef = nclassdef.mclassdef.as(not null)
614 mclassdef.add_in_hierarchy
615 end
616
617 # TODO: Check that the super-class is not intrusive
618
619 # TODO: Check that the super-class is not already known (by transitivity)
620
621 for nclassdef in nmodule.n_classdefs do
622 self.build_properties(nclassdef)
623 end
624 end
625
626 # Register the nmodule associated to each mmodule
627 # FIXME: why not refine the MModule class with a nullable attribute?
628 var mmodule2nmodule: HashMap[MModule, AModule] = new HashMap[MModule, AModule]
629 # Register the nclassdef associated to each mclassdef
630 # FIXME: why not refine the MClassDef class with a nullable attribute?
631 var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef]
632 # Register the npropdef associated to each mpropdef
633 # FIXME: why not refine the MPropDef class with a nullable attribute?
634 var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
635
636 # Build the properties of `nclassdef'.
637 # REQUIRE: all superclasses are built.
638 private fun build_properties(nclassdef: AClassdef)
639 do
640 # Force building recursively
641 if nclassdef.build_properties_is_done then return
642 nclassdef.build_properties_is_done = true
643 var mclassdef = nclassdef.mclassdef.as(not null)
644 if mclassdef.in_hierarchy == null then return # Skip error
645 for superclassdef in mclassdef.in_hierarchy.direct_greaters do
646 build_properties(mclassdef2nclassdef[superclassdef])
647 end
648
649 for npropdef in nclassdef.n_propdefs do
650 npropdef.build_property(self, nclassdef)
651 end
652 for npropdef in nclassdef.n_propdefs do
653 npropdef.build_signature(self, nclassdef)
654 end
655 for npropdef in nclassdef.n_propdefs do
656 npropdef.check_signature(self, nclassdef)
657 end
658 process_default_constructors(nclassdef)
659 end
660
661 # Introduce or inherit default constructor
662 # This is the last part of `build_properties'.
663 private fun process_default_constructors(nclassdef: AClassdef)
664 do
665 var mclassdef = nclassdef.mclassdef.as(not null)
666
667 # Are we a refinement
668 if not mclassdef.is_intro then return
669
670 # Is the class forbid constructors?
671 if not mclassdef.mclass.kind.need_init then return
672
673 # Is there already a constructor defined?
674 for mpropdef in mclassdef.mpropdefs do
675 if not mpropdef isa MMethodDef then continue
676 if mpropdef.mproperty.is_init then return
677 end
678
679 if not nclassdef isa AStdClassdef then return
680
681 var mmodule = nclassdef.mclassdef.mmodule
682 # Do we inherit for a constructor?
683 var combine = new Array[MMethod]
684 var inhc: nullable MClass = null
685 for st in mclassdef.supertypes do
686 var c = st.mclass
687 if not c.kind.need_init then continue
688 st = st.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
689 var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod)
690 if candidate != null and candidate.intro.msignature.arity == 0 then
691 combine.add(candidate)
692 continue
693 end
694 var inhc2 = c.inherit_init_from
695 if inhc2 == null then inhc2 = c
696 if inhc2 == inhc then continue
697 if inhc != null then
698 self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}")
699 else
700 inhc = inhc2
701 end
702 end
703 if combine.is_empty and inhc != null then
704 # TODO: actively inherit the consturctor
705 self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3)
706 mclassdef.mclass.inherit_init_from = inhc
707 return
708 end
709 if not combine.is_empty and inhc != null then
710 self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}")
711 return
712 end
713
714 if not combine.is_empty then
715 nclassdef.super_inits = combine
716 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
717 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
718 var mparameters = new Array[MParameter]
719 var msignature = new MSignature(mparameters, null)
720 mpropdef.msignature = msignature
721 mprop.is_init = true
722 nclassdef.mfree_init = mpropdef
723 self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
724 return
725 end
726
727 # Collect undefined attributes
728 var mparameters = new Array[MParameter]
729 for npropdef in nclassdef.n_propdefs do
730 if npropdef isa AAttrPropdef and npropdef.n_expr == null then
731 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
732 var ret_type = npropdef.mpropdef.static_mtype
733 if ret_type == null then return
734 var mparameter = new MParameter(paramname, ret_type, false)
735 mparameters.add(mparameter)
736 end
737 end
738
739 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
740 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
741 var msignature = new MSignature(mparameters, null)
742 mpropdef.msignature = msignature
743 mprop.is_init = true
744 nclassdef.mfree_init = mpropdef
745 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
746 end
747
748 # Return the static type associated to the node `ntype'.
749 # `classdef' is the context where the call is made (used to understand formal types)
750 # The mmodule used as context is `nclassdef.mmodule'
751 # In case of problem, an error is displayed on `ntype' and null is returned.
752 # FIXME: the name "resolve_mtype" is awful
753 fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
754 do
755 var name = ntype.n_id.text
756 var mclassdef = nclassdef.mclassdef
757 var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
758 var res: MType
759
760 # Check virtual type
761 if mclassdef != null then
762 var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp)
763 if prop != null 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 res = prop.mvirtualtype
768 if ntype.n_kwnullable != null then res = res.as_nullable
769 return res
770 end
771 end
772
773 # Check parameter type
774 if mclassdef != null and mclassdef.parameter_names.has(name) then
775 if not ntype.n_types.is_empty then
776 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
777 end
778 for i in [0..mclassdef.parameter_names.length[ do
779 if mclassdef.parameter_names[i] == name then
780 res = mclassdef.mclass.mclass_type.arguments[i]
781 if ntype.n_kwnullable != null then res = res.as_nullable
782 return res
783 end
784 end
785 abort
786 end
787
788 # Check class
789 var mclass = try_get_mclass_by_name(ntype, mmodule, name)
790 if mclass != null then
791 var arity = ntype.n_types.length
792 if arity != mclass.arity then
793 if arity == 0 then
794 error(ntype, "Type error: '{name}' is a generic class.")
795 else if mclass.arity == 0 then
796 error(ntype, "Type error: '{name}' is not a generic class.")
797 else
798 error(ntype, "Type error: '{name}' has {mclass.arity} parameters ({arity} are provided).")
799 end
800 return null
801 end
802 if arity == 0 then
803 res = mclass.mclass_type
804 if ntype.n_kwnullable != null then res = res.as_nullable
805 return res
806 else
807 var mtypes = new Array[MType]
808 for nt in ntype.n_types do
809 var mt = resolve_mtype(nclassdef, nt)
810 if mt == null then return null # Forward error
811 mtypes.add(mt)
812 end
813 res = mclass.get_mtype(mtypes)
814 if ntype.n_kwnullable != null then res = res.as_nullable
815 return res
816 end
817 end
818
819 # If everything fail, then give up :(
820 error(ntype, "Type error: class {name} not found in module {mmodule}.")
821 return null
822 end
823
824 # Helper function to display an error on a node.
825 # Alias for `self.toolcontext.error(n.hot_location, text)'
826 fun error(n: ANode, text: String)
827 do
828 self.toolcontext.error(n.hot_location, text)
829 end
830
831 # Helper function to display a warning on a node.
832 # Alias for: `self.toolcontext.warning(n.hot_location, text)'
833 fun warning(n: ANode, text: String)
834 do
835 self.toolcontext.warning(n.hot_location, text)
836 end
837
838 # Force to get the primitive method named `name' on the type `recv' or do a fatal error on `n'
839 fun force_get_primitive_method(n: ANode, name: String, recv: MType, mmodule: MModule): MMethod
840 do
841 var res = mmodule.try_get_primitive_method(name, recv)
842 if res == null then
843 self.toolcontext.fatal_error(n.hot_location, "Fatal Error: {recv} must have a property named {name}.")
844 abort
845 end
846 return res
847 end
848 end
849
850 redef class AModule
851 # The associated MModule once build by a `ModelBuilder'
852 var mmodule: nullable MModule
853 # Flag that indicate if the importation is already completed
854 var is_importation_done: Bool = false
855 # Flag that indicate if the class and prop building is already completed
856 var build_classes_is_done: Bool = false
857 # What is the AClassdef associated to a MClass?
858 # Used to check multiple definition of a class.
859 var mclass2nclassdef: Map[MClass, AClassdef] = new HashMap[MClass, AClassdef]
860
861 end
862
863 redef class MClass
864 # The class whose self inherit all the constructors.
865 # 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
866 var inherit_init_from: nullable MClass = null
867 end
868
869 redef class AClassdef
870 # The associated MClass once build by a `ModelBuilder'
871 var mclass: nullable MClass
872 # The associated MClassDef once build by a `ModelBuilder'
873 var mclassdef: nullable MClassDef
874 var build_properties_is_done: Bool = false
875 # The list of super-constructor to call at the start of the free constructor
876 # 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
877 var super_inits: nullable Collection[MMethod] = null
878
879 # The free init (implicitely constructed by the class if required)
880 var mfree_init: nullable MMethodDef = null
881 end
882
883 redef class AClasskind
884 # The class kind associated with the AST node class
885 private fun mkind: MClassKind is abstract
886 end
887 redef class AConcreteClasskind
888 redef fun mkind do return concrete_kind
889 end
890 redef class AAbstractClasskind
891 redef fun mkind do return abstract_kind
892 end
893 redef class AInterfaceClasskind
894 redef fun mkind do return interface_kind
895 end
896 redef class AEnumClasskind
897 redef fun mkind do return enum_kind
898 end
899 redef class AExternClasskind
900 redef fun mkind do return extern_kind
901 end
902
903 redef class AVisibility
904 # The visibility level associated with the AST node class
905 private fun mvisibility: MVisibility is abstract
906 end
907 redef class AIntrudeVisibility
908 redef fun mvisibility do return intrude_visibility
909 end
910 redef class APublicVisibility
911 redef fun mvisibility do return public_visibility
912 end
913 redef class AProtectedVisibility
914 redef fun mvisibility do return protected_visibility
915 end
916 redef class APrivateVisibility
917 redef fun mvisibility do return private_visibility
918 end
919
920
921 #
922
923 redef class Prod
924 # Join the text of all tokens
925 # Used to get the 'real name' of method definitions.
926 fun collect_text: String
927 do
928 var v = new TextCollectorVisitor
929 v.enter_visit(self)
930 assert v.text != ""
931 return v.text
932 end
933 end
934
935 private class TextCollectorVisitor
936 super Visitor
937 var text: String = ""
938 redef fun visit(n)
939 do
940 if n isa Token then text += n.text
941 n.visit_all(self)
942 end
943 end
944
945 redef class APropdef
946 private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef)
947 do
948 end
949 private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
950 do
951 end
952 private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
953 do
954 end
955 private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility
956 do
957 var mvisibility = public_visibility
958 if nvisibility != null then mvisibility = nvisibility.mvisibility
959 if nclassdef.mclassdef.mclass.visibility == private_visibility then
960 if mvisibility == protected_visibility then
961 assert nvisibility != null
962 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
963 else if mvisibility == private_visibility then
964 assert nvisibility != null
965 # Not yet
966 # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.")
967 end
968 mvisibility = private_visibility
969 end
970 return mvisibility
971 end
972
973 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty)
974 do
975 if nvisibility == null then return
976 var mvisibility = nvisibility.mvisibility
977 if mvisibility != mprop.visibility and mvisibility != public_visibility then
978 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
979 end
980 end
981
982 private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty)
983 do
984 if kwredef == null then
985 if need_redef then
986 modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
987 end
988 else
989 if not need_redef then
990 modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
991 end
992 end
993 end
994
995 end
996
997 redef class ASignature
998 # Is the model builder has correctly visited the signature
999 var is_visited = false
1000 # Names of parameters from the AST
1001 # REQUIRE: is_visited
1002 var param_names = new Array[String]
1003 # Types of parameters from the AST
1004 # REQUIRE: is_visited
1005 var param_types = new Array[MType]
1006 # Rank of the vararg (of -1 if none)
1007 # REQUIRE: is_visited
1008 var vararg_rank: Int = -1
1009 # Return type
1010 var ret_type: nullable MType = null
1011
1012 # Visit and fill information about a signature
1013 private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool
1014 do
1015 var param_names = self.param_names
1016 var param_types = self.param_types
1017 for np in self.n_params do
1018 param_names.add(np.n_id.text)
1019 var ntype = np.n_type
1020 if ntype != null then
1021 var mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1022 if mtype == null then return false # Skip error
1023 for i in [0..param_names.length-param_types.length[ do
1024 param_types.add(mtype)
1025 end
1026 if np.n_dotdotdot != null then
1027 if self.vararg_rank != -1 then
1028 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
1029 return false
1030 else
1031 self.vararg_rank = param_names.length - 1
1032 end
1033 end
1034 end
1035 end
1036 var ntype = self.n_type
1037 if ntype != null then
1038 self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype)
1039 if self.ret_type == null then return false # Skip errir
1040 end
1041
1042 for nclosure in self.n_closure_decls do
1043 if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false
1044 end
1045
1046 self.is_visited = true
1047 return true
1048 end
1049
1050 # Build a visited signature
1051 fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature
1052 do
1053 if param_names.length != param_types.length then
1054 # Some parameters are typed, other parameters are not typed.
1055 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1056 return null
1057 end
1058
1059 var mparameters = new Array[MParameter]
1060 for i in [0..param_names.length[ do
1061 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1062 mparameters.add(mparameter)
1063 end
1064
1065 var msignature = new MSignature(mparameters, ret_type)
1066 return msignature
1067 end
1068 end
1069
1070 redef class AMethPropdef
1071 # The associated MMethodDef once build by a `ModelBuilder'
1072 var mpropdef: nullable MMethodDef
1073
1074 # The associated super init if any
1075 var super_init: nullable MMethod
1076 redef fun build_property(modelbuilder, nclassdef)
1077 do
1078 var is_init = self isa AInitPropdef
1079 var mclassdef = nclassdef.mclassdef.as(not null)
1080 var name: String
1081 var amethodid = self.n_methid
1082 var name_node: ANode
1083 if amethodid == null then
1084 if self isa AMainMethPropdef then
1085 name = "main"
1086 name_node = self
1087 else if self isa AConcreteInitPropdef then
1088 name = "init"
1089 name_node = self.n_kwinit
1090 else if self isa AExternInitPropdef then
1091 name = "new"
1092 name_node = self.n_kwnew
1093 else
1094 abort
1095 end
1096 else if amethodid isa AIdMethid then
1097 name = amethodid.n_id.text
1098 name_node = amethodid
1099 else
1100 # operator, bracket or assign
1101 name = amethodid.collect_text
1102 name_node = amethodid
1103
1104 if name == "-" and self.n_signature.n_params.length == 0 then
1105 name = "unary -"
1106 end
1107 end
1108
1109 var mprop: nullable MMethod = null
1110 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
1111 if mprop == null then
1112 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1113 mprop = new MMethod(mclassdef, name, mvisibility)
1114 mprop.is_init = is_init
1115 mprop.is_new = self isa AExternInitPropdef
1116 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop)
1117 else
1118 if n_kwredef == null then
1119 if self isa AMainMethPropdef then
1120 # no warning
1121 else
1122 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop)
1123 end
1124 end
1125 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1126 end
1127
1128 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
1129
1130 self.mpropdef = mpropdef
1131 modelbuilder.mpropdef2npropdef[mpropdef] = self
1132 if mpropdef.is_intro then
1133 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
1134 else
1135 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
1136 end
1137 end
1138
1139 redef fun build_signature(modelbuilder, nclassdef)
1140 do
1141 var mpropdef = self.mpropdef
1142 if mpropdef == null then return # Error thus skiped
1143 var mmodule = mpropdef.mclassdef.mmodule
1144 var nsig = self.n_signature
1145
1146 # Retrieve info from the signature AST
1147 var param_names = new Array[String] # Names of parameters from the AST
1148 var param_types = new Array[MType] # Types of parameters from the AST
1149 var vararg_rank = -1
1150 var ret_type: nullable MType = null # Return type from the AST
1151 if nsig != null then
1152 if not nsig.visit_signature(modelbuilder, nclassdef) then return
1153 param_names = nsig.param_names
1154 param_types = nsig.param_types
1155 vararg_rank = nsig.vararg_rank
1156 ret_type = nsig.ret_type
1157 end
1158
1159 # Look for some signature to inherit
1160 # FIXME: do not inherit from the intro, but from the most specific
1161 var msignature: nullable MSignature = null
1162 if not mpropdef.is_intro then
1163 msignature = mpropdef.mproperty.intro.msignature
1164 if msignature == null then return # Skip error
1165
1166 # Check inherited signature arity
1167 if param_names.length != msignature.arity then
1168 var node: ANode
1169 if nsig != null then node = nsig else node = self
1170 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1171 return
1172 end
1173 else if mpropdef.mproperty.is_init then
1174 # FIXME UGLY: inherit signature from a super-constructor
1175 for msupertype in nclassdef.mclassdef.supertypes do
1176 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
1177 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
1178 if candidate != null then
1179 if msignature == null then
1180 msignature = candidate.intro.as(MMethodDef).msignature
1181 end
1182 end
1183 end
1184 end
1185
1186
1187 # Inherit the signature
1188 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
1189 # Parameters are untyped, thus inherit them
1190 param_types = new Array[MType]
1191 for mparameter in msignature.mparameters do
1192 param_types.add(mparameter.mtype)
1193 end
1194 vararg_rank = msignature.vararg_rank
1195 end
1196 if msignature != null and ret_type == null then
1197 ret_type = msignature.return_mtype
1198 end
1199
1200 if param_names.length != param_types.length then
1201 # Some parameters are typed, other parameters are not typed.
1202 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1203 return
1204 end
1205
1206 var mparameters = new Array[MParameter]
1207 for i in [0..param_names.length[ do
1208 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1209 mparameters.add(mparameter)
1210 end
1211
1212 msignature = new MSignature(mparameters, ret_type)
1213 mpropdef.msignature = msignature
1214
1215 if nsig != null then
1216 for nclosure in nsig.n_closure_decls do
1217 var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef)
1218 if clos_signature == null then return
1219 var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false)
1220 msignature.mclosures.add(mparameter)
1221 end
1222 end
1223
1224 end
1225
1226 redef fun check_signature(modelbuilder, nclassdef)
1227 do
1228 var mpropdef = self.mpropdef
1229 if mpropdef == null then return # Error thus skiped
1230 var mmodule = mpropdef.mclassdef.mmodule
1231 var nsig = self.n_signature
1232 var mysignature = self.mpropdef.msignature
1233 if mysignature == null then return # Error thus skiped
1234
1235 # Lookup for signature in the precursor
1236 # FIXME all precursors should be considered
1237 if not mpropdef.is_intro then
1238 var msignature = mpropdef.mproperty.intro.msignature
1239 if msignature == null then return
1240
1241 var precursor_ret_type = msignature.return_mtype
1242 var ret_type = mysignature.return_mtype
1243 if ret_type != null and precursor_ret_type == null then
1244 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1245 return
1246 end
1247
1248 if mysignature.arity > 0 then
1249 # Check parameters types
1250 for i in [0..mysignature.arity[ do
1251 var myt = mysignature.mparameters[i].mtype
1252 var prt = msignature.mparameters[i].mtype
1253 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1254 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1255 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1256 end
1257 end
1258 end
1259 if precursor_ret_type != null then
1260 if ret_type == null then
1261 # Inherit the return type
1262 ret_type = precursor_ret_type
1263 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1264 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1265 end
1266 end
1267 end
1268 end
1269 end
1270
1271 redef class AAttrPropdef
1272 # The associated MAttributeDef once build by a `ModelBuilder'
1273 var mpropdef: nullable MAttributeDef
1274 # The associated getter (read accessor) if any
1275 var mreadpropdef: nullable MMethodDef
1276 # The associated setter (write accessor) if any
1277 var mwritepropdef: nullable MMethodDef
1278 redef fun build_property(modelbuilder, nclassdef)
1279 do
1280 var mclassdef = nclassdef.mclassdef.as(not null)
1281 var mclass = mclassdef.mclass
1282
1283 var name: String
1284 if self.n_id != null then
1285 name = self.n_id.text
1286 else
1287 name = self.n_id2.text
1288 end
1289
1290 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
1291 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
1292 else if mclass.kind == enum_kind then
1293 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
1294 end
1295
1296 var nid = self.n_id
1297 if nid != null then
1298 # Old attribute style
1299 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
1300 if mprop == null then
1301 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1302 mprop = new MAttribute(mclassdef, name, mvisibility)
1303 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1304 else
1305 assert mprop isa MAttribute
1306 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1307 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1308 end
1309 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1310 self.mpropdef = mpropdef
1311 modelbuilder.mpropdef2npropdef[mpropdef] = self
1312
1313 var nreadable = self.n_readable
1314 if nreadable != null then
1315 var readname = name.substring_from(1)
1316 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
1317 if mreadprop == null then
1318 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
1319 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1320 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop)
1321 else
1322 self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop)
1323 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
1324 end
1325 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1326 self.mreadpropdef = mreadpropdef
1327 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1328 end
1329
1330 var nwritable = self.n_writable
1331 if nwritable != null then
1332 var writename = name.substring_from(1) + "="
1333 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
1334 if mwriteprop == null then
1335 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1336 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1337 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop)
1338 else
1339 self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop)
1340 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1341 end
1342 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1343 self.mwritepropdef = mwritepropdef
1344 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1345 end
1346 else
1347 # New attribute style
1348 var nid2 = self.n_id2.as(not null)
1349 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
1350 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1351 self.mpropdef = mpropdef
1352 modelbuilder.mpropdef2npropdef[mpropdef] = self
1353
1354 var readname = name
1355 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
1356 if mreadprop == null then
1357 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1358 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1359 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop)
1360 else
1361 self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop)
1362 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
1363 end
1364 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1365 self.mreadpropdef = mreadpropdef
1366 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1367
1368 var writename = name + "="
1369 var nwritable = self.n_writable
1370 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
1371 var nwkwredef: nullable Token = null
1372 if nwritable != null then nwkwredef = nwritable.n_kwredef
1373 if mwriteprop == null then
1374 var mvisibility
1375 if nwritable != null then
1376 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1377 else
1378 mvisibility = private_visibility
1379 end
1380 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1381 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop)
1382 else
1383 self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop)
1384 if nwritable != null then
1385 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1386 end
1387 end
1388 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1389 self.mwritepropdef = mwritepropdef
1390 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1391 end
1392 end
1393
1394 redef fun build_signature(modelbuilder, nclassdef)
1395 do
1396 var mpropdef = self.mpropdef
1397 if mpropdef == null then return # Error thus skiped
1398 var mmodule = mpropdef.mclassdef.mmodule
1399 var mtype: nullable MType = null
1400
1401 var ntype = self.n_type
1402 if ntype != null then
1403 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1404 if mtype == null then return
1405 end
1406
1407 if mtype == null then
1408 var nexpr = self.n_expr
1409 if nexpr != null then
1410 if nexpr isa ANewExpr then
1411 mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
1412 else if nexpr isa AIntExpr then
1413 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
1414 if cla != null then mtype = cla.mclass_type
1415 else if nexpr isa AFloatExpr then
1416 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
1417 if cla != null then mtype = cla.mclass_type
1418 else if nexpr isa ACharExpr then
1419 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
1420 if cla != null then mtype = cla.mclass_type
1421 else if nexpr isa ABoolExpr then
1422 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
1423 if cla != null then mtype = cla.mclass_type
1424 else if nexpr isa ASuperstringExpr then
1425 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1426 if cla != null then mtype = cla.mclass_type
1427 else if nexpr isa AStringFormExpr then
1428 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1429 if cla != null then mtype = cla.mclass_type
1430 else
1431 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
1432 end
1433
1434 else
1435 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
1436 end
1437 end
1438
1439 if mtype == null then return
1440
1441 mpropdef.static_mtype = mtype
1442
1443 var mreadpropdef = self.mreadpropdef
1444 if mreadpropdef != null then
1445 var msignature = new MSignature(new Array[MParameter], mtype)
1446 mreadpropdef.msignature = msignature
1447 end
1448
1449 var msritepropdef = self.mwritepropdef
1450 if mwritepropdef != null then
1451 var name: String
1452 if n_id != null then
1453 name = n_id.text.substring_from(1)
1454 else
1455 name = n_id2.text
1456 end
1457 var mparameter = new MParameter(name, mtype, false)
1458 var msignature = new MSignature([mparameter], null)
1459 mwritepropdef.msignature = msignature
1460 end
1461 end
1462
1463 redef fun check_signature(modelbuilder, nclassdef)
1464 do
1465 var mpropdef = self.mpropdef
1466 if mpropdef == null then return # Error thus skiped
1467 var mmodule = mpropdef.mclassdef.mmodule
1468 var ntype = self.n_type
1469 var mtype = self.mpropdef.static_mtype
1470 if mtype == 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 precursor_type = mpropdef.mproperty.intro.static_mtype
1476 if precursor_type == null then return
1477
1478 if mtype != precursor_type then
1479 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1480 return
1481 end
1482 end
1483
1484 # Check getter and setter
1485 var meth = self.mreadpropdef
1486 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1487 meth = self.mwritepropdef
1488 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1489 end
1490
1491 private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef)
1492 do
1493 var mmodule = mpropdef.mclassdef.mmodule
1494 var nsig = self.n_type
1495 var mysignature = mpropdef.msignature
1496 if mysignature == null then return # Error thus skiped
1497
1498 # Lookup for signature in the precursor
1499 # FIXME all precursors should be considered
1500 if not mpropdef.is_intro then
1501 var msignature = mpropdef.mproperty.intro.msignature
1502 if msignature == null then return
1503
1504 if mysignature.arity != msignature.arity then
1505 var node: ANode
1506 if nsig != null then node = nsig else node = self
1507 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1508 return
1509 end
1510 var precursor_ret_type = msignature.return_mtype
1511 var ret_type = mysignature.return_mtype
1512 if ret_type != null and precursor_ret_type == null then
1513 var node: ANode
1514 if nsig != null then node = nsig else node = self
1515 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1516 return
1517 end
1518
1519 if mysignature.arity > 0 then
1520 # Check parameters types
1521 for i in [0..mysignature.arity[ do
1522 var myt = mysignature.mparameters[i].mtype
1523 var prt = msignature.mparameters[i].mtype
1524 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1525 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1526 var node: ANode
1527 if nsig != null then node = nsig else node = self
1528 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1529 end
1530 end
1531 end
1532 if precursor_ret_type != null then
1533 if ret_type == null then
1534 # Inherit the return type
1535 ret_type = precursor_ret_type
1536 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1537 var node: ANode
1538 if nsig != null then node = nsig else node = self
1539 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1540 end
1541 end
1542 end
1543 end
1544 end
1545
1546 redef class ATypePropdef
1547 # The associated MVirtualTypeDef once build by a `ModelBuilder'
1548 var mpropdef: nullable MVirtualTypeDef
1549 redef fun build_property(modelbuilder, nclassdef)
1550 do
1551 var mclassdef = nclassdef.mclassdef.as(not null)
1552 var name = self.n_id.text
1553 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1554 if mprop == null then
1555 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1556 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1557 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop)
1558 else
1559 self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop)
1560 assert mprop isa MVirtualTypeProp
1561 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1562 end
1563 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1564 self.mpropdef = mpropdef
1565 end
1566
1567 redef fun build_signature(modelbuilder, nclassdef)
1568 do
1569 var mpropdef = self.mpropdef
1570 if mpropdef == null then return # Error thus skiped
1571 var mmodule = mpropdef.mclassdef.mmodule
1572 var mtype: nullable MType = null
1573
1574 var ntype = self.n_type
1575 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1576 if mtype == null then return
1577
1578 mpropdef.bound = mtype
1579 # print "{mpropdef}: {mtype}"
1580 end
1581
1582 redef fun check_signature(modelbuilder, nclassdef)
1583 do
1584 var bound = self.mpropdef.bound
1585
1586 # Fast case: the bound is not a formal type
1587 if not bound isa MVirtualType then return
1588
1589 var mmodule = nclassdef.mclassdef.mmodule
1590 var anchor = nclassdef.mclassdef.bound_mtype
1591
1592 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1593 var seen = [self.mpropdef.mproperty.mvirtualtype]
1594 loop
1595 if seen.has(bound) then
1596 seen.add(bound)
1597 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1598 return
1599 end
1600 seen.add(bound)
1601 var next = bound.lookup_bound(mmodule, anchor)
1602 if not next isa MVirtualType then return
1603 bound = next
1604 end
1605 end
1606 end