metamodel: give the burden of value conflict to the MMContext
[nit.git] / src / metamodel / inheritance.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 # Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17
18 # Compute importation of classes and inheritance of properties
19 package inheritance
20
21 intrude import static_type
22
23 redef class MMContext
24 # Method to redefine to handle the property conflicts
25 # FIXME: This is just a bad workaround because of a bad design
26 fun handle_property_conflict(lc: MMLocalClass, impls2: Array[MMLocalProperty])
27 do
28 var glob = impls2.first.global
29 stderr.write("Fatal error: inherit_local_property error\n")
30 print("------- {lc.mmmodule}::{lc} {glob.intro.full_name}")
31 for i in impls2 do
32 print(" {i.full_name}")
33 end
34 print("------- {glob.property_hierarchy.first}")
35 print("------- {glob.property_hierarchy.to_dot}")
36 exit(1)
37 end
38 end
39
40 redef class MMModule
41 # The root of the class hierarchy
42 fun type_any: MMType
43 do
44 var c_name = class_by_name(once ("Object".to_symbol))
45 return c_name.get_type
46 end
47
48 # Import global classes from supermodules
49 fun import_global_classes
50 do
51 var globals = new HashMap[MMGlobalClass,HashSet[MMLocalClass]]
52 assert mhe != null
53 for mod in mhe.direct_greaters do
54 for glob in mod.global_classes do
55 if global_classes.has(glob) then continue
56 _global_classes.add(glob)
57 _global_class_by_name[glob.name] = glob
58 end
59 end
60
61 end
62
63 # Create implicit local classes for global classes that are not refined
64 fun import_local_classes
65 do
66 for g in _global_classes do
67 if _local_class_by_global.has_key(g) then continue
68 var impl = new MMImplicitLocalClass(self, g)
69 end
70 end
71 end
72
73 redef class MMLocalClass
74 # List of all parents
75 var _direct_parents: Array[MMAncestor] = new Array[MMAncestor]
76
77 # Is the class computing super.
78 # Used to detect specialization loops.
79 var _computing_super: Bool = false
80
81 # Compute super classes of a class
82 fun compute_super_classes
83 do
84 if computed_super_classes then
85 # do no recompute if allready done
86 return
87 else if _computing_super then
88 stderr.write("Fatal error: Inheritance loop for class {self}\n")
89 exit(1)
90 end
91 _computing_super = true
92
93 var supers = new Array[MMLocalClass]
94 add_explicit_classes(supers)
95 add_super_classes(supers)
96 add_default_any_class(supers)
97 compute_super_parents(supers)
98 var set = new HashSet[MMLocalClass]
99 set.add_all(supers)
100 var u = set.to_a
101 mmmodule.set_supers_class(self,u)
102 assert _crhe != null
103 assert _cshe != null
104 _computing_super = false
105 end
106
107 # Compute ancestors for a class
108 fun compute_ancestors
109 do
110 assert computed_super_classes
111 if computed_ancestors then return
112
113 var ancestors = group_ancestors(build_ancestors)
114 _ancestors = new HashMap[MMLocalClass, MMAncestor]
115
116 for set in ancestors do
117 if set.length == 1 then
118 add_ancestor(set.first)
119 else
120 var ma = merge_ancestors(set)
121 add_ancestor(merge_ancestors(set))
122 end
123 end
124 end
125
126 var _are_global_properties_inherited: Bool = false
127
128 # Inherit global properties for a class
129 private fun inherit_global_properties
130 do
131 if _are_global_properties_inherited then return
132 _are_global_properties_inherited = true
133
134 var names = _properties_by_name
135 var set = _global_properties
136 for c in che.direct_greaters do
137 for glob in c.global_properties do
138 if set.has(glob) then continue
139
140 set.add(glob) # Add the global property
141
142 # Do not inherit constructors trough specialization
143 #print "{c.mmmodule}::{c} -> {mmmodule}::{self} for {glob.local_property.local_class.mmmodule}::{glob.local_property.local_class}::{glob.local_property} : {glob.is_init}"
144 if glob.is_init and glob.intro.local_class.global != global then
145 #print "pass"
146 continue
147 end
148
149 # Do not inherit new style attributes
150 if glob.intro.name.to_s[0] == '@' then continue
151
152 make_visible_an_inherited_global_property(glob)
153 end
154 end
155 end
156
157 redef fun global_properties
158 do
159 if _are_global_properties_inherited then return _global_properties
160 assert computed_super_classes
161 inherit_global_properties
162 return _global_properties
163 end
164
165 redef fun has_global_property(g)
166 do
167 # has_global_property can be called during the construction of the class
168 # hierarchy to check that a type "X" is not a formal type.
169 if not computed_super_classes then return false
170
171 var set = _global_properties
172 if set.has(g) then return true
173 for c in che.direct_greaters do
174 if c.has_global_property(g) then
175 set.add(g)
176 return true
177 end
178 end
179 return false
180 end
181
182 redef fun has_global_property_by_name(n)
183 do
184 # has_global_property can be called during the construction of the class
185 # hierarchy to check that a type "X" is not a formal type.
186 if not computed_super_classes then return false
187
188 # Ensure that super-classes are constructed
189 compute_super_classes
190
191 if _properties_by_name.has_key(n) then
192 return _properties_by_name[n].length == 1
193 end
194 var set = _global_properties
195 var nset
196 if _properties_by_name.has_key(n) then
197 nset = _properties_by_name[n]
198 else
199 nset = new Array[MMGlobalProperty]
200 _properties_by_name[n] = nset
201 end
202 for c in che.direct_greaters do
203 if c.has_global_property_by_name(n) then
204 var g = c.get_property_by_name(n)
205 if not set.has(g) then set.add(g)
206 if g.is_init and g.intro.local_class.global != global then continue
207 if g.intro.name.to_s.first == '@' then continue # inherited new style attibutes are invisible
208 if nset.has(g) then continue
209 nset.add(g)
210 end
211 end
212 return nset.length == 1
213 end
214
215 # Make the name of a global property meaningful in the class
216 fun make_visible_an_inherited_global_property(glob: MMGlobalProperty)
217 do
218 var names = _properties_by_name
219 var gname = glob.intro.name
220 var conf_set: Array[MMGlobalProperty]
221 if names.has_key(gname) then
222 conf_set = names[gname]
223 else
224 conf_set = new Array[MMGlobalProperty]
225 names[gname] = conf_set
226 end
227 conf_set.add(glob)
228 end
229
230 # Add super stype of this current local class
231 fun add_direct_parent(p: MMAncestor)
232 do
233 _direct_parents.add(p)
234 end
235
236 # Are super-class already computed?
237 fun computed_super_classes: Bool
238 do
239 return _crhe != null and _cshe != null
240 end
241
242 # Are ancestors already computed
243 fun computed_ancestors: Bool
244 do
245 return _ancestors != null
246 end
247
248 # Get the ancestor for a given class
249 # TODO: is this useful?
250 private fun ancestor_for(c: MMLocalClass): MMAncestor
251 do
252 assert ancestors != null
253
254 if _ancestors.has_key(c) then
255 return _ancestors[c]
256 end
257 var a = c.for_module(mmmodule)
258 assert cshe <= a
259 var ra: MMAncestor
260 if _ancestors.has_key(a) then
261 ra = _ancestors[a]
262 else if c.global == _global then
263 ra = new MMRefineAncestor(self,c)
264 else
265 ra = new MMSpecAncestor(get_type,c.get_type)
266 end
267 _ancestors[c] = ra
268 return ra
269 end
270
271 redef fun [](glob)
272 do
273 if _local_property_by_global.has_key(glob) then
274 return _local_property_by_global[glob]
275 else if has_global_property(glob) then
276 return inherit_local_property(glob)
277 else
278 abort
279 end
280 end
281
282 # Add default super class in direct parent and in super classes if this is not the Object class
283 private fun add_default_any_class(supers: Array[MMLocalClass])
284 do
285 if supers.is_empty and name != once ("Object".to_symbol) then
286 var t_any = mmmodule.type_any
287 supers.add(t_any.local_class)
288 var default = new MMDefaultAncestor(self, t_any)
289 add_direct_parent(default)
290 end
291 end
292
293 # Adding inherited class from previous refinement of self
294 private fun add_super_classes(supers: Array[MMLocalClass])
295 do
296 assert _crhe != null
297 for ref in _crhe.direct_greaters do
298 for sup in ref.cshe.direct_greaters do
299 var cla = sup.for_module(_mmmodule)
300 supers.add(cla)
301 end
302 end
303 end
304
305 # Add self parents of this local class
306 private fun add_explicit_classes(supers: Array[MMLocalClass])
307 do
308 for p in _direct_parents do
309 supers.add(p.local_class)
310 end
311 end
312
313 # Ensure all super parents are computed
314 private fun compute_super_parents(supers: Array[MMLocalClass])
315 do
316 for p in supers do
317 p.compute_super_classes
318 end
319 end
320
321 # compute all ancestors for a class (multiple)
322 private fun build_ancestors: Array[MMAncestor]
323 do
324 var all_ancestors = new Array[MMAncestor]
325 # Refined classes are ancestors
326 assert _crhe != null
327 for p in _crhe.direct_greaters do
328 assert p != self
329 var anc = new MMRefineAncestor(self, p)
330 anc.add_in(all_ancestors)
331 end
332 for anc in _direct_parents do
333 assert anc.local_class != self
334 anc.add_in(all_ancestors)
335 end
336 return all_ancestors
337 end
338
339 # Build an ancestor map indexed by LocalClass
340 private fun group_ancestors(all: Array[MMAncestor]): Map[MMLocalClass, Set[MMAncestor]]
341 do
342 #print "process {self}"
343 var map = new HashMap[MMLocalClass, Set[MMAncestor]]
344 for a in all do
345 var c = a.local_class
346 #print "ancestor is"
347 #print " {c}"
348 var set: Set[MMAncestor]
349 c.compute_ancestors
350 if map.has_key(c) then
351 set = map[c]
352 else
353 set = new HashSet[MMAncestor]
354 map[c] = set
355 end
356 set.add(a)
357 end
358 return map
359 end
360
361 # Remove duplicate ancestors and merge if compatible, in the other case do an error
362 private fun merge_ancestors(set: Set[MMAncestor]): MMAncestor
363 do
364 var marks = new HashSet[MMAncestor]
365 var res = new Array[MMAncestor]
366 for t in set do
367 var it = set.iterator
368 var search = true
369 while it.is_ok and search do
370
371 var a = t == it.item
372 a = marks.has(it.item)
373 a = it.item.stype < t.stype
374
375 if not(t == it.item or marks.has(it.item)) and
376 (it.item.stype < t.stype) then
377 marks.add(t)
378 search = false
379 end
380 it.next
381 end
382 if not marks.has(t) then
383 res.add(t)
384 end
385 end
386
387 if res.length > 1 then
388 stderr.write("Fatal error: Incompatibles ancestors for {self.name}: {res.join(", ")}\n")
389 exit(1)
390 end
391 return res.first
392 end
393
394 # Inherit a local property
395 # Is lazily called
396 # FIXME: dont crash lazily
397 private fun inherit_local_property(glob: MMGlobalProperty): MMLocalProperty
398 do
399 assert not _local_property_by_global.has_key(glob)
400
401 var impl: MMLocalProperty
402
403 var ghier = glob.property_hierarchy
404 var supers = che.direct_greaters
405 if ghier.length == 1 then
406 # Unredefined property
407 impl = glob.intro
408 else if supers.length == 1 then
409 # Single inheritance
410 impl = supers.first[glob]
411 else
412 # Hard multiple inheritance
413 # First compute the set of bottom properties
414 var impls = new ArraySet[MMLocalProperty]
415 for sc in supers do
416 if sc.has_global_property(glob) then impls.add(sc[glob])
417 end
418 # Second, extract most specific
419 var impls2 = ghier.select_smallests(impls)
420 # Conflict case (FIXME)
421 if impls2.length != 1 then
422 self.mmmodule.context.handle_property_conflict(self, impls2)
423 end
424 impl = impls2.first
425 end
426
427 # FIXME: Why these 3 lines ?
428 #var ac = ancestor_for(impl.local_class)
429 #ac.local_class.inherit_global_properties
430 #var a = ac.stype
431 #assert a.local_class != self
432
433 # Register the local property
434 _local_property_by_global[glob] = impl
435
436 return impl
437 end
438 end
439
440 redef class MMLocalProperty
441 # Attach self to a global property
442 fun inherit_global(g: MMGlobalProperty)
443 do
444 set_global(g)
445 var impls = new Array[MMLocalProperty]
446 for sc in local_class.che.direct_greaters do
447 if not sc.has_global_property(g) then continue
448 impls.add(sc[g])
449 end
450 g.add_local_property(self, impls)
451 end
452 end
453
454 redef class MMAncestor
455 # Add this ancestor and it's super one in tab
456 private fun add_in(tab: Array[MMAncestor])
457 do
458 tab.add(self)
459 stype.local_class.compute_ancestors
460 for anc in stype.local_class.ancestors.as(not null) do
461 var aaa = anc.stype.for_module(stype.mmmodule)
462 var a = aaa.adapt_to(stype).for_module(inheriter.mmmodule)
463 if a.local_class != inheriter.local_class then
464 var it = tab.iterator
465 var b = true
466 while it.is_ok and b do
467 b = not ( it.item.inheriter == inheriter and it.item.stype == a)
468 it.next
469 end
470 if b then
471 tab.add(new MMSpecAncestor(inheriter,a))
472 end
473 end
474 end
475 end
476 end
477
478 ##########################################
479
480 # A local class that is a pure importation of an other local class
481 class MMImplicitLocalClass
482 super MMLocalClass
483 init(mod: MMModule, g: MMGlobalClass)
484 do
485 var cla = g.intro
486 super(mod, cla.name, cla.arity)
487 set_global(g)
488 end
489 end
490
491 class MMRefineAncestor
492 super MMAncestor
493 redef readable var _local_class: MMLocalClass
494
495 init(b: MMLocalClass, a: MMLocalClass)
496 do
497 _local_class = a
498 inheriter = b.get_type
499 stype = _local_class.get_type
500 end
501 end
502
503
504 class MMSpecAncestor
505 super MMAncestor
506 redef fun local_class do return stype.local_class
507
508 init(inheriter: MMType, stype: MMType)
509 do
510 _inheriter = inheriter
511 _stype = stype
512 end
513 end
514
515 class MMDefaultAncestor
516 super MMAncestor
517 redef fun local_class do return stype.local_class
518
519 init(b: MMLocalClass, anc: MMType)
520 do
521 inheriter = b.get_type
522 stype = anc
523 end
524 end