/rpython/rtyper/test/test_normalizecalls.py

https://bitbucket.org/pypy/pypy/ · Python · 368 lines · 303 code · 41 blank · 24 comment · 31 complexity · e397e0fd4cc5bd6aef46e77d49588e3c MD5 · raw file

  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)