diff --git a/doc/ref_kernel.rst b/doc/ref_kernel.rst
index 46063310917fe4c111bac96a5a15c7c588974617..896388d2911a6d3c0e7783d7b1b3833b87c770d0 100644
--- a/doc/ref_kernel.rst
+++ b/doc/ref_kernel.rst
@@ -553,8 +553,6 @@ Helper values
 
 .. autoclass:: UniqueName
 
-.. autoclass:: Optional
-
 .. }}}
 
 Libraries: Extending and Interfacing with External Functionality
diff --git a/doc/tutorial.rst b/doc/tutorial.rst
index 3c85060dacf03b52f6e0b1faf05ad4697b6a5d07..397f34a987ed336795d00e2770c2fbeadf089ae7 100644
--- a/doc/tutorial.rst
+++ b/doc/tutorial.rst
@@ -1869,7 +1869,7 @@ Now to make things more interesting, we'll create a kernel with barriers:
     ...     e[i,j,k] = c[i,j,k+1]+c[i,j,k-1]
     ...     """
     ...     ], [
-    ...     lp.TemporaryVariable("c", dtype=None, shape=(50, 10, 99)),
+    ...     lp.TemporaryVariable("c", lp.auto, shape=(50, 10, 99)),
     ...     "..."
     ...     ])
     >>> knl = lp.add_and_infer_dtypes(knl, dict(a=np.int32))
diff --git a/loopy/__init__.py b/loopy/__init__.py
index d69a57bf1a5435adfb067df5cfb2080633cac765..f50ce237c0170d2b9703f1760a334e9f39e7f7f1 100644
--- a/loopy/__init__.py
+++ b/loopy/__init__.py
@@ -149,8 +149,6 @@ from loopy.target.pyopencl import PyOpenCLTarget
 from loopy.target.ispc import ISPCTarget
 from loopy.target.numba import NumbaTarget, NumbaCudaTarget
 
-from loopy.tools import Optional
-
 
 __all__ = [
         "TaggedVariable", "Reduction", "LinearSubscript", "TypeCast",
@@ -277,8 +275,6 @@ __all__ = [
         "NumbaTarget", "NumbaCudaTarget",
         "ASTBuilderBase",
 
-        "Optional",
-
         # {{{ from this file
 
         "register_preamble_generators",
diff --git a/loopy/kernel/array.py b/loopy/kernel/array.py
index 3588f38af13479b127208c25735f1046eaa82706..bae9d7d1fbc873076a84b933e5c78f5c9b19dbb5 100644
--- a/loopy/kernel/array.py
+++ b/loopy/kernel/array.py
@@ -693,9 +693,8 @@ class ArrayBase(ImmutableRecord):
 
         if dtype is lp.auto:
             from warnings import warn
-            warn("Argument/temporary data type for '%s' should be None if "
-                    "unspecified, not auto. This usage will be disallowed in 2018."
-                    % name,
+            warn("Argument/temporary data type should be None if unspecified, "
+                    "not auto. This usage will be disallowed in 2018.",
                     DeprecationWarning, stacklevel=2)
 
             dtype = None
diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py
index 2175b5f364796fed58d2373d82ae18ea7128ef2f..c42db348234345a48efcb22b842fd114c5f65f14 100644
--- a/loopy/kernel/creation.py
+++ b/loopy/kernel/creation.py
@@ -28,7 +28,7 @@ THE SOFTWARE.
 import numpy as np
 
 from pymbolic.mapper import CSECachingMapperMixin
-from loopy.tools import intern_frozenset_of_ids, Optional
+from loopy.tools import intern_frozenset_of_ids
 from loopy.symbolic import IdentityMapper, WalkMapper
 from loopy.kernel.data import (
         InstructionBase,
@@ -454,6 +454,7 @@ def parse_insn(groups, insn_options):
         and *inames_to_dup* is None or a list of tuples `(old, new)`.
     """
 
+    import loopy as lp
     from loopy.symbolic import parse
 
     if "lhs" in groups:
