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