 core/lib/Drupal/Core/Access/AccessManager.php      |  5 ++--
 core/modules/link/src/LinkItemInterface.php        |  7 ++---
 .../link/src/Plugin/Field/FieldType/LinkItem.php   | 17 ++---------
 .../src/Plugin/Field/FieldWidget/LinkWidget.php    | 22 +++++++++++----
 .../Validation/Constraint/LinkTypeConstraint.php   | 33 ++++++++++++++--------
 5 files changed, 47 insertions(+), 37 deletions(-)

diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php
index a59f697..3cd659d 100644
--- a/core/lib/Drupal/Core/Access/AccessManager.php
+++ b/core/lib/Drupal/Core/Access/AccessManager.php
@@ -87,8 +87,9 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun
     try {
       $route = $this->routeProvider->getRouteByName($route_name, $parameters);
 
-      // ParamConverterManager relies on the route object being available
-      // from the parameters array.
+      // ParamConverterManager relies on the route name and object being
+      // available from the parameters array.
+      $parameters[RouteObjectInterface::ROUTE_NAME] = $route_name;
       $parameters[RouteObjectInterface::ROUTE_OBJECT] = $route;
       $upcasted_parameters = $this->paramConverterManager->convert($parameters + $route->getDefaults());
 
diff --git a/core/modules/link/src/LinkItemInterface.php b/core/modules/link/src/LinkItemInterface.php
index cd3bce2..f6a48f0 100644
--- a/core/modules/link/src/LinkItemInterface.php
+++ b/core/modules/link/src/LinkItemInterface.php
@@ -40,11 +40,8 @@ public function isExternal();
   /**
    * Gets the URL object.
    *
-   * @return \Drupal\Core\Url|false
-   *   Returns an Url object if any of the following are true:
-   *   - The URI is external.
-   *   - The URI is internal and valid.
-   *   Otherwise, FALSE is returned.
+   * @return \Drupal\Core\Url
+   *   Returns an Url object.
    */
   public function getUrl();
 
diff --git a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
index 8af3c46..66433c7 100644
--- a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\MapDataDefinition;
+use Drupal\Core\Url;
 use Drupal\link\LinkItemInterface;
 
 /**
@@ -164,21 +165,9 @@ public static function mainPropertyName() {
 
   /**
    * {@inheritdoc}
-   *
-   * @todo Remove the $access_check parameter and replace all logic in the
-   *    function body with a call to Url::fromUri() in
-   *    https://www.drupal.org/node/2416987.
    */
-  public function getUrl($access_check = FALSE) {
-    $uri = $this->uri;
-    $scheme = parse_url($uri, PHP_URL_SCHEME);
-    if ($scheme === 'user-path') {
-      $uri_reference = explode(':', $uri, 2)[1];
-    }
-    else {
-      $uri_reference = $uri;
-    }
-    return $access_check ? \Drupal::pathValidator()->getUrlIfValid($uri_reference) : \Drupal::pathValidator()->getUrlIfValidWithoutAccessCheck($uri_reference);
+  public function getUrl() {
+    return Url::fromUri($this->uri);
   }
 
   /**
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 3230a41..ec3746b 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\link\LinkItemInterface;
 use Symfony\Component\Validator\ConstraintViolation;
 use Symfony\Component\Validator\ConstraintViolationInterface;
@@ -60,6 +61,18 @@ protected function getUriAsDisplayableString($uri) {
   }
 
   /**
+   * This link widget disallows saving URIs inaccessible to the current user.
+   */
+  public function elementValidate($element, FormStateInterface $form_state, $form) {
+    $uri = $element['#value'];
+    if (!empty($uri)) {
+      if (!Url::fromUri($uri)->access()) {
+        $form_state->setError($element, $this->t('The URL %uri is not valid.', ['%uri' => $this->getUriAsDisplayableString($uri)]));
+      }
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
@@ -73,9 +86,8 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       // The current field value could have been entered by a different user.
       // However, if it is inaccessible to the current user, do not display it
       // to them.
-      // @todo Revisit this access requirement in
-      //   https://www.drupal.org/node/2416987.
-      '#default_value' => $item->getUrl(TRUE) ? $this->getUriAsDisplayableString($item->uri) : NULL,
+      '#default_value' => (!$item->isEmpty() && $item->getUrl()->access()) ? $this->getUriAsDisplayableString($item->uri) : NULL,
+      '#element_validate' => array(array($this, 'elementValidate')),
       '#maxlength' => 2048,
       '#required' => $element['#required'],
     );
@@ -240,7 +252,7 @@ public function massageFormValues(array $values, array $form, FormStateInterface
   /**
    * {@inheritdoc}
    *
-   * Override the '%url' message parameter, to ensure that 'user-path:' URIs
+   * Override the '%uri' message parameter, to ensure that 'user-path:' URIs
    * show a validation error message that doesn't mention that scheme.
    */
   public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
@@ -248,7 +260,7 @@ public function flagErrors(FieldItemListInterface $items, ConstraintViolationLis
     foreach ($violations as $offset => $violation) {
       $parameters = $violation->getMessageParameters();
       if (isset($parameters['%url'])) {
-        $parameters['%url'] = $this->getUriAsDisplayableString($parameters['%url']);
+        $parameters['%uri'] = $this->getUriAsDisplayableString($parameters['%uri']);
         $violations->set($offset, new ConstraintViolation(
           $this->t($violation->getMessageTemplate(), $parameters),
           $violation->getMessageTemplate(),
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
index 22b1942..ca5ca43 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
@@ -22,7 +22,7 @@
  */
 class LinkTypeConstraint extends Constraint implements ConstraintValidatorInterface {
 
-  public $message = 'The URL %url is not valid.';
+  public $message = 'The URL %uri is not valid.';
 
   /**
    * @var \Symfony\Component\Validator\ExecutionContextInterface
@@ -48,23 +48,34 @@ public function validatedBy() {
    */
   public function validate($value, Constraint $constraint) {
     if (isset($value)) {
-      $url_is_valid = FALSE;
+      $uri_is_valid = TRUE;
+
       /** @var $link_item \Drupal\link\LinkItemInterface */
       $link_item = $value;
       $link_type = $link_item->getFieldDefinition()->getSetting('link_type');
-      $url = $link_item->getUrl(TRUE);
 
-      if ($url) {
-        $url_is_valid = TRUE;
-        if ($url->isExternal() && !($link_type & LinkItemInterface::LINK_EXTERNAL)) {
-          $url_is_valid = FALSE;
+      // Try to resolve the given URI to a URL. It may fail if it's schemeless.
+      try {
+        $url = $link_item->getUrl();
+      }
+      catch (\InvalidArgumentException $e) {
+        $uri_is_valid = FALSE;
+      }
+
+      // If the link field doesn't support both internal and external links,
+      // check whether the URL (a resolved URI) is in fact violating either
+      // restriction.
+      if ($uri_is_valid && !($link_type & LinkItemInterface::LINK_GENERIC)) {
+        if (!($link_type & LinkItemInterface::LINK_EXTERNAL) && $url->isExternal()) {
+          $uri_is_valid = FALSE;
         }
-        if (!$url->isExternal() && !($link_type & LinkItemInterface::LINK_INTERNAL)) {
-          $url_is_valid = FALSE;
+        if (!($link_type & LinkItemInterface::LINK_INTERNAL) && !$url->isExternal()) {
+          $uri_is_valid = FALSE;
         }
       }
-      if (!$url_is_valid) {
-        $this->context->addViolation($this->message, array('%url' => $link_item->uri));
+
+      if (!$uri_is_valid) {
+        $this->context->addViolation($this->message, array('%uri' => $link_item->uri));
       }
     }
   }
