Remove option type from OptionKey and get it from OptionStore instead.
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 1659d51..6fd0da0 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -22,7 +22,7 @@
     pickle_load
 )
 
-from .options import OptionKey, OptionType
+from .options import OptionKey
 
 from .machinefile import CmdLineFileParser
 
@@ -586,8 +586,6 @@
 
     def update_project_options(self, project_options: 'MutableKeyedOptionDictType', subproject: SubProject) -> None:
         for key, value in project_options.items():
-            if not self.optstore.is_project_option(key):
-                continue
             if key not in self.optstore:
                 self.optstore.add_project_option(key, value)
                 continue
@@ -654,7 +652,7 @@
                 continue
             elif k in self.optstore:
                 dirty |= self.set_option(k, v, first_invocation)
-            elif k.machine != MachineChoice.BUILD and k.type != OptionType.COMPILER:
+            elif k.machine != MachineChoice.BUILD and not self.optstore.is_compiler_option(k):
                 unknown_options.append(k)
         if unknown_options:
             unknown_options_str = ', '.join(sorted(str(s) for s in unknown_options))
@@ -700,9 +698,9 @@
                 continue
             # Skip base, compiler, and backend options, they are handled when
             # adding languages and setting backend.
-            if k.type in {OptionType.COMPILER, OptionType.BACKEND}:
+            if self.optstore.is_compiler_option(k) or self.optstore.is_backend_option(k):
                 continue
-            if k.type == OptionType.BASE and k.as_root() in base_options:
+            if self.optstore.is_base_option(k) and k.as_root() in base_options:
                 # set_options will report unknown base options
                 continue
             options[k] = v
@@ -908,7 +906,9 @@
         # FIXME: This is fundamentally the same algorithm than interpreter.get_option_internal().
         # We should try to share the code somehow.
         key = key.evolve(subproject=self.subproject)
-        if not key.is_project_hack_for_optionsview():
+        if not isinstance(self.original_options, options.OptionStore):
+            sys.exit('FAIL.')
+        if not self.original_options.is_project_option(key):
             opt = self.original_options.get(key)
             if opt is None or opt.yielding:
                 key2 = key.as_root()
diff --git a/mesonbuild/options.py b/mesonbuild/options.py
index f719517..9318351 100644
--- a/mesonbuild/options.py
+++ b/mesonbuild/options.py
@@ -39,32 +39,6 @@
 buildtypelist = ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']
 
 
-class OptionType(enum.IntEnum):
-
-    """Enum used to specify what kind of argument a thing is."""
-
-    BUILTIN = 0
-    BACKEND = 1
-    BASE = 2
-    COMPILER = 3
-    PROJECT = 4
-
-def _classify_argument(key: 'OptionKey') -> OptionType:
-    """Classify arguments into groups so we know which dict to assign them to."""
-
-    if key.name.startswith('b_'):
-        return OptionType.BASE
-    elif key.lang is not None:
-        return OptionType.COMPILER
-    elif key.name in _BUILTIN_NAMES or key.module:
-        return OptionType.BUILTIN
-    elif key.name.startswith('backend_'):
-        assert key.machine is MachineChoice.HOST, str(key)
-        return OptionType.BACKEND
-    else:
-        assert key.machine is MachineChoice.HOST, str(key)
-        return OptionType.PROJECT
-
 # This is copied from coredata. There is no way to share this, because this
 # is used in the OptionKey constructor, and the coredata lists are
 # OptionKeys...
