Skip to content
Snippets Groups Projects
Commit 582784a2 authored by Frederik Hennig's avatar Frederik Hennig
Browse files

elaborate documentation of type contexts

parent 03bd4bf4
No related branches found
No related tags found
1 merge request!418Nesting of Type Contexts, Type Hints, and Improved Array Typing
Pipeline #69356 passed
......@@ -4,6 +4,6 @@ Kernel Structure
.. automodule:: pystencils.sympyextensions.astnodes
.. autoclass:: pystencils.sympyextensions.AssignmentCollection
.. autoclass:: pystencils.AssignmentCollection
:members:
......@@ -13,6 +13,7 @@ who wish to customize or extend the behaviour of the code generator in their app
ast
iteration_space
translation
typification
platforms
transformations
jit
......
......@@ -13,6 +13,3 @@ Kernel Translation
.. autoclass:: pystencils.backend.kernelcreation.FreezeExpressions
:members:
.. autoclass:: pystencils.backend.kernelcreation.Typifier
:members:
**********
AST Typing
**********
In order for many transformations and code emission to function, all symbols and expressions
occuring inside a backend syntax tree must have a valid type.
In order to determine these types, pystencils provides an AST visitor (the `Typifier`) that attempts to
figure those out from the expression tree's structure and a few default data types.
.. autoclass:: pystencils.backend.kernelcreation.Typifier
:members:
.. autoclass:: pystencils.backend.kernelcreation.typification.TypificationError
:members:
Typifier Internal Classes
=========================
.. autoclass:: pystencils.backend.kernelcreation.typification.TypeContext
:members:
.. autoclass:: pystencils.backend.kernelcreation.typification.TypeHint
:members:
.. autoclass:: pystencils.backend.kernelcreation.typification.ToDefault
:members:
.. autoclass:: pystencils.backend.kernelcreation.typification.DereferencableTo
:members:
.. autodata :: pystencils.backend.kernelcreation.typification.InferenceHook
......@@ -49,7 +49,7 @@ from ..ast.expressions import (
)
from ..functions import PsMathFunction, CFunction
__all__ = ["Typifier"]
__all__ = ["Typifier", "TypificationError"]
class TypificationError(Exception):
......@@ -104,29 +104,43 @@ class TypeContext:
Instances of this class are used to propagate and check data types across expression subtrees
of the AST. Each type context has:
- A target type `target_type`, which shall be applied to all expressions it covers
- A set of restrictions on the target type:
- `require_nonconst` to make sure the target type is not `const`, as required on assignment left-hand sides
- Additional restrictions may be added in the future.
Each typing context needs to be resolved at some point.
This can happen immediately during its expansion in the following ways:
- During expansion, a node with a fixed type is encountered; then, that type is applied to the context; or
- A type is enforced directly from a surrounding or otherwise associated context; or
- the context is supplied with a type hint through `apply_hint`, which it can either use to figure out its type
directly, or pass on to any number of registered `InferenceHook`s;
one of those *must* then provide the target type.
If a type context cannot be resolved while it is being processed, its resolution needs to be deferred.
It must then be hooked into its surrounding (parent) context using an `InferenceHook`.
Through this hook, it receives two second chances for resolution:
- If the surrounding context gets resolved to a type, the type of the nested context *must* be inferred
from that surrounding type
- If the surrounding context is supplied with a type hint, that type hint is given to the inference hook
which then *may* use that to infer the type of its nested context;
it that is successful, the hook must also provide the type for the surrounding context.
- A target type ``target_type``, which shall be applied to all expressions it covers
- A set of restrictions on the target type:
- ``require_nonconst`` to make sure the target type is not ``const``,
as required on assignment left-hand sides
- Additional restrictions may be added in the future.
**Target type**
Each typing context needs to be assigned its target type at some point.
The target type may be
- predetermined by specifying it at the context's construction, or otherwise
- obtained from expressions covered by the context for which a type could be inferred, or otherwise
- inferred from the type of the enclosing context via an inference hook, or as a last resort
- determined from a type hint applied to the enclosing context via an inference hook.
**Expansion**
Expression nodes are added to a type context using either `apply_dtype` or `infer_dtype`.
In both cases, the context's target type will be applied to the node,
unless it already has a conflicting type.
If no target type is set yet, `infer_dtype` stores the node as *deferred*,
and the target type will be retroactively applied to it once it becomes known.
**Type Hints and Inference Hooks**
If a type context could not be resolved during its expansion, its type must be inferred from information
about its enclosing context.
To this end, it must be linked to its surrounding context using an `InferenceHook`.
If the surrounding context already has a target type, the inference hook is called directly with that type.
Otherwise, it will be called later when the surrounding context receives either its target type or a type hint.
If the enclosing context is resolved to a type, the nested context *must* be resolved by the inference hook,
or an error must be raised.
If the enclosing context receives a type hint instead, the inference hook *may* infer both the target types of
its inner **and** its outer context. In that case, the type of the enclosing context must be returned by the hook
callback.
"""
def __init__(
......@@ -222,7 +236,7 @@ class TypeContext:
"""Infer the data type for the given expression.
If the target_type of this context is already known, it will be applied to the given expression.
Otherwise, the expression is deferred, and a type will be applied to it as soon as `apply_type` is
Otherwise, the expression is deferred, and a type will be applied to it as soon as `apply_dtype` is
called on this context.
If the expression already has a data type set, it must be compatible with the target type
......@@ -235,6 +249,10 @@ class TypeContext:
self._apply_target_type(expr)
def _propagate_target_type(self):
"""Propagates the target type to any registered inference hooks and applies it to any deferred nodes.
Call after the target type of this context has been set.
"""
assert self._target_type is not None
for hook in self._inference_hooks:
......@@ -246,6 +264,7 @@ class TypeContext:
self._deferred_exprs = []
def _apply_target_type(self, expr: PsExpression):
"""Apply the target type to an expression node."""
assert self._target_type is not None
if expr.dtype is not None:
......@@ -328,7 +347,8 @@ class TypeContext:
else:
return dtype == self._target_type
def _fix_constness(self, dtype: PsType, expr: PsExpression | None = None):
def _fix_constness(self, dtype: PsType, expr: PsExpression | None = None) -> PsType:
"""Check and convert the constness of a type according to the ``require_nonconst`` restriction."""
if self._require_nonconst:
if dtype.const:
if expr is None:
......@@ -355,8 +375,8 @@ class Typifier:
All nodes covered by the same typing context must have the same type.
Starting from an expression's root, a typing context is implicitly expanded through
the recursive descent into a node's children. In particular, a child is typified within
the same context as its parent if the node's semantics require parent and child to have
the recursive descent into a node's children. In particular, a type context is extended from a parent
to its child node if the expressions's semantics require parent and child to have
the same type (e.g. at arithmetic operators, mathematical functions, etc.).
If a node's child is required to have a different type, a new context is opened.
......@@ -373,13 +393,13 @@ class Typifier:
The following general rules apply:
- The context's ``default_dtype`` is applied to all untyped symbols encountered inside a right-hand side expression
- If an untyped symbol is encountered on an assignment's left-hand side, it will first be attempted to infer its
type from the right-hand side. If that fails, the context's ``default_dtype`` will be applied.
- It is an error if an untyped symbol occurs in the same type context as a typed symbol or constant
with a non-default data type.
- By default, all expressions receive a ``const`` type unless they occur on a (non-declaration) assignment's
left-hand side
- The context's ``default_dtype`` is applied to all untyped symbols encountered inside a right-hand side expression
- If an untyped symbol is encountered on an assignment's left-hand side, it will first be attempted to infer its
type from the right-hand side. If that fails, the context's ``default_dtype`` will be applied.
- It is an error if an untyped symbol occurs in the same type context as a typed symbol or constant
with a non-default data type.
- By default, all expressions receive a ``const`` type unless they occur on a (non-declaration) assignment's
left-hand side
**Typing of symbol expressions**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment