ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
preprocessor.class_context_mixin.ClassContextMixin Class Reference
Inheritance diagram for preprocessor.class_context_mixin.ClassContextMixin:
[legend]

Public Member Functions

 hoist_nested_classes (self, module)
 copy_aliased_methods (self, module)
 visit_ClassDef (self, node)
 class_scope_default (self, default_node, method)
 names_bound_in (self, tree)

Public Attributes

 hoisted_classes = renames
 body
 name
 current_class_name = node.name
 is_entry_module
 blocked_type_functions
dict fixed_receivers
dict once_bound_names = {name for name, count in counts.items() if count == 1}
dict module_statements = {id(stmt): stmt for stmt in module.body}
 class_attr_annotations

Protected Member Functions

 _hoist_from_class (self, cls, taken, renames)
 _can_hoist (self, cls, index, taken)
 _aliased_method (self, cls, index)
 _copy_is_exact (self, cls, method, alias)
 _class_body_binds (self, stmt, name)
 _binds_name (self, tree, name, skip=None)
 _cls_constructor_calls (self, member)
 _rebind_cls_calls (self, method, class_name)
 _method_owner (self, classes, node, name)
 _unbindable_classes (self, classes)
 _classmethod_heirs (self, classes, owner, name)
 _can_bind (self, member, targets)
 _bind_classmethod_constructors (self, module)
 _self_called_methods (self, member)
 _bind_overridden_method_calls (self, module)
 _receiver (self, method)
 _inherited_nested_classes (self, classes, cls, excluded)
 _escaped_classes (self, module, classes)
 _inherits_from (self, classes, cls, names)
 _bind_nested_classes_via_self (self, module)
 _calls_unresolved_attribute (self, classes, cls, method)
 _heirs_needing_copy (self, classes, owner, member)
 _runs_only_on_subclasses (self, module, classes, owner, member)
 _copy_methods_into_heirs (self, module)
 _bind_class_alias_calls (self, module)
 _bind_type_values (self, module)
 _unroll_class_tuples (self, module, known, counts)
 _hoist_lambda_arguments (self, module, counts)
 _lower_called_lambdas (self, module, known, counts)
 _specialise_one_function (self, module, known, counts)
 _specialise_requested_function (self, module, known, counts)
 _bind_local_type_values (self, module, known)
 _type_parameters (self, func, known, calls)
 _type_copy (self, func, name, bindings)
 _specialise_type_parameters (self, module, func, known, calls, taken)
 _scan_fixed_receivers (self, module)
 _record_exit_suppresses_all (self, class_node)
 _record_class_with_exit (self, class_node)
 _class_inherits_exit_handler (self, class_node)
 _collect_class_attr_annotations (self, class_node)

Static Protected Member Functions

 _names_bound_by (node)
 _class_ancestry (classes, node)
 _class_defines (node, name)
 _indirect_attributes (module, classes)
 _top_level_classes (module)
 _names_mentioned (module)
 _recovers_classes (module)
 _is_name_copy (node)
 _module_level_nodes (module)
 _fold_class_generator (node, classes, copies, counts)
 _class_generator (node, counts)
 _known_classes (module, counts)
 _typing_any (module)
 _is_plain_function (stmt)
 _lambda_as_def (stmt, counts)
 _called_parameters (func)
 _module_level_calls (node)
 _replace_reads (nodes, name, value)
 _is_type_of_none (arg, known)
 _class_argument (call, func, index, known)
 _class_value (node, known)
 _class_tuple (node, known)
 _class_name (node, known)
 _drop_tuple_arguments (call, func, indexes, key)
 _construction (stmt)
 _assigned_attributes (module)
 _variable_names (module)
 _scope_binding_counts (module)
 _function_may_return_truthy (func_node)
 _return_may_be_truthy (value)

Protected Attributes

 _pending_method_default_inits
 _class_stack
 _exit_suppresses_all = set()
 _classes_with_exit = set()

Member Function Documentation

◆ _aliased_method()

preprocessor.class_context_mixin.ClassContextMixin._aliased_method ( self,
cls,
index )
protected
The def that `cls.body[index]` aliases, when a renamed copy of it is
exact, else None.

◆ _assigned_attributes()

