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