about summary refs log tree commit diff
diff options
context:
space:
mode:
authorUrgau <urgau@numericable.fr>2025-07-22 20:54:22 +0200
committerUrgau <urgau@numericable.fr>2025-07-31 22:35:22 +0200
commit8a3a7e625a8b607c018cdcf776fa79e29eaa56c8 (patch)
treeb34ccaa4dda9cf5e46ce33ace6ac34fc28be5af3
parentadcb3d3b4cd3b7c4cde642f3ed537037f293738e (diff)
downloadrust-8a3a7e625a8b607c018cdcf776fa79e29eaa56c8.tar.gz
rust-8a3a7e625a8b607c018cdcf776fa79e29eaa56c8.zip
Add lint against dangling pointers form local variables
-rw-r--r--compiler/rustc_lint/messages.ftl6
-rw-r--r--compiler/rustc_lint/src/dangling.rs150
-rw-r--r--compiler/rustc_lint/src/lints.rs16
-rw-r--r--tests/ui/lint/dangling-pointers-from-locals.rs188
-rw-r--r--tests/ui/lint/dangling-pointers-from-locals.stderr247
5 files changed, 599 insertions, 8 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 69fe7fe83ff..4d0c0c94a81 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -207,6 +207,12 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i
 
 lint_custom_inner_attribute_unstable = custom inner attributes are unstable
 
+lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped
+    .ret_ty = return type of the {$fn_kind} is `{$ret_ty}`
+    .local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind}
+    .created_at = dangling pointer created here
+    .note = pointers do not have a lifetime; after returning, the `{$local_var_ty}` will be deallocated at the end of the {$fn_kind} because nothing is referencing it as far as the type system is concerned
+
 lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped
     .label_ptr = this pointer will immediately be invalid
     .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs
index 9e19949c3b6..af4457f4314 100644
--- a/compiler/rustc_lint/src/dangling.rs
+++ b/compiler/rustc_lint/src/dangling.rs
@@ -1,13 +1,14 @@
 use rustc_ast::visit::{visit_opt, walk_list};
 use rustc_hir::attrs::AttributeKind;
+use rustc_hir::def::Res;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
-use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, find_attr};
-use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, LangItem, TyKind, find_attr};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::{Span, sym};
 
