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