@@ -117,21 +91,19 @@
     internally easier to reason about and produce.
     """
 
-    __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'type', 'module']
+    __slots__ = ['name', 'subproject', 'machine', 'lang', '_hash', 'module']
 
     name: str
     subproject: str
     machine: MachineChoice
     lang: T.Optional[str]
     _hash: int
-    type: OptionType
     module: T.Optional[str]
 
     def __init__(self, name: str, subproject: str = '',
                  machine: MachineChoice = MachineChoice.HOST,
                  lang: T.Optional[str] = None,
-                 module: T.Optional[str] = None,
-                 _type: T.Optional[OptionType] = None):
+                 module: T.Optional[str] = None):
         # the _type option to the constructor is kinda private. We want to be
         # able tos ave the state and avoid the lookup function when
         # pickling/unpickling, but we need to be able to calculate it when
@@ -142,9 +114,6 @@
         object.__setattr__(self, 'lang', lang)
         object.__setattr__(self, 'module', module)
         object.__setattr__(self, '_hash', hash((name, subproject, machine, lang, module)))
-        if _type is None:
-            _type = _classify_argument(self)
-        object.__setattr__(self, 'type', _type)
 
     def __setattr__(self, key: str, value: T.Any) -> None:
         raise AttributeError('OptionKey instances do not support mutation.')
@@ -155,7 +124,6 @@
             'subproject': self.subproject,
             'machine': self.machine,
             'lang': self.lang,
-            '_type': self.type,
             'module': self.module,
         }
 
@@ -173,8 +141,8 @@
     def __hash__(self) -> int:
         return self._hash
 
-    def _to_tuple(self) -> T.Tuple[str, OptionType, str, str, MachineChoice, str]:
-        return (self.subproject, self.type, self.lang or '', self.module or '', self.machine, self.name)
+    def _to_tuple(self) -> T.Tuple[str, str, str, MachineChoice, str]:
+        return (self.subproject, self.lang or '', self.module or '', self.machine, self.name)
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, OptionKey):
@@ -199,7 +167,7 @@
         return out
 
     def __repr__(self) -> str:
-        return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r}, {self.lang!r}, {self.module!r}, {self.type!r})'
+        return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r}, {self.lang!r}, {self.module!r})'
 
     @classmethod
     def from_string(cls, raw: str) -> 'OptionKey':
@@ -712,6 +680,7 @@
 class OptionStore:
     def __init__(self):
         self.d: T.Dict['OptionKey', 'UserOption[T.Any]'] = {}
+        self.project_options = set()
 
     def __len__(self):
         return len(self.d)
@@ -734,6 +703,7 @@
     def add_project_option(self, key: T.Union[OptionKey, str], valobj: 'UserOption[T.Any]'):
         key = self.ensure_key(key)
         self.d[key] = valobj
+        self.project_options.add(key)
 
     def set_value(self, key: T.Union[OptionKey, str], new_value: 'T.Any') -> bool:
         key = self.ensure_key(key)
@@ -774,23 +744,38 @@
 
     def is_project_option(self, key: OptionKey) -> bool:
         """Convenience method to check if this is a project option."""
-        return key.type is OptionType.PROJECT
+        return key in self.project_options
 
     def is_reserved_name(self, key: OptionKey) -> bool:
-        return not self.is_project_option(key)
+        from .compilers import all_languages
+        abc = all_languages
+        if key.name in _BUILTIN_NAMES:
+            return True
+        # FIXME, this hack is needed until the lang field is removed from OptionKey.
+        if key.lang is not None:
+            return True
+        if '_' not in key.name:
+            return False
+        prefix = key.name.split('_')[0]
+        if prefix in  ('b', 'backend'):
+            return True
+        from .compilers import all_languages
+        if prefix in all_languages:
+            return True
+        return False
 
     def is_builtin_option(self, key: OptionKey) -> bool:
         """Convenience method to check if this is a builtin option."""
-        return key.type is OptionType.BUILTIN
+        return key.name in _BUILTIN_NAMES or key.module
 
     def is_base_option(self, key: OptionKey) -> bool:
         """Convenience method to check if this is a base option."""
-        return key.type is OptionType.BASE
+        return key.name.startswith('b_')
 
     def is_backend_option(self, key: OptionKey) -> bool:
         """Convenience method to check if this is a backend option."""
-        return key.type is OptionType.BACKEND
+        return key.name.startswith('backend_')
 
     def is_compiler_option(self, key: OptionKey) -> bool:
         """Convenience method to check if this is a compiler option."""
-        return key.type is OptionType.COMPILER
+        return key.lang is not None
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index 5e16353..3a5ad72 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -3758,9 +3758,9 @@
 
               User defined options
                 backend        : ''' + self.backend_name + '''
+                enabled_opt    : enabled
                 libdir         : lib
                 prefix         : /usr
-                enabled_opt    : enabled
                 python         : ''' + sys.executable + '''
             ''')
         expected_lines = expected.split('\n')[1:]
@@ -3770,6 +3770,8 @@
             if e.startswith('    external dep'):
                 self.assertRegex(o, r'^    external dep   : (YES [0-9.]*|NO)$')
             else:
+                if o != e:
+                    pass
                 self.assertEqual(o, e)
 
     def test_meson_compile(self):
diff --git a/unittests/internaltests.py b/unittests/internaltests.py
index 109f28c..bbdf2d9 100644
--- a/unittests/internaltests.py
+++ b/unittests/internaltests.py
@@ -34,7 +34,7 @@
     LibType, MachineChoice, PerMachine, Version, is_windows, is_osx,
     is_cygwin, is_openbsd, search_version, MesonException,
 )
-from mesonbuild.options import OptionKey, OptionType
+from mesonbuild.options import OptionKey
 from mesonbuild.interpreter.type_checking import in_set_validator, NoneType
 from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface, PkgConfigCLI
 from mesonbuild.programs import ExternalProgram
@@ -1704,16 +1704,16 @@
 
     def test_option_key_from_string(self) -> None:
         cases = [
-            ('c_args', OptionKey('args', lang='c', _type=OptionType.COMPILER)),
-            ('build.cpp_args', OptionKey('args', machine=MachineChoice.BUILD, lang='cpp', _type=OptionType.COMPILER)),
-            ('prefix', OptionKey('prefix', _type=OptionType.BUILTIN)),
-            ('made_up', OptionKey('made_up', _type=OptionType.PROJECT)),
+            ('c_args', OptionKey('args', lang='c')),
+            ('build.cpp_args', OptionKey('args', machine=MachineChoice.BUILD, lang='cpp')),
+            ('prefix', OptionKey('prefix')),
+            ('made_up', OptionKey('made_up')),
 
             # TODO: the from_String method should be splitting the prefix off of
             # these, as we have the type already, but it doesn't. For now have a
             # test so that we don't change the behavior un-intentionally
-            ('b_lto', OptionKey('b_lto', _type=OptionType.BASE)),
-            ('backend_startup_project', OptionKey('backend_startup_project', _type=OptionType.BACKEND)),
+            ('b_lto', OptionKey('b_lto')),
+            ('backend_startup_project', OptionKey('backend_startup_project')),
         ]
 
         for raw, expected in cases: