diff --git a/core/modules/options/options.module b/core/modules/options/options.module
index d9a1b24..e6c9ba0 100644
--- a/core/modules/options/options.module
+++ b/core/modules/options/options.module
@@ -5,8 +5,8 @@
  * Defines selection, check box and radio button widgets for text and numeric fields.
  */
 
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\field\FieldConfigInterface;
@@ -54,30 +54,43 @@ function options_field_config_delete(FieldConfigInterface $field) {
  * The strings are not safe for output. Keys and values of the array should be
  * sanitized through field_filter_xss() before being displayed.
  *
- * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
- *   The field definition.
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity object.
+ * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
+ *   The field storage definition.
+ * @param \Drupal\Core\Field\FieldItemListInterface|NULL $items
+ *   (optional) If called from the context of a specific field on a specific
+ *   entity, then this is the corresponding field values presently set on the
+ *   entity (the field definition and entity can also be retrieved from this
+ *   object). This allows custom 'allowed_values_function' functions to either
+ *   restrict the values or customize the labels for particular bundles and
+ *   entities. Note that when configuring Views filters, this is not passed,
+ *   so it is recommended that such functions are written such that the
+ *   array keys returned when this value is NULL is the superset of what is
+ *   returned when it is provided.
  *
- * @return
+ * @return array
  *   The array of allowed values. Keys of the array are the raw stored values
  *   (number or text), values of the array are the display labels.
  */
-function options_allowed_values(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
+function options_allowed_values(FieldStorageDefinitionInterface $definition, FieldItemListInterface $items = NULL) {
   $allowed_values = &drupal_static(__FUNCTION__, array());
 
-  $cache_id = implode(':', array($entity->getEntityTypeId(), $entity->bundle(), $field_definition->getName()));
+  $cache_keys = array($definition->getTargetEntityTypeId(), $definition->getName());
+  if (isset($items)) {
+    $cache_keys[] = $items->getEntity()->bundle();
+  }
+  $cache_id = implode(':', $cache_keys);
+
   if (!isset($allowed_values[$cache_id])) {
-    $function = $field_definition->getSetting('allowed_values_function');
+    $function = $definition->getSetting('allowed_values_function');
     // If $cacheable is FALSE, then the allowed values are not statically
     // cached. See options_test_dynamic_values_callback() for an example of
     // generating dynamic and uncached values.
     $cacheable = TRUE;
     if (!empty($function)) {
-      $values = $function($field_definition, $entity, $cacheable);
+      $values = $function($definition, $items, $cacheable);
     }
     else {
-      $values = $field_definition->getSetting('allowed_values');
+      $values = $definition->getSetting('allowed_values');
     }
 
     if ($cacheable) {
diff --git a/core/modules/options/src/Plugin/Field/FieldFormatter/OptionsDefaultFormatter.php b/core/modules/options/src/Plugin/Field/FieldFormatter/OptionsDefaultFormatter.php
index 5118e5f..d3f1f56 100644
--- a/core/modules/options/src/Plugin/Field/FieldFormatter/OptionsDefaultFormatter.php
+++ b/core/modules/options/src/Plugin/Field/FieldFormatter/OptionsDefaultFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\OptGroup;
 
 /**
  * Plugin implementation of the 'list_default' formatter.
@@ -31,16 +32,26 @@ class OptionsDefaultFormatter extends FormatterBase {
   public function viewElements(FieldItemListInterface $items) {
     $elements = array();
 
-    $entity = $items->getEntity();
-    $allowed_values = options_allowed_values($this->fieldDefinition, $entity);
-
+    $possible_options = NULL;
     foreach ($items as $delta => $item) {
-      if (isset($allowed_values[$item->value])) {
-        $output = field_filter_xss($allowed_values[$item->value]);
+      $value = $item->value;
+
+      // We assume here that the possible options do not vary by item within
+      // a multivalued list field, and optimize for performance by only
+      // invoking it once. This assumption is not guaranteed by
+      // AllowedValuesInterface, but is assumed to be true for list fields.
+      // @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::getPossibleOptions().
+      if (!isset($possible_options)) {
+        /* @var \Drupal\Core\TypedData\AllowedValuesInterface $item */
+        $possible_options = OptGroup::flattenOptions($item->getPossibleOptions());
+      }
+
+      if (isset($possible_options[$value])) {
+        $output = field_filter_xss($possible_options[$value]);
       }
       else {
-        // If no match was found in allowed values, fall back to the key.
-        $output = field_filter_xss($item->value);
+        // If no match was found in the possible options, fall back to the key.
+        $output = field_filter_xss($value);
       }
       $elements[$delta] = array('#markup' => $output);
     }
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
index d61aef1..7c77321 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
@@ -58,7 +58,7 @@ public function getSettableValues(AccountInterface $account = NULL) {
    * {@inheritdoc}
    */
   public function getSettableOptions(AccountInterface $account = NULL) {
-    $allowed_options = options_allowed_values($this->getFieldDefinition(), $this->getEntity());
+    $allowed_options = options_allowed_values($this->getFieldDefinition()->getFieldStorageDefinition(), $this->getParent());
     return $allowed_options;
   }
 
diff --git a/core/modules/options/tests/options_test.module b/core/modules/options/tests/options_test.module
index 1416fa0..231f568 100644
--- a/core/modules/options/tests/options_test.module
+++ b/core/modules/options/tests/options_test.module
@@ -5,13 +5,15 @@
  * Helper module for the List module tests.
  */
 
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 
 /**
  * Allowed values callback.
+ *
+ * @see options_allowed_values().
  */
-function options_test_allowed_values_callback(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
+function options_test_allowed_values_callback(FieldStorageDefinitionInterface $definition, FieldItemListInterface $items) {
   $values = array(
     'Group 1' => array(
       0 => 'Zero',
@@ -27,15 +29,26 @@ function options_test_allowed_values_callback(FieldDefinitionInterface $field_de
 
 /**
  * An entity-bound allowed values callback.
+ *
+ * @todo This function violates the recommendation in options_allowed_values()
+ *   to return the superset of values when $items is NULL. Since this is not
+ *   yet used for testing Views integration, that is ok. Fix this in
+ *   https://www.drupal.org/node/2012130.
+ *
+ * @see options_allowed_values().
  */
-function options_test_dynamic_values_callback(FieldDefinitionInterface $field_definition, EntityInterface $entity, &$cacheable) {
-  $cacheable = FALSE;
-  $values = array(
-    $entity->label(),
-    $entity->url(),
-    $entity->uuid(),
-    $entity->bundle(),
-  );
+function options_test_dynamic_values_callback(FieldStorageDefinitionInterface $definition, FieldItemListInterface $items, &$cacheable) {
+  $values = array();
+  if (isset($items)) {
+    $cacheable = FALSE;
+    $entity = $items->getEntity();
+    $values = array(
+      $entity->label(),
+      $entity->url(),
+      $entity->uuid(),
+      $entity->bundle(),
+    );
+  }
   // We need the values of the entity as keys.
   return array_combine($values, $values);
 }
