diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 9f786d22c932f..7f122d2034637 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -27,6 +27,7 @@ pub mod decodable;
 pub mod default;
 pub mod encodable;
 pub mod hash;
+pub mod smart_ptr;
 
 #[path = "cmp/eq.rs"]
 pub mod eq;
diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
new file mode 100644
index 0000000000000..ff0c584d4378b
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
@@ -0,0 +1,113 @@
+use rustc_ast::{
+    self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
+    TraitBoundModifiers,
+};
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+use thin_vec::{thin_vec, ThinVec};
+
+macro_rules! path {
+    ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
+}
+
+pub fn expand_deriving_smart_ptr(
+    cx: &ExtCtxt<'_>,
+    span: Span,
+    _mitem: &MetaItem,
+    item: &Annotatable,
+    push: &mut dyn FnMut(Annotatable),
+    _is_const: bool,
+) {
+    let (name_ident, generics) = match item {
+        Annotatable::Item(aitem) => match &aitem.kind {
+            ItemKind::Struct(_, g) => (aitem.ident, g),
+            // FIXME: Improve error reporting.
+            _ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong kind"),
+        },
+        _ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong item"),
+    };
+
+    // Convert generic parameters (from the struct) into generic args.
+    let self_params = generics
+        .params
+        .iter()
+        .map(|p| match p.kind {
+            GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(span, p.ident)),
+            GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(span, p.ident)),
+            GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(span, p.ident)),
+        })
+        .collect::<Vec<_>>();
+
+    // Create the type of `self`.
+    let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
+    let self_type = cx.ty_path(path);
+
+    // Declare helper function that adds implementation blocks.
+    // FIXME: Copy attrs from struct?
+    let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
+    let mut add_impl_block = |generics, trait_symbol, trait_args| {
+        let mut parts = path!(span, core::ops);
+        parts.push(Ident::new(trait_symbol, span));
+        let trait_path = cx.path_all(span, true, parts, trait_args);
+        let trait_ref = cx.trait_ref(trait_path);
+        let item = cx.item(
+            span,
+            Ident::empty(),
+            attrs.clone(),
+            ast::ItemKind::Impl(Box::new(ast::Impl {
+                unsafety: ast::Unsafe::No,
+                polarity: ast::ImplPolarity::Positive,
+                defaultness: ast::Defaultness::Final,
+                constness: ast::Const::No,
+                generics,
+                of_trait: Some(trait_ref),
+                self_ty: self_type.clone(),
+                items: ThinVec::new(),
+            })),
+        );
+        push(Annotatable::Item(item));
+    };
+
+    // Create unsized `self`, that is, one where the first type arg is replace with `__S`. For
+    // example, instead of `MyType<'a, T>`, it will be `MyType<'a, __S>`.
+    let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
+    let mut alt_self_params = self_params;
+    for a in &mut alt_self_params {
+        if matches!(*a, GenericArg::Type(_)) {
+            *a = GenericArg::Type(s_ty.clone());
+            break;
+        }
+    }
+    let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
+
+    // Find the first type parameter and add an `Unsize<__S>` bound to it.
+    let mut impl_generics = generics.clone();
+    for p in &mut impl_generics.params {
+        if matches!(p.kind, ast::GenericParamKind::Type { .. }) {
+            let arg = GenericArg::Type(s_ty.clone());
+            let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
+            p.bounds.push(cx.trait_bound(unsize, false));
+            break;
+        }
+    }
+
+    // Add the `__S: ?Sized` extra parameter to the impl block.
+    let sized = cx.path_global(span, path!(span, core::marker::Sized));
+    let bound = GenericBound::Trait(
+        cx.poly_trait_ref(span, sized),
+        TraitBoundModifiers {
+            polarity: ast::BoundPolarity::Maybe(span),
+            constness: ast::BoundConstness::Never,
+            asyncness: ast::BoundAsyncness::Normal,
+        },
+    );
+    let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None);
+    impl_generics.params.push(extra_param);
+
+    // Add the impl blocks for `DispatchFromDyn`, `CoerceUnsized`, and `Receiver`.
+    let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
+    add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
+    add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone());
+    add_impl_block(generics.clone(), sym::Receiver, Vec::new());
+}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 1b4c6041294f4..48c71b96d2307 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -129,6 +129,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         PartialOrd: partial_ord::expand_deriving_partial_ord,
         RustcDecodable: decodable::expand_deriving_rustc_decodable,
         RustcEncodable: encodable::expand_deriving_rustc_encodable,
+        SmartPointer: smart_ptr::expand_deriving_smart_ptr,
     }
 
     let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index bfd0f77c237b2..2aa98a43f96c3 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -172,6 +172,7 @@ symbols! {
         Center,
         Cleanup,
         Clone,
+        CoerceUnsized,
         Command,
         ConstParamTy,
         Context,
@@ -187,6 +188,7 @@ symbols! {
         DiagMessage,
         Diagnostic,
         DirBuilder,
+        DispatchFromDyn,
         Display,
         DoubleEndedIterator,
         Duration,
@@ -297,8 +299,10 @@ symbols! {
         Saturating,
         Send,
         SeqCst,
+        Sized,
         SliceIndex,
         SliceIter,
+        SmartPointer,
         Some,
         SpanCtxt,
         String,
@@ -321,6 +325,7 @@ symbols! {
         TyCtxt,
         TyKind,
         Unknown,
+        Unsize,
         Upvars,
         Vec,
         VecDeque,
@@ -1284,6 +1289,7 @@ symbols! {
         on,
         on_unimplemented,
         opaque,
+        ops,
         opt_out_copy,
         optimize,
         optimize_attribute,
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 10d2698c5dd1b..798a2da0fa1a6 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -475,3 +475,12 @@ pub mod simd {
 }
 
 include!("primitive_docs.rs");
+
+/// Derive macro generating impls of traits related to smart pointers.
+#[cfg(not(bootstrap))]
+#[rustc_builtin_macro]
+#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, receiver_trait, unsize)]
+#[unstable(feature = "derive_smart_pointer", issue = "123430")]
+pub macro SmartPointer($item:item) {
+    /* compiler built-in */
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index ac475b5530a78..ba9ebfa0def8a 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -568,6 +568,9 @@ pub use core::u8;
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow(deprecated, deprecated_in_future)]
 pub use core::usize;
+#[cfg(not(bootstrap))]
+#[unstable(feature = "derive_smart_pointer", issue = "123430")]
+pub use core::SmartPointer;
 
 #[unstable(feature = "f128", issue = "116909")]
 pub mod f128;
diff --git a/tests/ui/deriving/deriving-smart-pointer.rs b/tests/ui/deriving/deriving-smart-pointer.rs
new file mode 100644
index 0000000000000..ef666fd8caef1
--- /dev/null
+++ b/tests/ui/deriving/deriving-smart-pointer.rs
@@ -0,0 +1,53 @@
+//@ run-pass
+#![feature(derive_smart_pointer)]
+
+#[derive(SmartPointer)]
+struct MyPointer<'a, T: ?Sized> {
+    ptr: &'a T,
+}
+
+impl<T: ?Sized> Copy for MyPointer<'_, T> {}
+impl<T: ?Sized> Clone for MyPointer<'_, T> {
+    fn clone(&self) -> Self {
+        Self { ptr: self.ptr }
+    }
+}
+
+impl<'a, T: ?Sized> core::ops::Deref for MyPointer<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &'a T {
+        self.ptr
+    }
+}
+
+struct MyValue(u32);
+impl MyValue {
+    fn through_pointer(self: MyPointer<'_, Self>) -> u32 {
+        self.ptr.0
+    }
+}
+
+trait MyTrait {
+    fn through_trait(&self) -> u32;
+    fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32;
+}
+
+impl MyTrait for MyValue {
+    fn through_trait(&self) -> u32 {
+        self.0
+    }
+
+    fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32 {
+        self.ptr.0
+    }
+}
+
+pub fn main() {
+    let v = MyValue(10);
+    let ptr = MyPointer { ptr: &v };
+    assert_eq!(v.0, ptr.through_pointer());
+    assert_eq!(v.0, ptr.through_pointer());
+    let dptr = ptr as MyPointer<dyn MyTrait>;
+    assert_eq!(v.0, dptr.through_trait());
+    assert_eq!(v.0, dptr.through_trait_and_pointer());
+}