diff --git a/includes/theme.inc b/includes/theme.inc
index 9bd7062..7944843 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -363,29 +363,47 @@ function drupal_theme_rebuild() {
 function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
   $result = array();
 
-  // Processor functions work in two distinct phases with the process
-  // functions always being executed after the preprocess functions.
-  $variable_process_phases = array(
-    'preprocess functions' => 'preprocess',
-    'process functions'    => 'process',
-  );
-
-  $hook_defaults = array(
-    'variables' => TRUE,
-    'render element' => TRUE,
-    'pattern' => TRUE,
-    'base hook' => TRUE,
-  );
-
   // Invoke the hook_theme() implementation, process what is returned, and
   // merge it into $cache.
   $function = $name . '_theme';
   if (function_exists($function)) {
     $result = $function($cache, $type, $theme, $path);
+
+    // Processor functions work in two distinct phases with the process
+    // functions always being executed after the preprocess functions.
+    $variable_process_phases = array(
+      'preprocess functions' => 'preprocess',
+      'process functions'    => 'process',
+    );
+
+    // Prepare prefixes for processor functions.
+    $prefixes = array();
+    if ($type == 'module') {
+      // Default variable processor prefix.
+      $prefixes[] = 'template';
+      // Add all modules so they can intervene with their own variable
+      // processors. This allows them to provide variable processors even
+      // if they are not the owner of the current hook.
+      $prefixes += module_list();
+    }
+    elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
+      // Theme engines get an extra set that come before the normally
+      // named variable processors.
+      $prefixes[] = $name . '_engine';
+      // The theme engine registers on behalf of the theme using the
+      // theme's name.
+      $prefixes[] = $theme;
+    }
+    else {
+      // This applies when the theme manually registers their own variable
+      // processors.
+      $prefixes[] = $name;
+    }
+
     foreach ($result as $hook => $info) {
       // When a theme or engine overrides a module's theme function
       // $result[$hook] will only contain key/value pairs for information being
-      // overridden.  Pull the rest of the information from what was defined by
+      // overridden. Pull the rest of the information from what was defined by
       // an earlier hook.
 
       // Fill in the type and path of the module, theme, or engine that
@@ -393,72 +411,65 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
       $result[$hook]['type'] = $type;
       $result[$hook]['theme path'] = $path;
 
-      // If function and file are omitted, default to standard naming
-      // conventions.
+      // If a theme function or template file is not defined, default to the
+      // standard naming convention for theme functions.
       if (!isset($info['template']) && !isset($info['function'])) {
         $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook;
       }
 
+      // Collect previously set files for inclusion.
       if (isset($cache[$hook]['includes'])) {
         $result[$hook]['includes'] = $cache[$hook]['includes'];
       }
 
-      // If the theme implementation defines a file, then also use the path
-      // that it defined. Otherwise use the default path. This allows
+      // If the theme implementation defines a file to include, then also use
+      // the path that it defined. Otherwise use the default path. This allows
       // system.module to declare theme functions on behalf of core .include
-      // files.
+      // files. These files will be included once by theme() calls.
+      $themed_path = isset($info['path']) ? $info['path'] : $path;
+
       if (isset($info['file'])) {
-        $include_file = isset($info['path']) ? $info['path'] : $path;
-        $include_file .= '/' . $info['file'];
-        include_once DRUPAL_ROOT . '/' . $include_file;
-        $result[$hook]['includes'][] = $include_file;
+        include_once DRUPAL_ROOT . '/' . $themed_path . '/' . $info['file'];
+        $result[$hook]['includes'][] = $themed_path . '/' . $info['file'];
+      }
+
+      // Do the same for template files.
+      if (isset($info['template'])) {
+        $result[$hook]['template'] = $themed_path . '/' . $info['template'];
       }
 
       // If the default keys are not set, use the default values registered
       // by the module.
       if (isset($cache[$hook])) {
+        $hook_defaults = array(
+          'variables'      => TRUE,
+          'render element' => TRUE,
+          'pattern'        => TRUE,
+          'base hook'      => TRUE,
+        );
         $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
       }
 
-      // The following apply only to theming hooks implemented as templates.
-      if (isset($info['template'])) {
-        // Prepend the current theming path when none is set.
-        if (!isset($info['path'])) {
-          $result[$hook]['template'] = $path . '/' . $info['template'];
-        }
+      $current_prefixes = $prefixes;
+      if (isset($cache[$hook]['function']) && $cache[$hook]['type'] === 'module' && isset($info['template'])) {
+        // If the default implementation is a function, but a template
+        // overrides that default implementation, introduce the default
+        // template processors. It is assumed that when it is implemented as
+        // a theme function, it must run as quickly as possible so only the
+        // default varible processors are introduced. The ones provided by
+        // modules not directly related to this theming hook will be ignored.
+        $current_prefixes = array('template') + $current_prefixes;
       }
 
       // Allow variable processors for all theming hooks, whether the hook is
       // implemented as a template or as a function.
       foreach ($variable_process_phases as $phase_key => $phase) {
-        // Check for existing variable processors. Ensure arrayness.
-        if (!isset($info[$phase_key]) || !is_array($info[$phase_key])) {
+        // Check for existing variable processors set through hook_theme().
+        if (!isset($info[$phase_key])) {
           $info[$phase_key] = array();
-          $prefixes = array();
-          if ($type == 'module') {
-            // Default variable processor prefix.
-            $prefixes[] = 'template';
-            // Add all modules so they can intervene with their own variable
-            // processors. This allows them to provide variable processors even
-            // if they are not the owner of the current hook.
-            $prefixes += module_list();
-          }
-          elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
-            // Theme engines get an extra set that come before the normally
-            // named variable processors.
-            $prefixes[] = $name . '_engine';
-            // The theme engine registers on behalf of the theme using the
-            // theme's name.
-            $prefixes[] = $theme;
-          }
-          else {
-            // This applies when the theme manually registers their own variable
-            // processors.
-            $prefixes[] = $name;
-          }
-          foreach ($prefixes as $prefix) {
+          foreach ($current_prefixes as $prefix) {
             // Only use non-hook-specific variable processors for theming hooks
-            // implemented as templates. See theme().
+            // implemented as templates.
             if (isset($info['template']) && function_exists($prefix . '_' . $phase)) {
               $info[$phase_key][] = $prefix . '_' . $phase;
             }
@@ -474,7 +485,7 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
           // Flag not needed inside the registry.
           unset($result[$hook]['override ' . $phase_key]);
         }
-        elseif (isset($cache[$hook][$phase_key]) && is_array($cache[$hook][$phase_key])) {
+        elseif (isset($cache[$hook][$phase_key])) {
           $info[$phase_key] = array_merge($cache[$hook][$phase_key], $info[$phase_key]);
         }
         $result[$hook][$phase_key] = $info[$phase_key];
@@ -484,31 +495,90 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
     // Merge the newly created theme hooks into the existing cache.
     $cache = $result + $cache;
   }
+}
 
-  // Let themes have variable processors even if they didn't register a template.
-  if ($type == 'theme' || $type == 'base_theme') {
-    foreach ($cache as $hook => $info) {
-      // Check only if not registered by the theme or engine.
-      if (empty($result[$hook])) {
-        foreach ($variable_process_phases as $phase_key => $phase) {
-          if (!isset($info[$phase_key])) {
-            $cache[$hook][$phase_key] = array();
-          }
-          // Only use non-hook-specific variable processors for theming hooks
-          // implemented as templates. See theme().
-          if (isset($info['template']) && function_exists($name . '_' . $phase)) {
-            $cache[$hook][$phase_key][] = $name . '_' . $phase;
-          }
-          if (function_exists($name . '_' . $phase . '_' . $hook)) {
-            $cache[$hook][$phase_key][] = $name . '_' . $phase . '_' . $hook;
-            $cache[$hook]['theme path'] = $path;
-          }
-          // Ensure uniqueness.
-          $cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]);
+/**
+ * This completes the theme registry adding missing functions and hooks.
+ */
+function _theme_post_process_registry(&$cache, $theme, $base_theme, $theme_engine) {
+
+  // Get all user defined functions.
+  list(, $user_func) = array_values(get_defined_functions());
+  $user_func = array_combine($user_func, $user_func);
+
+  // Gather prefixes. This will be used to limit the found functions to the
+  // expected naming conventions.
+  $prefixes = module_list();
+  if ($theme_engine) {
+    $prefixes[] = $theme_engine . '_engine';
+  }
+  foreach ($base_theme as $base) {
+    $prefixes[] = $base->name;
+  }
+  $prefixes[] = $theme->name;
+
+  // Collect all known hooks. Discovered functions must be based on a known hook.
+  $hooks = implode('|', array_keys($cache));
+
+  // Collect all variable processor functions in the correct order.
+  $processors = array();
+  foreach ($prefixes as $prefix) {
+    $processors += preg_grep("/^{$prefix}_(pre)?process_($hooks)(__)?/", $user_func);
+  }
+
+  // Add missing variable processors. This is needed for hooks that do not
+  // explictly register the hook. For example, when a theme contains a variable
+  // process function but it does not implement a template, it will go missing.
+  // This will add the expected function. It also allows modules or themes to
+  // have a variable process function based on a pattern even if the hook does
+  // not exist.
+  foreach ($processors as $processor) {
+    $hook  = substr($processor, strpos($processor, 'process_') + strlen('process_'));
+    $phase = strpos($processor, 'preprocess') ? 'preprocess functions' : 'process functions';
+
+    if (isset($cache[$hook][$phase]) && !in_array($processor, $cache[$hook][$phase])) {
+      // Add missing processor to existing hook.
+      $cache[$hook][$phase][] = $processor;
+    }
+    elseif (!isset($cache[$hook]) && strpos($hook, '__')) {
+      // Process non-existing hook and register it.
+      // Search for the base hook.
+      $base_hook = $hook;
+      while (!isset($cache[$base_hook]) && $pos = strrpos($base_hook, '__')) {
+        $base_hook = substr($base_hook, 0, $pos);
+        // If the current hook is based on a pattern, get the base hook.
+        if (isset($cache[$base_hook]['base hook'])) {
+          $base_hook = $cache[$base_hook]['base hook'];
+        }
+      }
+      if (isset($cache[$base_hook])) {
+        // Pull from the base hook and register the new
+        $cache[$hook] = $cache[$base_hook];
+        if (isset($cache[$hook][$phase])) {
+          $cache[$hook][$phase][] = $processor;
         }
       }
     }
   }
+
+  // Inherit all base hook variable processors into pattern hooks. This ensures
+  // that derivative hooks have a complete set of variable process functions.
+  foreach ($cache as $hook => $info) {
+    foreach (array('preprocess functions', 'process functions') as $phase) {
+      // The 'base hook' is only applied to derivative hooks already registered
+      // from a pattern. This is typically set from drupal_find_theme_functions()
+      // and drupal_find_theme_templates().
+      if (isset($info['base hook']) && isset($cache[$info['base hook']][$phase])) {
+        $diff = array_diff($cache[$info['base hook']][$phase], $info[$phase]);
+        $cache[$hook][$phase] = array_merge($diff, $info[$phase]);
+      }
+
+      // Optimize the registry.
+      if (isset($cache[$hook][$phase]) && empty($cache[$hook][$phase])) {
+        unset($cache[$hook][$phase]);
+      }
+    }
+  }
 }
 
 /**
@@ -548,17 +618,12 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) {
   // Finally, hooks provided by the theme itself.
   _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
 
+  // Finalize the build.
+  _theme_post_process_registry($cache, $theme, $base_theme, $theme_engine);
+
   // Let modules alter the registry.
   drupal_alter('theme_registry', $cache);
 
-  // Optimize the registry to not have empty arrays for functions.
-  foreach ($cache as $hook => $info) {
-    foreach (array('preprocess functions', 'process functions') as $phase) {
-      if (empty($info[$phase])) {
-        unset($cache[$hook][$phase]);
-      }
-    }
-  }
   return $cache;
 }
 
@@ -751,14 +816,13 @@ function list_themes($refresh = FALSE) {
 function theme($hook, $variables = array()) {
   static $hooks = NULL;
 
-  // If called before all modules are loaded, we do not necessarily have a full
-  // theme registry to work with, and therefore cannot process the theme
-  // request properly. See also _theme_load_registry().
-  if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) {
-    throw new Exception(t('theme() may not be called until all modules are loaded.'));
-  }
-
   if (!isset($hooks)) {
+    // If called before all modules are loaded, we do not necessarily have a full
+    // theme registry to work with, and therefore cannot process the theme
+    // request properly. See also _theme_load_registry().
+    if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) {
+      throw new Exception(t('theme() may not be called until all modules are loaded.'));
+    }
     drupal_theme_initialize();
     $hooks = theme_get_registry();
   }
@@ -806,6 +870,7 @@ function theme($hook, $variables = array()) {
     foreach ($info['includes'] as $include_file) {
       include_once DRUPAL_ROOT . '/' . $include_file;
     }
+    unset($hooks[$hook]['includes']);
   }
 
   // If a renderable array is passed as $variables, then set $variables to
@@ -814,39 +879,25 @@ function theme($hook, $variables = array()) {
     $element = $variables;
     $variables = array();
     if (isset($info['variables'])) {
-      foreach (array_keys($info['variables']) as $name) {
-        if (isset($element["#$name"])) {
-          $variables[$name] = $element["#$name"];
-        }
+      foreach ($info['variables'] as $name => $default) {
+        $variables[$name] = isset($element["#$name"]) ? $element["#$name"] : $default;
       }
     }
     else {
       $variables[$info['render element']] = $element;
     }
   }
-
-  // Merge in argument defaults.
-  if (!empty($info['variables'])) {
-    $variables += $info['variables'];
-  }
-  elseif (!empty($info['render element'])) {
-    $variables += array($info['render element'] => array());
-  }
-
-  // Invoke the variable processors, if any. The processors may specify
-  // alternate suggestions for which hook's template/function to use. If the
-  // hook is a suggestion of a base hook, invoke the variable processors of
-  // the base hook, but retain the suggestion as a high priority suggestion to
-  // be used unless overridden by a variable processor function.
-  if (isset($info['base hook'])) {
-    $base_hook = $info['base hook'];
-    $base_hook_info = $hooks[$base_hook];
-    if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
-      $variables['theme_hook_suggestion'] = $hook;
-      $hook = $base_hook;
-      $info = $base_hook_info;
+  else {
+    // Direct theme() call. Not passed from a renderable array.
+    // Merge in argument defaults.
+    if (!empty($info['variables'])) {
+      $variables += $info['variables'];
+    }
+    elseif (!empty($info['render element'])) {
+      $variables += array($info['render element'] => array());
     }
   }
+
   if (isset($info['preprocess functions']) || isset($info['process functions'])) {
     $variables['theme_hook_suggestions'] = array();
     foreach (array('preprocess functions', 'process functions') as $phase) {
@@ -890,9 +941,7 @@ function theme($hook, $variables = array()) {
   // Generate the output using either a function or a template.
   $output = '';
   if (isset($info['function'])) {
-    if (function_exists($info['function'])) {
-      $output = $info['function']($variables);
-    }
+    $output = $info['function']($variables);
   }
   else {
     // Default render function and extension.
@@ -913,28 +962,8 @@ function theme($hook, $variables = array()) {
       }
     }
 
-    // In some cases, a template implementation may not have had
-    // template_preprocess() run (for example, if the default implementation is
-    // a function, but a template overrides that default implementation). In
-    // these cases, a template should still be able to expect to have access to
-    // the variables provided by template_preprocess(), so we add them here if
-    // they don't already exist. We don't want to run template_preprocess()
-    // twice (it would be inefficient and mess up zebra striping), so we use the
-    // 'directory' variable to determine if it has already run, which while not
-    // completely intuitive, is reasonably safe, and allows us to save on the
-    // overhead of adding some new variable to track that.
-    if (!isset($variables['directory'])) {
-      $default_template_variables = array();
-      template_preprocess($default_template_variables, $hook);
-      $variables += $default_template_variables;
-    }
-
     // Render the output using the template file.
-    $template_file = $info['template'] . $extension;
-    if (isset($info['path'])) {
-      $template_file = $info['path'] . '/' . $template_file;
-    }
-    $output = $render_function($template_file, $variables);
+    $output = $render_function($info['template'] . $extension, $variables);
   }
 
   // restore path_to_theme()
