diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwigDrillable.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwigDrillable.php
new file mode 100644
index 0000000..5c54864
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwigDrillable.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Theme\ThemeTestTwigDrillable.
+ */
+
+namespace Drupal\system\Tests\Theme;
+
+use Drupal\Core\Language\Language;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests drillable variable structures in Twig templates.
+ */
+class ThemeTestTwigDrillable extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'image', 'field_ui', 'twig_theme_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Twig drillable variables',
+      'description' => 'Test Twig-specific drillable variables functionality.',
+      'group' => 'Theme',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    theme_enable(array('test_drillable'));
+
+    // Create Basic page and Article node types.
+    if ($this->profile != 'standard') {
+      $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+      $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+    }
+
+    $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer node fields', 'administer nodes', 'create article content', 'edit any article content', 'delete any article content', 'administer image styles'));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  /**
+   * Create a new image field.
+   *
+   * Copied from ImageFieldTestBase.
+   *
+   * @param $name
+   *   The name of the new field (all lowercase), exclude the "field_" prefix.
+   * @param $type_name
+   *   The node type that this field will be added to.
+   * @param $field_settings
+   *   A list of field settings that will be added to the defaults.
+   * @param $instance_settings
+   *   A list of instance settings that will be added to the instance defaults.
+   * @param $widget_settings
+   *   A list of widget settings that will be added to the widget defaults.
+   *
+   * @return mixed
+   *   Field instance.
+   */
+  function createImageField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
+    $field = array(
+      'field_name' => $name,
+      'type' => 'image',
+      'settings' => array(),
+      'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
+    );
+    $field['settings'] = array_merge($field['settings'], $field_settings);
+    field_create_field($field);
+
+    $instance = array(
+      'field_name' => $field['field_name'],
+      'entity_type' => 'node',
+      'label' => $name,
+      'bundle' => $type_name,
+      'required' => !empty($instance_settings['required']),
+      'description' => !empty($instance_settings['description']) ? $instance_settings['description'] : '',
+      'settings' => array(),
+    );
+    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+    $field_instance = field_create_instance($instance);
+
+    entity_get_form_display('node', $type_name, 'default')
+      ->setComponent($field['field_name'], array(
+        'type' => 'image_image',
+        'settings' => $widget_settings,
+      ))
+      ->save();
+
+    entity_get_display('node', $type_name, 'default')
+      ->setComponent($field['field_name'])
+      ->save();
+
+    return $field_instance;
+
+  }
+
+  /**
+   * Upload an image to a node.
+   *
+   * Copied from ImageFieldTestBase.
+   *
+   * @param $image
+   *   A file object representing the image to upload.
+   * @param $field_name
+   *   Name of the image field the image should be attached to.
+   * @param $type
+   *   The type of node to create.
+   */
+  function uploadNodeImage($image, $field_name, $type) {
+    $edit = array(
+      'title' => $this->randomName(),
+    );
+    $edit['files[' . $field_name . '_' . Language::LANGCODE_NOT_SPECIFIED . '_0]'] = drupal_realpath($image->uri);
+    $this->drupalPost('node/add/' . $type, $edit, t('Save and publish'));
+
+    // Retrieve ID of the newly created node from the current URL.
+    $matches = array();
+    preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
+    return isset($matches[1]) ? $matches[1] : FALSE;
+  }
+
+  /**
+   * Test that render element variables are available to Twig templates.
+   */
+  function testTwigDrillableElements() {
+    config('system.theme')
+      ->set('default', 'test_drillable')
+      ->save();
+
+    // Access render array children.
+    $this->drupalGet('twig-theme-test/drillable-elements');
+    $customized_link = $this->xpath('//a[@href="/links/link-1" and @cardinality="first"]');
+    $this->assertTrue(count($customized_link) === 1, 'Twig template can access variables prepared for child elements.');
+
+    // Access field instances.
+    $field_name = 'field_image';
+    $this->createImageField($field_name, 'article', array('uri_scheme' => 'public'));
+    $test_image = current($this->drupalGetTestFiles('image'));
+    $nid = $this->uploadNodeImage($test_image, $field_name, 'article');
+    $this->drupalGet('node/' . $nid);
+    $node = node_load($nid, TRUE);
+    $targeted_src = drupal_realpath($node->get('field_image', 0)->entity->uri);
+    $expected_output = '<img class="layout-first-image" src="' . $targeted_src . '" />';
+    $this->assertRaw($expected_output, t('Twig template can access prepared variables for field instances.'));
+  }
+
+}
diff --git a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
index ef5fb70..24749ca 100644
--- a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
+++ b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
@@ -29,4 +29,23 @@ public function phpVariablesRender() {
     return theme('twig_theme_test_php_variables');
   }
 
