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