Repository: reagento/adaptix (language: python)

Issue:
Added flag enums support
#197 


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index cea703e9..5d45af37 100644
--- a/.flake8
+++ b/.flake8
@@ -41,3 +41,5 @@ per-file-ignores=
 
     *_312.py:E999
     *tests/unit/morphing/model/shape_provider/data_gen_models.py:F821
+
+    tests/unit/provider/test_loc_stack_filtering.py:A001,A002
diff --git a/docs/changelog/fragments/+loc_stack.breaking.rst b/docs/changelog/fragments/+loc_stack.breaking.rst
new file mode 100644
index 00000000..78eb1382
--- /dev/null
+++ b/docs/changelog/fragments/+loc_stack.breaking.rst
@@ -0,0 +1,5 @@
+Due to refactoring of predicate system required for new features:
+
+1. ``create_request_checker`` was renamed to ``create_loc_stack_checker``
+2. ``LocStackPattern`` (class of ``P``) was renamed ``RequestPattern``
+3. method ``RequestPattern.build_request_checker()`` was renamed to ``LocStackPattern.build_loc_stack_checker()``
diff --git a/docs/changelog/fragments/197.feature.rst b/docs/changelog/fragments/197.feature.rst
new file mode 100644
index 00000000..65f3bf8a
--- /dev/null
+++ b/docs/changelog/fragments/197.feature.rst
@@ -0,0 +1,4 @@
+Added flags support
+
+Now adaptix has two different ways to process flags: :func:`.flag_by_exact_value` (by default)
+and :func:`.flag_by_member_names`.
diff --git a/docs/changelog/fragments/216.feature.rst b/docs/changelog/fragments/216.feature.rst
new file mode 100644
index 00000000..3881aff8
--- /dev/null
+++ b/docs/changelog/fragments/216.feature.rst
@@ -0,0 +1,1 @@
+    Added defaultdict support
diff --git a/docs/changelog/fragments/README.rst b/docs/changelog/fragments/README.rst
index 34e251fd..926977af 100644
--- a/docs/changelog/fragments/README.rst
+++ b/docs/changelog/fragments/README.rst
@@ -1,7 +1,7 @@
 This directory contains "news fragments" which are short files that contain a small **ReST**-formatted
 text that will be added to the next ``CHANGELOG``.
 
-The ``CHANGELOG`` will be read by **users**, so this description should be aimed to pytest users
+The ``CHANGELOG`` will be read by **users**, so this description should be aimed to users
 instead of describing internal changes which are only relevant to the developers.
 
 Make sure to use full sentences in the **past or present tense** and use punctuation, examples::
diff --git a/docs/conf.py b/docs/conf.py
index 0918d2de..60a0255d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -106,7 +106,6 @@
 add_function_parentheses = False
 
 repo = git.Repo(search_parent_directories=True)
-sha = repo.head.object.hexsha
 benchmark_data_submodule = next(submodule for submodule in repo.submodules if submodule.name == 'benchmark-data')
 
 extlinks = {
diff --git a/docs/loading-and-dumping/specific-types-behavior.rst b/docs/loading-and-dumping/specific-types-behavior.rst
index 61598791..277b185a 100644
--- a/docs/loading-and-dumping/specific-types-behavior.rst
+++ b/docs/loading-and-dumping/specific-types-behavior.rst
@@ -112,7 +112,18 @@ timedelta
 Loader accepts instance of ``int``, ``float`` or ``Decimal`` representing seconds,
 dumper serialize value via ``total_seconds`` method.
 
-Enum subclasses
+
+Flag subclasses
+'''''''''''''''''''''''
+
+Flag members by default are represented by their value. Note that flags with skipped
+bits and negative values are not supported, so it is highly recommended to define flag
+values via ``enum.auto()`` instead of manually specifying them.
+Besides, adaptix provides another way to process flags: by list using their names.
+See: :func:`.flag_by_member_names` for details.
+
+
+Other Enum subclasses
 '''''''''''''''''''''''
 
 Enum members are represented by their value without any conversion.
@@ -216,6 +227,11 @@ Dict and Mapping
 Loader accepts any other ``Mapping`` and makes ``dict`` instances.
 Dumper also constructs dict with converted keys and values.
 
+DefaultDict
+'''''''''''''''''''''
+Loader makes instances of ``defaultdict`` with the ``default_factory`` parameter set to ``None``.
+To customize this behavior, there are factory :func:`.default_dict` that have :paramref:`.default_dict.default_factory` parameter that can be overridden.
+
 Models
 ''''''''''
 
@@ -249,3 +265,4 @@ Known limitations:
 - ``__init__`` introspection or using :func:`.constructor`
 
   - Fields of unpacked typed dict (``**kwargs: Unpack[YourTypedDict]``) cannot collide with parameters of function
+
diff --git a/pyproject.toml b/pyproject.toml
index c6c937d3..6f7e065d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,6 +35,8 @@ classifiers = [
 [project.optional-dependencies]
 attrs = ['attrs >= 21.3.0']
 attrs-strict = ['attrs >= 21.3.0, <= 23.1.0']
+sqlalchemy = ['sqlalchemy >= 2.0.0']
+sqlalchemy-strict = ['sqlalchemy >= 2.0.0, <= 2.0.25']
 
 [project.urls]
 'Homepage' = 'https://github.com/reagento/dataclass_factory'
diff --git a/requirements/bench.txt b/requirements/bench.txt
index aca014fe..fbc9f2cf 100644
--- a/requirements/bench.txt
+++ b/requirements/bench.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/bench.txt --strip-extras requirements/raw/bench.txt
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 598bed34..e85a7c58 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/dev.txt --strip-extras requirements/raw/dev.txt
@@ -323,8 +323,10 @@ sphinxcontrib-serializinghtml==1.1.9
     # via sphinx
 sphinxext-opengraph==0.9.1
     # via -r requirements/raw/doc.txt
-sqlalchemy==2.0.23
-    # via -r requirements/raw/test_extra_none.txt
+sqlalchemy==2.0.25
+    # via
+    #   -r requirements/raw/test_extra_new.txt
+    #   -r requirements/raw/test_extra_none.txt
 stevedore==5.1.0
     # via bandit
 tenacity==8.2.3
diff --git a/requirements/doc.txt b/requirements/doc.txt
index 0f379fff..5af6ee2f 100644
--- a/requirements/doc.txt
+++ b/requirements/doc.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/doc.txt --strip-extras requirements/raw/doc.txt
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 9bd18394..4e4bfd7b 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/lint.txt --strip-extras requirements/raw/lint.txt
@@ -290,8 +290,10 @@ sphinxcontrib-serializinghtml==1.1.9
     # via sphinx
 sphinxext-opengraph==0.9.1
     # via -r requirements/raw/doc.txt
-sqlalchemy==2.0.23
-    # via -r requirements/raw/test_extra_none.txt
+sqlalchemy==2.0.25
+    # via
+    #   -r requirements/raw/test_extra_new.txt
+    #   -r requirements/raw/test_extra_none.txt
 stevedore==5.1.0
     # via bandit
 tenacity==8.2.3
diff --git a/requirements/pre.txt b/requirements/pre.txt
index d7f10afb..8196d707 100644
--- a/requirements/pre.txt
+++ b/requirements/pre.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/pre.txt --strip-extras requirements/raw/pre.txt
diff --git a/requirements/runner.txt b/requirements/runner.txt
index 9e2a273d..2f7744d7 100644
--- a/requirements/runner.txt
+++ b/requirements/runner.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/runner.txt --strip-extras requirements/raw/runner.txt
diff --git a/src/adaptix/__init__.py b/src/adaptix/__init__.py
index 5fa3feda..ca73f55d 100644
--- a/src/adaptix/__init__.py
+++ b/src/adaptix/__init__.py
@@ -8,10 +8,13 @@
     as_is_loader,
     bound,
     constructor,
+    default_dict,
     dumper,
     enum_by_exact_value,
     enum_by_name,
     enum_by_value,
+    flag_by_exact_value,
+    flag_by_member_names,
     loader,
     name_mapping,
     validator,
@@ -33,12 +36,12 @@
     AggregateCannotProvide,
     CannotProvide,
     Chain,
+    LocStackPattern,
     Mediator,
     P,
     Provider,
     Request,
-    RequestPattern,
-    create_request_checker,
+    create_loc_stack_checker,
 )
 from .retort import NoSuitableProvider
 
@@ -58,7 +61,10 @@
     'enum_by_exact_value',
     'enum_by_name',
     'enum_by_value',
+    'flag_by_exact_value',
+    'flag_by_member_names',
     'name_mapping',
+    'default_dict',
     'AdornedRetort',
     'FilledRetort',
     'Retort',
@@ -78,10 +84,10 @@
     'ExtraSkip',
     'Mediator',
     'NameStyle',
-    'RequestPattern',
+    'LocStackPattern',
     'P',
     'Saturator',
-    'create_request_checker',
+    'create_loc_stack_checker',
     'retort',
     'Provider',
     'NoSuitableProvider',
diff --git a/src/adaptix/_internal/datastructures.py b/src/adaptix/_internal/datastructures.py
index 0f86392d..211b05bb 100644
--- a/src/adaptix/_internal/datastructures.py
+++ b/src/adaptix/_internal/datastructures.py
@@ -11,14 +11,18 @@
     Mapping,
     Optional,
     Protocol,
+    Sequence,
     Tuple,
     Type,
     TypeVar,
     Union,
     ValuesView,
+    overload,
     runtime_checkable,
 )
 
+from .common import VarTuple
+
 K = TypeVar('K', bound=Hashable)
 V = TypeVar('V')
 _VT_co = TypeVar("_VT_co", covariant=True)
@@ -210,3 +214,42 @@ def discard(self: CM, *classes: Type[H]) -> CM:
             value for key, value in self._mapping.items()
             if key not in classes
         )
+
+
+T = TypeVar('T')
+CustomTupleT = TypeVar('CustomTupleT', bound='CustomTuple')
+
+
+class CustomTuple(Sequence[T], Generic[T]):
+    __slots__ = ('_tuple', )
+
+    def __init__(self, *args: T):
+        self._tuple = args
+
+    @classmethod
+    def from_tuple(cls: Type[CustomTupleT], tpl: VarTuple[T]) -> CustomTupleT:
+        self = cls.__new__(cls)
+        self._tuple = tpl
+        return self
+
+    def __repr__(self):
+        return f"{type(self).__name__}{self._tuple!r}"
+
+    @overload
+    def __getitem__(self, index: int) -> T:
+        ...
+
+    @overload
+    def __getitem__(self: CustomTupleT, index: slice) -> CustomTupleT:
+        ...
+
+    def __getitem__(self, index):
+        if isinstance(index, int):
+            return self._tuple[index]
+        return self.from_tuple(self._tuple[index])
+
+    def __len__(self):
+        return len(self._tuple)
+
+    def append_with(self: CustomTupleT, item: T) -> CustomTupleT:
+        return self.from_tuple(self._tuple + (item, ))
diff --git a/src/adaptix/_internal/feature_requirement.py b/src/adaptix/_internal/feature_requirement.py
index 44d4d144..bdb1a1d5 100644
--- a/src/adaptix/_internal/feature_requirement.py
+++ b/src/adaptix/_internal/feature_requirement.py
@@ -171,5 +171,8 @@ def fail_reason(self) -> str:
 HAS_SUPPORTED_ATTRS_PKG = DistributionVersionRequirement('attrs', '21.3.0')
 HAS_ATTRS_PKG = DistributionRequirement('attrs')
 
+HAS_SUPPORTED_SQLALCHEMY_PKG = DistributionVersionRequirement('sqlalchemy', '2.0.0')
+HAS_SQLALCHEMY_PKG = DistributionRequirement('sqlalchemy')
+
 IS_CPYTHON = PythonImplementationRequirement('cpython')
 IS_PYPY = PythonImplementationRequirement('pypy')
diff --git a/src/adaptix/_internal/model_tools/definitions.py b/src/adaptix/_internal/model_tools/definitions.py
index 5bef628d..df3c0790 100644
--- a/src/adaptix/_internal/model_tools/definitions.py
+++ b/src/adaptix/_internal/model_tools/definitions.py
@@ -126,7 +126,7 @@ def __hash__(self):
 
     def __repr__(self):
         return (
-            "{type(self).__qualname__}"
+            f"{type(self).__qualname__}"
             f"(key={self.key!r}, access_error={self.access_error}, path_element={self.trail_element!r})"
         )
 
diff --git a/src/adaptix/_internal/model_tools/introspection/sqlalchemy_tables.py b/src/adaptix/_internal/model_tools/introspection/sqlalchemy_tables.py
new file mode 100644
index 00000000..bc4dfb66
--- /dev/null
+++ b/src/adaptix/_internal/model_tools/introspection/sqlalchemy_tables.py
@@ -0,0 +1,197 @@
+from inspect import getfullargspec
+from typing import List, Optional
+
+try:
+    from sqlalchemy import inspect
+    from sqlalchemy.exc import NoInspectionAvailable
+    from sqlalchemy.orm import Mapper
+    from sqlalchemy.sql.schema import CallableColumnDefault, ScalarElementColumnDefault
+except ImportError:
+    pass
+
+from ...feature_requirement import HAS_SQLALCHEMY_PKG, HAS_SUPPORTED_SQLALCHEMY_PKG
+from ...type_tools import get_all_type_hints
+from ..definitions import (
+    DefaultFactory,
+    DefaultValue,
+    FullShape,
+    InputField,
+    InputShape,
+    IntrospectionImpossible,
+    NoDefault,
+    NoTargetPackage,
+    OutputField,
+    OutputShape,
+    PackageIsTooOld,
+    Param,
+    ParamKind,
+    Shape,
+    create_attr_accessor,
+)
+
+
+class ColumnWrapper:
+    def __init__(self, column):
+        self.column = column
+
+
+def _is_context_sensitive(default):
+    try:
+        wrapped_callable = default.arg.__wrapped__
+    except AttributeError:
+        return True
+
+    spec = getfullargspec(wrapped_callable)
+    return len(spec.args) > 0
+
+
+def _get_type_for_column(column, type_hints):
+    try:
+        return type_hints[column.name]
+    except KeyError:
+        if column.nullable:
+            return Optional[column.type.python_type]
+        return column.type.python_type
+
+
+def _get_type_for_relationship(relationship, type_hints):
+    try:
+        return type_hints[relationship.key]
+    except KeyError:
+        if relationship.uselist:
+            return List[relationship.entity.class_]
+        return Optional[relationship.entity.class_]
+
+
+def _get_default(column_default):
+    if isinstance(column_default, CallableColumnDefault) and not _is_context_sensitive(column_default):
+        return DefaultFactory(factory=column_default.arg.__wrapped__)
+    if isinstance(column_default, ScalarElementColumnDefault):
+        return DefaultValue(value=column_default.arg)
+    return NoDefault()
+
+
+def _is_input_required_for_column(column):
+    return not (
+        #  columns constrainted by FK are not required since they can be specified by instances
+        column.default or column.nullable or column.server_default or column.foreign_keys
+        or (column.primary_key and column.autoincrement and column.type.python_type is int)
+    )
+
+
+def _is_output_required_for_column(column):
+    return not column.nullable
+
+
+def _is_output_required_for_relationship(relationship):
+    if relationship.uselist:
+        return True
+    for column in relationship.local_columns:
+        if column.nullable:
+            return False
+    return True
+
+
+def _get_input_shape(tp, columns, relationships, type_hints) -> InputShape:
+    fields = []
+    params = []
+    for column in columns:
+        fields.append(
+            InputField(
+                id=column.name,
+                type=_get_type_for_column(column, type_hints),
+                default=_get_default(column.default),
+                is_required=_is_input_required_for_column(column),
+                metadata=column.info,
+                original=ColumnWrapper(column=column)
+            )
+        )
+        params.append(
+            Param(
+                field_id=column.name,
+                name=column.name,
+                kind=ParamKind.KW_ONLY
+            )
+        )
+
+    for relationship in relationships:
+        fields.append(
+            InputField(
+                id=relationship.key,
+                type=_get_type_for_relationship(relationship, type_hints),
+                default=NoDefault(),
+                is_required=False,
+                metadata={},
+                original=relationship
+            )
+        )
+        params.append(
+            Param(
+                field_id=relationship.key,
+                name=relationship.key,
+                kind=ParamKind.KW_ONLY
+            )
+        )
+
+    return InputShape(
+        constructor=tp,
+        fields=tuple(fields),
+        overriden_types=frozenset(),
+        kwargs=None,
+        params=tuple(params)
+    )
+
+
+def _get_output_shape(columns, relationships, type_hints) -> OutputShape:
+    output_fields = [
+        OutputField(
+            id=column.name,
+            type=_get_type_for_column(column, type_hints),
+            default=_get_default(column.default),
+            metadata=column.info,
+            original=ColumnWrapper(column=column),
+            accessor=create_attr_accessor(column.name, is_required=_is_output_required_for_column(column))
+        )
+        for column in columns
+    ]
+
+    for relationship in relationships:
+        name = str(relationship).split(".")[1]
+        output_fields.append(
+            OutputField(
+                id=name,
+                type=_get_type_for_relationship(relationship, type_hints),
+                default=NoDefault(),
+                metadata={},
+                original=relationship,
+                accessor=create_attr_accessor(name, is_required=relationship.uselist)
+            )
+        )
+
+    return OutputShape(
+        fields=tuple(output_fields),
+        overriden_types=frozenset()
+    )
+
+
+def get_sqlalchemy_shape(tp) -> FullShape:
+    if not HAS_SUPPORTED_SQLALCHEMY_PKG:
+        if not HAS_SQLALCHEMY_PKG:
+            raise NoTargetPackage
+        raise PackageIsTooOld(HAS_SUPPORTED_SQLALCHEMY_PKG.min_version)
+
+    try:
+        mapper = inspect(tp)
+    except NoInspectionAvailable:
+        raise IntrospectionImpossible
+
+    if not isinstance(mapper, Mapper):
+        raise IntrospectionImpossible
+
+    columns = mapper.columns
+    relationships = mapper.relationships
+    type_hints = get_all_type_hints(tp)
+    return Shape(
+        input=_get_input_shape(tp, columns, relationships, type_hints),
+        output=_get_output_shape(columns, relationships, type_hints)
+    )
diff --git a/src/adaptix/_internal/morphing/concrete_provider.py b/src/adaptix/_internal/morphing/concrete_provider.py
index a263ef0b..2207817f 100644
--- a/src/adaptix/_internal/morphing/concrete_provider.py
+++ b/src/adaptix/_internal/morphing/concrete_provider.py
@@ -11,29 +11,25 @@
 from ..common import Dumper, Loader
 from ..feature_requirement import HAS_PY_311, HAS_SELF_TYPE
 from ..provider.essential import CannotProvide, Mediator
+from ..provider.loc_stack_filtering import P, create_loc_stack_checker
 from ..provider.provider_template import for_predicate
-from ..provider.request_cls import FieldLoc, LocatedRequest, StrictCoercionRequest, TypeHintLoc
-from ..provider.request_filtering import P, create_request_checker
+from ..provider.request_cls import LocatedRequest, StrictCoercionRequest, TypeHintLoc, find_owner_with_field
 from ..provider.static_provider import static_provision_action
-from ..provider.utils import find_field_request
 from ..special_cases_optimization import as_is_stub
 from .load_error import DatetimeFormatMismatch, TypeLoadError, ValueLoadError
-from .provider_template import DumperProvider, LoaderProvider, ProviderWithAttachableRC
+from .provider_template import DumperProvider, LoaderProvider, ProviderWithAttachableLSC
 from .request_cls import DumperRequest, LoaderRequest
 
 T = TypeVar('T')
 
 
 @dataclass
-class ForAnyDateTime(ProviderWithAttachableRC):
+class IsoFormatProvider(LoaderProvider, DumperProvider):
     cls: Type[Union[date, time]]
 
     def __post_init__(self):
-        self._request_checker = create_request_checker(self.cls)
+        self._loc_stack_checker = create_loc_stack_checker(self.cls)
 
-
-@dataclass
-class IsoFormatProvider(ForAnyDateTime, LoaderProvider, DumperProvider):
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         raw_loader = self.cls.fromisoformat
 
@@ -147,9 +143,10 @@ class BytearrayBase64Provider(LoaderProvider, Base64DumperMixin):
     _BYTES_PROVIDER = BytesBase64Provider()
 
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        request.loc_map.get_or_raise(TypeHintLoc, lambda: CannotProvide)
+        request.last_map.get_or_raise(TypeHintLoc, lambda: CannotProvide)
         bytes_loader = self._BYTES_PROVIDER.apply_provider(
-            mediator, replace(request, loc_map=request.loc_map.add(TypeHintLoc(bytes)))
+            mediator,
+            replace(request, loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(bytes)))
         )
 
         def bytearray_base64_loader(data):
@@ -188,13 +185,13 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
 
 class ScalarLoaderProvider(LoaderProvider, Generic[T]):
     def __init__(self, pred: Type[T], strict_coercion_loader: Loader[T], lax_coercion_loader: Loader[T]):
-        self._request_checker = create_request_checker(pred)
+        self._loc_stack_checker = create_loc_stack_checker(pred)
         self._pred = pred
         self._strict_coercion_loader = strict_coercion_loader
         self._lax_coercion_loader = lax_coercion_loader
 
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
         return self._strict_coercion_loader if strict_coercion else self._lax_coercion_loader
 
 
@@ -357,13 +354,13 @@ def complex_lax_coercion_loader(data):
 
 
 @for_predicate(typing.Self if HAS_SELF_TYPE else ~P.ANY)
-class SelfTypeProvider(ProviderWithAttachableRC):
+class SelfTypeProvider(ProviderWithAttachableLSC):
     @static_provision_action
     def _provide_substitute(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
 
         try:
-            field_request = find_field_request(mediator.request_stack)
+            owner_loc_map, _field_loc_map = find_owner_with_field(request.loc_stack)
         except ValueError:
             raise CannotProvide(
                 'Owner type is not found',
@@ -371,11 +368,10 @@ def _provide_substitute(self, mediator: Mediator, request: LocatedRequest) -> Lo
                 is_demonstrative=True
             ) from None
 
-        owner_type = field_request.loc_map[FieldLoc].owner_type
         return mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(owner_type))
+                loc_stack=request.loc_stack.add_to_last_map(owner_loc_map[TypeHintLoc])
             ),
         )
 
@@ -383,7 +379,7 @@ def _provide_substitute(self, mediator: Mediator, request: LocatedRequest) -> Lo
 @for_predicate(typing.LiteralString if HAS_PY_311 else ~P.ANY)
 class LiteralStringProvider(LoaderProvider, DumperProvider):
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
         return str_strict_coercion_loader if strict_coercion else str  # type: ignore[return-value]
 
     def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
diff --git a/src/adaptix/_internal/morphing/constant_length_tuple_provider.py b/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
index bb02327b..128de58f 100644
--- a/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
+++ b/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
@@ -48,16 +48,18 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         loaders = mediator.mandatory_provide_by_iterable(
             [
                 LoaderRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=tp.source),
-                        GenericParamLoc(generic_pos=i),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=tp.source),
+                            GenericParamLoc(generic_pos=i),
+                        )
                     )
                 )
                 for i, tp in enumerate(norm.args)
             ]
         )
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._make_loader(tuple(loaders), strict_coercion, debug_trail)
 
     def _make_loader(self, loaders: Collection[Loader], strict_coercion: bool, debug_trail: DebugTrail):
@@ -185,8 +187,7 @@ def dt_disable_non_sc_loader(data):
 
             return tuple(
                 loader(field)
-                for loader, field
-                in zip(loaders, data)
+                for loader, field in zip(loaders, data)
             )
 
         return dt_disable_non_sc_loader
@@ -213,8 +214,7 @@ def dt_disable_sc_loader(data):
 
             return tuple(
                 loader(field)
-                for loader, field
-                in zip(loaders, data)
+                for loader, field in zip(loaders, data)
             )
 
         return dt_disable_sc_loader
@@ -232,15 +232,17 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         dumpers = mediator.mandatory_provide_by_iterable(
             [
                 DumperRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=tp.source),
-                        GenericParamLoc(generic_pos=i),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=tp.source),
+                            GenericParamLoc(generic_pos=i),
+                        )
                     )
                 )
                 for i, tp in enumerate(norm.args)
             ]
         )
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._make_dumper(tuple(dumpers), debug_trail)
 
     def _make_dumper(self, dumpers: Collection[Dumper], debug_trail: DebugTrail):
@@ -334,8 +336,7 @@ def tuple_dumper(data):
 
             return tuple(
                 dumper(field)
-                for dumper, field
-                in zip(dumpers, data)
+                for dumper, field in zip(dumpers, data)
             )
 
         return tuple_dumper
diff --git a/src/adaptix/_internal/morphing/dict_provider.py b/src/adaptix/_internal/morphing/dict_provider.py
index a8a5c182..17305e97 100644
--- a/src/adaptix/_internal/morphing/dict_provider.py
+++ b/src/adaptix/_internal/morphing/dict_provider.py
@@ -1,5 +1,7 @@
 import collections.abc
-from typing import Dict, Mapping, Tuple
+from collections import defaultdict
+from dataclasses import replace
+from typing import Callable, DefaultDict, Dict, Mapping, Optional, Tuple
 
 from ..common import Dumper, Loader
 from ..compat import CompatExceptionGroup
@@ -35,24 +37,28 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
 
         key_loader = mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=key.source),
-                    GenericParamLoc(generic_pos=0),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=key.source),
+                        GenericParamLoc(generic_pos=0),
+                    )
                 )
             ),
             lambda x: 'Cannot create loader for dict. Loader for key cannot be created',
         )
         value_loader = mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=value.source),
-                    GenericParamLoc(generic_pos=1),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=value.source),
+                        GenericParamLoc(generic_pos=1),
+                    )
                 )
             ),
             lambda x: 'Cannot create loader for dict. Loader for value cannot be created',
         )
         debug_trail = mediator.mandatory_provide(
-            DebugTrailRequest(loc_map=request.loc_map)
+            DebugTrailRequest(loc_stack=request.loc_stack)
         )
         return self._make_loader(
             key_loader=key_loader,
@@ -160,24 +166,28 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
 
         key_dumper = mediator.mandatory_provide(
             DumperRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=key.source),
-                    GenericParamLoc(generic_pos=0),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=key.source),
+                        GenericParamLoc(generic_pos=0),
+                    )
                 )
             ),
             lambda x: 'Cannot create dumper for dict. Dumper for key cannot be created',
         )
         value_dumper = mediator.mandatory_provide(
             DumperRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=value.source),
