diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index 31df6b0..240aae1 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\Component\Utility;
+use Drupal\Core\Render\Renderer;
 
 /**
  * Manages known safe strings for rendering at the theme layer.
@@ -241,6 +242,7 @@ public static function format($string, array $args) {
             $safe = FALSE;
           }
       }
+      $args[$key] = Renderer::toScalar($args[$key]);
     }
 
     $output = strtr($string, $args);
diff --git a/core/lib/Drupal/Component/Utility/SafeStringInterface.php b/core/lib/Drupal/Component/Utility/SafeStringInterface.php
index 136dd37..ac251fa 100644
--- a/core/lib/Drupal/Component/Utility/SafeStringInterface.php
+++ b/core/lib/Drupal/Component/Utility/SafeStringInterface.php
@@ -43,4 +43,14 @@
    */
   public function __toString();
 
+  /**
+   * Returns a safe string.
+   *
+   * Same as __toString but with less fatals.
+   *
+   * @return string
+   *   The safe string.
+   */
+  public function toString();
+
 }
diff --git a/core/lib/Drupal/Component/Utility/SafeStringTrait.php b/core/lib/Drupal/Component/Utility/SafeStringTrait.php
index a91e44b..29ddf21 100644
--- a/core/lib/Drupal/Component/Utility/SafeStringTrait.php
+++ b/core/lib/Drupal/Component/Utility/SafeStringTrait.php
@@ -54,6 +54,16 @@ public static function create($string) {
    *   The safe string content.
    */
   public function __toString() {
+    return $this->toString();
+  }
+
+  /**
+   * Returns the string version of the SafeString object.
+   *
+   * @return string
+   *   The safe string content.
+   */
+  public function toString() {
     return $this->string;
   }
 
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index f2be9ce..93d73b9 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Render;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\SafeStringInterface;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Access\AccessResultInterface;
@@ -452,7 +453,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // same process as Renderer::render() but is inlined for speed.
     if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) {
       foreach ($children as $key) {
-        $elements['#children'] .= $this->doRender($elements[$key]);
+        $elements['#children'] .= $this->doRender(static::toScalar($elements[$key]));
       }
       $elements['#children'] = SafeString::create($elements['#children']);
     }
@@ -465,7 +466,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // required. Eventually #theme_wrappers will expect both #markup and
     // #children to be a single string as #children.
     if (!$theme_is_implemented && isset($elements['#markup'])) {
-      $elements['#children'] = SafeString::create($elements['#markup'] . $elements['#children']);
+      $elements['#children'] = SafeString::create(static::toScalar($elements['#markup']) . static::toScalar($elements['#children']));
     }
 
     // Let the theme functions in #theme_wrappers add markup around the rendered
@@ -515,10 +516,10 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // with how render cached output gets stored. This ensures that placeholder
     // replacement logic gets the same data to work with, no matter if #cache is
     // disabled, #cache is enabled, there is a cache hit or miss.
-    $prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
-    $suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
+    $prefix = isset($elements['#prefix']) ? static::toScalar($this->xssFilterAdminIfUnsafe($elements['#prefix'])) : '';
+    $suffix = isset($elements['#suffix']) ? static::toScalar($this->xssFilterAdminIfUnsafe($elements['#suffix'])) : '';
 
-    $elements['#markup'] = SafeString::create($prefix . $elements['#children'] . $suffix);
+    $elements['#markup'] = SafeString::create($prefix . static::toScalar($elements['#children']) . $suffix);
 
     // We've rendered this element (and its subtree!), now update the context.
     $context->update($elements);
@@ -756,4 +757,48 @@ protected function xssFilterAdminIfUnsafe($string) {
     return SafeString::create($string);
   }
 
+  /**
+   * Convert an argument to string.
+   *
+   * @param $arg
+   *
+   * @return int|null|string
+   * @throws \Exception
+   */
+  public static function toScalar($arg) {
+    // Check for a numeric zero int or float.
+    if ($arg === 0 || $arg === 0.0) {
+      return 0;
+    }
+
+    // Return early for NULL and empty arrays.
+    if ($arg == NULL) {
+      return NULL;
+    }
+
+    // Optimize for strings as it is likely they come from the escape filter.
+    if (is_scalar($arg)) {
+      return $arg;
+    }
+
+    if (is_object($arg)) {
+      if ($arg instanceof SafeStringInterface) {
+        return $arg->toString();
+      }
+      if (method_exists($arg, '__toString')) {
+        return (string) $arg;
+      }
+      // You can't throw exceptions in the magic PHP __toString methods, see
+      // http://php.net/manual/en/language.oop5.magic.php#object.tostring so
+      // we also support a toString method.
+      elseif (method_exists($arg, 'toString')) {
+        return $arg->toString();
+      }
+      else {
+        throw new \Exception('Object of type "' . get_class($arg) . '" cannot be printed.');
+      }
+    }
+    throw new \Exception('Scalar conversion failed.');
+  }
+
 }
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index 6bf591a..ccb5c9b 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -98,6 +98,13 @@ public function getOptions() {
    * Implements the magic __toString() method.
    */
   public function __toString() {
+    return $this->toString();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function toString() {
     return $this->render();
   }
 
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index 8fe6788..efe6df7 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -253,6 +253,13 @@ public function hasClass($class) {
    * Implements the magic __toString() method.
    */
   public function __toString() {
+    return $this->toString();
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function toString() {
     $return = '';
     /** @var \Drupal\Core\Template\AttributeValueBase $value */
     foreach ($this->storage as $name => $value) {
diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 482c888..631bba6 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -14,6 +14,7 @@
 
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\SafeStringInterface;
+use Drupal\Core\Render\Renderer;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
@@ -470,39 +471,7 @@ public function escapeFilter(\Twig_Environment $env, $arg, $strategy = 'html', $
    * @see TwigNodeVisitor
    */
   public function renderVar($arg) {
-    // Check for a numeric zero int or float.
-    if ($arg === 0 || $arg === 0.0) {
-      return 0;
-    }
-
-    // Return early for NULL and empty arrays.
-    if ($arg == NULL) {
-      return NULL;
-    }
-
-    // Optimize for strings as it is likely they come from the escape filter.
-    if (is_string($arg)) {
-      return $arg;
-    }
-
-    if (is_scalar($arg)) {
-      return $arg;
-    }
-
-    if (is_object($arg)) {
-      if (method_exists($arg, '__toString')) {
-        return (string) $arg;
-      }
-      // You can't throw exceptions in the magic PHP __toString methods, see
-      // http://php.net/manual/en/language.oop5.magic.php#object.tostring so
-      // we also support a toString method.
-      elseif (method_exists($arg, 'toString')) {
-        return $arg->toString();
-      }
-      else {
-        throw new \Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
-      }
-    }
+    $arg = Renderer::toScalar($arg);
 
     // This is a render array, with special simple cases already handled.
     // Early return if this element was pre-rendered (no need to re-render).
diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
index 1278a6d..5378260 100644
--- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
@@ -245,6 +245,10 @@ public function __toString() {
     return $this->string;
   }
 
+  public function toString() {
+    return $this->string;
+  }
+
   public static function create($string) {
     $safe_string = new static($string);
     return $safe_string;