preprocessor.class_context_mixin.ClassContextMixin._assigned_attributes ( module)
staticprotected
The attribute names `module` assigns or deletes anywhere.

◆ _bind_class_alias_calls()

preprocessor.class_context_mixin.ClassContextMixin._bind_class_alias_calls ( self,
module )
protected
Rewrite `alias(...)` to `Class(...)` where a top-level `alias = Class`
statement is the only binding of `alias`, to a top-level class the
module never rebinds: the converter recognises a constructor call only
by the class's own name (#8352). A module-level call before the binding
stays as written, since CPython raises NameError there.

◆ _bind_classmethod_constructors()

preprocessor.class_context_mixin.ClassContextMixin._bind_classmethod_constructors ( self,
module )
protected
Rewrite `cls(...)` in a @classmethod to the class the method runs on.

Only in the entry module, and only when that class is known statically: every
reference to the method is a direct `Class.method(...)` call and the classes
involved use single inheritance. A subclass inheriting the method gets its own
copy. Anything else keeps `cls(...)`, which the converter rejects.

◆ _bind_local_type_values()

preprocessor.class_context_mixin.ClassContextMixin._bind_local_type_values ( self,
module,
known )
protected

◆ _bind_nested_classes_via_self()

preprocessor.class_context_mixin.ClassContextMixin._bind_nested_classes_via_self ( self,
module )
protected
Rewrite `self.Inner` in a method to the class hoisted out of the
method's class or one of its ancestors (#8357).

Only in the entry module, where every subclass is visible, and for a
class that it and its subclasses only construct, subclass or test with
isinstance, with single inheritance and no metaclass: otherwise a method
may run on an instance of another class. And only for a name hoisted out
of one class that the module mentions nowhere but in attribute loads:
any other mention could bind it on an instance or a subclass, as could
overriding `__getattribute__`.

◆ _bind_overridden_method_calls()

