diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index a0af702..7cf4b3c 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Theme;
 
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\DestructableInterface;
@@ -565,10 +566,18 @@ protected function processExtension(array &$cache, $name, $type, $theme, $path)
           $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
         }
         $result[$hook]['preprocess functions'] = $info['preprocess functions'];
+
+        // If a theme implementation definition provides both 'template' and
+        // 'function', the 'function' will be used. In this case, if the new
+        // result provides a 'template' value, any existing 'function' value
+        // must be removed for the override to be called.
+        if (isset($result[$hook]['template'])) {
+          unset($cache[$hook]['function']);
+        }
       }
 
       // Merge the newly created theme hooks into the existing cache.
-      $cache = $result + $cache;
+      $cache = NestedArray::mergeDeep($cache, $result);
     }
 
     // Let themes have variable preprocessors even if they didn't register a
diff --git a/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php b/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php
index 9d4c446..c3785df 100644
--- a/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php
+++ b/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php
@@ -34,6 +34,17 @@ protected function setUp() {
   }
 
   /**
+   * Tests that a layout provided by a theme has the preprocess function set.
+   */
+  public function testThemeProvidedLayout() {
+    $this->container->get('theme_installer')->install(['test_layout_theme']);
+    $this->config('system.theme')->set('default', 'test_layout_theme')->save();
+
+    $theme_definitions = $this->container->get('theme.registry')->get();
+    $this->assertTrue(in_array('template_preprocess_layout', $theme_definitions['test_layout_theme']['preprocess functions']));
+  }
+
+  /**
    * Test rendering a layout.
    *
    * @dataProvider renderLayoutData
diff --git a/core/modules/layout_discovery/tests/themes/test_layout_theme/templates/test-layout-theme.html.twig b/core/modules/layout_discovery/tests/themes/test_layout_theme/templates/test-layout-theme.html.twig
new file mode 100644
index 0000000..67675f6
--- /dev/null
+++ b/core/modules/layout_discovery/tests/themes/test_layout_theme/templates/test-layout-theme.html.twig
@@ -0,0 +1 @@
+{{ content.content }}
diff --git a/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.info.yml b/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.info.yml
new file mode 100644
index 0000000..021d43f
--- /dev/null
+++ b/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.info.yml
@@ -0,0 +1,6 @@
+name: 'Test layout theme'
+type: theme
+description: 'Theme for testing a theme-provided layout'
+version: VERSION
+base theme: classy
+core: 8.x
diff --git a/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.layouts.yml b/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.layouts.yml
new file mode 100644
index 0000000..9da19dd
--- /dev/null
+++ b/core/modules/layout_discovery/tests/themes/test_layout_theme/test_layout_theme.layouts.yml
@@ -0,0 +1,7 @@
+test_layout_theme:
+  label: 'Test Layout - Theme'
+  category: 'Test Layout Theme'
+  template: templates/test-layout-theme
+  regions:
+    content:
+      label: Content
