diff --git a/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php b/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
index b4c6720..2f14648 100644
--- a/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
+++ b/core/lib/Drupal/Core/Field/ConfigEntityReferenceItemBase.php
@@ -8,85 +8,14 @@
 namespace Drupal\Core\Field;
 
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
-use Drupal\field\FieldInstanceInterface;
-use Drupal\field\FieldInterface;
 
 /**
  * A common base class for configurable entity reference fields.
  *
- * Extends the Core 'entity_reference' entity field item with properties for
- * revision ids, labels (for autocreate) and access.
- *
- * Required settings (below the definition's 'settings' key) are:
- *  - target_type: The entity type to reference.
+ * Extends the Core 'entity_reference' entity field item with common methods
+ * used in general configurable entity reference field.
  */
-class ConfigEntityReferenceItemBase extends EntityReferenceItem implements ConfigFieldItemInterface {
-
-  /**
-   * Definitions of the contained properties.
-   *
-   * @see ConfigurableEntityReferenceItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPropertyDefinitions() {
-    $target_type = $this->definition['settings']['target_type'];
-
-    // Definitions vary by entity type and bundle, so key them accordingly.
-    $key = $target_type . ':';
-    $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : '';
-
-    if (!isset(static::$propertyDefinitions[$key])) {
-      // Call the parent to define the target_id and entity properties.
-      parent::getPropertyDefinitions();
-
-      // Only add the revision ID property if the target entity type supports
-      // revisions.
-      $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-      if (!empty($target_type_info['entity_keys']['revision']) && !empty($target_type_info['revision_table'])) {
-        static::$propertyDefinitions[$key]['revision_id'] = array(
-          'type' => 'integer',
-          'label' => t('Revision ID'),
-          'constraints' => array(
-            'Range' => array('min' => 0),
-          ),
-        );
-      }
-
-      static::$propertyDefinitions[$key]['label'] = array(
-        'type' => 'string',
-        'label' => t('Label (auto-create)'),
-        'computed' => TRUE,
-      );
-      static::$propertyDefinitions[$key]['access'] = array(
-        'type' => 'boolean',
-        'label' => t('Access'),
-        'computed' => TRUE,
-      );
-    }
-    return static::$propertyDefinitions[$key];
-  }
-
-  /**
-   * {@inheritdoc}
-   *
-   * Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
-   * since we cannot extend it.
-   */
-  public static function schema(FieldInterface $field) {
-    $definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->type);
-    $module = $definition['provider'];
-    module_load_install($module);
-    $callback = "{$module}_field_schema";
-    if (function_exists($callback)) {
-      return $callback($field);
-    }
-  }
+class ConfigEntityReferenceItemBase extends EntityReferenceItem {
 
   /**
    * {@inheritdoc}
@@ -106,76 +35,14 @@ public function isEmpty() {
 
   /**
    * {@inheritdoc}
-   *
-   * Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
-   * since we cannot extend it.
-   */
-  public function settingsForm(array $form, array &$form_state, $has_data) {
-    if ($callback = $this->getLegacyCallback('settings_form')) {
-      $instance = $this->getFieldDefinition();
-      if (!($instance instanceof FieldInstanceInterface)) {
-        throw new \UnexpectedValueException('ConfigEntityReferenceItemBase::settingsForm() called for a field whose definition is not a field instance.');
-      }
-      // hook_field_settings_form() used to receive the $instance (not actually
-      // needed), and the value of field_has_data().
-      return $callback($instance->getField(), $instance, $has_data);
-    }
-    return array();
-  }
-
-  /**
-   * {@inheritdoc}
-   *
-   * Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
-   * since we cannot extend it.
-   */
-  public function instanceSettingsForm(array $form, array &$form_state) {
-    if ($callback = $this->getLegacyCallback('instance_settings_form')) {
-      $instance = $this->getFieldDefinition();
-      if (!($instance instanceof FieldInstanceInterface)) {
-        throw new \UnexpectedValueException('ConfigEntityReferenceItemBase::instanceSettingsForm() called for a field whose definition is not a field instance.');
-      }
-      return $callback($instance->getField(), $instance, $form_state);
-    }
-    return array();
-  }
-
-  /**
-   * Returns options provided via the legacy callback hook_options_list().
-   *
-   * @todo: Convert all legacy callback implementations to methods.
-   *
-   * @see \Drupal\Core\TypedData\AllowedValuesInterface
    */
-  public function getSettableOptions() {
-    $definition = $this->getPluginDefinition();
-    $callback = "{$definition['provider']}_options_list";
-    if (function_exists($callback)) {
-      // We are at the field item level, so we need to go two levels up to get
-      // to the entity object.
-      return $callback($this->getFieldDefinition(), $this->getEntity());
-    }
-  }
+  public function preSave() {
+    $entity = $this->get('entity')->getValue();
+    $target_id = $this->get('target_id')->getValue();
 
-  /**
-   * Returns the legacy callback for a given field type "hook".
-   *
-   * Copied from \Drupal\field\Plugin\Field\FieldType\LegacyConfigFieldItem,
-   * since we cannot extend it.
-   *
-   * @param string $hook
-   *   The name of the hook, e.g. 'settings_form', 'is_empty'.
-   *
-   * @return string|null
-   *   The name of the legacy callback, or NULL if it does not exist.
-   */
-  protected function getLegacyCallback($hook) {
-    $definition = $this->getPluginDefinition();
-    $module = $definition['provider'];
-    $callback = "{$module}_field_{$hook}";
-    if (function_exists($callback)) {
-      return $callback;
+    if (!$target_id && !empty($entity) && $entity->isNew()) {
+      $entity->save();
+      $this->set('target_id', $entity->id());
     }
   }
-
 }
diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php
index 31c9e09..157df62 100644
--- a/core/lib/Drupal/Core/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Field/FieldItemBase.php
@@ -224,4 +224,10 @@ public function delete() { }
    */
   public function deleteRevision() { }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return 'value';
+  }
 }
