diff --git a/core/includes/form.inc b/core/includes/form.inc
index 81c6406..d150190 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -462,6 +462,22 @@ function template_preprocess_form_element(&$variables) {
   $variables['label'] = array('#theme' => 'form_element_label');
   $variables['label'] += array_intersect_key($element, array_flip(array('#id', '#required', '#title', '#title_display')));
 
+  // The element may have specified its label should refer to one of its
+  // children, or to a fixed string, rather than the default of itself.
+  if (!empty($element['#label_for'])) {
+    if (is_array($element['#label_for'])) {
+      // if #label_for is an array, it is interpreted as a reference to the
+      // #id of a child of $element.
+      $for_element = NestedArray::getValue($element, $element['#label_for']);
+      if (!empty($for_element['#id'])) {
+        $variables['label']['#for'] = $for_element['#id'];
+      }
+    }
+    else {
+      $variables['label']['#for'] = $element['#label_for'];
+    }
+  }
+
   $variables['children'] = $element['#children'];
 }
 
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index a24330d..33d805b 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -790,12 +790,14 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
       $element['#defaults_loaded'] = TRUE;
     }
     // Assign basic defaults common for all form elements.
-    $element += array(
-      '#required' => FALSE,
-      '#attributes' => array(),
-      '#title_display' => 'before',
-      '#description_display' => 'after',
-      '#errors' => NULL,
+    $element = array_merge(array(
+        '#required' => FALSE,
+        '#attributes' => array(),
+        '#title_display' => 'before',
+        '#description_display' => 'after',
+        '#errors' => NULL,
+      ),
+      $element
     );
 
     // Special handling if we're on the top level form element.
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index c2fa103..e2a2884 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -311,9 +311,8 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
       $element['upload']['#attached']['drupalSettings']['file']['elements']['#' . $element['#id']] = $extension_list;
     }
 
-    // Let #id point to the file element, so the field label's 'for' corresponds
-    // with it.
-    $element['#id'] = &$element['upload']['#id'];
+    // The FAPI-generated label should reference the nested upload input
+    $element['#label_for'] = &$element['upload']['#id'];
 
     // Prefix and suffix used for Ajax replacement.
     $element['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
