rta: add RapidTypeAnalysis::live_classes
[nit.git] / src / rapid_type_analysis.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
18 # Rapid type analysis on the AST with heterogenous generics and customization
19 #
20 # Rapid type analysis is an analyse that aproximates the set of live classes
21 # and the set of live methods starting from the entry point of the program.
22 # These two sets are interdependant and computed together.
23 # It is quite efficient but the type set is global and pollutes each call site.
24 #
25 # Heterogenous generics means that each intancied generic class is associated
26 # to a distinct rapid type.
27 # Heterogenous generics has the advantage to resolve the formal generic
28 # parameters types but increase the number of types.
29 # More important, heterogenous generics cannot deal with infinite number of rapid
30 # types since the analyse tries to list them all (so some programs will be badly refused)
31 #
32 # Customization means that each method definition is analyzed one per rapid
33 # type of receiver.
34 # Customization have the following advantages:
35 # * `self' is monomorphic
36 # * virtual types are all resolved
37 # * live attributes can be determined on each class
38 # But has the disadvantage to explode the number of rapid method: each method
39 # definition for each rapid type that need it
40 module rapid_type_analysis
41
42 import model
43 import modelbuilder
44 import typing
45 import auto_super_init
46
47 redef class ModelBuilder
48 fun do_rapid_type_analysis(mainmodule: MModule): RapidTypeAnalysis
49 do
50 var time0 = get_time
51 self.toolcontext.info("*** RAPID TYPE ANALYSIS ***", 1)
52
53 var model = self.model
54 var analysis = new RapidTypeAnalysis(self, mainmodule)
55 var nmodule = self.nmodules.first
56 var maintype = mainmodule.sys_type
57 if maintype == null then return analysis # No entry point
58 analysis.add_type(maintype)
59 var initprop = mainmodule.try_get_primitive_method("init", maintype)
60 if initprop != null then
61 analysis.add_monomorphic_send(maintype, initprop)
62 end
63 var mainprop = mainmodule.try_get_primitive_method("main", maintype)
64 if mainprop != null then
65 analysis.add_monomorphic_send(maintype, mainprop)
66 end
67 analysis.run_analysis
68
69 var time1 = get_time
70 self.toolcontext.info("*** END RAPID TYPE ANALYSIS: {time1-time0} ***", 2)
71
72 return analysis
73 end
74 end
75
76 # RapidTypeAnalysis looks for alive rapid types in application.
77 # The entry point of the analysis is the mainmodule of the application.
78 class RapidTypeAnalysis
79 # The modelbuilder used to get the AST.
80 var modelbuilder: ModelBuilder
81
82 # The main module of the analysis.
83 # Used to perform types operations.
84 var mainmodule: MModule
85
86 # The pool to live types.
87 # During the analysis, new types are added and combined with
88 # live_send_sites to determine new customized_methoddefs to visit
89 var live_types: HashSet[MClassType] = new HashSet[MClassType]
90
91 # Live (instantiated) classes.
92 var live_classes: HashSet[MClass] = new HashSet[MClass]
93
94 # The pool of types used to perform type checks (isa and as).
95 var live_cast_types: HashSet[MClassType] = new HashSet[MClassType]
96
97 # Live method definitions.
98 # These method definitions are:
99 # * visited trough a add_send on an already known live_type
100 # * visited trough a add_type for an already known live_send_sites
101 # * visided by a add_monomorphic_send or a add_static_call
102 var live_methoddefs: HashSet[MMethodDef] = new HashSet[MMethodDef]
103
104 # The pool of live customized method definitions
105 var live_customized_methoddefs: HashSet[CustomizedMethodDef] = new HashSet[CustomizedMethodDef]
106
107 # The pool of live RTA send site
108 # During the analysis, new live_send_sites are added and combined with
109 # live_types to determine new live_customized_methoddefs to visit
110 var live_send_sites: HashSet[RTASendSite] = new HashSet[RTASendSite]
111
112 # The customized method definitions that remain to visit
113 private var todo: List[CustomizedMethodDef] = new List[CustomizedMethodDef]
114
115 # Adapt and remove nullable
116 # return null if we got the null type
117 fun cleanup_type(mtype: MType, recvtype: MClassType): nullable MClassType
118 do
119 mtype = mtype.anchor_to(self.mainmodule, recvtype)
120 if mtype isa MNullType then return null
121 if mtype isa MNullableType then mtype = mtype.mtype
122 assert mtype isa MClassType
123 assert not mtype.need_anchor
124 return mtype
125 end
126
127 # Add a live type to the pool
128 #
129 # If the types is already live, then do nothing.
130 #
131 # REQUIRE: not mtype.need_anchor
132 fun add_type(mtype: MClassType)
133 do
134 if self.live_types.has(mtype) then return
135
136 assert not mtype.need_anchor
137 self.live_types.add(mtype)
138 self.check_depth(mtype)
139
140 self.live_classes.add(mtype.mclass)
141
142 # Collect default attributes
143 for cd in mtype.collect_mclassdefs(self.mainmodule)
144 do
145 var nclassdef = self.modelbuilder.mclassdef2nclassdef[cd]
146 for npropdef in nclassdef.n_propdefs do
147 if not npropdef isa AAttrPropdef then continue
148 var nexpr = npropdef.n_expr
149 if nexpr == null then continue
150 var v = new RapidTypeVisitor(self, nclassdef, npropdef.mpropdef.as(not null), mtype)
151 v.enter_visit(nexpr)
152 end
153 end
154
155 for rss in self.live_send_sites do
156 if not mtype.is_subtype(self.mainmodule, null, rss.receiver) then continue
157 if mtype.has_mproperty(self.mainmodule, rss.mmethod) then
158 self.add_monomorphic_send(mtype, rss.mmethod)
159 end
160 end
161 end
162
163 # Add a send site to the pool
164 #
165 # If the send site is already live, then do nothing.
166 fun add_send(mtype: MClassType, mmethod: MMethod)
167 do
168 var rss = new RTASendSite(mmethod, mtype)
169 if self.live_send_sites.has(rss) then return
170
171 self.live_send_sites.add(rss)
172
173 for mtype2 in self.live_types do
174 if not mtype2.is_subtype(self.mainmodule, null, mtype) then continue
175 if mtype2.has_mproperty(self.mainmodule, mmethod) then
176 self.add_monomorphic_send(mtype2, mmethod)
177 end
178 end
179 end
180
181 # Add a monomoprhic send.
182 # The send site is not added to the pool.
183 # The method just determine the associated method definition then
184 # performs a static_call
185 fun add_monomorphic_send(mtype: MClassType, mmethod: MMethod)
186 do
187 assert self.live_types.has(mtype)
188 if not mtype.has_mproperty(self.mainmodule, mmethod) then return
189 var def = mmethod.lookup_first_definition(self.mainmodule, mtype)
190 self.add_static_call(mtype, def)
191 end
192
193 # Add a customized_methoddefs to the pool
194 # Is the customized_methoddefs is already live, then do nothing
195 fun add_static_call(mtype: MClassType, mmethoddef: MMethodDef)
196 do
197 assert self.live_types.has(mtype)
198 var rm = new CustomizedMethodDef(mmethoddef, mtype)
199 if self.live_customized_methoddefs.has(rm) then return
200 self.live_customized_methoddefs.add(rm)
201 self.todo.add(rm)
202 self.live_methoddefs.add(mmethoddef)
203 end
204
205 # Add mtype to the cast pool.
206 fun add_cast_type(mtype: MClassType)
207 do
208 if self.live_cast_types.has(mtype) then return
209
210 assert not mtype.need_anchor
211 self.live_cast_types.add(mtype)
212 self.check_depth(mtype)
213 end
214
215 fun check_depth(mtype: MClassType)
216 do
217 var d = mtype.depth
218 if d > 255 then
219 self.modelbuilder.toolcontext.fatal_error(null, "Fatal error: limitation in the rapidtype analysis engine: a type depth of {d} is too important, the problematic type is {mtype}.")
220 end
221 end
222
223 # Run the analysis until all visitable method definitions are visited.
224 fun run_analysis
225 do
226 # Force Bool
227 var classes = self.modelbuilder.model.get_mclasses_by_name("Bool")
228 if classes != null then for c in classes do self.add_type(c.mclass_type)
229
230 while not todo.is_empty do
231 var mr = todo.shift
232
233 var vararg_rank = mr.mmethoddef.msignature.vararg_rank
234 if vararg_rank > -1 then
235 var node = self.modelbuilder.mpropdef2npropdef[mr.mmethoddef]
236 var elttype = mr.mmethoddef.msignature.mparameters[vararg_rank].mtype
237 elttype = elttype.anchor_to(self.mainmodule, mr.receiver)
238 var vararg = self.mainmodule.get_primitive_class("Array").get_mtype([elttype])
239 self.add_type(vararg)
240 self.add_monomorphic_send(vararg, self.modelbuilder.force_get_primitive_method(node, "with_native", vararg, self.mainmodule))
241 var native = self.mainmodule.get_primitive_class("NativeArray").get_mtype([elttype])
242 self.add_type(native)
243 end
244
245 for i in [0..mr.mmethoddef.msignature.arity[ do
246 var origtype = mr.mmethoddef.mproperty.intro.msignature.mparameters[i].mtype
247 if not origtype.need_anchor then continue # skip non covariant stuff
248 var paramtype = mr.mmethoddef.msignature.mparameters[i].mtype
249 paramtype = self.cleanup_type(paramtype, mr.receiver).as(not null)
250 self.add_cast_type(paramtype)
251 end
252
253 if not self.modelbuilder.mpropdef2npropdef.has_key(mr.mmethoddef) then
254 # It is an init for a class?
255 if mr.mmethoddef.mproperty.name == "init" then
256 var nclassdef = self.modelbuilder.mclassdef2nclassdef[mr.mmethoddef.mclassdef]
257 var super_inits = nclassdef.super_inits
258 if super_inits != null then
259 #assert args.length == 1
260 for su in super_inits do
261 self.add_monomorphic_send(mr.receiver, su)
262 end
263 end
264
265 else
266 abort
267 end
268 continue
269 end
270 var npropdef = self.modelbuilder.mpropdef2npropdef[mr.mmethoddef]
271 if npropdef isa AConcreteMethPropdef then
272 #npropdef.debug("Visit {mr.mmethoddef} for {mr.receiver}")
273 var nclassdef = npropdef.parent.as(AClassdef)
274 var mmethoddef = npropdef.mpropdef.as(not null)
275 var auto_super_inits = npropdef.auto_super_inits
276 if auto_super_inits != null then
277 for auto_super_init in auto_super_inits do
278 self.add_monomorphic_send(mr.receiver, auto_super_init)
279 end
280 end
281 var v = new RapidTypeVisitor(self, nclassdef, mmethoddef, mr.receiver)
282 v.enter_visit(npropdef.n_block)
283 else if npropdef isa ADeferredMethPropdef then
284 # nothing to do (maybe add a waring?)
285 else if npropdef isa AAttrPropdef then
286 # nothing to do
287 else if npropdef isa AInternMethPropdef or npropdef isa AExternMethPropdef then
288 # UGLY: We force the "instantation" of the concrete return type if any
289 var ret = mr.mmethoddef.msignature.return_mtype
290 if ret != null and ret isa MClassType and ret.mclass.kind != abstract_kind and ret.mclass.kind != interface_kind then
291 ret = ret.anchor_to(self.mainmodule, mr.receiver)
292 self.add_type(ret)
293 end
294 else if npropdef isa AExternInitPropdef then
295 self.add_type(mr.receiver)
296 else
297 npropdef.debug("Not yet implemented")
298 abort
299 end
300 end
301 end
302 end
303
304 # A method definitions customized to a specific receiver
305 class CustomizedMethodDef
306 var mmethoddef: MMethodDef
307 var receiver: MClassType
308
309 redef fun to_s
310 do
311 return "{self.mmethoddef}({receiver})"
312 end
313
314 redef fun ==(o)
315 do
316 return o isa CustomizedMethodDef and o.mmethoddef == self.mmethoddef and o.receiver == self.receiver
317 end
318
319 redef fun hash
320 do
321 return self.mmethoddef.hash + self.receiver.hash
322 end
323 end
324
325 # A method invokation site bounded by a specific receiver
326 class RTASendSite
327 var mmethod: MMethod
328 var receiver: MClassType
329
330 redef fun ==(o)
331 do
332 return o isa RTASendSite and o.mmethod == self.mmethod and o.receiver == self.receiver
333 end
334
335 redef fun hash
336 do
337 return self.mmethod.hash + self.receiver.hash
338 end
339 end
340
341 private class RapidTypeVisitor
342 super Visitor
343
344 var analysis: RapidTypeAnalysis
345
346 var nclassdef: AClassdef
347
348 var mpropdef: MPropDef
349
350 var receiver: MClassType
351
352 init(analysis: RapidTypeAnalysis, nclassdef: AClassdef, mpropdef: MPropDef, receiver: MClassType)
353 do
354 self.analysis = analysis
355 self.nclassdef = nclassdef
356 self.mpropdef = mpropdef
357 self.receiver = receiver
358 end
359
360 # Adapt and remove nullable
361 # return null if we got the null type
362 fun cleanup_type(mtype: MType): nullable MClassType
363 do
364 mtype = mtype.anchor_to(self.analysis.mainmodule, self.receiver)
365 if mtype isa MNullType then return null
366 if mtype isa MNullableType then mtype = mtype.mtype
367 assert mtype isa MClassType
368 assert not mtype.need_anchor
369 return mtype
370 end
371
372 fun add_type(mtype: MType)
373 do
374 var rapidtype = cleanup_type(mtype)
375 if rapidtype == null then return # we do not care about null
376
377 self.analysis.add_type(rapidtype)
378 #self.current_node.debug("add_type {rapidtype}")
379 end
380
381 fun add_send(mtype: MType, mproperty: MMethod)
382 do
383 var rapidtype = cleanup_type(mtype)
384 if rapidtype == null then return # we do not care about null
385
386 analysis.add_send(rapidtype, mproperty)
387 #self.current_node.debug("add_send {mproperty}")
388 end
389
390 fun add_monomorphic_send(mtype: MType, mproperty: MMethod)
391 do
392 var rapidtype = cleanup_type(mtype)
393 if rapidtype == null then return # we do not care about null
394
395 self.analysis.add_monomorphic_send(rapidtype, mproperty)
396 #self.current_node.debug("add_monomorphic_send {rapidtype} {mproperty}")
397 end
398
399 fun add_cast_type(mtype: MType)
400 do
401 var rapidtype = cleanup_type(mtype)
402 if rapidtype == null then return # we do not care about null
403
404 self.analysis.add_cast_type(rapidtype)
405 #self.current_node.debug("add_cast_type {rapidtype}")
406 end
407
408 redef fun visit(node)
409 do
410 if node == null then return
411 node.accept_rapid_type_vistor(self)
412 if node isa AExpr then
413 var implicit_cast_to = node.implicit_cast_to
414 if implicit_cast_to != null then self.add_cast_type(implicit_cast_to)
415 end
416 # RTA does not enter in AAnnotations
417 if not node isa AAnnotations then
418 node.visit_all(self)
419 end
420 end
421
422 # Force to get the primitive class named `name' or abort
423 fun get_class(name: String): MClass
424 do
425 return self.analysis.mainmodule.get_primitive_class(name)
426 end
427
428 # Force to get the primitive property named `name' in the instance `recv' or abort
429 fun get_method(recv: MType, name: String): MMethod
430 do
431 var rapidtype = cleanup_type(recv)
432 if rapidtype == null then abort
433
434 return self.analysis.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, rapidtype, self.analysis.mainmodule)
435 end
436 end
437
438 ###
439
440 redef class ANode
441 private fun accept_rapid_type_vistor(v: RapidTypeVisitor)
442 do
443 end
444 end
445
446 redef class AIntExpr
447 redef fun accept_rapid_type_vistor(v)
448 do
449 v.add_type(self.mtype.as(not null))
450 end
451 end
452
453 redef class AFloatExpr
454 redef fun accept_rapid_type_vistor(v)
455 do
456 v.add_type(self.mtype.as(not null))
457 end
458 end
459
460 redef class ACharExpr
461 redef fun accept_rapid_type_vistor(v)
462 do
463 v.add_type(self.mtype.as(not null))
464 end
465 end
466
467 redef class AArrayExpr
468 redef fun accept_rapid_type_vistor(v)
469 do
470 var mtype = self.mtype.as(MClassType)
471 v.add_type(mtype)
472 var native = v.get_class("NativeArray").get_mtype([mtype.arguments.first])
473 v.add_type(native)
474 var prop = v.get_method(mtype, "with_native")
475 v.add_monomorphic_send(mtype, prop)
476 end
477 end
478
479 redef class AStringFormExpr
480 redef fun accept_rapid_type_vistor(v)
481 do
482 var mtype = self.mtype.as(not null)
483 v.add_type(mtype)
484 var native = v.get_class("NativeString").mclass_type
485 v.add_type(native)
486 var prop = v.get_method(mtype, "from_cstring")
487 v.add_monomorphic_send(mtype, prop)
488 end
489 end
490
491 redef class ASuperstringExpr
492 redef fun accept_rapid_type_vistor(v)
493 do
494 var arraytype = v.get_class("Array").get_mtype([v.get_class("Object").mclass_type])
495 v.add_type(arraytype)
496 v.add_type(v.get_class("NativeArray").get_mtype([v.get_class("Object").mclass_type]))
497 var prop = v.get_method(arraytype, "join")
498 v.add_monomorphic_send(arraytype, prop)
499 var prop2 = v.get_method(arraytype, "with_native")
500 v.add_monomorphic_send(arraytype, prop2)
501 end
502 end
503
504 redef class ACrangeExpr
505 redef fun accept_rapid_type_vistor(v)
506 do
507 var mtype = self.mtype.as(not null)
508 v.add_type(mtype)
509 var prop = v.get_method(mtype, "init")
510 v.add_monomorphic_send(mtype, prop)
511 end
512 end
513
514 redef class AOrangeExpr
515 redef fun accept_rapid_type_vistor(v)
516 do
517 var mtype = self.mtype.as(not null)
518 v.add_type(mtype)
519 var prop = v.get_method(mtype, "without_last")
520 v.add_monomorphic_send(mtype, prop)
521 end
522 end
523
524 redef class ATrueExpr
525 redef fun accept_rapid_type_vistor(v)
526 do
527 v.add_type(self.mtype.as(not null))
528 end
529 end
530
531 redef class AFalseExpr
532 redef fun accept_rapid_type_vistor(v)
533 do
534 v.add_type(self.mtype.as(not null))
535 end
536 end
537
538 redef class AIsaExpr
539 redef fun accept_rapid_type_vistor(v)
540 do
541 v.add_cast_type(self.cast_type.as(not null))
542 end
543 end
544
545 redef class AAsCastExpr
546 redef fun accept_rapid_type_vistor(v)
547 do
548 v.add_cast_type(self.mtype.as(not null))
549 end
550 end
551
552 #
553
554 redef class ASendExpr
555 redef fun accept_rapid_type_vistor(v)
556 do
557 var mproperty = self.mproperty.as(not null)
558 if n_expr isa ASelfExpr then
559 v.add_monomorphic_send(v.receiver, mproperty)
560 else
561 var recvtype = self.n_expr.mtype.as(not null)
562 v.add_send(recvtype, mproperty)
563 end
564 end
565 end
566
567 redef class ASendReassignFormExpr
568 redef fun accept_rapid_type_vistor(v)
569 do
570 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
571 var mproperty = self.mproperty.as(not null)
572 var write_mproperty = self.write_mproperty.as(not null)
573 if n_expr isa ASelfExpr then
574 v.add_monomorphic_send(v.receiver, mproperty)
575 v.add_monomorphic_send(v.receiver, write_mproperty)
576 else
577 var recvtype = self.n_expr.mtype.as(not null)
578 v.add_send(recvtype, mproperty)
579 v.add_send(recvtype, write_mproperty)
580 end
581 end
582 end
583
584 redef class AVarReassignExpr
585 redef fun accept_rapid_type_vistor(v)
586 do
587 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
588 end
589 end
590
591 redef class AAttrReassignExpr
592 redef fun accept_rapid_type_vistor(v)
593 do
594 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
595 end
596 end
597
598 redef class ASuperExpr
599 redef fun accept_rapid_type_vistor(v)
600 do
601 var mproperty = self.mproperty
602 if mproperty != null then
603 v.add_monomorphic_send(v.receiver, mproperty)
604 return
605 end
606
607 var mpropdef = v.mpropdef.lookup_next_definition(v.analysis.mainmodule, v.receiver)
608 assert mpropdef isa MMethodDef
609 v.analysis.add_static_call(v.receiver, mpropdef)
610 end
611 end
612
613 redef class AForExpr
614 redef fun accept_rapid_type_vistor(v)
615 do
616 var recvtype = self.n_expr.mtype.as(not null)
617 var colltype = self.coltype.as(not null)
618 var itmeth = v.get_method(colltype, "iterator")
619 v.add_send(recvtype, itmeth)
620 var iteratortype = itmeth.intro.msignature.return_mtype.as(MClassType).mclass.intro.bound_mtype
621 var objtype = v.get_class("Object").mclass_type
622 v.add_send(objtype, v.get_method(iteratortype, "is_ok"))
623 if self.variables.length == 1 then
624 v.add_send(objtype, v.get_method(iteratortype, "item"))
625 else if self.variables.length == 2 then
626 v.add_send(objtype, v.get_method(iteratortype, "key"))
627 v.add_send(objtype, v.get_method(iteratortype, "item"))
628 else
629 abort
630 end
631 v.add_send(objtype, v.get_method(iteratortype, "next"))
632 end
633 end
634
635 redef class ANewExpr
636 redef fun accept_rapid_type_vistor(v)
637 do
638 var recvtype = self.mtype.as(not null)
639 v.add_type(recvtype)
640 v.add_monomorphic_send(recvtype, mproperty.as(not null))
641 end
642 end