diff --git a/core/lib/Drupal/Core/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Field/FieldItemInterface.php
index 5524cb4..4a2ecc2 100644
--- a/core/lib/Drupal/Core/Field/FieldItemInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemInterface.php
@@ -15,8 +15,9 @@
  * Entity field items are typed data objects containing the field values, i.e.
  * implementing the ComplexDataInterface.
  *
- * When implementing this interface which extends Traversable, make sure to list
- * IteratorAggregate or Iterator before this interface in the implements clause.
+ * When implementing this interface which extends Traversable, make sure to
+ * list IteratorAggregate or Iterator before this interface in the implements
+ * clause.
  *
  * @see \Drupal\Core\Field\FieldItemListInterface
  * @see \Drupal\Core\Field\FieldItemBase
@@ -122,8 +123,8 @@ public function update();
   /**
    * Defines custom delete behavior for field values.
    *
-   * This method is called during the process of deleting an entity, just before
-   * values are deleted from storage.
+   * This method is called during the process of deleting an entity, just
+   * before values are deleted from storage.
    */
   public function delete();
 
@@ -131,9 +132,23 @@ public function delete();
    * Defines custom revision delete behavior for field values.
    *
    * This method is called from during the process of deleting an entity
-   * revision, just before the field values are deleted from storage. It is only
-   * called for entity types that support revisioning.
+   * revision, just before the field values are deleted from storage. It is
+   * only called for entity types that support revisioning.
    */
   public function deleteRevision();
 