-use crate::lints::DanglingPointersFromTemporaries;
+use crate::lints::{DanglingPointersFromLocals, DanglingPointersFromTemporaries};
 use crate::{LateContext, LateLintPass};
 
 declare_lint! {
@@ -42,6 +43,36 @@ declare_lint! {
     "detects getting a pointer from a temporary"
 }
 
+declare_lint! {
+    /// The `dangling_pointers_from_locals` lint detects getting a pointer to data
+    /// of a local that will be dropped at the end of the function.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn f() -> *const u8 {
+    ///     let x = 0;
+    ///     &x // returns a dangling ptr to `x`
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Returning a pointer from a local value will not prolong its lifetime,
+    /// which means that the value can be dropped and the allocation freed
+    /// while the pointer still exists, making the pointer dangling.
+    /// This is not an error (as far as the type system is concerned)
+    /// but probably is not what the user intended either.
+    ///
+    /// If you need stronger guarantees, consider using references instead,
+    /// as they are statically verified by the borrow-checker to never dangle.
+    pub DANGLING_POINTERS_FROM_LOCALS,
+    Warn,
+    "detects returning a pointer from a local variable"
+}
+
 /// FIXME: false negatives (i.e. the lint is not emitted when it should be)
 /// 1. Ways to get a temporary that are not recognized:
 ///    - `owning_temporary.field`
@@ -53,20 +84,123 @@ declare_lint! {
 #[derive(Clone, Copy, Default)]
 pub(crate) struct DanglingPointers;
 
-impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]);
+impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES, DANGLING_POINTERS_FROM_LOCALS]);
 
 // This skips over const blocks, but they cannot use or return a dangling pointer anyways.
 impl<'tcx> LateLintPass<'tcx> for DanglingPointers {
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
+        fn_kind: FnKind<'tcx>,
+        fn_decl: &'tcx FnDecl<'tcx>,
         body: &'tcx Body<'tcx>,
         _: Span,
-        _: LocalDefId,
+        def_id: LocalDefId,
     ) {
-        DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body)
+        DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body);
+
+        if let FnRetTy::Return(ret_ty) = &fn_decl.output
+            && let TyKind::Ptr(_) = ret_ty.kind
+        {
+            // get the return type of the function or closure
+            let ty = match cx.tcx.type_of(def_id).instantiate_identity().kind() {
+                ty::FnDef(..) => cx.tcx.fn_sig(def_id).instantiate_identity(),
+                ty::Closure(_, args) => args.as_closure().sig(),
+                _ => return,
+            };
+            let ty = ty.output();
+
+            // this type is only used for layout computation and pretty-printing, neither of them rely on regions
+            let ty = cx.tcx.instantiate_bound_regions_with_erased(ty);
+
+            // verify that we have a pointer type
+            let inner_ty = match ty.kind() {
+                ty::RawPtr(inner_ty, _) => *inner_ty,
+                _ => return,
+            };
+
+            if cx
+                .tcx
+                .layout_of(cx.typing_env().as_query_input(inner_ty))
+                .is_ok_and(|layout| !layout.is_1zst())
+            {
+                let dcx = &DanglingPointerLocalContext {
+                    body: def_id,
+                    fn_ret: ty,
+                    fn_ret_span: ret_ty.span,
+                    fn_ret_inner: inner_ty,
+                    fn_kind: match fn_kind {
+                        FnKind::ItemFn(..) => "function",
+                        FnKind::Method(..) => "method",
+                        FnKind::Closure => "closure",
+                    },
+                };
+
+                // look for `return`s
+                DanglingPointerReturnSearcher { cx, dcx }.visit_body(body);
+
+                // analyze implicit return expression
+                if let ExprKind::Block(block, None) = &body.value.kind
+                    && let innermost_block = block.innermost_block()
+                    && let Some(expr) = innermost_block.expr
+                {
+                    lint_addr_of_local(cx, dcx, expr);
+                }
+            }
+        }
+    }
+}
+
+struct DanglingPointerLocalContext<'tcx> {
+    body: LocalDefId,
+    fn_ret: Ty<'tcx>,
+    fn_ret_span: Span,
+    fn_ret_inner: Ty<'tcx>,
+    fn_kind: &'static str,
+}
+
+struct DanglingPointerReturnSearcher<'lcx, 'tcx> {
+    cx: &'lcx LateContext<'tcx>,
+    dcx: &'lcx DanglingPointerLocalContext<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for DanglingPointerReturnSearcher<'_, 'tcx> {
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
+        if let ExprKind::Ret(Some(expr)) = expr.kind {
+            lint_addr_of_local(self.cx, self.dcx, expr);
+        }
+        walk_expr(self, expr)
+    }
+}
+
+/// Look for `&<path_to_local_in_same_body>` pattern and emit lint for it
+fn lint_addr_of_local<'a>(
+    cx: &LateContext<'a>,
+    dcx: &DanglingPointerLocalContext<'a>,
+    expr: &'a Expr<'a>,
+) {
+    // peel casts as they do not interest us here, we want the inner expression.
+    let (inner, _) = super::utils::peel_casts(cx, expr);
+
+    if let ExprKind::AddrOf(_, _, inner_of) = inner.kind
+        && let ExprKind::Path(ref qpath) = inner_of.peel_blocks().kind
+        && let Res::Local(from) = cx.qpath_res(qpath, inner_of.hir_id)
+        && cx.tcx.hir_enclosing_body_owner(from) == dcx.body
+    {
+        cx.tcx.emit_node_span_lint(
+            DANGLING_POINTERS_FROM_LOCALS,
+            expr.hir_id,
+            expr.span,
+            DanglingPointersFromLocals {
+                ret_ty: dcx.fn_ret,
+                ret_ty_span: dcx.fn_ret_span,
+                fn_kind: dcx.fn_kind,
+                local_var: cx.tcx.hir_span(from),
+                local_var_name: cx.tcx.hir_ident(from),
+                local_var_ty: dcx.fn_ret_inner,
+                created_at: (expr.hir_id != inner.hir_id).then_some(inner.span),
+            },
+        );
     }
 }
 
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index fd8d0f832aa..ef63c0dee8c 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1188,6 +1188,22 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
     pub temporary_span: Span,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(lint_dangling_pointers_from_locals)]
+#[note]
+pub(crate) struct DanglingPointersFromLocals<'tcx> {
+    pub ret_ty: Ty<'tcx>,
+    #[label(lint_ret_ty)]
+    pub ret_ty_span: Span,
+    pub fn_kind: &'static str,
+    #[label(lint_local_var)]
+    pub local_var: Span,
+    pub local_var_name: Ident,
+    pub local_var_ty: Ty<'tcx>,
+    #[label(lint_created_at)]
+    pub created_at: Option<Span>,
+}
+
 // multiple_supertrait_upcastable.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_multiple_supertrait_upcastable)]