@@ -485,11 +486,14 @@ def parse_insn(groups, insn_options):
 
     for lhs_i in lhs:
         if isinstance(lhs_i, TypeAnnotation):
-            assert isinstance(lhs_i.type, Optional)
-            temp_var_types.append(lhs_i.type)
+            if lhs_i.type is None:
+                temp_var_types.append(lp.auto)
+            else:
+                temp_var_types.append(lhs_i.type)
+
             lhs_i = lhs_i.child
         else:
-            temp_var_types.append(Optional())
+            temp_var_types.append(None)
 
         inner_lhs_i = lhs_i
         if isinstance(inner_lhs_i, Lookup):
@@ -1134,9 +1138,9 @@ class ArgumentGuesser:
 
     def make_new_arg(self, arg_name):
         arg_name = arg_name.strip()
-        import loopy as lp
 
         from loopy.kernel.data import ValueArg, ArrayArg, AddressSpace
+        import loopy as lp
 
         if arg_name in self.all_params:
             return ValueArg(arg_name)
@@ -1187,7 +1191,7 @@ class ArgumentGuesser:
                 for assignee_var_name, temp_var_type in zip(
                         insn.assignee_var_names(),
                         insn.temp_var_types):
-                    if temp_var_type.has_value:
+                    if temp_var_type is not None:
                         temp_var_names.add(assignee_var_name)
 
         # }}}
@@ -1427,7 +1431,7 @@ def create_temporaries(knl, default_order):
                     insn.assignee_var_names(),
                     insn.temp_var_types):
 
-                if not temp_var_type.has_value:
+                if temp_var_type is None:
                     continue
 
                 if assignee_name in new_temp_vars:
@@ -1442,17 +1446,17 @@ def create_temporaries(knl, default_order):
 
                 new_temp_vars[assignee_name] = lp.TemporaryVariable(
                         name=assignee_name,
-                        dtype=temp_var_type.value,
+                        dtype=temp_var_type,
                         address_space=lp.auto,
                         base_indices=lp.auto,
                         shape=lp.auto,
                         order=default_order,
                         target=knl.target)
 
-            if isinstance(insn, Assignment):
-                insn = insn.copy(temp_var_type=Optional())
-            else:
-                insn = insn.copy(temp_var_types=(Optional(),) * len(insn.assignees))
+                if isinstance(insn, Assignment):
+                    insn = insn.copy(temp_var_type=None)
+                else:
+                    insn = insn.copy(temp_var_types=None)
 
         new_insns.append(insn)
 
diff --git a/loopy/kernel/data.py b/loopy/kernel/data.py
index 8103029dc5f86896b4e08dd9e277b81b01e9ca27..7877f8b939444bf3dc095037ffeaa1bb548c39d6 100644
--- a/loopy/kernel/data.py
+++ b/loopy/kernel/data.py
@@ -330,9 +330,8 @@ class KernelArgument(ImmutableRecord):
 
         import loopy as lp
         if dtype is lp.auto:
-            warn("Argument/temporary data type for '%s' should be None if "
-                   "unspecified, not auto. This usage will be disallowed in 2018."
-                    % kwargs["name"],
+            warn("Argument/temporary data type should be None if unspecified, "
+                    "not auto. This usage will be disallowed in 2018.",
                     DeprecationWarning, stacklevel=2)
 
             dtype = None
diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py
index d7784eabff6a61b2c65425e2ba9506aa854a0d6e..e9c7bde9fd937dd58b5fa44476280a4b0c793dec 100644
--- a/loopy/kernel/instruction.py
+++ b/loopy/kernel/instruction.py
@@ -25,7 +25,6 @@ THE SOFTWARE.
 from six.moves import intern
 from pytools import ImmutableRecord, memoize_method
 from loopy.diagnostic import LoopyError
-from loopy.tools import Optional
 from warnings import warn
 
 
@@ -839,9 +838,8 @@ class Assignment(MultiAssignmentBase):
 
     .. attribute:: temp_var_type
 
-        A :class:`loopy.Optional`. If not empty, contains the type that
-        will be assigned to the new temporary variable created from the
-        assignment.
+        if not *None*, a type that will be assigned to the new temporary variable
+        created from the assignee
 
     .. attribute:: atomicity
 
@@ -895,7 +893,7 @@ class Assignment(MultiAssignmentBase):
             within_inames_is_final=None,
             within_inames=None,
             boostable=None, boostable_into=None, tags=None,
-            temp_var_type=Optional(), atomicity=(),
+            temp_var_type=None, atomicity=(),
             priority=0, predicates=frozenset(),
             insn_deps=None, insn_deps_is_final=None,
             forced_iname_deps=None, forced_iname_deps_is_final=None):