+  /**
+   * Returns the name of the main property, if any.
+   *
+   * Some field items consist mainly of one main property, e.g. the value of a
+   * text field or the @code target_id @endcode of an entity reference. If the
+   * field item has no main property, the method returns NULL.
+   *
+   * @return string|null
+   *   The name of the value property, or NULL if there is none.
+   *
+   * @todo: Move this to ComplexDataInterface once we improved Typed data to do
+   *   not enforce having all methods on the data objects.
+   */
+  public function getMainPropertyName();
 }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 61f624f..2749428 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -144,4 +144,11 @@ public function onChange($property_name) {
     }
     parent::onChange($property_name);
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMainPropertyName() {
+    return 'target_id';
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
index 9abe52d..5c782ae 100644
--- a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
+++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
@@ -24,6 +24,11 @@
  * as structured options arrays that can be used in an Options widget such as a
  * select box or checkboxes.
  *
+ * Note that this interface is mostly applicable for primitive data values, but
+ * can be used on complex data structures if a (primitive) main property is
+ * specified. In that case, the allowed values and options apply to the main
+ * property only.
+ *
  * @see \Drupal\options\Plugin\Field\FieldWidget\OptionsWidgetBase
  */
 interface AllowedValuesInterface {
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 1428822..7a7d30a 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -377,7 +377,7 @@ public function getConstraints($definition) {
     // definition for performing interface checks.
     $class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
     // Check if the class provides allowed values.
-    if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
+    if (is_subclass_of($class,'Drupal\Core\TypedData\AllowedValuesInterface')) {
       $constraints[] = $validation_manager->create('AllowedValues', array());
     }
 
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
index 7ad2cfd..05c8d98 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
 
 use Drupal\Core\TypedData\AllowedValuesInterface;
+use Drupal\Core\TypedData\ComplexDataInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\Constraints\ChoiceValidator;
 
@@ -20,10 +21,29 @@ class AllowedValuesConstraintValidator extends ChoiceValidator {
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
-    if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
+    $typed_data = $this->context->getMetadata()->getTypedData();
+
+    if ($typed_data instanceof AllowedValuesInterface) {
       $account = \Drupal::currentUser();
-      $allowed_values = $this->context->getMetadata()->getTypedData()->getSettableValues($account);
+      $allowed_values = $typed_data->getSettableValues($account);
       $constraint->choices = $allowed_values;
+
+      // If the data is complex, we have to validate its main property.
+      if ($typed_data instanceof ComplexDataInterface) {
+        $name = $typed_data->getMainPropertyName();
+        if (!isset($name)) {
+          throw new \LogicException('Cannot validate allowed values for complex data without a main property.');
+        }
+        $value = $typed_data->get($name)->getValue();
+      }
+    }
+
+    // The parent implementation ignores values that are not set, but makes
+    // sure some choices are available firstly. However, we want to support
+    // empty choices for undefined values, e.g. if a term reference field
+    // points to an empty vocabulary.
+    if (!isset($value)) {
+      return TRUE;
     }
     return parent::validate($value, $constraint);
   }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
index bf58a7f..118a5ba 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
@@ -17,6 +17,9 @@
  * Replaces the Core 'entity_reference' entity field type implementation, this
  * supports configurable fields, auto-creation of referenced entities and more.
  *
+ * Required settings (below the definition's 'settings' key) are:
+ *  - target_type: The entity type to reference.
+ *
  * @see entity_reference_field_info_alter().
  *
  */
@@ -25,6 +28,37 @@ class ConfigurableEntityReferenceItem extends ConfigEntityReferenceItemBase impl
   /**
    * {@inheritdoc}
    */
+  public function getPropertyDefinitions() {
+    $target_type = $this->definition['settings']['target_type'];
+
+    // Definitions vary by entity type and bundle, so key them accordingly.
+    $key = $target_type . ':';
+    $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : '';
+
+    if (!isset(static::$propertyDefinitions[$key])) {
+      // Call the parent to define the target_id and entity properties.
+      parent::getPropertyDefinitions();
+
+      // Only add the revision ID property if the target entity type supports
+      // revisions.
+      $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
+      if (!empty($target_type_info['entity_keys']['revision']) && !empty($target_type_info['revision_table'])) {
+        static::$propertyDefinitions[$key]['revision_id'] = array(
+          'type' => 'integer',
+          'label' => t('Revision ID'),
+          'constraints' => array(
+            'Range' => array('min' => 0),
+          ),
+        );
+      }
+    }
+
+    return static::$propertyDefinitions[$key];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public static function schema(FieldInterface $field) {
     $target_type = $field->getFieldSetting('target_type');
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
@@ -68,19 +102,6 @@ public static function schema(FieldInterface $field) {
   /**
    * {@inheritdoc}
    */
-  public function preSave() {
-    $entity = $this->get('entity')->getValue();
-    $target_id = $this->get('target_id')->getValue();
-
-    if (!$target_id && !empty($entity) && $entity->isNew()) {
-      $entity->save();
-      $this->set('target_id', $entity->id());
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function settingsForm(array $form, array &$form_state, $has_data) {
     $element['target_type'] = array(
       '#type' => 'select',
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php
index fd942d9..dbfe4d1 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php
@@ -27,7 +27,7 @@ public function prepareView(array $entities_items) {
     foreach ($entities_items as $items) {
       foreach ($items as $item) {
         // Force the array key to prevent duplicates.
-        if ($item->target_id !== 0) {
+        if ($item->target_id != NULL) {
           $tids[$item->target_id] = $item->target_id;
         }
       }
@@ -48,7 +48,7 @@ public function prepareView(array $entities_items) {
             $item->entity = $terms[$item->target_id];
           }
           // Terms to be created are not in $terms, but are still legitimate.
-          elseif ($item->target_id === 0 && isset($item->entity)) {
+          elseif ($item->target_id === NULL && isset($item->entity)) {
             // Leave the item in place.
           }
           // Otherwise, unset the instance value, since the term does not exist.
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
new file mode 100644
index 0000000..a6ca29f
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Plugin\field\field_type\TaxonomyTermReferenceItem.
+ */
+
+namespace Drupal\taxonomy\Plugin\Field\FieldType;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Field\ConfigEntityReferenceItemBase;
+use Drupal\Core\Field\ConfigFieldItemInterface;
+use Drupal\field\FieldInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TypedData\AllowedValuesInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Plugin implementation of the 'term_reference' field type.
+ *
+ * @FieldType(
+ *   id = "taxonomy_term_reference",
+ *   label = @Translation("Term Reference"),
+ *   description = @Translation("This field stores a reference to a taxonomy term."),
+ *   settings = {
+ *     "options_list_callback" = NULL,
+ *     "allowed_values" = {
+ *       {
+ *         "vocabulary" = "",
+ *         "parent" = "0"
+ *       }
+ *     }
+ *   },
+ *   instance_settings = { },
+ *   default_widget = "options_select",
+ *   default_formatter = "taxonomy_term_reference_link"
+ * )
+ */
+class TaxonomyTermReferenceItem extends ConfigEntityReferenceItemBase implements ConfigFieldItemInterface, AllowedValuesInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPossibleValues(AccountInterface $account = NULL) {
+    // Flatten options firstly, because Possible Options may contain group
+    // arrays.
+    $flatten_options = $this->flattenOptions($this->getPossibleOptions($account));
+    return array_keys($flatten_options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPossibleOptions(AccountInterface $account = NULL) {
+    return $this->getSettableOptions($account);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSettableValues(AccountInterface $account = NULL) {
+   // Flatten options firstly, because Settable Options may contain group
+   // arrays.
+    $flatten_options = $this->flattenOptions($this->getSettableOptions($account));
+    return array_keys($flatten_options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSettableOptions(AccountInterface $account = NULL) {
+    $instance = $this->getFieldDefinition();
+    $entity = $this->getParent()->getParent();
+    $function = $this->getFieldSetting('options_list_callback') ? $this->getFieldSetting('options_list_callback') : array($this, 'getDefaultOptions');
+    return call_user_func_array($function, array($instance, $entity));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    $this->definition['settings']['target_type'] = 'taxonomy_term';
+    return parent::getPropertyDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldInterface $field) {
+    return array(
+      'columns' => array(
+        'target_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => FALSE,
+        ),
+      ),
+      'indexes' => array(
+        'target_id' => array('target_id'),
+      ),
+      'foreign keys' => array(
+        'target_id' => array(
+          'table' => 'taxonomy_term_data',
+          'columns' => array('target_id' => 'tid'),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, array &$form_state, $has_data) {
+    // Get proper values for 'allowed_values_function', which is a core setting.
+    $vocabularies = entity_load_multiple('taxonomy_vocabulary');
+    $options = array();
+    foreach ($vocabularies as $vocabulary) {
+      $options[$vocabulary->id()] = $vocabulary->name;
+    }
+
+    $settings = $this->getFieldSettings();
+    $element = array();
+    $element['#tree'] = TRUE;
+
+    foreach ($settings['allowed_values'] as $delta => $tree) {
+      $element['allowed_values'][$delta]['vocabulary'] = array(
+        '#type' => 'select',
+        '#title' => t('Vocabulary'),
+        '#default_value' => $tree['vocabulary'],
+        '#options' => $options,
+        '#required' => TRUE,
+        '#description' => t('The vocabulary which supplies the options for this field.'),
+        '#disabled' => $has_data,
+      );
+      $element['allowed_values'][$delta]['parent'] = array(
+        '#type' => 'value',
+        '#value' => $tree['parent'],
+      );
+    }
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function instanceSettingsForm(array $form, array &$form_state) {
+    return array();
+  }
+
+  /**
+  * Flattens an array of allowed values.
+  *
+  * @param array $array
+  *   A single or multidimensional array.
+  *
+  * @return array
+  *   The flattened array.
+  */
+  protected function flattenOptions(array $array) {
+    $result = array();
+    array_walk_recursive($array, function($a, $b) use (&$result) { $result[$b] = $a; });
+    return $result;
+  }
+
+  /**
+   * Returns the set of valid terms for a taxonomy field.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object the field is attached to.
+   *
+   * @return array
+   *   The array of valid terms for this field, keyed by term id.
+   */
+  public function getDefaultOptions(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
+    $options = array();
+    foreach ($field_definition->getFieldSetting('allowed_values') as $tree) {
+      if ($vocabulary = entity_load('taxonomy_vocabulary', $tree['vocabulary'])) {
+        if ($terms = taxonomy_get_tree($vocabulary->id(), $tree['parent'], NULL, TRUE)) {
+          foreach ($terms as $term) {
+            $options[$term->id()] = str_repeat('-', $term->depth) . $term->label();
+          }
+        }
+      }
+    }
+    return $options;
+  }
+
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldWidget/TaxonomyAutocompleteWidget.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldWidget/TaxonomyAutocompleteWidget.php
index 81bdefa..a4938f2 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldWidget/TaxonomyAutocompleteWidget.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldWidget/TaxonomyAutocompleteWidget.php
@@ -114,7 +114,7 @@ public function massageFormValues(array $values, array $form, array &$form_state
           'vid' => $vocabulary->id(),
           'name' => $value,
         ));
-        $item = array('target_id' => 0, 'entity' => $term);
+        $item = array('target_id' => NULL, 'entity' => $term);
       }
       $items[] = $item;
     }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
deleted file mode 100644
index 85adae2..0000000
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\taxonomy\Type\TaxonomyTermReferenceItem.
- */
-
-namespace Drupal\taxonomy\Type;
-
-use Drupal\Core\Field\ConfigEntityReferenceItemBase;
-
-/**
- * Defines the 'taxonomy_term_reference' entity field item.
- */
-class TaxonomyTermReferenceItem extends ConfigEntityReferenceItemBase {
-
-  /**
-   * Property definitions of the contained properties.
-   *
-   * @see TaxonomyTermReferenceItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPropertyDefinitions() {
-    $this->definition['settings']['target_type'] = 'taxonomy_term';
-    return parent::getPropertyDefinitions();
-  }
-
-}
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index 845a44e..8531fb7 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -165,30 +165,6 @@ function taxonomy_schema() {
 }
 
 /**
- * Implements hook_field_schema().
- */
-function taxonomy_field_schema($field) {
-  return array(
-    'columns' => array(
-      'target_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-      ),
-    ),
-    'indexes' => array(
-      'target_id' => array('target_id'),
-    ),
-    'foreign keys' => array(
-      'target_id' => array(
-        'table' => 'taxonomy_term_data',
-        'columns' => array('target_id' => 'tid'),
-      ),
-    ),
-  );
-}
-
-/**
  * Implements hook_update_dependencies().
  */
 function taxonomy_update_dependencies() {
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 9c89610..7efe333 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -7,9 +7,6 @@
 
 use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
-use Drupal\field\FieldInterface;
-use Drupal\field\FieldInstanceInterface;
 use Drupal\file\FileInterface;
 use Drupal\node\Entity\Node;
 use Drupal\taxonomy\Entity\Term;
@@ -871,38 +868,6 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
 }
 
 /**
- * Implements hook_field_info().
- *
- * Field settings:
- * - allowed_values: a list array of one or more vocabulary trees:
- *   - vocabulary: a vocabulary machine name.
- *   - parent: a term ID of a term whose children are allowed. This should be
- *     '0' if all terms in a vocabulary are allowed. The allowed values do not
- *     include the parent term.
- *
- */
-function taxonomy_field_info() {
-  return array(
-    'taxonomy_term_reference' => array(
-      'label' => t('Term reference'),
-      'description' => t('This field stores a reference to a taxonomy term.'),
-      'default_widget' => 'options_select',
-      'default_formatter' => 'taxonomy_term_reference_link',
-      'class' => 'Drupal\taxonomy\Type\TaxonomyTermReferenceItem',
-      'settings' => array(
-        'options_list_callback' => NULL,
-        'allowed_values' => array(
-          array(
-            'vocabulary' => '',
-            'parent' => '0',
-          ),
-        ),
-      ),
-    ),
-  );
-}
-
-/**
  * Implements hook_field_widget_info_alter().
  */
 function taxonomy_field_widget_info_alter(&$info) {
@@ -911,105 +876,6 @@ function taxonomy_field_widget_info_alter(&$info) {
 }
 
 /**
- * Implements hook_options_list().
- */
-function taxonomy_options_list(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
-  $function = $field_definition->getFieldSetting('options_list_callback') ?: 'taxonomy_allowed_values';
-  return $function($field_definition, $entity);
-}
-
-/**
- * Implements hook_field_validate().
- *
- * Taxonomy field settings allow for either a single vocabulary ID, multiple
- * vocabulary IDs, or sub-trees of a vocabulary to be specified as allowed
- * values, although only the first of these is supported via the field UI.
- * Confirm that terms entered as values meet at least one of these conditions.
- *
- * Possible error codes:
- * - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values.
- */
-function taxonomy_field_validate(EntityInterface $entity = NULL, FieldInterface $field, FieldInstanceInterface $instance, $langcode, $items, &$errors) {
-  // Build an array of existing term IDs so they can be loaded with
-  // entity_load_multiple('taxonomy_term');
-  foreach ($items as $delta => $item) {
-    if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
-      $tids[] = $item['target_id'];
-    }
-  }
-  if (!empty($tids)) {
-    $terms = entity_load_multiple('taxonomy_term', $tids);
-
-    // Check each existing item to ensure it can be found in the
-    // allowed values for this field.
-    foreach ($items as $delta => $item) {
-      $validate = TRUE;
-      if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
-        $validate = FALSE;
-        foreach ($instance->getFieldSetting('allowed_values') as $settings) {
-          // If no parent is specified, check if the term is in the vocabulary.
-          if (isset($settings['vocabulary']) && empty($settings['parent'])) {
-            if ($settings['vocabulary'] == $terms[$item['target_id']]->bundle()) {
-              $validate = TRUE;
-              break;
-            }
-          }
-          // If a parent is specified, then to validate it must appear in the
-          // array returned by taxonomy_term_load_parents_all().
-          elseif (!empty($settings['parent'])) {
-            $ancestors = taxonomy_term_load_parents_all($item['target_id']);
-            foreach ($ancestors as $ancestor) {
-              if ($ancestor->id() == $settings['parent']) {
-                $validate = TRUE;
-                break 2;
-              }
-            }
-          }
-        }
-      }
-      if (!$validate) {
-        $errors[$instance->getFieldName()][$langcode][$delta][] = array(
-          'error' => 'taxonomy_term_reference_illegal_value',
-          'message' => t('%name: illegal value.', array('%name' => $instance->getFieldLabel())),
-        );
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_is_empty().
- */
-function taxonomy_field_is_empty($item, $field_type) {
-  return !is_array($item) || (empty($item['target_id']) && empty($item['entity']));
-}
-
-/**
- * Returns the set of valid terms for a taxonomy field.
- *
- * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
- *   The field definition.
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity object the field is attached to.
- *
- * @return
- *   The array of valid terms for this field, keyed by term id.
- */
-function taxonomy_allowed_values(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
-  $options = array();
-  foreach ($field_definition->getFieldSetting('allowed_values') as $tree) {
-    if ($vocabulary = entity_load('taxonomy_vocabulary', $tree['vocabulary'])) {
-      if ($terms = taxonomy_get_tree($vocabulary->id(), $tree['parent'], NULL, TRUE)) {
-        foreach ($terms as $term) {
-          $options[$term->id()] = str_repeat('-', $term->depth) . $term->label();
-        }
-      }
-    }
-  }
-  return $options;
-}
-
-/**
  * Title callback for term pages.
  *
  * @param \Drupal\taxonomy\Entity\Term $term
@@ -1036,39 +902,6 @@ function taxonomy_autocomplete_validate($element, &$form_state) {
 }
 
 /**
- * Implements hook_field_settings_form().
- */
-function taxonomy_field_settings_form($field, $instance) {
-  // Get proper values for 'allowed_values_function', which is a core setting.
-  $vocabularies = entity_load_multiple('taxonomy_vocabulary');
-  $options = array();
-  foreach ($vocabularies as $vocabulary) {
-    $options[$vocabulary->id()] = $vocabulary->name;
-  }
-  $form['allowed_values'] = array(
-    '#tree' => TRUE,
-  );
-
-  foreach ($field->getFieldSetting('allowed_values') as $delta => $tree) {
-    $form['allowed_values'][$delta]['vocabulary'] = array(
-      '#type' => 'select',
-      '#title' => t('Vocabulary'),
-      '#default_value' => $tree['vocabulary'],
-      '#options' => $options,
-      '#required' => TRUE,
-      '#description' => t('The vocabulary which supplies the options for this field.'),
-      '#disabled' => $field->hasData(),
-    );
-    $form['allowed_values'][$delta]['parent'] = array(
-      '#type' => 'value',
-      '#value' => $tree['parent'],
-    );
-  }
-
-  return $form;
-}
-
-/**
  * @defgroup taxonomy_index Taxonomy indexing
  * @{
  * Functions to maintain taxonomy indexing.
@@ -1087,20 +920,6 @@ function taxonomy_field_settings_form($field, $instance) {
  */
 
 /**
- * Implements hook_field_presave().
- *
- * Create any new terms defined in a freetagging vocabulary.
- */
-function taxonomy_field_presave(EntityInterface $entity, $field, $instance, $langcode, &$items) {
-  foreach ($items as $delta => $item) {
-    if (!$item['target_id'] && isset($item['target_id'])) {
-      $item['entity']->save();
-      $items[$delta]['target_id'] = $item['entity']->id();
-    }
-  }
-}
-
-/**
  * Implements hook_node_insert().
  */
 function taxonomy_node_insert(EntityInterface $node) {
