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