@@ -1008,9 +1006,8 @@ class CallInstruction(MultiAssignmentBase):
 
     .. attribute:: temp_var_types
 
-        A tuple of `:class:loopy.Optional`. If an entry is not empty, it
-        contains the type that will be assigned to the new temporary variable
-        created from the assigment.
+        if not *None*, a type that will be assigned to the new temporary variable
+        created from the assignee
 
     .. automethod:: __init__
     """
@@ -1082,7 +1079,7 @@ class CallInstruction(MultiAssignmentBase):
         self.expression = expression
 
         if temp_var_types is None:
-            self.temp_var_types = (Optional(),) * len(self.assignees)
+            self.temp_var_types = (None,) * len(self.assignees)
         else:
             self.temp_var_types = temp_var_types
 
@@ -1130,33 +1127,34 @@ class CallInstruction(MultiAssignmentBase):
 
 
 def make_assignment(assignees, expression, temp_var_types=None, **kwargs):
-    if temp_var_types is None:
-        temp_var_types = (Optional(),) * len(assignees)
+    if len(assignees) > 1 or len(assignees) == 0:
+        atomicity = kwargs.pop("atomicity", ())
+        if atomicity:
+            raise LoopyError("atomic operations with more than one "
+                    "left-hand side not supported")
 
-    if len(assignees) == 1:
+        from pymbolic.primitives import Call
+        from loopy.symbolic import Reduction
+        if not isinstance(expression, (Call, Reduction)):
+            raise LoopyError("right-hand side in multiple assignment must be "
+                    "function call or reduction, got: '%s'" % expression)
+
+        return CallInstruction(
+                assignees=assignees,
+                expression=expression,
+                temp_var_types=temp_var_types,
+                **kwargs)
+
+    else:
         return Assignment(
                 assignee=assignees[0],
                 expression=expression,
-                temp_var_type=temp_var_types[0],
+                temp_var_type=(
+                    temp_var_types[0]
+                    if temp_var_types is not None
+                    else None),
                 **kwargs)
 
-    atomicity = kwargs.pop("atomicity", ())
-    if atomicity:
-        raise LoopyError("atomic operations with more than one "
-                "left-hand side not supported")
-
-    from pymbolic.primitives import Call
-    from loopy.symbolic import Reduction
-    if not isinstance(expression, (Call, Reduction)):
-        raise LoopyError("right-hand side in multiple assignment must be "
-                "function call or reduction, got: '%s'" % expression)
-
-    return CallInstruction(
-            assignees=assignees,
-            expression=expression,
-            temp_var_types=temp_var_types,
-            **kwargs)
-
 
 # {{{ c instruction
 
diff --git a/loopy/symbolic.py b/loopy/symbolic.py
index ae0c8999eef2b4cfea2ff89eee28a85173916628..f4d46854b8dd15c8c1e9a716017ce2724b4db2fc 100644
--- a/loopy/symbolic.py
+++ b/loopy/symbolic.py
@@ -1131,15 +1131,14 @@ class LoopyParser(ParserBase):
 
     def parse_prefix(self, pstate):
         from pymbolic.parser import _PREC_UNARY, _less, _greater, _identifier
-        import loopy as lp
         if pstate.is_next(_less):
             pstate.advance()
             if pstate.is_next(_greater):
-                typename = lp.Optional(None)
+                typename = None
                 pstate.advance()
             else:
                 pstate.expect(_identifier)
-                typename = lp.Optional(pstate.next_str())
+                typename = pstate.next_str()
                 pstate.advance()
                 pstate.expect(_greater)
                 pstate.advance()
diff --git a/loopy/tools.py b/loopy/tools.py
index 470921e31293192b6e96e90b02ec41968bc5fad9..7e9a8921477403a280f08c6c79fe380d5cb5b2f3 100644
--- a/loopy/tools.py
+++ b/loopy/tools.py
@@ -587,78 +587,6 @@ class LazilyUnpicklingListWithEqAndPersistentHashing(LazilyUnpicklingList):
 # }}}
 
 
-# {{{ optional object
-
-class _no_value(object):  # noqa
-    pass
-
-
-class Optional(object):
-    """A wrapper for an optionally present object.
-
-    .. attribute:: has_value
-
-        *True* if and only if this object contains a value.
-
-    .. attribute:: value
-
-        The value, if present.
-    """
-
-    __slots__ = ("has_value", "_value")
-
-    def __init__(self, value=_no_value):
-        self.has_value = value is not _no_value
-        if self.has_value:
-            self._value = value
-
-    def __str__(self):
-        if not self.has_value:
-            return "Optional()"
-        return "Optional(%s)" % self._value
-
-    def __repr__(self):
-        if not self.has_value:
-            return "Optional()"
-        return "Optional(%r)" % self._value
-
-    def __getstate__(self):
-        if not self.has_value:
-            return _no_value
-
-        return (self._value,)
-
-    def __setstate__(self, state):
-        if state is _no_value:
-            self.has_value = False
-            return
-
-        self.has_value = True
-        self._value, = state
-
-    def __eq__(self, other):
-        if not self.has_value:
-            return not other.has_value
-
-        return self.value == other.value if other.has_value else False
-
-    def __neq__(self, other):
-        return not self.__eq__(other)
-
-    @property
-    def value(self):
-        if not self.has_value:
-            raise AttributeError("optional value not present")
-        return self._value
-
-    def update_persistent_hash(self, key_hash, key_builder):
-        key_builder.rec(
-                key_hash,
-                (self._value,) if self.has_value else ())
-
-# }}}
-
-
 def unpickles_equally(obj):
     from six.moves.cPickle import loads, dumps
     return loads(dumps(obj)) == obj
diff --git a/test/test_misc.py b/test/test_misc.py
index 7a834a6f5d393298e97df22d47a1de3b64354a42..05df0317a6a39823dc8ac0c6a51d992336bb81d1 100644
--- a/test/test_misc.py
+++ b/test/test_misc.py
@@ -287,46 +287,6 @@ def test_LazilyUnpicklingListWithEqAndPersistentHashing():
     # }}}
 
 
-def test_Optional():  # noqa
-    from loopy import Optional
-
-    # {{{ test API
-
-    opt = Optional()
-    assert not opt.has_value
-    with pytest.raises(AttributeError):
-        opt.value
-
-    opt = Optional(1)
-    assert opt.has_value
-    assert 1 == opt.value
-
-    assert Optional(1) == Optional(1)
-    assert Optional(1) != Optional(2)
-    assert Optional() == Optional()
-    assert Optional() != Optional(1)
-
-    # }}}
-
-    # {{{ test pickling
-
-    import pickle
-
-    assert not pickle.loads(pickle.dumps(Optional())).has_value
-    assert pickle.loads(pickle.dumps(Optional(1))).value == 1
-
-    # }}}
-
-    # {{{ test key builder
-
-    from loopy.tools import LoopyKeyBuilder
-    kb = LoopyKeyBuilder()
-    kb(Optional())
-    kb(Optional(None))
-
-    # }}}
-
-
 if __name__ == "__main__":
     if len(sys.argv) > 1:
         exec(sys.argv[1])