modelbuilder: stop building a property on 'redef' error
[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 if npropdef.mpropdef == null then return # Skip broken attribute
732 var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
733 var ret_type = npropdef.mpropdef.static_mtype
734 if ret_type == null then return
735 var mparameter = new MParameter(paramname, ret_type, false)
736 mparameters.add(mparameter)
737 end
738 end
739
740 var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
741 var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
742 var msignature = new MSignature(mparameters, null)
743 mpropdef.msignature = msignature
744 mprop.is_init = true
745 nclassdef.mfree_init = mpropdef
746 self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
747 end
748
749 # Return the static type associated to the node `ntype'.
750 # `classdef' is the context where the call is made (used to understand formal types)
751 # The mmodule used as context is `nclassdef.mmodule'
752 # In case of problem, an error is displayed on `ntype' and null is returned.
753 # FIXME: the name "resolve_mtype" is awful
754 fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
755 do
756 var name = ntype.n_id.text
757 var mclassdef = nclassdef.mclassdef
758 var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
759 var res: MType
760
761 # Check virtual type
762 if mclassdef != null then
763 var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp)
764 if prop != null 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 res = prop.mvirtualtype
769 if ntype.n_kwnullable != null then res = res.as_nullable
770 return res
771 end
772 end
773
774 # Check parameter type
775 if mclassdef != null and mclassdef.parameter_names.has(name) then
776 if not ntype.n_types.is_empty then
777 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
778 end
779 for i in [0..mclassdef.parameter_names.length[ do
780 if mclassdef.parameter_names[i] == name then
781 res = mclassdef.mclass.mclass_type.arguments[i]
782 if ntype.n_kwnullable != null then res = res.as_nullable
783 return res
784 end
785 end
786 abort
787 end
788
789 # Check class
790 var mclass = try_get_mclass_by_name(ntype, mmodule, name)
791 if mclass != null then
792 var arity = ntype.n_types.length
793 if arity != mclass.arity then
794 if arity == 0 then
795 error(ntype, "Type error: '{name}' is a generic class.")
796 else if mclass.arity == 0 then
797 error(ntype, "Type error: '{name}' is not a generic class.")
798 else
799 error(ntype, "Type error: '{name}' has {mclass.arity} parameters ({arity} are provided).")
800 end
801 return null
802 end
803 if arity == 0 then
804 res = mclass.mclass_type
805 if ntype.n_kwnullable != null then res = res.as_nullable
806 return res
807 else
808 var mtypes = new Array[MType]
809 for nt in ntype.n_types do
810 var mt = resolve_mtype(nclassdef, nt)
811 if mt == null then return null # Forward error
812 mtypes.add(mt)
813 end
814 res = mclass.get_mtype(mtypes)
815 if ntype.n_kwnullable != null then res = res.as_nullable
816 return res
817 end
818 end
819
820 # If everything fail, then give up :(
821 error(ntype, "Type error: class {name} not found in module {mmodule}.")
822 return null
823 end
824
825 # Helper function to display an error on a node.
826 # Alias for `self.toolcontext.error(n.hot_location, text)'
827 fun error(n: ANode, text: String)
828 do
829 self.toolcontext.error(n.hot_location, text)
830 end
831
832 # Helper function to display a warning on a node.
833 # Alias for: `self.toolcontext.warning(n.hot_location, text)'
834 fun warning(n: ANode, text: String)
835 do
836 self.toolcontext.warning(n.hot_location, text)
837 end
838
839 # Force to get the primitive method named `name' on the type `recv' or do a fatal error on `n'
840 fun force_get_primitive_method(n: ANode, name: String, recv: MType, mmodule: MModule): MMethod
841 do
842 var res = mmodule.try_get_primitive_method(name, recv)
843 if res == null then
844 self.toolcontext.fatal_error(n.hot_location, "Fatal Error: {recv} must have a property named {name}.")
845 abort
846 end
847 return res
848 end
849 end
850
851 redef class AModule
852 # The associated MModule once build by a `ModelBuilder'
853 var mmodule: nullable MModule
854 # Flag that indicate if the importation is already completed
855 var is_importation_done: Bool = false
856 # Flag that indicate if the class and prop building is already completed
857 var build_classes_is_done: Bool = false
858 # What is the AClassdef associated to a MClass?
859 # Used to check multiple definition of a class.
860 var mclass2nclassdef: Map[MClass, AClassdef] = new HashMap[MClass, AClassdef]
861
862 end
863
864 redef class MClass
865 # The class whose self inherit all the constructors.
866 # 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
867 var inherit_init_from: nullable MClass = null
868 end
869
870 redef class AClassdef
871 # The associated MClass once build by a `ModelBuilder'
872 var mclass: nullable MClass
873 # The associated MClassDef once build by a `ModelBuilder'
874 var mclassdef: nullable MClassDef
875 var build_properties_is_done: Bool = false
876 # The list of super-constructor to call at the start of the free constructor
877 # 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
878 var super_inits: nullable Collection[MMethod] = null
879
880 # The free init (implicitely constructed by the class if required)
881 var mfree_init: nullable MMethodDef = null
882 end
883
884 redef class AClasskind
885 # The class kind associated with the AST node class
886 private fun mkind: MClassKind is abstract
887 end
888 redef class AConcreteClasskind
889 redef fun mkind do return concrete_kind
890 end
891 redef class AAbstractClasskind
892 redef fun mkind do return abstract_kind
893 end
894 redef class AInterfaceClasskind
895 redef fun mkind do return interface_kind
896 end
897 redef class AEnumClasskind
898 redef fun mkind do return enum_kind
899 end
900 redef class AExternClasskind
901 redef fun mkind do return extern_kind
902 end
903
904 redef class AVisibility
905 # The visibility level associated with the AST node class
906 private fun mvisibility: MVisibility is abstract
907 end
908 redef class AIntrudeVisibility
909 redef fun mvisibility do return intrude_visibility
910 end
911 redef class APublicVisibility
912 redef fun mvisibility do return public_visibility
913 end
914 redef class AProtectedVisibility
915 redef fun mvisibility do return protected_visibility
916 end
917 redef class APrivateVisibility
918 redef fun mvisibility do return private_visibility
919 end
920
921
922 #
923
924 redef class Prod
925 # Join the text of all tokens
926 # Used to get the 'real name' of method definitions.
927 fun collect_text: String
928 do
929 var v = new TextCollectorVisitor
930 v.enter_visit(self)
931 assert v.text != ""
932 return v.text
933 end
934 end
935
936 private class TextCollectorVisitor
937 super Visitor
938 var text: String = ""
939 redef fun visit(n)
940 do
941 if n isa Token then text += n.text
942 n.visit_all(self)
943 end
944 end
945
946 redef class APropdef
947 private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef)
948 do
949 end
950 private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
951 do
952 end
953 private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef)
954 do
955 end
956 private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility
957 do
958 var mvisibility = public_visibility
959 if nvisibility != null then mvisibility = nvisibility.mvisibility
960 if nclassdef.mclassdef.mclass.visibility == private_visibility then
961 if mvisibility == protected_visibility then
962 assert nvisibility != null
963 modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.")
964 else if mvisibility == private_visibility then
965 assert nvisibility != null
966 # Not yet
967 # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.")
968 end
969 mvisibility = private_visibility
970 end
971 return mvisibility
972 end
973
974 private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty)
975 do
976 if nvisibility == null then return
977 var mvisibility = nvisibility.mvisibility
978 if mvisibility != mprop.visibility and mvisibility != public_visibility then
979 modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}")
980 end
981 end
982
983 private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool
984 do
985 if kwredef == null then
986 if need_redef then
987 modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
988 return false
989 end
990 else
991 if not need_redef then
992 modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
993 return false
994 end
995 end
996 return true
997 end
998
999 end
1000
1001 redef class ASignature
1002 # Is the model builder has correctly visited the signature
1003 var is_visited = false
1004 # Names of parameters from the AST
1005 # REQUIRE: is_visited
1006 var param_names = new Array[String]
1007 # Types of parameters from the AST
1008 # REQUIRE: is_visited
1009 var param_types = new Array[MType]
1010 # Rank of the vararg (of -1 if none)
1011 # REQUIRE: is_visited
1012 var vararg_rank: Int = -1
1013 # Return type
1014 var ret_type: nullable MType = null
1015
1016 # Visit and fill information about a signature
1017 private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool
1018 do
1019 var param_names = self.param_names
1020 var param_types = self.param_types
1021 for np in self.n_params do
1022 param_names.add(np.n_id.text)
1023 var ntype = np.n_type
1024 if ntype != null then
1025 var mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1026 if mtype == null then return false # Skip error
1027 for i in [0..param_names.length-param_types.length[ do
1028 param_types.add(mtype)
1029 end
1030 if np.n_dotdotdot != null then
1031 if self.vararg_rank != -1 then
1032 modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg")
1033 return false
1034 else
1035 self.vararg_rank = param_names.length - 1
1036 end
1037 end
1038 end
1039 end
1040 var ntype = self.n_type
1041 if ntype != null then
1042 self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype)
1043 if self.ret_type == null then return false # Skip errir
1044 end
1045
1046 for nclosure in self.n_closure_decls do
1047 if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false
1048 end
1049
1050 self.is_visited = true
1051 return true
1052 end
1053
1054 # Build a visited signature
1055 fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature
1056 do
1057 if param_names.length != param_types.length then
1058 # Some parameters are typed, other parameters are not typed.
1059 modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1060 return null
1061 end
1062
1063 var mparameters = new Array[MParameter]
1064 for i in [0..param_names.length[ do
1065 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1066 mparameters.add(mparameter)
1067 end
1068
1069 var msignature = new MSignature(mparameters, ret_type)
1070 return msignature
1071 end
1072 end
1073
1074 redef class AMethPropdef
1075 # The associated MMethodDef once build by a `ModelBuilder'
1076 var mpropdef: nullable MMethodDef
1077
1078 # The associated super init if any
1079 var super_init: nullable MMethod
1080 redef fun build_property(modelbuilder, nclassdef)
1081 do
1082 var is_init = self isa AInitPropdef
1083 var mclassdef = nclassdef.mclassdef.as(not null)
1084 var name: String
1085 var amethodid = self.n_methid
1086 var name_node: ANode
1087 if amethodid == null then
1088 if self isa AMainMethPropdef then
1089 name = "main"
1090 name_node = self
1091 else if self isa AConcreteInitPropdef then
1092 name = "init"
1093 name_node = self.n_kwinit
1094 else if self isa AExternInitPropdef then
1095 name = "new"
1096 name_node = self.n_kwnew
1097 else
1098 abort
1099 end
1100 else if amethodid isa AIdMethid then
1101 name = amethodid.n_id.text
1102 name_node = amethodid
1103 else
1104 # operator, bracket or assign
1105 name = amethodid.collect_text
1106 name_node = amethodid
1107
1108 if name == "-" and self.n_signature.n_params.length == 0 then
1109 name = "unary -"
1110 end
1111 end
1112
1113 var mprop: nullable MMethod = null
1114 if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod)
1115 if mprop == null then
1116 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1117 mprop = new MMethod(mclassdef, name, mvisibility)
1118 mprop.is_init = is_init
1119 mprop.is_new = self isa AExternInitPropdef
1120 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return
1121 else
1122 if n_kwredef == null then
1123 if self isa AMainMethPropdef then
1124 # no warning
1125 else
1126 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop) then return
1127 end
1128 end
1129 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1130 end
1131
1132 var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
1133
1134 self.mpropdef = mpropdef
1135 modelbuilder.mpropdef2npropdef[mpropdef] = self
1136 if mpropdef.is_intro then
1137 modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
1138 else
1139 modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
1140 end
1141 end
1142
1143 redef fun build_signature(modelbuilder, nclassdef)
1144 do
1145 var mpropdef = self.mpropdef
1146 if mpropdef == null then return # Error thus skiped
1147 var mmodule = mpropdef.mclassdef.mmodule
1148 var nsig = self.n_signature
1149
1150 # Retrieve info from the signature AST
1151 var param_names = new Array[String] # Names of parameters from the AST
1152 var param_types = new Array[MType] # Types of parameters from the AST
1153 var vararg_rank = -1
1154 var ret_type: nullable MType = null # Return type from the AST
1155 if nsig != null then
1156 if not nsig.visit_signature(modelbuilder, nclassdef) then return
1157 param_names = nsig.param_names
1158 param_types = nsig.param_types
1159 vararg_rank = nsig.vararg_rank
1160 ret_type = nsig.ret_type
1161 end
1162
1163 # Look for some signature to inherit
1164 # FIXME: do not inherit from the intro, but from the most specific
1165 var msignature: nullable MSignature = null
1166 if not mpropdef.is_intro then
1167 msignature = mpropdef.mproperty.intro.msignature
1168 if msignature == null then return # Skip error
1169
1170 # Check inherited signature arity
1171 if param_names.length != msignature.arity then
1172 var node: ANode
1173 if nsig != null then node = nsig else node = self
1174 modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1175 return
1176 end
1177 else if mpropdef.mproperty.is_init then
1178 # FIXME UGLY: inherit signature from a super-constructor
1179 for msupertype in nclassdef.mclassdef.supertypes do
1180 msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype)
1181 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
1182 if candidate != null then
1183 if msignature == null then
1184 msignature = candidate.intro.as(MMethodDef).msignature
1185 end
1186 end
1187 end
1188 end
1189
1190
1191 # Inherit the signature
1192 if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then
1193 # Parameters are untyped, thus inherit them
1194 param_types = new Array[MType]
1195 for mparameter in msignature.mparameters do
1196 param_types.add(mparameter.mtype)
1197 end
1198 vararg_rank = msignature.vararg_rank
1199 end
1200 if msignature != null and ret_type == null then
1201 ret_type = msignature.return_mtype
1202 end
1203
1204 if param_names.length != param_types.length then
1205 # Some parameters are typed, other parameters are not typed.
1206 modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.")
1207 return
1208 end
1209
1210 var mparameters = new Array[MParameter]
1211 for i in [0..param_names.length[ do
1212 var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank)
1213 mparameters.add(mparameter)
1214 end
1215
1216 msignature = new MSignature(mparameters, ret_type)
1217 mpropdef.msignature = msignature
1218
1219 if nsig != null then
1220 for nclosure in nsig.n_closure_decls do
1221 var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef)
1222 if clos_signature == null then return
1223 var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false)
1224 msignature.mclosures.add(mparameter)
1225 end
1226 end
1227
1228 end
1229
1230 redef fun check_signature(modelbuilder, nclassdef)
1231 do
1232 var mpropdef = self.mpropdef
1233 if mpropdef == null then return # Error thus skiped
1234 var mmodule = mpropdef.mclassdef.mmodule
1235 var nsig = self.n_signature
1236 var mysignature = self.mpropdef.msignature
1237 if mysignature == null then return # Error thus skiped
1238
1239 # Lookup for signature in the precursor
1240 # FIXME all precursors should be considered
1241 if not mpropdef.is_intro then
1242 var msignature = mpropdef.mproperty.intro.msignature
1243 if msignature == null then return
1244
1245 var precursor_ret_type = msignature.return_mtype
1246 var ret_type = mysignature.return_mtype
1247 if ret_type != null and precursor_ret_type == null then
1248 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1249 return
1250 end
1251
1252 if mysignature.arity > 0 then
1253 # Check parameters types
1254 for i in [0..mysignature.arity[ do
1255 var myt = mysignature.mparameters[i].mtype
1256 var prt = msignature.mparameters[i].mtype
1257 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1258 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1259 modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1260 end
1261 end
1262 end
1263 if precursor_ret_type != null then
1264 if ret_type == null then
1265 # Inherit the return type
1266 ret_type = precursor_ret_type
1267 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1268 modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1269 end
1270 end
1271 end
1272 end
1273 end
1274
1275 redef class AAttrPropdef
1276 # The associated MAttributeDef once build by a `ModelBuilder'
1277 var mpropdef: nullable MAttributeDef
1278 # The associated getter (read accessor) if any
1279 var mreadpropdef: nullable MMethodDef
1280 # The associated setter (write accessor) if any
1281 var mwritepropdef: nullable MMethodDef
1282 redef fun build_property(modelbuilder, nclassdef)
1283 do
1284 var mclassdef = nclassdef.mclassdef.as(not null)
1285 var mclass = mclassdef.mclass
1286
1287 var name: String
1288 if self.n_id != null then
1289 name = self.n_id.text
1290 else
1291 name = self.n_id2.text
1292 end
1293
1294 if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
1295 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
1296 else if mclass.kind == enum_kind then
1297 modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
1298 end
1299
1300 var nid = self.n_id
1301 if nid != null then
1302 # Old attribute style
1303 var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name)
1304 if mprop == null then
1305 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1306 mprop = new MAttribute(mclassdef, name, mvisibility)
1307 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
1308 else
1309 assert mprop isa MAttribute
1310 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1311 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
1312 end
1313 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1314 self.mpropdef = mpropdef
1315 modelbuilder.mpropdef2npropdef[mpropdef] = self
1316
1317 var nreadable = self.n_readable
1318 if nreadable != null then
1319 var readname = name.substring_from(1)
1320 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod)
1321 if mreadprop == null then
1322 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility)
1323 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1324 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return
1325 else
1326 if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return
1327 check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop)
1328 end
1329 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1330 self.mreadpropdef = mreadpropdef
1331 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1332 end
1333
1334 var nwritable = self.n_writable
1335 if nwritable != null then
1336 var writename = name.substring_from(1) + "="
1337 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod)
1338 if mwriteprop == null then
1339 var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1340 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1341 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return
1342 else
1343 if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return
1344 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1345 end
1346 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1347 self.mwritepropdef = mwritepropdef
1348 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1349 end
1350 else
1351 # New attribute style
1352 var nid2 = self.n_id2.as(not null)
1353 var mprop = new MAttribute(mclassdef, "@" + name, none_visibility)
1354 var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
1355 self.mpropdef = mpropdef
1356 modelbuilder.mpropdef2npropdef[mpropdef] = self
1357
1358 var readname = name
1359 var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
1360 if mreadprop == null then
1361 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1362 mreadprop = new MMethod(mclassdef, readname, mvisibility)
1363 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return
1364 else
1365 if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return
1366 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop)
1367 end
1368 var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
1369 self.mreadpropdef = mreadpropdef
1370 modelbuilder.mpropdef2npropdef[mreadpropdef] = self
1371
1372 var writename = name + "="
1373 var nwritable = self.n_writable
1374 var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod)
1375 var nwkwredef: nullable Token = null
1376 if nwritable != null then nwkwredef = nwritable.n_kwredef
1377 if mwriteprop == null then
1378 var mvisibility
1379 if nwritable != null then
1380 mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility)
1381 else
1382 mvisibility = private_visibility
1383 end
1384 mwriteprop = new MMethod(mclassdef, writename, mvisibility)
1385 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return
1386 else
1387 if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return
1388 if nwritable != null then
1389 check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop)
1390 end
1391 end
1392 var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
1393 self.mwritepropdef = mwritepropdef
1394 modelbuilder.mpropdef2npropdef[mwritepropdef] = self
1395 end
1396 end
1397
1398 redef fun build_signature(modelbuilder, nclassdef)
1399 do
1400 var mpropdef = self.mpropdef
1401 if mpropdef == null then return # Error thus skiped
1402 var mmodule = mpropdef.mclassdef.mmodule
1403 var mtype: nullable MType = null
1404
1405 var ntype = self.n_type
1406 if ntype != null then
1407 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1408 if mtype == null then return
1409 end
1410
1411 if mtype == null then
1412 var nexpr = self.n_expr
1413 if nexpr != null then
1414 if nexpr isa ANewExpr then
1415 mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type)
1416 else if nexpr isa AIntExpr then
1417 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int")
1418 if cla != null then mtype = cla.mclass_type
1419 else if nexpr isa AFloatExpr then
1420 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float")
1421 if cla != null then mtype = cla.mclass_type
1422 else if nexpr isa ACharExpr then
1423 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
1424 if cla != null then mtype = cla.mclass_type
1425 else if nexpr isa ABoolExpr then
1426 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool")
1427 if cla != null then mtype = cla.mclass_type
1428 else if nexpr isa ASuperstringExpr then
1429 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1430 if cla != null then mtype = cla.mclass_type
1431 else if nexpr isa AStringFormExpr then
1432 var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
1433 if cla != null then mtype = cla.mclass_type
1434 else
1435 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
1436 end
1437
1438 else
1439 modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
1440 end
1441 end
1442
1443 if mtype == null then return
1444
1445 mpropdef.static_mtype = mtype
1446
1447 var mreadpropdef = self.mreadpropdef
1448 if mreadpropdef != null then
1449 var msignature = new MSignature(new Array[MParameter], mtype)
1450 mreadpropdef.msignature = msignature
1451 end
1452
1453 var msritepropdef = self.mwritepropdef
1454 if mwritepropdef != null then
1455 var name: String
1456 if n_id != null then
1457 name = n_id.text.substring_from(1)
1458 else
1459 name = n_id2.text
1460 end
1461 var mparameter = new MParameter(name, mtype, false)
1462 var msignature = new MSignature([mparameter], null)
1463 mwritepropdef.msignature = msignature
1464 end
1465 end
1466
1467 redef fun check_signature(modelbuilder, nclassdef)
1468 do
1469 var mpropdef = self.mpropdef
1470 if mpropdef == null then return # Error thus skiped
1471 var mmodule = mpropdef.mclassdef.mmodule
1472 var ntype = self.n_type
1473 var mtype = self.mpropdef.static_mtype
1474 if mtype == null then return # Error thus skiped
1475
1476 # Lookup for signature in the precursor
1477 # FIXME all precursors should be considered
1478 if not mpropdef.is_intro then
1479 var precursor_type = mpropdef.mproperty.intro.static_mtype
1480 if precursor_type == null then return
1481
1482 if mtype != precursor_type then
1483 modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.")
1484 return
1485 end
1486 end
1487
1488 # Check getter and setter
1489 var meth = self.mreadpropdef
1490 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1491 meth = self.mwritepropdef
1492 if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth)
1493 end
1494
1495 private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef)
1496 do
1497 var mmodule = mpropdef.mclassdef.mmodule
1498 var nsig = self.n_type
1499 var mysignature = mpropdef.msignature
1500 if mysignature == null then return # Error thus skiped
1501
1502 # Lookup for signature in the precursor
1503 # FIXME all precursors should be considered
1504 if not mpropdef.is_intro then
1505 var msignature = mpropdef.mproperty.intro.msignature
1506 if msignature == null then return
1507
1508 if mysignature.arity != msignature.arity then
1509 var node: ANode
1510 if nsig != null then node = nsig else node = self
1511 modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}")
1512 return
1513 end
1514 var precursor_ret_type = msignature.return_mtype
1515 var ret_type = mysignature.return_mtype
1516 if ret_type != null and precursor_ret_type == null then
1517 var node: ANode
1518 if nsig != null then node = nsig else node = self
1519 modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
1520 return
1521 end
1522
1523 if mysignature.arity > 0 then
1524 # Check parameters types
1525 for i in [0..mysignature.arity[ do
1526 var myt = mysignature.mparameters[i].mtype
1527 var prt = msignature.mparameters[i].mtype
1528 if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and
1529 not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then
1530 var node: ANode
1531 if nsig != null then node = nsig else node = self
1532 modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
1533 end
1534 end
1535 end
1536 if precursor_ret_type != null then
1537 if ret_type == null then
1538 # Inherit the return type
1539 ret_type = precursor_ret_type
1540 else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then
1541 var node: ANode
1542 if nsig != null then node = nsig else node = self
1543 modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
1544 end
1545 end
1546 end
1547 end
1548 end
1549
1550 redef class ATypePropdef
1551 # The associated MVirtualTypeDef once build by a `ModelBuilder'
1552 var mpropdef: nullable MVirtualTypeDef
1553 redef fun build_property(modelbuilder, nclassdef)
1554 do
1555 var mclassdef = nclassdef.mclassdef.as(not null)
1556 var name = self.n_id.text
1557 var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name)
1558 if mprop == null then
1559 var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility)
1560 mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
1561 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return
1562 else
1563 if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return
1564 assert mprop isa MVirtualTypeProp
1565 check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop)
1566 end
1567 var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
1568 self.mpropdef = mpropdef
1569 end
1570
1571 redef fun build_signature(modelbuilder, nclassdef)
1572 do
1573 var mpropdef = self.mpropdef
1574 if mpropdef == null then return # Error thus skiped
1575 var mmodule = mpropdef.mclassdef.mmodule
1576 var mtype: nullable MType = null
1577
1578 var ntype = self.n_type
1579 mtype = modelbuilder.resolve_mtype(nclassdef, ntype)
1580 if mtype == null then return
1581
1582 mpropdef.bound = mtype
1583 # print "{mpropdef}: {mtype}"
1584 end
1585
1586 redef fun check_signature(modelbuilder, nclassdef)
1587 do
1588 var bound = self.mpropdef.bound
1589
1590 # Fast case: the bound is not a formal type
1591 if not bound isa MVirtualType then return
1592
1593 var mmodule = nclassdef.mclassdef.mmodule
1594 var anchor = nclassdef.mclassdef.bound_mtype
1595
1596 # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
1597 var seen = [self.mpropdef.mproperty.mvirtualtype]
1598 loop
1599 if seen.has(bound) then
1600 seen.add(bound)
1601 modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
1602 return
1603 end
1604 seen.add(bound)
1605 var next = bound.lookup_bound(mmodule, anchor)
1606 if not next isa MVirtualType then return
1607 bound = next
1608 end
1609 end
1610 end