summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2023-01-05 15:26:23 +0100
committerStefan Behnel <stefan_ml@behnel.de>2023-01-05 16:00:15 +0100
commitcf19b86385cf9aee013ffc70443b8f40952708df (patch)
tree0727e5da687513e7e2f72398b0cbb4301621632e
parent2cff1bfc9151f770196b33c5ae0a27a1445110f4 (diff)
downloadcython-cf19b86385cf9aee013ffc70443b8f40952708df.tar.gz
Avoid exponential recursion when coercing nested conditional expressions.
This used to coerce the nesting tree twice at each condition, once for `coerce_to()` and once for `analyse_result_type()`, both calling each other for the entire subtree. Closes https://github.com/cython/cython/issues/5197
-rw-r--r--Cython/Compiler/ExprNodes.py28
-rw-r--r--tests/run/if_else_expr.pyx46
2 files changed, 65 insertions, 9 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index 746e1772b..305eae9eb 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -12118,30 +12118,40 @@ class CondExprNode(ExprNode):
return self.analyse_result_type(env)
def analyse_result_type(self, env):
- self.type = PyrexTypes.independent_spanning_type(
- self.true_val.type, self.false_val.type)
+ true_val_type = self.true_val.type
+ false_val_type = self.false_val.type
+ self.type = PyrexTypes.independent_spanning_type(true_val_type, false_val_type)
+
if self.type.is_reference:
self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
if self.type.is_pyobject:
self.result_ctype = py_object_type
elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
- if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
- self.true_val = self.true_val.coerce_to(self.type, env)
- self.false_val = self.false_val.coerce_to(self.type, env)
+
+ if true_val_type.is_pyobject or false_val_type.is_pyobject:
+ if true_val_type != self.type:
+ self.true_val = self.true_val.coerce_to(self.type, env)
+ if false_val_type != self.type:
+ self.false_val = self.false_val.coerce_to(self.type, env)
+
if self.type.is_error:
self.type_error()
return self
def coerce_to_integer(self, env):
- self.true_val = self.true_val.coerce_to_integer(env)
- self.false_val = self.false_val.coerce_to_integer(env)
+ if not self.true_val.type.is_int:
+ self.true_val = self.true_val.coerce_to_integer(env)
+ if not self.false_val.type.is_int:
+ self.false_val = self.false_val.coerce_to_integer(env)
self.result_ctype = None
return self.analyse_result_type(env)
def coerce_to(self, dst_type, env):
- self.true_val = self.true_val.coerce_to(dst_type, env)
- self.false_val = self.false_val.coerce_to(dst_type, env)
+ if self.true_val.type != dst_type:
+ self.true_val = self.true_val.coerce_to(dst_type, env)
+ if self.false_val.type != dst_type:
+ self.false_val = self.false_val.coerce_to(dst_type, env)
self.result_ctype = None
return self.analyse_result_type(env)
diff --git a/tests/run/if_else_expr.pyx b/tests/run/if_else_expr.pyx
index 41657ec5a..1d5143f22 100644
--- a/tests/run/if_else_expr.pyx
+++ b/tests/run/if_else_expr.pyx
@@ -1,5 +1,6 @@
# mode: run
# tag: condexpr
+# ticket: 5197
cimport cython
@@ -67,3 +68,48 @@ def test_cfunc_ptrs(double x, bint round_down):
3.0
"""
return (math.floor if round_down else math.ceil)(x)
+
+
+def performance_gh5197(patternsList):
+ """
+ >>> performance_gh5197([]) # do not actually run anything, just see that things work at all
+ """
+ # Coercing the types in nested conditional expressions used to slow down exponentially.
+ # See https://github.com/cython/cython/issues/5197
+ import re
+ matched=[]
+ for _ in range(len(patternsList)):
+ try:
+ matched.append(patternsList[_].split('|')[-1].split('/')[-1] + 'pattr1' if re.search('^SomeString.*EndIng$')\
+ else patternsList[_].split('|a')[-1].split('/a')[-1] + 'pattr2' if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|a')[-1].split('/a')[-1] + 'pattr2' if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ # else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] if re.search('^SomeOtherString.?Number.*EndIng$')\
+ else patternsList[_].split('|b')[-1].split('/b')[-1] + 'pattr2' + patternsList[_].split('/')[-1].split('//')[-1] )
+ except Exception as e:
+ matched.append('Error at Indx:%s-%s' %(_, patternsList[_]))