diff --git a/doc/conf.py b/doc/conf.py
index 05ac74793137da6fb78b1c46960107d256216055..068615d4a494bf9a3c624b51288ed6f806491853 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -30,3 +30,8 @@ intersphinx_mapping = {
     "https://documen.tician.de/pymbolic/": None,
     "https://documen.tician.de/loopy/": None,
     }
+
+
+nitpick_ignore_regex = [
+        ["py:class", r"typing_extensions\.(.+)"],
+        ]
diff --git a/pytools/tag.py b/pytools/tag.py
index e6dd9f87a7374701d2b2ff2e398d8b49921a70fd..032a85070f19dc7ccb63b291c58db90e3718adc4 100644
--- a/pytools/tag.py
+++ b/pytools/tag.py
@@ -1,5 +1,4 @@
 """
-
 Tag Interface
 ---------------
 .. ``normalize_tags`` undocumented for now. (Not ready to commit.)
@@ -19,20 +18,24 @@ Supporting Functionality
 Internal stuff that is only here because the documentation tool wants it
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. class:: T_co
-
-    A covariant type variable lower-bounded by :class:`Taggable`.
-
 .. class:: TagsOfTypeT
 
     A type variable used in :meth:`Taggable.tags_of_type`.
 """
 
+import sys
 from dataclasses import dataclass
 from typing import (Tuple, Set, Any, FrozenSet, Union, Iterable,  # noqa: F401
                     TypeVar, Type)
 from pytools import memoize, memoize_method
 
+
+try:
+    from typing import Self  # type: ignore[attr-defined]
+except ImportError:
+    from typing_extensions import Self
+
+
 __copyright__ = """
 Copyright (C) 2020 Andreas Klöckner
 Copyright (C) 2020 Matt Wala
@@ -162,9 +165,7 @@ class UniqueTag(Tag):
 # }}}
 
 
-TagsType = FrozenSet[Tag]
-TagOrIterableType = Union[Iterable[Tag], Tag, None]
-T_co = TypeVar("T_co", bound="Taggable")
+ToTagSetConvertible = Union[Iterable[Tag], Tag, None]
 TagsOfTypeT = TypeVar("TagsOfTypeT", bound="Type[Tag]")
 
 
@@ -190,7 +191,7 @@ class NonUniqueTagError(ValueError):
     pass
 
 
-def check_tag_uniqueness(tags: TagsType):
+def check_tag_uniqueness(tags: FrozenSet[Tag]):
     """Ensure that *tags* obeys the rules set forth in :class:`UniqueTag`.
     If not, raise :exc:`NonUniqueTagError`. If any *tags* are not
     subclasses of :class:`Tag`, a :exc:`TypeError` will be raised.
@@ -216,7 +217,7 @@ def check_tag_uniqueness(tags: TagsType):
 # }}}
 
 
-def normalize_tags(tags: TagOrIterableType) -> TagsType:
+def normalize_tags(tags: ToTagSetConvertible) -> FrozenSet[Tag]:
     if isinstance(tags, Tag):
         tags = frozenset([tags])
     elif tags is None:
@@ -249,7 +250,7 @@ class Taggable:
     # ReST references in docstrings must be fully qualified, as docstrings may
     # be inherited and appear in different contexts.
 
-    def __init__(self, tags: TagsType = frozenset()):
+    def __init__(self, tags: FrozenSet[Tag] = frozenset()):
         """
         Constructor for all objects that possess a `tags` attribute.
 
@@ -262,7 +263,7 @@ class Taggable:
         """
         self.tags = tags
 
-    def _with_new_tags(self: T_co, tags: TagsType) -> T_co:
+    def _with_new_tags(self, tags: FrozenSet[Tag]) -> Self:
         """
         Returns a copy of *self* with the specified tags. This method
         should be overridden by subclasses.
@@ -279,7 +280,7 @@ class Taggable:
         # abstract method.
         return self.copy(tags=tags)  # type: ignore[attr-defined]  # pylint: disable=no-member  # noqa: E501
 
-    def tagged(self: T_co, tags: TagOrIterableType) -> T_co:
+    def tagged(self, tags: ToTagSetConvertible) -> Self:
         """
         Return a copy of *self* with the specified
         tag or tags added to the set of tags. If the resulting set of
@@ -292,8 +293,8 @@ class Taggable:
         return self._with_new_tags(
                 tags=check_tag_uniqueness(normalize_tags(tags) | self.tags))
 
-    def without_tags(self: T_co,
-            tags: TagOrIterableType, verify_existence: bool = True) -> T_co:
+    def without_tags(self,
+            tags: ToTagSetConvertible, verify_existence: bool = True) -> Self:
         """
         Return a copy of *self* without the specified tags.
 
@@ -324,4 +325,41 @@ class Taggable:
 
 # }}}
 
+
+# {{{ deprecation
+
+_depr_name_to_replacement_and_obj = {
+        "TagsType": (
+            "FrozenSet[Tag]",
+            FrozenSet[Tag], 2023),
+        "TagOrIterableType": (
+            "ToTagSetConvertible",
+            ToTagSetConvertible, 2023),
+        "T_co": (
+            "Self (i.e. the self type from Python 3.11)",
+            TypeVar("TaggableT", bound="Taggable"), 2023),
+        }
+
+
+if sys.version_info >= (3, 7):
+    def __getattr__(name):
+        replacement_and_obj = _depr_name_to_replacement_and_obj.get(name, None)
+        if replacement_and_obj is not None:
+            replacement, obj, year = replacement_and_obj
+            from warnings import warn
+            warn(f"'arraycontext.{name}' is deprecated. "
+                    f"Use '{replacement}' instead. "
+                    f"'arraycontext.{name}' will continue to work until {year}.",
+                    DeprecationWarning, stacklevel=2)
+            return obj
+        else:
+            raise AttributeError(name)
+else:
+    TagsType = FrozenSet[Tag]
+    TagOrIterableType = ToTagSetConvertible
+    T_co = TypeVar("TaggableT", bound="Taggable")
+
+# }}}
+
+
 # vim: foldmethod=marker
diff --git a/setup.py b/setup.py
index aea3d2132805bb64ae1ea45a20d8fb70cd2b22f7..8b96504d0e481440156d5fcc0087035edf5e975f 100644
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,8 @@ setup(name="pytools",
       install_requires=[
           "platformdirs>=2.2.0",
           "numpy>=1.6.0",
-          "dataclasses>=0.7;python_version<='3.6'"
+          "dataclasses>=0.7;python_version<='3.6'",
+          "typing_extensions; python_version<'3.11'",
           ],
 
       package_data={"pytools": ["py.typed"]},