private function _compile()
    {
        // There's nothing to do if the class is already in the cache.
        if (isset($this->_propertyMapCache->classMaps[$this->_solveClassName])) {
            return; // Abort.
        }

        // Let's compile the desired "solve-class", since it wasn't cached.
        // NOTE: This entire algorithm is EXCESSIVELY commented so that everyone
        // will understand it, since it's very complex thanks to its support for
        // inheritance and "multiple inheritance" (imports), which in turn uses
        // advanced anti-circular dependency protection to detect bad imports.
        // As well as its automatic end-of-run compilation of any encountered,
        // current uncompiled classes from any properties in the tree.

        // Prepare the class hierarchy for compilation.
        $this->_reflectSolveClassHierarchy(); // Throws.
        $this->_checkCurrentLocks(); // Throws.
        $this->_lockUncompiledClasses();

        // Traverse the class hierarchy and compile and merge their property
        // maps, giving precedence to later classes in case of name clashes.
        //
        // And because we build the list top-down through the chain of class
        // inheritance (starting at base and going to the deepest child), and
        // thanks to the fact that PHP classes can only extend from 1 parent
        // class, it also means that we're actually able to save the per-class
        // lists at every step of the way, for each class we encounter along the
        // way. To avoid needing to process those later.
        //
        // This builds a properly inherited class property map and constructs
        // and validates all property definitions so that we don't get any nasty
        // surprises during data-parsing later.
        //
        // The top-down order also ensures that we re-use our parent's inherited
        // PropertyDefinition objects, so that the compiled classes all share
        // memory whenever they inherit from the same parent classes & imports.
        try {
            foreach ($this->_classHierarchy as $classInfo) {
                $this->_currentClassInfo = $classInfo;
                $this->_processCurrentClass(); // Throws.
            }
        } finally {
            // IMPORTANT: If we've compiled all classes, or if uncaught
            // exceptions were thrown during processing, then we must simply
            // ensure that we unlock all of OUR remaining locks before we allow
            // the exception to keep bubbling upwards OR processing to continue.
            // NOTE: We AREN'T allowed to unlock classes we didn't lock.
            foreach ($this->_ourCompilerClassLocks as $lockedClassName => $x) {
                unset($this->_ourCompilerClassLocks[$lockedClassName]);
                unset($this->_propertyMapCache->compilerLocks[$lockedClassName]);
            }
        }

        // If we've reached this point, it means that our solve-class SHOULD be
        // in the cache since its entire compilation ran successfully above.
        // Just verify it to be extra safe against any random future mistakes.
        // NOTE: This step should never go wrong.
        if (!isset($this->_propertyMapCache->classMaps[$this->_solveClassName])) {
            throw new BadPropertyMapException(sprintf(
                'Error while compiling class "%s". Could not find the class in the cache afterwards.',
                $this->_strictSolveClassName
            ));
        }

        // If we came this far, it means that NOTHING above threw, which means
        // that our class and its hierarchy of inheritance (and map-imports), as
        // well as all PropertyDefinitions, have succeeded for the whole tree...
        //
        // Now the only remaining question is whether it's safe for us to check
        // the list of encountered classes in properties and ensure that those
        // can all be compiled too... And the answer is NO, unless we are the
        // absolute ROOT CALL which began the whole compilation process. If not,
        // then we should merely finish cleaning up our list of encountered
        // "uncompiled" classes and then let our parent-caller deal with it.

        // If we are a subcall (sub-compilation within a main compilation),
        // simply let our parent compiler bubble our data to the root call...
        if (!$this->_isRootCompiler) {
            // Some already-processed classes may have slipped onto the list due
            // to being encountered as properties before those classes became
            // compiled. So before returning to the parent, we'll just ensure
            // that we remove all keys that refer to classes that have already
            // been compiled. That saves RAM and processing power during the
            // array merging in the parent. Basically, we want this list to
            // always be as short as possible so there's less need for any HUGE
            // array manipulation at a higher stage while it's bubbling up.
            foreach ($this->uncompiledPropertyClasses as $uncompiledClassName => $x) {
                if (isset($this->_propertyMapCache->classMaps[$uncompiledClassName])) {
                    unset($this->uncompiledPropertyClasses[$uncompiledClassName]);
                }
            }

            return; // Skip the rest of the code.
        }

        // We're the root call! So it is now safe (and IMPORTANT!) to compile
        // all of the not-yet-compiled classes from the inheritance tree, to
        // ensure that EVERY part of the tree is ready as far as classmaps go.
        // NOTE: We have no information about how deeply the encountered classes
        // existed. But it doesn't matter, since they may exist in multiple
        // places. If there's a problem we'll simply say that "the class (this
        // initial root/non-subcall one) or one of its parents or imports has a
        // property which is linked to bad class X". Especially since there may
        // also be problems within properties of THESE classes that we're going
        // to compile. So trying to pinpoint exactly which class had the bad
        // reference to an uncompilable class is overkill. We'll just show the
        // uncompilable class name plus its regular high-quality compilation
        // error message which describes why that class failed to compile.
        // The user should simply fix their code in THAT particular class.
        while (!empty($this->uncompiledPropertyClasses)) {
            // IMPORTANT: Create a COPY-ON-WRITE version of the current contents
            // of the "uncompiledPropertyClasses" array. Because that array will
            // be changing whenever we sub-compile, so we'll use a stable COPY.
            $workQueue = $this->uncompiledPropertyClasses;

            // Process all entries (classes to compile) in the current queue.
            foreach ($workQueue as $uncompiledClassName => $x) {
                // Skip this class if it's already been successfully compiled.
                if (isset($this->_propertyMapCache->classMaps[$uncompiledClassName])) {
                    unset($this->uncompiledPropertyClasses[$uncompiledClassName]);
                    continue;
                }

                // Attempt to compile the missing class. We do this one by one
                // in isolation, so that each extra class we compile gets its
                // entirely own competition-free sub-compiler with its own
                // class-locks (since our own top-level rootCompiler's hierarchy
                // is already fully compiled by this point). Which means that
                // the sub-classes are welcome to refer to anything we've
                // already compiled, exactly as if each of these extra classes
                // were new top-level compiles "running in isolation". The only
                // difference is that they aren't marked as the root compiler,
                // since we still want to be the one to resolve all of THEIR
                // uncompiled property-classes here during THIS loop. Mainly so
                // that we can be the one to throw exception messages, referring
                // to the correct root-level class as the one that failed.
                try {
                    // NOTE: If this subcompile is successful and encounters any
                    // more uncompiled property-classes within the classes it
                    // compiles, they'll be automatically added to OUR OWN list
                    // of "uncompiledPropertyClasses" and WILL be resolved too.
                    //
                    // This will carry on until ALL of the linked classes are
                    // compiled and no more work remains. In other words, we
                    // will resolve the COMPLETE hierarchies of every linked
                    // class and ALL of their properties too. However, this
                    // subcompilation is still very fast since most end-stage
                    // jobs refer to classes that are already mostly-compiled
                    // due to having most of their own dependencies already
                    // pre-compiled at that point.
                    $this->_subcompile($uncompiledClassName); // Throws.
                } catch (\Exception $e) { // NOTE: Could target exact type, but meh.
                    // Failed to compile the class we discovered in a property.
                    // It can be due to all kinds of problems, such as circular
                    // property maps or bad imports or bad definitions or
                    // corrupt map variables or anything else. We'll wrap the
                    // error message in a slightly prefixed message just to hint
                    // that this problem is with a property. Because the
                    // compilation error message itself is already very clear
                    // about which class failed to compile and why.
                    // NOTE: This "prefixed" message handling is unlike parents
                    // and imports, where we simply let their original exception
                    // message bubble up as if they were part of the core class,
                    // since imports are a more "integral" part of a class (a
                    // class literally CANNOT be built without its parents and
                    // its imports compiling). But CLASSES IN PROPERTIES are
                    // different and are more numerous and are capable of being
                    // resolved by _getProperty() LATER without having been
                    // pre-compiled when the main class map itself was compiled
                    // (although we just DID that pre-compilation above; we'll
                    // NEVER let them wait to get resolved later).
                    // NOTE: This WILL cause us to lose the specific class-type
                    // of the exception, such as CircularPropertyMapException,
                    // etc. That's intentional, since THIS error is about a bad
                    // map in the PARENT-hierarchy (IT pointing at a bad class).
                    throw new BadPropertyMapException(sprintf(
                        'Compilation of sub-property hierarchy failed for class "%s". Reason: %s',
                        $this->_strictSolveClassName, $e->getMessage()
                    ));
                } // End of _subcompile() try-catch.

                // The sub-compile was successful (nothing was thrown), which
                // means that it's now in the compiled property map cache. Let's
                // unset its entry from our list of uncompiled classes.
                unset($this->uncompiledPropertyClasses[$uncompiledClassName]);
            } // End of work-queue loop.
        } // End of "handle uncompiled property classes" loop.
    }