-                    GenericParamLoc(generic_pos=1),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=value.source),
+                        GenericParamLoc(generic_pos=1),
+                    )
                 )
             ),
             lambda x: 'Cannot create dumper for dict. Dumper for value cannot be created',
         )
         debug_trail = mediator.mandatory_provide(
-            DebugTrailRequest(loc_map=request.loc_map)
+            DebugTrailRequest(loc_stack=request.loc_stack)
         )
         return self._make_dumper(
             key_dumper=key_dumper,
@@ -261,3 +271,38 @@ def dict_dumper_dt_all(data: Mapping):
             return result
 
         return dict_dumper_dt_all
+
+
+@for_predicate(DefaultDict)
+class DefaultDictProvider(LoaderProvider, DumperProvider):
+    _DICT_PROVIDER = DictProvider()
+
+    def __init__(self, default_factory: Optional[Callable] = None):
+        self.default_factory = default_factory
+
+    def _extract_key_value(self, request: LocatedRequest) -> Tuple[BaseNormType, BaseNormType]:
+        norm = try_normalize_type(get_type_from_request(request))
+        return norm.args
+
+    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        key, value = self._extract_key_value(request)
+        dict_type_hint = Dict[key.source, value.source]  # type: ignore
+        dict_loader = self._DICT_PROVIDER.apply_provider(
+            mediator,
+            replace(request, loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(dict_type_hint)))
+        )
+        default_factory = self.default_factory
+
+        def defaultdict_loader(data):
+            return defaultdict(default_factory, dict_loader(data))
+
+        return defaultdict_loader
+
+    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        key, value = self._extract_key_value(request)
+        dict_type_hint = Dict[key.source, value.source]  # type: ignore
+
+        return self._DICT_PROVIDER.apply_provider(
+            mediator,
+            replace(request, loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(dict_type_hint)))
+        )
diff --git a/src/adaptix/_internal/morphing/enum_provider.py b/src/adaptix/_internal/morphing/enum_provider.py
index 6e7d3ddf..81806078 100644
--- a/src/adaptix/_internal/morphing/enum_provider.py
+++ b/src/adaptix/_internal/morphing/enum_provider.py
@@ -1,29 +1,105 @@
-from abc import ABC
+import collections
+import math
+from abc import ABC, abstractmethod
 from enum import Enum, EnumMeta, Flag
-from typing import Any, Mapping, Optional, Type
+from functools import reduce
+from operator import or_
+from typing import Any, Iterable, Mapping, Optional, Sequence, Type, TypeVar, Union, final
 
 from ..common import Dumper, Loader, TypeHint
 from ..morphing.provider_template import DumperProvider, LoaderProvider
-from ..provider.essential import CannotProvide, Mediator, Request
-from ..provider.request_cls import LocatedRequest, LocMap, TypeHintLoc, get_type_from_request, try_normalize_type
-from ..provider.request_filtering import DirectMediator, RequestChecker
-from .load_error import BadVariantError, MsgError
+from ..name_style import NameStyle, convert_snake_style
+from ..provider.essential import CannotProvide, Mediator
+from ..provider.loc_stack_filtering import DirectMediator, LastLocMapChecker
+from ..provider.provider_template import for_predicate
+from ..provider.request_cls import LocMap, StrictCoercionRequest, TypeHintLoc, get_type_from_request
+from ..type_tools import is_subclass_soft, normalize_type
+from .load_error import (
+    BadVariantError,
+    DuplicatedValues,
+    ExcludedTypeLoadError,
+    MsgError,
+    MultipleBadVariant,
+    OutOfRange,
+    TypeLoadError,
+)
 from .request_cls import DumperRequest, LoaderRequest
 
+EnumT = TypeVar("EnumT", bound=Enum)
+FlagT = TypeVar("FlagT", bound=Flag)
+CollectionsMapping = collections.abc.Mapping
 
-class AnyEnumRC(RequestChecker):
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide
 
-        norm = try_normalize_type(get_type_from_request(request))
+class BaseEnumMappingGenerator(ABC):
+    @abstractmethod
+    def _generate_mapping(self, cases: Iterable[EnumT]) -> Mapping[EnumT, str]:
+        ...
 
-        if not isinstance(norm.origin, EnumMeta):
-            raise CannotProvide
+    @final
+    def generate_for_dumping(self, cases: Iterable[EnumT]) -> Mapping[EnumT, str]:
+        return self._generate_mapping(cases)
 
+    @final
+    def generate_for_loading(self, cases: Iterable[EnumT]) -> Mapping[str, EnumT]:
+        return {
+            mapping_result: case
+            for case, mapping_result in self._generate_mapping(cases).items()
+        }
 
+
+class ByNameEnumMappingGenerator(BaseEnumMappingGenerator):
+    def __init__(
+        self,
+        name_style: Optional[NameStyle] = None,
+        map: Optional[Mapping[Union[str, Enum], str]] = None  # noqa: A002
+    ):
+        self._name_style = name_style
+        self._map = map if map is not None else {}
+
+    def _generate_mapping(self, cases: Iterable[EnumT]) -> Mapping[EnumT, str]:
+        result = {}
+
+        for case in cases:
+            if case in self._map:
+                mapped = self._map[case]
+            elif case.name in self._map:
+                mapped = self._map[case.name]
+            elif self._name_style:
+                mapped = convert_snake_style(case.name, self._name_style)
+            else:
+                mapped = case.name
+            result[case] = mapped
+
+        return result
+
+
+class AnyEnumLSC(LastLocMapChecker):
+    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
+        try:
+            norm = normalize_type(loc.type)
+        except ValueError:
+            return False
+        origin = norm.origin
+        return isinstance(origin, EnumMeta) and not is_subclass_soft(origin, Flag)
+
+
+class FlagEnumLSC(LastLocMapChecker):
+    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
+        try:
+            norm = normalize_type(loc.type)
+        except ValueError:
+            return False
+        return is_subclass_soft(norm.origin, Flag)
+
+
+@for_predicate(AnyEnumLSC())
 class BaseEnumProvider(LoaderProvider, DumperProvider, ABC):
-    _request_checker = AnyEnumRC()
+    pass
+
+
+@for_predicate(FlagEnumLSC())
+class BaseFlagProvider(LoaderProvider, DumperProvider, ABC):
+    pass
 
 
 def _enum_name_dumper(data):
@@ -35,14 +111,6 @@ class EnumNameProvider(BaseEnumProvider):
 
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         enum = get_type_from_request(request)
-
-        if issubclass(enum, Flag):
-            raise CannotProvide(
-                "Flag subclasses is not supported yet",
-                is_terminal=True,
-                is_demonstrative=True
-            )
-
         variants = [case.name for case in enum]
 
         def enum_loader(data):
@@ -67,8 +135,10 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         enum = get_type_from_request(request)
         value_loader = mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=self._value_type),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=self._value_type),
+                    )
                 )
             ),
         )
@@ -85,8 +155,10 @@ def enum_loader(data):
     def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         value_dumper = mediator.mandatory_provide(
             DumperRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=self._value_type)
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=self._value_type)
+                    )
                 )
             ),
         )
@@ -150,3 +222,137 @@ def _get_exact_value_to_member(self, enum: Type[Enum]) -> Optional[Mapping[Any,
 
     def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return _enum_exact_value_dumper
+
+
+class FlagByExactValueProvider(BaseFlagProvider):
+    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        enum = get_type_from_request(request)
+        flag_mask = reduce(or_, enum.__members__.values()).value
+
+        if flag_mask < 0:
+            raise CannotProvide(
+                "Cannot create a loader for flag with negative values",
+                is_terminal=True,
+                is_demonstrative=True,
+            )
+
+        all_bits = 2 ** flag_mask.bit_length() - 1
+        if all_bits != flag_mask:
+            raise CannotProvide(
+                "Cannot create a loader for flag with skipped bits",
+                is_terminal=True,
+                is_demonstrative=True,
+            )
+
+        def flag_loader(data):
+            if type(data) is not int:   # pylint: disable=unidiomatic-typecheck
+                raise TypeLoadError(int, data)
+
+            if data < 0 or data > flag_mask:
+                raise OutOfRange(0, flag_mask, data)
+
+            #  data already has been validated for all edge cases
+            #  so enum lookup cannot raise an error
+
+            return enum(data)
+
+        return flag_loader
+
+    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        return _enum_exact_value_dumper
+
+
+def _extract_non_compound_cases_from_flag(enum: Type[FlagT]) -> Sequence[FlagT]:
+    return [case for case in enum.__members__.values() if not math.log2(case.value) % 1]
+
+
+class FlagByListProvider(BaseFlagProvider):
+    def __init__(
+        self,
+        mapping_generator: BaseEnumMappingGenerator,
+        allow_single_value: bool = False,
+        allow_duplicates: bool = True,
+        allow_compound: bool = True,
+    ):
+        self._mapping_generator = mapping_generator
+        self._allow_single_value = allow_single_value
+        self._allow_duplicates = allow_duplicates
+        self._allow_compound = allow_compound
+
+    def _get_cases(self, enum: Type[FlagT]) -> Sequence[FlagT]:
+        if self._allow_compound:
+            return list(enum.__members__.values())
+        return _extract_non_compound_cases_from_flag(enum)
+
+    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:  # noqa: CCR001
+        enum = get_type_from_request(request)
+
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
+        allow_single_value = self._allow_single_value
+        allow_duplicates = self._allow_duplicates
+
+        cases = self._get_cases(enum)
+        mapping = self._mapping_generator.generate_for_loading(cases)
+        variants = list(mapping.keys())
+        zero_case = enum(0)
+
+        #  treat str and Iterable[str] as different types
+        expected_type = Union[str, Iterable[str]] if allow_single_value else Iterable[str]
+
+        def flag_loader(data) -> Flag:  # noqa: CCR001
+            data_type = type(data)
+
+            if isinstance(data, Iterable) and data_type is not str:
+                if strict_coercion and isinstance(data, CollectionsMapping):
+                    raise ExcludedTypeLoadError(expected_type, Mapping, data)
+                process_data = tuple(data)
+            else:
+                if not allow_single_value or data_type is not str:
+                    raise TypeLoadError(expected_type, data)
+                process_data = (data,)
+
+            if not allow_duplicates:
+                if len(process_data) != len(set(process_data)):
+                    raise DuplicatedValues(data)
+
+            bad_variants = []
+            result = zero_case
+            for item in process_data:
+                if item not in variants:
+                    bad_variants.append(item)
+                    continue
+                result |= mapping[item]
+
+            if bad_variants:
+                raise MultipleBadVariant(
+                    allowed_values=variants,
+                    invalid_values=bad_variants,
+                    input_value=data,
+                )
+
+            return result
+
+        return flag_loader
+
+    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        enum = get_type_from_request(request)
+
+        cases = self._get_cases(enum)
+        need_to_reverse = self._allow_compound and cases != _extract_non_compound_cases_from_flag(enum)
+        if need_to_reverse:
+            cases = tuple(reversed(cases))
+
+        mapping = self._mapping_generator.generate_for_dumping(cases)
+
+        zero_case = enum(0)
+
+        def flag_dumper(value: Flag) -> Sequence[str]:
+            result = []
+            cases_sum = zero_case
+            for case in cases:
+                if case in value and case not in cases_sum:
+                    cases_sum |= case
+                    result.append(mapping[case])
+            return list(reversed(result)) if need_to_reverse else result
+
+        return flag_dumper
diff --git a/src/adaptix/_internal/morphing/facade/provider.py b/src/adaptix/_internal/morphing/facade/provider.py
index 7bb29b3b..517a8644 100644
--- a/src/adaptix/_internal/morphing/facade/provider.py
+++ b/src/adaptix/_internal/morphing/facade/provider.py
@@ -9,21 +9,29 @@
 from ...model_tools.introspection.callable import get_callable_shape
 from ...name_style import NameStyle
 from ...provider.essential import Provider
+from ...provider.loc_stack_filtering import (
+    AnyLocStackChecker,
+    LocStackChecker,
+    LocStackPattern,
+    OrLocStackChecker,
+    Pred,
+    create_loc_stack_checker,
+)
 from ...provider.overlay_schema import OverlayProvider
 from ...provider.provider_template import ValueProvider
 from ...provider.provider_wrapper import BoundingProvider, Chain, ChainingProvider
-from ...provider.request_filtering import (
-    AnyRequestChecker,
-    OrRequestChecker,
-    Pred,
-    RequestChecker,
-    RequestPattern,
-    create_request_checker,
-)
 from ...provider.shape_provider import PropertyExtender
 from ...special_cases_optimization import as_is_stub
 from ...utils import Omittable, Omitted
-from ..enum_provider import EnumExactValueProvider, EnumNameProvider, EnumValueProvider
+from ..dict_provider import DefaultDictProvider
+from ..enum_provider import (
+    ByNameEnumMappingGenerator,
+    EnumExactValueProvider,
+    EnumNameProvider,
+    EnumValueProvider,
+    FlagByExactValueProvider,
+    FlagByListProvider,
+)
 from ..load_error import LoadError, ValidationError
 from ..model.loader_provider import InlinedShapeModelLoaderProvider
 from ..name_layout.base import ExtraIn, ExtraOut
@@ -42,7 +50,7 @@
 def bound(pred: Pred, provider: Provider) -> Provider:
     if pred == Omitted():
         return provider
-    return BoundingProvider(create_request_checker(pred), provider)
+    return BoundingProvider(create_loc_stack_checker(pred), provider)
 
 
 def make_chain(chain: Optional[Chain], provider: Provider) -> Provider:
@@ -148,26 +156,26 @@ def _name_mapping_convert_map(name_map: Omittable[NameMap]) -> VarTuple[Provider
         else:
             pred, value = element
             result.append(
-                FuncNameMappingProvider(create_request_checker(pred), value)
+                FuncNameMappingProvider(create_loc_stack_checker(pred), value)
                 if callable(value) else
-                ConstNameMappingProvider(create_request_checker(pred), value)
+                ConstNameMappingProvider(create_loc_stack_checker(pred), value)
             )
     return tuple(result)
 
 
-def _name_mapping_convert_preds(value: Omittable[Union[Iterable[Pred], Pred]]) -> Omittable[RequestChecker]:
+def _name_mapping_convert_preds(value: Omittable[Union[Iterable[Pred], Pred]]) -> Omittable[LocStackChecker]:
     if isinstance(value, Omitted):
         return value
     if isinstance(value, Iterable) and not isinstance(value, str):
-        return OrRequestChecker([create_request_checker(el) for el in value])
-    return create_request_checker(value)
+        return OrLocStackChecker([create_loc_stack_checker(el) for el in value])
+    return create_loc_stack_checker(value)
 
 
 def _name_mapping_convert_omit_default(
     value: Omittable[Union[Iterable[Pred], Pred, bool]]
-) -> Omittable[RequestChecker]:
+) -> Omittable[LocStackChecker]:
     if isinstance(value, bool):
-        return AnyRequestChecker() if value else ~AnyRequestChecker()
+        return AnyLocStackChecker() if value else ~AnyLocStackChecker()
     return _name_mapping_convert_preds(value)
 
 
@@ -294,7 +302,7 @@ def _ensure_attr_name(prop: NameOrProp) -> str:
     return fget.__name__
 
 
-EnumPred = Union[TypeHint, str, EnumMeta, RequestPattern]
+EnumPred = Union[TypeHint, str, EnumMeta, LocStackPattern]
 
 
 def _wrap_enum_provider(preds: Sequence[EnumPred], provider: Provider) -> Provider:
@@ -304,8 +312,8 @@ def _wrap_enum_provider(preds: Sequence[EnumPred], provider: Provider) -> Provid
     if Enum in preds:
         raise ValueError(f"Can not apply enum provider to {Enum}")
 
-    return BoundingProvider(
-        OrRequestChecker([create_request_checker(pred) for pred in preds]),
+    return bound(
+        OrLocStackChecker([create_loc_stack_checker(pred) for pred in preds]),
         provider,
     )
 
@@ -349,6 +357,64 @@ def enum_by_value(first_pred: EnumPred, /, *preds: EnumPred, tp: TypeHint) -> Pr
     return _wrap_enum_provider([first_pred, *preds], EnumValueProvider(tp))
 
 
+def flag_by_exact_value(*preds: EnumPred) -> Provider:
+    """Provider that represents flag members to the outside world by their value without any processing.
+    It does not support flags with skipped bits and negative values (it is recommended to use ``enum.auto()``
+    to define flag values instead of manually specifying them).
+
+    :param preds: Predicates specifying where the provider should be used.
+        The provider will be applied if any predicates meet the conditions,
+        if no predicates are passed, the provider will be used for all Flags.
+        See :ref:`predicate-system` for details.
+    :return: desired provider
+    """
+    return _wrap_enum_provider(preds, FlagByExactValueProvider())
+
+
+def flag_by_member_names(
+    *preds: EnumPred,
+    allow_single_value: bool = False,
+    allow_duplicates: bool = True,
+    allow_compound: bool = True,
+    name_style: Optional[NameStyle] = None,
+    map: Optional[Mapping[Union[str, Enum], str]] = None  # noqa: A002
+) -> Provider:
+    """Provider that represents flag members to the outside world by list of their names.
+
+    Loader takes a flag members name list and returns united flag member
+    (given members combined by operator ``|``, namely `bitwise or`).
+
+    Dumper takes a flag member and returns a list of names of flag members, included in the given flag member.
+
+    :param preds: Predicates specifying where the provider should be used.
+        The provider will be applied if any predicates meet the conditions,
+        if no predicates are passed, the provider will be used for all Flags.
+        See :ref:`predicate-system` for details.
+    :param allow_single_value: Allows calling the loader with a single value.
+        If this is allowed, singlular values are treated as one element list.
+    :param allow_duplicates: Allows calling the loader with a list containing non-unique elements.
+        Unless this is allowed, loader will raise :exc:`.DuplicatedValues` in that case.
+    :param allow_compound: Allows the loader to accept names of compound members
+        (e.g. ``WHITE = RED | GREEN | BLUE``) and the dumper to return names of compound members.
+        If this is allowed, dumper will use compound members names to serialize value.
+    :param name_style: Name style for representing members to the outside world.
+        If it is set, the provider will automatically convert the names of all flag members to the specified convention.
+    :param map: Mapping for representing members to the outside world.
+        If it is set, the provider will use it to rename members individually;
+        its keys can either be member names as strings or member instances.
+    :return: desired provider
+    """
+    return _wrap_enum_provider(
+        preds,
+        FlagByListProvider(
+            ByNameEnumMappingGenerator(name_style=name_style, map=map),
+            allow_single_value=allow_single_value,
+            allow_duplicates=allow_duplicates,
+            allow_compound=allow_compound,
+        ),
+    )
+
+
 def validator(
     pred: Pred,
     func: Callable[[Any], bool],
@@ -368,3 +434,13 @@ def validating_loader(data):
         raise exception_factory(data)
 
     return loader(pred, validating_loader, chain)
+
+
+def default_dict(pred: Pred, default_factory: Callable) -> Provider:
+    """DefaultDict provider with overriden default_factory parameter
+
+    :param pred: Predicate specifying where the provider should be used.
+        See :ref:`predicate-system` for details.
+    :param default_factory: default_factory parameter of the defaultdict instance to be created by the loader
+    """
+    return bound(pred, DefaultDictProvider(default_factory))
diff --git a/src/adaptix/_internal/morphing/facade/retort.py b/src/adaptix/_internal/morphing/facade/retort.py
index beb6a91d..c988bf8d 100644
--- a/src/adaptix/_internal/morphing/facade/retort.py
+++ b/src/adaptix/_internal/morphing/facade/retort.py
@@ -11,9 +11,9 @@
 from ...common import Dumper, Loader, TypeHint, VarTuple
 from ...definitions import DebugTrail
 from ...provider.essential import Provider, Request
+from ...provider.loc_stack_filtering import P
 from ...provider.provider_template import ValueProvider
-from ...provider.request_cls import DebugTrailRequest, LocMap, StrictCoercionRequest, TypeHintLoc
-from ...provider.request_filtering import P
+from ...provider.request_cls import DebugTrailRequest, LocMap, LocStack, StrictCoercionRequest, TypeHintLoc
 from ...provider.shape_provider import (
     ATTRS_SHAPE_PROVIDER,
     CLASS_INIT_SHAPE_PROVIDER,
@@ -42,8 +42,7 @@
     SelfTypeProvider,
 )
 from ..constant_length_tuple_provider import ConstantLengthTupleProvider
-from ..dict_provider import DictProvider
-from ..enum_provider import EnumExactValueProvider
+from ..dict_provider import DefaultDictProvider, DictProvider
 from ..generic_provider import (
     LiteralProvider,
     NewTypeUnwrappingProvider,
@@ -60,7 +59,7 @@
 from ..name_layout.provider import BuiltinNameLayoutProvider
 from ..provider_template import ABCProxy
 from ..request_cls import DumperRequest, LoaderRequest
-from .provider import as_is_dumper, as_is_loader, dumper, loader, name_mapping
+from .provider import as_is_dumper, as_is_loader, dumper, enum_by_exact_value, flag_by_exact_value, loader, name_mapping
 
 
 class FilledRetort(OperatingRetort, ABC):
@@ -79,7 +78,8 @@ class FilledRetort(OperatingRetort, ABC):
         IsoFormatProvider(time),
         SecondsTimedeltaProvider(),
 
-        EnumExactValueProvider(),  # it has higher priority than scalar types for Enum with mixins
+        flag_by_exact_value(),
+        enum_by_exact_value(),  # it has higher priority than scalar types for Enum with mixins
 
         INT_LOADER_PROVIDER,
         as_is_dumper(int),
@@ -135,6 +135,7 @@ class FilledRetort(OperatingRetort, ABC):
         ConstantLengthTupleProvider(),
         IterableProvider(),
         DictProvider(),
+        DefaultDictProvider(),
         RegexPatternProvider(),
         SelfTypeProvider(),
         LiteralStringProvider(),
@@ -243,7 +244,7 @@ def get_loader(self, tp: Type[T]) -> Loader[T]:
 
     def _make_loader(self, tp: Type[T]) -> Loader[T]:
         loader_ = self._facade_provide(
-            LoaderRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+            LoaderRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
             error_message=f'Cannot produce loader for type {tp!r}',
         )
         if self._debug_trail == DebugTrail.FIRST:
@@ -269,7 +270,7 @@ def get_dumper(self, tp: Type[T]) -> Dumper[T]:
 
     def _make_dumper(self, tp: Type[T]) -> Dumper[T]:
         dumper_ = self._facade_provide(
-            DumperRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+            DumperRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
             error_message=f'Cannot produce dumper for type {tp!r}',
         )
         if self._debug_trail == DebugTrail.FIRST:
diff --git a/src/adaptix/_internal/morphing/generic_provider.py b/src/adaptix/_internal/morphing/generic_provider.py
index a4dac014..43e112c1 100644
--- a/src/adaptix/_internal/morphing/generic_provider.py
+++ b/src/adaptix/_internal/morphing/generic_provider.py
@@ -34,7 +34,7 @@
 class NewTypeUnwrappingProvider(StaticProvider):
     @static_provision_action
     def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide)
+        loc = request.last_map.get_or_raise(TypeHintLoc, CannotProvide)
 
         if not is_new_type(loc.type):
             raise CannotProvide
@@ -42,7 +42,7 @@ def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Lo
         return mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(type=loc.type.__supertype__))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=loc.type.__supertype__))
             ),
         )
 
@@ -50,7 +50,7 @@ def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Lo
 class TypeHintTagsUnwrappingProvider(StaticProvider):
     @static_provision_action
     def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide)
+        loc = request.last_map.get_or_raise(TypeHintLoc, CannotProvide)
         norm = try_normalize_type(loc.type)
         unwrapped = strip_tags(norm)
         if unwrapped.source == loc.type:  # type has not changed, continue search
@@ -59,7 +59,7 @@ def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Lo
         return mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(type=unwrapped.source))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=unwrapped.source))
             ),
         )
 
@@ -67,7 +67,7 @@ def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Lo
 class TypeAliasUnwrappingProvider(StaticProvider):
     @static_provision_action
     def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide)
+        loc = request.last_map.get_or_raise(TypeHintLoc, CannotProvide)
         norm = try_normalize_type(loc.type)
         if not isinstance(norm, NormTypeAlias):
             raise CannotProvide
@@ -80,7 +80,7 @@ def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Lo
         return mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(type=unwrapped))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=unwrapped))
             ),
         )
 
@@ -101,7 +101,7 @@ def _get_allowed_values_collection(self, args: Collection) -> Collection:
 
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         norm = try_normalize_type(get_type_from_request(request))
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
 
         cleaned_args = [strip_annotated(arg) for arg in norm.args]
 
@@ -158,15 +158,17 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
 class UnionProvider(LoaderProvider, DumperProvider):
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         norm = try_normalize_type(get_type_from_request(request))
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
 
         if self._is_single_optional(norm):
             not_none = next(case for case in norm.args if case.origin is not None)
             not_none_loader = mediator.mandatory_provide(
                 LoaderRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=not_none.source),
-                        GenericParamLoc(generic_pos=0),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=not_none.source),
+                            GenericParamLoc(generic_pos=0),
+                        )
                     )
                 ),
                 lambda x: 'Cannot create loader for union. Loaders for some union cases cannot be created',
@@ -180,9 +182,11 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         loaders = mediator.mandatory_provide_by_iterable(
             [
                 LoaderRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=tp.source),
-                        GenericParamLoc(generic_pos=i),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=tp.source),
+                            GenericParamLoc(generic_pos=i),
+                        )
                     )
                 )
                 for i, tp in enumerate(norm.args)
@@ -278,9 +282,11 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
             not_none = next(case for case in norm.args if case.origin is not None)
             not_none_dumper = mediator.mandatory_provide(
                 DumperRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=not_none.source),
-                        GenericParamLoc(generic_pos=0),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=not_none.source),
+                            GenericParamLoc(generic_pos=0),
+                        )
                     )
                 ),
                 lambda x: 'Cannot create dumper for union. Dumpers for some union cases cannot be created',
@@ -300,9 +306,11 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         dumpers = mediator.mandatory_provide_by_iterable(
             [
                 DumperRequest(
-                    loc_map=LocMap(
-                        TypeHintLoc(type=tp.source),
-                        GenericParamLoc(generic_pos=i),
+                    loc_stack=request.loc_stack.append_with(
+                        LocMap(
+                            TypeHintLoc(type=tp.source),
+                            GenericParamLoc(generic_pos=i),
+                        )
                     )
                 )
                 for i, tp in enumerate(norm.args)
@@ -343,7 +351,7 @@ class PathLikeProvider(LoaderProvider, DumperProvider):
     def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         return mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(TypeHintLoc(type=self._impl))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=self._impl))
             ),
             lambda x: f'Cannot create loader for {PathLike}. Loader for {Path} cannot be created',
         )
diff --git a/src/adaptix/_internal/morphing/iterable_provider.py b/src/adaptix/_internal/morphing/iterable_provider.py
index 442f6242..9eaed81e 100644
--- a/src/adaptix/_internal/morphing/iterable_provider.py
+++ b/src/adaptix/_internal/morphing/iterable_provider.py
@@ -73,15 +73,17 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         iter_factory = self._get_iter_factory(norm.origin)
         arg_loader = mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=arg),
-                    GenericParamLoc(generic_pos=0),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=arg),
+                        GenericParamLoc(generic_pos=0),
+                    )
                 )
             ),
             lambda x: 'Cannot create loader for iterable. Loader for element cannot be created',
         )
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._make_loader(
             origin=norm.origin,
             iter_factory=iter_factory,
@@ -212,14 +214,16 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         iter_factory = self._get_iter_factory(norm.origin)
         arg_dumper = mediator.mandatory_provide(
             DumperRequest(
-                loc_map=LocMap(
-                    TypeHintLoc(type=arg),
-                    GenericParamLoc(generic_pos=0),
+                loc_stack=request.loc_stack.append_with(
+                    LocMap(
+                        TypeHintLoc(type=arg),
+                        GenericParamLoc(generic_pos=0),
+                    )
                 )
             ),
             lambda x: 'Cannot create dumper for iterable. Dumper for element cannot be created',
         )
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._make_dumper(
             origin=norm.origin,
             iter_factory=iter_factory,
diff --git a/src/adaptix/_internal/morphing/load_error.py b/src/adaptix/_internal/morphing/load_error.py
index f24632bc..ba785c18 100644
--- a/src/adaptix/_internal/morphing/load_error.py
+++ b/src/adaptix/_internal/morphing/load_error.py
@@ -1,7 +1,7 @@
 import dataclasses
 from dataclasses import dataclass
 from functools import partial
-from typing import Any, Iterable, Optional
+from typing import Any, Iterable, Optional, Union
 
 from ..common import TypeHint, VarTuple
 from ..compat import CompatExceptionGroup
@@ -130,8 +130,30 @@ class BadVariantError(LoadError):
     input_value: Any
 
 
+@custom_exception
+@dataclass(eq=False)
+class MultipleBadVariant(LoadError):
+    allowed_values: Iterable[Any]
+    invalid_values: Iterable[Any]
+    input_value: Any
+
+
 @custom_exception
 @dataclass(eq=False)
 class DatetimeFormatMismatch(LoadError):
     format: str
     input_value: Any
+
+
+@custom_exception
+@dataclass(eq=False)
+class DuplicatedValues(LoadError):
+    input_value: Any
+
+
+@custom_exception
+@dataclass(eq=False)
+class OutOfRange(LoadError):
+    min_value: Optional[Union[int, float]]
+    max_value: Optional[Union[int, float]]
+    input_value: Any
diff --git a/src/adaptix/_internal/morphing/model/basic_gen.py b/src/adaptix/_internal/morphing/model/basic_gen.py
index 38e8e493..4835745c 100644
--- a/src/adaptix/_internal/morphing/model/basic_gen.py
+++ b/src/adaptix/_internal/morphing/model/basic_gen.py
@@ -2,27 +2,13 @@
 import re
 import string
 from dataclasses import dataclass, replace
-from typing import (
-    Any,
-    Callable,
-    Collection,
-    Container,
-    Dict,
-    Iterable,
-    List,
-    Mapping,
-    Sequence,
-    Set,
-    Tuple,
-    TypeVar,
-    Union,
-)
+from typing import Any, Callable, Collection, Container, Dict, Iterable, List, Mapping, Set, Tuple, TypeVar, Union
 
 from ...code_tools.code_builder import CodeBuilder
 from ...code_tools.compiler import ClosureCompiler
 from ...code_tools.utils import get_literal_expr
 from ...model_tools.definitions import InputField, InputShape, OutputField, OutputShape
-from ...provider.essential import Mediator, Request
+from ...provider.essential import Mediator
 from ...provider.request_cls import LocatedRequest, TypeHintLoc
 from ...provider.static_provider import StaticProvider, static_provision_action
 from .crown_definitions import (
@@ -59,7 +45,7 @@ def stub_code_gen_hook(data: CodeGenHookData):
 
 
 @dataclass(frozen=True)
-class CodeGenHookRequest(Request[CodeGenHook]):
+class CodeGenHookRequest(LocatedRequest[CodeGenHook]):
     pass
 
 
@@ -67,27 +53,23 @@ class CodeGenAccumulator(StaticProvider):
     """Accumulates all generated code. It may be useful for debugging"""
 
     def __init__(self) -> None:
-        self.list: List[Tuple[Sequence[Request], CodeGenHookData]] = []
+        self.list: List[Tuple[CodeGenHookRequest, CodeGenHookData]] = []
 
     @static_provision_action
     def _provide_code_gen_hook(self, mediator: Mediator, request: CodeGenHookRequest) -> CodeGenHook:
-        request_stack = mediator.request_stack
-
         def hook(data: CodeGenHookData):
-            self.list.append((request_stack, data))
+            self.list.append((request, data))
 
         return hook
 
     @property
     def code_pairs(self):
         return [
-            (request_stack[-2].loc_map[TypeHintLoc].type, hook_data.source)
-            for request_stack, hook_data in self.list
+            (request.loc_stack[-2].loc_map[TypeHintLoc].type, hook_data.source)
+            for request, hook_data in self.list
             if (
-                len(request_stack) >= 2
-                and isinstance(request_stack[-2], LocatedRequest)
-                and request_stack[-2].loc_map.has(TypeHintLoc)
-                and request_stack[-2].loc_map[TypeHintLoc].type
+                len(request.loc_stack) >= 2
+                and request.loc_stack[-2].loc_map.has(TypeHintLoc)
             )
         ]
 
diff --git a/src/adaptix/_internal/morphing/model/dumper_provider.py b/src/adaptix/_internal/morphing/model/dumper_provider.py
index edf30acc..1e86dfd3 100644
--- a/src/adaptix/_internal/morphing/model/dumper_provider.py
+++ b/src/adaptix/_internal/morphing/model/dumper_provider.py
@@ -7,6 +7,7 @@
 from ...definitions import DebugTrail
 from ...model_tools.definitions import OutputShape
 from ...provider.essential import CannotProvide, Mediator
+from ...provider.fields import output_field_to_loc_map
 from ...provider.request_cls import DebugTrailRequest, TypeHintLoc
 from ...provider.shape_provider import OutputShapeRequest, provide_generic_resolved_shape
 from ..provider_template import DumperProvider
@@ -24,7 +25,6 @@
 )
 from .crown_definitions import OutputNameLayout, OutputNameLayoutRequest
 from .dumper_gen import ModelDumperGen
-from .fields import output_field_to_loc_map
 
 
 class ModelDumperProvider(DumperProvider):
@@ -37,7 +37,7 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         dumper_code_builder = dumper_gen.produce_code(ctx_namespace)
 
         try:
-            code_gen_hook = mediator.delegating_provide(CodeGenHookRequest())
+            code_gen_hook = mediator.delegating_provide(CodeGenHookRequest(loc_stack=request.loc_stack))
         except CannotProvide:
             code_gen_hook = stub_code_gen_hook
 
@@ -57,7 +57,7 @@ def _fetch_model_dumper_gen(self, mediator: Mediator, request: DumperRequest) ->
         shape = self._process_shape(shape, name_layout)
 
         fields_dumpers = self._fetch_field_dumpers(mediator, request, shape)
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._create_model_dumper_gen(
             debug_trail=debug_trail,
             shape=shape,
@@ -74,8 +74,8 @@ def _fetch_model_identity(
         name_layout: OutputNameLayout,
     ) -> str:
         return (
-            repr(request.loc_map[TypeHintLoc].type)
-            if request.loc_map.has(TypeHintLoc) else
+            repr(request.last_map[TypeHintLoc].type)
+            if request.last_map.has(TypeHintLoc) else
             '<unknown model>'
         )
 
@@ -96,8 +96,8 @@ def _create_model_dumper_gen(
         )
 
     def _request_to_view_string(self, request: DumperRequest) -> str:
-        if request.loc_map.has(TypeHintLoc):
-            tp = request.loc_map[TypeHintLoc].type
+        if request.last_map.has(TypeHintLoc):
+            tp = request.last_map[TypeHintLoc].type
             if isinstance(tp, type):
                 return tp.__name__
             return str(tp)
@@ -120,12 +120,12 @@ def _get_compiler(self):
         return BasicClosureCompiler()
 
     def _fetch_shape(self, mediator: Mediator, request: DumperRequest) -> OutputShape:
-        return provide_generic_resolved_shape(mediator, OutputShapeRequest(loc_map=request.loc_map))
+        return provide_generic_resolved_shape(mediator, OutputShapeRequest(loc_stack=request.loc_stack))
 
     def _fetch_name_layout(self, mediator: Mediator, request: DumperRequest, shape: OutputShape) -> OutputNameLayout:
         return mediator.delegating_provide(
             OutputNameLayoutRequest(
-                loc_map=request.loc_map,
+                loc_stack=request.loc_stack,
                 shape=shape,
             )
         )
@@ -136,10 +136,9 @@ def _fetch_field_dumpers(
         request: DumperRequest,
         shape: OutputShape,
     ) -> Mapping[str, Dumper]:
-        owner_type = request.loc_map[TypeHintLoc].type
         dumpers = mediator.mandatory_provide_by_iterable(
             [
-                DumperRequest(loc_map=output_field_to_loc_map(owner_type, field))
+                DumperRequest(loc_stack=request.loc_stack.append_with(output_field_to_loc_map(field)))
                 for field in shape.fields
             ],
             lambda: "Cannot create dumper for model. Dumpers for some fields cannot be created",
diff --git a/src/adaptix/_internal/morphing/model/loader_gen.py b/src/adaptix/_internal/morphing/model/loader_gen.py
index bab8c1cf..2e09779d 100644
--- a/src/adaptix/_internal/morphing/model/loader_gen.py
+++ b/src/adaptix/_internal/morphing/model/loader_gen.py
@@ -218,7 +218,7 @@ def _create_state(self, ctx_namespace: ContextNamespace) -> GenState:
         )
 
     @property
-    def has_packed_fields(self):
+    def _has_packed_fields(self):
         return any(self._is_packed_field(fld) for fld in self._shape.fields)
 
     def _is_packed_field(self, field: InputField) -> bool:
@@ -251,7 +251,7 @@ def produce_code(self, ctx_namespace: ContextNamespace) -> CodeBuilder:
             state.builder += "has_unexpected_error = False"
             state.ctx_namespace.add('model_identity', self._model_identity)
 
-        if self.has_packed_fields:
+        if self._has_packed_fields:
             state.builder += "packed_fields = {}"
 
         if not self._gen_root_crown_dispatch(state, self._name_layout.crown):
@@ -316,7 +316,7 @@ def _gen_constructor_call(self, state: GenState) -> None:
                 else:
                     constructor_builder(f"{value},")
 
-            if self.has_packed_fields:
+            if self._has_packed_fields:
                 constructor_builder("**packed_fields,")
 
             if self._name_layout.extra_move == ExtraKwargs():
diff --git a/src/adaptix/_internal/morphing/model/loader_provider.py b/src/adaptix/_internal/morphing/model/loader_provider.py
index ef464d47..c54f8390 100644
--- a/src/adaptix/_internal/morphing/model/loader_provider.py
+++ b/src/adaptix/_internal/morphing/model/loader_provider.py
@@ -7,6 +7,7 @@
 from ...definitions import DebugTrail
 from ...model_tools.definitions import InputShape
 from ...provider.essential import CannotProvide, Mediator
+from ...provider.fields import input_field_to_loc_map
 from ...provider.request_cls import DebugTrailRequest, StrictCoercionRequest, TypeHintLoc
 from ...provider.shape_provider import InputShapeRequest, provide_generic_resolved_shape
 from ..model.loader_gen import ModelLoaderGen, ModelLoaderProps
@@ -25,7 +26,6 @@
     stub_code_gen_hook,
 )
 from .crown_definitions import InputNameLayout, InputNameLayoutRequest
-from .fields import input_field_to_loc_map
 
 
 class ModelLoaderProvider(LoaderProvider):
@@ -44,7 +44,7 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         loader_code_builder = loader_gen.produce_code(ctx_namespace)
 
         try:
-            code_gen_hook = mediator.delegating_provide(CodeGenHookRequest())
+            code_gen_hook = mediator.delegating_provide(CodeGenHookRequest(loc_stack=request.loc_stack))
         except CannotProvide:
             code_gen_hook = stub_code_gen_hook
 
@@ -65,8 +65,8 @@ def _fetch_model_loader_gen(self, mediator: Mediator, request: LoaderRequest) ->
         self._validate_params(shape, name_layout)
 
         field_loaders = self._fetch_field_loaders(mediator, request, shape)
-        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_map=request.loc_map))
-        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_map=request.loc_map))
+        strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
+        debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
         return self._create_model_loader_gen(
             debug_trail=debug_trail,
             strict_coercion=strict_coercion,
@@ -84,8 +84,8 @@ def _fetch_model_identity(
         name_layout: InputNameLayout,
     ) -> str:
         return (
-            repr(request.loc_map[TypeHintLoc].type)
-            if request.loc_map.has(TypeHintLoc) else
+            repr(request.last_map[TypeHintLoc].type)
+            if request.last_map.has(TypeHintLoc) else
             repr(shape.constructor)
         )
 
@@ -109,8 +109,8 @@ def _create_model_loader_gen(
         )
 
     def _request_to_view_string(self, request: LoaderRequest) -> str:
-        if request.loc_map.has(TypeHintLoc):
-            tp = request.loc_map[TypeHintLoc].type
+        if request.last_map.has(TypeHintLoc):
+            tp = request.last_map[TypeHintLoc].type
             if isinstance(tp, type):
                 return tp.__name__
             return str(tp)
@@ -133,12 +133,12 @@ def _get_compiler(self):
         return BasicClosureCompiler()
 
     def _fetch_shape(self, mediator: Mediator, request: LoaderRequest) -> InputShape:
-        return provide_generic_resolved_shape(mediator, InputShapeRequest(loc_map=request.loc_map))
+        return provide_generic_resolved_shape(mediator, InputShapeRequest(loc_stack=request.loc_stack))
 
     def _fetch_name_layout(self, mediator: Mediator, request: LoaderRequest, shape: InputShape) -> InputNameLayout:
         return mediator.mandatory_provide(
             InputNameLayoutRequest(
-                loc_map=request.loc_map,
+                loc_stack=request.loc_stack,
                 shape=shape,
             ),
             lambda x: 'Cannot create loader for model. Cannot fetch InputNameLayout',
@@ -150,10 +150,9 @@ def _fetch_field_loaders(
         request: LoaderRequest,
         shape: InputShape,
     ) -> Mapping[str, Loader]:
-        owner_type = request.loc_map[TypeHintLoc].type
         loaders = mediator.mandatory_provide_by_iterable(
             [
-                LoaderRequest(loc_map=input_field_to_loc_map(owner_type, field))
+                LoaderRequest(loc_stack=request.loc_stack.append_with(input_field_to_loc_map(field)))
                 for field in shape.fields
             ],
             lambda: "Cannot create loader for model. Loaders for some fields cannot be created",
diff --git a/src/adaptix/_internal/morphing/model/request_filtering.py b/src/adaptix/_internal/morphing/model/request_filtering.py
index 97036d1b..f6a897ed 100644
--- a/src/adaptix/_internal/morphing/model/request_filtering.py
+++ b/src/adaptix/_internal/morphing/model/request_filtering.py
@@ -1,15 +1,20 @@
-from ...provider.essential import CannotProvide, Request
-from ...provider.request_cls import LocatedRequest
-from ...provider.request_filtering import DirectMediator, RequestChecker
+from ...provider.essential import CannotProvide
+from ...provider.loc_stack_filtering import DirectMediator, LocStackChecker
+from ...provider.request_cls import LocStack
 from ...provider.shape_provider import InputShapeRequest, OutputShapeRequest
 
 
-class AnyModelRC(RequestChecker):
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide
+class AnyModelLSC(LocStackChecker):
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        try:
+            mediator.provide(InputShapeRequest(loc_stack=loc_stack))
+        except CannotProvide:
+            pass
+        else:
+            return True
 
         try:
-            mediator.provide(InputShapeRequest(loc_map=request.loc_map))
+            mediator.provide(OutputShapeRequest(loc_stack=loc_stack))
         except CannotProvide:
-            mediator.provide(OutputShapeRequest(loc_map=request.loc_map))
+            return False
+        return True
diff --git a/src/adaptix/_internal/morphing/name_layout/component.py b/src/adaptix/_internal/morphing/name_layout/component.py
index 7af89ab0..91d5b3f1 100644
--- a/src/adaptix/_internal/morphing/name_layout/component.py
+++ b/src/adaptix/_internal/morphing/name_layout/component.py
@@ -15,9 +15,10 @@
 )
 from ...name_style import NameStyle, convert_snake_style
 from ...provider.essential import CannotProvide, Mediator, Provider
+from ...provider.fields import field_to_loc_map
+from ...provider.loc_stack_filtering import LocStackChecker
 from ...provider.overlay_schema import Overlay, Schema, provide_schema
-from ...provider.request_cls import LocatedRequest, TypeHintLoc
-from ...provider.request_filtering import ExtraStackMediator, RequestChecker
+from ...provider.request_cls import LocatedRequest
 from ...retort.operating_retort import OperatingRetort
 from ...special_cases_optimization import with_default_clause
 from ...utils import Omittable, get_prefix_groups
@@ -45,7 +46,6 @@
     OutputNameLayoutRequest,
     Sieve,
 )
-from ..model.fields import field_to_loc_map
 from .base import (
     ExtraIn,
     ExtraMoveMaker,
@@ -57,13 +57,13 @@
     SievesMaker,
     StructureMaker,
 )
-from .name_mapping import NameMappingFilterRequest, NameMappingRequest
+from .name_mapping import NameMappingRequest
 
 
 @dataclass(frozen=True)
 class StructureSchema(Schema):
-    skip: RequestChecker
-    only: RequestChecker
+    skip: LocStackChecker
+    only: LocStackChecker
 
     map: VarTuple[Provider]
     trim_trailing_underscore: bool
@@ -73,8 +73,8 @@ class StructureSchema(Schema):
 
 @dataclass(frozen=True)
 class StructureOverlay(Overlay[StructureSchema]):
-    skip: Omittable[RequestChecker]
-    only: Omittable[RequestChecker]
+    skip: Omittable[LocStackChecker]
+    only: Omittable[LocStackChecker]
 
     map: Omittable[VarTuple[Provider]]
     trim_trailing_underscore: Omittable[bool]
@@ -92,22 +92,14 @@ def _merge_map(self, old: VarTuple[Provider], new: VarTuple[Provider]) -> VarTup
 FieldAndPath = Tuple[F, Optional[KeyPath]]
 
 
-def apply_rc(
+def apply_lsc(
     mediator: Mediator,
     request: BaseNameLayoutRequest,
-    request_checker: RequestChecker,
+    loc_stack_checker: LocStackChecker,
     field: BaseField,
 ) -> bool:
-    owner_type = request.loc_map[TypeHintLoc].type
-    filter_request = NameMappingFilterRequest(loc_map=field_to_loc_map(owner_type, field))
-    try:
-        request_checker.check_request(
-            ExtraStackMediator(mediator, [filter_request]),
-            filter_request,
-        )
-    except CannotProvide:
-        return False
-    return True
+    loc_stack = request.loc_stack.append_with(field_to_loc_map(field))
+    return loc_stack_checker.check_loc_stack(mediator, loc_stack)
 
 
 class BuiltinStructureMaker(StructureMaker):
@@ -146,7 +138,7 @@ def _map_fields(
                         shape=request.shape,
                         field=field,
                         generated_key=generated_key,
-                        loc_map=field_to_loc_map(request.loc_map[TypeHintLoc].type, field),
+                        loc_stack=request.loc_stack.append_with(field_to_loc_map(field)),
                     )
                 )
             except CannotProvide:
@@ -155,8 +147,8 @@ def _map_fields(
             if path is None:
                 yield field, None
             elif (
-                not apply_rc(mediator, request, schema.skip, field)
-                and apply_rc(mediator, request, schema.only, field)
+                not apply_lsc(mediator, request, schema.skip, field)
+                and apply_lsc(mediator, request, schema.only, field)
             ):
                 yield field, path
             else:
@@ -290,7 +282,7 @@ def make_inp_structure(
         request: InputNameLayoutRequest,
         extra_move: InpExtraMove,
     ) -> PathsTo[LeafInpCrown]:
-        schema = provide_schema(StructureOverlay, mediator, request.loc_map)
+        schema = provide_schema(StructureOverlay, mediator, request.loc_stack)
         fields_to_paths: List[FieldAndPath[InputField]] = list(
             self._map_fields(mediator, request, schema, extra_move)
         )
@@ -315,7 +307,7 @@ def make_out_structure(
         request: OutputNameLayoutRequest,
         extra_move: OutExtraMove,
     ) -> PathsTo[LeafOutCrown]:
-        schema = provide_schema(StructureOverlay, mediator, request.loc_map)
+        schema = provide_schema(StructureOverlay, mediator, request.loc_stack)
         fields_to_paths: List[FieldAndPath[OutputField]] = list(
             self._map_fields(mediator, request, schema, extra_move)
         )
@@ -324,20 +316,20 @@ def make_out_structure(
         return paths_to_leaves
 
     def empty_as_list_inp(self, mediator: Mediator, request: InputNameLayoutRequest) -> bool:
-        return provide_schema(StructureOverlay, mediator, request.loc_map).as_list
+        return provide_schema(StructureOverlay, mediator, request.loc_stack).as_list
 
     def empty_as_list_out(self, mediator: Mediator, request: OutputNameLayoutRequest) -> bool:
-        return provide_schema(StructureOverlay, mediator, request.loc_map).as_list
+        return provide_schema(StructureOverlay, mediator, request.loc_stack).as_list
 
 
 @dataclass(frozen=True)
 class SievesSchema(Schema):
-    omit_default: RequestChecker
+    omit_default: LocStackChecker
 
 
 @dataclass(frozen=True)
 class SievesOverlay(Overlay[SievesSchema]):
-    omit_default: Omittable[RequestChecker]
+    omit_default: Omittable[LocStackChecker]
 
 
 class BuiltinSievesMaker(SievesMaker):
@@ -362,12 +354,12 @@ def make_sieves(
         request: OutputNameLayoutRequest,
         paths_to_leaves: PathsTo[LeafOutCrown],
     ) -> PathsTo[Sieve]:
-        schema = provide_schema(SievesOverlay, mediator, request.loc_map)
+        schema = provide_schema(SievesOverlay, mediator, request.loc_stack)
         result = {}
         for path, leaf in paths_to_leaves.items():
             if isinstance(leaf, OutFieldCrown):
                 field = request.shape.fields_dict[leaf.id]
-                if field.default != NoDefault() and apply_rc(mediator, request, schema.omit_default, field):
+                if field.default != NoDefault() and apply_lsc(mediator, request, schema.omit_default, field):
                     result[path] = self._create_sieve(field)
         return result
 
@@ -406,7 +398,7 @@ def make_inp_extra_move(
         mediator: Mediator,
         request: InputNameLayoutRequest,
     ) -> InpExtraMove:
-        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_map)
+        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_stack)
         if schema.extra_in in (ExtraForbid(), ExtraSkip()):
             return None
         if schema.extra_in == ExtraKwargs():
@@ -420,7 +412,7 @@ def make_out_extra_move(
         mediator: Mediator,
         request: OutputNameLayoutRequest,
     ) -> OutExtraMove:
-        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_map)
+        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_stack)
         if schema.extra_out == ExtraSkip():
             return None
         if callable(schema.extra_out):
@@ -440,7 +432,7 @@ def make_extra_policies(
         request: InputNameLayoutRequest,
         paths_to_leaves: PathsTo[LeafInpCrown],
     ) -> PathsTo[DictExtraPolicy]:
-        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_map)
+        schema = provide_schema(ExtraMoveAndPoliciesOverlay, mediator, request.loc_stack)
         policy = self._get_extra_policy(schema)
         path_to_extra_policy: Dict[KeyPath, DictExtraPolicy] = {
             (): policy,
diff --git a/src/adaptix/_internal/morphing/name_layout/name_mapping.py b/src/adaptix/_internal/morphing/name_layout/name_mapping.py
index af97c748..348c0021 100644
--- a/src/adaptix/_internal/morphing/name_layout/name_mapping.py
+++ b/src/adaptix/_internal/morphing/name_layout/name_mapping.py
@@ -6,8 +6,9 @@
 from ...common import EllipsisType
 from ...model_tools.definitions import BaseField, BaseShape, is_valid_field_id
 from ...provider.essential import CannotProvide, Mediator, Provider
+from ...provider.loc_stack_filtering import LocStackChecker, Pred
+from ...provider.provider_wrapper import ProviderWithLSC
 from ...provider.request_cls import LocatedRequest
-from ...provider.request_filtering import Pred, ProviderWithRC, RequestChecker
 from ...provider.static_provider import StaticProvider, static_provision_action
 from .base import Key, KeyPath
 
@@ -66,35 +67,30 @@ def _provide_input_name_layout(self, mediator: Mediator, request: NameMappingReq
         return resolve_map_result(request.generated_key, map_result)
 
 
-class ConstNameMappingProvider(StaticProvider, ProviderWithRC):
-    def __init__(self, request_checker: RequestChecker, result: MapResult):
-        self._request_checker = request_checker
+class ConstNameMappingProvider(StaticProvider, ProviderWithLSC):
+    def __init__(self, loc_stack_checker: LocStackChecker, result: MapResult):
+        self._loc_stack_checker = loc_stack_checker
         self._result = result
 
-    def get_request_checker(self) -> Optional[RequestChecker]:
-        return self._request_checker
+    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
+        return self._loc_stack_checker
 
     @static_provision_action
     def _provide_input_name_layout(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         return resolve_map_result(request.generated_key, self._result)
 
 
-class FuncNameMappingProvider(StaticProvider, ProviderWithRC):
-    def __init__(self, request_checker: RequestChecker, func: Callable[[BaseShape, BaseField], MapResult]):
-        self._request_checker = request_checker
+class FuncNameMappingProvider(StaticProvider, ProviderWithLSC):
+    def __init__(self, loc_stack_checker: LocStackChecker, func: Callable[[BaseShape, BaseField], MapResult]):
+        self._loc_stack_checker = loc_stack_checker
         self._func = func
 
-    def get_request_checker(self) -> Optional[RequestChecker]:
-        return self._request_checker
+    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
+        return self._loc_stack_checker
 
     @static_provision_action
     def _provide_input_name_layout(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         result = self._func(request.shape, request.field)
         return resolve_map_result(request.generated_key, result)
-
-
-@dataclass(frozen=True)
-class NameMappingFilterRequest(LocatedRequest[None]):
-    pass
diff --git a/src/adaptix/_internal/morphing/provider_template.py b/src/adaptix/_internal/morphing/provider_template.py
index a9a698fa..8bcdf440 100644
--- a/src/adaptix/_internal/morphing/provider_template.py
+++ b/src/adaptix/_internal/morphing/provider_template.py
@@ -3,19 +3,19 @@
 
 from ..common import Dumper, Loader, TypeHint
 from ..provider.essential import CannotProvide, Mediator
-from ..provider.provider_template import ProviderWithAttachableRC
-from ..provider.request_cls import LocMap, TypeHintLoc
-from ..provider.request_filtering import ExactOriginRC
+from ..provider.loc_stack_filtering import ExactOriginLSC
+from ..provider.provider_template import ProviderWithAttachableLSC
+from ..provider.request_cls import TypeHintLoc
 from ..provider.static_provider import static_provision_action
 from ..type_tools import normalize_type
 from .request_cls import DumperRequest, LoaderRequest
 
 
-class LoaderProvider(ProviderWithAttachableRC, ABC):
+class LoaderProvider(ProviderWithAttachableLSC, ABC):
     @final
     @static_provision_action
     def _outer_provide_loader(self, mediator: Mediator, request: LoaderRequest):
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         return self._provide_loader(mediator, request)
 
     @abstractmethod
@@ -23,11 +23,11 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         ...
 
 
-class DumperProvider(ProviderWithAttachableRC, ABC):
+class DumperProvider(ProviderWithAttachableLSC, ABC):
     @final
     @static_provision_action
     def _outer_provide_dumper(self, mediator: Mediator, request: DumperRequest):
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         return self._provide_dumper(mediator, request)
 
     @abstractmethod
@@ -39,7 +39,7 @@ class ABCProxy(LoaderProvider, DumperProvider):
     def __init__(self, abstract: TypeHint, impl: TypeHint, for_loader: bool = True, for_dumper: bool = True):
         self._abstract = normalize_type(abstract).origin
         self._impl = impl
-        self._request_checker = ExactOriginRC(self._abstract)
+        self._loc_stack_checker = ExactOriginLSC(self._abstract)
         self._for_loader = for_loader
         self._for_dumper = for_dumper
 
@@ -49,7 +49,7 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
 
         return mediator.mandatory_provide(
             LoaderRequest(
-                loc_map=LocMap(TypeHintLoc(type=self._impl))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=self._impl))
             ),
             lambda x: f'Cannot create loader for union. Loader for {self._impl} cannot be created',
         )
@@ -60,7 +60,7 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
 
         return mediator.mandatory_provide(
             DumperRequest(
-                loc_map=LocMap(TypeHintLoc(type=self._impl))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(type=self._impl))
             ),
             lambda x: f'Cannot create dumper for union. Dumper for {self._impl} cannot be created',
         )
diff --git a/src/adaptix/_internal/name_style.py b/src/adaptix/_internal/name_style.py
index be02e3ed..245a3d2b 100644
--- a/src/adaptix/_internal/name_style.py
+++ b/src/adaptix/_internal/name_style.py
@@ -66,11 +66,8 @@ class StyleConversion:
 ONLY_WORD_CHARS = re.compile(r'\w+')
 
 
-def is_snake_case(name: str) -> bool:
-    return (
-        ONLY_WORD_CHARS.fullmatch(name) is not None
-        and name.lower() == name
-    )
+def is_snake_style(name: str) -> bool:
+    return ONLY_WORD_CHARS.fullmatch(name) is not None
 
 
 SNAKE_SPLITTER = re.compile(r'(_*)([^_]+)(.*?)(_*)$')
@@ -84,8 +81,8 @@ def rest_sub(conv: StyleConversion, match_: re.Match):
 
 
 def convert_snake_style(name: str, style: NameStyle) -> str:
-    if not is_snake_case(name):
-        raise ValueError("Cannot convert a name that not follows snake_case")
+    if not is_snake_style(name):
+        raise ValueError("Cannot convert a name that not follows snake style")
 
     match = SNAKE_SPLITTER.match(name)
     if match is None:
diff --git a/src/adaptix/_internal/provider/essential.py b/src/adaptix/_internal/provider/essential.py
index 78624439..85976516 100644
--- a/src/adaptix/_internal/provider/essential.py
+++ b/src/adaptix/_internal/provider/essential.py
@@ -1,6 +1,6 @@
 from abc import ABC, abstractmethod
 from dataclasses import dataclass
-from typing import Any, Callable, Generic, Iterable, Optional, Sequence, TypeVar, final
+from typing import Callable, Generic, Iterable, Optional, Sequence, TypeVar, final
 
 from ..compat import CompatExceptionGroup
 from ..feature_requirement import HAS_NATIVE_EXC_GROUP
@@ -116,15 +116,20 @@ class Mediator(ABC, Generic[V]):
     """
 
     @abstractmethod
-    def provide(self, request: Request[T], *, extra_stack: Sequence[Request[Any]] = ()) -> T:
+    def provide(self, request: Request[T]) -> T:
         """Get response of sent request.
 
         :param request: A request instance
-        :param extra_stack: Additional stack that will be added to :attr:`.request_stack` before passed request
         :return: Result of the request processing
         :raise CannotProvide: A provider able to process the request does not be found
         """
 
+    @abstractmethod
+    def provide_from_next(self) -> V:
+        """Forward current request to providers
+        that placed after current provider at the recipe.
+        """
+
     @final
     def delegating_provide(
         self,
@@ -181,19 +186,6 @@ def mandatory_provide_by_iterable(
             )
         return results
 
-    @abstractmethod
-    def provide_from_next(self) -> V:
-        """Forward current request to providers
-        that placed after current provider at the recipe.
-        """
-
-    @property
-    @abstractmethod
-    def request_stack(self) -> Sequence[Request[Any]]:
-        """Call stack, but consisting of requests.
-        Last element of ``request_stack`` is current request.
-        """
-
 
 class Provider(ABC):
     """An object that can process Request instances"""
diff --git a/src/adaptix/_internal/morphing/model/fields.py b/src/adaptix/_internal/provider/fields.py
similarity index 52%
rename from src/adaptix/_internal/morphing/model/fields.py
rename to src/adaptix/_internal/provider/fields.py
index 7446f577..5284fe14 100644
--- a/src/adaptix/_internal/morphing/model/fields.py
+++ b/src/adaptix/_internal/provider/fields.py
@@ -1,15 +1,13 @@
-from ...common import TypeHint
-from ...model_tools.definitions import BaseField, InputField, OutputField
-from ...provider.request_cls import FieldLoc, InputFieldLoc, LocMap, OutputFieldLoc, TypeHintLoc
+from ..model_tools.definitions import BaseField, InputField, OutputField
+from .request_cls import FieldLoc, InputFieldLoc, LocMap, OutputFieldLoc, TypeHintLoc
 
 
-def input_field_to_loc_map(owner_type: TypeHint, field: InputField) -> LocMap:
+def input_field_to_loc_map(field: InputField) -> LocMap:
     return LocMap(
         TypeHintLoc(
             type=field.type,
         ),
         FieldLoc(
-            owner_type=owner_type,
             field_id=field.id,
             default=field.default,
             metadata=field.metadata,
@@ -20,13 +18,12 @@ def input_field_to_loc_map(owner_type: TypeHint, field: InputField) -> LocMap:
     )
 
 
-def output_field_to_loc_map(owner_type: TypeHint, field: OutputField) -> LocMap:
+def output_field_to_loc_map(field: OutputField) -> LocMap:
     return LocMap(
         TypeHintLoc(
             type=field.type,
         ),
         FieldLoc(
-            owner_type=owner_type,
             field_id=field.id,
             default=field.default,
             metadata=field.metadata,
@@ -37,9 +34,9 @@ def output_field_to_loc_map(owner_type: TypeHint, field: OutputField) -> LocMap:
     )
 
 
-def field_to_loc_map(owner_type: TypeHint, field: BaseField) -> LocMap:
+def field_to_loc_map(field: BaseField) -> LocMap:
     if isinstance(field, InputField):
-        return input_field_to_loc_map(owner_type, field)
+        return input_field_to_loc_map(field)
     if isinstance(field, OutputField):
-        return output_field_to_loc_map(owner_type, field)
+        return output_field_to_loc_map(field)
     raise TypeError
diff --git a/src/adaptix/_internal/provider/loc_stack_filtering.py b/src/adaptix/_internal/provider/loc_stack_filtering.py
new file mode 100644
index 00000000..ecc25095
--- /dev/null
+++ b/src/adaptix/_internal/provider/loc_stack_filtering.py
@@ -0,0 +1,372 @@
+import inspect
+import operator
+import re
+from abc import ABC, abstractmethod
+from copy import copy
+from dataclasses import dataclass
+from functools import reduce
+from inspect import isabstract, isgenerator
+from typing import Any, Callable, ClassVar, Iterable, Optional, Pattern, Protocol, Sequence, Type, TypeVar, Union, final
+
+from ..common import TypeHint, VarTuple
+from ..type_tools import (
+    BaseNormType,
+    NormTV,
+    is_bare_generic,
+    is_generic,
+    is_parametrized,
+    is_protocol,
+    is_subclass_soft,
+    normalize_type,
+)
+from ..type_tools.normalize_type import NotSubscribedError
+from .essential import CannotProvide, Request
+from .request_cls import FieldLoc, GenericParamLoc, Location, LocStack, TypeHintLoc
+
+T = TypeVar('T')
+
+
+class DirectMediator(Protocol):
+    """This is a copy of Mediator protocol but without provide_from_next() method"""
+
+    def provide(self, request: Request[T]) -> T:
+        ...
+
+    def delegating_provide(
+        self,
+        request: Request[T],
+        error_describer: Optional[Callable[[CannotProvide], str]] = None,
+    ) -> T:
+        ...
+
+    def mandatory_provide(
+        self,
+        request: Request[T],
+        error_describer: Optional[Callable[[CannotProvide], str]] = None,
+    ) -> T:
+        ...
+
+    def mandatory_provide_by_iterable(
+        self,
+        requests: Iterable[Request[T]],
+        error_describer: Optional[Callable[[], str]] = None,
+    ) -> Iterable[T]:
+        ...
+
+
+class LocStackChecker(ABC):
+    @abstractmethod
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        ...
+
+    @final
+    def __or__(self, other: Any) -> 'LocStackChecker':
+        if isinstance(other, LocStackChecker):
+            return OrLocStackChecker([self, other])
+        return NotImplemented
+
+    @final
+    def __and__(self, other: Any) -> 'LocStackChecker':
+        if isinstance(other, LocStackChecker):
+            return AndLocStackChecker([self, other])
+        return NotImplemented
+
+    @final
+    def __xor__(self, other: Any) -> 'LocStackChecker':
+        if isinstance(other, LocStackChecker):
+            return XorLocStackChecker([self, other])
+        return NotImplemented
+
+    @final
+    def __invert__(self) -> 'LocStackChecker':
+        return InvertLSC(self)
+
+
+class InvertLSC(LocStackChecker):
+    def __init__(self, lsc: LocStackChecker):
+        self._lsc = lsc
+
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        return not self._lsc.check_loc_stack(mediator, loc_stack)
+
+
+class BinOperatorLSC(LocStackChecker, ABC):
+    def __init__(self, loc_stack_checkers: Iterable[LocStackChecker]):
+        self._loc_stack_checkers = loc_stack_checkers
+
+    @abstractmethod
+    def reduce(self, elements: Iterable[bool], /) -> bool:
+        ...
+
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        return self.reduce(
+            loc_stack_checker.check_loc_stack(mediator, loc_stack)
+            for loc_stack_checker in self._loc_stack_checkers
+        )
+
+
+class OrLocStackChecker(BinOperatorLSC):
+    reduce = any  # type: ignore[assignment]
+
+
+class AndLocStackChecker(BinOperatorLSC):
+    reduce = all  # type: ignore[assignment]
+
+
+class XorLocStackChecker(BinOperatorLSC):
+    def reduce(self, elements: Iterable[bool], /) -> bool:
+        return reduce(operator.xor, elements)
+
+
+class LastLocMapChecker(LocStackChecker, ABC):
+    _expected_location: ClassVar[Type[Location]]
+
+    def __init_subclass__(cls, **kwargs):
+        param_list = list(inspect.signature(cls._check_location).parameters.values())
+        cls._expected_location = param_list[2].annotation
+
+    @final
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        loc_map = loc_stack[-1]
+        if loc_map.has(self._expected_location):
+            return self._check_location(mediator, loc_map[self._expected_location])
+        return False
+
+    @abstractmethod
+    def _check_location(self, mediator: DirectMediator, loc: Any) -> bool:
+        pass
+
+
+@dataclass(frozen=True)
+class ExactFieldNameLSC(LastLocMapChecker):
+    field_id: str
+
+    def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
+        return self.field_id == loc.field_id
+
+
+@dataclass(frozen=True)
+class ReFieldNameLSC(LastLocMapChecker):
+    pattern: Pattern[str]
+
+    def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
+        return self.pattern.fullmatch(loc.field_id) is not None
+
+
+@dataclass(frozen=True)
+class ExactTypeLSC(LastLocMapChecker):
+    norm: BaseNormType
+
+    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
+        try:
+            norm = normalize_type(loc.type)
+        except ValueError:
+            return False
+        return norm == self.norm
+
+
+@dataclass(frozen=True)
+class OriginSubclassLSC(LastLocMapChecker):
+    type_: type
+
+    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
+        try:
+            norm = normalize_type(loc.type)
+        except ValueError:
+            return False
+        return is_subclass_soft(norm.origin, self.type_)
+
+
+@dataclass(frozen=True)
+class ExactOriginLSC(LastLocMapChecker):
+    origin: Any
+
+    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
+        try:
+            norm = normalize_type(loc.type)
+        except ValueError:
+            return False
+        return norm.origin == self.origin
+
+
+@dataclass(frozen=True)
+class GenericParamLSC(LastLocMapChecker):
+    pos: int
+
+    def _check_location(self, mediator: DirectMediator, loc: GenericParamLoc) -> bool:
+        return loc.generic_pos == self.pos
+
+
+@dataclass(frozen=True)
+class LocStackEndChecker(LocStackChecker):
+    loc_stack_checkers: Sequence[LocStackChecker]
+
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        if len(loc_stack) < len(self.loc_stack_checkers):
+            return False
+
+        for i, checker in enumerate(reversed(self.loc_stack_checkers), start=0):
+            if not checker.check_loc_stack(mediator, loc_stack[:len(loc_stack) - i]):
+                return False
+        return True
+
+
+class AnyLocStackChecker(LocStackChecker):
+    def check_loc_stack(self, mediator: DirectMediator, loc_stack: LocStack) -> bool:
+        return True
+
+
+Pred = Union[str, re.Pattern, type, TypeHint, LocStackChecker, 'LocStackPattern']
+
+
+def _create_non_type_hint_loc_stack_checker(pred: Pred) -> Optional[LocStackChecker]:
+    if isinstance(pred, str):
+        if pred.isidentifier():
+            return ExactFieldNameLSC(pred)  # this is only an optimization
+        return ReFieldNameLSC(re.compile(pred))
+
+    if isinstance(pred, re.Pattern):
+        return ReFieldNameLSC(pred)
+
+    if isinstance(pred, LocStackChecker):
+        return pred
+
+    if isinstance(pred, LocStackPattern):
+        return pred.build_loc_stack_checker()
+
+    return None
+
+
+def _create_loc_stack_checker_by_origin(origin):
+    if is_protocol(origin) or isabstract(origin):
+        return OriginSubclassLSC(origin)
+    return ExactOriginLSC(origin)
+
+
+def create_loc_stack_checker(pred: Pred) -> LocStackChecker:
+    result = _create_non_type_hint_loc_stack_checker(pred)
+    if result is not None:
+        return result
+
+    try:
+        norm = normalize_type(pred)
+    except NotSubscribedError:
+        return ExactOriginLSC(pred)
+    except ValueError:
+        raise ValueError(f'Can not create LocStackChecker from {pred}')
+
+    if isinstance(norm, NormTV):
+        raise ValueError(f'Can not create LocStackChecker from {pred} type var')
+
+    if is_bare_generic(pred):
+        return _create_loc_stack_checker_by_origin(norm.origin)
+
+    if is_generic(pred):
+        raise ValueError(
+            f'Can not create LocStackChecker from {pred} generic alias (parametrized generic)'
+        )
+
+    if not is_generic(norm.origin) and not is_parametrized(pred):
+        return _create_loc_stack_checker_by_origin(norm.origin)   # this is only an optimization
+    return ExactTypeLSC(norm)
+
+
+Pat = TypeVar('Pat', bound='LocStackPattern')
+
+
+class LocStackPattern:
+    ANY = AnyLocStackChecker()
+
+    def __init__(self, stack: VarTuple[LocStackChecker]):
+        self._stack = stack
+
+    @classmethod
+    def _from_lsc(cls: Type[Pat], lsc: LocStackChecker) -> Pat:
+        return cls((lsc, ))
+
+    def _extend_stack(self: Pat, elements: Iterable[LocStackChecker]) -> Pat:
+        self_copy = copy(self)
+        self_copy._stack = self._stack + tuple(elements)  # pylint: disable=protected-access
+        return self_copy
+
+    def __getattr__(self: Pat, item: str) -> Pat:
+        if item.startswith('__') and item.endswith('__'):
+            raise AttributeError
+        return self[item]
+
+    def __getitem__(self: Pat, item: Union[Pred, VarTuple[Pred]]) -> Pat:
+        if isinstance(item, tuple) or isgenerator(item):
+            return self._extend_stack(
+                [OrLocStackChecker([self._ensure_loc_stack_checker_from_pred(el) for el in item])]
+            )
+        return self._extend_stack([self._ensure_loc_stack_checker_from_pred(item)])
+
+    def _ensure_loc_stack_checker(self: Pat, other: Union[Pat, LocStackChecker]) -> LocStackChecker:
+        if isinstance(other, LocStackChecker):
+            return other
+        return other.build_loc_stack_checker()
+
+    def __or__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self.build_loc_stack_checker() | self._ensure_loc_stack_checker(other)
+        )
+
+    def __ror__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self._ensure_loc_stack_checker(other) | self.build_loc_stack_checker()
+        )
+
+    def __and__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self.build_loc_stack_checker() & self._ensure_loc_stack_checker(other)
+        )
+
+    def __rand__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self._ensure_loc_stack_checker(other) & self.build_loc_stack_checker()
+        )
+
+    def __xor__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self.build_loc_stack_checker() ^ self._ensure_loc_stack_checker(other)
+        )
+
+    def __rxor__(self: Pat, other: Union[Pat, LocStackChecker]) -> Pat:
+        return self._from_lsc(
+            self._ensure_loc_stack_checker(other) ^ self.build_loc_stack_checker()
+        )
+
+    def __invert__(self: Pat) -> Pat:
+        return self._from_lsc(
+            ~self.build_loc_stack_checker()
+        )
+
+    def __add__(self: Pat, other: Pat) -> Pat:
+        return self._extend_stack(other._stack)
+
+    def _ensure_loc_stack_checker_from_pred(self, pred: Any) -> LocStackChecker:
+        if isinstance(pred, LocStackPattern):
+            raise TypeError(
+                "Can not use LocStackPattern as predicate inside LocStackPattern."
+                " If you want to combine several LocStackPattern, you can use `+` operator"
+            )
+
+        return create_loc_stack_checker(pred)
+
+    def generic_arg(self: Pat, pos: int, pred: Pred) -> Pat:
+        return self._extend_stack(
+            [GenericParamLSC(pos) & self._ensure_loc_stack_checker_from_pred(pred)]
+        )
+
+    def build_loc_stack_checker(self) -> LocStackChecker:
+        if len(self._stack) == 0:
+            raise ValueError(
+                'Can not produce LocStackChecker from LocStackPattern without stack.'
+                ' You need to parametrize P object with predicates.'
+            )
+        if len(self._stack) == 1:
+            return self._stack[0]
+        return LocStackEndChecker(self._stack)
+
+
+P = LocStackPattern(stack=())
diff --git a/src/adaptix/_internal/provider/overlay_schema.py b/src/adaptix/_internal/provider/overlay_schema.py
index 27386c7c..5b53b0bd 100644
--- a/src/adaptix/_internal/provider/overlay_schema.py
+++ b/src/adaptix/_internal/provider/overlay_schema.py
@@ -6,7 +6,7 @@
 from ..utils import Omitted
 from .essential import CannotProvide, Mediator
 from .provider_wrapper import Chain
-from .request_cls import LocatedRequest, LocMap, TypeHintLoc
+from .request_cls import LocatedRequest, LocStack, TypeHintLoc
 from .static_provider import StaticProvider, static_provision_action
 
 
@@ -82,20 +82,20 @@ class OverlayRequest(LocatedRequest[Ov], Generic[Ov]):
     overlay_cls: Type[Ov]
 
 
-def provide_schema(overlay: Type[Overlay[Sc]], mediator: Mediator, loc_map: LocMap) -> Sc:
+def provide_schema(overlay: Type[Overlay[Sc]], mediator: Mediator, loc_stack: LocStack) -> Sc:
     stacked_overlay = mediator.mandatory_provide(
         OverlayRequest(
-            loc_map=loc_map,
+            loc_stack=loc_stack,
             overlay_cls=overlay,
         )
     )
-
+    loc_map = loc_stack[-1]
     if loc_map.has(TypeHintLoc) and isinstance(loc_map[TypeHintLoc].type, type):
         for parent in loc_map[TypeHintLoc].type.mro()[1:]:
             try:
                 new_overlay = mediator.delegating_provide(
                     OverlayRequest(
-                        loc_map=loc_map.add(TypeHintLoc(type=parent)),
+                        loc_stack=loc_stack.add_to_last_map(TypeHintLoc(type=parent)),
                         overlay_cls=overlay,
                     )
                 )
diff --git a/src/adaptix/_internal/provider/provider_template.py b/src/adaptix/_internal/provider/provider_template.py
index 6f1504ae..6f8929df 100644
--- a/src/adaptix/_internal/provider/provider_template.py
+++ b/src/adaptix/_internal/provider/provider_template.py
@@ -1,28 +1,28 @@
 from typing import Generic, Optional, Type, TypeVar
 
 from .essential import CannotProvide, Mediator, Request
-from .provider_wrapper import RequestClassDeterminedProvider
-from .request_filtering import AnyRequestChecker, Pred, ProviderWithRC, RequestChecker, create_request_checker
+from .loc_stack_filtering import LocStackChecker, P, Pred, create_loc_stack_checker
+from .provider_wrapper import ProviderWithLSC, RequestClassDeterminedProvider
 from .static_provider import StaticProvider
 
 T = TypeVar('T')
 
 
-class ProviderWithAttachableRC(StaticProvider, ProviderWithRC):
-    _request_checker: RequestChecker = AnyRequestChecker()
+class ProviderWithAttachableLSC(StaticProvider, ProviderWithLSC):
+    _loc_stack_checker: LocStackChecker = P.ANY
 
-    def get_request_checker(self) -> Optional[RequestChecker]:
-        return self._request_checker
+    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
+        return self._loc_stack_checker
 
 
 def for_predicate(pred: Pred):
-    def decorator(cls: Type[ProviderWithAttachableRC]):
-        if not (isinstance(cls, type) and issubclass(cls, ProviderWithAttachableRC)):
-            raise TypeError(f"Only {ProviderWithAttachableRC} child is allowed")
+    def decorator(cls: Type[ProviderWithAttachableLSC]):
+        if not (isinstance(cls, type) and issubclass(cls, ProviderWithAttachableLSC)):
+            raise TypeError(f"Only {ProviderWithAttachableLSC} child is allowed")
 
         # noinspection PyProtectedMember
         # pylint: disable=protected-access
-        cls._request_checker = create_request_checker(pred)
+        cls._loc_stack_checker = create_loc_stack_checker(pred)
         return cls
 
     return decorator
diff --git a/src/adaptix/_internal/provider/provider_wrapper.py b/src/adaptix/_internal/provider/provider_wrapper.py
index bef7fa5e..53d75fec 100644
--- a/src/adaptix/_internal/provider/provider_wrapper.py
+++ b/src/adaptix/_internal/provider/provider_wrapper.py
@@ -3,7 +3,8 @@
 from typing import Optional, Type, TypeVar
 
 from .essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
-from .request_filtering import ProviderWithRC, RequestChecker
+from .loc_stack_filtering import LocStackChecker
+from .request_cls import LocatedRequest
 
 T = TypeVar('T')
 
@@ -14,25 +15,42 @@ def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
         ...
 
 
-class BoundingProvider(RequestClassDeterminedProvider, ProviderWithRC):
-    def __init__(self, request_checker: RequestChecker, provider: Provider):
-        self._request_checker = request_checker
+class ProviderWithLSC(Provider, ABC):
+    @abstractmethod
+    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
+        ...
+
+    def _apply_loc_stack_checker(self, mediator: Mediator, request: Request) -> None:
+        if not isinstance(request, LocatedRequest):
+            raise CannotProvide
+
+        loc_stack_checker = self.get_loc_stack_checker()
+        if loc_stack_checker is None:
+            return
+
+        if not loc_stack_checker.check_loc_stack(mediator, request.loc_stack):
+            raise CannotProvide
+
+
+class BoundingProvider(RequestClassDeterminedProvider, ProviderWithLSC):
+    def __init__(self, loc_stack_checker: LocStackChecker, provider: Provider):
+        self._loc_stack_checker = loc_stack_checker
         self._provider = provider
 
     def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         return self._provider.apply_provider(mediator, request)
 
     def __repr__(self):
-        return f"{type(self).__name__}({self._request_checker}, {self._provider})"
+        return f"{type(self).__name__}({self._loc_stack_checker}, {self._provider})"
 
     def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
         if isinstance(self._provider, RequestClassDeterminedProvider):
             return self._provider.maybe_can_process_request_cls(request_cls)
         return True
 
-    def get_request_checker(self) -> Optional[RequestChecker]:
-        return self._request_checker
+    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
+        return self._loc_stack_checker
 
 
 class ConcatProvider(RequestClassDeterminedProvider):
diff --git a/src/adaptix/_internal/provider/request_cls.py b/src/adaptix/_internal/provider/request_cls.py
index a1986f2f..2463b867 100644
--- a/src/adaptix/_internal/provider/request_cls.py
+++ b/src/adaptix/_internal/provider/request_cls.py
@@ -1,11 +1,12 @@
 from dataclasses import dataclass, field
-from typing import Any, Mapping, TypeVar
+from typing import Any, Mapping, Tuple, TypeVar
 
 from ..common import TypeHint
-from ..datastructures import ClassMap
+from ..datastructures import ClassMap, CustomTuple
 from ..definitions import DebugTrail
 from ..model_tools.definitions import Accessor, Default
 from ..type_tools import BaseNormType, normalize_type
+from ..utils import pairs
 from .essential import CannotProvide, Request
 
 T = TypeVar('T')
@@ -23,7 +24,6 @@ class TypeHintLoc(Location):
 
 @dataclass(frozen=True)
 class FieldLoc(Location):
-    owner_type: TypeHint
     field_id: str
     default: Default
     metadata: Mapping[Any, Any] = field(hash=False)
@@ -46,15 +46,28 @@ class GenericParamLoc(Location):
 
 LocMap = ClassMap[Location]
 LR = TypeVar('LR', bound='LocatedRequest')
+LocStackT = TypeVar('LocStackT', bound='LocStack')
+
+
+class LocStack(CustomTuple[LocMap]):
+    def replace_last(self: LocStackT, loc: LocMap) -> LocStackT:
+        return self[:-1].append_with(loc)
+
+    def add_to_last_map(self: LocStackT, *locs: Location) -> LocStackT:
+        return self.replace_last(self[-1].add(*locs))
 
 
 @dataclass(frozen=True)
 class LocatedRequest(Request[T]):
-    loc_map: LocMap
+    loc_stack: LocStack
+
+    @property
+    def last_map(self) -> LocMap:
+        return self.loc_stack[-1]
 
 
 def get_type_from_request(request: LocatedRequest) -> TypeHint:
-    return request.loc_map.get_or_raise(TypeHintLoc, CannotProvide).type
+    return request.last_map.get_or_raise(TypeHintLoc, CannotProvide).type
 
 
 def try_normalize_type(tp: TypeHint) -> BaseNormType:
@@ -70,3 +83,10 @@ class StrictCoercionRequest(LocatedRequest[bool]):
 
 class DebugTrailRequest(LocatedRequest[DebugTrail]):
     pass
+
+
+def find_owner_with_field(stack: LocStack) -> Tuple[LocMap, LocMap]:
+    for next_loc_map, prev_loc_map in pairs(reversed(stack)):
+        if next_loc_map.has(FieldLoc):
+            return prev_loc_map, next_loc_map
+    raise ValueError
diff --git a/src/adaptix/_internal/provider/request_filtering.py b/src/adaptix/_internal/provider/request_filtering.py
deleted file mode 100644
index c378dd86..00000000
--- a/src/adaptix/_internal/provider/request_filtering.py
+++ /dev/null
@@ -1,440 +0,0 @@
-import re
-from abc import ABC, abstractmethod
-from copy import copy
-from dataclasses import dataclass
-from inspect import isabstract, isgenerator
-from typing import Any, ClassVar, Iterable, Optional, Pattern, Protocol, Sequence, Tuple, Type, TypeVar, Union
-
-from ..common import TypeHint, VarTuple
-from ..type_tools import (
-    BaseNormType,
-    NormTV,
-    is_bare_generic,
-    is_generic,
-    is_parametrized,
-    is_protocol,
-    is_subclass_soft,
-    normalize_type,
-)
-from ..type_tools.normalize_type import NotSubscribedError
-from .essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
-from .request_cls import FieldLoc, GenericParamLoc, LocatedRequest, Location, TypeHintLoc, try_normalize_type
-
-T = TypeVar('T')
-
-
-class DirectMediator(Protocol):
-    """This is copy of Mediator protocol but without provide_from_next() method"""
-
-    def provide(self, request: Request[T]) -> T:
-        ...
-
-    @property
-    def request_stack(self) -> Sequence[Request[Any]]:
-        ...
-
-
-class RequestChecker(ABC):
-    @abstractmethod
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        """Raise CannotProvide if the request does not meet the conditions"""
-
-    def __or__(self, other: Any) -> 'RequestChecker':
-        if isinstance(other, RequestChecker):
-            return OrRequestChecker([self, other])
-        return NotImplemented
-
-    def __and__(self, other: Any) -> 'RequestChecker':
-        if isinstance(other, RequestChecker):
-            return AndRequestChecker([self, other])
-        return NotImplemented
-
-    def __xor__(self, other: Any) -> 'RequestChecker':
-        if isinstance(other, RequestChecker):
-            return XorRequestChecker(self, other)
-        return NotImplemented
-
-    def __invert__(self) -> 'RequestChecker':
-        return NegRequestChecker(self)
-
-
-class ProviderWithRC(Provider, ABC):
-    @abstractmethod
-    def get_request_checker(self) -> Optional[RequestChecker]:
-        ...
-
-
-class OrRequestChecker(RequestChecker):
-    def __init__(self, request_checkers: Iterable[RequestChecker]):
-        self._request_checkers = request_checkers
-
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        sub_errors = []
-
-        for checker in self._request_checkers:
-            try:
-                checker.check_request(mediator, request)
-            except CannotProvide as e:
-                sub_errors.append(e)
-            else:
-                return
-
-        raise AggregateCannotProvide.make('', exceptions=sub_errors)
-
-
-class AndRequestChecker(RequestChecker):
-    def __init__(self, request_checkers: Iterable[RequestChecker]):
-        self._request_checkers = request_checkers
-
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        for checker in self._request_checkers:
-            checker.check_request(mediator, request)
-
-
-class NegRequestChecker(RequestChecker):
-    def __init__(self, rc: RequestChecker):
-        self._rc = rc
-
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        try:
-            self._rc.check_request(mediator, request)
-        except CannotProvide:
-            return
-        raise CannotProvide
-
-
-class XorRequestChecker(RequestChecker):
-    def __init__(self, left: RequestChecker, right: RequestChecker):
-        self._left = left
-        self._right = right
-
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        exceptions = []
-
-        try:
-            self._left.check_request(mediator, request)
-        except CannotProvide as exc:
-            exceptions.append(exc)
-
-        try:
-            self._right.check_request(mediator, request)
-        except CannotProvide as exc:
-            exceptions.append(exc)
-
-        if len(exceptions) == 0:
-            raise CannotProvide
-
-        if len(exceptions) == 2:
-            raise AggregateCannotProvide.make('', exceptions=exceptions)
-
-
-class LocatedRequestChecker(RequestChecker, ABC):
-    LOCATION: ClassVar[Type[Location]]
-
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide(f'Request must be instance of {LocatedRequest}')
-        if self.LOCATION not in request.loc_map:
-            raise CannotProvide(f'Request location must be instance of {self.LOCATION}')
-        self._check_location(mediator, request.loc_map[self.LOCATION])
-
-    @abstractmethod
-    def _check_location(self, mediator: DirectMediator, loc: Any) -> None:
-        ...
-
-
-@dataclass(frozen=True)
-class ExactFieldNameRC(LocatedRequestChecker):
-    LOCATION = FieldLoc
-    field_id: str
-
-    def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> None:
-        if self.field_id == loc.field_id:
-            return
-        raise CannotProvide(f'field_id must be a {self.field_id!r}')
-
-
-@dataclass(frozen=True)
-class ReFieldNameRC(LocatedRequestChecker):
-    LOCATION = FieldLoc
-    pattern: Pattern[str]
-
-    def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> None:
-        if self.pattern.fullmatch(loc.field_id):
-            return
-
-        raise CannotProvide(f'field_id must be matched by {self.pattern!r}')
-
-
-@dataclass(frozen=True)
-class ExactTypeRC(LocatedRequestChecker):
-    LOCATION = TypeHintLoc
-    norm: BaseNormType
-
-    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> None:
-        if try_normalize_type(loc.type) == self.norm:
-            return
-        raise CannotProvide(f'{loc.type} must be a equal to {self.norm.source}')
-
-
-@dataclass(frozen=True)
-class OriginSubclassRC(LocatedRequestChecker):
-    LOCATION = TypeHintLoc
-    type_: type
-
-    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> None:
-        norm = try_normalize_type(loc.type)
-        if is_subclass_soft(norm.origin, self.type_):
-            return
-        raise CannotProvide(f'{loc.type} must be a subclass of {self.type_}')
-
-
-@dataclass(frozen=True)
-class ExactOriginRC(LocatedRequestChecker):
-    LOCATION = TypeHintLoc
-    origin: Any
-
-    def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> None:
-        if try_normalize_type(loc.type).origin == self.origin:
-            return
-        raise CannotProvide(f'{loc.type} must have origin {self.origin}')
-
-
-class ExactOriginMergedProvider(Provider):
-    def __init__(self, origins_to_providers: Sequence[Tuple[ExactOriginRC, Provider]]):
-        self.origin_to_provider = {
-            request_checker.origin: provider
-            for request_checker, provider in reversed(origins_to_providers)
-        }
-
-    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide(f'Request must be instance of {LocatedRequest}')
-
-        loc = request.loc_map.get_or_raise(
-            TypeHintLoc,
-            lambda: CannotProvide(f'Request location must be instance of {TypeHintLoc}')
-        )
-        norm = try_normalize_type(loc.type)
-        try:
-            provider = self.origin_to_provider[norm.origin]
-        except KeyError:
-            raise CannotProvide from None
-
-        return provider.apply_provider(mediator, request)
-
-
-class RequestStackCutterMediator(DirectMediator):
-    def __init__(self, mediator: DirectMediator, end_offset: int):
-        self._mediator = mediator
-        self._end_offset = end_offset
-
-    def provide(self, request: Request[T]) -> T:
-        return self._mediator.provide(request)
-
-    @property
-    def request_stack(self) -> Sequence[Request[Any]]:
-        return self._mediator.request_stack[:-self._end_offset or None]
-
-
-class ExtraStackMediator(DirectMediator):
-    def __init__(self, mediator: Mediator, extra_stack: Sequence[Request[Any]]):
-        self._mediator = mediator
-        self._extra_stack = extra_stack
-
-    def provide(self, request: Request[T]) -> T:
-        return self._mediator.provide(request, extra_stack=self._extra_stack)
-
-    @property
-    def request_stack(self) -> Sequence[Request[Any]]:
-        return [*self._mediator.request_stack, *self._extra_stack]
-
-
-@dataclass(frozen=True)
-class StackEndRC(RequestChecker):
-    request_checkers: Sequence[RequestChecker]
-
-    def check_request(self, mediator: DirectMediator, request: Request[T]) -> None:
-        stack = mediator.request_stack
-        offset = len(stack) - len(self.request_checkers)
-
-        if offset < 0:
-            raise CannotProvide("Request stack is too small")
-
-        for i, (checker, stack_request) in enumerate(zip(self.request_checkers, stack[offset:]), start=0):
-            checker.check_request(
-                RequestStackCutterMediator(mediator, i),
-                stack_request,
-            )
-
-
-@dataclass(frozen=True)
-class GenericParamRC(LocatedRequestChecker):
-    LOCATION = GenericParamLoc
-    pos: int
-
-    def _check_location(self, mediator: DirectMediator, loc: GenericParamLoc) -> None:
-        if loc.generic_pos == self.pos:
-            return
-        raise CannotProvide(f'Generic param position {loc.generic_pos} must be equal to {self.pos}')
-
-
-class AnyRequestChecker(RequestChecker):
-    def check_request(self, mediator: DirectMediator, request: Request) -> None:
-        return
-
-
-Pred = Union[str, re.Pattern, type, TypeHint, RequestChecker, 'RequestPattern']
-
-
-def _create_non_type_hint_request_checker(pred: Pred) -> Optional[RequestChecker]:
-    if isinstance(pred, str):
-        if pred.isidentifier():
-            return ExactFieldNameRC(pred)  # this is only an optimization
-        return ReFieldNameRC(re.compile(pred))
-
-    if isinstance(pred, re.Pattern):
-        return ReFieldNameRC(pred)
-
-    if isinstance(pred, RequestChecker):
-        return pred
-
-    if isinstance(pred, RequestPattern):
-        return pred.build_request_checker()
-
-    return None
-
-
-def _create_request_checker_by_origin(origin):
-    if is_protocol(origin) or isabstract(origin):
-        return OriginSubclassRC(origin)
-    return ExactOriginRC(origin)
-
-
-def create_request_checker(pred: Pred) -> RequestChecker:
-    result = _create_non_type_hint_request_checker(pred)
-    if result is not None:
-        return result
-
-    try:
-        norm = normalize_type(pred)
-    except NotSubscribedError:
-        return ExactOriginRC(pred)
-    except ValueError:
-        raise ValueError(f'Can not create RequestChecker from {pred}')
-
-    if isinstance(norm, NormTV):
-        raise ValueError(f'Can not create RequestChecker from {pred} type var')
-
-    if is_bare_generic(pred):
-        return _create_request_checker_by_origin(norm.origin)
-
-    if is_generic(pred):
-        raise ValueError(
-            f'Can not create RequestChecker from {pred} generic alias (parametrized generic)'
-        )
-
-    if not is_generic(norm.origin) and not is_parametrized(pred):
-        return _create_request_checker_by_origin(norm.origin)   # this is only an optimization
-    return ExactTypeRC(norm)
-
-
-Pat = TypeVar('Pat', bound='RequestPattern')
-
-
-class RequestPattern:
-    ANY = AnyRequestChecker()
-
-    def __init__(self, stack: VarTuple[RequestChecker]):
-        self._stack = stack
-
-    @classmethod
-    def _from_rc(cls: Type[Pat], rc: RequestChecker) -> Pat:
-        return cls((rc, ))
-
-    def _extend_stack(self: Pat, elements: Iterable[RequestChecker]) -> Pat:
-        self_copy = copy(self)
-        self_copy._stack = self._stack + tuple(elements)  # pylint: disable=protected-access
-        return self_copy
-
-    def __getattr__(self: Pat, item: str) -> Pat:
-        if item.startswith('__') and item.endswith('__'):
-            raise AttributeError
-        return self[item]
-
-    def __getitem__(self: Pat, item: Union[Pred, VarTuple[Pred]]) -> Pat:
-        if isinstance(item, tuple) or isgenerator(item):
-            return self._extend_stack(
-                [OrRequestChecker([self._ensure_request_checker_from_pred(el) for el in item])]
-            )
-        return self._extend_stack([self._ensure_request_checker_from_pred(item)])
-
-    def _ensure_request_checker(self: Pat, other: Union[Pat, RequestChecker]) -> RequestChecker:
-        if isinstance(other, RequestChecker):
-            return other
-        return other.build_request_checker()
-
-    def __or__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self.build_request_checker() | self._ensure_request_checker(other)
-        )
-
-    def __ror__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self._ensure_request_checker(other) | self.build_request_checker()
-        )
-
-    def __and__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self.build_request_checker() & self._ensure_request_checker(other)
-        )
-
-    def __rand__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self._ensure_request_checker(other) & self.build_request_checker()
-        )
-
-    def __xor__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self.build_request_checker() ^ self._ensure_request_checker(other)
-        )
-
-    def __rxor__(self: Pat, other: Union[Pat, RequestChecker]) -> Pat:
-        return self._from_rc(
-            self._ensure_request_checker(other) ^ self.build_request_checker()
-        )
-
-    def __invert__(self: Pat) -> Pat:
-        return self._from_rc(
-            ~self.build_request_checker()
-        )
-
-    def __add__(self: Pat, other: Pat) -> Pat:
-        return self._extend_stack(other._stack)
-
-    def _ensure_request_checker_from_pred(self, pred: Any) -> RequestChecker:
-        if isinstance(pred, RequestPattern):
-            raise TypeError(
-                "Can not use RequestPattern as predicate inside RequestPattern."
-                " If you want to combine several RequestPattern, you can use `+` operator"
-            )
-
-        return create_request_checker(pred)
-
-    def generic_arg(self: Pat, pos: int, pred: Pred) -> Pat:
-        return self._extend_stack(
-            [GenericParamRC(pos) & self._ensure_request_checker_from_pred(pred)]
-        )
-
-    def build_request_checker(self) -> RequestChecker:
-        if len(self._stack) == 0:
-            raise ValueError(
-                'Can not produce RequestChecker from RequestPattern without stack.'
-                ' You need to parametrize P object with predicates.'
-            )
-        if len(self._stack) == 1:
-            return self._stack[0]
-        return StackEndRC(self._stack)
-
-
-P = RequestPattern(stack=())
diff --git a/src/adaptix/_internal/provider/shape_provider.py b/src/adaptix/_internal/provider/shape_provider.py
index 4e9e75f9..27de993a 100644
--- a/src/adaptix/_internal/provider/shape_provider.py
+++ b/src/adaptix/_internal/provider/shape_provider.py
@@ -17,10 +17,10 @@
 from ..model_tools.introspection.named_tuple import get_named_tuple_shape
 from ..model_tools.introspection.typed_dict import get_typed_dict_shape
 from ..provider.essential import CannotProvide, Mediator
+from ..provider.loc_stack_filtering import create_loc_stack_checker
 from ..type_tools.generic_resolver import GenericResolver, MembersStorage
-from .provider_template import ProviderWithAttachableRC
+from .provider_template import ProviderWithAttachableLSC
 from .request_cls import LocatedRequest, TypeHintLoc
-from .request_filtering import create_request_checker
 from .static_provider import StaticProvider, static_provision_action
 
 
@@ -40,7 +40,7 @@ def __init__(self, introspector: ShapeIntrospector):
 
     @static_provision_action
     def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -> InputShape:
-        loc = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide)
+        loc = request.last_map.get_or_raise(TypeHintLoc, CannotProvide)
         try:
             shape = self._introspector(loc.type)
         except IntrospectionImpossible:
@@ -53,7 +53,7 @@ def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -
 
     @static_provision_action
     def _provide_output_shape(self, mediator: Mediator, request: OutputShapeRequest) -> OutputShape:
-        loc = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide)
+        loc = request.last_map.get_or_raise(TypeHintLoc, CannotProvide)
         try:
             shape = self._introspector(loc.type)
         except IntrospectionImpossible:
@@ -92,7 +92,7 @@ def __init__(
 
     @static_provision_action
     def _provide_output_shape(self, mediator: Mediator[OutputShape], request: OutputShapeRequest) -> OutputShape:
-        tp = request.loc_map.get_or_raise(TypeHintLoc, CannotProvide).type
+        tp = request.last_map.get_or_raise(TypeHintLoc, CannotProvide).type
         shape = mediator.provide_from_next()
 
         additional_fields = tuple(
@@ -133,7 +133,7 @@ def __init__(self, mediator: Mediator, initial_request: LocatedRequest[ShapeT]):
     def provide(self) -> ShapeT:
         resolver = GenericResolver(self._get_members)
         members_storage = resolver.get_resolved_members(
-            self._initial_request.loc_map[TypeHintLoc].type
+            self._initial_request.last_map[TypeHintLoc].type
         )
         if members_storage.meta is None:
             raise CannotProvide
@@ -150,7 +150,7 @@ def _get_members(self, tp) -> MembersStorage[str, Optional[ShapeT]]:
             shape = self._mediator.delegating_provide(
                 replace(
                     self._initial_request,
-                    loc_map=self._initial_request.loc_map.add(TypeHintLoc(type=tp)),
+                    loc_stack=self._initial_request.loc_stack.add_to_last_map(TypeHintLoc(type=tp)),
                 )
             )
         except CannotProvide:
@@ -167,7 +167,7 @@ def _get_members(self, tp) -> MembersStorage[str, Optional[ShapeT]]:
 
 
 def provide_generic_resolved_shape(mediator: Mediator, request: LocatedRequest[ShapeT]) -> ShapeT:
-    if not request.loc_map.has(TypeHintLoc):
+    if not request.last_map.has(TypeHintLoc):
         return mediator.delegating_provide(request)
     return ShapeGenericResolver(mediator, request).provide()
 
@@ -175,11 +175,11 @@ def provide_generic_resolved_shape(mediator: Mediator, request: LocatedRequest[S
 T = TypeVar('T')
 
 
-class SimilarShapeProvider(ProviderWithAttachableRC):
+class SimilarShapeProvider(ProviderWithAttachableLSC):
     def __init__(self, target: TypeHint, prototype: TypeHint, for_input: bool = True, for_output: bool = True):
         self._target = target
         self._prototype = prototype
-        self._request_checker = create_request_checker(self._target)
+        self._loc_stack_checker = create_loc_stack_checker(self._target)
         self._for_input = for_input
         self._for_output = for_output
 
@@ -188,11 +188,11 @@ def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -
         if not self._for_input:
             raise CannotProvide
 
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         shape = mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(self._prototype))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(self._prototype)),
             )
         )
         return replace(shape, constructor=self._target)
@@ -202,11 +202,11 @@ def _provide_output_shape(self, mediator: Mediator, request: OutputShapeRequest)
         if not self._for_output:
             raise CannotProvide
 
-        self._request_checker.check_request(mediator, request)
+        self._apply_loc_stack_checker(mediator, request)
         shape = mediator.delegating_provide(
             replace(
                 request,
-                loc_map=request.loc_map.add(TypeHintLoc(self._prototype))
+                loc_stack=request.loc_stack.add_to_last_map(TypeHintLoc(self._prototype)),
             )
         )
         return shape
diff --git a/src/adaptix/_internal/provider/utils.py b/src/adaptix/_internal/provider/utils.py
deleted file mode 100644
index 1315f2e6..00000000
--- a/src/adaptix/_internal/provider/utils.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Sequence
-
-from .essential import Request
-from .request_cls import FieldLoc, LocatedRequest
-
-
-def find_field_request(request_stack: Sequence[Request]) -> LocatedRequest:
-    for parent_request in request_stack:
-        if isinstance(parent_request, LocatedRequest) and parent_request.loc_map.has(FieldLoc):
-            return parent_request
-    raise ValueError('Owner type is not found')
diff --git a/src/adaptix/_internal/retort/base_retort.py b/src/adaptix/_internal/retort/base_retort.py
index 7da67b63..751a8885 100644
--- a/src/adaptix/_internal/retort/base_retort.py
+++ b/src/adaptix/_internal/retort/base_retort.py
@@ -4,7 +4,8 @@
 from ..common import VarTuple
 from ..provider.essential import Mediator, Provider, Request
 from ..utils import Cloneable, ForbiddingDescriptor
-from .mediator import BuiltinMediator, ErrorRepresentor, IntrospectingRecipeSearcher, RecipeSearcher, RecursionResolving
+from .mediator import BuiltinMediator, ErrorRepresentor, RecursionResolver
+from .routing import IntrospectingRecipeSearcher, RecipeSearcher
 
 
 class RetortMeta(ABCMeta):  # inherits from ABCMeta to be compatible with ABC
@@ -67,28 +68,27 @@ def _create_searcher(self, full_recipe: Sequence[Provider]) -> RecipeSearcher:
         return IntrospectingRecipeSearcher(full_recipe)
 
     @abstractmethod
-    def _get_recursion_resolving(self) -> RecursionResolving:
+    def _create_recursion_resolver(self) -> RecursionResolver:
         ...
 
     @abstractmethod
     def _get_error_representor(self) -> ErrorRepresentor:
         ...
 
-    def _create_mediator(self, request_stack: Sequence[Request]) -> Mediator:
-        recursion_resolving = self._get_recursion_resolving()
+    def _create_mediator(self) -> Mediator:
+        recursion_resolver = self._create_recursion_resolver()
         error_representor = self._get_error_representor()
         return BuiltinMediator(
             self._searcher,
-            recursion_resolving,
+            recursion_resolver,
             error_representor,
-            request_stack,
         )
 
-    def _provide_from_recipe(self, request: Request[T], request_stack: Sequence[Request]) -> T:
+    def _provide_from_recipe(self, request: Request[T]) -> T:
         """Process request iterating over the result of _get_full_recipe()
         :param request:
         :return: request result
         :raise CannotProvide: request was not processed
         """
-        mediator = self._create_mediator(request_stack)
+        mediator = self._create_mediator()
         return mediator.provide(request)
diff --git a/src/adaptix/_internal/retort/mediator.py b/src/adaptix/_internal/retort/mediator.py
index e2d85c0b..fb78a139 100644
--- a/src/adaptix/_internal/retort/mediator.py
+++ b/src/adaptix/_internal/retort/mediator.py
@@ -1,51 +1,23 @@
 from abc import ABC, abstractmethod
-from itertools import islice
-from typing import Any, Callable, Dict, Generic, Iterable, List, Sequence, Set, Tuple, Type, TypeVar
+from typing import Any, Dict, Generic, Iterable, Optional, TypeVar
 
-from ..common import TypeHint
-from ..datastructures import ClassDispatcher
-from ..provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
-from ..provider.provider_wrapper import RequestClassDeterminedProvider
-from ..provider.request_filtering import ExactOriginMergedProvider, ExactOriginRC, ProviderWithRC
+from ..provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Request
 from ..utils import add_note
+from .routing import RecipeSearcher
 
 T = TypeVar('T')
 
 
-class StubsRecursionResolver(ABC, Generic[T]):
+class RecursionResolver(ABC, Generic[T]):
     @abstractmethod
-    def create_stub(self, request: Request[T]) -> T:
+    def track_recursion(self, request: Request[T]) -> Optional[Any]:
         ...
 
     @abstractmethod
-    def saturate_stub(self, actual: T, stub: T) -> None:
+    def process_request_result(self, request: Request[T], result: T) -> None:
         ...
 
 
-ProvideCallable = Callable[[Mediator, Request[T]], T]
-SearchResult = Tuple[ProvideCallable[T], int]
-
-
-class RecipeSearcher(ABC):
-    """An object that implements iterating over recipe list.
-
-    An offset of each element must belong to [0; max_offset)
-    """
-
-    @abstractmethod
-    def search_candidates(self, search_offset: int, request: Request[T]) -> Iterable[SearchResult[T]]:
-        ...
-
-    @abstractmethod
-    def get_max_offset(self) -> int:
-        ...
-
-    @abstractmethod
-    def clear_cache(self):
-        ...
-
-
-RecursionResolving = ClassDispatcher[Request, StubsRecursionResolver]
 E = TypeVar('E', bound=Exception)
 
 
@@ -55,7 +27,7 @@ def get_no_provider_description(self, request: Request) -> str:
         ...
 
     @abstractmethod
-    def get_request_context_notes(self, request: Request, request_stack: Sequence[Request]) -> Iterable[str]:
+    def get_request_context_notes(self, request: Request) -> Iterable[str]:
         ...
 
 
@@ -63,57 +35,35 @@ class BuiltinMediator(Mediator):
     def __init__(
         self,
         searcher: RecipeSearcher,
-        recursion_resolving: RecursionResolving,
+        recursion_resolver: RecursionResolver,
         error_representor: ErrorRepresentor,
-        request_stack: Sequence[Request[Any]],
     ):
         self.searcher = searcher
-        self.recursion_resolving = recursion_resolving
+        self.recursion_resolver = recursion_resolver
         self.error_representor = error_representor
 
-        self._request_stack = list(request_stack)
-        self._sent_request: Set[Request] = set()
+        self._current_request: Optional[Request] = None
         self.next_offset = 0
         self.recursion_stubs: Dict[Request, Any] = {}
 
-    def provide(self, request: Request[T], *, extra_stack: Sequence[Request[Any]] = ()) -> T:
-        if request in self._sent_request:
-            if request in self.recursion_stubs:
-                return self.recursion_stubs[request]
-            try:
-                resolver = self._get_resolver(request)
-            except KeyError:
-                raise RecursionError("Infinite recursion has been detected that can not be resolved") from None
-
-            stub = resolver.create_stub(request)
-            self.recursion_stubs[request] = stub
+    def provide(self, request: Request[T]) -> T:
+        stub = self.recursion_resolver.track_recursion(request)
+        if stub is not None:
             return stub
 
-        self._request_stack.extend(extra_stack)
-        self._request_stack.append(request)
-        self._sent_request.add(request)
+        self._current_request = request
         try:
             result = self._provide_non_recursive(request, 0)
         finally:
-            del self._request_stack[-1 - len(extra_stack):]
-            self._sent_request.discard(request)
-
-        if request in self.recursion_stubs:
-            resolver = self._get_resolver(request)
-            stub = self.recursion_stubs.pop(request)
-            resolver.saturate_stub(result, stub)
+            self._current_request = None
 
+        self.recursion_resolver.process_request_result(request, result)
         return result
 
     def provide_from_next(self) -> Any:
-        return self._provide_non_recursive(self._request_stack[-1], self.next_offset)
-
-    @property
-    def request_stack(self) -> Sequence[Request[Any]]:
-        return self._request_stack.copy()
-
-    def _get_resolver(self, request: Request) -> StubsRecursionResolver:
-        return self.recursion_resolving.dispatch(type(request))
+        if self._current_request is None:
+            raise ValueError
+        return self._provide_non_recursive(self._current_request, self.next_offset)
 
     def _provide_non_recursive(self, request: Request[T], search_offset: int) -> T:
         init_next_offset = self.next_offset
@@ -144,130 +94,7 @@ def _provide_non_recursive(self, request: Request[T], search_offset: int) -> T:
         )
 
     def _attach_request_context_note(self, exc: E, request: Request) -> E:
-        notes = self.error_representor.get_request_context_notes(request, self.request_stack)
+        notes = self.error_representor.get_request_context_notes(request)
         for note in notes:
             add_note(exc, note)
         return exc
-
-
-class RawRecipeSearcher(RecipeSearcher):
-    def __init__(self, recipe: Sequence[Provider]):
-        self.recipe = recipe
-
-    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
-        for i, provider in enumerate(
-            islice(self.recipe, search_offset, None),
-            start=search_offset
-        ):
-            yield provider.apply_provider, i + 1
-
-    def clear_cache(self):
-        pass
-
-    def get_max_offset(self) -> int:
-        return len(self.recipe)
-
-
-class Combiner(ABC):
-    @abstractmethod
-    def add_element(self, provider: Provider) -> bool:
-        ...
-
-    @abstractmethod
-    def combine_elements(self) -> Sequence[Provider]:
-        ...
-
-    @abstractmethod
-    def has_elements(self) -> bool:
-        ...
-
-
-class ExactOriginCombiner(Combiner):
-    def __init__(self) -> None:
-        self._combo: List[Tuple[ExactOriginRC, Provider]] = []
-        self._origins: Set[TypeHint] = set()
-
-    def add_element(self, provider: Provider) -> bool:
-        if not isinstance(provider, ProviderWithRC):
-            return False
-        request_checker = provider.get_request_checker()
-        if request_checker is None:
-            return False
-        if not isinstance(request_checker, ExactOriginRC):
-            return False
-        if request_checker.origin in self._origins:
-            return False
-
-        self._combo.append((request_checker, provider))
-        self._origins.add(request_checker.origin)
-        return True
-
-    def combine_elements(self) -> Sequence[Provider]:
-        if len(self._combo) == 1:
-            element = self._combo[0][1]
-            self._combo.clear()
-            self._origins.clear()
-            return [element]
-
-        merged_provider = ExactOriginMergedProvider(self._combo)
-        self._combo.clear()
-        self._origins.clear()
-        return [merged_provider]
-
-    def has_elements(self) -> bool:
-        return bool(self._combo)
-
-
-class IntrospectingRecipeSearcher(RecipeSearcher):
-    def __init__(self, recipe: Sequence[Provider]):
-        self._recipe = recipe
-        self._cls_to_recipe: Dict[Type[Request], Sequence[Provider]] = {}
-
-    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
-        request_cls = type(request)
-        try:
-            sub_recipe = self._cls_to_recipe[request_cls]
-        except KeyError:
-            sub_recipe = self._collect_candidates(request_cls, self._recipe)
-            self._cls_to_recipe[request_cls] = sub_recipe
-
-        for i, provider in enumerate(
-            islice(sub_recipe, search_offset, None),
-            start=search_offset
-        ):
-            yield provider.apply_provider, i + 1
-
-    def _create_combiner(self) -> Combiner:
-        return ExactOriginCombiner()
-
-    def _merge_providers(self, recipe: Sequence[Provider]) -> Sequence[Provider]:
-        combiner = self._create_combiner()
-
-        result: List[Provider] = []
-        for provider in recipe:
-            is_added = combiner.add_element(provider)
-            if not is_added:
-                if combiner.has_elements():
-                    result.extend(combiner.combine_elements())
-                result.append(provider)
-
-        if combiner.has_elements():
-            result.extend(combiner.combine_elements())
-        return result
-
-    def _collect_candidates(self, request_cls: Type[Request], recipe: Sequence[Provider]) -> Sequence[Provider]:
-        candidates = [
-            provider
-            for provider in recipe
-            if (
-                not isinstance(provider, RequestClassDeterminedProvider)
-                or provider.maybe_can_process_request_cls(request_cls)
-            )
-        ]
-        return self._merge_providers(candidates)
-
-    def clear_cache(self):
-        self._cls_to_recipe = {}
-
-    def get_max_offset(self) -> int:
-        return len(self._recipe)
diff --git a/src/adaptix/_internal/retort/operating_retort.py b/src/adaptix/_internal/retort/operating_retort.py
index 9d1954da..44e68d5c 100644
--- a/src/adaptix/_internal/retort/operating_retort.py
+++ b/src/adaptix/_internal/retort/operating_retort.py
@@ -1,13 +1,13 @@
 from abc import ABC
-from typing import Iterable, List, Mapping, Optional, Sequence, Type, TypeVar
+from typing import Any, Dict, Iterable, List, Mapping, Optional, Type
 
+from ..common import VarTuple
 from ..morphing.request_cls import DumperRequest, LoaderRequest
 from ..provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
-from ..provider.request_cls import FieldLoc, LocatedRequest
-from ..provider.utils import find_field_request
+from ..provider.request_cls import FieldLoc, LocatedRequest, LocMap, TypeHintLoc, find_owner_with_field
 from ..utils import copy_exception_dunders, with_module
 from .base_retort import BaseRetort
-from .mediator import ErrorRepresentor, RecursionResolving, StubsRecursionResolver
+from .mediator import ErrorRepresentor, RecursionResolver, T
 
 
 class FuncWrapper:
@@ -20,12 +20,26 @@ def set_func(self, func):
         self.__call__ = func.__call__
 
 
-class FuncRecursionResolver(StubsRecursionResolver):
-    def create_stub(self, request):
-        return FuncWrapper()
+class MorphingRecursionResolver(RecursionResolver):
+    REQUEST_CLASSES: VarTuple[Type[LocatedRequest]] = (LoaderRequest, DumperRequest)
 
-    def saturate_stub(self, actual, stub) -> None:
-        stub.set_func(actual)
+    def __init__(self) -> None:
+        self._loc_map_to_stub: Dict[LocMap, FuncWrapper] = {}
+
+    def track_recursion(self, request: Request[T]) -> Optional[Any]:
+        if not isinstance(request, self.REQUEST_CLASSES):
+            return None
+
+        if request.loc_stack.count(request.last_map) == 1:
+            return None
+
+        stub = FuncWrapper()
+        self._loc_map_to_stub[request.last_map] = stub
+        return stub
+
+    def process_request_result(self, request: Request[T], result: T) -> None:
+        if isinstance(request, self.REQUEST_CLASSES) and request.last_map in self._loc_map_to_stub:
+            self._loc_map_to_stub.pop(request.last_map).set_func(result)
 
 
 @with_module('adaptix')
@@ -34,9 +48,6 @@ def __init__(self, message: str):
         self.message = message
 
 
-T = TypeVar('T')
-
-
 class BuiltinErrorRepresentor(ErrorRepresentor):
     _NO_PROVIDER_DESCRIPTION_MAP: Mapping[Type[Request], str] = {
         LoaderRequest: "There is no provider that can create specified loader",
@@ -51,40 +62,43 @@ def get_no_provider_description(self, request: Request) -> str:
 
     _LOC_KEYS_ORDER = {fld: idx for idx, fld in enumerate(['type', 'field_id'])}
 
-    def get_request_context_notes(self, request: Request, request_stack: Sequence[Request]) -> Iterable[str]:
-        if isinstance(request, LocatedRequest):
-            location_desc = ', '.join(
-                f'{key}={value!r}'
-                for key, value in sorted(
-                    (
-                        (key, value)
-                        for loc in request.loc_map.values()
-                        for key, value in vars(loc).items()
-                    ),
-                    key=lambda item: self._LOC_KEYS_ORDER.get(item[0], 1000),
-                )
+    def get_request_context_notes(self, request: Request) -> Iterable[str]:
+        if not isinstance(request, LocatedRequest):
+            return
+
+        location_desc = ', '.join(
+            f'{key}={value!r}'
+            for key, value in sorted(
+                (
+                    (key, value)
+                    for loc in request.last_map.values()
+                    for key, value in vars(loc).items()
+                ),
+                key=lambda item: self._LOC_KEYS_ORDER.get(item[0], 1000),
             )
-            if location_desc:
-                yield f'Location: {location_desc}'
+        )
+        if location_desc:
+            yield f'Location: {location_desc}'
 
-            try:
-                field_request = find_field_request(request_stack)
-            except ValueError:
-                pass
-            else:
-                field_loc = field_request.loc_map[FieldLoc]
-                yield f'Exception was raised while processing field {field_loc.field_id!r} of {field_loc.owner_type}'
+        try:
+            owner_loc_map, field_loc_map = find_owner_with_field(request.loc_stack)
+        except ValueError:
+            pass
+        else:
+            owner_type = owner_loc_map[TypeHintLoc].type
+            field_id = field_loc_map[FieldLoc].field_id
+            yield f'Exception was raised while processing field {field_id!r} of {owner_type}'
 
 
 class OperatingRetort(BaseRetort, Provider, ABC):
     """A retort that can operate as Retort but have no predefined providers and no high-level user interface"""
 
     def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        return self._provide_from_recipe(request, mediator.request_stack[:-1])
+        return self._provide_from_recipe(request)
 
     def _facade_provide(self, request: Request[T], *, error_message: str) -> T:
         try:
-            return self._provide_from_recipe(request, [])
+            return self._provide_from_recipe(request)
         except CannotProvide as e:
             cause = self._get_exception_cause(e)
             raise NoSuitableProvider(error_message) from cause
@@ -110,13 +124,8 @@ def _extract_demonstrative_exc(self, exc: AggregateCannotProvide) -> Optional[Ca
         copy_exception_dunders(source=exc, target=new_exc)
         return new_exc
 
-    def _get_recursion_resolving(self) -> RecursionResolving:
-        return RecursionResolving(
-            {
-                LoaderRequest: FuncRecursionResolver(),
-                DumperRequest: FuncRecursionResolver(),
-            }
-        )
+    def _create_recursion_resolver(self) -> RecursionResolver:
+        return MorphingRecursionResolver()
 
     def _get_error_representor(self) -> ErrorRepresentor:
         return BuiltinErrorRepresentor()
diff --git a/src/adaptix/_internal/retort/routing.py b/src/adaptix/_internal/retort/routing.py
new file mode 100644
index 00000000..ecc3a1a5
--- /dev/null
+++ b/src/adaptix/_internal/retort/routing.py
@@ -0,0 +1,177 @@
+from abc import ABC, abstractmethod
+from itertools import islice
+from typing import Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type, TypeVar
+
+from ..common import TypeHint
+from ..provider.essential import CannotProvide, Mediator, Provider, Request
+from ..provider.loc_stack_filtering import ExactOriginLSC
+from ..provider.provider_wrapper import ProviderWithLSC, RequestClassDeterminedProvider
+from ..provider.request_cls import LocatedRequest, TypeHintLoc, try_normalize_type
+
+T = TypeVar('T')
+ProvideCallable = Callable[[Mediator, Request[T]], T]
+SearchResult = Tuple[ProvideCallable[T], int]
+
+
+class RecipeSearcher(ABC):
+    """An object that implements iterating over recipe list.
+
+    An offset of each element must belong to [0; max_offset)
+    """
+
+    @abstractmethod
+    def search_candidates(self, search_offset: int, request: Request[T]) -> Iterable[SearchResult[T]]:
+        ...
+
+    @abstractmethod
+    def get_max_offset(self) -> int:
+        ...
+
+    @abstractmethod
+    def clear_cache(self):
+        ...
+
+
+class RawRecipeSearcher(RecipeSearcher):
+    def __init__(self, recipe: Sequence[Provider]):
+        self.recipe = recipe
+
+    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
+        for i, provider in enumerate(
+            islice(self.recipe, search_offset, None),
+            start=search_offset
+        ):
+            yield provider.apply_provider, i + 1
+
+    def clear_cache(self):
+        pass
+
+    def get_max_offset(self) -> int:
+        return len(self.recipe)
+
+
+class Combiner(ABC):
+    @abstractmethod
+    def add_element(self, provider: Provider) -> bool:
+        ...
+
+    @abstractmethod
+    def combine_elements(self) -> Sequence[Provider]:
+        ...
+
+    @abstractmethod
+    def has_elements(self) -> bool:
+        ...
+
+
+class ExactOriginCombiner(Combiner):
+    def __init__(self) -> None:
+        self._combo: List[Tuple[ExactOriginLSC, Provider]] = []
+        self._origins: Set[TypeHint] = set()
+
+    def add_element(self, provider: Provider) -> bool:
+        if not isinstance(provider, ProviderWithLSC):
+            return False
+        loc_stack_checker = provider.get_loc_stack_checker()
+        if not isinstance(loc_stack_checker, ExactOriginLSC):
+            return False
+        if loc_stack_checker.origin in self._origins:
+            return False
+
+        self._combo.append((loc_stack_checker, provider))
+        self._origins.add(loc_stack_checker.origin)
+        return True
+
+    def combine_elements(self) -> Sequence[Provider]:
+        if len(self._combo) == 1:
+            element = self._combo[0][1]
+            self._combo.clear()
+            self._origins.clear()
+            return [element]
+
+        merged_provider = ExactOriginMergedProvider(self._combo)
+        self._combo.clear()
+        self._origins.clear()
+        return [merged_provider]
+
+    def has_elements(self) -> bool:
+        return bool(self._combo)
+
+
+class ExactOriginMergedProvider(Provider):
+    def __init__(self, origins_to_providers: Sequence[Tuple[ExactOriginLSC, Provider]]):
+        self.origin_to_provider = {
+            loc_stack_checker.origin: provider
+            for loc_stack_checker, provider in reversed(origins_to_providers)
+        }
+
+    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
+        if not isinstance(request, LocatedRequest):
+            raise CannotProvide(f'Request must be instance of {LocatedRequest}')
+
+        loc = request.last_map.get_or_raise(
+            TypeHintLoc,
+            lambda: CannotProvide(f'Request location must be instance of {TypeHintLoc}')
+        )
+        norm = try_normalize_type(loc.type)
+        try:
+            provider = self.origin_to_provider[norm.origin]
+        except KeyError:
+            raise CannotProvide from None
+
+        return provider.apply_provider(mediator, request)
+
+
+class IntrospectingRecipeSearcher(RecipeSearcher):
+    def __init__(self, recipe: Sequence[Provider]):
+        self._recipe = recipe
+        self._cls_to_recipe: Dict[Type[Request], Sequence[Provider]] = {}
+
+    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
+        request_cls = type(request)
+        try:
+            sub_recipe = self._cls_to_recipe[request_cls]
+        except KeyError:
+            sub_recipe = self._collect_candidates(request_cls, self._recipe)
+            self._cls_to_recipe[request_cls] = sub_recipe
+
+        for i, provider in enumerate(
+            islice(sub_recipe, search_offset, None),
+            start=search_offset
+        ):
+            yield provider.apply_provider, i + 1
+
+    def _create_combiner(self) -> Combiner:
+        return ExactOriginCombiner()
+
+    def _merge_providers(self, recipe: Sequence[Provider]) -> Sequence[Provider]:
+        combiner = self._create_combiner()
+
+        result: List[Provider] = []
+        for provider in recipe:
+            is_added = combiner.add_element(provider)
+            if not is_added:
+                if combiner.has_elements():
+                    result.extend(combiner.combine_elements())
+                result.append(provider)
+
+        if combiner.has_elements():
+            result.extend(combiner.combine_elements())
+        return result
+
+    def _collect_candidates(self, request_cls: Type[Request], recipe: Sequence[Provider]) -> Sequence[Provider]:
+        candidates = [
+            provider
+            for provider in recipe
+            if (
+                not isinstance(provider, RequestClassDeterminedProvider)
+                or provider.maybe_can_process_request_cls(request_cls)
+            )
+        ]
+        return self._merge_providers(candidates)
+
+    def clear_cache(self):
+        self._cls_to_recipe = {}
+
+    def get_max_offset(self) -> int:
+        return len(self._recipe)
diff --git a/src/adaptix/load_error.py b/src/adaptix/load_error.py
index c16b7348..01ade039 100644
--- a/src/adaptix/load_error.py
+++ b/src/adaptix/load_error.py
@@ -2,14 +2,17 @@
     AggregateLoadError,
     BadVariantError,
     DatetimeFormatMismatch,
+    DuplicatedValues,
     ExcludedTypeLoadError,
     ExtraFieldsError,
     ExtraItemsError,
     LoadError,
     LoadExceptionGroup,
     MsgError,
+    MultipleBadVariant,
     NoRequiredFieldsError,
     NoRequiredItemsError,
+    OutOfRange,
     TypeLoadError,
     UnionLoadError,
     ValidationError,
@@ -32,4 +35,7 @@
     'ValidationError',
     'BadVariantError',
     'DatetimeFormatMismatch',
+    'DuplicatedValues',
+    'OutOfRange',
+    'MultipleBadVariant'
 )
diff --git a/src/adaptix/provider/__init__.py b/src/adaptix/provider/__init__.py
index bf9f473a..39cb346f 100644
--- a/src/adaptix/provider/__init__.py
+++ b/src/adaptix/provider/__init__.py
@@ -1,6 +1,6 @@
 from adaptix._internal.provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
+from adaptix._internal.provider.loc_stack_filtering import LocStackPattern, P, create_loc_stack_checker
 from adaptix._internal.provider.provider_wrapper import Chain
-from adaptix._internal.provider.request_filtering import P, RequestPattern, create_request_checker
 
 __all__ = (
     'CannotProvide',
@@ -9,7 +9,7 @@
     'Provider',
     'Request',
     'Chain',
-    'RequestPattern',
+    'LocStackPattern',
     'P',
-    'create_request_checker',
+    'create_loc_stack_checker',
 )


Test patch:
diff --git a/requirements/raw/test_extra_new.txt b/requirements/raw/test_extra_new.txt
index 4956f5a3..86664b78 100644
--- a/requirements/raw/test_extra_new.txt
+++ b/requirements/raw/test_extra_new.txt
@@ -1,2 +1,3 @@
 -r test_extra_none.txt
 attrs==23.1.0
+sqlalchemy==2.0.25
diff --git a/requirements/raw/test_extra_none.txt b/requirements/raw/test_extra_none.txt
index ca7fbb4e..767f0019 100644
--- a/requirements/raw/test_extra_none.txt
+++ b/requirements/raw/test_extra_none.txt
@@ -2,7 +2,7 @@ pytest==7.4.2
 -e ./tests/tests_helpers
 
 phonenumberslite==8.13.26
-sqlalchemy==2.0.23
+sqlalchemy>=2.0.0
 
 dirty-equals==0.7.1.post0
 
diff --git a/requirements/raw/test_extra_old.txt b/requirements/raw/test_extra_old.txt
index 1029b111..b314752c 100644
--- a/requirements/raw/test_extra_old.txt
+++ b/requirements/raw/test_extra_old.txt
@@ -1,2 +1,3 @@
 -r test_extra_none.txt
 attrs==21.3.0
+sqlalchemy==2.0.0
diff --git a/requirements/test_extra_new.txt b/requirements/test_extra_new.txt
index eeb55d5f..65fc94c8 100644
--- a/requirements/test_extra_new.txt
+++ b/requirements/test_extra_new.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/test_extra_new.txt --strip-extras requirements/raw/test_extra_new.txt
@@ -26,8 +26,10 @@ pytest==7.4.2
     # via -r requirements/raw/test_extra_none.txt
 pytz==2023.3.post1
     # via dirty-equals
-sqlalchemy==2.0.23
-    # via -r requirements/raw/test_extra_none.txt
+sqlalchemy==2.0.25
+    # via
+    #   -r requirements/raw/test_extra_new.txt
+    #   -r requirements/raw/test_extra_none.txt
 typing-extensions==4.9.0
     # via
     #   -r requirements/raw/test_extra_none.txt
diff --git a/requirements/test_extra_none.txt b/requirements/test_extra_none.txt
index 4e202537..7bf3debf 100644
--- a/requirements/test_extra_none.txt
+++ b/requirements/test_extra_none.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/test_extra_none.txt --strip-extras requirements/raw/test_extra_none.txt
diff --git a/requirements/test_extra_old.txt b/requirements/test_extra_old.txt
index ef21d7a6..d1977c94 100644
--- a/requirements/test_extra_old.txt
+++ b/requirements/test_extra_old.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=requirements/test_extra_old.txt --strip-extras requirements/raw/test_extra_old.txt
@@ -26,8 +26,10 @@ pytest==7.4.2
     # via -r requirements/raw/test_extra_none.txt
 pytz==2023.3.post1
     # via dirty-equals
-sqlalchemy==2.0.23
-    # via -r requirements/raw/test_extra_none.txt
+sqlalchemy==2.0.0
+    # via
+    #   -r requirements/raw/test_extra_none.txt
+    #   -r requirements/raw/test_extra_old.txt
 typing-extensions==4.9.0
     # via
     #   -r requirements/raw/test_extra_none.txt
diff --git a/tests/unit/model_tools/introspection/test_sqlalchemy.py b/tests/unit/model_tools/introspection/test_sqlalchemy.py
new file mode 100644
index 00000000..b025a2c8
--- /dev/null
+++ b/tests/unit/model_tools/introspection/test_sqlalchemy.py
@@ -0,0 +1,361 @@
+from typing import List, Optional
+from unittest.mock import ANY
+
+from sqlalchemy import Column, ForeignKey, Integer, String, Table
+from sqlalchemy.orm import Mapped, mapped_column, registry, relationship
+
+from adaptix._internal.model_tools.definitions import (
+    DefaultFactory,
+    DefaultValue,
+    InputField,
+    InputShape,
+    NoDefault,
+    OutputField,
+    OutputShape,
+    Param,
+    ParamKind,
+    Shape,
+    create_attr_accessor,
+)
+from adaptix._internal.model_tools.introspection.sqlalchemy_tables import get_sqlalchemy_shape
+
+mapper_registry = registry()
+
+
+def default_factory():
+    return 2
+
+
+@mapper_registry.mapped
+class Declarative:
+    __tablename__ = "DeclarativeModel"
+
+    id: Mapped[int] = mapped_column(primary_key=True)
+    text: Mapped[str]
+    nullable_field: Mapped[Optional[int]]
+    field_with_default: Mapped[int] = mapped_column(default=2)
+    field_with_default_factory: Mapped[int] = mapped_column(default=default_factory)
+    field_with_default_context_factory: Mapped[int] = mapped_column(default=lambda ctx: 2)
+    field_with_old_syntax = Column(String(), nullable=False)
+    nullable_field_with_old_syntax = Column(Integer(), nullable=True)
+
+    parent_id: Mapped[Optional[int]] = mapped_column(ForeignKey("Imperative.id"))
+    parent: Mapped[Optional["Imperative"]] = relationship(
+        "Imperative", back_populates="children"
+    )
+
+
+imperative_table = Table(
+    "Imperative",
+    mapper_registry.metadata,
+    Column("id", Integer, primary_key=True)
+)
+
+
+class Imperative:
+    pass
+
+
+mapper_registry.map_imperatively(
+    Imperative,
+    imperative_table,
+    properties={
+        "children": relationship(Declarative, back_populates="parent")
+    }
+)
+
+
+def test_shape_getter():
+    assert (
+        get_sqlalchemy_shape(Declarative)
+        ==
+        Shape(
+            input=InputShape(
+                constructor=Declarative,
+                kwargs=None,
+                fields=(
+                    InputField(
+                        type=Mapped[int],
+                        id="id",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[str],
+                        id="text",
+                        default=NoDefault(),
+                        is_required=True,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[Optional[int]],
+                        id="nullable_field",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[int],
+                        id="field_with_default",
+                        default=DefaultValue(2),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[int],
+                        id="field_with_default_factory",
+                        default=DefaultFactory(default_factory),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[int],
+                        id="field_with_default_context_factory",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=str,
+                        id="field_with_old_syntax",
+                        default=NoDefault(),
+                        is_required=True,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Optional[int],
+                        id="nullable_field_with_old_syntax",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[Optional[int]],
+                        id="parent_id",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=Mapped[Optional[Imperative]],
+                        id="parent",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=Declarative.parent.property
+                    ),
+                ),
+                overriden_types=frozenset(),
+                params=(
+                    Param(
+                        field_id='id',
+                        name='id',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='text',
+                        name='text',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='nullable_field',
+                        name='nullable_field',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='field_with_default',
+                        name='field_with_default',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='field_with_default_factory',
+                        name='field_with_default_factory',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='field_with_default_context_factory',
+                        name='field_with_default_context_factory',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='field_with_old_syntax',
+                        name='field_with_old_syntax',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='nullable_field_with_old_syntax',
+                        name='nullable_field_with_old_syntax',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='parent_id',
+                        name='parent_id',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='parent',
+                        name='parent',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                )
+            ),
+            output=OutputShape(
+                fields=(
+                    OutputField(
+                        type=Mapped[int],
+                        id="id",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('id', is_required=True),
+                    ),
+                    OutputField(
+                        type=Mapped[str],
+                        id="text",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('text', is_required=True),
+                    ),
+                    OutputField(
+                        type=Mapped[Optional[int]],
+                        id="nullable_field",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('nullable_field', is_required=False),
+                    ),
+                    OutputField(
+                        type=Mapped[int],
+                        id="field_with_default",
+                        default=DefaultValue(2),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('field_with_default', is_required=True),
+                    ),
+                    OutputField(
+                        type=Mapped[int],
+                        id="field_with_default_factory",
+                        default=DefaultFactory(default_factory),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('field_with_default_factory', is_required=True),
+                    ),
+                    OutputField(
+                        type=Mapped[int],
+                        id="field_with_default_context_factory",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('field_with_default_context_factory', is_required=True),
+                    ),
+                    OutputField(
+                        type=str,
+                        id="field_with_old_syntax",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('field_with_old_syntax', is_required=True),
+                    ),
+                    OutputField(
+                        type=Optional[int],
+                        id="nullable_field_with_old_syntax",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('nullable_field_with_old_syntax', is_required=False),
+                    ),
+                    OutputField(
+                        type=Mapped[Optional[int]],
+                        id="parent_id",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('parent_id', is_required=False),
+                    ),
+                    OutputField(
+                        type=Mapped[Optional[Imperative]],
+                        id="parent",
+                        default=NoDefault(),
+                        metadata={},
+                        original=Declarative.parent.property,
+                        accessor=create_attr_accessor('parent', is_required=False),
+                    ),
+                ),
+                overriden_types=frozenset()
+            )
+        )
+    )
+
+    assert (
+        get_sqlalchemy_shape(Imperative)
+        ==
+        Shape(
+            input=InputShape(
+                constructor=Imperative,
+                kwargs=None,
+                fields=(
+                    InputField(
+                        type=int,
+                        id="id",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                    InputField(
+                        type=List[Declarative],
+                        id="children",
+                        default=NoDefault(),
+                        is_required=False,
+                        metadata={},
+                        original=ANY
+                    ),
+                ),
+                params=(
+                    Param(
+                        field_id='id',
+                        name='id',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                    Param(
+                        field_id='children',
+                        name='children',
+                        kind=ParamKind.KW_ONLY,
+                    ),
+                ),
+                overriden_types=frozenset()
+            ),
+            output=OutputShape(
+                fields=(
+                    OutputField(
+                        type=int,
+                        id="id",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('id', is_required=True),
+                    ),
+                    OutputField(
+                        type=List[Declarative],
+                        id="children",
+                        default=NoDefault(),
+                        metadata={},
+                        original=ANY,
+                        accessor=create_attr_accessor('children', is_required=True),
+                    ),
+                ),
+                overriden_types=frozenset()
+            )
+        )
+    )
diff --git a/tests/unit/morphing/model/shape_provider/local_helpers.py b/tests/unit/morphing/model/shape_provider/local_helpers.py
index 67913ac0..22d651d2 100644
--- a/tests/unit/morphing/model/shape_provider/local_helpers.py
+++ b/tests/unit/morphing/model/shape_provider/local_helpers.py
@@ -6,7 +6,7 @@
 
 from adaptix import Retort, TypeHint
 from adaptix._internal.feature_requirement import HAS_ATTRS_PKG, HAS_PY_311
-from adaptix._internal.provider.request_cls import LocMap, TypeHintLoc
+from adaptix._internal.provider.request_cls import LocMap, LocStack, TypeHintLoc
 from adaptix._internal.provider.shape_provider import (
     InputShapeRequest,
     OutputShapeRequest,
@@ -16,18 +16,18 @@
 
 def assert_fields_types(tp: TypeHint, expected: Mapping[str, TypeHint]) -> None:
     retort = Retort()
-    mediator = retort._create_mediator(request_stack=())
+    mediator = retort._create_mediator()
 
     input_shape = provide_generic_resolved_shape(
         mediator,
-        InputShapeRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+        InputShapeRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
     )
     input_field_types = {field.id: field.type for field in input_shape.fields}
     assert input_field_types == expected
 
     output_shape = provide_generic_resolved_shape(
         mediator,
-        OutputShapeRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+        OutputShapeRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
     )
     output_field_types = {field.id: field.type for field in output_shape.fields}
     assert output_field_types == expected
diff --git a/tests/unit/morphing/model/shape_provider/test_generic_resolving.py b/tests/unit/morphing/model/shape_provider/test_generic_resolving.py
index 69921d06..5b2b9802 100644
--- a/tests/unit/morphing/model/shape_provider/test_generic_resolving.py
+++ b/tests/unit/morphing/model/shape_provider/test_generic_resolving.py
@@ -14,7 +14,7 @@
     HAS_TV_TUPLE,
     IS_PYPY,
 )
-from adaptix._internal.provider.request_cls import LocMap, TypeHintLoc
+from adaptix._internal.provider.request_cls import LocMap, LocStack, TypeHintLoc
 from adaptix._internal.provider.shape_provider import (
     InputShapeRequest,
     OutputShapeRequest,
@@ -256,18 +256,18 @@ class Child(Parent1[int, bool], Parent2[str, bytes], Generic[T7]):
 )
 def test_not_a_model(tp):
     retort = Retort()
-    mediator = retort._create_mediator(request_stack=())
+    mediator = retort._create_mediator()
 
     with pytest.raises(CannotProvide):
         provide_generic_resolved_shape(
             mediator,
-            InputShapeRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+            InputShapeRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
         )
 
     with pytest.raises(CannotProvide):
         provide_generic_resolved_shape(
             mediator,
-            OutputShapeRequest(loc_map=LocMap(TypeHintLoc(type=tp))),
+            OutputShapeRequest(loc_stack=LocStack(LocMap(TypeHintLoc(type=tp)))),
         )
 
 
diff --git a/tests/unit/morphing/name_layout/test_provider.py b/tests/unit/morphing/name_layout/test_provider.py
index ef50d6e4..45588c41 100644
--- a/tests/unit/morphing/name_layout/test_provider.py
+++ b/tests/unit/morphing/name_layout/test_provider.py
@@ -62,9 +62,9 @@
 )
 from adaptix._internal.morphing.name_layout.provider import BuiltinNameLayoutProvider
 from adaptix._internal.morphing.request_cls import DumperRequest, LoaderRequest
+from adaptix._internal.provider.loc_stack_filtering import P
 from adaptix._internal.provider.provider_template import ValueProvider
-from adaptix._internal.provider.request_cls import LocMap, TypeHintLoc
-from adaptix._internal.provider.request_filtering import AnyRequestChecker
+from adaptix._internal.provider.request_cls import LocMap, LocStack, TypeHintLoc
 from adaptix._internal.provider.shape_provider import InputShapeRequest, OutputShapeRequest
 
 
@@ -175,23 +175,23 @@ def make_layouts(
     retort.get_loader(Stub)
     retort.get_dumper(Stub)
     inp_request = InputNameLayoutRequest(
-        loc_map=loc_map,
+        loc_stack=LocStack(loc_map),
         shape=input_shape,
     )
     out_request = OutputNameLayoutRequest(
-        loc_map=loc_map,
+        loc_stack=LocStack(loc_map),
         shape=output_shape,
     )
     inp_name_layout = retort.provide(inp_request)
     out_name_layout = retort.provide(out_request)
     retort.provide(
         LoaderRequest(
-            loc_map=loc_map,
+            loc_stack=LocStack(loc_map),
         )
     )
     retort.provide(
         DumperRequest(
-            loc_map=loc_map,
+            loc_stack=LocStack(loc_map),
         )
     )
     return Layouts(inp_name_layout, out_name_layout)
@@ -200,7 +200,7 @@ def make_layouts(
 DEFAULT_NAME_MAPPING = name_mapping(
     chain=None,
     skip=(),
-    only=AnyRequestChecker(),
+    only=P.ANY,
     map={},
     trim_trailing_underscore=True,
     name_style=None,
@@ -325,7 +325,7 @@ def test_name_filtering():
     assert_flat_name_mapping(
         name_mapping(
             skip=['a', 'xxx'],
-            only=AnyRequestChecker(),
+            only=P.ANY,
             map={},
         ),
         {
@@ -349,7 +349,7 @@ def test_name_filtering():
     assert_flat_name_mapping(
         name_mapping(
             skip=['b'],
-            only=AnyRequestChecker(),
+            only=P.ANY,
             map={},
         ),
         {
diff --git a/tests/unit/morphing/test_dict_provider.py b/tests/unit/morphing/test_dict_provider.py
index 8670feba..7ed1de8a 100644
--- a/tests/unit/morphing/test_dict_provider.py
+++ b/tests/unit/morphing/test_dict_provider.py
@@ -1,13 +1,15 @@
 import collections.abc
-from typing import Dict
+from collections import defaultdict
+from typing import DefaultDict, Dict, List
 
 import pytest
 from tests_helpers import TestRetort, raises_exc, with_trail
 
-from adaptix import DebugTrail, dumper, loader
+from adaptix import DebugTrail, default_dict, dumper, loader
 from adaptix._internal.compat import CompatExceptionGroup
 from adaptix._internal.morphing.concrete_provider import STR_LOADER_PROVIDER
-from adaptix._internal.morphing.dict_provider import DictProvider
+from adaptix._internal.morphing.dict_provider import DefaultDictProvider, DictProvider
+from adaptix._internal.morphing.iterable_provider import IterableProvider
 from adaptix._internal.morphing.load_error import AggregateLoadError
 from adaptix._internal.struct_trail import ItemKey
 from adaptix.load_error import TypeLoadError
@@ -24,8 +26,10 @@ def retort():
     return TestRetort(
         recipe=[
             DictProvider(),
+            DefaultDictProvider(),
             STR_LOADER_PROVIDER,
             dumper(str, string_dumper),
+            IterableProvider()
         ]
     )
 
@@ -190,3 +194,39 @@ def test_dumping(retort, debug_trail):
             ),
             lambda: dumper_({'a': 'b', 0: 'd'}),
         )
+
+
+def test_defaultdict_loading(retort, strict_coercion, debug_trail):
+    loader_ = retort.replace(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+    ).get_loader(
+        DefaultDict[str, str],
+    )
+
+    assert loader_({'a': 'b', 'c': 'd'}) == defaultdict(None, {'a': 'b', 'c': 'd'})
+
+
+def test_defaultdict_loader(retort, strict_coercion, debug_trail):
+    df = list
+    loader_ = retort.replace(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+    ).extend(
+        recipe=[
+            default_dict(defaultdict, default_factory=df),
+        ]
+    ).get_loader(DefaultDict[str, List[str]])
+
+    assert loader_({'a': ['b', 'c']}) == defaultdict(list, {'a': ['b', 'c']})
+    assert loader_({'a': ['b', 'c']}).default_factory == df
+
+
+def test_defaultdict_dumping(retort, debug_trail):
+    dumper_ = retort.replace(
+        debug_trail=debug_trail,
+    ).get_dumper(
+        DefaultDict[str, str],
+    )
+
+    assert dumper_(defaultdict(None, {'a': 'b', 'c': 'd'})) == {'a': 'b', 'c': 'd'}
diff --git a/tests/unit/morphing/test_enum_provider.py b/tests/unit/morphing/test_enum_provider.py
index 5422c344..3849a2ce 100644
--- a/tests/unit/morphing/test_enum_provider.py
+++ b/tests/unit/morphing/test_enum_provider.py
@@ -1,10 +1,27 @@
-from enum import Enum, IntEnum
+from enum import Enum, Flag, IntEnum, auto
+from typing import Iterable, Mapping, Union
 
 import pytest
-from tests_helpers import TestRetort, raises_exc
-
-from adaptix import dumper, enum_by_value, loader
+from tests_helpers import TestRetort, parametrize_bool, raises_exc, with_cause, with_notes
+
+from adaptix import (
+    CannotProvide,
+    NameStyle,
+    NoSuitableProvider,
+    Retort,
+    dumper,
+    enum_by_value,
+    flag_by_member_names,
+    loader,
+)
 from adaptix._internal.morphing.enum_provider import EnumExactValueProvider, EnumNameProvider
+from adaptix._internal.morphing.load_error import (
+    DuplicatedValues,
+    ExcludedTypeLoadError,
+    MultipleBadVariant,
+    OutOfRange,
+    TypeLoadError,
+)
 from adaptix.load_error import BadVariantError, MsgError
 
 
@@ -24,6 +41,23 @@ def _missing_(cls, value: object) -> 'MyEnumWithMissingHook':
         raise ValueError
 
 
+class FlagEnum(Flag):
+    CASE_ONE = auto()
+    CASE_TWO = auto()
+    CASE_THREE = CASE_ONE | CASE_TWO
+    CASE_FOUR = auto()
+    CASE_EIGHT = auto()
+
+
+class FlagEnumWithSkippedBit(Flag):
+    CASE_ONE = 1
+    CASE_FOUR = 4
+
+
+class FlagEnumWithNegativeValue(Flag):
+    CASE_ONE = -1
+
+
 def test_name_provider(strict_coercion, debug_trail):
     retort = TestRetort(
         strict_coercion=strict_coercion,
@@ -34,7 +68,6 @@ def test_name_provider(strict_coercion, debug_trail):
     )
 
     loader = retort.get_loader(MyEnum)
-
     assert loader("V1") == MyEnum.V1
 
     raises_exc(
@@ -152,3 +185,231 @@ def test_value_provider(strict_coercion, debug_trail):
     enum_dumper = retort.get_dumper(MyEnum)
 
     assert enum_dumper(MyEnum.V1) == "PREFIX 1"
+
+
+def test_flag_by_exact_value(strict_coercion, debug_trail):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+    )
+
+    loader = retort.get_loader(FlagEnum)
+    assert loader(1) == FlagEnum.CASE_ONE
+    assert loader(3) == FlagEnum.CASE_THREE
+    assert loader(5) == FlagEnum.CASE_ONE | FlagEnum.CASE_FOUR
+
+    dumper = retort.get_dumper(FlagEnum)
+    assert dumper(FlagEnum.CASE_ONE) == 1
+    assert dumper(FlagEnum.CASE_ONE | FlagEnum.CASE_FOUR) == 5
+
+    raises_exc(
+        OutOfRange(0, 15, 16), lambda: loader(16)
+    )
+    raises_exc(
+        OutOfRange(0, 15, -1), lambda: loader(-1)
+    )
+
+
+@pytest.mark.parametrize('data', [{"data": 1}, "1", [1], None])
+def test_flag_by_exact_value_loader_with_bad_types(strict_coercion, debug_trail, data):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+    )
+    loader = retort.get_loader(FlagEnum)
+    raises_exc(TypeLoadError(int, data), lambda: loader(data))
+
+
+def test_flag_by_exact_value_loader_creation_fail(strict_coercion, debug_trail):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+    )
+
+    raises_exc(
+        with_cause(
+            NoSuitableProvider(f'Cannot produce loader for type {FlagEnumWithSkippedBit}'),
+            with_notes(
+                with_notes(
+                    CannotProvide(
+                        'Cannot create a loader for flag with skipped bits',
+                        is_terminal=True,
+                        is_demonstrative=True,
+                    ),
+                    f"Location: type={FlagEnumWithSkippedBit}",
+                ),
+            )
+        ),
+        lambda: retort.get_loader(FlagEnumWithSkippedBit)
+    )
+    raises_exc(
+        with_cause(
+            NoSuitableProvider(f'Cannot produce loader for type {FlagEnumWithNegativeValue}'),
+            with_notes(
+                with_notes(
+                    CannotProvide(
+                        'Cannot create a loader for flag with negative values',
+                        is_terminal=True,
+                        is_demonstrative=True,
+                    ),
+                    f"Location: type={FlagEnumWithNegativeValue}",
+                ),
+            )
+        ),
+        lambda: retort.get_loader(FlagEnumWithNegativeValue)
+    )
+
+
+@parametrize_bool("allow_single_value", "allow_duplicates", "allow_compound")
+def test_flag_by_member_names(
+    strict_coercion, debug_trail, allow_single_value, allow_duplicates, allow_compound
+):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+        recipe=[
+            flag_by_member_names(
+                allow_single_value=allow_single_value,
+                allow_duplicates=allow_duplicates,
+                allow_compound=allow_compound
+            )
+        ]
+    )
+
+    loader = retort.get_loader(FlagEnum)
+    assert loader(["CASE_ONE"]) == FlagEnum.CASE_ONE
+    assert loader(["CASE_ONE", "CASE_TWO"]) == FlagEnum.CASE_THREE
+    assert loader(["CASE_ONE", "CASE_FOUR"]) == FlagEnum.CASE_ONE | FlagEnum.CASE_FOUR
+
+    if allow_compound:
+        variants = ["CASE_ONE", "CASE_TWO", "CASE_THREE", "CASE_FOUR", "CASE_EIGHT"]
+    else:
+        variants = ["CASE_ONE", "CASE_TWO", "CASE_FOUR", "CASE_EIGHT"]
+
+    raises_exc(
+        MultipleBadVariant(
+            allowed_values=variants,
+            invalid_values=["NOT_EXISTING_CASE_1", "NOT_EXISTING_CASE_2"],
+            input_value=["CASE_ONE", "NOT_EXISTING_CASE_1", "NOT_EXISTING_CASE_2"],
+        ),
+        lambda: loader(["CASE_ONE", "NOT_EXISTING_CASE_1", "NOT_EXISTING_CASE_2"])
+    )
+
+    data_with_compound = ["CASE_THREE"]
+    if allow_compound:
+        assert loader(data_with_compound) == FlagEnum.CASE_THREE
+    else:
+        raises_exc(
+            MultipleBadVariant(variants, ["CASE_THREE"], data_with_compound),
+            lambda: loader(data_with_compound)
+        )
+
+    data_with_duplicates = ["CASE_ONE", "CASE_ONE"]
+    if allow_duplicates:
+        assert loader(data_with_duplicates) == FlagEnum.CASE_ONE
+    else:
+        raises_exc(
+            DuplicatedValues(data_with_duplicates),
+            lambda: loader(data_with_duplicates)
+        )
+
+    dumper = retort.get_dumper(FlagEnum)
+    assert dumper(FlagEnum.CASE_ONE) == ["CASE_ONE"]
+    assert dumper(FlagEnum.CASE_ONE & FlagEnum.CASE_TWO) == []
+    assert dumper(~FlagEnum.CASE_TWO) == ["CASE_ONE", "CASE_FOUR", "CASE_EIGHT"]
+
+    compound = FlagEnum.CASE_THREE
+    compound_with_non_compound = FlagEnum.CASE_FOUR | FlagEnum.CASE_THREE
+    if allow_compound:
+        assert dumper(compound) == ["CASE_THREE"]
+        assert dumper(compound_with_non_compound) == ["CASE_THREE", "CASE_FOUR"]
+    else:
+        assert dumper(compound) == ["CASE_ONE", "CASE_TWO"]
+        assert dumper(compound_with_non_compound) == ["CASE_ONE", "CASE_TWO", "CASE_FOUR"]
+
+
+@parametrize_bool("allow_single_value", "allow_duplicates", "allow_compound")
+def test_flag_by_member_names_with_bad_types(
+    strict_coercion, debug_trail, allow_single_value, allow_duplicates, allow_compound
+):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+        recipe=[
+            flag_by_member_names(
+                allow_single_value=allow_single_value,
+                allow_duplicates=allow_duplicates,
+                allow_compound=allow_compound
+            )
+        ]
+    )
+
+    loader = retort.get_loader(FlagEnum)
+    expected_type = Union[str, Iterable[str]] if allow_single_value else Iterable[str]
+
+    dict_data = {"CASE_ONE": 1}
+    if strict_coercion:
+        raises_exc(
+            ExcludedTypeLoadError(expected_type, Mapping, dict_data),
+            lambda: loader(dict_data)
+        )
+    else:
+        assert loader(dict_data) == FlagEnum.CASE_ONE
+
+    str_data = "CASE_ONE"
+    if allow_single_value:
+        assert loader(str_data) == loader([str_data])
+    else:
+        raises_exc(
+            TypeLoadError(expected_type, str_data),
+            lambda: loader(str_data)
+        )
+
+    raises_exc(
+        TypeLoadError(expected_type, 1),
+        lambda: loader(1)
+    )
+    raises_exc(
+        TypeLoadError(expected_type, None),
+        lambda: loader(None)
+    )
+
+
+def test_flag_by_member_names_with_mapping(strict_coercion, debug_trail):
+    retort = Retort(
+        strict_coercion=strict_coercion,
+        debug_trail=debug_trail,
+        recipe=[
+            flag_by_member_names(
+                name_style=NameStyle.CAMEL,
+                map={
+                    "CASE_ONE": "CASE_1",
+                    #  maps given by cases have higher priority that
+                    #  ones given by names.
+                    FlagEnum.CASE_ONE: "caseFirst",
+                    "CASE_TWO": "caseSecond",
+                    "ignore": "ignore"
+                }
+            ),
+        ]
+    )
+
+    loader = retort.get_loader(FlagEnum)
+    assert loader(["caseFirst"]) == FlagEnum.CASE_ONE
+    assert loader(["caseFirst", "caseSecond"]) == FlagEnum.CASE_THREE
+    assert loader(["caseThree"]) == FlagEnum.CASE_THREE
+
+    variants = ["caseFirst", "caseSecond", "caseThree", "caseFour", "caseEight"]
+    raises_exc(
+        MultipleBadVariant(
+            allowed_values=variants,
+            input_value=["caseThree", "CASE_TWO", "CASE_1"],
+            invalid_values=["CASE_TWO", "CASE_1"]
+        ),
+        lambda: loader(["caseThree", "CASE_TWO", "CASE_1"])
+    )
+
+    dumper = retort.get_dumper(FlagEnum)
+    assert dumper(FlagEnum.CASE_ONE) == ["caseFirst"]
+    assert dumper(FlagEnum.CASE_TWO) == ["caseSecond"]
+    assert dumper(FlagEnum.CASE_ONE | FlagEnum.CASE_TWO) == ["caseThree"]
diff --git a/tests/unit/provider/test_loc_stack_filtering.py b/tests/unit/provider/test_loc_stack_filtering.py
new file mode 100644
index 00000000..5d4fec99
--- /dev/null
+++ b/tests/unit/provider/test_loc_stack_filtering.py
@@ -0,0 +1,394 @@
+import collections.abc
+import typing
+from contextlib import nullcontext
+from dataclasses import dataclass
+from typing import Any, Dict, Generic, Iterable, List, Optional, Type, TypeVar, Union, overload
+
+import pytest
+from tests_helpers import cond_list, full_match_regex_str
+
+from adaptix import Chain, P, Retort, loader
+from adaptix._internal.common import TypeHint
+from adaptix._internal.feature_requirement import HAS_ANNOTATED
+from adaptix._internal.model_tools.definitions import NoDefault
+from adaptix._internal.provider.loc_stack_filtering import (
+    ExactOriginLSC,
+    ExactTypeLSC,
+    LocStackEndChecker,
+    OriginSubclassLSC,
+    create_loc_stack_checker,
+)
+from adaptix._internal.provider.request_cls import FieldLoc, GenericParamLoc, LocMap, LocStack, TypeHintLoc
+from adaptix._internal.type_tools import normalize_type
+
+
+@overload
+def param_result(*values: Any, id: Optional[str] = None, result: Any):
+    ...
+
+
+@overload
+def param_result(*values: Any, id: Optional[str] = None, raises: Type[Exception]):
+    ...
+
+
+@overload
+def param_result(*values: Any, id: Optional[str] = None, raises: Type[Exception], exact_match: str):
+    ...
+
+
+@overload
+def param_result(*values: Any, id: Optional[str] = None, raises: Type[Exception], match: str):
+    ...
+
+
+def param_result(*values, result=None, raises=None, exact_match=None, match=None, id=None):
+    if raises is not None:
+        context = pytest.raises(
+            raises, match=full_match_regex_str(exact_match) if exact_match is not None else match
+        )
+    else:
+        context = None
+    if id is None:
+        if len(values) == 1:
+            id = str(values[0])
+        else:
+            id = str(values)
+    return pytest.param(*values, result, context, id=id)
+
+
+def create_mediator():
+    return Retort()._create_mediator()
+
+
+@pytest.mark.parametrize(
+    ['loc_stack', 'result', 'context'],
+    [
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+            ),
+            result=False,
+            id='1-item',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+            ),
+            result=False,
+            id='2-items',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=True,
+            id='ok',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(str)),
+            ),
+            result=False,
+            id='last-is-bad',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=True,
+            id='extra-stack-matched-with-prev',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=True,
+            id='extra-stack',
+        ),
+    ],
+)
+def test_stack_end_rc(loc_stack, result, context):
+    checker = LocStackEndChecker(
+        [
+            create_loc_stack_checker(int),
+            create_loc_stack_checker(str),
+            create_loc_stack_checker(bool),
+        ]
+    )
+    assert checker.check_loc_stack(create_mediator(), loc_stack) == result
+
+
+@pytest.mark.parametrize(
+    ['loc_stack', 'result', 'context'],
+    [
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(bool)),
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=True,
+            id='ok',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=False,
+            id='too-small',
+        ),
+        param_result(
+            LocStack(
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(int)),
+                LocMap(TypeHintLoc(str)),
+                LocMap(TypeHintLoc(bool)),
+            ),
+            result=False,
+            id='bad',
+        ),
+    ],
+)
+def test_nested_start_rc(loc_stack, result, context):
+    checker = LocStackEndChecker(
+        [
+            P.ANY,
+            LocStackEndChecker(
+                [
+                    create_loc_stack_checker(bool),
+                    create_loc_stack_checker(int),
+                    create_loc_stack_checker(str),
+                ]
+            ),
+            create_loc_stack_checker(bool),
+        ]
+    )
+    assert checker.check_loc_stack(create_mediator(), loc_stack) == result
+
+
+@dataclass
+class WithUserName:
+    user_name: str
+
+
+def field_loc_map(name: str, tp: TypeHint) -> LocMap:
+    return LocMap(
+        TypeHintLoc(tp),
+        FieldLoc(
+            field_id=name,
+            default=NoDefault(),
+            metadata={},
+        )
+    )
+
+
+@pytest.mark.parametrize(
+    ['pattern', 'loc_stack', 'result', 'context'],
+    [
+        param_result(
+            P[int],
+            LocStack(LocMap(TypeHintLoc(int))),
+            result=True,
+            id='int-ok',
+        ),
+        param_result(
+            P[int],
+            LocStack(LocMap(TypeHintLoc(str))),
+            result=False,
+            id='int-fail',
+        ),
+        param_result(
+            P[WithUserName].user_name,
+            LocStack(
+                LocMap(TypeHintLoc(WithUserName)),
+                field_loc_map('user_name', str),
+            ),
+            result=True,
+            id='model-attr-ok-1',
+        ),
+        param_result(
+            P[WithUserName].user_name,
+            LocStack(
+                LocMap(TypeHintLoc(WithUserName)),
+                field_loc_map('user_name', int),
+            ),
+            result=True,
+            id='model-attr-ok-2',
+        ),
+        param_result(
+            P[WithUserName].user_id,
+            LocStack(
+                LocMap(TypeHintLoc(WithUserName)),
+                field_loc_map('user_name', int),
+            ),
+            result=False,
+            id='model-attr-fail',
+        ),
+        param_result(
+            P[WithUserName] + P.user_name,
+            LocStack(
+                LocMap(TypeHintLoc(WithUserName)),
+                field_loc_map('user_name', int),
+            ),
+            result=True,
+            id='model-attr-ok-concat',
+        ),
+        param_result(
+            P[Dict].generic_arg(0, str),
+            LocStack(
+                LocMap(TypeHintLoc(Dict)),
+                LocMap(TypeHintLoc(str), GenericParamLoc(0)),
+            ),
+            result=True,
+            id='generic-arg-ok',
+        ),
+        param_result(
+            P[Dict].generic_arg(0, str),
+            LocStack(
+                LocMap(TypeHintLoc(Dict)),
+                LocMap(TypeHintLoc(str), GenericParamLoc(1)),
+            ),
+            result=False,
+            id='generic-arg-fail',
+        ),
+    ],
+)
+def test_request_pattern(pattern, loc_stack, result, context):
+    assert pattern.build_loc_stack_checker().check_loc_stack(create_mediator(), loc_stack) == result
+
+
+def test_generic_pattern_create_fail():
+    with pytest.raises(
+        TypeError,
+        match=full_match_regex_str(
+            'Can not use LocStackPattern as predicate inside LocStackPattern.'
+            ' If you want to combine several LocStackPattern, you can use `+` operator'
+        )
+    ):
+        P[WithUserName][P.user_name]
+
+
+def plus_one(data):
+    return data + 1
+
+
+def plus_two(data):
+    return data + 2
+
+
+def test_request_pattern_generic_arg_dict_override():
+    retort = Retort(
+        recipe=[
+            loader(P[Dict].generic_arg(0, int), plus_one, Chain.LAST),
+            loader(P[Dict].generic_arg(1, int), plus_two, Chain.LAST),
+        ]
+    )
+
+    loaded_dict = retort.load({10: 20}, Dict[int, int])
+    assert loaded_dict == {11: 22}
+
+
+T = TypeVar('T')
+
+
+class MyGeneric(Generic[T]):
+    pass
+
+
+@pytest.mark.parametrize(
+    ['value', 'result', 'context'],
+    [
+        param_result(
+            int,
+            result=ExactOriginLSC(int),
+        ),
+        param_result(
+            List,
+            result=ExactOriginLSC(list),
+        ),
+        param_result(
+            dict,
+            result=ExactOriginLSC(dict),
+        ),
+        param_result(
+            Iterable,
+            result=OriginSubclassLSC(collections.abc.Iterable),
+        ),
+        param_result(
+            Iterable[int],
+            result=ExactTypeLSC(normalize_type(Iterable[int])),
+        ),
+        param_result(
+            List[int],
+            result=ExactTypeLSC(normalize_type(List[int])),
+        ),
+        param_result(
+            List[T],
+            raises=ValueError,
+            exact_match='Can not create LocStackChecker from typing.List[~T] generic alias (parametrized generic)',
+        ),
+        param_result(
+            Union,
+            result=ExactOriginLSC(Union)
+        ),
+        *cond_list(
+            HAS_ANNOTATED,
+            lambda: [
+                param_result(
+                    typing.Annotated,
+                    result=ExactOriginLSC(typing.Annotated)
+                ),
+                param_result(
+                    typing.Annotated[int, 'meta'],
+                    result=ExactTypeLSC(normalize_type(typing.Annotated[int, 'meta']))
+                ),
+                param_result(
+                    typing.Annotated[List[int], 'meta'],
+                    result=ExactTypeLSC(normalize_type(typing.Annotated[list[int], 'meta']))
+                ),
+                param_result(
+                    typing.Annotated[list, 'meta'],
+                    raises=ValueError,
+                    exact_match=(
+                        "Can not create LocStackChecker from"
+                        " typing.Annotated[list, 'meta'] generic alias (parametrized generic)"
+                    ),
+                ),
+                param_result(
+                    typing.Annotated[List[T], 'meta'],
+                    raises=ValueError,
+                    exact_match=(
+                        "Can not create LocStackChecker from"
+                        " typing.Annotated[typing.List[~T], 'meta'] generic alias (parametrized generic)"
+                    ),
+                ),
+                param_result(
+                    typing.Annotated[Dict[int, T], 'meta'],
+                    raises=ValueError,
+                    exact_match=(
+                        "Can not create LocStackChecker from"
+                        " typing.Annotated[typing.Dict[int, ~T], 'meta'] generic alias (parametrized generic)"
+                    ),
+                ),
+            ]
+        ),
+    ],
+)
+def test_create_request_checker(value, result, context):
+    with context or nullcontext():
+        assert create_loc_stack_checker(value) == result
diff --git a/tests/unit/provider/test_overlay_schema.py b/tests/unit/provider/test_overlay_schema.py
index 0a52cbae..72051acd 100644
--- a/tests/unit/provider/test_overlay_schema.py
+++ b/tests/unit/provider/test_overlay_schema.py
@@ -7,7 +7,7 @@
 from adaptix import Chain, Mediator, Omittable, Omitted, Provider, Request, bound
 from adaptix._internal.common import VarTuple
 from adaptix._internal.provider.overlay_schema import Overlay, OverlayProvider, Schema, provide_schema
-from adaptix._internal.provider.request_cls import LocMap, TypeHintLoc
+from adaptix._internal.provider.request_cls import LocMap, LocStack, TypeHintLoc
 from adaptix._internal.provider.static_provider import StaticProvider, static_provision_action
 
 
@@ -64,7 +64,7 @@ def test_simple():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=1,
         char_list=('a', 'b'),
@@ -93,7 +93,7 @@ def test_chaining():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=1,
         char_list=('a', 'b', 'c', 'd'),
@@ -120,7 +120,7 @@ def test_chaining():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=2,
         char_list=('c', 'd', 'a', 'b'),
@@ -147,7 +147,7 @@ def test_chaining():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=2,
         char_list=('a', 'b', 'c', 'd'),
@@ -174,7 +174,7 @@ def test_chaining():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=1,
         char_list=('c', 'd'),
@@ -201,7 +201,7 @@ def test_chaining():
                 chain=None,
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
     ) == MySchema(
         number=2,
         char_list=('c', 'd'),
@@ -244,7 +244,7 @@ def test_typehint_location():
                 ),
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap(TypeHintLoc(type=MyClass2)))
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap(TypeHintLoc(type=MyClass2))))
     ) == MySchema(
         number=1,
         char_list=('a', 'b', 'c', 'd'),
@@ -289,7 +289,7 @@ def test_typehint_location():
                 ),
             ),
         ],
-        provide_action=lambda m: provide_schema(MyOverlay, m, LocMap(TypeHintLoc(type=MyClass2)))
+        provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap(TypeHintLoc(type=MyClass2))))
     ) == MySchema(
         number=1,
         char_list=('a', 'b', 'c', 'd', 'e', 'f'),
@@ -322,5 +322,5 @@ def test_omitted_fields():
                     chain=None,
                 ),
             ],
-            provide_action=lambda m: provide_schema(MyOverlay, m, LocMap())
+            provide_action=lambda m: provide_schema(MyOverlay, m, LocStack(LocMap()))
         )
diff --git a/tests/unit/provider/test_request_filtering.py b/tests/unit/provider/test_request_filtering.py
deleted file mode 100644
index 65a6e7f6..00000000
--- a/tests/unit/provider/test_request_filtering.py
+++ /dev/null
@@ -1,401 +0,0 @@
-import collections.abc
-import typing
-from contextlib import nullcontext
-from dataclasses import dataclass
-from typing import Any, Dict, Generic, Iterable, List, Type, TypeVar, Union, overload
-
-import pytest
-from tests_helpers import cond_list, full_match_regex_str
-
-from adaptix import CannotProvide, Chain, P, Request, Retort, loader
-from adaptix._internal.common import TypeHint
-from adaptix._internal.feature_requirement import HAS_ANNOTATED
-from adaptix._internal.model_tools.definitions import NoDefault
-from adaptix._internal.provider.request_cls import FieldLoc, GenericParamLoc, LocatedRequest, LocMap, TypeHintLoc
-from adaptix._internal.provider.request_filtering import (
-    AnyRequestChecker,
-    ExactOriginRC,
-    ExactTypeRC,
-    OriginSubclassRC,
-    RequestPattern,
-    StackEndRC,
-    create_request_checker,
-)
-from adaptix._internal.retort.mediator import BuiltinMediator, RawRecipeSearcher, RecursionResolving
-from adaptix._internal.retort.operating_retort import BuiltinErrorRepresentor
-from adaptix._internal.type_tools import normalize_type
-
-
-def create_mediator(*elements: Request[Any]):
-    return BuiltinMediator(
-        searcher=RawRecipeSearcher([]),
-        recursion_resolving=RecursionResolving(),
-        request_stack=elements,
-        error_representor=BuiltinErrorRepresentor(),
-    )
-
-
-def test_stack_end_rc():
-    checker = StackEndRC(
-        [
-            create_request_checker(int),
-            create_request_checker(str),
-            create_request_checker(bool),
-        ]
-    )
-
-    with pytest.raises(CannotProvide, match="Request stack is too small"):
-        checker.check_request(
-            create_mediator(
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            ),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-        )
-
-    with pytest.raises(CannotProvide, match="Request stack is too small"):
-        checker.check_request(
-            create_mediator(
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            ),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-        )
-
-    with pytest.raises(CannotProvide):
-        checker.check_request(
-            create_mediator(
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            ),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-        )
-
-    checker.check_request(
-        create_mediator(
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        ),
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-    )
-    checker.check_request(
-        create_mediator(
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        ),
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-    )
-    checker.check_request(
-        create_mediator(
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        ),
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-    )
-
-
-def test_nested_start_rc():
-    checker = StackEndRC(
-        [
-            AnyRequestChecker(),
-            StackEndRC(
-                [
-                    create_request_checker(bool),
-                    create_request_checker(int),
-                    create_request_checker(str),
-                ]
-            ),
-            create_request_checker(bool),
-        ]
-    )
-
-    checker.check_request(
-        create_mediator(
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        ),
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-    )
-
-    with pytest.raises(CannotProvide, match="Request stack is too small"):
-        checker.check_request(
-            create_mediator(
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-            ),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        )
-
-    with pytest.raises(CannotProvide):
-        checker.check_request(
-            create_mediator(
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-                LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-            ),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(bool))),
-        )
-
-
-def check_request_pattern(
-    request_pattern: RequestPattern,
-    request_or_stack: Union[Request, List[Request]],
-    *,
-    fail: bool,
-) -> None:
-    if isinstance(request_or_stack, list):
-        request = request_or_stack[-1]
-        stack = request_or_stack
-    else:
-        request = request_or_stack
-        stack = []
-
-    with pytest.raises(CannotProvide) if fail else nullcontext():
-        request_pattern.build_request_checker().check_request(
-            create_mediator(*stack),
-            request,
-        )
-
-
-@dataclass
-class WithUserName:
-    user_name: str
-
-
-def field_loc_map(name: str, tp: TypeHint) -> LocMap:
-    return LocMap(
-        TypeHintLoc(tp),
-        FieldLoc(
-            field_id=name,
-            default=NoDefault(),
-            metadata={},
-            owner_type=WithUserName,
-        )
-    )
-
-
-def test_request_pattern():
-    check_request_pattern(
-        P[int],
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(int))),
-        fail=False,
-    )
-    check_request_pattern(
-        P[int],
-        LocatedRequest(loc_map=LocMap(TypeHintLoc(str))),
-        fail=True,
-    )
-
-    check_request_pattern(
-        P[WithUserName].user_name,
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(WithUserName))),
-            LocatedRequest(loc_map=field_loc_map('user_name', str)),
-        ],
-        fail=False,
-    )
-    check_request_pattern(
-        P[WithUserName].user_name,
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(WithUserName))),
-            LocatedRequest(loc_map=field_loc_map('user_name', int)),
-        ],
-        fail=False,
-    )
-    check_request_pattern(
-        P[WithUserName].user_id,
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(WithUserName))),
-            LocatedRequest(loc_map=field_loc_map('user_name', int)),
-        ],
-        fail=True,
-    )
-
-    with pytest.raises(
-        TypeError,
-        match=full_match_regex_str(
-            'Can not use RequestPattern as predicate inside RequestPattern.'
-            ' If you want to combine several RequestPattern, you can use `+` operator'
-        )
-    ):
-        P[WithUserName][P.user_name]
-
-    check_request_pattern(
-        P[WithUserName] + P.user_name,
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(WithUserName))),
-            LocatedRequest(loc_map=field_loc_map('user_name', int)),
-        ],
-        fail=False,
-    )
-
-
-def test_request_pattern_generic_arg():
-    check_request_pattern(
-        P[Dict].generic_arg(0, str),
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(Dict))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str), GenericParamLoc(0))),
-        ],
-        fail=False,
-    )
-    check_request_pattern(
-        P[Dict].generic_arg(0, str),
-        [
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(Dict))),
-            LocatedRequest(loc_map=LocMap(TypeHintLoc(str), GenericParamLoc(1))),
-        ],
-        fail=True,
-    )
-
-
-def plus_one(data):
-    return data + 1
-
-
-def plus_two(data):
-    return data + 2
-
-
-def test_request_pattern_generic_arg_dict_override():
-    retort = Retort(
-        recipe=[
-            loader(P[Dict].generic_arg(0, int), plus_one, Chain.LAST),
-            loader(P[Dict].generic_arg(1, int), plus_two, Chain.LAST),
-        ]
-    )
-
-    loaded_dict = retort.load({10: 20}, Dict[int, int])
-    assert loaded_dict == {11: 22}
-
-
-T = TypeVar('T')
-
-
-@overload
-def param_result(value: Any, *, result: Any):
-    ...
-
-
-@overload
-def param_result(value: Any, *, raises: Type[Exception]):
-    ...
-
-
-@overload
-def param_result(value: Any, *, raises: Type[Exception], exact_match: str):
-    ...
-
-
-@overload
-def param_result(value: Any, *, raises: Type[Exception], match: str):
-    ...
-
-
-def param_result(value, *, result=None, raises=None, exact_match=None, match=None):
-    if raises is not None:
-        context = pytest.raises(
-            raises, match=full_match_regex_str(exact_match) if exact_match is not None else match
-        )
-    else:
-        context = None
-    return pytest.param(value, result, context, id=str(value))
-
-
-class MyGeneric(Generic[T]):
-    pass
-
-
-@pytest.mark.parametrize(
-    ['value', 'result', 'context'],
-    [
-        param_result(
-            int,
-            result=ExactOriginRC(int),
-        ),
-        param_result(
-            List,
-            result=ExactOriginRC(list),
-        ),
-        param_result(
-            dict,
-            result=ExactOriginRC(dict),
-        ),
-        param_result(
-            Iterable,
-            result=OriginSubclassRC(collections.abc.Iterable),
-        ),
-        param_result(
-            Iterable[int],
-            result=ExactTypeRC(normalize_type(Iterable[int])),
-        ),
-        param_result(
-            List[int],
-            result=ExactTypeRC(normalize_type(List[int])),
-        ),
-        param_result(
-            List[T],
-            raises=ValueError,
-            exact_match='Can not create RequestChecker from typing.List[~T] generic alias (parametrized generic)',
-        ),
-        param_result(
-            Union,
-            result=ExactOriginRC(Union)
-        ),
-    ] + cond_list(
-        HAS_ANNOTATED,
-        lambda: [
-            param_result(
-                typing.Annotated,
-                result=ExactOriginRC(typing.Annotated)
-            ),
-            param_result(
-                typing.Annotated[int, 'meta'],
-                result=ExactTypeRC(normalize_type(typing.Annotated[int, 'meta']))
-            ),
-            param_result(
-                typing.Annotated[List[int], 'meta'],
-                result=ExactTypeRC(normalize_type(typing.Annotated[list[int], 'meta']))
-            ),
-            param_result(
-                typing.Annotated[list, 'meta'],
-                raises=ValueError,
-                exact_match=(
-                    "Can not create RequestChecker from"
-                    " typing.Annotated[list, 'meta'] generic alias (parametrized generic)"
-                ),
-            ),
-            param_result(
-                typing.Annotated[List[T], 'meta'],
-                raises=ValueError,
-                exact_match=(
-                    "Can not create RequestChecker from"
-                    " typing.Annotated[typing.List[~T], 'meta'] generic alias (parametrized generic)"
-                ),
-            ),
-            param_result(
-                typing.Annotated[Dict[int, T], 'meta'],
-                raises=ValueError,
-                exact_match=(
-                    "Can not create RequestChecker from"
-                    " typing.Annotated[typing.Dict[int, ~T], 'meta'] generic alias (parametrized generic)"
-                ),
-            ),
-        ]
-    ),
-)
-def test_create_request_checker(value, result, context):
-    if context is None:
-        assert create_request_checker(value) == result
-    else:
-        with context:
-            create_request_checker(value)
diff --git a/tests/unit/retort/test_mediator.py b/tests/unit/retort/test_mediator.py
deleted file mode 100644
index 500a87b5..00000000
--- a/tests/unit/retort/test_mediator.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from dataclasses import dataclass
-from itertools import permutations
-from typing import Optional, Sequence, Type
-
-import pytest
-
-from adaptix import CannotProvide, Mediator, Provider, Request
-from adaptix._internal.retort.mediator import BuiltinMediator, RawRecipeSearcher, RecursionResolving
-from adaptix._internal.retort.operating_retort import BuiltinErrorRepresentor
-
-
-@dataclass(frozen=True)
-class SampleRequest1(Request[None]):
-    pass
-
-
-@dataclass(frozen=True)
-class SampleRequest2(Request[None]):
-    pass
-
-
-@dataclass(frozen=True)
-class SampleRequest3(Request[None]):
-    pass
-
-
-@dataclass
-class StackAsserter(Provider):
-    request_type: Type[Request]
-    expected_stack: Sequence[Request]
-    send_next: Optional[Request]
-
-    def apply_provider(self, mediator: Mediator, request: Request):
-        if not isinstance(request, self.request_type):
-            raise CannotProvide
-
-        assert list(self.expected_stack) == list(mediator.request_stack)
-
-        if self.send_next is not None:
-            mediator.delegating_provide(self.send_next)
-
-
-ASSERTER_LIST = [
-    StackAsserter(SampleRequest1, [SampleRequest1()], send_next=SampleRequest2()),
-    StackAsserter(SampleRequest2, [SampleRequest1(), SampleRequest2()], send_next=SampleRequest3()),
-    StackAsserter(SampleRequest3, [SampleRequest1(), SampleRequest2(), SampleRequest3()], send_next=None),
-]
-
-
-@pytest.mark.parametrize(
-    'order',
-    [
-        pytest.param(order, id=str(order))
-        for order in permutations([1, 2, 3])
-    ],
-)
-def test_request_stack(order):
-    searcher = RawRecipeSearcher(recipe=[ASSERTER_LIST[value - 1] for value in order])
-
-    mediator = BuiltinMediator(
-        searcher=searcher,
-        recursion_resolving=RecursionResolving(),
-        request_stack=[],
-        error_representor=BuiltinErrorRepresentor(),
-    )
-
-    mediator.delegating_provide(
-        SampleRequest1(),
-    )
diff --git a/tests/unit/test_name_style.py b/tests/unit/test_name_style.py
index ab753c2b..725c8010 100644
--- a/tests/unit/test_name_style.py
+++ b/tests/unit/test_name_style.py
@@ -1,47 +1,48 @@
 import pytest
 
 from adaptix import NameStyle
-from adaptix._internal.name_style import convert_snake_style, is_snake_case
+from adaptix._internal.name_style import convert_snake_style, is_snake_style
 
 
-def test_is_snake_case():
-    assert is_snake_case('a')
-    assert is_snake_case('a_b')
-    assert is_snake_case('a_')
-    assert is_snake_case('_a')
-    assert is_snake_case('a_b_')
-    assert is_snake_case('_a_')
-    assert is_snake_case('_a_b')
-    assert is_snake_case('_a_b_')
+def test_is_snake_style():
+    assert is_snake_style('a')
+    assert is_snake_style('a_b')
+    assert is_snake_style('a_')
+    assert is_snake_style('_a')
+    assert is_snake_style('a_b_')
+    assert is_snake_style('_a_')
+    assert is_snake_style('_a_b')
+    assert is_snake_style('_a_b_')
 
-    assert is_snake_case('1')
-    assert is_snake_case('1_2')
-    assert is_snake_case('1_')
-    assert is_snake_case('_1')
-    assert is_snake_case('1_2_')
-    assert is_snake_case('_1_')
-    assert is_snake_case('_1_2')
-    assert is_snake_case('_1_2_')
+    assert is_snake_style('1')
+    assert is_snake_style('1_2')
+    assert is_snake_style('1_')
+    assert is_snake_style('_1')
+    assert is_snake_style('1_2_')
+    assert is_snake_style('_1_')
+    assert is_snake_style('_1_2')
+    assert is_snake_style('_1_2_')
 
-    assert is_snake_case('a_1')
-    assert is_snake_case('a_1_')
+    assert is_snake_style('a_1')
+    assert is_snake_style('a_1_')
 
-    assert is_snake_case('a__b')
-    assert is_snake_case('a__b_')
+    assert is_snake_style('a__b')
+    assert is_snake_style('a__b_')
 
-    assert is_snake_case('_')
-    assert is_snake_case('___')
+    assert is_snake_style('_')
+    assert is_snake_style('___')
 
-    assert not is_snake_case('A_1_')
-    assert not is_snake_case('Aa_1_')
+    assert is_snake_style('A_1_')
+    assert is_snake_style('Aa_1_')
 
-    assert not is_snake_case('123%')
-    assert not is_snake_case('_123%')
+    assert not is_snake_style('123%')
+    assert not is_snake_style('_123%')
 
 
 def check_conversion(style, maps):
     for src, trg in maps.items():
         assert convert_snake_style(src, style) == trg
+        assert convert_snake_style(src.upper(), style) == trg
 
 
 def test_snake_case_conversion():
@@ -146,6 +147,6 @@ def test_snake_case_conversion_fail():
             convert_snake_style('___', style)
 
     for style in NameStyle:
-        for name in ['AbcXyz', 'abcXyz', 'abcxyz?']:
+        for name in ['abcxyz?']:
             with pytest.raises(ValueError):
                 convert_snake_style(name, style)