Commit cc9c688a authored by Stefan Behnel's avatar Stefan Behnel

another rewrite to catch comparisons of invalid types

parent 8924c146
...@@ -5108,25 +5108,50 @@ class CmpNode(object): ...@@ -5108,25 +5108,50 @@ class CmpNode(object):
if type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or \ if type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or \
type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type)): type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type)):
error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3") error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
new_common_type = error_type
elif operand1.type.is_complex or operand2.type.is_complex: elif type1.is_complex or type2.is_complex:
if op not in ('==', '!='): if op not in ('==', '!='):
error(self.pos, "complex types unordered") error(self.pos, "complex types are unordered")
if operand1.type.is_pyobject: new_common_type = error_type
new_common_type = operand1.type if type1.is_pyobject:
elif operand2.type.is_pyobject: new_common_type = type1
new_common_type = operand2.type elif type2.is_pyobject:
new_common_type = type2
else: else:
new_common_type = PyrexTypes.widest_numeric_type(type1, type2) new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
elif type1.is_numeric and type2.is_numeric:
new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
elif common_type is None or not common_type.is_pyobject: elif common_type is None or not common_type.is_pyobject:
if not type1.is_int or not type2.is_int:
new_common_type = self.find_common_int_type(env, op, operand1, operand2) new_common_type = self.find_common_int_type(env, op, operand1, operand2)
if new_common_type is None: if new_common_type is None:
new_common_type = PyrexTypes.spanning_type(operand1.type, operand2.type) if type1 == type2 or type1.assignable_from(type2):
new_common_type = type1
elif type2.assignable_from(type1):
new_common_type = type2
elif type1.is_pyobject and type2.is_pyobject:
new_common_type = py_object_type
elif type1.is_pyobject or type2.is_pyobject:
if type2.is_numeric or type2.is_string:
if operand2.check_for_coercion_error(type1):
new_common_type = error_type
else:
new_common_type = type1
elif type1.is_numeric or type1.is_string:
if operand1.check_for_coercion_error(type2):
new_common_type = error_type
else:
new_common_type = type2
else:
# one Python type and one non-Python type, not assignable
self.invalid_types_error(operand1, op, operand2)
new_common_type = error_type
else:
# C types that we couldn't handle up to here are an error
self.invalid_types_error(operand1, op, operand2)
new_common_type = error_type
if common_type is None: if common_type is None or new_common_type.is_error:
common_type = new_common_type common_type = new_common_type
else: else:
# we could do a lot better by splitting the comparison # we could do a lot better by splitting the comparison
...@@ -5139,6 +5164,10 @@ class CmpNode(object): ...@@ -5139,6 +5164,10 @@ class CmpNode(object):
return common_type return common_type
def invalid_types_error(self, operand1, op, operand2):
error(self.pos, "Invalid types for '%s' (%s, %s)" %
(op, operand1.type, operand2.type))
def coerce_operands_to(self, dst_type, env): def coerce_operands_to(self, dst_type, env):
operand2 = self.operand2 operand2 = self.operand2
if operand2.type != dst_type: if operand2.type != dst_type:
...@@ -5146,39 +5175,11 @@ class CmpNode(object): ...@@ -5146,39 +5175,11 @@ class CmpNode(object):
if self.cascade: if self.cascade:
self.cascade.coerce_operands_to(dst_type, env) self.cascade.coerce_operands_to(dst_type, env)
def is_python_comparison(self):
return (self.has_python_operands()
or (self.cascade and self.cascade.is_python_comparison())
or self.operator in ('in', 'not_in'))
def is_python_result(self): def is_python_result(self):
return ((self.has_python_operands() and self.operator not in ('is', 'is_not', 'in', 'not_in')) return ((self.has_python_operands() and
self.operator not in ('is', 'is_not', 'in', 'not_in'))
or (self.cascade and self.cascade.is_python_result())) or (self.cascade and self.cascade.is_python_result()))
def check_types(self, env, operand1, op, operand2):
if not self.types_okay(operand1, op, operand2):
error(self.pos, "Invalid types for '%s' (%s, %s)" %
(self.operator, operand1.type, operand2.type))
def types_okay(self, operand1, op, operand2):
type1 = operand1.type
type2 = operand2.type
if type1.is_error or type2.is_error:
return 1
if type1.is_pyobject: # type2 will be, too
return 1
elif type1.is_ptr or type1.is_array:
return type1.is_null_ptr or type2.is_null_ptr \
or ((type2.is_ptr or type2.is_array)
and type1.base_type.same_as(type2.base_type))
elif ((type1.is_numeric and type2.is_numeric
or type1.is_enum and (type1 is type2 or type2.is_int)
or type1.is_int and type2.is_enum)
and op not in ('is', 'is_not')):
return 1
else:
return type1.is_cfunction and type1.is_cfunction and type1 == type2
def generate_operation_code(self, code, result_code, def generate_operation_code(self, code, result_code,
operand1, op , operand2): operand1, op , operand2):
if self.type is PyrexTypes.py_object_type: if self.type is PyrexTypes.py_object_type:
...@@ -5301,8 +5302,14 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -5301,8 +5302,14 @@ class PrimaryCmpNode(ExprNode, CmpNode):
if self.cascade: if self.cascade:
self.cascade.analyse_types(env) self.cascade.analyse_types(env)
if self.operator in ('in', 'not_in'):
common_type = py_object_type
self.is_pycmp = True
else:
common_type = self.find_common_type(env, self.operator, self.operand1) common_type = self.find_common_type(env, self.operator, self.operand1)
self.is_pycmp = common_type.is_pyobject self.is_pycmp = common_type.is_pyobject
if not common_type.is_error:
if self.operand1.type != common_type: if self.operand1.type != common_type:
self.operand1 = self.operand1.coerce_to(common_type, env) self.operand1 = self.operand1.coerce_to(common_type, env)
self.coerce_operands_to(common_type, env) self.coerce_operands_to(common_type, env)
...@@ -5310,7 +5317,6 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -5310,7 +5317,6 @@ class PrimaryCmpNode(ExprNode, CmpNode):
if self.cascade: if self.cascade:
self.operand2 = self.operand2.coerce_to_simple(env) self.operand2 = self.operand2.coerce_to_simple(env)
self.cascade.coerce_cascaded_operands_to_temp(env) self.cascade.coerce_cascaded_operands_to_temp(env)
self.check_operand_types(env)
if self.is_python_result(): if self.is_python_result():
self.type = PyrexTypes.py_object_type self.type = PyrexTypes.py_object_type
else: else:
...@@ -5322,22 +5328,10 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -5322,22 +5328,10 @@ class PrimaryCmpNode(ExprNode, CmpNode):
if self.is_pycmp or self.cascade: if self.is_pycmp or self.cascade:
self.is_temp = 1 self.is_temp = 1
def check_operand_types(self, env):
self.check_types(env,
self.operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.check_operand_types(env, self.operand2)
def has_python_operands(self): def has_python_operands(self):
return (self.operand1.type.is_pyobject return (self.operand1.type.is_pyobject
or self.operand2.type.is_pyobject) or self.operand2.type.is_pyobject)
def coerce_operands_to_pyobjects(self, env):
self.operand1 = self.operand1.coerce_to_pyobject(env)
self.operand2 = self.operand2.coerce_to_pyobject(env)
if self.cascade:
self.cascade.coerce_operands_to_pyobjects(env)
def check_const(self): def check_const(self):
self.operand1.check_const() self.operand1.check_const()
self.operand2.check_const() self.operand2.check_const()
...@@ -5422,12 +5416,6 @@ class CascadedCmpNode(Node, CmpNode): ...@@ -5422,12 +5416,6 @@ class CascadedCmpNode(Node, CmpNode):
if self.cascade: if self.cascade:
self.cascade.analyse_types(env) self.cascade.analyse_types(env)
def check_operand_types(self, env, operand1):
self.check_types(env,
operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.check_operand_types(env, self.operand2)
def has_python_operands(self): def has_python_operands(self):
return self.operand2.type.is_pyobject return self.operand2.type.is_pyobject
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment