 core/includes/pager.inc                            | 33 ++++++++------
 core/includes/theme.inc                            |  2 +-
 core/lib/Drupal/Core/Entity/EntityListBuilder.php  |  2 +-
 core/lib/Drupal/Core/Render/Element/Pager.php      | 53 ++++++++++++++++++++++
 .../src/Controller/AggregatorController.php        |  2 +-
 core/modules/aggregator/src/FeedViewBuilder.php    |  2 +-
 core/modules/comment/comment.module                | 20 --------
 .../comment/src/Form/CommentAdminOverview.php      |  2 +-
 .../FieldFormatter/CommentDefaultFormatter.php     |  2 +-
 .../dblog/src/Controller/DbLogController.php       |  4 +-
 core/modules/forum/forum.module                    |  4 +-
 core/modules/locale/src/Form/TranslateEditForm.php |  2 +-
 .../modules/path/src/Controller/PathController.php |  2 +-
 .../search/src/Controller/SearchController.php     |  2 +-
 .../src/Plugin/Search/SearchExtraTypeSearch.php    |  2 +-
 core/modules/system/src/Tests/Pager/PagerTest.php  |  7 ++-
 .../src/Controller/PagerTestController.php         | 14 +++++-
 core/modules/tracker/tracker.pages.inc             |  2 +-
 core/modules/user/src/UserListBuilder.php          |  2 +-
 core/modules/views/src/Plugin/views/pager/Full.php |  1 +
 core/modules/views/src/Plugin/views/pager/Mini.php |  1 +
 21 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/core/includes/pager.inc b/core/includes/pager.inc
index a2483ab..9904409 100644
--- a/core/includes/pager.inc
+++ b/core/includes/pager.inc
@@ -73,7 +73,7 @@ function pager_find_page($element = 0) {
  *   );
  *
  *   // Finally, display the pager controls, and return.
- *   $pager = array('#theme' => 'pager');
+ *   $pager = array('#type' => 'pager');
  *   $output .= drupal_render($pager);
  *   return $output;
  * @endcode
