 core/includes/common.inc                           | 60 +++------------
 core/includes/module.inc                           |  2 +-
 core/lib/Drupal/Core/Access/AccessManager.php      |  2 +-
 core/lib/Drupal/Core/Access/AccessResult.php       | 15 +---
 .../Core/Asset/LibraryDiscoveryCollector.php       |  2 +-
 core/lib/Drupal/Core/Block/BlockBase.php           |  3 +-
 core/lib/Drupal/Core/Cache/ApcuBackend.php         | 37 ++--------
 core/lib/Drupal/Core/Cache/Cache.php               | 52 +++++++++++++
 core/lib/Drupal/Core/Cache/CacheCollector.php      |  3 +-
 core/lib/Drupal/Core/Cache/DatabaseBackend.php     | 58 ++++-----------
 core/lib/Drupal/Core/Cache/MemoryBackend.php       | 37 +---------
 core/lib/Drupal/Core/Cache/PhpBackend.php          | 37 +---------
 core/lib/Drupal/Core/Config/CachedStorage.php      |  8 +-
 .../Drupal/Core/Config/Entity/ConfigEntityBase.php |  2 +-
 .../lib/Drupal/Core/Datetime/Entity/DateFormat.php |  4 +-
 core/lib/Drupal/Core/Entity/Entity.php             |  6 +-
 core/lib/Drupal/Core/Entity/EntityManager.php      | 20 ++---
 core/lib/Drupal/Core/Entity/EntityViewBuilder.php  |  8 +-
 .../Core/Entity/Sql/SqlContentEntityStorage.php    |  6 +-
 .../Core/EventSubscriber/HtmlViewSubscriber.php    | 58 +--------------
 .../MenuRouterRebuildSubscriber.php                |  2 +-
 core/lib/Drupal/Core/Extension/ThemeHandler.php    |  2 +-
 .../lib/Drupal/Core/Menu/ContextualLinkManager.php |  2 +-
 core/lib/Drupal/Core/Menu/LocalActionManager.php   |  2 +-
 core/lib/Drupal/Core/Menu/LocalTaskManager.php     |  2 +-
 core/lib/Drupal/Core/Menu/MenuLinkTree.php         |  4 +-
 core/lib/Drupal/Core/Menu/MenuTreeStorage.php      | 16 +++-
 .../Core/Page/DefaultHtmlFragmentRenderer.php      | 11 ++-
 .../Drupal/Core/Plugin/DefaultPluginManager.php    |  1 +
 core/lib/Drupal/Core/Render/Element/Page.php       |  4 +-
 core/lib/Drupal/Core/Theme/Registry.php            |  8 +-
 core/lib/Drupal/Core/Theme/ThemeAccessCheck.php    |  2 +-
 core/lib/Drupal/Core/Utility/Token.php             |  2 +-
 .../src/Tests/AggregatorRenderingTest.php          |  8 +-
 core/modules/block/src/BlockViewBuilder.php        |  6 +-
 core/modules/block/src/Entity/Block.php            |  2 +-
 core/modules/block/src/Tests/BlockCacheTest.php    |  2 +-
 core/modules/block/src/Tests/BlockTest.php         | 29 +++++---
 .../block/src/Tests/BlockViewBuilderTest.php       | 15 ++--
 .../tests/modules/block_test/block_test.module     |  3 +-
 core/modules/book/src/BookManager.php              | 12 +--
 core/modules/breakpoint/src/BreakpointManager.php  |  6 +-
 .../comment/src/Tests/CommentCacheTagsTest.php     |  2 +-
 .../Tests/CommentDefaultFormatterCacheTagsTest.php | 22 +++---
 .../tests/src/Unit/Entity/CommentLockTest.php      |  4 +-
 .../config_translation/src/ConfigMapperManager.php |  2 +-
 .../ContentTranslationManageAccessCheckTest.php    |  2 +-
 .../src/Tests/EditorFileReferenceFilterTest.php    | 13 ++--
 .../src/Tests/EntityReferenceFormatterTest.php     | 15 ++--
 core/modules/filter/filter.module                  | 14 ++--
 core/modules/filter/src/FilterPluginManager.php    |  2 +-
 core/modules/filter/src/FilterProcessResult.php    |  3 +-
 core/modules/filter/src/Tests/FilterAPITest.php    | 10 +--
 .../src/Plugin/Filter/FilterTestCacheTags.php      |  4 +-
 core/modules/locale/locale.module                  |  2 +-
 core/modules/locale/src/LocaleLookup.php           |  2 +-
 .../menu_ui/src/Tests/MenuCacheTagsTest.php        |  8 +-
 core/modules/node/src/Tests/NodeCacheTagsTest.php  |  2 +-
 .../FieldFormatter/ResponsiveImageFormatter.php    | 10 +--
 .../rest/src/LinkManager/RelationLinkManager.php   |  2 +-
 .../rest/src/LinkManager/TypeLinkManager.php       |  2 +-
 core/modules/shortcut/shortcut.module              |  8 +-
 .../shortcut/src/Tests/ShortcutCacheTagsTest.php   |  2 +-
 core/modules/system/core.api.php                   |  4 +-
 core/modules/system/src/Form/ThemeSettingsForm.php |  4 +-
 .../src/Plugin/Block/SystemBrandingBlock.php       |  6 +-
 .../system/src/Plugin/Block/SystemMenuBlock.php    |  3 +-
 .../system/src/Tests/Bootstrap/PageCacheTest.php   | 10 +--
 .../src/Tests/Cache/DatabaseBackendTagTest.php     | 10 +--
 .../Cache/GenericCacheBackendUnitTestBase.php      | 50 ++++++-------
 .../Tests/Cache/PageCacheTagsIntegrationTest.php   | 16 ++--
 .../modules/system/src/Tests/Common/RenderTest.php | 31 ++++----
 .../src/Tests/Entity/EntityCacheTagsTestBase.php   | 31 ++++----
 .../Entity/EntityWithUriCacheTagsTestBase.php      | 14 ++--
 .../system/src/Tests/Theme/RegistryTest.php        |  4 +-
 .../src/Controller/SystemTestController.php        |  4 +-
 .../text/src/Tests/Formatter/TextFormatterTest.php |  6 +-
 .../toolbar/src/Tests/ToolbarAdminMenuTest.php     |  2 +-
 core/modules/toolbar/toolbar.module                |  2 +-
 core/modules/tour/src/Tests/TourCacheTagsTest.php  |  8 +-
 core/modules/user/src/Entity/Role.php              |  1 +
 core/modules/user/src/PermissionsHash.php          |  6 +-
 .../views/src/Plugin/ViewsHandlerManager.php       |  2 +-
 .../views/src/Plugin/ViewsPluginManager.php        |  2 +-
 .../src/Plugin/views/cache/CachePluginBase.php     |  6 +-
 .../src/Plugin/views/display/DisplayPluginBase.php |  2 +-
 core/modules/views/src/ViewsData.php               |  4 +-
 core/modules/views/views.api.php                   |  2 +-
 .../Drupal/Tests/Core/Access/AccessManagerTest.php |  2 +-
 .../Drupal/Tests/Core/Access/AccessResultTest.php  | 51 ++++++-------
 .../Core/Asset/LibraryDiscoveryCollectorTest.php   |  2 +-
 .../Cache/BackendChainImplementationUnitTest.php   | 20 ++---
 .../Drupal/Tests/Core/Cache/CacheCollectorTest.php |  2 +-
 core/tests/Drupal/Tests/Core/Cache/CacheTest.php   | 85 ++++++++++++++++++++++
 .../Config/Entity/ConfigEntityBaseUnitTest.php     |  2 +-
 .../Core/Config/Entity/ConfigEntityStorageTest.php | 17 +++--
 .../Drupal/Tests/Core/Entity/EntityManagerTest.php | 16 ++--
 .../Drupal/Tests/Core/Entity/EntityUnitTest.php    | 10 +--
 .../Entity/Sql/SqlContentEntityStorageTest.php     |  2 +-
 .../Tests/Core/Menu/LocalActionManagerTest.php     |  2 +-
 .../Tests/Core/Menu/LocalTaskIntegrationTest.php   |  2 +-
 .../Tests/Core/Menu/LocalTaskManagerTest.php       |  4 +-
 .../Tests/Core/Plugin/DefaultPluginManagerTest.php |  4 +-
 103 files changed, 532 insertions(+), 611 deletions(-)

diff --git a/core/includes/common.inc b/core/includes/common.inc
index 3d245a0..24ac99a 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1783,41 +1783,6 @@ function drupal_merge_attached(array $a, array $b) {
 }
 
 /**
- * Merges sets of cache tags.
- *
- * The cache tags array is returned in a format that is valid for
- * \Drupal\Core\Cache\CacheBackendInterface::set().
- *
- * When caching elements, it is necessary to collect all cache tags into a
- * single array, from both the element itself and all child elements. This
- * allows items to be invalidated based on all tags attached to the content
- * they're constituted from.
- *
- * @param array $tags
- *   The first set of cache tags.
- * @param array $other
- *   The other set of cache tags.
- *
- * @return array
- *   The merged set of cache tags.
- */
-function drupal_merge_cache_tags(array $tags, array $other) {
-  foreach ($other as $namespace => $values) {
-    if (is_array($values)) {
-      foreach ($values as $value) {
-        $tags[$namespace][$value] = $value;
-      }
-    }
-    else {
-      if (!isset($tags[$namespace])) {
-        $tags[$namespace] = $values;
-      }
-    }
-  }
-  return $tags;
-}
-
-/**
  * Adds attachments to a render() structure.
  *
  * Libraries, JavaScript, CSS and other types of custom structures are attached
@@ -2317,7 +2282,7 @@ function drupal_page_set_cache(Response $response, Request $request) {
   $expire = ($date > (new DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
 
   $cid = drupal_page_cache_get_cid($request);
-  $tags = HtmlViewSubscriber::convertHeaderToCacheTags($response->headers->get('X-Drupal-Cache-Tags'));
+  $tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
   \Drupal::cache('render')->set($cid, $response, $expire, $tags);
 }
 
@@ -2508,11 +2473,11 @@ function drupal_prepare_page($page) {
   // @todo Remove this once https://drupal.org/node/1869476 lands.
   if (theme_get_setting('features.main_menu') && count(menu_main_menu())) {
     $main_links_source = _menu_get_links_source('main_links', 'main');
-    $page['page_top']['#cache']['tags']['menu'][$main_links_source] = $main_links_source;
+    $page['page_top']['#cache']['tags'][] = 'menu:' . $main_links_source;
   }
   if (theme_get_setting('features.secondary_menu') && count(menu_secondary_menu())) {
     $secondary_links_source = _menu_get_links_source('secondary_links', 'account');
-    $page['page_top']['#cache']['tags']['menu'][$secondary_links_source] = $secondary_links_source;
+    $page['page_top']['#cache']['tags'][] = 'menu:' . $secondary_links_source;
   }
 
   // If no module has taken care of the main content, add it to the page now.
@@ -2724,7 +2689,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     $frame = $stack->top();
     // Update the frame, but also update the current element, to ensure it
     // contains up-to-date information in case it gets render cached.
-    $frame->tags = $element['#cache']['tags'] = drupal_merge_cache_tags($element['#cache']['tags'], $frame->tags);
+    $frame->tags = $element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $frame->tags);
     $frame->attached = $element['#attached'] = drupal_merge_attached($element['#attached'], $frame->attached);
     $frame->postRenderCache = $element['#post_render_cache'] = NestedArray::mergeDeep($element['#post_render_cache'], $frame->postRenderCache);
   };
@@ -2740,7 +2705,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     // Merge the current and the parent stack frame.
     $current = $stack->pop();
     $parent = $stack->pop();
-    $current->tags = drupal_merge_cache_tags($current->tags, $parent->tags);
+    $current->tags = Cache::mergeTags($current->tags, $parent->tags);
     $current->attached = drupal_merge_attached($current->attached, $parent->attached);
     $current->postRenderCache = NestedArray::mergeDeep($current->postRenderCache, $parent->postRenderCache);
     $stack->push($current);
@@ -2957,7 +2922,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     $stack->push(new RenderStackFrame());
     _drupal_render_process_post_render_cache($elements);
     $post_render_additions = $stack->pop();
-    $elements['#cache']['tags'] = drupal_merge_cache_tags($elements['#cache']['tags'], $post_render_additions->tags);
+    $elements['#cache']['tags'] = Cache::mergeTags($elements['#cache']['tags'], $post_render_additions->tags);
     $elements['#attached'] = drupal_merge_attached($elements['#attached'], $post_render_additions->attached);
     $elements['#post_render_cache'] = NestedArray::mergeDeep($elements['#post_render_cache'], $post_render_additions->postRenderCache);
   }
@@ -3150,17 +3115,14 @@ function drupal_render_cache_set(&$markup, array $elements) {
     $data['#post_render_cache'] = $elements['#post_render_cache'];
   }
 
-  // Tag every render cache item with the "rendered" cache tag. This allows us
-  // to invalidate the entire render cache, regardless of the cache bin.
-  $cache_tags = $elements['#cache']['tags'] ?: array();
-  $cache_tags['rendered'] = TRUE;
-
-  // Persist cache tags associated with this element.
-  $data['#cache']['tags'] = $cache_tags;
+  // Persist cache tags associated with this element. Also associate the
+  // "rendered" cache tag. This allows us to invalidate the entire render cache,
+  // regardless of the cache bin.
+  $data['#cache']['tags'] = Cache::mergeTags($elements['#cache']['tags'], ['rendered']);
 
   $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
   $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
-  \Drupal::cache($bin)->set($cid, $data, $expire, $cache_tags);
+  \Drupal::cache($bin)->set($cid, $data, $expire, $data['#cache']['tags']);
 }
 
 /**
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 023c102..ca755b9 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -69,7 +69,7 @@ function system_list_reset() {
   // @todo Trigger an event upon module install/uninstall and theme
   //   enable/disable, and move this into an event subscriber.
   // @see https://drupal.org/node/2206347
-  Cache::invalidateTags(array('extension' => TRUE));
+  Cache::invalidateTags(array('extension'));
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php
index 625d891..f051300 100644
--- a/core/lib/Drupal/Core/Access/AccessManager.php
+++ b/core/lib/Drupal/Core/Access/AccessManager.php
@@ -194,7 +194,7 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun
     }
     catch (RouteNotFoundException $e) {
       // Cacheable until extensions change.
-      $result = AccessResult::forbidden()->addCacheTags(array('extension' => TRUE));
+      $result = AccessResult::forbidden()->addCacheTags(array('extension'));
       return $return_as_object ? $result : $result->isAllowed();
     }
     catch (ParamNotConvertedException $e) {
diff --git a/core/lib/Drupal/Core/Access/AccessResult.php b/core/lib/Drupal/Core/Access/AccessResult.php
index c87b80f..285cc7b 100644
--- a/core/lib/Drupal/Core/Access/AccessResult.php
+++ b/core/lib/Drupal/Core/Access/AccessResult.php
@@ -318,20 +318,7 @@ public function resetCacheContexts() {
    * @return $this
    */
   public function addCacheTags(array $tags) {
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $this->tags[$namespace][$value] = $value;
-        }
-        ksort($this->tags[$namespace]);
-      }
-      else {
-        if (!isset($this->tags[$namespace])) {
-          $this->tags[$namespace] = $values;
-        }
-      }
-    }
-    ksort($this->tags);
+    $this->tags = Cache::mergeTags($this->tags, $tags);
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php
index e174a17..dfac4fa 100644
--- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php
+++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php
@@ -57,7 +57,7 @@ class LibraryDiscoveryCollector extends CacheCollector {
    *   The library discovery parser.
    */
   public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser) {
-    parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey => array(TRUE)));
+    parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey));
 
     $this->discoveryParser = $discovery_parser;
   }
diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index 6266502..5642fff 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -487,8 +487,7 @@ public function getCacheTags() {
     // If a block plugin's output changes, then it must be able to invalidate a
     // cache tag that affects all instances of this block: across themes and
     // across regions.
-    $block_plugin_cache_tag = str_replace(':', '__', $this->getPluginID());
-    return array('block_plugin' => array($block_plugin_cache_tag));
+    return array('block_plugin:' . str_replace(':', '__', $this->getPluginID()));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php
index 0ec166f..f787401 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackend.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php
@@ -189,11 +189,12 @@ protected function prepareItem($cache, $allow_invalid) {
    * {@inheritdoc}
    */
   public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
+    Cache::validateTags($tags);
     $cache = new \stdClass();
     $cache->cid = $cid;
     $cache->created = round(microtime(TRUE), 3);
     $cache->expire = $expire;
-    $cache->tags = implode(' ', $this->flattenTags($tags));
+    $cache->tags = implode(' ', $tags);
     $checksum = $this->checksumTags($tags);
     $cache->checksum_invalidations = $checksum['invalidations'];
     $cache->checksum_deletions = $checksum['deletions'];
@@ -285,7 +286,7 @@ public function invalidateAll() {
    * {@inheritdoc}
    */
   public function deleteTags(array $tags) {
-    foreach ($this->flattenTags($tags) as $tag) {
+    foreach ($tags as $tag) {
       apc_inc($this->deletionsTagsPrefix . $tag, 1, $success);
       if (!$success) {
         apc_store($this->deletionsTagsPrefix . $tag, 1);
@@ -297,7 +298,7 @@ public function deleteTags(array $tags) {
    * {@inheritdoc}
    */
   public function invalidateTags(array $tags) {
-    foreach ($this->flattenTags($tags) as $tag) {
+    foreach ($tags as $tag) {
       apc_inc($this->invalidationsTagsPrefix . $tag, 1, $success);
       if (!$success) {
         apc_store($this->invalidationsTagsPrefix . $tag, 1);
@@ -306,34 +307,6 @@ public function invalidateTags(array $tags) {
   }
 
   /**
-   * Flattens a tags array into a numeric array suitable for string storage.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   Indexed array of flattened tag identifiers.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * Returns the sum total of validations for a given set of tags.
    *
    * @param array $tags
@@ -346,7 +319,7 @@ protected function checksumTags(array $tags) {
     $checksum = array('invalidations' => 0, 'deletions' => 0);
     $query_tags = array('invalidations' => array(), 'deletions' => array());
 
-    foreach ($this->flattenTags($tags) as $tag) {
+    foreach ($tags as $tag) {
       foreach (array('deletions', 'invalidations') as $type) {
         if (isset(static::$tagCache[$type][$tag])) {
           $checksum[$type] += static::$tagCache[$type][$tag];
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index ad59409..68d316c 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -22,6 +22,56 @@ class Cache {
   const PERMANENT = CacheBackendInterface::CACHE_PERMANENT;
 
   /**
+   * Merges arrays of cache tags.
+   *
+   * The cache tags array is returned in a format that is valid for
+   * \Drupal\Core\Cache\CacheBackendInterface::set().
+   *
+   * When caching elements, it is necessary to collect all cache tags into a
+   * single array, from both the element itself and all child elements. This
+   * allows items to be invalidated based on all tags attached to the content
+   * they're constituted from.
+   *
+   * @param array …
+   *   Arrays of cache tags to merge.
+   *
+   * @return array
+   *   The merged array of cache tags.
+   */
+  public static function mergeTags() {
+    $cache_tag_arrays = func_get_args();
+    $cache_tags = [];
+    foreach ($cache_tag_arrays as $tags) {
+      static::validateTags($tags);
+      $cache_tags = array_merge($cache_tags, $tags);
+    }
+    $cache_tags = array_unique($cache_tags);
+    sort($cache_tags);
+    return $cache_tags;
+  }
+
+  /**
+   * Validates an array of cache tags.
+   *
+   * Can be called before using cache tags in operations, to ensure validity.
+   *
+   * @param array $tags
+   *   An array of cache tags.
+   *
+   * @throws \LogicException
+   */
+  public static function validateTags(array $tags) {
+    if (empty($tags)) {
+      return;
+    }
+    foreach ($tags as $value) {
+      if (!is_string($value)) {
+        throw new \LogicException('Cache tags must be strings, ' . gettype($value) . ' given.');
+      }
+    }
+  }
+
+  /**
    * Deletes items from all bins with any of the specified tags.
    *
    * Many sites have more than one active cache backend, and each backend may
@@ -35,6 +85,7 @@ class Cache {
    *   The list of tags to delete cache items for.
    */
   public static function deleteTags(array $tags) {
+    static::validateTags($tags);
     foreach (static::getBins() as $cache_backend) {
       $cache_backend->deleteTags($tags);
     }
@@ -54,6 +105,7 @@ public static function deleteTags(array $tags) {
    *   The list of tags to invalidate cache items for.
    */
   public static function invalidateTags(array $tags) {
+    static::validateTags($tags);
     foreach (static::getBins() as $cache_backend) {
       $cache_backend->invalidateTags($tags);
     }
diff --git a/core/lib/Drupal/Core/Cache/CacheCollector.php b/core/lib/Drupal/Core/Cache/CacheCollector.php
index b90327e..d1689af 100644
--- a/core/lib/Drupal/Core/Cache/CacheCollector.php
+++ b/core/lib/Drupal/Core/Cache/CacheCollector.php
@@ -114,7 +114,8 @@
    * @param array $tags
    *   (optional) The tags to specify for the cache item.
    */
-  public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = array()) {
+  public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) {
+    Cache::validateTags($tags);
     $this->cid = $cid;
     $this->cache = $cache;
     $this->tags = $tags;
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 9386610..b243967 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -147,6 +147,7 @@ protected function prepareItem($cache, $allow_invalid) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::set().
    */
   public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
+    Cache::validateTags($tags);
     $try_again = FALSE;
     try {
       // The bin might not yet exist.
@@ -170,13 +171,12 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array
    * Actually set the cache.
    */
   protected function doSet($cid, $data, $expire, $tags) {
-    $flat_tags = $this->flattenTags($tags);
     $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
     $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
     // Remove tags that were already deleted or invalidated during this request
     // from the static caches so that another deletion or invalidation can
     // occur.
-    foreach ($flat_tags as $tag) {
+    foreach ($tags as $tag) {
       if (isset($deleted_tags[$tag])) {
         unset($deleted_tags[$tag]);
       }
@@ -184,12 +184,12 @@ protected function doSet($cid, $data, $expire, $tags) {
         unset($invalidated_tags[$tag]);
       }
     }
-    $checksum = $this->checksumTags($flat_tags);
+    $checksum = $this->checksumTags($tags);
     $fields = array(
       'serialized' => 0,
       'created' => round(microtime(TRUE), 3),
       'expire' => $expire,
-      'tags' => implode(' ', $flat_tags),
+      'tags' => implode(' ', $tags),
       'checksum_invalidations' => $checksum['invalidations'],
       'checksum_deletions' => $checksum['deletions'],
     );
@@ -234,12 +234,12 @@ public function setMultiple(array $items) {
           'tags' => array(),
         );
 
-        $flat_tags = $this->flattenTags($item['tags']);
+        Cache::validateTags($item['tags']);
 
         // Remove tags that were already deleted or invalidated during this
         // request from the static caches so that another deletion or
         // invalidation can occur.
-        foreach ($flat_tags as $tag) {
+        foreach ($item['tags'] as $tag) {
           if (isset($deleted_tags[$tag])) {
             unset($deleted_tags[$tag]);
           }
@@ -248,13 +248,13 @@ public function setMultiple(array $items) {
           }
         }
 
-        $checksum = $this->checksumTags($flat_tags);
+        $checksum = $this->checksumTags($item['tags']);
 
         $fields = array(
           'cid' => $cid,
           'expire' => $item['expire'],
           'created' => round(microtime(TRUE), 3),
-          'tags' => implode(' ', $flat_tags),
+          'tags' => implode(' ', $item['tags']),
           'checksum_invalidations' => $checksum['invalidations'],
           'checksum_deletions' => $checksum['deletions'],
         );
@@ -316,7 +316,7 @@ public function deleteMultiple(array $cids) {
   public function deleteTags(array $tags) {
     $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
     $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
-    foreach ($this->flattenTags($tags) as $tag) {
+    foreach ($tags as $tag) {
       // Only delete tags once per request unless they are written again.
       if (isset($deleted_tags[$tag])) {
         continue;
@@ -386,7 +386,7 @@ public function invalidateTags(array $tags) {
     try {
       $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
       $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
-      foreach ($this->flattenTags($tags) as $tag) {
+      foreach ($tags as $tag) {
         // Only invalidate tags once per request unless they are written again.
         if (isset($invalidated_tags[$tag])) {
           continue;
@@ -437,45 +437,15 @@ public function garbageCollection() {
   }
 
   /**
-   * 'Flattens' a tags array into an array of strings.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   An indexed array of flattened tag identifiers.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * Returns the sum total of validations for a given set of tags.
    *
    * @param array $tags
-   *   Array of flat tags.
+   *   Array of cache tags.
    *
    * @return int
    *   Sum of all invalidations.
-   *
-   * @see \Drupal\Core\Cache\DatabaseBackend::flattenTags()
    */
-  protected function checksumTags($flat_tags) {
+  protected function checksumTags(array $tags) {
     $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
 
     $checksum = array(
@@ -483,7 +453,7 @@ protected function checksumTags($flat_tags) {
       'deletions' => 0,
     );
 
-    $query_tags = array_diff($flat_tags, array_keys($tag_cache));
+    $query_tags = array_diff($tags, array_keys($tag_cache));
     if ($query_tags) {
       $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cachetags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC);
       $tag_cache += $db_tags;
@@ -492,7 +462,7 @@ protected function checksumTags($flat_tags) {
       $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum);
     }
 
-    foreach ($flat_tags as $tag) {
+    foreach ($tags as $tag) {
       $checksum['invalidations'] += $tag_cache[$tag]['invalidations'];
       $checksum['deletions'] += $tag_cache[$tag]['deletions'];
     }
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index ce8e199..a8814ac 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -107,12 +107,13 @@ protected function prepareItem($cache, $allow_invalid) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::set().
    */
   public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
+    Cache::validateTags($tags);
     $this->cache[$cid] = (object) array(
       'cid' => $cid,
       'data' => serialize($data),
       'created' => REQUEST_TIME,
       'expire' => $expire,
-      'tags' => $this->flattenTags($tags),
+      'tags' => $tags,
     );
   }
 
@@ -143,9 +144,8 @@ public function deleteMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
    */
   public function deleteTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
     foreach ($this->cache as $cid => $item) {
-      if (array_intersect($flat_tags, $item->tags)) {
+      if (array_intersect($tags, $item->tags)) {
         unset($this->cache[$cid]);
       }
     }
@@ -180,9 +180,8 @@ public function invalidateMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
    */
   public function invalidateTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
     foreach ($this->cache as $cid => $item) {
-      if (array_intersect($flat_tags, $item->tags)) {
+      if (array_intersect($tags, $item->tags)) {
         $this->cache[$cid]->expire = REQUEST_TIME - 1;
       }
     }
@@ -198,34 +197,6 @@ public function invalidateAll() {
   }
 
   /**
-   * 'Flattens' a tags array into an array of strings.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   An indexed array of strings.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection()
    */
   public function garbageCollection() {
diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php
index 8613859..26eaf7e 100644
--- a/core/lib/Drupal/Core/Cache/PhpBackend.php
+++ b/core/lib/Drupal/Core/Cache/PhpBackend.php
@@ -138,8 +138,9 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array
       'data' => $data,
       'created' => round(microtime(TRUE), 3),
       'expire' => $expire,
+      'tags' => $tags,
     );
-    $item->tags = $this->flattenTags($tags);
+    Cache::validateTags($item->tags);
     $this->writeItem($this->normalizeCid($cid), $item);
   }
 
@@ -163,10 +164,9 @@ public function deleteMultiple(array $cids) {
    * {@inheritdoc}
    */
   public function deleteTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
     foreach ($this->storage()->listAll() as $cidhash) {
       $item = $this->getByHash($cidhash);
-      if (is_object($item) && array_intersect($flat_tags, $item->tags)) {
+      if (is_object($item) && array_intersect($tags, $item->tags)) {
         $this->delete($item->cid);
       }
     }
@@ -212,10 +212,9 @@ public function invalidateMultiple(array $cids) {
    * {@inheritdoc}
    */
   public function invalidateTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
     foreach ($this->storage()->listAll() as $cidhash) {
       $item = $this->getByHash($cidhash);
-      if ($item && array_intersect($flat_tags, $item->tags)) {
+      if ($item && array_intersect($tags, $item->tags)) {
         $this->invalidate($item->cid);
       }
     }
@@ -231,34 +230,6 @@ public function invalidateAll() {
   }
 
   /**
-   * 'Flattens' a tags array into an array of strings.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   An indexed array of strings.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function garbageCollection() {
diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index 0b089ca..b657f66 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -131,7 +131,7 @@ public function write($name, array $data) {
       // While not all written data is read back, setting the cache instead of
       // just deleting it avoids cache rebuild stampedes.
       $this->cache->set($this->getCacheKey($name), $data);
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
+      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
@@ -146,7 +146,7 @@ public function delete($name) {
     // rebuilding the cache before the storage is gone.
     if ($this->storage->delete($name)) {
       $this->cache->delete($this->getCacheKey($name));
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
+      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
@@ -162,7 +162,7 @@ public function rename($name, $new_name) {
     if ($this->storage->rename($name, $new_name)) {
       $this->cache->delete($this->getCacheKey($name));
       $this->cache->delete($this->getCacheKey($new_name));
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
+      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
@@ -224,7 +224,7 @@ protected function findByPrefix($prefix) {
           'find:' . $cache_key,
           $this->findByPrefixCache[$cache_key],
           Cache::PERMANENT,
-          array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)
+          array($this::FIND_BY_PREFIX_CACHE_TAG)
         );
       }
     }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index c329032..3dfe714 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -163,7 +163,7 @@ public function enable() {
    */
   public function disable() {
     // An entity was disabled, invalidate its own cache tag.
-    Cache::invalidateTags(array($this->entityTypeId => array($this->id())));
+    Cache::invalidateTags($this->getCacheTag());
     return $this->setStatus(FALSE);
   }
 
diff --git a/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php b/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php
index 2cc01e8..bb4efe0 100644
--- a/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php
+++ b/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php
@@ -95,14 +95,14 @@ public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b)
    * {@inheritdoc}
    */
   public function getCacheTag() {
-    return array('rendered' => TRUE);
+    return ['rendered'];
   }
 
   /**
    * {@inheritdoc}
    */
   public function getListCacheTags() {
-    return array('rendered' => TRUE);
+    return ['rendered'];
   }
 
 }
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 9424994..a2cf4fa 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -404,7 +404,7 @@ public function referencedEntities() {
    * {@inheritdoc}
    */
   public function getCacheTag() {
-    return array($this->entityTypeId => array($this->id()));
+    return [$this->entityTypeId . ':' . $this->id()];
   }
 
   /**
@@ -412,7 +412,7 @@ public function getCacheTag() {
    */
   public function getListCacheTags() {
     // @todo Add bundle-specific listing cache tag? https://drupal.org/node/2145751
-    return array($this->entityTypeId . 's' => TRUE);
+    return [$this->entityTypeId . 's'];
   }
 
   /**
@@ -493,7 +493,7 @@ protected static function invalidateTagsOnDelete(array $entities) {
       // other pages than the one it's on. The one it's on is handled by its own
       // cache tag, but subsequent list pages would not be invalidated, hence we
       // must invalidate its list cache tags as well.)
-      $tags = NestedArray::mergeDeepArray(array($tags, $entity->getCacheTag(), $entity->getListCacheTags()));
+      $tags = Cache::mergeTags($tags, $entity->getCacheTag(), $entity->getListCacheTags());
       $entity->onSaveOrDelete();
     }
     Cache::invalidateTags($tags);
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index a2aedf3..021ca89 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -187,7 +187,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
   public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueStoreInterface $installed_definitions) {
     parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface', 'Drupal\Core\Entity\Annotation\EntityType');
 
-    $this->setCacheBackend($cache, 'entity_type', array('entity_types' => TRUE));
+    $this->setCacheBackend($cache, 'entity_type', array('entity_types'));
     $this->alterInfo('entity_type');
 
     $this->languageManager = $language_manager;
@@ -342,7 +342,7 @@ public function getBaseFieldDefinitions($entity_type_id) {
       else {
         // Rebuild the definitions and put it into the cache.
         $this->baseFieldDefinitions[$entity_type_id] = $this->buildBaseFieldDefinitions($entity_type_id);
-        $this->cacheBackend->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types', 'entity_field_info'));
        }
      }
     return $this->baseFieldDefinitions[$entity_type_id];
@@ -440,7 +440,7 @@ public function getFieldDefinitions($entity_type_id, $bundle) {
       else {
         // Rebuild the definitions and put it into the cache.
         $bundle_field_definitions = $this->buildBundleFieldDefinitions($entity_type_id, $bundle, $base_field_definitions);
-        $this->cacheBackend->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
       }
       // Field definitions consist of the bundle specific overrides and the
       // base fields, merge them together. Use array_replace() to replace base
@@ -548,7 +548,7 @@ public function getFieldStorageDefinitions($entity_type_id) {
       else {
         // Rebuild the definitions and put it into the cache.
         $field_storage_definitions = $this->buildFieldStorageDefinitions($entity_type_id);
-        $this->cacheBackend->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
       }
       $this->fieldStorageDefinitions[$entity_type_id] += $field_storage_definitions;
     }
@@ -578,7 +578,7 @@ public function getFieldMap() {
           }
         }
 
-        $this->cacheBackend->set($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
       }
     }
     return $this->fieldMap;
@@ -651,7 +651,7 @@ public function clearCachedFieldDefinitions() {
     $this->fieldMapByFieldType = array();
     $this->displayModeInfo = array();
     $this->extraFields = array();
-    Cache::deleteTags(array('entity_field_info' => TRUE));
+    Cache::deleteTags(array('entity_field_info'));
     // The typed data manager statically caches prototype objects with injected
     // definitions, clear those as well.
     $this->typedDataManager->clearCachedDefinitions();
@@ -662,7 +662,7 @@ public function clearCachedFieldDefinitions() {
    */
   public function clearCachedBundles() {
     $this->bundleInfo = array();
-    Cache::deleteTags(array('entity_bundles' => TRUE));
+    Cache::deleteTags(array('entity_bundles'));
     // Entity bundles are exposed as data types, clear that cache too.
     $this->typedDataManager->clearCachedDefinitions();
   }
@@ -702,7 +702,7 @@ public function getAllBundleInfo() {
           }
         }
         $this->moduleHandler->alter('entity_bundle_info', $this->bundleInfo);
-        $this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_bundles' => TRUE));
+        $this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types', 'entity_bundles'));
       }
     }
 
@@ -739,7 +739,7 @@ public function getExtraFields($entity_type_id, $bundle) {
     // Store in the 'static' and persistent caches.
     $this->extraFields[$entity_type_id][$bundle] = $info;
     $this->cacheBackend->set($cache_id, $info, Cache::PERMANENT, array(
-      'entity_field_info' => TRUE,
+      'entity_field_info',
     ));
 
     return $this->extraFields[$entity_type_id][$bundle];
@@ -860,7 +860,7 @@ protected function getAllDisplayModesByEntityType($display_type) {
           $this->displayModeInfo[$display_type][$display_mode_entity_type][$display_mode_name] = $display_mode->toArray();
         }
         $this->moduleHandler->alter($key, $this->displayModeInfo[$display_type]);
-        $this->cacheBackend->set("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types', 'entity_field_info'));
       }
     }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index cd27e7d..e989182 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -160,7 +160,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       '#langcode' => $langcode,
       // Collect cache defaults for this entity.
       '#cache' => array(
-        'tags' => NestedArray::mergeDeep($this->getCacheTag(), $entity->getCacheTag()),
+        'tags' => Cache::mergeTags($this->getCacheTag(), $entity->getCacheTag()),
       ),
     );
 
@@ -343,7 +343,7 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView
    * {@inheritdoc}
    */
   public function getCacheTag() {
-    return array($this->entityTypeId . '_view' => TRUE);
+    return array($this->entityTypeId . '_view');
   }
 
   /**
@@ -352,9 +352,9 @@ public function getCacheTag() {
   public function resetCache(array $entities = NULL) {
     if (isset($entities)) {
       // Always invalidate the ENTITY_TYPE_list tag.
-      $tags = array($this->entityTypeId . '_list' => TRUE);
+      $tags = array($this->entityTypeId . '_list');
       foreach ($entities as $entity) {
-        $tags = NestedArray::mergeDeep($tags, $entity->getCacheTag());
+        $tags = Cache::mergeTags($tags, $entity->getCacheTag());
       }
       Cache::invalidateTags($tags);
     }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index a46df01..1f75d40 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -436,8 +436,8 @@ protected function setPersistentCache($entities) {
     }
 
     $cache_tags = array(
-      $this->entityTypeId . '_values' => TRUE,
-      'entity_field_info' => TRUE,
+      $this->entityTypeId . '_values',
+      'entity_field_info',
     );
     foreach ($entities as $id => $entity) {
       $this->cacheBackend->set($this->buildCacheId($id), $entity, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
@@ -482,7 +482,7 @@ public function resetCache(array $ids = NULL) {
     else {
       $this->entities = array();
       if ($this->entityType->isPersistentlyCacheable()) {
-        $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values' => TRUE));
+        $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values'));
       }
     }
   }
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
index 6a854d6..2151638 100644
--- a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
@@ -77,7 +77,7 @@ public function onHtmlPage(GetResponseForControllerResultEvent $event) {
       // recommended.
       $response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode());
       if ($tags = $page->getCacheTags()) {
-        $response->headers->set('X-Drupal-Cache-Tags', static::convertCacheTagsToHeader($tags));
+        $response->headers->set('X-Drupal-Cache-Tags', implode(' ', $tags));
       }
       if ($keys = $page->getCacheKeys()) {
         $response->headers->set('cache_keys', serialize($keys));
@@ -110,60 +110,4 @@ static function getSubscribedEvents() {
     return $events;
   }
 
-  /**
-   * Converts a cache tags array into a X-Drupal-Cache-Tags header value.
-   *
-   * @param array $tags
-   *   Associative array of cache tags to flatten.
-   *
-   * @return string
-   *   A space-separated list of flattened cache tag identifiers.
-   */
-  public static function convertCacheTagsToHeader(array $tags) {
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return implode(' ', $flat_tags);
-  }
-
-  /**
-   * Converts a X-Drupal-Cache-Tags header value into a cache tags array.
-   *
-   * @param string $tags_header
-   *   A space-separated list of flattened cache tag identifiers.
-   *
-   * @return array
-   *   Associative array of cache tags to flatten.
-   */
-  public static function convertHeaderToCacheTags($tags_header) {
-    if (!is_string($tags_header) || strlen(trim($tags_header)) == 0) {
-      return array();
-    }
-
-    $flat_tags = explode(' ', trim($tags_header));
-    $tags = array();
-    foreach ($flat_tags as $flat_tag) {
-      list($namespace, $value) = explode(':', $flat_tag);
-      if (!isset($tags[$namespace])) {
-        $tags[$namespace] = $value;
-      }
-      // Multiple values in this namespace.
-      else {
-        if (!is_array($tags[$namespace])) {
-          $tags[$namespace] = array($tags[$namespace]);
-        }
-        $tags[$namespace][] = $value;
-      }
-    }
-    return $tags;
-  }
-
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
index 4b0b41f..41b5a87 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
@@ -57,7 +57,7 @@ public function __construct(LockBackendInterface $lock, MenuLinkManagerInterface
    */
   public function onRouterRebuild(Event $event) {
     $this->menuLinksRebuild();
-    Cache::deleteTags(array('local_task' => 1));
+    Cache::deleteTags(array('local_task'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index a4a086a..0425df5 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -643,7 +643,7 @@ protected function resetSystem() {
 
     // @todo It feels wrong to have the requirement to clear the local tasks
     //   cache here.
-    Cache::deleteTags(array('local_task' => 1));
+    Cache::deleteTags(array('local_task'));
     $this->themeRegistryRebuild();
   }
 
diff --git a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
index b99c11f..140a69a 100644
--- a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
+++ b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
@@ -113,7 +113,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Mo
     $this->moduleHandler = $module_handler;
     $this->requestStack = $request_stack;
     $this->alterInfo('contextual_links_plugins');
-    $this->setCacheBackend($cache_backend, 'contextual_links_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('contextual_links_plugins' => TRUE));
+    $this->setCacheBackend($cache_backend, 'contextual_links_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('contextual_links_plugins'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php
index 1033d79..2e4eb92 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php
@@ -124,7 +124,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re
     $this->moduleHandler = $module_handler;
     $this->account = $account;
     $this->alterInfo('menu_local_actions');
-    $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_action' => TRUE));
+    $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_action'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index 862050c..4e1f675 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -135,7 +135,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re
     $this->account = $account;
     $this->moduleHandler = $module_handler;
     $this->alterInfo('local_tasks');
-    $this->setCacheBackend($cache, 'local_task_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_task' => TRUE));
+    $this->setCacheBackend($cache, 'local_task_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_task'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTree.php b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
index 25dede7..be55a81 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkTree.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
@@ -124,7 +124,7 @@ public function getCurrentRouteMenuTreeParameters($menu_name) {
           // expanded.
           ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail));
 
-        $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu' => $menu_name));
+        $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu:' . $menu_name));
       }
       $this->cachedCurrentRouteParameters[$menu_name] = $parameters;
     }
@@ -246,7 +246,7 @@ public function build(array $tree) {
       // Allow menu-specific theme overrides.
       $build['#theme_wrappers'][] = 'menu_tree__' . strtr($menu_name, '-', '_');
       // Set cache tag.
-      $build['#cache']['tags']['menu'][$menu_name] = $menu_name;
+      $build['#cache']['tags'][] = 'menu:' . $menu_name;
     }
 
     return $build;
diff --git a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
index 92775b4..b7e525f 100644
--- a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
+++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
@@ -193,7 +193,11 @@ public function rebuild(array $definitions) {
     $this->resetDefinitions();
     $affected_menus = $this->getMenuNames() + $before_menus;
     // Invalidate any cache tagged with any menu name.
-    Cache::invalidateTags(array('menu' => $affected_menus));
+    $cache_tags = [];
+    foreach ($affected_menus as $affected_menu) {
+      $cache_tags[] = 'menu:' . $affected_menu;
+    }
+    Cache::invalidateTags($cache_tags);
     $this->resetDefinitions();
     // Every item in the cache bin should have one of the menu cache tags but it
     // is not guaranteed, so invalidate everything in the bin.
@@ -255,7 +259,11 @@ protected function safeExecuteSelect(SelectInterface $query) {
   public function save(array $link) {
     $affected_menus = $this->doSave($link);
     $this->resetDefinitions();
-    Cache::invalidateTags(array('menu' => $affected_menus));
+    $cache_tags = [];
+    foreach ($affected_menus as $affected_menu) {
+      $cache_tags[] = 'menu:' . $affected_menu;
+    }
+    Cache::invalidateTags($cache_tags);
     return $affected_menus;
   }
 
@@ -436,7 +444,7 @@ public function delete($id) {
       $this->updateParentalStatus($item);
       // Many children may have moved.
       $this->resetDefinitions();
-      Cache::invalidateTags(array('menu' => $item['menu_name']));
+      Cache::invalidateTags(array('menu:' . $item['menu_name']));
     }
   }
 
@@ -837,7 +845,7 @@ public function loadTreeData($menu_name, MenuTreeParameters $parameters) {
       $data['tree'] = $this->doBuildTreeData($links, $parameters->activeTrail, $parameters->minDepth);
       $data['definitions'] = array();
       $data['route_names'] = $this->collectRoutesAndDefinitions($data['tree'], $data['definitions']);
-      $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, array('menu' => $menu_name));
+      $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, array('menu:' . $menu_name));
       // The definitions were already added to $this->definitions in
       // $this->doBuildTreeData()
       unset($data['definitions']);
diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
index bcb8146..77bfc3c 100644
--- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
+++ b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Page;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 
@@ -68,12 +69,10 @@ public function render(HtmlFragmentInterface $fragment, $status_code = 200) {
     }
 
     if ($fragment instanceof CacheableInterface) {
-      // Collect cache tags for all the content in all the regions on the page.
-      $tags = $page_array['#cache']['tags'];
-      // Tag every render cache item with the "rendered" cache tag. This allows us
-      // to invalidate the entire render cache, regardless of the cache bin.
-      $tags['rendered'] = TRUE;
-      $page->setCacheTags($tags);
+      // Persist cache tags associated with this page. Also associate the
+      // "rendered" cache tag. This allows us to invalidate the entire render
+      // cache, regardless of the cache bin.
+      $page->setCacheTags(Cache::mergeTags($page_array['#cache']['tags'], ['rendered']));
     }
 
     return $page;
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index a2d4cf2..5828d33 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -125,6 +125,7 @@ public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInte
    *   definitions should be cleared along with other, related cache entries.
    */
   public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) {
+    Cache::validateTags($cache_tags);
     $this->cacheBackend = $cache_backend;
     $this->cacheKey = $cache_key;
     $this->cacheTags = $cache_tags;
diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php
index f12c8f3..cf133af 100644
--- a/core/lib/Drupal/Core/Render/Element/Page.php
+++ b/core/lib/Drupal/Core/Render/Element/Page.php
@@ -38,8 +38,8 @@ public function getInfo() {
    * @return array
    */
   public static function preRenderPage($element) {
-    $element['#cache']['tags']['theme'] = \Drupal::theme()->getActiveTheme()->getName();
-    $element['#cache']['tags']['theme_global_settings'] = TRUE;
+    $element['#cache']['tags'][] = 'theme:' . \Drupal::theme()->getActiveTheme()->getName();
+    $element['#cache']['tags'][] = 'theme_global_settings';
     return $element;
   }
 
diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index 0ad005d..da3122c 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -235,7 +235,7 @@ public function get() {
   public function getRuntime() {
     $this->init($this->themeName);
     if (!isset($this->runtimeRegistry)) {
-      $this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry' => TRUE), $this->moduleHandler->isLoaded());
+      $this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry'), $this->moduleHandler->isLoaded());
     }
     return $this->runtimeRegistry;
   }
@@ -244,7 +244,7 @@ public function getRuntime() {
    * Persists the theme registry in the cache backend.
    */
   protected function setCache() {
-    $this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry' => TRUE));
+    $this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry'));
   }
 
   /**
@@ -318,7 +318,7 @@ protected function build() {
       }
       // Only cache this registry if all modules are loaded.
       if ($this->moduleHandler->isLoaded()) {
-        $this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry' => TRUE));
+        $this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry'));
       }
     }
 
@@ -557,7 +557,7 @@ public function reset() {
     $this->runtimeRegistry = NULL;
 
     $this->registry = NULL;
-    Cache::invalidateTags(array('theme_registry' => TRUE));
+    Cache::invalidateTags(array('theme_registry'));
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
index 6c2a4c1..b81aee3 100644
--- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
+++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
@@ -26,7 +26,7 @@ class ThemeAccessCheck implements AccessInterface {
    */
   public function access($theme) {
     // Cacheable until the theme is modified.
-    return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(array('theme' => $theme));
+    return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(array('theme:' . $theme));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Utility/Token.php b/core/lib/Drupal/Core/Utility/Token.php
index d03d645..a59becb 100644
--- a/core/lib/Drupal/Core/Utility/Token.php
+++ b/core/lib/Drupal/Core/Utility/Token.php
@@ -320,7 +320,7 @@ public function getInfo() {
         $this->tokenInfo = $this->moduleHandler->invokeAll('token_info');
         $this->moduleHandler->alter('token_info', $this->tokenInfo);
         $this->cache->set($cache_id, $this->tokenInfo, CacheBackendInterface::CACHE_PERMANENT, array(
-          static::TOKEN_INFO_CACHE_TAG => TRUE,
+          static::TOKEN_INFO_CACHE_TAG,
         ));
       }
     }
diff --git a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php
index d967d1b..22515a3 100644
--- a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php
+++ b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php
@@ -67,8 +67,8 @@ public function testBlockLinks() {
     $this->assertFalse(empty($correct_titles), 'Aggregator feed page is available and has the correct title.');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
     $this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
-    $this->assertTrue(in_array('aggregator_feed_view:1', $cache_tags));
-    $this->assertTrue(in_array('aggregator_item_view:1', $cache_tags));
+    $this->assertTrue(in_array('aggregator_feed_view', $cache_tags));
+    $this->assertTrue(in_array('aggregator_item_view', $cache_tags));
 
     // Set the number of news items to 0 to test that the block does not show
     // up.
@@ -119,8 +119,8 @@ public function testFeedPage() {
     $this->assertTrue(!empty($elements), 'Individual source page contains a pager.');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
     $this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
-    $this->assertTrue(in_array('aggregator_feed_view:1', $cache_tags));
-    $this->assertTrue(in_array('aggregator_item_view:1', $cache_tags));
+    $this->assertTrue(in_array('aggregator_feed_view', $cache_tags));
+    $this->assertTrue(in_array('aggregator_item_view', $cache_tags));
   }
 
 }
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index f35bb71..c3d6147 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -71,12 +71,12 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
 
       // Set cache tags; these always need to be set, whether the block is
       // cacheable or not, so that the page cache is correctly informed.
-      $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeepArray(array(
+      $build[$entity_id]['#cache']['tags'] = Cache::mergeTags(
         $this->getCacheTag(), // Block view builder cache tag.
         $entity->getCacheTag(), // Block entity cache tag.
         $entity->getListCacheTags(), // Block entity list cache tags.
-        $plugin->getCacheTags(), // Block plugin cache tags.
-      ));
+        $plugin->getCacheTags() // Block plugin cache tags.
+      );
 
       if ($plugin->isCacheable()) {
         $build[$entity_id]['#pre_render'][] = array($this, 'buildBlock');
diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php
index ee977d0..0c71ed1 100644
--- a/core/modules/block/src/Entity/Block.php
+++ b/core/modules/block/src/Entity/Block.php
@@ -164,7 +164,7 @@ public function calculateDependencies() {
    * this block is placed in instead.
    */
   public function getListCacheTags() {
-    return array('theme' => $this->theme);
+    return array('theme:' . $this->theme);
   }
 
   /**
diff --git a/core/modules/block/src/Tests/BlockCacheTest.php b/core/modules/block/src/Tests/BlockCacheTest.php
index 94babf7..fd9fc88 100644
--- a/core/modules/block/src/Tests/BlockCacheTest.php
+++ b/core/modules/block/src/Tests/BlockCacheTest.php
@@ -78,7 +78,7 @@ function testCachePerRole() {
     $this->assertText($old_content, 'Block is served from the cache.');
 
     // Clear the cache and verify that the stale data is no longer there.
-    Cache::invalidateTags(array('block_view' => TRUE));
+    Cache::invalidateTags(array('block_view'));
     $this->drupalGet('');
     $this->assertNoText($old_content, 'Block cache clear removes stale cache data.');
     $this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.');
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 6240fcd..c560236 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -294,21 +294,23 @@ public function testBlockCacheTags() {
     $cache_entry = \Drupal::cache('render')->get($cid);
     $expected_cache_tags = array(
       'theme:stark',
-      'theme_global_settings:1',
-      'block_view:1',
+      'theme_global_settings',
+      'block_view',
       'block:powered',
       'block_plugin:system_powered_by_block',
-      'rendered:1',
+      'rendered',
     );
+    sort($expected_cache_tags);
     $this->assertIdentical($cache_entry->tags, $expected_cache_tags);
     $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:stark');
     $expected_cache_tags = array(
-      'block_view:1',
+      'block_view',
       'block:powered',
       'theme:stark',
       'block_plugin:system_powered_by_block',
-      'rendered:1',
+      'rendered',
     );
+    sort($expected_cache_tags);
     $this->assertIdentical($cache_entry->tags, $expected_cache_tags);
 
     // The "Powered by Drupal" block is modified; verify a cache miss.
@@ -334,30 +336,33 @@ public function testBlockCacheTags() {
     $cache_entry = \Drupal::cache('render')->get($cid);
     $expected_cache_tags = array(
       'theme:stark',
-      'theme_global_settings:1',
-      'block_view:1',
+      'theme_global_settings',
+      'block_view',
       'block:powered-2',
       'block:powered',
       'block_plugin:system_powered_by_block',
-      'rendered:1',
+      'rendered',
     );
+    sort($expected_cache_tags);
     $this->assertEqual($cache_entry->tags, $expected_cache_tags);
     $expected_cache_tags = array(
-      'block_view:1',
+      'block_view',
       'block:powered',
       'theme:stark',
       'block_plugin:system_powered_by_block',
-      'rendered:1',
+      'rendered',
     );
+    sort($expected_cache_tags);
     $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:stark');
     $this->assertIdentical($cache_entry->tags, $expected_cache_tags);
     $expected_cache_tags = array(
-      'block_view:1',
+      'block_view',
       'block:powered-2',
       'theme:stark',
       'block_plugin:system_powered_by_block',
-      'rendered:1',
+      'rendered',
     );
+    sort($expected_cache_tags);
     $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:en:stark');
     $this->assertIdentical($cache_entry->tags, $expected_cache_tags);
 
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index 8c9fb13..a115c6d 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\UrlCacheContext;
 use Drupal\simpletest\DrupalUnitTestBase;
 use Symfony\Component\HttpFoundation\Request;
@@ -221,7 +222,7 @@ public function testBlockViewBuilderAlter() {
     $request->setMethod('GET');
 
     $default_keys = array('entity_view', 'block', 'test_block', 'en', 'cache_context.theme');
-    $default_tags = array('block_view' => TRUE, 'block' => array('test_block'), 'theme' => 'stark', 'block_plugin' => array('test_cache'));
+    $default_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache');
 
     // Advanced: cached block, but an alter hook adds an additional cache key.
     $this->setBlockCacheConfig(array(
@@ -236,22 +237,24 @@ public function testBlockViewBuilderAlter() {
     $this->assertIdentical(drupal_render($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
-    $expected_flattened_tags = array('block_view:1', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', 'rendered:1');
-    $this->assertIdentical($cache_entry->tags, $expected_flattened_tags, 'The block render element has been cached with the expected cache tags.');
+    $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', 'rendered');
+    sort($expected_tags);
+    $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
     $this->container->get('cache.render')->delete($cid);
 
     // Advanced: cached block, but an alter hook adds an additional cache tag.
     $alter_add_tag = $this->randomMachineName();
     \Drupal::state()->set('block_test_view_alter_cache_tag', $alter_add_tag);
-    $expected_tags = NestedArray::mergeDeep($default_tags, array($alter_add_tag => TRUE));
+    $expected_tags = Cache::mergeTags($default_tags, array($alter_add_tag));
     $build = $this->getBlockRenderArray();
     $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
     $cid = drupal_render_cid_create(array('#cache' => array('keys' => $expected_keys)));
     $this->assertIdentical(drupal_render($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
-    $expected_flattened_tags = array('block_view:1', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', $alter_add_tag . ':1', 'rendered:1');
-    $this->assertIdentical($cache_entry->tags, $expected_flattened_tags, 'The block render element has been cached with the expected cache tags.');
+    $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', $alter_add_tag, 'rendered');
+    sort($expected_tags);
+    $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
     $this->container->get('cache.render')->delete($cid);
 
     // Advanced: cached block, but an alter hook adds a #pre_render callback to
diff --git a/core/modules/block/tests/modules/block_test/block_test.module b/core/modules/block/tests/modules/block_test/block_test.module
index d4bf97f..19e65ad 100644
--- a/core/modules/block/tests/modules/block_test/block_test.module
+++ b/core/modules/block/tests/modules/block_test/block_test.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Cache\Cache;
 
 /**
  * Implements hook_block_alter().
@@ -27,7 +28,7 @@ function block_test_block_view_test_cache_alter(array &$build, BlockPluginInterf
     $build['#cache']['keys'][] = \Drupal::state()->get('block_test_view_alter_cache_key');
   }
   if (\Drupal::state()->get('block_test_view_alter_cache_tag') !== NULL) {
-    $build['#cache']['tags'][\Drupal::state()->get('block_test_view_alter_cache_tag')] = TRUE;
+    $build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], [\Drupal::state()->get('block_test_view_alter_cache_tag')]);
   }
   if (\Drupal::state()->get('block_test_view_alter_append_pre_render_prefix') !== NULL) {
     $build['#pre_render'][] = 'block_test_pre_render_alter_content';
diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php
index ad906ed..a5ef6d1 100644
--- a/core/modules/book/src/BookManager.php
+++ b/core/modules/book/src/BookManager.php
@@ -451,7 +451,7 @@ public function deleteFromBook($nid) {
     }
     $this->updateOriginalParent($original);
     $this->books = NULL;
-    \Drupal::cache('data')->deleteTags(array('bid' => $original['bid']));
+    \Drupal::cache('data')->deleteTags(array('bid:' . $original['bid']));
   }
 
   /**
@@ -666,7 +666,7 @@ protected function doBookTreeBuild($bid, array $parameters = array()) {
       $this->bookTreeCollectNodeLinks($data['tree'], $data['node_links']);
 
       // Cache the data, if it is not already in the cache.
-      \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid' => $bid));
+      \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid:' . $bid));
       $trees[$tree_cid] = $data;
     }
 
@@ -802,9 +802,11 @@ public function saveBookLink(array $link, $new) {
       $query->condition('nid', $link['nid']);
       $query->execute();
     }
+    $cache_tags = [];
     foreach ($affected_bids as $bid) {
-      \Drupal::cache('data')->deleteTags(array('bid' => $bid));
+      $cache_tags[] = 'bid:' . $bid;
     }
+    \Drupal::cache('data')->deleteTags($cache_tags);
     return $link;
   }
 
@@ -1124,10 +1126,10 @@ public function bookSubtreeData($link) {
         // Cache the data, if it is not already in the cache.
 
         if (!\Drupal::cache('data')->get($tree_cid)) {
-          \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid' => $link['bid']));
+          \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid:' . $link['bid']));
         }
         // Cache the cid of the (shared) data using the book and item-specific cid.
-        \Drupal::cache('data')->set($cid, $tree_cid, Cache::PERMANENT, array('bid' => $link['bid']));
+        \Drupal::cache('data')->set($cid, $tree_cid, Cache::PERMANENT, array('bid:' . $link['bid']));
       }
       // Check access for the current user to each item in the tree.
       $this->bookTreeCheckAccess($data['tree'], $data['node_links']);
diff --git a/core/modules/breakpoint/src/BreakpointManager.php b/core/modules/breakpoint/src/BreakpointManager.php
index 757f2cf..3187912 100644
--- a/core/modules/breakpoint/src/BreakpointManager.php
+++ b/core/modules/breakpoint/src/BreakpointManager.php
@@ -113,7 +113,7 @@ public function __construct(ModuleHandlerInterface $module_handler, ThemeHandler
     $this->themeHandler = $theme_handler;
     $this->setStringTranslation($string_translation);
     $this->alterInfo('breakpoints');
-    $this->setCacheBackend($cache_backend, 'breakpoints', array('breakpoints' => TRUE));
+    $this->setCacheBackend($cache_backend, 'breakpoints', array('breakpoints'));
   }
 
   /**
@@ -176,7 +176,7 @@ public function getBreakpointsByGroup($group) {
           }
         }
         uasort($breakpoints, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
-        $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, array('breakpoints' => TRUE));
+        $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, array('breakpoints'));
         $this->breakpointsByGroup[$group] = $breakpoints;
       }
     }
@@ -205,7 +205,7 @@ public function getGroups() {
           $groups[$plugin_definition['group']] = $plugin_definition['group'];
         }
       }
-      $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, array('breakpoints' => TRUE));
+      $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, array('breakpoints'));
     }
     // Get the labels. This is not cacheable due to translation.
     $group_labels = array();
diff --git a/core/modules/comment/src/Tests/CommentCacheTagsTest.php b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
index f7bd90e..daa816e 100644
--- a/core/modules/comment/src/Tests/CommentCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
@@ -77,7 +77,7 @@ protected function createEntity() {
    */
   protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
     /** @var \Drupal\comment\CommentInterface $entity */
-    return array('filter_format:plain_text', 'user:' . $entity->getOwnerId(), 'user_view:1');
+    return array('filter_format:plain_text', 'user:' . $entity->getOwnerId(), 'user_view');
   }
 
 }
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index e5c1c41..9c14685 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -66,9 +66,10 @@ public function testCacheTags() {
       ->view($commented_entity);
     drupal_render($build);
     $expected_cache_tags = array(
-      'entity_test_view' => TRUE,
-      'entity_test' => array($commented_entity->id()),
+      'entity_test_view',
+      'entity_test:'  . $commented_entity->id(),
     );
+    sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.');
 
     // Create a comment on that entity. Comment loading requires that the uid
@@ -101,16 +102,15 @@ public function testCacheTags() {
       ->view($commented_entity);
     drupal_render($build);
     $expected_cache_tags = array(
-      'entity_test_view' => TRUE,
-      'entity_test' => array($commented_entity->id()),
-      'comment_view' => TRUE,
-      'comment' => array(1 => $comment->id()),
-      'filter_format' => array(
-        'plain_text' => 'plain_text',
-      ),
-      'user_view' => TRUE,
-      'user' => array(2 => 2),
+      'entity_test_view',
+      'entity_test:' . $commented_entity->id(),
+      'comment_view',
+      'comment:' . $comment->id(),
+      'filter_format:plain_text',
+      'user_view',
+      'user:2',
     );
+    sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
   }
 
diff --git a/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php b/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php
index a540a7e..aef189e 100644
--- a/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php
+++ b/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php
@@ -81,10 +81,10 @@ public function testLocks() {
       ->will($this->returnValue((object) array('value' => NULL)));
     $comment->expects($this->once())
       ->method('getCacheTag')
-      ->will($this->returnValue(array('comment' => array($cid))));
+      ->will($this->returnValue(array('comment:' . $cid)));
     $comment->expects($this->once())
       ->method('getListCacheTags')
-      ->will($this->returnValue(array('comments' => TRUE)));
+      ->will($this->returnValue(array('comments')));
     $storage = $this->getMock('Drupal\comment\CommentStorageInterface');
 
     // preSave() should acquire the lock. (This is what's really being tested.)
diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php
index 6c99ce1..a14c628 100644
--- a/core/modules/config_translation/src/ConfigMapperManager.php
+++ b/core/modules/config_translation/src/ConfigMapperManager.php
@@ -99,7 +99,7 @@ public function __construct(CacheBackendInterface $cache_backend, LanguageManage
     $this->alterInfo('config_translation_info');
     // Config translation only uses an info hook discovery, cache by language.
     $cache_key = 'config_translation_info_plugins' . ':' . $language_manager->getCurrentLanguage()->getId();
-    $this->setCacheBackend($cache_backend, $cache_key, array('config_translation_info_plugins' => TRUE));
+    $this->setCacheBackend($cache_backend, $cache_key, array('config_translation_info_plugins'));
   }
 
   /**
diff --git a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
index 7ea7546..6d96727 100644
--- a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
+++ b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
@@ -71,7 +71,7 @@ public function testCreateAccess() {
       ->will($this->returnValue(array()));
     $entity->expects($this->once())
       ->method('getCacheTag')
-      ->will($this->returnValue(array('node' => 1337)));
+      ->will($this->returnValue(array('node:1337')));
 
     // Set the route requirements.
     $route = new Route('test_route');
diff --git a/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php b/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php
index e03066e..7fa1813 100644
--- a/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php
+++ b/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\editor\Tests;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\simpletest\KernelTestBase;
 use Drupal\filter\FilterBag;
 
@@ -58,12 +59,14 @@ function testEditorFileReferenceFilter() {
     $image->save();
     $id = $image->id();
     $uuid = $image->uuid();
+    $cache_tag = ['file:' . $id];
 
     file_put_contents('public://alpaca.jpg', $this->randomMachineName());
     $image_2 = entity_create('file', array('uri' => 'public://alpaca.jpg'));
     $image_2->save();
     $id_2 = $image_2->id();
     $uuid_2 = $image_2->uuid();
+    $cache_tag_2 = ['file:' . $id_2];
 
     $this->pass('No data-editor-file-uuid attribute.');
     $input = '<img src="llama.jpg" />';
@@ -74,19 +77,19 @@ function testEditorFileReferenceFilter() {
     $input = '<img src="llama.jpg" data-editor-file-uuid="' . $uuid . '" />';
     $output = $test($input);
     $this->assertIdentical($input, $output->getProcessedText());
-    $this->assertEqual(array('file' => array($id)), $output->getCacheTags());
+    $this->assertEqual($cache_tag, $output->getCacheTags());
 
     $this->pass('One data-editor-file-uuid attribute with odd capitalization.');
     $input = '<img src="llama.jpg" DATA-editor-file-UUID =   "' . $uuid . '" />';
     $output = $test($input);
     $this->assertIdentical($input, $output->getProcessedText());
-    $this->assertEqual(array('file' => array($id)), $output->getCacheTags());
+    $this->assertEqual($cache_tag, $output->getCacheTags());
 
     $this->pass('One data-editor-file-uuid attribute on a non-image tag.');
     $input = '<video src="llama.jpg" data-editor-file-uuid="' . $uuid . '" />';
     $output = $test($input);
     $this->assertIdentical($input, $output->getProcessedText());
-    $this->assertEqual(array('file' => array($id)), $output->getCacheTags());
+    $this->assertEqual($cache_tag, $output->getCacheTags());
 
     $this->pass('One data-editor-file-uuid attribute with an invalid value.');
     $input = '<img src="llama.jpg" data-editor-file-uuid="invalid-' . $uuid . '" />';
@@ -99,14 +102,14 @@ function testEditorFileReferenceFilter() {
     $input .= '<img src="alpaca.jpg" data-editor-file-uuid="' . $uuid_2 . '" />';
     $output = $test($input);
     $this->assertIdentical($input, $output->getProcessedText());
-    $this->assertEqual(array('file' => array($id, $id_2)), $output->getCacheTags());
+    $this->assertEqual(Cache::mergeTags($cache_tag, $cache_tag_2), $output->getCacheTags());
 
     $this->pass('Two identical  data-editor-file-uuid attributes.');
     $input = '<img src="llama.jpg" data-editor-file-uuid="' . $uuid . '" />';
     $input .= '<img src="llama.jpg" data-editor-file-uuid="' . $uuid . '" />';
     $output = $test($input);
     $this->assertIdentical($input, $output->getProcessedText());
-    $this->assertEqual(array('file' => array($id)), $output->getCacheTags());
+    $this->assertEqual($cache_tag, $output->getCacheTags());
   }
 
 }
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceFormatterTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceFormatterTest.php
index 33b4af0..ed4d65e 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceFormatterTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceFormatterTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\entity_reference\Tests;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\filter\Entity\FilterFormat;
 use Drupal\system\Tests\Entity\EntityUnitTestBase;
 
 /**
@@ -141,10 +143,7 @@ public function testIdFormatter() {
     $build = $items->view(array('type' => $formatter));
 
     $this->assertEqual($build[0]['#markup'], $this->referencedEntity->id(), format_string('The markup returned by the @formatter formatter is correct.', array('@formatter' => $formatter)));
-    $expected_cache_tags = array(
-      $this->entityType => array($this->referencedEntity->id()),
-    );
-    $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
+    $this->assertEqual($build[0]['#cache']['tags'], $this->referencedEntity->getCacheTag(), format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
 
   }
 
@@ -180,10 +179,10 @@ public function testEntityFormatter() {
 ';
     drupal_render($build[0]);
     $this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() .  $expected_rendered_name_field . $expected_rendered_body_field, format_string('The markup returned by the @formatter formatter is correct.', array('@formatter' => $formatter)));
-    $expected_cache_tags = array(
-      $this->entityType . '_view' => TRUE,
-      $this->entityType => array($this->referencedEntity->id()),
-      'filter_format' => array('full_html' => 'full_html'),
+    $expected_cache_tags = Cache::mergeTags(
+      \Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTag(),
+      $this->referencedEntity->getCacheTag(),
+      FilterFormat::load('full_html')->getCacheTag()
     );
     $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
   }
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 1423f5e..320f492 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -182,14 +182,14 @@ function filter_pre_render_text(array $element) {
   }
 
   // Perform filtering.
-  $all_cache_tags = array();
+  $cache_tags = array();
   $all_assets = array();
   $all_post_render_cache_callbacks = array();
   foreach ($filters as $filter) {
     if ($filter_must_be_applied($filter)) {
       $result = $filter->process($text, $langcode);
       $all_assets[] = $result->getAssets();
-      $all_cache_tags[] = $result->getCacheTags();
+      $cache_tags = Cache::mergeTags($cache_tags, $result->getCacheTags());
       $all_post_render_cache_callbacks[] = $result->getPostRenderCacheCallbacks();
       $text = $result->getProcessedText();
     }
@@ -200,12 +200,12 @@ function filter_pre_render_text(array $element) {
 
   // Collect all cache tags.
   if (isset($element['#cache']) && isset($element['#cache']['tags'])) {
-    // Prepend the original cache tags array.
-    array_unshift($all_cache_tags, $element['#cache']['tags']);
+    // Merge the original cache tags array.
+    $cache_tags = Cache::mergeTags($cache_tags, $element['#cache']['tags']);
   }
   // Prepend the text format's cache tags array.
-  array_unshift($all_cache_tags, $format->getCacheTag());
-  $element['#cache']['tags'] = NestedArray::mergeDeepArray($all_cache_tags);
+  $cache_tags = Cache::mergeTags($cache_tags, $format->getCacheTag());
+  $element['#cache']['tags'] = $cache_tags;
 
   // Collect all attached assets.
   if (isset($element['#attached'])) {
@@ -250,7 +250,7 @@ function filter_formats(AccountInterface $account = NULL) {
     else {
       $formats['all'] = \Drupal::entityManager()->getStorage('filter_format')->loadByProperties(array('status' => TRUE));
       uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort');
-      \Drupal::cache()->set("filter_formats:{$language_interface->id}", $formats['all'], Cache::PERMANENT, array('filter_formats' => TRUE));
+      \Drupal::cache()->set("filter_formats:{$language_interface->id}", $formats['all'], Cache::PERMANENT, array('filter_formats'));
     }
   }
 
diff --git a/core/modules/filter/src/FilterPluginManager.php b/core/modules/filter/src/FilterPluginManager.php
index 8eb1144..df211a5 100644
--- a/core/modules/filter/src/FilterPluginManager.php
+++ b/core/modules/filter/src/FilterPluginManager.php
@@ -36,7 +36,7 @@ class FilterPluginManager extends DefaultPluginManager {
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Plugin\FilterInterface', 'Drupal\filter\Annotation\Filter');
     $this->alterInfo('filter_info');
-    $this->setCacheBackend($cache_backend, 'filter_plugins', array('filter_formats' => TRUE));
+    $this->setCacheBackend($cache_backend, 'filter_plugins', array('filter_formats'));
   }
 
   /**
diff --git a/core/modules/filter/src/FilterProcessResult.php b/core/modules/filter/src/FilterProcessResult.php
index 71edc45..5597271 100644
--- a/core/modules/filter/src/FilterProcessResult.php
+++ b/core/modules/filter/src/FilterProcessResult.php
@@ -8,6 +8,7 @@
 namespace Drupal\filter;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\Cache;
 
 /**
  * Used to return values from a text filter plugin's processing method.
@@ -155,7 +156,7 @@ public function getCacheTags() {
    * @return $this
    */
   public function addCacheTags(array $cache_tags) {
-    $this->cacheTags = drupal_merge_attached($this->cacheTags, $cache_tags);
+    $this->cacheTags = Cache::mergeTags($this->cacheTags, $cache_tags);
     return $this;
   }
 
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 7c513a4..b09bfb2 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -246,14 +246,10 @@ function testProcessedTextElement() {
     $this->assertEqual($expected_assets, $build['#attached'], 'Expected assets present');
     $expected_cache_tags = array(
       // The cache tag set by the processed_text element itself.
-      'filter_format' => array(
-        'element_test',
-      ),
+      'filter_format:element_test',
       // The cache tags set by the filter_test_cache_tags filter.
-      'foo' => array(
-        'bar',
-        'baz',
-      ),
+      'foo:bar',
+      'foo:baz',
     );
     $this->assertEqual($expected_cache_tags, $build['#cache']['tags'], 'Expected cache tags present.');
     $expected_markup = '<p>Hello, world!</p><p>This is a dynamic llama.</p>';
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
index 3c03c3f..aa04616 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
@@ -27,8 +27,8 @@ class FilterTestCacheTags extends FilterBase {
    */
   public function process($text, $langcode) {
     $result = new FilterProcessResult($text);
-    $result->addCacheTags(array('foo' => array('bar')));
-    $result->addCacheTags(array('foo' => array('baz')));
+    $result->addCacheTags(array('foo:bar'));
+    $result->addCacheTags(array('foo:baz'));
     return $result;
   }
 
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 11ffc27..aa42fc6 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -1073,7 +1073,7 @@ function _locale_refresh_translations($langcodes, $lids = array()) {
     }
   }
   // Clear locale cache.
-  Cache::deleteTags(array('locale' => TRUE));
+  Cache::deleteTags(array('locale'));
 }
 
 /**
diff --git a/core/modules/locale/src/LocaleLookup.php b/core/modules/locale/src/LocaleLookup.php
index 2f03bc3..8aa1809 100644
--- a/core/modules/locale/src/LocaleLookup.php
+++ b/core/modules/locale/src/LocaleLookup.php
@@ -94,7 +94,7 @@ public function __construct($langcode, $context, StringStorageInterface $string_
 
     $this->cache = $cache;
     $this->lock = $lock;
-    $this->tags = array('locale' => TRUE);
+    $this->tags = array('locale');
   }
 
   /**
diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
index 4a19182..a465fc1 100644
--- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
@@ -49,9 +49,9 @@ public function testMenuBlock() {
     // Verify a cache hit, but also the presence of the correct cache tags.
     $expected_tags = array(
       'theme:stark',
-      'theme_global_settings:1',
-      'rendered:1',
-      'block_view:1',
+      'theme_global_settings',
+      'rendered',
+      'block_view',
       'block:' . $block->id(),
       'block_plugin:system_menu_block__llama',
       'menu:llama',
@@ -106,7 +106,7 @@ public function testMenuBlock() {
     $this->verifyPageCache($path, 'MISS');
 
     // Verify a cache hit.
-    $this->verifyPageCache($path, 'HIT', array('rendered:1', 'theme:stark', 'theme_global_settings:1'));
+    $this->verifyPageCache($path, 'HIT', array('rendered', 'theme:stark', 'theme_global_settings'));
   }
 
 }
diff --git a/core/modules/node/src/Tests/NodeCacheTagsTest.php b/core/modules/node/src/Tests/NodeCacheTagsTest.php
index f17a39e..881ae54 100644
--- a/core/modules/node/src/Tests/NodeCacheTagsTest.php
+++ b/core/modules/node/src/Tests/NodeCacheTagsTest.php
@@ -60,7 +60,7 @@ protected function createEntity() {
    * Each node must have an author.
    */
   protected function getAdditionalCacheTagsForEntity(EntityInterface $node) {
-    return array('user:' . $node->getOwnerId(), 'user_view:1');
+    return array('user:' . $node->getOwnerId(), 'user_view');
   }
 
 }
diff --git a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
index 68aa58b..588be8d 100644
--- a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
+++ b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\responsive_image\Plugin\Field\FieldFormatter;
 
-use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase;
@@ -145,9 +145,9 @@ public function viewElements(FieldItemListInterface $items) {
     if ($fallback_image_style) {
       $image_styles_to_load[] = $fallback_image_style;
     }
-    $all_cache_tags = array();
+    $cache_tags = [];
     if ($responsive_image_mapping) {
-      $all_cache_tags[] = $responsive_image_mapping->getCacheTag();
+      $cache_tags = Cache::mergeTags($cache_tags, $responsive_image_mapping->getCacheTag());
       foreach ($responsive_image_mapping->getMappings() as $mapping) {
         // First mapping found is used as fallback.
         if (empty($fallback_image_style)) {
@@ -158,11 +158,9 @@ public function viewElements(FieldItemListInterface $items) {
     }
     $image_styles = entity_load_multiple('image_style', $image_styles_to_load);
     foreach ($image_styles as $image_style) {
-      $all_cache_tags[] = $image_style->getCacheTag();
+      $cache_tags = Cache::mergeTags($cache_tags, $image_style->getCacheTag());
     }
 
-    $cache_tags = NestedArray::mergeDeepArray($all_cache_tags);
-
     foreach ($items as $delta => $item) {
       if (isset($link_file)) {
         $uri = array(
diff --git a/core/modules/rest/src/LinkManager/RelationLinkManager.php b/core/modules/rest/src/LinkManager/RelationLinkManager.php
index 8cf4afa..8a56fa8 100644
--- a/core/modules/rest/src/LinkManager/RelationLinkManager.php
+++ b/core/modules/rest/src/LinkManager/RelationLinkManager.php
@@ -102,6 +102,6 @@ protected function writeCache() {
     }
     // These URIs only change when field info changes, so cache it permanently
     // and only clear it when the fields cache is cleared.
-    $this->cache->set('rest:links:relations', $data, Cache::PERMANENT, array('entity_field_info' => TRUE));
+    $this->cache->set('rest:links:relations', $data, Cache::PERMANENT, array('entity_field_info'));
   }
 }
diff --git a/core/modules/rest/src/LinkManager/TypeLinkManager.php b/core/modules/rest/src/LinkManager/TypeLinkManager.php
index 488032c..fb21a5f 100644
--- a/core/modules/rest/src/LinkManager/TypeLinkManager.php
+++ b/core/modules/rest/src/LinkManager/TypeLinkManager.php
@@ -99,6 +99,6 @@ protected function writeCache() {
     }
     // These URIs only change when entity info changes, so cache it permanently
     // and only clear it when entity_info is cleared.
-    $this->cache->set('rest:links:types', $data, Cache::PERMANENT, array('entity_types' => TRUE));
+    $this->cache->set('rest:links:types', $data, Cache::PERMANENT, array('entity_types'));
   }
 }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 61fc895..05f94c9 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -6,8 +6,8 @@
  */
 
 use Drupal\Core\Access\AccessResult;
-use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Routing\UrlMatcher;
 use Drupal\Core\Url;
@@ -253,14 +253,14 @@ function shortcut_renderable_links($shortcut_set = NULL) {
 
   /** @var \Drupal\shortcut\ShortcutInterface[] $shortcuts  */
   $shortcuts = \Drupal::entityManager()->getStorage('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id()));
-  $all_cache_tags = array();
+  $cache_tags = array();
   foreach ($shortcuts as $shortcut) {
     $shortcut = \Drupal::entityManager()->getTranslationFromContext($shortcut);
     $links[] = array(
       'title' => $shortcut->label(),
       'href' => $shortcut->path->value,
     );
-    $all_cache_tags[] = $shortcut->getCacheTag();
+    $cache_tags = Cache::mergeTags($cache_tags, $shortcut->getCacheTag());
   }
 
   if (!empty($links)) {
@@ -271,7 +271,7 @@ function shortcut_renderable_links($shortcut_set = NULL) {
         'class' => array('menu'),
       ),
       '#cache' => array(
-        'tags' => NestedArray::mergeDeepArray($all_cache_tags),
+        'tags' => $cache_tags,
       ),
     );
   }
diff --git a/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
index 3ea471a..836cf4f 100644
--- a/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
@@ -57,7 +57,7 @@ protected function createEntity() {
    */
   public function testEntityCreation() {
     // Create a cache entry that is tagged with a shortcut set cache tag.
-    $cache_tags = array('shortcut_set' => 'default');
+    $cache_tags = array('shortcut_set:default');
     \Drupal::cache('render')->set('foo', 'bar', \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
 
     // Verify a cache hit.
diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php
index 8852c31..26c8454 100644
--- a/core/modules/system/core.api.php
+++ b/core/modules/system/core.api.php
@@ -472,8 +472,8 @@
  * \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);
  *
  * // Delete or invalidate all cache items with certain tags.
- * \Drupal\Core\Cache\Cache::deleteTags(array('node' => array(1)));
- * \Drupal\Core\Cache\Cache::invalidateTags(array('user' => array(1)));
+ * \Drupal\Core\Cache\Cache::deleteTags(array('node:1'));
+ * \Drupal\Core\Cache\Cache::invalidateTags(array('user:1'));
  * @endcode
  *
  * Drupal is a content management system, so naturally you want changes to your
diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php
index b65628a..fb4df2a 100644
--- a/core/modules/system/src/Form/ThemeSettingsForm.php
+++ b/core/modules/system/src/Form/ThemeSettingsForm.php
@@ -426,10 +426,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // Invalidate either the theme-specific cache tag or the global theme
     // settings cache tag, depending on whose settings were actually changed.
     if (isset($values['theme'])) {
-      Cache::invalidateTags(array('theme' => $values['theme']));
+      Cache::invalidateTags(array('theme:' . $values['theme']));
     }
     else {
-      Cache::invalidateTags(array('theme_global_settings' => TRUE));
+      Cache::invalidateTags(array('theme_global_settings'));
     }
   }
 
diff --git a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
index f50a287..e65109e 100644
--- a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -192,10 +193,7 @@ public function build() {
   public function getCacheTags() {
     // The theme-specific cache tag is set automatically for each block, but the
     // output of this block also depends on the global theme settings.
-    $tags = array(
-      'theme_global_setting' => TRUE,
-    );
-    return NestedArray::mergeDeep(parent::getCacheTags(), $tags);
+    return Cache::mergeTags(parent::getCacheTags(), ['theme_global_setting']);
   }
 
   /**
diff --git a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
index d2c0270..fc57930 100644
--- a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
@@ -190,8 +190,7 @@ public function getCacheTags() {
     // the cache tag for this menu to be set: whenever the menu is changed, this
     // menu block must also be re-rendered for that user, because maybe a menu
     // link that is accessible for that user has been added.
-    $tags = array('menu' => array($this->getDerivativeId()));
-    return NestedArray::mergeDeep(parent::getCacheTags(), $tags);
+    return Cache::mergeTags(parent::getCacheTags(), ['menu:' . $this->getDerivativeId()]);
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
index 262be48..ebf2b97 100644
--- a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
+++ b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
@@ -50,7 +50,7 @@ function testPageCacheTags() {
     $config->save();
 
     $path = 'system-test/cache_tags_page';
-    $tags = array('system_test_cache_tags_page' => TRUE);
+    $tags = array('system_test_cache_tags_page');
     $this->drupalGet($path);
     $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
 
@@ -62,11 +62,11 @@ function testPageCacheTags() {
     $cache_entry = \Drupal::cache('render')->get($cid);
     sort($cache_entry->tags);
     $expected_tags = array(
-      'pre_render:1',
-      'rendered:1',
-      'system_test_cache_tags_page:1',
+      'pre_render',
+      'rendered',
+      'system_test_cache_tags_page',
       'theme:stark',
-      'theme_global_settings:1',
+      'theme_global_settings',
     );
     $this->assertIdentical($cache_entry->tags, $expected_tags);
 
diff --git a/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
index 2112a60..c832ded 100644
--- a/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
+++ b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
@@ -40,7 +40,7 @@ public function containerBuild(ContainerBuilder $container) {
 
   public function testTagInvalidations() {
     // Create cache entry in multiple bins.
-    $tags = array('test_tag' => array(1, 2, 3));
+    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
     $bins = array('data', 'bootstrap', 'render');
     foreach ($bins as $bin) {
       $bin = \Drupal::cache($bin);
@@ -49,7 +49,7 @@ public function testTagInvalidations() {
     }
 
     $invalidations_before = intval(db_select('cachetags')->fields('cachetags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
-    Cache::invalidateTags(array('test_tag' => array(2)));
+    Cache::invalidateTags(array('test_tag:2'));
 
     // Test that cache entry has been invalidated in multiple bins.
     foreach ($bins as $bin) {
@@ -62,9 +62,9 @@ public function testTagInvalidations() {
     $this->assertEqual($invalidations_after, $invalidations_before + 1, 'Only one addition cache tag invalidation has occurred after invalidating a tag used in multiple bins.');
   }
 
-  public function testTagDeletetions() {
+  public function testTagDeletions() {
     // Create cache entry in multiple bins.
-    $tags = array('test_tag' => array(1, 2, 3));
+    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
     $bins = array('data', 'bootstrap', 'render');
     foreach ($bins as $bin) {
       $bin = \Drupal::cache($bin);
@@ -73,7 +73,7 @@ public function testTagDeletetions() {
     }
 
     $deletions_before = intval(db_select('cachetags')->fields('cachetags', array('deletions'))->condition('tag', 'test_tag:2')->execute()->fetchField());
-    Cache::deleteTags(array('test_tag' => array(2)));
+    Cache::deleteTags(array('test_tag:2'));
 
     // Test that cache entry has been deleted in multiple bins.
     foreach ($bins as $bin) {
diff --git a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
index 23ddbdc..44e6a94 100644
--- a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
+++ b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
@@ -376,7 +376,7 @@ public function testSetMultiple() {
       'cid_2' => array('data' => 2),
       'cid_3' => array('data' => array(1, 2)),
       'cid_4' => array('data' => 1, 'expire' => $future_expiration),
-      'cid_5' => array('data' => 1, 'tags' => array('test' => array('a', 'b'))),
+      'cid_5' => array('data' => 1, 'tags' => array('test:a', 'test:b')),
     );
     $backend->setMultiple($items);
     $cids = array_keys($items);
@@ -452,38 +452,38 @@ function testDeleteTags() {
     $backend = $this->getCacheBackend();
 
     // Create two cache entries with the same tag and tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Delete test_tag of value 1. This should delete both entries.
-    $backend->deleteTags(array('test_tag' => 2));
+    $backend->deleteTags(array('test_tag:2'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleting a cache tag.');
     $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
 
     // Create two cache entries with the same tag and an array tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Delete test_tag of value 1. This should delete both entries.
-    $backend->deleteTags(array('test_tag' => array(1)));
+    $backend->deleteTags(array('test_tag:1'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleted a cache tag.');
     $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
 
     // Create three cache entries with a mix of tags and tag values.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(2)));
-    $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo' => array(3)));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
+    $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
-    $backend->deleteTags(array('test_tag_foo' => array(3)));
+    $backend->deleteTags(array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cached items not matching the tag were not deleted.');
     $this->assertFalse($backend->get('test_cid_invalidated3', TRUE), 'Cache item matching the tag was deleted.');
 
     // Create cache entry in multiple bins. Two cache entries
     // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
     // tests.
-    $tags = array('test_tag' => array(1, 2, 3));
+    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
     $bins = array('path', 'bootstrap', 'page');
     foreach ($bins as $bin) {
       $this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags);
@@ -492,7 +492,7 @@ function testDeleteTags() {
 
     // Delete tag in mulitple bins.
     foreach ($bins as $bin) {
-      $this->getCacheBackend($bin)->deleteTags(array('test_tag' => array(2)));
+      $this->getCacheBackend($bin)->deleteTags(array('test_tag:2'));
     }
 
     // Test that cache entry has been deleted in multple bins.
@@ -564,38 +564,38 @@ function testInvalidateTags() {
     $backend = $this->getCacheBackend();
 
     // Create two cache entries with the same tag and tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $backend->invalidateTags(array('test_tag' => 2));
+    $backend->invalidateTags(array('test_tag:2'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.');
     $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
 
     // Create two cache entries with the same tag and an array tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $backend->invalidateTags(array('test_tag' => array(1)));
+    $backend->invalidateTags(array('test_tag:1'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.');
     $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
 
     // Create three cache entries with a mix of tags and tag values.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1)));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(2)));
-    $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo' => array(3)));
+    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
+    $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
-    $backend->invalidateTags(array('test_tag_foo' => array(3)));
+    $backend->invalidateTags(array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.');
     $this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.');
 
     // Create cache entry in multiple bins. Two cache entries
     // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
     // tests.
-    $tags = array('test_tag' => array(1, 2, 3));
+    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
     $bins = array('path', 'bootstrap', 'page');
     foreach ($bins as $bin) {
       $this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags);
@@ -604,7 +604,7 @@ function testInvalidateTags() {
 
     // Invalidate tag in mulitple bins.
     foreach ($bins as $bin) {
-      $this->getCacheBackend($bin)->invalidateTags(array('test_tag' => array(2)));
+      $this->getCacheBackend($bin)->invalidateTags(array('test_tag:2'));
     }
 
     // Test that cache entry has been invalidated in multple bins.
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
index 9c41eb8..be9027f 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -68,10 +68,10 @@ function testPageCacheTags() {
 
     // Full node page 1.
     $this->verifyPageCacheTags('node/' . $node_1->id(), array(
-      'rendered:1',
+      'rendered',
       'theme:bartik',
-      'theme_global_settings:1',
-      'block_view:1',
+      'theme_global_settings',
+      'block_view',
       'block:bartik_content',
       'block:bartik_tools',
       'block:bartik_login',
@@ -82,7 +82,7 @@ function testPageCacheTags() {
       'block_plugin:user_login_block',
       'block_plugin:system_menu_block__footer',
       'block_plugin:system_powered_by_block',
-      'node_view:1',
+      'node_view',
       'node:' . $node_1->id(),
       'user:' . $author_1->id(),
       'filter_format:basic_html',
@@ -93,10 +93,10 @@ function testPageCacheTags() {
 
     // Full node page 2.
     $this->verifyPageCacheTags('node/' . $node_2->id(), array(
-      'rendered:1',
+      'rendered',
       'theme:bartik',
-      'theme_global_settings:1',
-      'block_view:1',
+      'theme_global_settings',
+      'block_view',
       'block:bartik_content',
       'block:bartik_tools',
       'block:bartik_login',
@@ -109,7 +109,7 @@ function testPageCacheTags() {
       'block_plugin:views_block__comments_recent-block_1',
       'block_plugin:system_menu_block__footer',
       'block_plugin:system_powered_by_block',
-      'node_view:1',
+      'node_view',
       'node:' . $node_2->id(),
       'user:' . $author_2->id(),
       'filter_format:full_html',
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index 57f8c08..d07085c 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -422,13 +422,13 @@ function testDrupalRenderCache() {
     $test_element = array(
       '#cache' => array(
         'cid' => 'render_cache_test',
-        'tags' => array('render_cache_tag' => TRUE),
+        'tags' => array('render_cache_tag'),
       ),
       '#markup' => '',
       'child' => array(
         '#cache' => array(
           'cid' => 'render_cache_test_child',
-          'tags' => array('render_cache_tag_child' => array(1, 2))
+          'tags' => array('render_cache_tag_child:1', 'render_cache_tag_child:2'),
         ),
         '#markup' => '',
       ),
@@ -449,9 +449,10 @@ function testDrupalRenderCache() {
     // Test that cache tags are correctly collected from the render element,
     // including the ones from its subchild.
     $expected_tags = array(
-      'rendered' => TRUE,
-      'render_cache_tag' => TRUE,
-      'render_cache_tag_child' => array(1 => 1, 2 => 2),
+      'render_cache_tag',
+      'render_cache_tag_child:1',
+      'render_cache_tag_child:2',
+      'rendered',
     );
     $this->assertEqual($expected_tags, $element['#cache']['tags'], 'Cache tags were collected from the element and its subchild.');
 
@@ -508,7 +509,7 @@ function testDrupalRenderPostRenderCache() {
       '#markup' => '<p>#cache enabled, GET</p>',
       '#attached' => $test_element['#attached'],
       '#post_render_cache' => $test_element['#post_render_cache'],
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
     $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
 
@@ -629,7 +630,7 @@ function testDrupalRenderChildrenPostRenderCache() {
           $context_3,
         )
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
 
     $dom = Html::load($cached_element['#markup']);
@@ -699,7 +700,7 @@ function testDrupalRenderChildrenPostRenderCache() {
           $context_3,
         )
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
 
     $dom = Html::load($cached_parent_element['#markup']);
@@ -725,7 +726,7 @@ function testDrupalRenderChildrenPostRenderCache() {
           $context_3,
         )
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
 
     $dom = Html::load($cached_child_element['#markup']);
@@ -832,7 +833,7 @@ function testDrupalRenderRenderCachePlaceholder() {
           $context
         ),
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
     $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
 
@@ -923,7 +924,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
           $context,
         ),
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
     $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
 
@@ -949,7 +950,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
           $context,
         ),
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
     $this->assertIdentical($cached_element, $expected_element); //, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
 
@@ -979,7 +980,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
           $context,
         ),
       ),
-      '#cache' => array('tags' => array('rendered' => TRUE)),
+      '#cache' => array('tags' => array('rendered')),
     );
     $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
 
@@ -1010,7 +1011,7 @@ public static function bubblingPreRender($elements) {
     $elements += array(
       'child_cache_tag' => array(
         '#cache' => array(
-          'tags' => array('child' => 'cache_tag'),
+          'tags' => array('child:cache_tag'),
         ),
         '#markup' => 'Cache tag!',
       ),
@@ -1086,7 +1087,7 @@ function testDrupalRenderBubbling() {
       $output = drupal_render($test_element);
       // Assert top-level.
       $this->assertEqual('Cache tag!Asset!Post-render cache!barquxNested!Cached nested!', trim($output), 'Expected HTML generated.');
-      $this->assertEqual(array('child' => 'cache_tag'), $test_element['#cache']['tags'], 'Expected cache tags found.');
+      $this->assertEqual(array('child:cache_tag'), $test_element['#cache']['tags'], 'Expected cache tags found.');
       $expected_attached = array(
         'js' => array(
           0 => array(
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 795ed05..c2db659 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -255,9 +255,9 @@ protected function createReferenceTestEntities($referenced_entity) {
    * Tests cache tags presence and invalidation of the entity when referenced.
    *
    * Tests the following cache tags:
-   * - "<entity type>_view:1"
+   * - "<entity type>_view"
    * - "<entity type>:<entity ID>"
-   * - "<referencing entity type>_view:1"
+   * - "<referencing entity type>_view"
    * * - "<referencing entity type>:<referencing entity ID>"
    */
   public function testReferencedEntity() {
@@ -266,8 +266,8 @@ public function testReferencedEntity() {
     $non_referencing_entity_path = $this->non_referencing_entity->getSystemPath();
     $listing_path = 'entity_test/list/' . $entity_type . '_reference/' . $entity_type . '/' . $this->entity->id();
 
-    $render_cache_tags = array('rendered:1');
-    $theme_cache_tags = array('theme:stark', 'theme_global_settings:1');
+    $render_cache_tags = array('rendered');
+    $theme_cache_tags = array('theme:stark', 'theme_global_settings');
 
     $view_cache_tag = array();
     if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) {
@@ -276,40 +276,38 @@ public function testReferencedEntity() {
     }
 
     // Generate the cache tags for the (non) referencing entities.
-    $referencing_entity_cache_tags = NestedArray::mergeDeep(
+    $referencing_entity_cache_tags = Cache::mergeTags(
       $this->referencing_entity->getCacheTag(),
       \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag(),
       // Includes the main entity's cache tags, since this entity references it.
       $this->entity->getCacheTag(),
+      $this->getAdditionalCacheTagsForEntity($this->entity),
       $view_cache_tag
     );
-    $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags));
-    $referencing_entity_cache_tags = array_merge($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
-    $non_referencing_entity_cache_tags = NestedArray::mergeDeep(
+    $non_referencing_entity_cache_tags = Cache::mergeTags(
       $this->non_referencing_entity->getCacheTag(),
       \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag()
     );
-    $non_referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($non_referencing_entity_cache_tags));
 
 
     $this->pass("Test referencing entity.", 'Debug');
     $this->verifyPageCache($referencing_entity_path, 'MISS');
     // Verify a cache hit, but also the presence of the correct cache tags.
-    $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
     $this->verifyPageCache($referencing_entity_path, 'HIT', $tags);
     // Also verify the existence of an entity render cache entry.
     $cid = 'entity_view:entity_test:' . $this->referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get();
-    $tags = array_merge($render_cache_tags, $referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $referencing_entity_cache_tags);
     $this->verifyRenderCache($cid, $tags);
 
     $this->pass("Test non-referencing entity.", 'Debug');
     $this->verifyPageCache($non_referencing_entity_path, 'MISS');
     // Verify a cache hit, but also the presence of the correct cache tags.
-    $tags = array_merge($render_cache_tags, $theme_cache_tags, $non_referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $non_referencing_entity_cache_tags);
     $this->verifyPageCache($non_referencing_entity_path, 'HIT', $tags);
     // Also verify the existence of an entity render cache entry.
     $cid = 'entity_view:entity_test:' . $this->non_referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get();
-    $tags = array_merge($render_cache_tags, $non_referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $non_referencing_entity_cache_tags);
     $this->verifyRenderCache($cid, $tags);
 
 
@@ -317,7 +315,7 @@ public function testReferencedEntity() {
     // Prime the page cache for the listing of referencing entities.
     $this->verifyPageCache($listing_path, 'MISS');
     // Verify a cache hit, but also the presence of the correct cache tags.
-    $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
     $this->verifyPageCache($listing_path, 'HIT', $tags);
 
 
@@ -473,10 +471,9 @@ public function testReferencedEntity() {
       $this->referencing_entity->getCacheTag(),
       \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag()
     );
-    $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags));
-    $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags);
     $this->verifyPageCache($referencing_entity_path, 'HIT', $tags);
-    $tags = array_merge($render_cache_tags, $theme_cache_tags);
+    $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags);
     $this->verifyPageCache($listing_path, 'HIT', $tags);
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
index 95ee88b..5077325 100644
--- a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
@@ -18,7 +18,7 @@
    * Tests cache tags presence and invalidation of the entity at its URI.
    *
    * Tests the following cache tags:
-   * - "<entity type>_view:1"
+   * - "<entity type>_view"
    * - "<entity_type>:<entity ID>"
    */
   public function testEntityUri() {
@@ -29,9 +29,9 @@ public function testEntityUri() {
     $view_mode = $this->selectViewMode($entity_type);
 
     // Generate the standardized entity cache tags.
-    $cache_tag = $entity_type . ':' . $this->entity->id();
-    $view_cache_tag = $entity_type . '_view:1';
-    $render_cache_tag = 'rendered:1';
+    $cache_tag = $this->entity->getCacheTag();
+    $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTag();
+    $render_cache_tag = 'rendered';
 
 
     $this->pass("Test entity.", 'Debug');
@@ -45,7 +45,7 @@ public function testEntityUri() {
     if (\Drupal::entityManager()->getDefinition($entity_type)->isRenderCacheable()) {
       $cid = 'entity_view:' . $entity_type . ':' . $this->entity->id() . ':' . $view_mode . ':stark:r.anonymous:' . date_default_timezone_get();
       $cache_entry = \Drupal::cache('render')->get($cid);
-      $expected_cache_tags = array_merge(array($view_cache_tag, $cache_tag), $this->getAdditionalCacheTagsForEntity($this->entity), array($render_cache_tag));
+      $expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag, $this->getAdditionalCacheTagsForEntity($this->entity), array($render_cache_tag));
       $this->verifyRenderCache($cid, $expected_cache_tags);
     }
 
@@ -110,7 +110,7 @@ public function testEntityUri() {
     // Verify that after invalidating the entity's cache tag directly, there is
     // a cache miss.
     $this->pass("Test invalidation of entity's cache tag.", 'Debug');
-    Cache::invalidateTags(array($entity_type => array($this->entity->id())));
+    Cache::invalidateTags($this->entity->getCacheTag());
     $this->verifyPageCache($entity_path, 'MISS');
 
     // Verify a cache hit.
@@ -120,7 +120,7 @@ public function testEntityUri() {
     // Verify that after invalidating the generic entity type's view cache tag
     // directly, there is a cache miss.
     $this->pass("Test invalidation of entity's 'view' cache tag.", 'Debug');
-    Cache::invalidateTags(array($entity_type . '_view' => TRUE));
+    Cache::invalidateTags($view_cache_tag);
     $this->verifyPageCache($entity_path, 'MISS');
 
     // Verify a cache hit.
diff --git a/core/modules/system/src/Tests/Theme/RegistryTest.php b/core/modules/system/src/Tests/Theme/RegistryTest.php
index cb4de8d..8851ebb 100644
--- a/core/modules/system/src/Tests/Theme/RegistryTest.php
+++ b/core/modules/system/src/Tests/Theme/RegistryTest.php
@@ -38,7 +38,7 @@ function testRaceCondition() {
     // entry to be written in __construct().
     $cache = \Drupal::cache();
     $lock_backend = \Drupal::lock();
-    $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry' => TRUE), $this->container->get('module_handler')->isLoaded());
+    $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded());
 
     $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.');
 
@@ -58,7 +58,7 @@ function testRaceCondition() {
     // Create a new instance of the class. Confirm that both the offset
     // requested previously, and one that has not yet been requested are both
     // available.
-    $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry' => TRUE), $this->container->get('module_handler')->isLoaded());
+    $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded());
     $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry');
     $this->assertTrue($registry->get('theme_test_template_test_2'), 'Offset was returned correctly from the theme registry');
   }
diff --git a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
index 0b9290b..c5a10b8 100644
--- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
+++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
@@ -43,7 +43,7 @@ public function lockExit() {
    */
   public function system_test_cache_tags_page() {
     $build['main'] = array(
-      '#cache' => array('tags' => array('system_test_cache_tags_page' => TRUE)),
+      '#cache' => array('tags' => array('system_test_cache_tags_page')),
       '#pre_render' => array(
         '\Drupal\system_test\Controller\SystemTestController::preRenderCacheTags',
       ),
@@ -58,7 +58,7 @@ public function system_test_cache_tags_page() {
    * Sets a cache tag on an element to help test #pre_render and cache tags.
    */
   public static function preRenderCacheTags($elements) {
-    $elements['#cache']['tags']['pre_render'] = TRUE;
+    $elements['#cache']['tags'][] = 'pre_render';
     return $elements;
   }
 
diff --git a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
index c9e0422..9a6d0ab 100644
--- a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
+++ b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\text\Tests\Formatter;
 
+use Drupal\filter\Entity\FilterFormat;
 use Drupal\system\Tests\Entity\EntityUnitTestBase;
 
 /**
@@ -91,10 +92,7 @@ public function testFormatters() {
       $build = $entity->get('formatted_text')->view(array('type' => $formatter));
       drupal_render($build[0]);
       $this->assertEqual($build[0]['#markup'], "<p>Hello, world!</p>\n");
-      $expected_cache_tags = array(
-        'filter_format' => array('my_text_format'),
-      );
-      $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags when formatting a formatted text field.', array('@formatter' => $formatter)));
+      $this->assertEqual($build[0]['#cache']['tags'], FilterFormat::load('my_text_format')->getCacheTag(), format_string('The @formatter formatter has the expected cache tags when formatting a formatted text field.', array('@formatter' => $formatter)));
     }
   }
 
diff --git a/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php b/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
index fefb74b..14454dd 100644
--- a/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
+++ b/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
@@ -267,7 +267,7 @@ function testCacheClearByCacheTag() {
 
     // Log in admin_user and clear the caches for this user using a tag.
     $this->drupalLogin($this->admin_user);
-    Cache::deleteTags(array('user' => array($admin_user_id)));
+    Cache::deleteTags(array('user:' . $admin_user_id));
 
     // Assert that no toolbar cache exists for admin_user against the
     // language "en".
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 07deb3c..59778d6 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -508,7 +508,7 @@ function _toolbar_get_subtrees_hash($langcode) {
     // caches later, based on the user's ID regardless of language.
     // Clear the cache when the 'locale' tag is deleted. This ensures a fresh
     // subtrees rendering when string translations are made.
-    \Drupal::cache('toolbar')->set($cid, $hash, Cache::PERMANENT, array('user' => array($uid), 'locale' => TRUE, 'menu' => 'admin', 'user_roles' => TRUE));
+    \Drupal::cache('toolbar')->set($cid, $hash, Cache::PERMANENT, array('user:' . $uid, 'locale', 'menu:admin', 'user_roles'));
   }
   return $hash;
 }
diff --git a/core/modules/tour/src/Tests/TourCacheTagsTest.php b/core/modules/tour/src/Tests/TourCacheTagsTest.php
index c0c3cac..ab376e2 100644
--- a/core/modules/tour/src/Tests/TourCacheTagsTest.php
+++ b/core/modules/tour/src/Tests/TourCacheTagsTest.php
@@ -39,7 +39,7 @@ protected function setUp() {
    * Tests cache tags presence and invalidation of the Tour entity.
    *
    * Tests the following cache tags:
-   * - ['tour' => '<tour ID>']
+   * - 'tour:<tour ID>'
    */
   public function testRenderedTour() {
     $path = 'tour-test-1';
@@ -50,9 +50,9 @@ public function testRenderedTour() {
     // Verify a cache hit, but also the presence of the correct cache tags.
     $expected_tags = array(
       'theme:stark',
-      'theme_global_settings:1',
+      'theme_global_settings',
       'tour:tour-test',
-      'rendered:1',
+      'rendered',
     );
     $this->verifyPageCache($path, 'HIT', $expected_tags);
 
@@ -70,7 +70,7 @@ public function testRenderedTour() {
     $this->verifyPageCache($path, 'MISS');
 
     // Verify a cache hit.
-    $this->verifyPageCache($path, 'HIT', array('rendered:1', 'theme:stark', 'theme_global_settings:1'));
+    $this->verifyPageCache($path, 'HIT', array('rendered', 'theme:stark', 'theme_global_settings'));
   }
 
 }
diff --git a/core/modules/user/src/Entity/Role.php b/core/modules/user/src/Entity/Role.php
index 927b1c8..cad0530 100644
--- a/core/modules/user/src/Entity/Role.php
+++ b/core/modules/user/src/Entity/Role.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\user\Entity;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\user\RoleInterface;
diff --git a/core/modules/user/src/PermissionsHash.php b/core/modules/user/src/PermissionsHash.php
index 4e4bf3c..3d91576 100644
--- a/core/modules/user/src/PermissionsHash.php
+++ b/core/modules/user/src/PermissionsHash.php
@@ -59,7 +59,11 @@ public function generate(AccountInterface $account) {
     }
     else {
       $permissions_hash = $this->doGenerate($sorted_roles);
-      $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, array('user_role' => $sorted_roles));
+      $tags = [];
+      foreach ($sorted_roles as $role) {
+        $tags[] = 'user_role:' . $role;
+      }
+      $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, $tags);
     }
 
     return $permissions_hash;
diff --git a/core/modules/views/src/Plugin/ViewsHandlerManager.php b/core/modules/views/src/Plugin/ViewsHandlerManager.php
index e7e1c89..33322f9 100644
--- a/core/modules/views/src/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/src/Plugin/ViewsHandlerManager.php
@@ -58,7 +58,7 @@ public function __construct($handler_type, \Traversable $namespaces, ViewsData $
     }
     parent::__construct("Plugin/views/$handler_type", $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
 
-    $this->setCacheBackend($cache_backend, "views:$handler_type", array('extension' => array(TRUE, 'views')));
+    $this->setCacheBackend($cache_backend, "views:$handler_type", array('extension', 'extension:views'));
 
     $this->viewsData = $views_data;
     $this->handlerType = $handler_type;
diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php
index 3b43981..c340ca2 100644
--- a/core/modules/views/src/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/src/Plugin/ViewsPluginManager.php
@@ -43,7 +43,7 @@ public function __construct($type, \Traversable $namespaces, CacheBackendInterfa
     );
 
     $this->alterInfo('views_plugins_' . $type);
-    $this->setCacheBackend($cache_backend, "views:{$type}_plugins", array('extension' => array(TRUE, 'views')));
+    $this->setCacheBackend($cache_backend, "views:{$type}_plugins", array('extension', 'extension:views'));
   }
 
 }
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index d747283..9eb822a 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -199,7 +199,7 @@ public function cacheGet($type) {
    */
   public function cacheFlush() {
     $id = $this->view->storage->id();
-    Cache::invalidateTags(array('view' => array($id => $id)));
+    Cache::invalidateTags(array('view:' . $id));
   }
 
   /**
@@ -353,14 +353,14 @@ public function generateOutputKey() {
    */
   protected function getCacheTags() {
     $id = $this->view->storage->id();
-    $tags = array('view' => array($id => $id));
+    $tags = array('view:' . $id);
 
     $entity_information = $this->view->query->getEntityTableInfo();
 
     if (!empty($entity_information)) {
       // Add an ENTITY_TYPE_list tag for each entity type used by this view.
       foreach (array_keys($entity_information) as $entity_type) {
-        $tags[$entity_type . '_list'] = TRUE;
+        $tags[] = $entity_type . '_list';
       }
     }
 
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 22e8d16..3444faf 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -180,7 +180,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
         else {
           $this->unpackOptions($this->options, $options);
           $id = $this->view->storage->id();
-          \Drupal::cache('data')->set($cid, $this->options, Cache::PERMANENT, array('extension' => array(TRUE, 'views'), 'view' => array($id => $id)));
+          \Drupal::cache('data')->set($cid, $this->options, Cache::PERMANENT, array('extension', 'extension:views', 'view:' . $id));
         }
         static::$unpackOptions[$cid] = $this->options;
       }
diff --git a/core/modules/views/src/ViewsData.php b/core/modules/views/src/ViewsData.php
index bd65aa8..cfbf254 100644
--- a/core/modules/views/src/ViewsData.php
+++ b/core/modules/views/src/ViewsData.php
@@ -199,7 +199,7 @@ protected function cacheGet($cid) {
    *   The data that will be cached.
    */
   protected function cacheSet($cid, $data) {
-    return $this->cacheBackend->set($this->prepareCid($cid), $data, Cache::PERMANENT, array('views_data' => TRUE, 'extension' => array(TRUE, 'views')));
+    return $this->cacheBackend->set($this->prepareCid($cid), $data, Cache::PERMANENT, array('views_data', 'extension', 'extension:views'));
   }
 
   /**
@@ -310,6 +310,6 @@ public function clear() {
     $this->storage = array();
     $this->allStorage = array();
     $this->fullyLoaded = FALSE;
-    Cache::deleteTags(array('views_data' => TRUE));
+    Cache::deleteTags(array('views_data'));
   }
 }
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 5534df6..256a221 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -868,7 +868,7 @@ function hook_views_ui_display_top_links_alter(array &$links, ViewExecutable $vi
  * @see views_invalidate_cache()
  */
 function hook_views_invalidate_cache() {
-  \Drupal\Core\Cache\Cache::invalidateTags(array('views' => TRUE));
+  \Drupal\Core\Cache\Cache::invalidateTags(array('views'));
 }
 
 /**
diff --git a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
index 717e8fc..9a92f20 100644
--- a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
@@ -553,7 +553,7 @@ public function testCheckNamedRouteWithNonExistingRoute() {
     $this->setupAccessChecker();
 
     $this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account), 'A non existing route lead to access.');
-    $this->assertEquals(AccessResult::forbidden()->addCacheTags(array('extension' => TRUE)), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
+    $this->assertEquals(AccessResult::forbidden()->addCacheTags(array('extension')), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
index 67d25d5..e722178 100644
--- a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
+++ b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
@@ -420,44 +420,35 @@ public function testCacheTags() {
       $this->assertSame($tags, $access->getCacheTags());
     };
 
-    $access = AccessResult::create()->addCacheTags(['foo' => ['bar']]);
-    $verify($access, ['foo' => ['bar' => 'bar']]);
+    $access = AccessResult::create()->addCacheTags(['foo:bar']);
+    $verify($access, ['foo:bar']);
     // Verify resetting works.
     $access->resetCacheTags();
     $verify($access, []);
     // Verify idempotency.
-    $access->addCacheTags(['foo' => ['bar']])
-      ->addCacheTags(['foo' => ['bar']]);
-    $verify($access, ['foo' => ['bar' => 'bar']]);
+    $access->addCacheTags(['foo:bar'])
+      ->addCacheTags(['foo:bar']);
+    $verify($access, ['foo:bar']);
     // Verify same values in different call order yields the same result.
     $access->resetCacheTags()
-      ->addCacheTags(['bar' => ['baz']])
-      ->addCacheTags(['bar' => ['qux']])
-      ->addCacheTags(['foo' => ['bar']])
-      ->addCacheTags(['foo' => ['baz']]);
-    $verify($access, ['bar' => ['baz' => 'baz', 'qux' => 'qux'], 'foo' => ['bar' => 'bar', 'baz' => 'baz']]);
+      ->addCacheTags(['bar:baz'])
+      ->addCacheTags(['bar:qux'])
+      ->addCacheTags(['foo:bar'])
+      ->addCacheTags(['foo:baz']);
+    $verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
     $access->resetCacheTags()
-      ->addCacheTags(['foo' => ['bar']])
-      ->addCacheTags(['bar' => ['qux']])
-      ->addCacheTags(['foo' => ['baz']])
-      ->addCacheTags(['bar' => ['baz']]);
-    $verify($access, ['bar' => ['baz' => 'baz', 'qux' => 'qux'], 'foo' => ['bar' => 'bar', 'baz' => 'baz']]);
-    // Verify tags with nested arrays and without.
-    $access->resetCacheTags()
-      // Array.
-      ->addCacheTags(['foo' => ['bar']])
-      // String.
-      ->addCacheTags(['bar' => 'baz'])
-      // Boolean.
-      ->addCacheTags(['qux' => TRUE]);
-    $verify($access, ['bar' => 'baz', 'foo' => ['bar' => 'bar'], 'qux' => TRUE]);
+      ->addCacheTags(['foo:bar'])
+      ->addCacheTags(['bar:qux'])
+      ->addCacheTags(['foo:baz'])
+      ->addCacheTags(['bar:baz']);
+    $verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
 
     // ::cacheUntilEntityChanges() convenience method.
     $node = $this->getMock('\Drupal\node\NodeInterface');
     $node->expects($this->any())
       ->method('getCacheTag')
-      ->will($this->returnValue(array('node' => array(20011988))));
-    $tags = array('node' => array(20011988 => 20011988));
+      ->will($this->returnValue(array('node:20011988')));
+    $tags = array('node:20011988');
     $a = AccessResult::create()->addCacheTags($tags);
     $verify($a, $tags);
     $b = AccessResult::create()->cacheUntilEntityChanges($node);
@@ -484,23 +475,23 @@ public function testCacheabilityMerging() {
 
     // andIf(); 1st has defaults, 2nd has custom tags, contexts and max-age.
     $access = AccessResult::allowed()
-      ->andIf(AccessResult::allowed()->setCacheMaxAge(1500)->cachePerRole()->addCacheTags(['node' => [20011988]]));
+      ->andIf(AccessResult::allowed()->setCacheMaxAge(1500)->cachePerRole()->addCacheTags(['node:20011988']));
     $this->assertTrue($access->isAllowed());
     $this->assertFalse($access->isForbidden());
     $this->assertTrue($access->isCacheable());
     $this->assertSame(['cache_context.user.roles'], $access->getCacheKeys());
-    $this->assertSame(['node' => [20011988 => 20011988]], $access->getCacheTags());
+    $this->assertSame(['node:20011988'], $access->getCacheTags());
     $this->assertSame('default', $access->getCacheBin());
     $this->assertSame(1500, $access->getCacheMaxAge());
 
     // andIf(); 1st has custom tags, max-age, 2nd has custom contexts and max-age.
     $access = AccessResult::allowed()->cachePerUser()->setCacheMaxAge(43200)
-      ->andIf(AccessResult::forbidden()->addCacheTags(['node' => [14031991]])->setCacheMaxAge(86400));
+      ->andIf(AccessResult::forbidden()->addCacheTags(['node:14031991'])->setCacheMaxAge(86400));
     $this->assertFalse($access->isAllowed());
     $this->assertTrue($access->isForbidden());
     $this->assertTrue($access->isCacheable());
     $this->assertSame(['cache_context.user'], $access->getCacheKeys());
-    $this->assertSame(['node' => [14031991 => 14031991]], $access->getCacheTags());
+    $this->assertSame(['node:14031991'], $access->getCacheTags());
     $this->assertSame('default', $access->getCacheBin());
     $this->assertSame(43200, $access->getCacheMaxAge());
 
diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php
index 690d302..5993669 100644
--- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php
@@ -107,7 +107,7 @@ public function testDestruct() {
       ->will($this->returnValue(FALSE));
     $this->cache->expects($this->once())
       ->method('set')
-      ->with('library_info', array('test' => $this->libraryData), Cache::PERMANENT, array('library_info' => array(TRUE)));
+      ->with('library_info', array('test' => $this->libraryData), Cache::PERMANENT, array('library_info'));
     $this->lock->expects($this->once())
       ->method('release')
       ->with($lock_key);
diff --git a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
index 8b1cc39..f977695 100644
--- a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
@@ -218,8 +218,8 @@ public function testDeleteAllPropagation() {
    */
   public function testDeleteTagsPropagation() {
     // Create two cache entries with the same tag and tag value.
-    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => 2));
-    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => 2));
+    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:2'));
+    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:2'));
     $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -229,7 +229,7 @@ public function testDeleteTagsPropagation() {
       'Two cache items were created in all backends.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $this->chain->deleteTags(array('test_tag' => 2));
+    $this->chain->deleteTags(array('test_tag:2'));
     $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -239,8 +239,8 @@ public function testDeleteTagsPropagation() {
       'Two caches removed from all backends after clearing a cache tag.');
 
     // Create two cache entries with the same tag and an array tag value.
-    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => array(1)));
-    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => array(1)));
+    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:1'));
+    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:1'));
     $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -250,7 +250,7 @@ public function testDeleteTagsPropagation() {
       'Two cache items were created in all backends.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $this->chain->deleteTags(array('test_tag' => array(1)));
+    $this->chain->deleteTags(array('test_tag:1'));
     $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -260,9 +260,9 @@ public function testDeleteTagsPropagation() {
       'Two caches removed from all backends after clearing a cache tag.');
 
     // Create three cache entries with a mix of tags and tag values.
-    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => array(1)));
-    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => array(2)));
-    $this->chain->set('test_cid_clear3', 'foo', Cache::PERMANENT, array('test_tag_foo' => array(3)));
+    $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:1'));
+    $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:2'));
+    $this->chain->set('test_cid_clear3', 'foo', Cache::PERMANENT, array('test_tag_foo:3'));
     $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->firstBackend->get('test_cid_clear3')
@@ -274,7 +274,7 @@ public function testDeleteTagsPropagation() {
       && $this->thirdBackend->get('test_cid_clear3'),
       'Three cached items were created in all backends.');
 
-    $this->chain->deleteTags(array('test_tag_foo' => array(3)));
+    $this->chain->deleteTags(array('test_tag_foo:3'));
     $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
index cc8012d..cf5b3f1 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
@@ -389,7 +389,7 @@ public function testUpdateCacheClear() {
   public function testUpdateCacheClearTags() {
     $key = $this->randomMachineName();
     $value = $this->randomMachineName();
-    $tags = array($this->randomMachineName() => TRUE);
+    $tags = array($this->randomMachineName());
     $this->collector = new CacheCollectorHelper($this->cid, $this->cache, $this->lock, $tags);
 
     // Set the data and request it.
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
new file mode 100644
index 0000000..612899d
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Cache\CacheTest.
+ */
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Cache\Cache
+ * @group Cache
+ */
+class CacheTest extends UnitTestCase {
+
+  /**
+   * Provides a list of cache tags arrays.
+   *
+   * @return array
+   */
+  public function validateTagsProvider() {
+    return [
+      [[], FALSE],
+      [['foo'], FALSE],
+      [['foo', 'bar'], FALSE],
+      [['foo', 'bar', 'llama:2001988', 'baz', 'llama:14031991'], FALSE],
+      [[FALSE], 'Cache tags must be strings, boolean given.'],
+      [[TRUE], 'Cache tags must be strings, boolean given.'],
+      [['foo', FALSE], 'Cache tags must be strings, boolean given.'],
+      [[NULL], 'Cache tags must be strings, NULL given.'],
+      [['foo', NULL], 'Cache tags must be strings, NULL given.'],
+      [[1337], 'Cache tags must be strings, integer given.'],
+      [['foo', 1337], 'Cache tags must be strings, integer given.'],
+      [[3.14], 'Cache tags must be strings, double given.'],
+      [['foo', 3.14], 'Cache tags must be strings, double given.'],
+      [[[]], 'Cache tags must be strings, array given.'],
+      [['foo', []], 'Cache tags must be strings, array given.'],
+      [['foo', ['bar']], 'Cache tags must be strings, array given.'],
+      [[new \stdClass()], 'Cache tags must be strings, object given.'],
+      [['foo', new \stdClass()], 'Cache tags must be strings, object given.'],
+    ];
+  }
+
+  /**
+   * @covers validateTags
+   *
+   * @dataProvider validateTagsProvider
+   */
+  public function testValidateTags(array $tags, $expected_exception_message) {
+    if ($expected_exception_message !== FALSE) {
+      $this->setExpectedException('LogicException', $expected_exception_message);
+    }
+    Cache::validateTags($tags);
+  }
+
+  /**
+   * Provides a list of pairs of cache tags arrays to be merged.
+   *
+   * @return array
+   */
+  public function mergeTagsProvider() {
+    return [
+      [[], [], []],
+      [['bar'], ['foo'], ['bar', 'foo']],
+      [['foo'], ['bar'], ['bar', 'foo']],
+      [['foo'], ['bar', 'foo'], ['bar', 'foo']],
+      [['foo'], ['foo', 'bar'], ['bar', 'foo']],
+      [['bar', 'foo'], ['foo', 'bar'], ['bar', 'foo']],
+      [['foo', 'bar'], ['foo', 'bar'], ['bar', 'foo']],
+    ];
+  }
+
+  /**
+   * @covers mergeTags
+   *
+   * @dataProvider mergeTagsProvider
+   */
+  public function testMergeTags(array $a, array $b, array $expected) {
+    $this->assertEquals($expected, Cache::mergeTags($a, $b));
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index 492d884..bdf3c07 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -355,7 +355,7 @@ public function testEnable() {
   public function testDisable() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
-      ->with(array($this->entityTypeId => array($this->id)));
+      ->with(array($this->entityTypeId . ':' . $this->id));
 
     $this->entity->setStatus(TRUE);
     $this->assertSame($this->entity, $this->entity->disable());
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index 44914b0..a343e9e 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -241,7 +241,7 @@ public function testSaveInsert(EntityInterface $entity) {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId . 's', // List cache tag.
       ));
 
     $this->configFactory->expects($this->exactly(2))
@@ -300,8 +300,8 @@ public function testSaveUpdate(EntityInterface $entity) {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
-        $this->entityTypeId => array('foo'), // Own cache tag.
+        $this->entityTypeId . 's', // List cache tag.
+        $this->entityTypeId . ':foo', // Own cache tag.
       ));
 
     $this->configFactory->expects($this->exactly(2))
@@ -360,8 +360,8 @@ public function testSaveRename(ConfigEntityInterface $entity) {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
-        $this->entityTypeId => array('bar'), // Own cache tag.
+        $this->entityTypeId . 's', // List cache tag.
+        $this->entityTypeId .':bar', // Own cache tag.
       ));
 
     $this->configFactory->expects($this->once())
@@ -493,7 +493,7 @@ public function testSaveNoMismatch() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId . 's', // List cache tag.
       ));
 
     $this->configFactory->expects($this->once())
@@ -726,8 +726,9 @@ public function testDelete() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
-        $this->entityTypeId => array('foo', 'bar'), // Own cache tag.
+        $this->entityTypeId . ':bar', // Own cache tag.
+        $this->entityTypeId . ':foo', // Own cache tag.
+        $this->entityTypeId . 's', // List cache tag.
       ));
 
     $this->configFactory->expects($this->exactly(2))
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index a0125c1..fa3678c 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -182,13 +182,13 @@ public function testClearCachedDefinitions() {
     $this->setUpEntityManager();
     $this->cache->expects($this->at(0))
       ->method('deleteTags')
-      ->with(array('entity_types' => TRUE));
+      ->with(array('entity_types'));
     $this->cache->expects($this->at(1))
       ->method('deleteTags')
-      ->with(array('entity_bundles' => TRUE));
+      ->with(array('entity_bundles'));
     $this->cache->expects($this->at(2))
       ->method('deleteTags')
-      ->with(array('entity_field_info' => TRUE));
+      ->with(array('entity_field_info'));
 
     $this->entityManager->clearCachedDefinitions();
   }
@@ -760,7 +760,7 @@ public function testClearCachedFieldDefinitions() {
     $this->setUpEntityManager();
     $this->cache->expects($this->once())
       ->method('deleteTags')
-      ->with(array('entity_field_info' => TRUE));
+      ->with(array('entity_field_info'));
     $this->typedDataManager->expects($this->once())
       ->method('clearCachedDefinitions');
 
@@ -776,7 +776,7 @@ public function testClearCachedBundles() {
     $this->setUpEntityManager();
     $this->cache->expects($this->once())
       ->method('deleteTags')
-      ->with(array('entity_bundles' => TRUE));
+      ->with(array('entity_bundles'));
 
     $this->entityManager->clearCachedBundles();
   }
@@ -862,13 +862,13 @@ public function testGetAllBundleInfo() {
       ->with("entity_bundle_info:en");
     $this->cache->expects($this->at(4))
       ->method('deleteTags')
-      ->with(array('entity_types' => TRUE));
+      ->with(array('entity_types'));
     $this->cache->expects($this->at(5))
       ->method('deleteTags')
-      ->with(array('entity_bundles' => TRUE));
+      ->with(array('entity_bundles'));
     $this->cache->expects($this->at(6))
       ->method('deleteTags')
-      ->with(array('entity_field_info' => TRUE));
+      ->with(array('entity_field_info'));
     $this->cache->expects($this->at(7))
       ->method('get')
       ->with("entity_bundle_info:en", FALSE)
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index 4913446..8b91074 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -392,13 +392,13 @@ public function testPostSave() {
     $this->cacheBackend->expects($this->at(0))
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId . 's', // List cache tag.
       ));
     $this->cacheBackend->expects($this->at(1))
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . 's' => TRUE, // List cache tag.
-        $this->entityTypeId => array($this->values['id']), // Own cache tag.
+        $this->entityTypeId . 's', // List cache tag.
+        $this->entityTypeId . ':' . $this->values['id'], // Own cache tag.
       ));
 
     // This method is internal, so check for errors on calling it only.
@@ -449,8 +449,8 @@ public function testPostDelete() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId => array($this->values['id']),
-        $this->entityTypeId . 's' => TRUE,
+        $this->entityTypeId . ':' . $this->values['id'],
+        $this->entityTypeId . 's',
       ));
     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 24b90d3..9aa4bf3 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -1234,7 +1234,7 @@ public function testLoadMultiplePersistentCacheMiss() {
       ->will($this->returnValue(array()));
     $this->cache->expects($this->once())
       ->method('set')
-      ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, array($this->entityTypeId . '_values' => TRUE, 'entity_field_info' => TRUE));
+      ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, array($this->entityTypeId . '_values', 'entity_field_info'));
 
     $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
       ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache))
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
index a01afea..1942fc3 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
@@ -355,7 +355,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re
     $this->requestStack->push($request);
     $this->moduleHandler = $module_handler;
     $this->alterInfo('menu_local_actions');
-    $this->setCacheBackend($cache_backend, 'local_action_plugins', array('local_action' => TRUE));
+    $this->setCacheBackend($cache_backend, 'local_action_plugins', array('local_action'));
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
index 8470a6f..baa7584 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
@@ -123,7 +123,7 @@ protected function getLocalTaskManager($module_dirs, $route_name, $route_params)
     $property->setValue($manager, $factory);
 
     $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
-    $manager->setCacheBackend($cache_backend, 'local_task.en', array('local_task' => 1));
+    $manager->setCacheBackend($cache_backend, 'local_task.en', array('local_task'));
 
     return $manager;
   }
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
index 2d6390c..e94660d 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
@@ -185,7 +185,7 @@ public function testGetLocalTaskForRouteWithEmptyCache() {
 
     $this->cacheBackend->expects($this->at(3))
       ->method('set')
-      ->with('local_task:en:menu_local_task_test_tasks_view', $expected_set, Cache::PERMANENT, array('local_task' => 1));
+      ->with('local_task:en:menu_local_task_test_tasks_view', $expected_set, Cache::PERMANENT, array('local_task'));
 
     $local_tasks = $this->manager->getLocalTasksForRoute('menu_local_task_test_tasks_view');
     $this->assertEquals($result, $local_tasks);
@@ -280,7 +280,7 @@ protected function setupLocalTaskManager() {
       ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
-    $this->manager->setCacheBackend($this->cacheBackend, 'local_task:en', array('local_task' => 1));
+    $this->manager->setCacheBackend($this->cacheBackend, 'local_task:en', array('local_task'));
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 274d46d..9d7f08f 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -196,7 +196,7 @@ public function testCacheClearWithTags() {
     $cache_backend
       ->expects($this->once())
       ->method('deleteTags')
-      ->with(array('tag' => TRUE));
+      ->with(array('tag'));
     $cache_backend
       ->expects($this->never())
       ->method('deleteMultiple');
@@ -204,7 +204,7 @@ public function testCacheClearWithTags() {
     $this->getContainerWithCacheBins($cache_backend);
 
     $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
-    $plugin_manager->setCacheBackend($cache_backend, $cid, array('tag' => TRUE));
+    $plugin_manager->setCacheBackend($cache_backend, $cid, array('tag'));
 
     $plugin_manager->clearCachedDefinitions();
   }
