PageRenderTime 415ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 1ms

/rpython/rtyper/test/test_normalizecalls.py

https://bitbucket.org/pypy/pypy/
Python | 368 lines | 329 code | 21 blank | 18 comment | 20 complexity | e397e0fd4cc5bd6aef46e77d49588e3c MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from rpython.annotator import model as annmodel
  3. from rpython.translator.translator import TranslationContext, graphof
  4. from rpython.rtyper.llinterp import LLInterpreter
  5. from rpython.rtyper.error import TyperError
  6. from rpython.rtyper.test.test_llinterp import interpret
  7. from rpython.rtyper.lltypesystem import lltype
  8. from rpython.rtyper.normalizecalls import TotalOrderSymbolic, MAX
  9. from rpython.rtyper.normalizecalls import TooLateForNewSubclass
  10. def test_TotalOrderSymbolic():
  11. lst = []
  12. t1 = TotalOrderSymbolic([3, 4], lst)
  13. t2 = TotalOrderSymbolic([3, 4, 2], lst)
  14. t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
  15. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  16. assert t1 < t2 < t3 < t4
  17. assert t1.value is t2.value is t3.value is t4.value is None
  18. assert 1 <= t3
  19. assert t3.value == 2
  20. assert t1 <= 5
  21. assert t1.value == 0
  22. def test_TotalOrderSymbolic_with_subclasses():
  23. lst = []
  24. t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
  25. t1 = TotalOrderSymbolic([3, 4], lst)
  26. t2 = TotalOrderSymbolic([3, 4, 2], lst)
  27. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  28. assert t1.number_with_subclasses()
  29. assert not t2.number_with_subclasses()
  30. assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
  31. #
  32. lst = []
  33. t1 = TotalOrderSymbolic([3, 4], lst)
  34. t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
  35. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  36. t2 = TotalOrderSymbolic([3, 4, 2], lst)
  37. assert not t2.number_with_subclasses()
  38. assert t1.number_with_subclasses()
  39. assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
  40. #
  41. lst = []
  42. t1 = TotalOrderSymbolic([3, 4], lst)
  43. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  44. assert not t1.number_with_subclasses()
  45. t2 = TotalOrderSymbolic([3, 4, 2], lst)
  46. t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
  47. py.test.raises(TooLateForNewSubclass, t2.compute_fn)
  48. #
  49. lst = []
  50. t1 = TotalOrderSymbolic([3, 4], lst)
  51. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  52. assert not t1.number_with_subclasses()
  53. t2 = TotalOrderSymbolic([1], lst)
  54. t3 = TotalOrderSymbolic([1, MAX], lst)
  55. assert [t.compute_fn() for t in [t2, t3, t1, t4]] == range(4)
  56. #
  57. lst = []
  58. t1 = TotalOrderSymbolic([3, 4], lst)
  59. t4 = TotalOrderSymbolic([3, 4, MAX], lst)
  60. assert not t1.number_with_subclasses()
  61. t2 = TotalOrderSymbolic([6], lst)
  62. t3 = TotalOrderSymbolic([6, MAX], lst)
  63. assert [t.compute_fn() for t in [t1, t4, t2, t3]] == range(4)
  64. # ____________________________________________________________
  65. class TestNormalize(object):
  66. def rtype(self, fn, argtypes, resulttype):
  67. t = TranslationContext()
  68. a = t.buildannotator()
  69. s = a.build_types(fn, argtypes)
  70. assert s == a.typeannotation(resulttype)
  71. typer = t.buildrtyper()
  72. typer.specialize()
  73. #t.view()
  74. t.checkgraphs()
  75. return t
  76. def test_normalize_f2_as_taking_string_argument(self):
  77. def f1(l1):
  78. pass
  79. def f2(l2):
  80. pass
  81. def g(n):
  82. if n > 0:
  83. f1("123")
  84. f = f1
  85. else:
  86. f2("b")
  87. f = f2
  88. f("a")
  89. # The call table looks like:
  90. #
  91. # FuncDesc(f1) FuncDesc(f2)
  92. # --------------------------------------------
  93. # line g+2: graph1
  94. # line g+5: graph2
  95. # line g+7: graph1 graph2
  96. #
  97. # But all lines get compressed to a single line.
  98. translator = self.rtype(g, [int], annmodel.s_None)
  99. f1graph = graphof(translator, f1)
  100. f2graph = graphof(translator, f2)
  101. s_l1 = translator.annotator.binding(f1graph.getargs()[0])
  102. s_l2 = translator.annotator.binding(f2graph.getargs()[0])
  103. assert s_l1.__class__ == annmodel.SomeString # and not SomeChar
  104. assert s_l2.__class__ == annmodel.SomeString # and not SomeChar
  105. #translator.view()
  106. def test_normalize_keyword_call(self):
  107. def f1(a, b):
  108. return (a, b, 0, 0)
  109. def f2(b, c=123, a=456, d=789):
  110. return (a, b, c, d)
  111. def g(n):
  112. if n > 0:
  113. f = f1
  114. else:
  115. f = f2
  116. f(a=5, b=6)
  117. translator = self.rtype(g, [int], annmodel.s_None)
  118. f1graph = graphof(translator, f1)
  119. f2graph = graphof(translator, f2)
  120. assert len(f1graph.getargs()) == 2
  121. assert len(f2graph.getargs()) == 2 # normalized to the common call pattern
  122. #translator.view()
  123. def test_normalize_returnvar(self):
  124. def add_one(n):
  125. return n+1
  126. def add_half(n):
  127. return n+0.5
  128. def dummyfn(n, i):
  129. if i == 1:
  130. adder = add_one
  131. else:
  132. adder = add_half
  133. return adder(n)
  134. res = interpret(dummyfn, [52, 1])
  135. assert type(res) is float and res == 53.0
  136. res = interpret(dummyfn, [7, 2])
  137. assert type(res) is float and res == 7.5
  138. def test_normalize_missing_return(self):
  139. def add_one(n):
  140. return n+1
  141. def oups(n):
  142. raise ValueError
  143. def dummyfn(n, i):
  144. if i == 1:
  145. adder = add_one
  146. else:
  147. adder = oups
  148. try:
  149. return adder(n)
  150. except ValueError:
  151. return -1
  152. translator = self.rtype(dummyfn, [int, int], int)
  153. add_one_graph = graphof(translator, add_one)
  154. oups_graph = graphof(translator, oups)
  155. assert add_one_graph.getreturnvar().concretetype == lltype.Signed
  156. assert oups_graph .getreturnvar().concretetype == lltype.Signed
  157. #translator.view()
  158. def test_normalize_abstract_method(self):
  159. class Base:
  160. def fn(self):
  161. raise NotImplementedError
  162. class Sub1(Base):
  163. def fn(self):
  164. return 1
  165. class Sub2(Base):
  166. def fn(self):
  167. return -2
  168. def dummyfn(n):
  169. if n == 1:
  170. x = Sub1()
  171. else:
  172. x = Sub2()
  173. return x.fn()
  174. translator = self.rtype(dummyfn, [int], int)
  175. base_graph = graphof(translator, Base.fn.im_func)
  176. sub1_graph = graphof(translator, Sub1.fn.im_func)
  177. sub2_graph = graphof(translator, Sub2.fn.im_func)
  178. assert base_graph.getreturnvar().concretetype == lltype.Signed
  179. assert sub1_graph.getreturnvar().concretetype == lltype.Signed
  180. assert sub2_graph.getreturnvar().concretetype == lltype.Signed
  181. llinterp = LLInterpreter(translator.rtyper)
  182. res = llinterp.eval_graph(graphof(translator, dummyfn), [1])
  183. assert res == 1
  184. res = llinterp.eval_graph(graphof(translator, dummyfn), [2])
  185. assert res == -2
  186. def test_methods_with_defaults(self):
  187. class Base:
  188. def fn(self):
  189. raise NotImplementedError
  190. class Sub1(Base):
  191. def fn(self, x=1):
  192. return 1 + x
  193. class Sub2(Base):
  194. def fn(self):
  195. return -2
  196. def otherfunc(x):
  197. return x.fn()
  198. def dummyfn(n):
  199. if n == 1:
  200. x = Sub1()
  201. n = x.fn(2)
  202. else:
  203. x = Sub2()
  204. return otherfunc(x) + x.fn()
  205. excinfo = py.test.raises(TyperError, "self.rtype(dummyfn, [int], int)")
  206. msg = """the following functions:
  207. .+Base.fn
  208. .+Sub1.fn
  209. .+Sub2.fn
  210. are called with inconsistent numbers of arguments
  211. \(and/or the argument names are different, which is not supported in this case\)
  212. sometimes with \d arguments, sometimes with \d
  213. the callers of these functions are:
  214. .+otherfunc
  215. .+dummyfn"""
  216. import re
  217. assert re.match(msg, excinfo.value.args[0])
  218. class PBase:
  219. def fn(self):
  220. raise NotImplementedError
  221. class PSub1(PBase):
  222. def fn(self):
  223. return 1
  224. class PSub2(PBase):
  225. def fn(self):
  226. return 2
  227. def prefn(n):
  228. if n == 1:
  229. x = PSub1()
  230. else:
  231. x = PSub2()
  232. return x.fn() * 100 + isinstance(x, PSub2)
  233. class TestNormalizeAfterTheFact(TestNormalize):
  234. def rtype(self, fn, argtypes, resulttype, checkfunction=None):
  235. t = TranslationContext()
  236. a = t.buildannotator()
  237. a.build_types(prefn, [int])
  238. typer = t.buildrtyper()
  239. typer.specialize()
  240. #t.view()
  241. s_result = a.typeannotation(resulttype)
  242. from rpython.rtyper import annlowlevel
  243. # annotate, normalize and rtype fn after the fact
  244. annhelper = annlowlevel.MixLevelHelperAnnotator(typer)
  245. graph = annhelper.getgraph(fn, [a.typeannotation(argtype) for argtype in argtypes],
  246. s_result)
  247. annhelper.finish()
  248. t.checkgraphs()
  249. if checkfunction is not None:
  250. checkfunction(t)
  251. # sanity check prefn
  252. llinterp = LLInterpreter(typer)
  253. res = llinterp.eval_graph(graphof(t, prefn), [1])
  254. assert res == 100
  255. res = llinterp.eval_graph(graphof(t, prefn), [2])
  256. assert res == 201
  257. return t
  258. def test_mix_after_recursion(self):
  259. def prefn(n):
  260. if n:
  261. return 2*prefn(n-1)
  262. else:
  263. return 1
  264. t = TranslationContext()
  265. a = t.buildannotator()
  266. a.build_types(prefn, [int])
  267. typer = t.buildrtyper()
  268. typer.specialize()
  269. #t.view()
  270. def f():
  271. return 1
  272. from rpython.rtyper import annlowlevel
  273. annhelper = annlowlevel.MixLevelHelperAnnotator(typer)
  274. graph = annhelper.getgraph(f, [], annmodel.SomeInteger())
  275. annhelper.finish()
  276. def test_add_more_subclasses(self):
  277. from rpython.rtyper import rclass
  278. from rpython.rtyper.rclass import ll_issubclass, CLASSTYPE
  279. class Sub3(PBase):
  280. def newmethod(self):
  281. return 3
  282. def dummyfn(n):
  283. x = Sub3()
  284. return x.newmethod()
  285. def checkfunction(translator):
  286. # make sure that there is a sensible comparison defined on the
  287. # symbolics
  288. bk = translator.annotator.bookkeeper
  289. rtyper = translator.rtyper
  290. base_classdef = bk.getuniqueclassdef(PBase)
  291. base_vtable = rclass.getclassrepr(rtyper, base_classdef).getruntime(CLASSTYPE)
  292. sub3_classdef = bk.getuniqueclassdef(Sub3)
  293. sub3_vtable = rclass.getclassrepr(rtyper, sub3_classdef).getruntime(CLASSTYPE)
  294. assert ll_issubclass(sub3_vtable, base_vtable)
  295. assert not ll_issubclass(base_vtable, sub3_vtable)
  296. translator = self.rtype(dummyfn, [int], int, checkfunction)
  297. base_graph = graphof(translator, PBase.fn.im_func)
  298. sub1_graph = graphof(translator, PSub1.fn.im_func)
  299. sub2_graph = graphof(translator, PSub2.fn.im_func)
  300. sub3_graph = graphof(translator, Sub3.fn.im_func)
  301. dummyfn_graph = graphof(translator, dummyfn)
  302. assert base_graph.getreturnvar().concretetype == lltype.Signed
  303. assert sub1_graph.getreturnvar().concretetype == lltype.Signed
  304. assert sub2_graph.getreturnvar().concretetype == lltype.Signed
  305. assert sub3_graph.getreturnvar().concretetype == lltype.Signed
  306. assert dummyfn_graph.getreturnvar().concretetype == lltype.Signed
  307. def test_call_memoized_function_with_defaults(self):
  308. class Freezing:
  309. def _freeze_(self):
  310. return True
  311. fr1 = Freezing(); fr1.x = 1
  312. fr2 = Freezing(); fr2.x = 2
  313. def getorbuild(key1, key2=fr2, flag3=True):
  314. return key1.x * 100 + key2.x * 10 + flag3
  315. getorbuild._annspecialcase_ = "specialize:memo"
  316. def f1(i):
  317. if i > 0:
  318. fr = fr1
  319. else:
  320. fr = fr2
  321. if i % 2:
  322. return getorbuild(fr)
  323. else:
  324. return getorbuild(fr, fr2, False)
  325. for i in [-7, -2, 100, 5]:
  326. res = interpret(f1, [i])
  327. assert res == f1(i)