+  /**
+   * Menu callback for testing drillable elements in Twig templates.
+   */
+  public function drillableElementsRender() {
+    $links = array();
+    foreach (range(1, 5) as $i) {
+      $links[] = array(
+        '#type' => 'link',
+        '#title' => t('Link ' . $i),
+        '#href' => 'links/link-' . $i,
+        '#options' => array(),
+      );
+    }
+
+    return array(
+      '#theme' => 'twig_theme_test_drillable_elements',
+      '#links' => $links
+    );
+  }
 }
diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig-theme-test-drillable-elements.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig-theme-test-drillable-elements.html.twig
new file mode 100644
index 0000000..3647735
--- /dev/null
+++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig-theme-test-drillable-elements.html.twig
@@ -0,0 +1,8 @@
+{# Test overriding one of a set of links. #}
+{% for link in links %}
+  {% if loop.first %}
+    <a href="{{ link.attributes.href }}" {{ link.attributes }} cardinality="first">{{ link.title }}</a>
+  {% else %}
+    {{ link }}
+  {% endif %}
+{% endfor %}
diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
index 2ca2cd0..435da1c 100644
--- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
+++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
@@ -7,6 +7,12 @@ function twig_theme_test_theme($existing, $type, $theme, $path) {
   $items['twig_theme_test_php_variables'] = array(
     'template' => 'twig_theme_test.php_variables',
   );
+  $items['twig_theme_test_drillable_elements'] = array(
+    'template' => 'twig-theme-test-drillable-elements',
+    'variables' => array(
+      'links' => array(),
+    )
+  );
   return $items;
 }
 
diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
index cdc0ac1..ee5dc94 100644
--- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
+++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
@@ -4,3 +4,10 @@ twig_theme_test_php_variables:
     _content: '\Drupal\twig_theme_test\TwigThemeTestController::phpVariablesRender'
   requirements:
     _permission: 'access content'
+
+twig_theme_test_drillable_elements:
+  pattern: '/twig-theme-test/drillable-elements'
+  defaults:
+    _content: '\Drupal\twig_theme_test\TwigThemeTestController::drillableElementsRender'
+  requirements:
+    _permission: 'access content'
diff --git a/core/themes/test_drillable/node.html.twig b/core/themes/test_drillable/node.html.twig
new file mode 100644
index 0000000..9714a7a
--- /dev/null
+++ b/core/themes/test_drillable/node.html.twig
@@ -0,0 +1,134 @@
+{#
+/**
+ * @file
+ * Custom theme implementation to display a node.
+ *
+ * Available variables:
+ * - node: Full node entity.
+ *   - type: The type of the node, for example, "page" or "article".
+ *   - uid: The user ID of the node author.
+ *   - created: Formatted creation date. Preprocess functions can reformat it by
+ *     calling format_date() with the desired parameters on
+ *     $variables['node']->created.
+ *   - promote: Whether the node is promoted to the front page.
+ *   - sticky: Whether the node is 'sticky'. Sticky nodes are ordered above
+ *     other non-sticky nodes in teaser listings
+ *   - status: Whether the node is published.
+ *   - comment: A value representing the comment status of the current node. May
+ *     be one of the following:
+ *     - 0: The comment form and any existing comments are hidden.
+ *     - 1: Comments are closed. No new comments may be posted, but existing
+ *       comments are displayed.
+ *     - 2: Comments are open on this node.
+ *   - comment_count: Number of comments attached to the node.
+ * - label: The title of the node.
+ * - content: All node items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {% hide(content.field_example) %} to temporarily suppress the printing
+ *   of a given element.
+ * - user_picture: The node author's picture from user-picture.html.twig.
+ * - date: Formatted creation date. Preprocess functions can reformat it by
+ *   calling format_date() with the desired parameters on
+ *   $variables['created'].
+ * - name: Themed username of node author output from theme_username().
+ * - node_url: Direct URL of the current node.
+ * - display_submitted: Whether submission information should be displayed.
+ * - submitted: Submission information created from name and date during
+ *   template_preprocess_node().
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class element may contain one or more of the following
+ *   classes:
+ *   - node: The current template type (also known as a "theming hook").
+ *   - node-[type]: The current node type. For example, if the node is a
+ *     "Article" it would result in "node-article". Note that the machine
+ *     name will often be in a short form of the human readable label.
+ *   - view-mode-[view_mode]: The View Mode of the node; for example, a teaser
+ *     would result in: "view-mode-teaser", and full: "view-mode-full".
+ *   - preview: Whether a node is in preview mode.
+ *   The following are controlled through the node publishing options.
+ *   - promoted: Appears on nodes promoted to the front page.
+ *   - sticky: Appears on nodes ordered above other non-sticky nodes in teaser
+ *     listings.
+ *   - unpublished: Appears on unpublished nodes visible only to site admins.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
+ * - page: Flag for the full page state. Will be true if view_mode is 'full'.
+ * - readmore: Flag for more state. Will be true if the teaser content of the
+ *   node cannot hold the main body content.
+ * - is_front: Flag for front. Will be true when presented on the front page.
+ * - logged_in: Flag for authenticated user status. Will be true when the
+ *   current user is a logged-in member.
+ * - is_admin: Flag for admin user status. Will be true when the current user
+ *   is an administrator.
+ *
+ * In field variables, each field instance attached to the node a corresponding
+ * variable is defined; for example, 'node.body' becomes 'body'. When needing to
+ * access a field's raw values, developers/themers are strongly encouraged to
+ * use these variables. Otherwise they will have to explicitly specify the
+ * desired field language; for example, 'node.body.en', thus overriding any
+ * language negotiation rule that may have been applied previously.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_node()
+ *
+ * @todo Remove the id attribute (or make it a class), because if that gets
+ *   rendered twice on a page this is invalid CSS for example: two lists
+ *   in different view modes.
+ *
+ * @ingroup themeable
+ */
+#}
+<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix"{{ attributes }}>
+
+  {{ title_prefix }}
+  {% if not page %}
+    <h2{{ title_attributes }}>
+      <a href="{{ node_url }}" rel="bookmark">{{ label }}</a>
+    </h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {% if display_submitted %}
+    <footer>
+      {{ user_picture }}
+      <p class="submitted">{{ submitted }}</p>
+    </footer>
+  {% endif %}
+
+  <div{{ content_attributes }}>
+    {# We hide the comments and links now so that we can render them later. #}
+    {% hide(content.comments) %}
+    {% hide(content.links) %}
+
+    {#
+      Test that we can drill into variables of child elements.
+    #}
+    {% if content.field_image.0 is not empty %}
+
+      {#{{ dump(content.field_image.0) }}#}
+      {#{{ hide(content.field_image.0) }}#}
+
+      {#
+        When we access attributes from a field instance below, Twig should:
+          1. Realize the requested attributes do not exist.
+          2. Realize the field instance is still a render array.
+          3. Pre-render the field instance, returning the prepared variables.
+        When we print {{ content }}, field instances should be rendered with
+          render() as usual.
+      #}
+
+      <img class="layout-first-image" src="{{ content.field_image.0.attributes.src }}" />
+
+    {% endif %}
+
+    {{ content }}
+  </div>
+
+  {{ content.links }}
+  {{ content.comments }}
+
+</article>
diff --git a/core/themes/test_drillable/test_drillable.info.yml b/core/themes/test_drillable/test_drillable.info.yml
new file mode 100644
index 0000000..dd6acb4
--- /dev/null
+++ b/core/themes/test_drillable/test_drillable.info.yml
@@ -0,0 +1,6 @@
+name: 'Test drillable theme'
+type: theme
+description: 'Theme for testing drillable variables in Twig templates'
+core: 8.x
+version: VERSION
+hidden: false
