From 3db2ae3151d0f3d3bba578d5fba5738521091c5e Mon Sep 17 00:00:00 2001
From: omelancon
Date: Fri, 27 Oct 2023 13:36:43 +0100
Subject: [PATCH 1/3] improve dead code removal of evaluator
---
semPy/compute_behaviors.py | 69 ++++++++++++++++++++++++++++++++++----
1 file changed, 62 insertions(+), 7 deletions(-)
diff --git a/semPy/compute_behaviors.py b/semPy/compute_behaviors.py
index 2650d9e..df17671 100755
--- a/semPy/compute_behaviors.py
+++ b/semPy/compute_behaviors.py
@@ -217,6 +217,9 @@ def visit_PPYConditional(self, node):
def visit_PPYIntrinsic(self, node):
return node
+ def visit_PPYSingleton(self, node):
+ return node
+
class DuplicateBranchingRemover(ast.NodeTransformer):
# TODO this case is tricky...
@@ -248,6 +251,50 @@ def visit_If(self, node):
return node
+class DeadCodeRemover(ast.NodeTransformer):
+ def __init__(self, node):
+ self.node = node
+ self._memo = set()
+
+ def apply(self):
+ return self.visit(self.node)
+
+ def is_dead_end(self, stmt):
+ if isinstance(stmt, ast.Return) or isinstance(stmt, ast.Raise):
+ return True
+ elif stmt in self._memo:
+ return True
+ elif isinstance(stmt, ast.If):
+ if any(map(self.is_dead_end, stmt.body)) and any(map(self.is_dead_end, stmt.orelse)):
+ self._memo.add(stmt)
+ return True
+ else:
+ return False
+
+ def remove_body_dead_code(self, body):
+ new_body = []
+ for stmt in body:
+ new_body.append(stmt)
+ if self.is_dead_end(stmt):
+ break
+ body[:] = new_body
+
+ def remove_stmt_dead_code(self, node, fields=('body',)):
+ for field in fields:
+ self.remove_body_dead_code(getattr(node, field))
+
+ def visit_FunctionDef(self, node):
+ node = self.generic_visit(node)
+ self.remove_stmt_dead_code(node)
+ return node
+
+ def visit_If(self, node):
+ node = self.generic_visit(node)
+ self.remove_stmt_dead_code(node, fields=('body', 'orelse'))
+ return node
+
+
+
class AssignmentRemover(PPYNodeTransformer):
def __init__(self, fn_or_body):
self.fn_or_body = fn_or_body
@@ -266,23 +313,24 @@ def apply(self):
# assignments which can be fully removed as they were inlined
to_remove = self.to_remove
-
for name, values in self.assignments.items():
if ref_counter[name] == 0 and len(values) == 1:
to_remove.add(name)
# assignments which can be inlined as the variable is single use
- to_inline = self.to_inline
for name, count in ref_counter.items():
if count == 1:
name_assignments = assignments[name]
if len(name_assignments) == 1:
- to_inline[name] = name_assignments[0].value
+ self.to_inline[name] = name_assignments[0].value
new_body = [self.visit(s) for s in body]
body[:] = [s for s in new_body if s is not None]
def visit_Assign(self, node):
+ if node.value is ppy_NotImplemented:
+ return None # We know that semantically, all assignment to NotImplemented can be removed
+
targets = node.targets
new_targets = [t for t in targets if not isinstance(t, ast.Name) or t.id not in self.to_remove]
@@ -318,6 +366,9 @@ def visit_If(self, node):
return res
+ def visit_PPYSingleton(self, node):
+ return node
+
def visit_PPYValue(self, node):
return node
@@ -360,6 +411,9 @@ def visit_PPYConditional(self, node):
def visit_PPYClass(self, node):
return ast.Name(node.name, ast.Load())
+ def visit_PPYSingleton(self, node):
+ raise NotImplementedError # that should not happen unless we add singletons other than NotImplemented!
+
def visit_Return(self, node):
value = node.value
if isinstance(value, PPYConditional):
@@ -1022,10 +1076,10 @@ def builtin_eval_issubclass_call(args, rte):
def partial_eval_stmts(stmts, rte):
for stmt in stmts:
- yield from partial_eval_stmt(stmt, rte)
-
- if isinstance(stmt, ast.Return) or isinstance(stmt, ast.Raise):
- break
+ for evaluated_stmt in partial_eval_stmt(stmt, rte):
+ yield evaluated_stmt
+ if isinstance(evaluated_stmt, ast.Return) or isinstance(evaluated_stmt, ast.Raise):
+ break
def partial_eval_stmt(stmt, rte):
@@ -1755,6 +1809,7 @@ def compute_single_behavior(behavior_name, module_name, callee_name, types, as_t
remove_extra_pass(behavior)
AssignmentRemover(behavior).apply()
DuplicateBranchingRemover(behavior).apply()
+ DeadCodeRemover(behavior).apply()
PPYValuesReplacer(behavior).apply()
if as_test:
From e3ebcb94c0e55ef02871f8b4b920af5fa892d7bc Mon Sep 17 00:00:00 2001
From: omelancon
Date: Fri, 27 Oct 2023 14:00:25 +0100
Subject: [PATCH 2/3] fix missing subtype check in arithmetic semantics
---
out/builtins.py | 4 +-
out/semantics.py | 559 +++++++++++++++++++----------
semPy/code_generation/templates.py | 45 ++-
semPy/compute_behaviors.py | 3 +
4 files changed, 398 insertions(+), 213 deletions(-)
diff --git a/out/builtins.py b/out/builtins.py
index 938d809..75e4883 100644
--- a/out/builtins.py
+++ b/out/builtins.py
@@ -332,7 +332,7 @@ def __rlshift__(self, other):
if isinstance(self, int):
if isinstance(other, int):
if py_int_to_host(self) >= py_int_to_host(0):
- return py_int_from_host(py_int_to_host(self) << py_int_to_host(other))
+ return py_int_from_host(py_int_to_host(other) << py_int_to_host(self))
else:
raise ValueError("negative shift count")
else:
@@ -368,7 +368,7 @@ def __rrshift__(self, other):
if isinstance(self, int):
if isinstance(other, int):
if py_int_to_host(self) >= py_int_to_host(0):
- return py_int_from_host(py_int_to_host(self) >> py_int_to_host(other))
+ return py_int_from_host(py_int_to_host(other) >> py_int_to_host(self))
else:
raise ValueError("negative shift count")
else:
diff --git a/out/semantics.py b/out/semantics.py
index 47507ba..7947d56 100644
--- a/out/semantics.py
+++ b/out/semantics.py
@@ -4,352 +4,521 @@
@define_semantics
def add(x, y):
def normal():
- magic_method = class_getattr(x, "__add__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__radd__")
- if magic_method is absent:
- return raise_binary_TypeError("+", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__add__")
+ y_method = class_getattr(y, "__radd__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("+", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("+", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("+", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def bitand(x, y):
def normal():
- magic_method = class_getattr(x, "__and__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rand__")
- if magic_method is absent:
- return raise_binary_TypeError("&", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__and__")
+ y_method = class_getattr(y, "__rand__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("&", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("&", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("&", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def bitor(x, y):
def normal():
- magic_method = class_getattr(x, "__or__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__ror__")
- if magic_method is absent:
- return raise_binary_TypeError("|", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__or__")
+ y_method = class_getattr(y, "__ror__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("|", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("|", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("|", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def floordiv(x, y):
def normal():
- magic_method = class_getattr(x, "__floordiv__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rfloordiv__")
- if magic_method is absent:
- return raise_binary_TypeError("//", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__floordiv__")
+ y_method = class_getattr(y, "__rfloordiv__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("//", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("//", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("//", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def lshift(x, y):
def normal():
- magic_method = class_getattr(x, "__lshift__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rlshift__")
- if magic_method is absent:
- return raise_binary_TypeError("<<", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__lshift__")
+ y_method = class_getattr(y, "__rlshift__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("<<", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("<<", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("<<", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def matmul(x, y):
def normal():
- magic_method = class_getattr(x, "__matmul__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rmatmul__")
- if magic_method is absent:
- return raise_binary_TypeError("@", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__matmul__")
+ y_method = class_getattr(y, "__rmatmul__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("@", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("@", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("@", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def mod(x, y):
def normal():
- magic_method = class_getattr(x, "__mod__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rmod__")
- if magic_method is absent:
- return raise_binary_TypeError("%", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__mod__")
+ y_method = class_getattr(y, "__rmod__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("%", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("%", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("%", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def mul(x, y):
def normal():
- magic_method = class_getattr(x, "__mul__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rmul__")
- if magic_method is absent:
- return raise_binary_TypeError("*", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__mul__")
+ y_method = class_getattr(y, "__rmul__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("*", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("*", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("*", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def pow(x, y):
def normal():
- magic_method = class_getattr(x, "__pow__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rpow__")
- if magic_method is absent:
- return raise_binary_TypeError("**", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__pow__")
+ y_method = class_getattr(y, "__rpow__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("**", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("**", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("**", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def rshift(x, y):
def normal():
- magic_method = class_getattr(x, "__rshift__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rrshift__")
- if magic_method is absent:
- return raise_binary_TypeError(">>", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__rshift__")
+ y_method = class_getattr(y, "__rrshift__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError(">>", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError(">>", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError(">>", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def sub(x, y):
def normal():
- magic_method = class_getattr(x, "__sub__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rsub__")
- if magic_method is absent:
- return raise_binary_TypeError("-", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__sub__")
+ y_method = class_getattr(y, "__rsub__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("-", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("-", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("-", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def truediv(x, y):
def normal():
- magic_method = class_getattr(x, "__truediv__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rtruediv__")
- if magic_method is absent:
- return raise_binary_TypeError("/", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__truediv__")
+ y_method = class_getattr(y, "__rtruediv__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("/", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("/", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("/", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
def xor(x, y):
def normal():
- magic_method = class_getattr(x, "__xor__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__rxor__")
- if magic_method is absent:
- return raise_binary_TypeError("^", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__xor__")
+ y_method = class_getattr(y, "__rxor__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("^", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("^", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("^", x, y)
- else:
- return result
- return normal()
+ return result
# Auto-generated
@define_semantics
diff --git a/semPy/code_generation/templates.py b/semPy/code_generation/templates.py
index 2c171bf..aa50ad4 100644
--- a/semPy/code_generation/templates.py
+++ b/semPy/code_generation/templates.py
@@ -256,28 +256,41 @@ def descriptor_get(attr, instance, owner):
@define_semantics
def {op}(x, y):
def normal():
- magic_method = class_getattr(x, "__{magic_method}__")
- if magic_method is absent:
- return reflected()
+ if x_method is absent:
+ return NotImplemented
else:
- result = magic_method(x, y)
- if result is NotImplemented:
- return reflected()
- else:
- return result
+ return x_method(x, y)
def reflected():
- magic_method = class_getattr(y, "__r{magic_method}__")
- if magic_method is absent:
- return raise_binary_TypeError("{symbol}", x, y)
+ if y_method is absent:
+ return NotImplemented
else:
- result = magic_method(y, x)
+ return y_method(y, x)
+
+ x_type = type(x)
+ y_type = type(y)
+
+ x_method = class_getattr(x, "__{magic_method}__")
+ y_method = class_getattr(y, "__r{magic_method}__")
+
+ if x_type is y_type:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("{symbol}", x, y)
+ elif issubclass(x_type, y_type) and x_method is not y_method:
+ result = reflected()
+ if result is NotImplemented:
+ result = normal()
+ if result is NotImplemented:
+ return raise_binary_TypeError("{symbol}", x, y)
+ else:
+ result = normal()
+ if result is NotImplemented:
+ result = reflected()
if result is NotImplemented:
return raise_binary_TypeError("{symbol}", x, y)
- else:
- return result
- return normal()"""
+ return result"""
arithmetic_info = {'add': {"op": "add", "magic_method": "add", "symbol": "+"},
'bitand': {"op": "bitand", "magic_method": "and", "symbol": "&"},
@@ -500,7 +513,7 @@ def __r{method_name}__(self, other):
if isinstance(self, int):
if isinstance(other, int):
if py_int_to_host(self) >= py_int_to_host(0):
- return py_int_from_host(py_int_to_host(self) {op} py_int_to_host(other))
+ return py_int_from_host(py_int_to_host(other) {op} py_int_to_host(self))
else:
raise ValueError("negative shift count")
else:
diff --git a/semPy/compute_behaviors.py b/semPy/compute_behaviors.py
index df17671..8ae73b0 100755
--- a/semPy/compute_behaviors.py
+++ b/semPy/compute_behaviors.py
@@ -1630,6 +1630,9 @@ def partial_eval_is(left, right, rte):
if isinstance(left.origin, ast.Constant) and isinstance(right.origin, ast.Constant):
return ast.Constant(left.value.value is right.value.value)
+ if isinstance(left, PPYFunction) and isinstance(right, PPYFunction):
+ return ast.Constant(left.origin is right.origin)
+
# Unresolved
return None
From 21730f605ea83609e5dbf184e77d2331d23debba Mon Sep 17 00:00:00 2001
From: omelancon
Date: Fri, 27 Oct 2023 14:52:01 +0100
Subject: [PATCH 3/3] implement the 'mro_lookup' primitive in the evaluator
---
out/semantics.py | 54 +++++++++++++++---------------
semPy/code_generation/templates.py | 6 ++--
semPy/compute_behaviors.py | 39 ++++++++++++++-------
3 files changed, 57 insertions(+), 42 deletions(-)
diff --git a/out/semantics.py b/out/semantics.py
index 7947d56..ae960b9 100644
--- a/out/semantics.py
+++ b/out/semantics.py
@@ -1,4 +1,4 @@
-from __compiler_intrinsics__ import define_semantics, class_getattr, py_bool_to_host_bool, absent, sint, bint
+from __compiler_intrinsics__ import define_semantics, class_getattr, mro_lookup, py_bool_to_host_bool, absent, sint, bint
# Auto-generated
@define_semantics
@@ -18,8 +18,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__add__")
- y_method = class_getattr(y, "__radd__")
+ x_method = mro_lookup(x_type, "__add__")
+ y_method = mro_lookup(y_type, "__radd__")
if x_type is y_type:
result = normal()
@@ -58,8 +58,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__and__")
- y_method = class_getattr(y, "__rand__")
+ x_method = mro_lookup(x_type, "__and__")
+ y_method = mro_lookup(y_type, "__rand__")
if x_type is y_type:
result = normal()
@@ -98,8 +98,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__or__")
- y_method = class_getattr(y, "__ror__")
+ x_method = mro_lookup(x_type, "__or__")
+ y_method = mro_lookup(y_type, "__ror__")
if x_type is y_type:
result = normal()
@@ -138,8 +138,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__floordiv__")
- y_method = class_getattr(y, "__rfloordiv__")
+ x_method = mro_lookup(x_type, "__floordiv__")
+ y_method = mro_lookup(y_type, "__rfloordiv__")
if x_type is y_type:
result = normal()
@@ -178,8 +178,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__lshift__")
- y_method = class_getattr(y, "__rlshift__")
+ x_method = mro_lookup(x_type, "__lshift__")
+ y_method = mro_lookup(y_type, "__rlshift__")
if x_type is y_type:
result = normal()
@@ -218,8 +218,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__matmul__")
- y_method = class_getattr(y, "__rmatmul__")
+ x_method = mro_lookup(x_type, "__matmul__")
+ y_method = mro_lookup(y_type, "__rmatmul__")
if x_type is y_type:
result = normal()
@@ -258,8 +258,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__mod__")
- y_method = class_getattr(y, "__rmod__")
+ x_method = mro_lookup(x_type, "__mod__")
+ y_method = mro_lookup(y_type, "__rmod__")
if x_type is y_type:
result = normal()
@@ -298,8 +298,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__mul__")
- y_method = class_getattr(y, "__rmul__")
+ x_method = mro_lookup(x_type, "__mul__")
+ y_method = mro_lookup(y_type, "__rmul__")
if x_type is y_type:
result = normal()
@@ -338,8 +338,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__pow__")
- y_method = class_getattr(y, "__rpow__")
+ x_method = mro_lookup(x_type, "__pow__")
+ y_method = mro_lookup(y_type, "__rpow__")
if x_type is y_type:
result = normal()
@@ -378,8 +378,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__rshift__")
- y_method = class_getattr(y, "__rrshift__")
+ x_method = mro_lookup(x_type, "__rshift__")
+ y_method = mro_lookup(y_type, "__rrshift__")
if x_type is y_type:
result = normal()
@@ -418,8 +418,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__sub__")
- y_method = class_getattr(y, "__rsub__")
+ x_method = mro_lookup(x_type, "__sub__")
+ y_method = mro_lookup(y_type, "__rsub__")
if x_type is y_type:
result = normal()
@@ -458,8 +458,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__truediv__")
- y_method = class_getattr(y, "__rtruediv__")
+ x_method = mro_lookup(x_type, "__truediv__")
+ y_method = mro_lookup(y_type, "__rtruediv__")
if x_type is y_type:
result = normal()
@@ -498,8 +498,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__xor__")
- y_method = class_getattr(y, "__rxor__")
+ x_method = mro_lookup(x_type, "__xor__")
+ y_method = mro_lookup(y_type, "__rxor__")
if x_type is y_type:
result = normal()
diff --git a/semPy/code_generation/templates.py b/semPy/code_generation/templates.py
index aa50ad4..a1d6db2 100644
--- a/semPy/code_generation/templates.py
+++ b/semPy/code_generation/templates.py
@@ -5,7 +5,7 @@
# ======================================================================================================================
semantics_template = \
- """from __compiler_intrinsics__ import define_semantics, class_getattr, py_bool_to_host_bool, absent, sint, bint
+ """from __compiler_intrinsics__ import define_semantics, class_getattr, mro_lookup, py_bool_to_host_bool, absent, sint, bint
{arithmetic}
@@ -270,8 +270,8 @@ def reflected():
x_type = type(x)
y_type = type(y)
- x_method = class_getattr(x, "__{magic_method}__")
- y_method = class_getattr(y, "__r{magic_method}__")
+ x_method = mro_lookup(x_type, "__{magic_method}__")
+ y_method = mro_lookup(y_type, "__r{magic_method}__")
if x_type is y_type:
result = normal()
diff --git a/semPy/compute_behaviors.py b/semPy/compute_behaviors.py
index 8ae73b0..e02746e 100755
--- a/semPy/compute_behaviors.py
+++ b/semPy/compute_behaviors.py
@@ -824,6 +824,7 @@ def merge(self, condition, then_rte, orelse_rte):
ppy_intrinsic_sint = PPYIntrinsic("sint")
ppy_intrinsic_bint = PPYIntrinsic("bint")
ppy_intrinsic_class_getattr = PPYIntrinsic("class_getattr")
+ppy_intrinsic_mro_lookup = PPYIntrinsic("mro_lookup")
ppy_intrinsic_define_semantics = PPYIntrinsic("define_semantics")
ppy_intrinsic_builtin = PPYIntrinsic("builtin")
ppy_intrinsic_private = PPYIntrinsic("private")
@@ -960,6 +961,8 @@ def simplify_intrinsic_call(fn, args, keywords, rte):
def partial_eval_intrinsic_call(fn, args, keywords, rte, return_kind=ReturnKind.as_return()):
if fn is ppy_intrinsic_class_getattr:
yield return_kind.do_return(intrinsic_eval_class_getattr_call(args, rte))
+ elif fn is ppy_intrinsic_mro_lookup:
+ yield return_kind.do_return(intrinsic_eval_mro_lookup_call(args, rte))
elif isinstance(fn, PPYIntrinsic):
# Cannot evaluate but can simplify
value = simplify_intrinsic_call(fn, args, keywords, rte)
@@ -979,27 +982,39 @@ def intrinsic_eval_class_getattr_call(args, rte):
if isinstance(obj, PPYValue):
if isinstance(attr, ast.Constant) and isinstance(attr.value, str):
- type_ = rte.modules["builtins"]["int"] if obj.type in (ppy_intrinsic_sint, ppy_intrinsic_bint) else obj.type
+ type_ = obj.type
if type_ is None:
raise NotImplementedError(f"partial_eval_class_getattr_call: could not resolve type")
- else:
- attr_name = attr.value
-
- for ppy_class in type_.mro:
- attr = ppy_class.get(attr_name)
- if attr is not None:
- return attr
-
- return ppy_intrinsic_absent
- else:
- raise ValueError("partial_eval_class_getattr_call: second argument must be a string literal")
+ return intrinsic_eval_mro_lookup_call([type_, attr], rte)
else:
raise NotImplementedError(
"partial_eval_class_getattr_call: first argument must have been resolved to a ppy object")
+def intrinsic_eval_mro_lookup_call(args, rte):
+ type_ = args[0]
+ attr = args[1]
+
+ type_ = rte.modules["builtins"]["int"] if type_ in (ppy_intrinsic_sint, ppy_intrinsic_bint) else type_
+
+ if not isinstance(type_, PPYClass):
+ raise TypeError('intrinsic_eval_mro_lookup_call: first argument is not a type')
+
+ if not isinstance(attr, ast.Constant) or not isinstance(attr.value, str):
+ raise TypeError("intrinsic_eval_mro_lookup_call: second argument must be a string literal")
+
+ attr_name = attr.value
+
+ for ppy_class in type_.mro:
+ attr = ppy_class.get(attr_name)
+
+ if attr is not None:
+ return attr
+
+ return ppy_intrinsic_absent
+
# End of intrinsic calls
def partial_eval_builtin_call(fn, args, rte, return_kind=ReturnKind.as_return()):