@@ -105,7 +105,7 @@ function pager_find_page($element = 0) {
  *   $output = drupal_render($search_results);
  *
  *   // Finally, display the pager controls, and return.
- *   $pager = array('#theme' => 'pager');
+ *   $pager = array('#type' => 'pager');
  *   $output .= drupal_render($pager);
  *   return $output;
  * @endcode
@@ -158,23 +158,30 @@ function pager_get_query_parameters() {
  *
  * Default template: pager.html.twig.
  *
- * Menu callbacks that display paged query results should call _theme('pager')
+ * Menu callbacks that display paged query results should use #type => pager
  * to retrieve a pager control so that users can view other results. Format a
  * list of nearby pages with additional query results.
  *
  * @param array $variables
  *   An associative array containing:
- *   - tags: An array of labels for the controls in the pager.
- *   - element: An optional integer to distinguish between multiple pagers on
- *     one page.
- *   - parameters: An associative array of query string parameters to append to
- *     the pager links.
- *   - quantity: The number of pages in the list.
+ *   - pager: A render element containing:
+ *     - #tags: An array of labels for the controls in the pager.
+ *     - #element: An optional integer to distinguish between multiple pagers on
+ *       one page.
+ *     - #parameters: An associative array of query string parameters to append to
+ *       the pager links.
+ *     - #quantity: The number of pages in the list.
+ *
+ * @throws \RuntimeException
+ *   Whenever using #theme => pager instead of #type => pager.
  */
 function template_preprocess_pager(&$variables) {
-  $element = $variables['element'];
-  $parameters = $variables['parameters'];
-  $quantity = $variables['quantity'];
+  if (!isset($variables['pager']['#type'])) {
+    throw new \RuntimeException("You must use '#type' => 'pager', not '#theme' => 'pager'.");
+  }
+  $element = $variables['pager']['#element'];
+  $parameters = $variables['pager']['#parameters'];
+  $quantity = $variables['pager']['#quantity'];
   global $pager_page_array, $pager_total;
 
   // Nothing to do if there is only one page.
@@ -182,7 +189,7 @@ function template_preprocess_pager(&$variables) {
     return;
   }
 
-  $tags = $variables['tags'];
+  $tags = $variables['pager']['#tags'];
 
   // Calculate various markers within this pager piece:
   // Middle is used to "center" pages around the current page.
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index b6078ed..1ecd5e8 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1861,7 +1861,7 @@ function drupal_common_theme() {
     ),
     // From pager.inc.
     'pager' => array(
-      'variables' => array('tags' => array(), 'element' => 0, 'parameters' => array(), 'quantity' => 9),
+      'render element' => 'pager',
     ),
     // From menu.inc.
     'menu' => array(
diff --git a/core/lib/Drupal/Core/Entity/EntityListBuilder.php b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
index 7137951..7e4448b 100644
--- a/core/lib/Drupal/Core/Entity/EntityListBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
@@ -223,7 +223,7 @@ public function render() {
       }
     }
     $build['pager'] = array(
-      '#theme' => 'pager',
+      '#type' => 'pager',
     );
     return $build;
   }
diff --git a/core/lib/Drupal/Core/Render/Element/Pager.php b/core/lib/Drupal/Core/Render/Element/Pager.php
new file mode 100644
index 0000000..006064a
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Pager.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\Pager.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Core\Render\Element;
+
+/**
+ * Provides a render element for a pager.
+ *
+ * @RenderElement("pager")
+ */
+class Pager extends RenderElement{
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    return [
+      '#pre_render' => [
+        get_class($this) . '::preRenderPager',
+      ],
+      '#theme' => 'pager',
+      // The pager ID, to distinguish between multiple pagers on the same page.
+      '#element' => 0,
+      // An associative array of query string parameters to append to the pager
+      // links.
+      '#parameters' => [],
+      // The number of pages in the list.
+      '#quantity' => 9,
+      // An array of labels for the controls in the pager.
+      '#tags' => [],
+    ];
+  }
+
+  /**
+   * #pre_render callback to associate the appropriate cache context.
+   *
+   * @param array $pager
+   *   A renderable array of #type => pager.
+   *
+   * @return array
+   */
+  public static function preRenderPager(array $pager) {
+    $pager['#cache']['contexts'][] = 'pager:' . $pager['#element'];
+    return $pager;
+  }
+
+}
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index f66ae0f..937b40c 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -82,7 +82,7 @@ protected function buildPageList(array $items, $feed_source = '') {
     if ($items) {
       $build['items'] = $this->entityManager()->getViewBuilder('aggregator_item')
         ->viewMultiple($items, 'default');
-      $build['pager'] = array('#theme' => 'pager');
+      $build['pager'] = array('#type' => 'pager');
     }
     return $build;
   }
diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php
index e05f7c0..89acc4a 100644
--- a/core/modules/aggregator/src/FeedViewBuilder.php
+++ b/core/modules/aggregator/src/FeedViewBuilder.php
@@ -73,7 +73,7 @@ public function buildComponents(array &$build, array $entities, array $displays,
 
         if ($view_mode == 'full') {
           // Also add the pager.
-          $build[$id]['pager'] = array('#theme' => 'pager');
+          $build[$id]['pager'] = array('#type' => 'pager');
         }
       }
 
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 446c856..27977cf 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -196,26 +196,6 @@ function comment_field_config_delete(FieldConfigInterface $field) {
 }
 
 /**
- * Implements hook_entity_build_defaults_alter().
- *
- * @todo Remove this hook implementation in https://www.drupal.org/node/2433599
- */
-function comment_entity_build_defaults_alter(array &$build, EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
-  // Get the corresponding display settings.
-  $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
-  // Add the comment page number to the cache key if render caching is enabled.
-  if (isset($build['#cache']) && isset($build['#cache']['keys'])) {
-    foreach ($entity->getFieldDefinitions() as $field_name => $definition) {
-      if ($definition->getType() === 'comment' && ($display_options = $display->getComponent($field_name))) {
-        $pager_id = $display_options['settings']['pager_id'];
-        $build['#cache']['contexts'][] = 'pager:' . $pager_id;
-      }
-    }
-  }
-  return $build;
-}
-
-/**
  * Implements hook_node_links_alter().
  */
 function comment_node_links_alter(array &$node_links, NodeInterface $node, array &$context) {
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index c241841..8db0ed3 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -240,7 +240,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
       '#empty' => $this->t('No comments available.'),
     );
 
-    $form['pager'] = array('#theme' => 'pager');
+    $form['pager'] = array('#type' => 'pager');
 
     return $form;
   }
