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..ae960b9 100644 --- a/out/semantics.py +++ b/out/semantics.py @@ -1,355 +1,524 @@ -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 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 = mro_lookup(x_type, "__add__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__and__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__or__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__floordiv__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__lshift__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__matmul__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__mod__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__mul__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__pow__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__rshift__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__sub__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__truediv__") + y_method = mro_lookup(y_type, "__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 = mro_lookup(x_type, "__xor__") + y_method = mro_lookup(y_type, "__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..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} @@ -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 = mro_lookup(x_type, "__{magic_method}__") + y_method = mro_lookup(y_type, "__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 2650d9e..e02746e 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): @@ -770,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") @@ -906,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) @@ -925,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()): @@ -1022,10 +1091,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): @@ -1576,6 +1645,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 @@ -1755,6 +1827,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: