diff --git modules/field/field.api.php modules/field/field.api.php
index fc5b4fb..dc76e96 100644
--- modules/field/field.api.php
+++ modules/field/field.api.php
@@ -789,7 +789,8 @@ function hook_field_widget_info_alter(&$info) {
  * @see field_widget_instance()
  *
  * @param $form
- *   The entire form array.
+ *   The form structure where widgets are being attached to. This might be a
+ *   full form structure, or a sub-element of a larger form.
  * @param $form_state
  *   An associative array containing the current state of the form.
  * @param $field
@@ -808,6 +809,7 @@ function hook_field_widget_info_alter(&$info) {
  *   - #bundle: The name of the field bundle the field is contained in.
  *   - #field_name: The name of the field.
  *   - #language: The language the field is being edited in.
+ *   - #field_parents: The '#parents' space for the field in the form.
  *   - #columns: A list of field storage columns of the field.
  *   - #title: The sanitized element label for the field instance, ready for
  *     output.
@@ -844,7 +846,8 @@ function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langco
  *     errors to different form elements inside the widget.
  *   - message: the human readable message to be displayed.
  * @param $form
- *   The form array.
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-element of a larger form.
  * @param $form_state
  *   An associative array containing the current state of the form.
  */
@@ -1079,7 +1082,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
  */
 
 /**
- * Act on field_attach_form.
+ * Act on field_attach_form().
  *
  * This hook is invoked after the field module has performed the operation.
  * Implementing modules should alter the $form or $form_state parameters.
@@ -1087,10 +1090,15 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
  * @param $entity_type
  *   The type of $entity; for example, 'node' or 'user'.
  * @param $entity
- *   The entity for which to load form elements, used to initialize
- *   default form values.
+ *   The entity for which an edit form is being built.
  * @param $form
- *   The form structure to fill in.
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-element of a larger form. The
+ *   $form['#parents'] property can be used to identify the corresponding part
+ *   of $form_state['values']. Hook implementations that need to act on the
+ *   top-level properties of the global form (like #submit, #validate...) can
+ *   add a #process callback to the array received in the $form parameter, and
+ *   act on the $complete_form parameter in the process callback.
  * @param $form_state
  *   An associative array containing the current state of the form.
  * @param $langcode
@@ -1098,7 +1106,12 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
  *   is provided the default site language will be used.
  */
 function hook_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
-  // @todo Needs function body.
+  // Add a checkbox allowing a given field to be emptied.
+  // See hook_field_attach_submit() for the corresponding processing code.
+  $form['empty_field_foo'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("Empty the 'field_foo' field"),
+  );
 }
 
 /**
@@ -1136,10 +1149,26 @@ function hook_field_attach_validate($entity_type, $entity, &$errors) {
  *
  * This hook is invoked after the field module has performed the operation.
  *
- * See field_attach_submit() for details and arguments.
+ * @param $entity_type
+ *   The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ *   The entity for which an edit form is being submitted. The incoming form
+ *   values have been extracted as field values of the $entity object.
+ * @param $form
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-part of a larger form. The $form['#parents']
+ *   property can be used to identify the corresponding part of
+ *   $form_state['values'].
+ * @param $form_state
+ *   An associative array containing the current state of the form.
  */
 function hook_field_attach_submit($entity_type, $entity, $form, &$form_state) {
-  // @todo Needs function body.
+  // Sample case of an 'Empty the field' checkbox added on the form, allowing
+  // a given field to be emptied.
+  $values = drupal_array_get_nested_value($form_state['values'], $form['#parents']);
+  if (!empty($values['empty_field_foo'])) {
+    unset($entity->field_foo);
+  }
 }
 
 /**
diff --git modules/field/field.attach.inc modules/field/field.attach.inc
index 5a496e7..23ade15 100644
--- modules/field/field.attach.inc
+++ modules/field/field.attach.inc
@@ -442,9 +442,26 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
 /**
  * Add form elements for all fields for an entity to a form structure.
  *
- * Sample structure for $form:
+ * The form elements for the entity's fields are added by reference as direct
+ * children in the $form parameter. This parameter can be a full form structure
+ * (most common case for entity edit forms), or a sub-element of a larger form.
+ *
+ * By default, submitted field values appear at the top-level of
+ * $form_state['values']. A different location within $form_state['values'] can
+ * be specified by setting the '#parents' property on the incoming $form
+ * parameter. Because of name clashes, two instances of the same field cannot
+ * appear within the same $form element, or within the same '#parents' space.
+ *
+ * For each call to field_attach_form(), field values are processed by calling
+ * field_attach_form_validate() and field_attach_submit() on the same $form
+ * element.
+ *
+ * Sample resulting structure in $form:
  * @code
- *   // One sub-array per field appearing in the form, keyed by field name.
+ *   '#parents' => The location of field values in $form_state['values'],
+ *   '#entity_type' => The name of the entity type,
+ *   '#bundle' => The name of the bundle,
+ *   // One sub-array per field appearing in the entity, keyed by field name.
  *   // The structure of the array differs slightly depending on whether the
  *   // widget is 'single-value' (provides the input for one field value,
  *   // most common case), and will therefore be repeated as many times as
@@ -456,29 +473,32 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  *   // to access the field sub-array when $langcode is unknown.
  *   'field_foo' => array(
  *     '#tree' => TRUE,
- *     '#field_name' => the name of the field,
+ *     '#field_name' => The name of the field,
  *     '#language' => $langcode,
  *     $langcode => array(
- *       '#field_name' => the name of the field,
- *       '#tree' => TRUE,
- *       '#required' => whether or not the field is required,
- *       '#title' => the label of the field instance,
- *       '#description' => the description text for the field instance,
+ *       '#field_name' => The name of the field,
+ *       '#language' => $langcode,
+ *       '#field_parents' => The '#parents' space for the field in the form,
+ *       '#required' => Whether or not the field is required,
+ *       '#title' => The label of the field instance,
+ *       '#description' => The description text for the field instance,
  *
  *       // Only for 'single' widgets:
  *       '#theme' => 'field_multiple_value_form',
- *       '#cardinality' => the field cardinality,
+ *       '#cardinality' => The field cardinality,
  *       // One sub-array per copy of the widget, keyed by delta.
  *       0 => array(
- *         '#title' => the title to be displayed by the widget,
- *         '#default_value' => the field value for delta 0,
- *         '#required' => whether the widget should be marked required,
+ *         '#entity_type' => The name of the entity type,
+ *         '#bundle' => The name of the bundle,
+ *         '#field_name' => The name of the field,
+ *         '#field_parents' => The '#parents' space for the field in the form,
+ *         '#title' => The title to be displayed by the widget,
+ *         '#default_value' => The field value for delta 0,
+ *         '#required' => Whether the widget should be marked required,
  *         '#delta' => 0,
- *         '#field_name' => the name of the field,
- *         '#bundle' => the name of the bundle,
- *         '#columns' => the array of field columns,
+ *         '#columns' => The array of field columns,
  *         // The remaining elements in the sub-array depend on the widget.
- *         '#type' => the type of the widget,
+ *         '#type' => The type of the widget,
  *         ...
  *       ),
  *       1 => array(
@@ -486,10 +506,11 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  *       ),
  *
  *       // Only for multiple widgets:
+ *       '#entity_type' => The name of the entity type,
  *       '#bundle' => $instance['bundle'],
  *       '#columns'  => array_keys($field['columns']),
  *       // The remaining elements in the sub-array depend on the widget.
- *       '#type' => the type of the widget,
+ *       '#type' => The type of the widget,
  *       ...
  *     ),
  *     ...
@@ -497,27 +518,8 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  * )
  * @endcode
  *
- * Sample structure for $form_state['field']:
- * @code
- * array(
- *   // One sub-array per field appearing in the form, keyed by field name.
- *   'field_foo' => array(
- *     $langcode => array(
- *       'field' => the field definition array,
- *       'instance' => the field instance definition array,
- *       'array_parents' => an array of keys indicating the path to the field
- *         element within the full $form structure. This entry is populated at
- *         form build time.
- *       'errors' => the array of field validation errors reported on the
- *         field. This entry is populated at form validation time.
- *     ),
- *   ),
- * ),
- * @endcode
- *
- * field_attach_load() is automatically called by the default entity controller
- * class, and thus, in most cases, doesn't need to be explicitly called by the
- * entity type module.
+ * Additionally, some processing data is placed in $form_state, and can be
+ * accessed by field_form_get_state() and field_form_set_state().
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
@@ -525,18 +527,24 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  *   The entity for which to load form elements, used to initialize
  *   default form values.
  * @param $form
- *   The form structure to fill in.
+ *   The form structure to fill in. This can be a full form structure, or a
+ *   sub-element of a larger form. The #parents property can be set to control
+ *   the location of submitted field values within $form_state['values']. If
+ *   not specified, $form['#parents'] is set to an empty array, placing field
+ *   values at the top-level of $form_state['values'].
  * @param $form_state
  *   An associative array containing the current state of the form.
  * @param $langcode
  *   The language the field values are going to be entered, if no language
  *   is provided the default site language will be used.
- * @return
- *   The form elements are added by reference at the top level of the $form
- *   parameter. Processing information is added by reference in
- *   $form_state['field'].
+ *
+ * @see field_form_get_state()
+ * @see field_form_set_state()
  */
 function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL) {
+  // Set #parents to 'top-level' by default.
+  $form += array('#parents' => array());
+
   // If no language is provided use the default site language.
   $options = array('language' => field_valid_language($langcode));
   $form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
@@ -561,6 +569,10 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
  * Loads all fields for each entity object in a group of a single entity type.
  * The loaded field values are added directly to the entity objects.
  *
+ * field_attach_load() is automatically called by the default entity controller
+ * class, and thus, in most cases, doesn't need to be explicitly called by the
+ * entity type module.
+ *
  * @param $entity_type
  *   The type of $entity; e.g., 'node' or 'user'.
  * @param $entities
@@ -782,7 +794,8 @@ function field_attach_validate($entity_type, $entity) {
  *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
  * @param $form
- *   The form structure.
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-element of a larger form.
  * @param $form_state
  *   An associative array containing the current state of the form.
  */
@@ -798,8 +811,10 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
     // Pass field-level validation errors back to widgets for accurate error
     // flagging.
     foreach ($e->errors as $field_name => $field_errors) {
-      foreach ($field_errors as $langcode => $language_errors) {
-        $form_state['field'][$field_name][$langcode]['errors'] = $language_errors;
+      foreach ($field_errors as $langcode => $errors) {
+        $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+        $field_state['errors'] = $errors;
+        field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
       }
     }
     _field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state);
@@ -819,7 +834,8 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
  *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
  * @param $form
- *   The form structure to fill in.
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-element of a larger form.
  * @param $form_state
  *   An associative array containing the current state of the form.
  */
diff --git modules/field/field.default.inc modules/field/field.default.inc
index 1353bd6..d63e398 100644
--- modules/field/field.default.inc
+++ modules/field/field.default.inc
@@ -11,13 +11,36 @@
  * the corresponding field_attach_[operation]() function.
  */
 
+/**
+ * Extracts field values from submitted form values.
+ *
+ * @param $entity_type
+ *   The type of $entity.
+ * @param $entity
+ *   The entity for the operation.
+ * @param $field
+ *   The field structure for the operation.
+ * @param $instance
+ *   The instance structure for $field on $entity's bundle.
+ * @param $langcode
+ *   The language associated to $items.
+ * @param $items
+ *   The field values. This parameter is altered by reference to receive the
+ *   incoming form values.
+ * @param $form
+ *   The form structure where field elements are attached to. This might be a
+ *   full form structure, or a sub-element of a larger form.
+ * @param $form_state
+ *   The form state.
+ */
 function field_default_extract_form_values($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
-  $field_name = $field['field_name'];
-
-  if (isset($form_state['values'][$field_name][$langcode])) {
-    $items = $form_state['values'][$field_name][$langcode];
+  $path = array_merge($form['#parents'], array($field['field_name'], $langcode));
+  $key_exists = NULL;
+  $values = drupal_array_get_nested_value($form_state['values'], $path, $key_exists);
+  if ($key_exists) {
     // Remove the 'value' of the 'add more' button.
-    unset($items['add_more']);
+    unset($values['add_more']);
+    $items = $values;
   }
 }
 
diff --git modules/field/field.form.inc modules/field/field.form.inc
index c3ac156..825060a 100644
--- modules/field/field.form.inc
+++ modules/field/field.form.inc
@@ -16,6 +16,8 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
     list($id, , ) = entity_extract_ids($entity_type, $entity);
   }
 
+  $parents = $form['#parents'];
+
   $addition = array();
   $field_name = $field['field_name'];
   $addition[$field_name] = array();
@@ -37,6 +39,18 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
   // Collect widget elements.
   $elements = array();
   if (field_access('edit', $field, $entity_type, $entity)) {
+    // Store field information in $form_state.
+    if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) {
+      $field_state = array(
+        'field' => $field,
+        'instance' => $instance,
+        'items_count' => count($items),
+        'array_parents' => array(),
+        'errors' => array(),
+      );
+      field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+    }
+
     // If field module handles multiple values for this form element, and we
     // are displaying an individual element, process the multiple value form.
     if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
@@ -54,6 +68,7 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
           '#bundle' => $instance['bundle'],
           '#field_name' => $field_name,
           '#language' => $langcode,
+          '#field_parents' => $parents,
           '#columns' => array_keys($field['columns']),
           '#title' => check_plain(t($instance['label'])),
           '#description' => field_filter_xss($instance['description']),
@@ -79,16 +94,6 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
   }
 
   if ($elements) {
-    // Store field information in $form_state.
-    $form_state['field'][$field_name][$langcode] = array(
-      'field' => $field,
-      'instance' => $instance,
-      // This entry will be populated at form build time.
-      'array_parents' => array(),
-      // This entry will be populated at form validation time.
-      'errors' => array(),
-    );
-
     // Also aid in theming of field widgets by rendering a classified
     // container.
     $addition[$field_name] = array(
@@ -110,6 +115,7 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
   $elements['#after_build'][] = 'field_form_element_after_build';
   $elements['#field_name'] = $field_name;
   $elements['#language'] = $langcode;
+  $elements['#field_parents'] = $parents;
 
   $addition[$field_name] += array(
     '#tree' => TRUE,
@@ -132,20 +138,15 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
  */
 function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) {
   $field_name = $field['field_name'];
+  $parents = $form['#parents'];
 
   // Determine the number of widgets to display.
   switch ($field['cardinality']) {
     case FIELD_CARDINALITY_UNLIMITED:
-      $filled_items = _field_filter_items($field, $items);
-      $current_item_count = isset($form_state['field_item_count'][$field_name])
-                            ? $form_state['field_item_count'][$field_name]
-                            : count($items);
-      // We always want at least one empty icon for the user to fill in.
-      $max = ($current_item_count > count($filled_items))
-              ? $current_item_count - 1
-              : $current_item_count;
-
+      $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+      $max = $field_state['items_count'];
       break;
+
     default:
       $max = $field['cardinality'] - 1;
       break;
@@ -153,7 +154,10 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form,
 
   $title = check_plain(t($instance['label']));
   $description = field_filter_xss(t($instance['description']));
-  $wrapper_id = drupal_html_id($field_name . '-add-more-wrapper');
+
+  $id_prefix = implode('-', array_merge($parents, array($field_name)));
+  $wrapper_id = drupal_html_id($id_prefix . '-add-more-wrapper');
+
   $field_elements = array();
 
   $function = $instance['widget']['module'] . '_field_widget_form';
@@ -165,6 +169,7 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form,
         '#bundle' => $instance['bundle'],
         '#field_name' => $field_name,
         '#language' => $langcode,
+        '#field_parents' => $parents,
         '#columns' => array_keys($field['columns']),
         // For multiple fields, title and description are handled by the wrapping table.
         '#title' => $multiple ? '' : $title,
@@ -209,20 +214,16 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form,
       if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
         $field_elements['add_more'] = array(
           '#type' => 'submit',
-          '#name' => $field_name . '_add_more',
+          '#name' => strtr($id_prefix, '-', '_') . '_add_more',
           '#value' => t('Add another item'),
           '#attributes' => array('class' => array('field-add-more-submit')),
-          '#limit_validation_errors' => array(array($field_name, $langcode)),
+          '#limit_validation_errors' => array(array_merge($parents, array($field_name, $langcode))),
           '#submit' => array('field_add_more_submit'),
           '#ajax' => array(
             'callback' => 'field_add_more_js',
             'wrapper' => $wrapper_id,
             'effect' => 'fade',
           ),
-          // The field_add_more_submit() and field_add_more_js() handlers will
-          // find the relevant field using those entries.
-          '#field_name' => $field_name,
-          '#language' => $langcode,
         );
       }
     }
@@ -308,18 +309,20 @@ function theme_field_multiple_value_form($variables) {
 }
 
 /**
- * Stores information about the built form structure in the form storage.
+ * #after_build callback for field_default_form() to store information about the form structure.
  *
  * The 'array_parents' array is used to assign validation errors to actual form
- * elements, and to identify the form element to return in the AJAX 'add more'
- * button handler.
+ * elements.
  * @see field_default_form_errors()
- * @see field_add_more_js()
  */
 function field_form_element_after_build($element, &$form_state) {
+  $parents = $element['#field_parents'];
   $field_name = $element['#field_name'];
   $langcode = $element['#language'];
-  $form_state['field'][$field_name][$langcode]['array_parents'] = $element['#array_parents'];
+
+  $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+  $field_state['array_parents'] = $element['#array_parents'];
+  field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
 
   return $element;
 }
@@ -328,21 +331,17 @@ function field_form_element_after_build($element, &$form_state) {
  * Transfer field-level validation errors to widgets.
  */
 function field_default_form_errors($entity_type, $entity, $field, $instance, $langcode, $items, $form, &$form_state) {
-  $field_name = $field['field_name'];
-  $field_info = $form_state['field'][$field_name][$langcode];
+  $field_state = field_form_get_state($form['#parents'], $field['field_name'], $langcode, $form_state);
 
-  if (!empty($field_info['errors'])) {
+  if (!empty($field_state['errors'])) {
     $function = $instance['widget']['module'] . '_field_widget_error';
     $function_exists = function_exists($function);
 
-    // Walk the form down to where the widget lives.
-    $element = $form;
-    foreach ($field_info['array_parents'] as $key) {
-      $element = $element[$key];
-    }
+    // Locate the correct element in the the form.
+    $element = drupal_array_get_nested_value($form_state['complete form'], $field_state['array_parents']);
 
     $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT;
-    foreach ($field_info['errors'] as $delta => $delta_errors) {
+    foreach ($field_state['errors'] as $delta => $delta_errors) {
       // For multiple single-value widgets, pass errors by delta.
       // For a multiple-value widget, all errors are passed to the main widget.
       $error_element = $multiple_widget ? $element : $element[$delta];
@@ -353,10 +352,13 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la
         else {
           // Make sure that errors are reported (even incorrectly flagged) if
           // the widget module fails to implement hook_field_widget_error().
-          form_error($error_element, $error['error'], $form, $form_state);
+          form_error($error_element, $error['error']);
         }
       }
     }
+    // Reinitialize the errors list for the next submit.
+    $field_state['errors'] = array();
+    field_form_set_state($form['#parents'], $field['field_name'], $langcode, $form_state, $field_state);
   }
 }
 
@@ -370,11 +372,19 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la
  * to return just the changed part of the form.
  */
 function field_add_more_submit($form, &$form_state) {
-  $field_name = $form_state['clicked_button']['#field_name'];
-  $langcode = $form_state['clicked_button']['#language'];
-  if ($form_state['values'][$field_name . '_add_more']) {
-    $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
-  }
+  $button = $form_state['clicked_button'];
+
+  // Go one level up in the form, to the widgets container.
+  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+  $field_name = $element['#field_name'];
+  $langcode = $element['#language'];
+  $parents = $element['#field_parents'];
+
+  // Increment the items count.
+  $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+  $field_state['items_count']++;
+  field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+
   $form_state['rebuild'] = TRUE;
 }
 
@@ -387,24 +397,22 @@ function field_add_more_submit($form, &$form_state) {
  * @see field_add_more_submit()
  */
 function field_add_more_js($form, $form_state) {
-  // Retrieve field information.
-  $field_name = $form_state['clicked_button']['#field_name'];
-  $langcode = $form_state['clicked_button']['#language'];
-  $field_info = $form_state['field'][$field_name][$langcode];
-  $field = $field_info['field'];
+  $button = $form_state['clicked_button'];
 
-  if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
-    return;
-  }
+  // Go one level up in the form, to the widgets container.
+  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+  $field_name = $element['#field_name'];
+  $langcode = $element['#language'];
+  $parents = $element['#field_parents'];
 
-  // Navigate to the right element in the the form.
-  $element = $form;
+  $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
 
-  foreach ($field_info['array_parents'] as $key) {
-    $element = $element[$key];
+  $field = $field_state['field'];
+  if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
+    return;
   }
 
-  // Add a DIV around the new field to receive the AJAX effect.
+  // Add a DIV around the delta receiving the AJAX effect.
   $delta = $element['#max_delta'];
   $element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
   $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
@@ -413,6 +421,81 @@ function field_add_more_js($form, $form_state) {
 }
 
 /**
+ * Retrieves processing information about a field from $form_state.
+ *
+ * @param $parents
+ *   The array of #parents where the field lives in the form.
+ * @param $field_name
+ *   The field name.
+ * @param $langcode
+ *   The language in which the field values are entered.
+ * @param $form_state
+ *   The form state.
+ *
+ * @return
+ *   An array with the following key/data pairs:
+ *   - field: the field definition array,
+ *   - instance: the field instance definition array,
+ *   - items_count: the number of widgets to display for the field,
+ *   - array_parents: the location of the field's widgets within the $form
+ *     structure. This entry is populated at '#fter_build' time.
+ *   - errors: the array of field validation errors reported on the field. This
+ *     entry is populated at field_attach_form_validate() time.
+ *
+ * @see field_form_set_state()
+ */
+function field_form_get_state($parents, $field_name, $langcode, &$form_state) {
+  $field_parents = _field_form_state_parents($parents, $field_name, $langcode);
+  return drupal_array_get_nested_value($form_state, $field_parents);
+}
+
+/**
+ * Stores processing information about a field in $form_state.
+ *
+ * @param $parents
+ *   The array of #parents where the field lives in the form.
+ * @param $field_name
+ *   The field name.
+ * @param $langcode
+ *   The language in which the field values are entered.
+ * @param $form_state
+ *   The form state.
+ * @param $field_state
+ *   The array of data to store. See field_form_get_state() for the structure
+ *   and content of the array.
+ *
+ * @see field_form_get_state()
+ */
+function field_form_set_state($parents, $field_name, $langcode, &$form_state, $field_state) {
+  $field_parents = _field_form_state_parents($parents, $field_name, $langcode);
+  drupal_array_set_nested_value($form_state, $field_parents, $field_state);
+}
+
+/**
+ * Internal helper function: defines the location of field information within $form_state
+ */
+function _field_form_state_parents($parents, $field_name, $langcode) {
+  // To ensure backwards compatibility on regular entity forms for widgets that
+  // still access $form_state['field'][$field_name] directly,
+  // - top-level fields (empty $parents) are placed directly under
+  //   $form_state['fields'][$field_name].
+  // - Other fields are placed under
+  //   $form_state['field']['#parents'][...$parents...]['#fields'][$field_name]
+  //   to avoid clashes between field names and $parents parts.
+  // @todo Remove backwards compatibility in Drupal 8, and use a unique
+  // $form_state['field'][...$parents...]['#fields'][$field_name] structure.
+  if (!empty($parents)) {
+    $field_parents = array_merge(array('#parents'), $parents, array('#fields'));
+  }
+  else {
+    $field_parents = array();
+  }
+  $field_parents = array_merge(array('field'), $field_parents, array($field_name, $langcode));
+
+  return $field_parents;
+}
+
+/**
  * Retrieves the field definition for a widget's helper callbacks.
  *
  * Widgets helper element callbacks (such as #process, #element_validate,
@@ -421,23 +504,24 @@ function field_add_more_js($form, $form_state) {
  * field_info_instance() when they need to access field or instance properties.
  * See hook_field_widget_form() for more details.
  *
- * @see field_widget_instance()
- * @see hook_field_widget_form()
- *
  * @param $element
  *   The structured array for the widget.
  * @param $form_state
  *   The form state.
+ *
  * @return
  *   The $field definition array for the current widget.
+ *
+ * @see field_widget_instance()
+ * @see hook_field_widget_form()
  */
 function field_widget_field($element, $form_state) {
-  $info = $form_state['field'][$element['#field_name']][$element['#language']];
-  return $info['field'];
+  $field_state = field_form_get_state($element['#field_parents'], $element['#field_name'], $element['#language'], $form_state);
+  return $field_state['field'];
 }
 
 /**
- * Provides the instance definition array for a widget's helper callbacks.
+ * Retrieves the instance definition array for a widget's helper callbacks.
  *
  * Widgets helper element callbacks (such as #process, #element_validate,
  * #value_callback, ...) should use field_widget_field() and
@@ -445,17 +529,18 @@ function field_widget_field($element, $form_state) {
  * field_info_instance() when they need to access field or instance properties.
  * See hook_field_widget_form() for more details.
  *
- * @see field_widget_field()
- * @see hook_field_widget_form()
- *
  * @param $element
  *   The structured array for the widget.
  * @param $form_state
  *   The form state.
+ *
  * @return
  *   The $instance definition array for the current widget.
+ *
+ * @see field_widget_field()
+ * @see hook_field_widget_form()
  */
 function field_widget_instance($element, $form_state) {
-  $info = $form_state['field'][$element['#field_name']][$element['#language']];
-  return $info['instance'];
+  $field_state = field_form_get_state($element['#field_parents'], $element['#field_name'], $element['#language'], $form_state);
+  return $field_state['instance'];
 }
diff --git modules/field/tests/field.test modules/field/tests/field.test
index 155ee57..b9e68a6 100644
--- modules/field/tests/field.test
+++ modules/field/tests/field.test
@@ -1212,9 +1212,9 @@ class FieldFormTestCase extends FieldTestCase {
     $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
     $this->drupalLogin($web_user);
 
-    $this->field_single = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field');
-    $this->field_multiple = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => 4);
-    $this->field_unlimited = array('field_name' => drupal_strtolower($this->randomName()), 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+    $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
+    $this->field_multiple = array('field_name' => 'field_multiple', 'type' => 'test_field', 'cardinality' => 4);
+    $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
 
     $this->instance = array(
       'entity_type' => 'test_entity',
@@ -1551,6 +1551,115 @@ class FieldFormTestCase extends FieldTestCase {
     $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.'));
     $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.'));
   }
+
+  /**
+   * Tests Field API form integration within a subform.
+   */
+  function testNestedFieldForm() {
+     // Add two instances on the 'test_bundle'
+     field_create_field($this->field_single);
+     field_create_field($this->field_unlimited);
+     $this->instance['field_name'] = 'field_single';
+     $this->instance['label'] = 'Single field';
+     field_create_instance($this->instance);
+     $this->instance['field_name'] = 'field_unlimited';
+     $this->instance['label'] = 'Unlimited field';
+     field_create_instance($this->instance);
+
+     // Create two entities.
+     $entity_1 = field_test_create_stub_entity(1, 1);
+     $entity_1->is_new = TRUE;
+     $entity_1->field_single[LANGUAGE_NONE][] = array('value' => 0);
+     $entity_1->field_unlimited[LANGUAGE_NONE][] = array('value' => 1);
+     field_test_entity_save($entity_1);
+
+     $entity_2 = field_test_create_stub_entity(2, 2);
+     $entity_2->is_new = TRUE;
+     $entity_2->field_single[LANGUAGE_NONE][] = array('value' => 10);
+     $entity_2->field_unlimited[LANGUAGE_NONE][] = array('value' => 11);
+     field_test_entity_save($entity_2);
+
+     // Display the 'combined form'.
+     $this->drupalGet('test-entity/nested/1/2');
+     $this->assertFieldByName('field_single[und][0][value]', 0, t('Entity 1: field_single value appears correctly is the form.'));
+     $this->assertFieldByName('field_unlimited[und][0][value]', 1, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
+     $this->assertFieldByName('entity_2[field_single][und][0][value]', 10, t('Entity 2: field_single value appears correctly is the form.'));
+     $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 11, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
+
+     // Submit the form and check that the entities are updated accordingly.
+     $edit = array(
+       'field_single[und][0][value]' => 1,
+       'field_unlimited[und][0][value]' => 2,
+       'field_unlimited[und][1][value]' => 3,
+       'entity_2[field_single][und][0][value]' => 11,
+       'entity_2[field_unlimited][und][0][value]' => 12,
+       'entity_2[field_unlimited][und][1][value]' => 13,
+     );
+     $this->drupalPost(NULL, $edit, t('Save'));
+     field_cache_clear();
+     $entity_1 = field_test_create_stub_entity(1);
+     $entity_2 = field_test_create_stub_entity(2);
+     $this->assertFieldValues($entity_1, 'field_single', LANGUAGE_NONE, array(1));
+     $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(2, 3));
+     $this->assertFieldValues($entity_2, 'field_single', LANGUAGE_NONE, array(11));
+     $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(12, 13));
+
+     // Submit invalid values and check that errors are reported on the
+     // correct widgets.
+     $edit = array(
+       'field_unlimited[und][1][value]' => -1,
+     );
+     $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
+     $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 1: the field validation error was reported.'));
+     $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-und-1-value'));
+     $this->assertTrue($error_field, t('Entity 1: the error was flagged on the correct element.'));
+     $edit = array(
+       'entity_2[field_unlimited][und][1][value]' => -1,
+     );
+     $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
+     $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), t('Entity 2: the field validation error was reported.'));
+     $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-und-1-value'));
+     $this->assertTrue($error_field, t('Entity 2: the error was flagged on the correct element.'));
+
+     // Test that reordering works on both entities.
+     $edit = array(
+       'field_unlimited[und][0][_weight]' => 0,
+       'field_unlimited[und][1][_weight]' => -1,
+       'entity_2[field_unlimited][und][0][_weight]' => 0,
+       'entity_2[field_unlimited][und][1][_weight]' => -1,
+     );
+     $this->drupalPost('test-entity/nested/1/2', $edit, t('Save'));
+     field_cache_clear();
+     $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(3, 2));
+     $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(13, 12));
+
+     // Test the 'add more' buttons. Only AJAX submission is tested, because
+     // the two 'add more' buttons present in the form have the same #value,
+     // which confuses drupalPost().
+     // 'Add more' button in the first entity:
+     $this->drupalGet('test-entity/nested/1/2');
+     $this->drupalPostAJAX(NULL, array(), 'field_unlimited_add_more');
+     $this->assertFieldByName('field_unlimited[und][0][value]', 3, t('Entity 1: field_unlimited value 0 appears correctly is the form.'));
+     $this->assertFieldByName('field_unlimited[und][1][value]', 2, t('Entity 1: field_unlimited value 1 appears correctly is the form.'));
+     $this->assertFieldByName('field_unlimited[und][2][value]', '', t('Entity 1: field_unlimited value 2 appears correctly is the form.'));
+     $this->assertFieldByName('field_unlimited[und][3][value]', '', t('Entity 1: an empty widget was added for field_unlimited value 3.'));
+     // 'Add more' button in the first entity (changing field values):
+     $edit = array(
+       'entity_2[field_unlimited][und][0][value]' => 13,
+       'entity_2[field_unlimited][und][1][value]' => 14,
+       'entity_2[field_unlimited][und][2][value]' => 15,
+     );
+     $this->drupalPostAJAX(NULL, $edit, 'entity_2_field_unlimited_add_more');
+     $this->assertFieldByName('entity_2[field_unlimited][und][0][value]', 13, t('Entity 2: field_unlimited value 0 appears correctly is the form.'));
+     $this->assertFieldByName('entity_2[field_unlimited][und][1][value]', 14, t('Entity 2: field_unlimited value 1 appears correctly is the form.'));
+     $this->assertFieldByName('entity_2[field_unlimited][und][2][value]', 15, t('Entity 2: field_unlimited value 2 appears correctly is the form.'));
+     $this->assertFieldByName('entity_2[field_unlimited][und][3][value]', '', t('Entity 2: an empty widget was added for field_unlimited value 3.'));
+     // Save the form and check values are saved correclty.
+     $this->drupalPost(NULL, array(), t('Save'));
+     field_cache_clear();
+     $this->assertFieldValues($entity_1, 'field_unlimited', LANGUAGE_NONE, array(3, 2));
+     $this->assertFieldValues($entity_2, 'field_unlimited', LANGUAGE_NONE, array(13, 14, 15));
+  }
 }
 
 class FieldDisplayAPITestCase extends FieldTestCase {
diff --git modules/field/tests/field_test.entity.inc modules/field/tests/field_test.entity.inc
index b565ac8..17afb6e 100644
--- modules/field/tests/field_test.entity.inc
+++ modules/field/tests/field_test.entity.inc
@@ -414,6 +414,70 @@ function field_test_entity_form_submit_builder($form, &$form_state) {
 }
 
 /**
+ * Form combining two separate entities.
+ */
+function field_test_entity_nested_form($form, &$form_state, $entity_1, $entity_2) {
+  // First entity.
+  foreach (array('ftid', 'ftvid', 'fttype') as $key) {
+    $form[$key] = array(
+      '#type' => 'value',
+      '#value' => $entity_1->$key,
+    );
+  }
+  field_attach_form('test_entity', $entity_1, $form, $form_state);
+
+  // Second entity.
+  $form['entity_2'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Second entity'),
+    '#tree' => TRUE,
+    '#parents' => array('entity_2'),
+    '#weight' => 50,
+  );
+  foreach (array('ftid', 'ftvid', 'fttype') as $key) {
+    $form['entity_2'][$key] = array(
+      '#type' => 'value',
+      '#value' => $entity_2->$key,
+    );
+  }
+  field_attach_form('test_entity', $entity_2, $form['entity_2'], $form_state);
+
+  $form['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => 100,
+  );
+
+  return $form;
+}
+
+/**
+ * Validate handler for field_test_entity_nested_form().
+ */
+function field_test_entity_nested_form_validate($form, &$form_state) {
+  $entity_1 = (object) $form_state['values'];
+  field_attach_form_validate('test_entity', $entity_1, $form, $form_state);
+
+  $entity_2 = (object) $form_state['values']['entity_2'];
+  field_attach_form_validate('test_entity', $entity_2, $form['entity_2'], $form_state);
+}
+
+/**
+ * Submit handler for field_test_entity_nested_form().
+ */
+function field_test_entity_nested_form_submit($form, &$form_state) {
+  $entity_1 = (object) $form_state['values'];
+  field_attach_submit('test_entity', $entity_1, $form, $form_state);
+  field_test_entity_save($entity_1);
+
+  $entity_2 = (object) $form_state['values']['entity_2'];
+  field_attach_submit('test_entity', $entity_2, $form['entity_2'], $form_state);
+  field_test_entity_save($entity_2);
+
+  drupal_set_message(t('test_entities @id_1 and @id_2 have been updated.', array('@id_1' => $entity_1->ftid, '@id_2' => $entity_2->ftid)));
+}
+
+/**
  * Controller class for the test_entity_bundle entity type.
  *
  * This extends the DrupalDefaultEntityController class, adding required
diff --git modules/field/tests/field_test.module modules/field/tests/field_test.module
index baaef3f..4f54f8c 100644
--- modules/field/tests/field_test.module
+++ modules/field/tests/field_test.module
@@ -60,6 +60,14 @@ function field_test_menu() {
     'type' => MENU_NORMAL_ITEM,
   );
 
+  $items['test-entity/nested/%field_test_entity_test/%field_test_entity_test'] = array(
+    'title' => 'Nested entity form',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('field_test_entity_nested_form', 2, 3),
+    'access arguments' => array('administer field_test content'),
+    'type' => MENU_NORMAL_ITEM,
+  );
+
   return $items;
 }
 
diff --git modules/field_ui/field_ui.admin.inc modules/field_ui/field_ui.admin.inc
index c617438..3b4ebed 100644
--- modules/field_ui/field_ui.admin.inc
+++ modules/field_ui/field_ui.admin.inc
@@ -1913,7 +1913,9 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
     '#collapsible' => FALSE,
     '#tree' => TRUE,
     '#description' => t('The default value for this field, used when creating new content.'),
-    // Make sure the values appear at the top-level of $form_state['values'].
+    // Stick to an empty 'parents' on this form in order not to breaks widgets
+    // that do not use field_widget_[field|instance]() and still access
+    // $form_state['field'] directly.
     '#parents' => array(),
   );
 
@@ -1921,8 +1923,9 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
   $items = $instance['default_value'];
   $instance['required'] = FALSE;
   $instance['description'] = '';
+
   // @todo Allow multiple values (requires more work on 'add more' JS handler).
-  $element += field_default_form(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state, 0);
+  $element += field_default_form(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state, 0);
 
   return $element;
 }
@@ -1931,22 +1934,34 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
  * Form validation handler for field instance settings form.
  */
 function field_ui_field_edit_form_validate($form, &$form_state) {
+  // Take the incoming values as the $instance definition, so that the 'default
+  // value' gets validated using the instance settings being submitted.
   $instance = $form_state['values']['instance'];
   $field_name = $instance['field_name'];
-  $field = field_info_field($field_name);
 
-  // Extract the 'default value'.
-  $items = array();
-  field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
-  // Validate the value and report errors.
-  $errors = array();
-  $function = $field['module'] . '_field_validate';
-  if (function_exists($function)) {
-    $function(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $errors);
-  }
-  if (isset($errors[$field_name][LANGUAGE_NONE])) {
-    $form_state['field'][$field_name][LANGUAGE_NONE]['errors'] = $errors[$field_name][LANGUAGE_NONE];
-    field_default_form_errors(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
+  if (isset($form['instance']['default_value_widget'])) {
+    $element = $form['instance']['default_value_widget'];
+
+    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NONE, $form_state);
+    $field = $field_state['field'];
+
+    // Extract the 'default value'.
+    $items = array();
+    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
+
+    // Validate the value and report errors.
+    $errors = array();
+    $function = $field['module'] . '_field_validate';
+    if (function_exists($function)) {
+      $function(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $errors);
+    }
+    if (isset($errors[$field_name][LANGUAGE_NONE])) {
+      // Store reported errors in $form_state.
+      $field_state['errors'] = $errors[$field_name][LANGUAGE_NONE];
+      field_form_set_state($element['#parents'], $field_name, LANGUAGE_NONE, $form_state, $field_state);
+      // Assign reported errors to the correct form element.
+      field_default_form_errors(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
+    }
   }
 }
 
@@ -1963,12 +1978,16 @@ function field_ui_field_edit_form_submit($form, &$form_state) {
   field_update_field($field);
 
   // Handle the default value.
-  // Extract field values.
-  $items = array();
-  field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
-  // Prepare field values.
-  field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $form, $form_state);
-  $instance['default_value'] = $items ? $items : NULL;
+  if (isset($form['instance']['default_value_widget'])) {
+    $element = $form['instance']['default_value_widget'];
+
+    // Extract field values.
+    $items = array();
+    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
+    field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
+
+    $instance['default_value'] = $items ? $items : NULL;
+  }
 
   // Retrieve the stored instance settings to merge with the incoming values.
   $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
diff --git modules/file/file.field.inc modules/file/file.field.inc
index 1a0a4b1..8b14b7a 100644
--- modules/file/file.field.inc
+++ modules/file/file.field.inc
@@ -450,9 +450,14 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco
 
   // Retrieve any values set in $form_state, as will be the case during AJAX
   // rebuilds of this form.
-  if (isset($form_state['values'][$field['field_name']][$langcode])) {
-    $items = $form_state['values'][$field['field_name']][$langcode];
-    unset($form_state['values'][$field['field_name']][$langcode]);
+  if (isset($form_state['values'])) {
+    $path = array_merge($element['#field_parents'], array($field['field_name'], $langcode));
+    $path_exists = FALSE;
+    $values = drupal_array_get_nested_value($form_state['values'], $path, $path_exists);
+    if ($path_exists) {
+      $items = $values;
+      drupal_array_set_nested_value($form_state['values'], $path, NULL);
+    }
   }
 
   foreach ($items as $delta => $item) {
@@ -748,8 +753,8 @@ function file_field_widget_submit($form, &$form_state) {
   // to avoid a mismatch between old and new deltas. The rebuilt elements will
   // have #default_value set appropriately for the current state of the field,
   // so nothing is lost in doing this.
-  list($field_name, $langcode) = $form_state['triggering_element']['#parents'];
-  unset($form_state['input'][$field_name][$langcode]);
+  $parents = array_slice($form_state['triggering_element']['#parents'], 0, -2);
+  drupal_array_set_nested_value($form_state['input'], $parents, NULL);
 }
 
 /**