diff --git a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
index be747b4..75b85f0 100644
--- a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
+++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
@@ -170,7 +170,7 @@ public function viewElements(FieldItemListInterface $items) {
           if ($comments) {
             comment_prepare_thread($comments);
             $build = $this->viewBuilder->viewMultiple($comments);
-            $build['pager']['#theme'] = 'pager';
+            $build['pager']['#type'] = 'pager';
             if ($this->getSetting('pager_id')) {
               $build['pager']['#element'] = $this->getSetting('pager_id');
             }
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 44828e4..8022123 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -214,7 +214,7 @@ public function overview() {
         'library' => array('dblog/drupal.dblog'),
       ),
     );
-    $build['dblog_pager'] = array('#theme' => 'pager');
+    $build['dblog_pager'] = array('#type' => 'pager');
 
     return $build;
 
@@ -409,7 +409,7 @@ public function topLogMessages($type) {
         'library' => array('dblog/drupal.dblog'),
       ),
     );
-    $build['dblog_top_pager'] = array('#theme' => 'pager');
+    $build['dblog_top_pager'] = array('#type' => 'pager');
 
     return $build;
   }
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 845173c..2cf0c53 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -515,7 +515,7 @@ function template_preprocess_forums(&$variables) {
 
       $variables['topics'] = $table;
       $variables['topics_pager'] = array(
-        '#theme' => 'pager',
+        '#type' => 'pager',
       );
     }
   }
@@ -567,7 +567,7 @@ function template_preprocess_forum_list(&$variables) {
   }
 
   $variables['pager'] = array(
-   '#theme' => 'pager',
+   '#type' => 'pager',
   );
 
   // Give meaning to $tid for themers. $tid actually stands for term ID.
diff --git a/core/modules/locale/src/Form/TranslateEditForm.php b/core/modules/locale/src/Form/TranslateEditForm.php
index 454a65b..e85a09e 100644
--- a/core/modules/locale/src/Form/TranslateEditForm.php
+++ b/core/modules/locale/src/Form/TranslateEditForm.php
@@ -161,7 +161,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         );
       }
     }
-    $form['pager']['#theme'] = 'pager';
+    $form['pager']['#type'] = 'pager';
     return $form;
   }
 
diff --git a/core/modules/path/src/Controller/PathController.php b/core/modules/path/src/Controller/PathController.php
index f21c6b7..8756270 100644
--- a/core/modules/path/src/Controller/PathController.php
+++ b/core/modules/path/src/Controller/PathController.php
@@ -130,7 +130,7 @@ public function adminOverview(Request $request) {
       '#rows' => $rows,
       '#empty' => $this->t('No URL aliases available. <a href="@link">Add URL alias</a>.', array('@link' => $this->url('path.admin_add'))),
     );
-    $build['path_pager'] = array('#theme' => 'pager');
+    $build['path_pager'] = array('#type' => 'pager');
 
     return $build;
   }
diff --git a/core/modules/search/src/Controller/SearchController.php b/core/modules/search/src/Controller/SearchController.php
index f3e0e06..db9efe9 100644
--- a/core/modules/search/src/Controller/SearchController.php
+++ b/core/modules/search/src/Controller/SearchController.php
@@ -134,7 +134,7 @@ public function view(Request $request, SearchPageInterface $entity) {
     );
 
     $build['pager'] = array(
-      '#theme' => 'pager',
+      '#type' => 'pager',
     );
 
     $build['#attached']['library'][] = 'search/drupal.search.results';
diff --git a/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php b/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
index fc358d0..5c97934 100644
--- a/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
+++ b/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
@@ -84,7 +84,7 @@ public function buildResults() {
       );
     }
     $pager = array(
-      '#theme' => 'pager',
+      '#type' => 'pager',
     );
     $output['suffix']['#markup'] = '</ol>' . drupal_render($pager);
 
diff --git a/core/modules/system/src/Tests/Pager/PagerTest.php b/core/modules/system/src/Tests/Pager/PagerTest.php
index 6595de3..e3269fe 100644
--- a/core/modules/system/src/Tests/Pager/PagerTest.php
+++ b/core/modules/system/src/Tests/Pager/PagerTest.php
@@ -63,22 +63,25 @@ function testActiveClass() {
   }
 
   /**
-   * Test proper functioning of the query parameters.
+   * Test proper functioning of the query parameters and the pager cache context.
    */