preprocessor.class_context_mixin.ClassContextMixin._bind_overridden_method_calls ( self,
module )
protected
Give a subclass its own copy of an inherited method that calls
`self.m()` where the subclass resolves `m` to another definition than
the method's class does, so the copy types `self` as the subclass and
the call reaches the override (#9035).

Only in the entry module, with single inheritance and no metaclass.

◆ _bind_type_values()

preprocessor.class_context_mixin.ClassContextMixin._bind_type_values ( self,
module )
protected
Replace a parameter or local that only ever holds one known class by
that class's name, which the converter resolves statically (#8389).

A known class is a top-level class or undecorated function bound only
by its definition, or a builtin class the module never binds, so its
name denotes the same object wherever it is read. A local qualifies
when a statement `t = C` directly in its function's body is its only
binding: the reads after that statement become `C`. A parameter
qualifies when, in the entry module, every reference to its
undecorated top-level function is a call passing a known class for it
and nothing rebinds it; each distinct combination of such classes gets
its own copy of the function, and the calls keep their arguments, so
they are still evaluated.

◆ _binds_name()

preprocessor.class_context_mixin.ClassContextMixin._binds_name ( self,
tree,
name,
skip = None )
protected
Whether any node in `tree` other than `skip` binds `name`.

◆ _called_parameters()

preprocessor.class_context_mixin.ClassContextMixin._called_parameters ( func)
staticprotected
The indexes of `func`'s positional parameters it calls by name.

◆ _calls_unresolved_attribute()

preprocessor.class_context_mixin.ClassContextMixin._calls_unresolved_attribute ( self,
classes,
cls,
method )
protected
Whether `method` of `cls` calls `self.x(...)` with an `x` that no
class in the ancestry of `cls` defines, so only an attribute an
instance gets elsewhere can supply it.

◆ _can_bind()

preprocessor.class_context_mixin.ClassContextMixin._can_bind ( self,
member,
targets )
protected
Whether `member` can be bound to each class in `targets`, copying it if needed.

◆ _can_hoist()

preprocessor.class_context_mixin.ClassContextMixin._can_hoist ( self,
cls,
index,
taken )
protected
Whether `cls.body[index]` is a nested class that keeps its meaning
at module level: its name is bound by nothing else in the class scope,
and moving its definition ahead of the outer class does not reorder
two calls.

◆ _class_ancestry()

preprocessor.class_context_mixin.ClassContextMixin._class_ancestry ( classes,
node )
staticprotected
`node` and its single-inheritance ancestors in `classes`, nearest first.

◆ _class_argument()

preprocessor.class_context_mixin.ClassContextMixin._class_argument ( call,
func,
index,
known )
staticprotected
The known class `call` passes for `func`'s positional parameter
`index`, else None.

◆ _class_body_binds()

preprocessor.class_context_mixin.ClassContextMixin._class_body_binds ( self,
stmt,
name )
protected
Whether a statement directly in a class body binds `name` there.

◆ _class_defines()

preprocessor.class_context_mixin.ClassContextMixin._class_defines ( node,
name )
staticprotected

◆ _class_generator()

preprocessor.class_context_mixin.ClassContextMixin._class_generator ( node,
counts )
staticprotected
The one plain `for x in ...` clause of a call `any(...)` or
`all(...)` on a generator, with neither name rebound, else None.

◆ _class_inherits_exit_handler()

preprocessor.class_context_mixin.ClassContextMixin._class_inherits_exit_handler ( self,
class_node )
protected

◆ _class_name()

preprocessor.class_context_mixin.ClassContextMixin._class_name ( node,
known )
staticprotected
The known class `node` names, or `NoneType` for `type(None)`, else
None.

◆ _class_tuple()

preprocessor.class_context_mixin.ClassContextMixin._class_tuple ( node,
known )
staticprotected
The source of a tuple whose elements each spell a known class or
`type(None)`, else None.

◆ _class_value()

preprocessor.class_context_mixin.ClassContextMixin._class_value ( node,
known )
staticprotected
The source of the known class, `type(None)`, or tuple of them that
`node` spells, else None.

◆ _classmethod_heirs()

preprocessor.class_context_mixin.ClassContextMixin._classmethod_heirs ( self,
classes,
owner,
name )
protected
Classes other than `owner` whose `name` resolves to `owner`'s method.

◆ _cls_constructor_calls()

preprocessor.class_context_mixin.ClassContextMixin._cls_constructor_calls ( self,
member )
protected
The `cls(...)` calls of a @classmethod whose `cls` is never rebound, else [].

◆ _collect_class_attr_annotations()

preprocessor.class_context_mixin.ClassContextMixin._collect_class_attr_annotations ( self,
class_node )
protected
Scan __init__ for self.attr: T = ... and cache annotations.

◆ _construction()

preprocessor.class_context_mixin.ClassContextMixin._construction ( stmt)
staticprotected
`(o, C)` for a statement `o = C(...)`, else None.

◆ _copy_is_exact()

preprocessor.class_context_mixin.ClassContextMixin._copy_is_exact ( self,
cls,
method,
alias )
protected
Whether a copy of `method` named `alias` behaves as the alias: the
def is undecorated, its defaults are constants, since a copy evaluates
them again, and both names are bound once in the class scope.

◆ _copy_methods_into_heirs()

preprocessor.class_context_mixin.ClassContextMixin._copy_methods_into_heirs ( self,
module )
protected
Give each subclass inheriting a method that calls `self.x(...)`,
with `x` defined by no ancestor, its own copy (#8956).

The copy types its receiver as the subclass, which may give `x`. Only
in the entry module, for a class that no other class's object reaches
(see _bind_nested_classes_via_self). The original is dropped when it
can run on no object; otherwise the converter refuses its call.

◆ _drop_tuple_arguments()

preprocessor.class_context_mixin.ClassContextMixin._drop_tuple_arguments ( call,
func,
indexes,
key )
staticprotected
Pass `None` for each tuple of classes `call` gives a specialised
parameter: the copy reads the tuple in place, and spelling it has no
effect to keep, while a tuple argument would not match the copy's
unread, untyped parameter.

◆ _escaped_classes()

preprocessor.class_context_mixin.ClassContextMixin._escaped_classes ( self,
module,
classes )
protected
Classes whose object may reach a method with a receiver of another
class: all of them when the module can recover a class, else those
named other than as a constructor call, a base, or the class tested by
isinstance or issubclass.

◆ _fold_class_generator()

preprocessor.class_context_mixin.ClassContextMixin._fold_class_generator ( node,
classes,
copies,
counts )
staticprotected
`bool(e1 or e2 ...)` for `any(e for x in <classes>)`, with `and`
for `all`, else None.

◆ _function_may_return_truthy()

preprocessor.class_context_mixin.ClassContextMixin._function_may_return_truthy ( func_node)
staticprotected

◆ _heirs_needing_copy()

preprocessor.class_context_mixin.ClassContextMixin._heirs_needing_copy ( self,
classes,
owner,
member )
protected
The subclasses of `owner` to give their own copy of `member`, or
none when one overrides it or it uses `super()`, which would bind
another class.

◆ _hoist_from_class()

preprocessor.class_context_mixin.ClassContextMixin._hoist_from_class ( self,
cls,
taken,
renames )
protected
`cls` preceded by the classes hoisted out of it, recursively.

◆ _hoist_lambda_arguments()

preprocessor.class_context_mixin.ClassContextMixin._hoist_lambda_arguments ( self,
module,
counts )
protected
Turn a lambda passed at module level to a top-level function that
calls that parameter into a def named for it, so that the function is
specialised on it as on any def (#8979); whether any was. At module
level a lambda's free names are globals either way.

◆ _indirect_attributes()

preprocessor.class_context_mixin.ClassContextMixin._indirect_attributes ( module,
classes )
staticprotected
Attribute names used anywhere other than as a direct `Class.name(...)` call.

◆ _inherited_nested_classes()

preprocessor.class_context_mixin.ClassContextMixin._inherited_nested_classes ( self,
classes,
cls,
excluded )
protected
The classes hoisted out of `cls` or its ancestors, by attribute name,
leaving out the names in `excluded`.

◆ _inherits_from()

preprocessor.class_context_mixin.ClassContextMixin._inherits_from ( self,
classes,
cls,
names )
protected
Whether a class named in `names` is `cls` or a subclass of it.

◆ _is_name_copy()

preprocessor.class_context_mixin.ClassContextMixin._is_name_copy ( node)
staticprotected
Whether `node` is a plain `x = y` assignment.

◆ _is_plain_function()

preprocessor.class_context_mixin.ClassContextMixin._is_plain_function ( stmt)
staticprotected
Whether `stmt` defines an undecorated function without *args or
**kwargs: calls to a variadic function are lowered before this pass,
so a call a copy introduces would be left unlowered (#8498).

◆ _is_type_of_none()

preprocessor.class_context_mixin.ClassContextMixin._is_type_of_none ( arg,
known )
staticprotected
Whether `arg` is `type(None)`, which has no builtin name; the frontend
reads NoneType as it (#8764).

◆ _known_classes()

preprocessor.class_context_mixin.ClassContextMixin._known_classes ( module,
counts )
staticprotected

◆ _lambda_as_def()

preprocessor.class_context_mixin.ClassContextMixin._lambda_as_def ( stmt,
counts )
staticprotected
`def f(...): return body` for a statement `f = lambda ...: body`
that is the only binding of `f`, else None.

◆ _lower_called_lambdas()

preprocessor.class_context_mixin.ClassContextMixin._lower_called_lambdas ( self,
module,
known,
counts )
protected
Rewrite a top-level `f = lambda ...` that calls one of its
parameters to `def f(...)` when every reference to `f` is a call
passing a known class or function for each called parameter, so that
the def is specialised. The converter types lambda parameters as
numbers and cannot call them (#8485).

◆ _method_owner()

preprocessor.class_context_mixin.ClassContextMixin._method_owner ( self,
classes,
node,
name )
protected
The nearest class in `node`'s ancestry that defines `name`, or None.

◆ _module_level_calls()

preprocessor.class_context_mixin.ClassContextMixin._module_level_calls ( node)
staticprotected
The calls in `node` that run at module level, outside any def,
lambda or comprehension whose names a lambda could capture.

◆ _module_level_nodes()

preprocessor.class_context_mixin.ClassContextMixin._module_level_nodes ( module)
staticprotected
The nodes of `module` outside any function or lambda body.

◆ _names_bound_by()

preprocessor.class_context_mixin.ClassContextMixin._names_bound_by ( node)
staticprotected
The names a single AST node binds in its scope.

◆ _names_mentioned()

preprocessor.class_context_mixin.ClassContextMixin._names_mentioned ( module)
staticprotected
Every identifier or string `module` mentions other than by loading an attribute.

◆ _rebind_cls_calls()

preprocessor.class_context_mixin.ClassContextMixin._rebind_cls_calls ( self,
method,
class_name )
protected

◆ _receiver()

preprocessor.class_context_mixin.ClassContextMixin._receiver ( self,
method )
protected
The first parameter of an undecorated method that never rebinds it, else None.

◆ _record_class_with_exit()

preprocessor.class_context_mixin.ClassContextMixin._record_class_with_exit ( self,
class_node )
protected
Cache classes whose __exit__ may suppress exceptions.

◆ _record_exit_suppresses_all()

preprocessor.class_context_mixin.ClassContextMixin._record_exit_suppresses_all ( self,
class_node )
protected
Cache classes whose __exit__ unconditionally returns True.

◆ _recovers_classes()

preprocessor.class_context_mixin.ClassContextMixin._recovers_classes ( module)
staticprotected
Whether `module` can reach a class from an instance or by name. A
`type(x)` that is only an operand of `is` or `is not` reaches no method.

◆ _replace_reads()

preprocessor.class_context_mixin.ClassContextMixin._replace_reads ( nodes,
name,
value )
staticprotected
Reads of `name` in `nodes` become `value`: a class name, or the
source of a tuple of classes.

◆ _return_may_be_truthy()

preprocessor.class_context_mixin.ClassContextMixin._return_may_be_truthy ( value)
staticprotected

◆ _runs_only_on_subclasses()

preprocessor.class_context_mixin.ClassContextMixin._runs_only_on_subclasses ( self,
module,
classes,
owner,
member )
protected
Whether `member` of `owner`, which every subclass copies, runs on
no object: `owner` is never constructed, nothing in `owner` names the
method, and no class deriving from it names `super`, which could
reach the original.

◆ _scan_fixed_receivers()

preprocessor.class_context_mixin.ClassContextMixin._scan_fixed_receivers ( self,
module )
protected
Map each `o` of a top-level `o = C(...)` to the methods `C` defines,
where `o` and `C` are each bound only once and `C` is a top-level
class, less any attribute name the module ever assigns: there `o.m`
names the same method wherever it is evaluated (#8354).

◆ _scope_binding_counts()

preprocessor.class_context_mixin.ClassContextMixin._scope_binding_counts ( module)
staticprotected
For each def in `module`, keyed by node id, and for `module`, keyed
by None, the node and how often each name is bound in it outside its
nested defs.

◆ _self_called_methods()

preprocessor.class_context_mixin.ClassContextMixin._self_called_methods ( self,
member )
protected
The names `member` calls on its receiver, when a copy of `member` is
exact: it is undecorated, never rebinds its receiver, does not use
`super` or `__class__`, and has constant defaults.

◆ _specialise_one_function()

preprocessor.class_context_mixin.ClassContextMixin._specialise_one_function ( self,
module,
known,
counts )
protected
Specialise the first top-level function that only ever is called
directly by name; whether one was.

◆ _specialise_requested_function()

preprocessor.class_context_mixin.ClassContextMixin._specialise_requested_function ( self,
module,
known,
counts )
protected
Specialise the first top-level function the entry module calls with
builtin classes (#9049) and nothing else references; whether one was.
The entry module's calls are renamed to the copies.

◆ _specialise_type_parameters()

preprocessor.class_context_mixin.ClassContextMixin._specialise_type_parameters ( self,
module,
func,
known,
calls,
taken )
protected
Copy `func` once per combination of known classes its `calls` pass
for its type parameters, naming no copy after a name in `taken`;
whether it did.

◆ _top_level_classes()

preprocessor.class_context_mixin.ClassContextMixin._top_level_classes ( module)
staticprotected
Top-level classes by name, or None when a name is defined twice.

◆ _type_copy()

preprocessor.class_context_mixin.ClassContextMixin._type_copy ( self,
func,
name,
bindings )
protected
A copy of `func` called `name` that reads each `(param, cls)` in
`bindings` as `cls`.

◆ _type_parameters()

preprocessor.class_context_mixin.ClassContextMixin._type_parameters ( self,
func,
known,
calls )
protected
The indexes of the parameters `func` reads and never rebinds, for
which every one of `calls` passes a known class.

◆ _typing_any()

preprocessor.class_context_mixin.ClassContextMixin._typing_any ( module)
staticprotected
The names `from typing import Any` binds, when nothing rebinds them.
Counted afresh, as `self._names_bound_by` resolves to
GeneratorMixin's, which skips imports.

◆ _unbindable_classes()

preprocessor.class_context_mixin.ClassContextMixin._unbindable_classes ( self,
classes )
protected
Classes reached through multiple inheritance or a metaclass keyword.

◆ _unroll_class_tuples()

preprocessor.class_context_mixin.ClassContextMixin._unroll_class_tuples ( self,
module,
known,
counts )
protected
Unroll a `for` loop, and an `any`/`all` over a generator, that runs
over a tuple of known classes, so each copy reads one class by name
(#8765). A loop qualifies when its target is a name and it has no
`break`, `continue` or `else`; the target is left bound to the last
class, as the loop leaves it. The tuple is written in place, or bound
to a name nothing else binds.

◆ _variable_names()

preprocessor.class_context_mixin.ClassContextMixin._variable_names ( module)
staticprotected
The names `module` binds anywhere other than by an import or a
definition.

◆ class_scope_default()

preprocessor.class_context_mixin.ClassContextMixin.class_scope_default ( self,
default_node,
method )
A method default naming an attribute its top-level class bound before
the method, as `Class.name`: the default is hoisted past the class, where
the bare name no longer resolves (#8200).

◆ copy_aliased_methods()

preprocessor.class_context_mixin.ClassContextMixin.copy_aliased_methods ( self,
module )
Replace each class-body `alias = name` that names an earlier def of
the same class body with a copy of that def called `alias` (#8356).
Python binds the alias to the def's function object, so the copy runs
the same body even when a subclass overrides `name`.

◆ hoist_nested_classes()

preprocessor.class_context_mixin.ClassContextMixin.hoist_nested_classes ( self,
module )
Lift each class nested in a module-level class body to module level
as `Outer__Inner`, since the converter only builds module-level classes
(#8256). `Outer.Inner` is rewritten everywhere, and so is a bare `Inner`
that the outer class body's scope resolves to the nested class.

The rewrite is purely by name, so an outer class whose name is bound
anywhere else in the module is left alone.

◆ names_bound_in()

preprocessor.class_context_mixin.ClassContextMixin.names_bound_in ( self,
tree )
Every name `tree` binds.

◆ visit_ClassDef()

preprocessor.class_context_mixin.ClassContextMixin.visit_ClassDef ( self,
node )
Track class context for method definitions.

Member Data Documentation

◆ _class_stack

preprocessor.class_context_mixin.ClassContextMixin._class_stack
protected

◆ _classes_with_exit

preprocessor.class_context_mixin.ClassContextMixin._classes_with_exit = set()
protected

◆ _exit_suppresses_all

preprocessor.class_context_mixin.ClassContextMixin._exit_suppresses_all = set()
protected

◆ _pending_method_default_inits

preprocessor.class_context_mixin.ClassContextMixin._pending_method_default_inits
protected

◆ blocked_type_functions

preprocessor.class_context_mixin.ClassContextMixin.blocked_type_functions

◆ body

preprocessor.class_context_mixin.ClassContextMixin.body

◆ class_attr_annotations

preprocessor.class_context_mixin.ClassContextMixin.class_attr_annotations

◆ current_class_name

preprocessor.class_context_mixin.ClassContextMixin.current_class_name = node.name

◆ fixed_receivers

dict preprocessor.class_context_mixin.ClassContextMixin.fixed_receivers
Initial value:
= {
name: methods[cls]
for name, cls in filter(None, map(self._construction, module.body))
if counts[name] == 1 and cls in methods
}

◆ hoisted_classes

preprocessor.class_context_mixin.ClassContextMixin.hoisted_classes = renames

◆ is_entry_module

preprocessor.class_context_mixin.ClassContextMixin.is_entry_module

◆ module_statements

dict preprocessor.class_context_mixin.ClassContextMixin.module_statements = {id(stmt): stmt for stmt in module.body}

◆ name

preprocessor.class_context_mixin.ClassContextMixin.name

◆ once_bound_names

dict preprocessor.class_context_mixin.ClassContextMixin.once_bound_names = {name for name, count in counts.items() if count == 1}

The documentation for this class was generated from the following file: