diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index c0607a102a90e..f89b5486c3470 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -30,10 +30,10 @@ use super::*;
 
 macro_rules! p {
     (@$lit:literal) => {
-        write!(scoped_cx!(), $lit)?
+        scoped_cx!().write_fmt(format_args!($lit))?
     };
     (@write($($data:expr),+)) => {
-        write!(scoped_cx!(), $($data),+)?
+        scoped_cx!().write_fmt(format_args!($($data),+))?
     };
     (@print($x:expr)) => {
         scoped_cx!() = $x.print(scoped_cx!())?
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index fd96e1ff77d53..3949b836c37a7 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -515,9 +515,39 @@ macro_rules! r#try {
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "write_macro")]
+#[allow_internal_unstable(autoref)]
 macro_rules! write {
+    // Retrocompat workaround to support two phased borrows:
+    // when `$dst` is made of chaining `.field` (or `.idx`) accesses to a local,
+    // it is guaranteed to be a *place* and thus there is no question of
+    // late-dropping anything inside it.
+    //
+    // Whilst this won't cover *all* the possible syntaxes for places (it
+    // won't handle places with `[…]`-indexing inside them (see
+    // https://github.com/rust-lang/rust/pull/100202#pullrequestreview-1064499226)
+    // qualified paths to `static mut`s, nor macro invocations), it ought to
+    // cover the vast majority of uses in practice, especially regarding
+    // retro-compatibility.
+    (
+        $dst_place:ident $(. $field_or_idx:tt)* ,
+        $($arg:tt)*
+    ) => ({
+        let result =
+            $dst_place $(. $field_or_idx )*
+                .write_fmt($crate::format_args!($($arg)*))
+        ;
+        result
+    });
+
+    // default case: early-dropped `format_args!($($args)*)` while keeping
+    // `$dst` late-dropped.
     ($dst:expr, $($arg:tt)*) => {
-        $dst.write_fmt($crate::format_args!($($arg)*))
+        match $crate::ops::autoref::autoref_mut!($dst) {
+            mut _dst => {
+                let result = _dst.write_fmt($crate::format_args!($($arg)*));
+                result
+            }
+        }
     };
 }
 
@@ -549,13 +579,30 @@ macro_rules! write {
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "writeln_macro")]
-#[allow_internal_unstable(format_args_nl)]
+#[allow_internal_unstable(autoref, format_args_nl)]
 macro_rules! writeln {
     ($dst:expr $(,)?) => {
         $crate::write!($dst, "\n")
     };
+
+    (
+        $dst_place:ident $(. $field_or_idx:tt)* ,
+        $($arg:tt)*
+    ) => ({
+        let result =
+            $dst_place $(. $field_or_idx)*
+                .write_fmt($crate::format_args_nl!($($arg)*))
+        ;
+        result
+    });
+
     ($dst:expr, $($arg:tt)*) => {
-        $dst.write_fmt($crate::format_args_nl!($($arg)*))
+        match $crate::ops::autoref::autoref_mut!($dst) {
+            mut _dst => {
+                let result = _dst.write_fmt($crate::format_args_nl!($($arg)*));
+                result
+            }
+        }
     };
 }
 
diff --git a/library/core/src/ops/autoref.rs b/library/core/src/ops/autoref.rs
new file mode 100644
index 0000000000000..0e07277986c46
--- /dev/null
+++ b/library/core/src/ops/autoref.rs
@@ -0,0 +1,46 @@
+#![allow(missing_docs, missing_debug_implementations)]
+// Poor man's `k#autoref` operator.
+//
+// See https://github.com/rust-lang/rust/issues/99684
+// and https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Desired.20behavior.20of.20.60write!.60.20is.20unimplementable
+// for some more context about this idea.
+//
+// Right now we polyfill this idea, by reducing support to `&mut`-autoref-only
+// (effectively "just" preventing an unnecessary `&mut` level of indirection
+// from being applied for a thing already behind a `&mut …`), which happens to
+// work for `&mut`-based `.write_fmt()` methods, and some cases of `&`-based
+// `.write_fmt()` —the whole duck-typed design / API of `write!` is asking for
+// trouble—, but won't work for a `self`-based `.write_fmt()`, as pointed out
+// here: https://github.com/rust-lang/rust/pull/100202#pullrequestreview-1064499226
+//
+// Finally, in order to reduce the chances of name conflicts as much as
+// possible, the method name is a bit mangled, and to prevent usage of this
+// method in stable-rust, an unstable const generic parameter that needs to be
+// turbofished is added to it as well.
+
+/// The unstable const generic parameter achieving the "unstable seal" effect.
+#[unstable(feature = "autoref", issue = "none")]
+#[derive(Eq, PartialEq)]
+pub struct UnstableMethodSeal;
+
+#[unstable(feature = "autoref", issue = "none")]
+pub trait AutoRef {
+    #[unstable(feature = "autoref", issue = "none")]
+    #[inline(always)]
+    fn __rustc_unstable_auto_ref_mut_helper<const _SEAL: UnstableMethodSeal>(
+        &mut self,
+    ) -> &mut Self {
+        self
+    }
+}
+
+#[unstable(feature = "autoref", issue = "none")]
+impl<T: ?Sized> AutoRef for T {}
+
+#[unstable(feature = "autoref", issue = "none")]
+#[allow_internal_unstable(autoref)]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro autoref_mut($x:expr) {{
+    use $crate::ops::autoref::AutoRef as _;
+    $x.__rustc_unstable_auto_ref_mut_helper::<{ $crate::ops::autoref::UnstableMethodSeal }>()
+}}
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index a5e5b13b33674..36eced1bfb0de 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -139,6 +139,9 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 mod arith;
+#[doc(hidden)]
+#[unstable(feature = "autoref", issue = "none")]
+pub mod autoref;
 mod bit;
 mod control_flow;
 mod deref;
diff --git a/src/test/ui/macros/format-args-temporaries-in-write.rs b/src/test/ui/macros/format-args-temporaries-in-write.rs
index 339ccbc33ac98..f2e96fcd98045 100644
--- a/src/test/ui/macros/format-args-temporaries-in-write.rs
+++ b/src/test/ui/macros/format-args-temporaries-in-write.rs
@@ -1,50 +1,53 @@
-// check-fail
+// check-pass
+#![crate_type = "lib"]
 
 use std::fmt::{self, Display};
 
 struct Mutex;
 
 impl Mutex {
-    fn lock(&self) -> MutexGuard {
-        MutexGuard(self)
+    /// Dependent item with (potential) drop glue to disable NLL.
+    fn lock(&self) -> impl '_ + Display {
+        42
     }
 }
 
-struct MutexGuard<'a>(&'a Mutex);
+struct Stderr();
 
-impl<'a> Drop for MutexGuard<'a> {
-    fn drop(&mut self) {
-        // Empty but this is a necessary part of the repro. Otherwise borrow
-        // checker is fine with 'a dangling at the time that MutexGuard goes out
-        // of scope.
-    }
-}
-
-struct Out;
-
-impl Out {
-    fn write_fmt(&self, _args: fmt::Arguments) {}
-}
-
-impl<'a> Display for MutexGuard<'a> {
-    fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
-        Ok(())
-    }
+impl Stderr {
+    /// A "lending" `write_fmt` method. See:
+    /// https://docs.rs/async-std/1.12.0/async_std/io/prelude/trait.WriteExt.html#method.write_fmt
+    fn write_fmt(&mut self, _args: fmt::Arguments) -> &() { &() }
 }
 
-fn main() {
-    // FIXME(dtolnay): We actually want both of these to work. I think it's
-    // sadly unimplementable today though.
+fn early_drop_for_format_args_temporaries() {
+    let mut out = Stderr();
 
     let _write = {
         let mutex = Mutex;
-        write!(Out, "{}", mutex.lock()) /* no semicolon */
-        //~^ ERROR `mutex` does not live long enough
+        write!(out, "{}", mutex.lock()) /* no semicolon */
     };
 
     let _writeln = {
         let mutex = Mutex;
-        writeln!(Out, "{}", mutex.lock()) /* no semicolon */
-        //~^ ERROR `mutex` does not live long enough
+        writeln!(out, "{}", mutex.lock()) /* no semicolon */
     };
 }
+
+fn late_drop_for_receiver() {
+    let mutex = Mutex;
+    drop(write!(&mut Stderr(), "{}", mutex.lock()));
+    drop(writeln!(&mut Stderr(), "{}", mutex.lock()));
+}
+
+fn two_phased_borrows_retrocompat(w: (&mut Stderr, i32)) {
+    write!(w.0, "{}", w.1);
+    writeln!(w.0, "{}", w.1);
+    struct Struct<W> {
+        w: W,
+        len: i32
+    }
+    let s = (Struct { w: (w.0, ), len: w.1 }, );
+    write!(s.0.w.0, "{}", s.0.len);
+    writeln!(s.0.w.0, "{}", s.0.len);
+}
diff --git a/src/test/ui/macros/format-args-temporaries-in-write.stderr b/src/test/ui/macros/format-args-temporaries-in-write.stderr
deleted file mode 100644
index 03ecc4b4418c6..0000000000000
--- a/src/test/ui/macros/format-args-temporaries-in-write.stderr
+++ /dev/null
@@ -1,43 +0,0 @@
-error[E0597]: `mutex` does not live long enough
-  --> $DIR/format-args-temporaries-in-write.rs:41:27
-   |
-LL |         write!(Out, "{}", mutex.lock()) /* no semicolon */
-   |                           ^^^^^^^^^^^^
-   |                           |
-   |                           borrowed value does not live long enough
-   |                           a temporary with access to the borrow is created here ...
-LL |
-LL |     };
-   |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
-   |     |
-   |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-  --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
-   |
-LL |         $dst.write_fmt($crate::format_args!($($arg)*));
-   |                                                       +
-
-error[E0597]: `mutex` does not live long enough
-  --> $DIR/format-args-temporaries-in-write.rs:47:29
-   |
-LL |         writeln!(Out, "{}", mutex.lock()) /* no semicolon */
-   |                             ^^^^^^^^^^^^
-   |                             |
-   |                             borrowed value does not live long enough
-   |                             a temporary with access to the borrow is created here ...
-LL |
-LL |     };
-   |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
-   |     |
-   |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-  --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
-   |
-LL |         $dst.write_fmt($crate::format_args_nl!($($arg)*));
-   |                                                          +
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0597`.