-  protected function testPagerQueryParameters() {
+  protected function testPagerQueryParametersAndCacheContext() {
     // First page.
     $this->drupalGet('pager-test/query-parameters');
     $this->assertText(t('Pager calls: 0'), 'Initial call to pager shows 0 calls.');
+    $this->assertText('pager.0.0');
 
     // Go to last page, the count of pager calls need to go to 1.
     $elements = $this->xpath('//li[contains(@class, :class)]/a', array(':class' => 'pager__item--last'));
     $this->drupalGet($GLOBALS['base_root'] . $elements[0]['href'], array('external' => TRUE));
     $this->assertText(t('Pager calls: 1'), 'First link call to pager shows 1 calls.');
+    $this->assertText('pager.0.60');
 
     // Go back to first page, the count of pager calls need to go to 2.
     $elements = $this->xpath('//li[contains(@class, :class)]/a', array(':class' => 'pager__item--first'));
     $this->drupalGet($GLOBALS['base_root'] . $elements[0]['href'], array('external' => TRUE));
     $this->assertText(t('Pager calls: 2'), 'Second link call to pager shows 2 calls.');
+    $this->assertText('pager.0.0');
   }
 
   /**
diff --git a/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php b/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
index 3a22070..b277c75 100644
--- a/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
+++ b/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
@@ -52,13 +52,25 @@ public function queryParameters() {
 
     // Pager.
     $build['pager_pager_0'] = array(
-      '#theme' => 'pager',
+      '#type' => 'pager',
       '#element' => 0,
       '#parameters' => array(
         'pager_calls' => ++$pager_calls,
       ),
+      '#pre_render' => [
+        'Drupal\pager_test\Controller\PagerTestController::showPagerCacheContext',
+      ]
     );
 
     return $build;
   }
+
+  /**
+   * #pre_render callback for #type => pager that shows the pager cache context.
+   */
+  public static function showPagerCacheContext(array $pager) {
+    drupal_set_message(\Drupal::service('cache_contexts')->convertTokensToKeys(['pager:' . $pager['#element']])[0]);
+    return $pager;
+  }
+
 }
diff --git a/core/modules/tracker/tracker.pages.inc b/core/modules/tracker/tracker.pages.inc
index c0077a3..2b32ed3 100644
--- a/core/modules/tracker/tracker.pages.inc
+++ b/core/modules/tracker/tracker.pages.inc
@@ -128,7 +128,7 @@ function tracker_page($account = NULL) {
     '#empty' => t('No content available.'),
   );
   $page['pager'] = array(
-    '#theme' => 'pager',
+    '#type' => 'pager',
     '#weight' => 10,
   );
   $page['#sorted'] = TRUE;
diff --git a/core/modules/user/src/UserListBuilder.php b/core/modules/user/src/UserListBuilder.php
index 667ba4f..c9672a9 100644
--- a/core/modules/user/src/UserListBuilder.php
+++ b/core/modules/user/src/UserListBuilder.php
@@ -164,7 +164,7 @@ public function getOperations(EntityInterface $entity) {
   public function render() {
     $build['accounts'] = parent::render();
     $build['accounts']['#empty'] = $this->t('No people available.');
-    $build['pager']['#theme'] = 'pager';
+    $build['pager']['#type'] = 'pager';
     return $build;
   }
 
diff --git a/core/modules/views/src/Plugin/views/pager/Full.php b/core/modules/views/src/Plugin/views/pager/Full.php
index 43a0ca7..7e5d3eb 100644
--- a/core/modules/views/src/Plugin/views/pager/Full.php
+++ b/core/modules/views/src/Plugin/views/pager/Full.php
@@ -91,6 +91,7 @@ public function render($input) {
       4 => $this->options['tags']['last'],
     );
     return array(
+      '#type' => 'pager',
       '#theme' => $this->themeFunctions(),
       '#tags' => $tags,
       '#element' => $this->options['id'],
diff --git a/core/modules/views/src/Plugin/views/pager/Mini.php b/core/modules/views/src/Plugin/views/pager/Mini.php
index b08c9d2..bb5c563 100644
--- a/core/modules/views/src/Plugin/views/pager/Mini.php
+++ b/core/modules/views/src/Plugin/views/pager/Mini.php
@@ -99,6 +99,7 @@ public function render($input) {
       3 => $this->options['tags']['next'],
     );
     return array(
+      '#type' => 'pager',
       '#theme' => $this->themeFunctions(),
       '#tags' => $tags,
       '#element' => $this->options['id'],