diff --git a/tests/ui/lint/dangling-pointers-from-locals.rs b/tests/ui/lint/dangling-pointers-from-locals.rs
new file mode 100644
index 00000000000..e321df2f427
--- /dev/null
+++ b/tests/ui/lint/dangling-pointers-from-locals.rs
@@ -0,0 +1,188 @@
+//@ check-pass
+
+struct Zst((), ());
+struct Adt(u8);
+
+const X: u8 = 5;
+
+fn simple() -> *const u8 {
+    let x = 0;
+    &x
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn bindings() -> *const u8 {
+    let x = 0;
+    let x = &x;
+    x
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn bindings_with_return() -> *const u8 {
+    let x = 42;
+    let y = &x;
+    return y;
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn with_simple_cast() -> *const u8 {
+    let x = 0u8;
+    &x as *const u8
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn bindings_and_casts() -> *const u8 {
+    let x = 0u8;
+    let x = &x as *const u8;
+    x as *const u8
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn return_with_complex_cast() -> *mut u8 {
+    let mut x = 0u8;
+    return &mut x as *mut u8 as *const u8 as *mut u8;
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn with_block() -> *const u8 {
+    let x = 0;
+    &{ x }
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn with_many_blocks() -> *const u8 {
+    let x = 0;
+    {
+        {
+            &{
+                //~^ WARN a dangling pointer will be produced
+                { x }
+            }
+        }
+    }
+}
+
+fn simple_return() -> *const u8 {
+    let x = 0;
+    return &x;
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn return_mut() -> *mut u8 {
+    let mut x = 0;
+    return &mut x;
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn const_and_flow() -> *const u8 {
+    if false {
+        let x = 8;
+        return &x;
+        //~^ WARN a dangling pointer will be produced
+    }
+    &X // not dangling
+}
+
+fn vector<T: Default>() -> *const Vec<T> {
+    let x = vec![T::default()];
+    &x
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn local_adt() -> *const Adt {
+    let x = Adt(5);
+    return &x;
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn closure() -> *const u8 {
+    let _x = || -> *const u8 {
+        let x = 8;
+        return &x;
+        //~^ WARN a dangling pointer will be produced
+    };
+    &X // not dangling
+}
+
+fn fn_ptr() -> *const fn() -> u8 {
+    fn ret_u8() -> u8 {
+        0
+    }
+
+    let x = ret_u8 as fn() -> u8;
+    &x
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn as_arg(a: Adt) -> *const Adt {
+    &a
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn fn_ptr_as_arg(a: fn() -> u8) -> *const fn() -> u8 {
+    &a
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn ptr_as_arg(a: *const Adt) -> *const *const Adt {
+    &a
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn adt_as_arg(a: &Adt) -> *const &Adt {
+    &a
+    //~^ WARN a dangling pointer will be produced
+}
+
+fn unit() -> *const () {
+    let x = ();
+    &x // not dangling
+}
+
+fn zst() -> *const Zst {
+    let x = Zst((), ());
+    &x // not dangling
+}
+
+fn ref_implicit(a: &Adt) -> *const Adt {
+    a // not dangling
+}
+
+fn ref_explicit(a: &Adt) -> *const Adt {
+    &*a // not dangling
+}
+
+fn identity(a: *const Adt) -> *const Adt {
+    a // not dangling
+}
+
+fn from_ref(a: &Adt) -> *const Adt {
+    std::ptr::from_ref(a) // not dangling
+}
+
+fn inner_static() -> *const u8 {
+    static U: u8 = 5;
+    if false {
+        return &U as *const u8; // not dangling
+    }
+    &U // not dangling
+}
+
+fn return_in_closure() {
+    let x = 0;
+    let c = || -> *const u8 {
+        &x // not dangling by it-self
+    };
+}
+
+fn option<T: Default>() -> *const Option<T> {
+    let x = Some(T::default());
+    &x // can't compute layout of `Option<T>`, so cnat' be sure it won't be a ZST
+}
+
+fn generic<T: Default>() -> *const T {
+    let x = T::default();
+    &x // can't compute layout of `T`, so can't be sure it won't be a ZST
+}
+
+fn main() {}
diff --git a/tests/ui/lint/dangling-pointers-from-locals.stderr b/tests/ui/lint/dangling-pointers-from-locals.stderr
new file mode 100644
index 00000000000..e1d28bf22a0
--- /dev/null
+++ b/tests/ui/lint/dangling-pointers-from-locals.stderr
@@ -0,0 +1,247 @@
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:10:5
+   |
+LL | fn simple() -> *const u8 {
+   |                --------- return type of the function is `*const u8`
+LL |     let x = 0;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     &x
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+   = note: `#[warn(dangling_pointers_from_locals)]` on by default
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:17:5
+   |
+LL | fn bindings() -> *const u8 {
+   |                  --------- return type of the function is `*const u8`
+LL |     let x = 0;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     let x = &x;
+   |             -- dangling pointer created here
+LL |     x
+   |     ^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:24:12
+   |
+LL | fn bindings_with_return() -> *const u8 {
+   |                              --------- return type of the function is `*const u8`
+LL |     let x = 42;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     let y = &x;
+   |             -- dangling pointer created here
+LL |     return y;
+   |            ^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:30:5
+   |
+LL | fn with_simple_cast() -> *const u8 {
+   |                          --------- return type of the function is `*const u8`
+LL |     let x = 0u8;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     &x as *const u8
+   |     --^^^^^^^^^^^^^
+   |     |
+   |     dangling pointer created here
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:37:5
+   |
+LL | fn bindings_and_casts() -> *const u8 {
+   |                            --------- return type of the function is `*const u8`
+LL |     let x = 0u8;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     let x = &x as *const u8;
+   |             -- dangling pointer created here
+LL |     x as *const u8
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:43:12
+   |
+LL | fn return_with_complex_cast() -> *mut u8 {
+   |                                  ------- return type of the function is `*mut u8`
+LL |     let mut x = 0u8;
+   |         ----- `x` is part the function and will be dropped at the end of the function
+LL |     return &mut x as *mut u8 as *const u8 as *mut u8;
+   |            ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |            |
+   |            dangling pointer created here
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:49:5
+   |
+LL | fn with_block() -> *const u8 {
+   |                    --------- return type of the function is `*const u8`
+LL |     let x = 0;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     &{ x }
+   |     ^^^^^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:57:13
+   |
+LL |   fn with_many_blocks() -> *const u8 {
+   |                            --------- return type of the function is `*const u8`
+LL |       let x = 0;
+   |           - `x` is part the function and will be dropped at the end of the function
+...
+LL | /             &{
+LL | |
+LL | |                 { x }
+LL | |             }
+   | |_____________^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:67:12
+   |
+LL | fn simple_return() -> *const u8 {
+   |                       --------- return type of the function is `*const u8`
+LL |     let x = 0;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     return &x;
+   |            ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:73:12
+   |
+LL | fn return_mut() -> *mut u8 {
+   |                    ------- return type of the function is `*mut u8`
+LL |     let mut x = 0;
+   |         ----- `x` is part the function and will be dropped at the end of the function
+LL |     return &mut x;
+   |            ^^^^^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:80:16
+   |
+LL | fn const_and_flow() -> *const u8 {
+   |                        --------- return type of the function is `*const u8`
+LL |     if false {
+LL |         let x = 8;
+   |             - `x` is part the function and will be dropped at the end of the function
+LL |         return &x;
+   |                ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:88:5
+   |
+LL | fn vector<T: Default>() -> *const Vec<T> {
+   |                            ------------- return type of the function is `*const Vec<T>`
+LL |     let x = vec![T::default()];
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     &x
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `Vec<T>` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:94:12
+   |
+LL | fn local_adt() -> *const Adt {
+   |                   ---------- return type of the function is `*const Adt`
+LL |     let x = Adt(5);
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     return &x;
+   |            ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `Adt` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:101:16
+   |
+LL |     let _x = || -> *const u8 {
+   |                    --------- return type of the closure is `*const u8`
+LL |         let x = 8;
+   |             - `x` is part the closure and will be dropped at the end of the closure
+LL |         return &x;
+   |                ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `u8` will be deallocated at the end of the closure because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `x` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:113:5
+   |
+LL | fn fn_ptr() -> *const fn() -> u8 {
+   |                ----------------- return type of the function is `*const fn() -> u8`
+...
+LL |     let x = ret_u8 as fn() -> u8;
+   |         - `x` is part the function and will be dropped at the end of the function
+LL |     &x
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `fn() -> u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `a` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:118:5
+   |
+LL | fn as_arg(a: Adt) -> *const Adt {
+   |           -          ---------- return type of the function is `*const Adt`
+   |           |
+   |           `a` is part the function and will be dropped at the end of the function
+LL |     &a
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `Adt` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `a` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:123:5
+   |
+LL | fn fn_ptr_as_arg(a: fn() -> u8) -> *const fn() -> u8 {
+   |                  -                 ----------------- return type of the function is `*const fn() -> u8`
+   |                  |
+   |                  `a` is part the function and will be dropped at the end of the function
+LL |     &a
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `fn() -> u8` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `a` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:128:5
+   |
+LL | fn ptr_as_arg(a: *const Adt) -> *const *const Adt {
+   |               -                 ----------------- return type of the function is `*const *const Adt`
+   |               |
+   |               `a` is part the function and will be dropped at the end of the function
+LL |     &a
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `*const Adt` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: a dangling pointer will be produced because the local variable `a` will be dropped
+  --> $DIR/dangling-pointers-from-locals.rs:133:5
+   |
+LL | fn adt_as_arg(a: &Adt) -> *const &Adt {
+   |               -           ----------- return type of the function is `*const &Adt`
+   |               |
+   |               `a` is part the function and will be dropped at the end of the function
+LL |     &a
+   |     ^^
+   |
+   = note: pointers do not have a lifetime; after returning, the `&Adt` will be deallocated at the end of the function because nothing is referencing it as far as the type system is concerned
+
+warning: 19 warnings emitted
+