diff --git a/core/includes/common.inc b/core/includes/common.inc
index a68fa5a..26680c1 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1036,7 +1036,7 @@ function drupal_pre_render_links($element) {
     $child = &$element[$key];
     // If the child has links which have not been printed yet and the user has
     // access to it, merge its links in to the parent.
-    if (isset($child['#links']) && empty($child['#printed']) && (!isset($child['#access']) || $child['#access'])) {
+    if (isset($child['#links']) && empty($child['#printed']) && Element::isVisibleElement($child)) {
       $element['#links'] += $child['#links'];
       // Mark the child as having been printed already (so that its links
       // cannot be mistakenly rendered twice).
diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php
index cc13ae4..ae58a5c 100644
--- a/core/lib/Drupal/Core/Field/WidgetBase.php
+++ b/core/lib/Drupal/Core/Field/WidgetBase.php
@@ -12,6 +12,7 @@
 use Drupal\Component\Utility\SortArray;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
 use Symfony\Component\Validator\ConstraintViolationInterface;
 use Symfony\Component\Validator\ConstraintViolationListInterface;
 
@@ -416,7 +417,7 @@ public function flagErrors(FieldItemListInterface $items, ConstraintViolationLis
       }
 
       // Only set errors if the element is accessible.
-      if (!isset($element['#access']) || $element['#access']) {
+      if (Element::isVisibleElement($element)) {
         $handles_multiple = $this->handlesMultipleValues();
 
         $violations_by_delta = array();
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 2e0b192..f80fd59 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -12,6 +12,7 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Access\AccessResultInterface;
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
@@ -829,6 +830,15 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
 
     // Recurse through all child elements.
     $count = 0;
+    if (isset($element['#access'])) {
+      $access = $element['#access'];
+      if ($access instanceof AccessResultInterface || $access === FALSE) {
+        $child_denied = $access;
+      }
+      else {
+        $child_access = NULL;
+      }
+    }
     foreach (Element::children($element) as $key) {
       // Prior to checking properties of child elements, their default
       // properties need to be loaded.
@@ -843,8 +853,8 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
       }
 
       // Deny access to child elements if parent is denied.
-      if (isset($element['#access']) && !$element['#access']) {
-        $element[$key]['#access'] = FALSE;
+      if (isset($child_access)) {
+        $element[$key]['#access'] = $child_access;
       }
 
       // Make child elements inherit their parent's #disabled and #allow_focus
diff --git a/core/lib/Drupal/Core/Render/Element.php b/core/lib/Drupal/Core/Render/Element.php
index 67186f7..b69bc42 100644
--- a/core/lib/Drupal/Core/Render/Element.php
+++ b/core/lib/Drupal/Core/Render/Element.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Render;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Access\AccessResultInterface;
 
 /**
  * Provides helper methods for Drupal render elements.
@@ -136,11 +137,6 @@ public static function getVisibleChildren(array $elements) {
     foreach (static::children($elements) as $key) {
       $child = $elements[$key];
 
-      // Skip un-accessible children.
-      if (isset($child['#access']) && !$child['#access']) {
-        continue;
-      }
-
       // Skip value and hidden elements, since they are not rendered.
       if (!static::isVisibleElement($child)) {
         continue;
@@ -162,7 +158,9 @@ public static function getVisibleChildren(array $elements) {
    *   TRUE if the element is visible, otherwise FALSE.
    */
   public static function isVisibleElement($element) {
-    return (!isset($element['#type']) || !in_array($element['#type'], ['value', 'hidden', 'token'])) && (!isset($element['#access']) || $element['#access']);
+    return (!isset($element['#type']) || !in_array($element['#type'], ['value', 'hidden', 'token']))
+      && (!isset($element['#access'])
+        || (($element['#access'] instanceof AccessResultInterface && $element['#access']->isAllowed()) || ($element['#access'] === TRUE)));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index d24b7d7..5d2f2e6 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Access\AccessResultInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Controller\ControllerResolverInterface;
@@ -168,6 +169,10 @@ public function render(&$elements, $is_root_call = FALSE) {
    * See the docs for ::render().
    */
   protected function doRender(&$elements, $is_root_call = FALSE) {
+    if (empty($elements)) {
+      return '';
+    }
+
     if (!isset($elements['#access']) && isset($elements['#access_callback'])) {
       if (is_string($elements['#access_callback']) && strpos($elements['#access_callback'], '::') === FALSE) {
         $elements['#access_callback'] = $this->controllerResolver->getControllerFromDefinition($elements['#access_callback']);
@@ -176,8 +181,18 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
 
     // Early-return nothing if user does not have access.
-    if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
-      return '';
+    if (isset($elements['#access'])) {
+      // If #access is an AccessResultInterface object, we must apply its
+      // cacheability metadata to the render array.
+      if ($elements['#access'] instanceof AccessResultInterface) {
+        $this->addCacheableDependency($elements, $elements['#access']);
+        if (!$elements['#access']->isAllowed()) {
+          return '';
+        }
+      }
+      elseif (!$elements['#access']) {
+        return '';
+      }
     }
 
     // Do not print elements twice.
diff --git a/core/tests/Drupal/Tests/Core/Render/ElementTest.php b/core/tests/Drupal/Tests/Core/Render/ElementTest.php
index 75c0645..880bf91 100644
--- a/core/tests/Drupal/Tests/Core/Render/ElementTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/ElementTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Render;
 
+use Drupal\Core\Access\AccessResult;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Core\Render\Element;
 
@@ -151,6 +152,8 @@ public function providerVisibleChildren() {
       array(array('#property1' => '', 'child1' => array()), array('child1')),
       array(array('#property1' => '', 'child1' => array(), 'child2' => array('#access' => TRUE)), array('child1', 'child2')),
       array(array('#property1' => '', 'child1' => array(), 'child2' => array('#access' => FALSE)), array('child1')),
+      'access_result_object_allowed' => array(array('#property1' => '', 'child1' => array(), 'child2' => array('#access' => AccessResult::allowed())), array('child1', 'child2')),
+      'access_result_object_forbidden' =array(array('#property1' => '', 'child1' => array(), 'child2' => array('#access' => AccessResult::forbidden())), array('child1')),
       array(array('#property1' => '', 'child1' => array(), 'child2' => array('#type' => 'textfield')), array('child1', 'child2')),
       array(array('#property1' => '', 'child1' => array(), 'child2' => array('#type' => 'value')), array('child1')),
       array(array('#property1' => '', 'child1' => array(), 'child2' => array('#type' => 'hidden')), array('child1')),
