Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,17 @@ lint_dangling_pointers_from_locals = {$fn_kind} returns a dangling pointer to dr
.ret_ty = return type is `{$ret_ty}`
.local_var = local variable `{$local_var_name}` is dropped at the end of the {$fn_kind}
.created_at = dangling pointer created here
.note = a dangling pointer is safe, but dereferencing one is undefined behavior

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
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}`
.help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice
.help_visit = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
.note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior
.note_more_info = for more information, see <https://doc.rust-lang.org/reference/destructors.html>

lint_dangling_pointers_from_temporaries = expression creates a dangling pointer to dropped temporary `{$ty}`
Copy link
Member

@Urgau Urgau Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's the a good description of the issue, I feel like it's a bit misleading, the temporary is not yet dropped (otherwise this would insta UB), it's dropped only after creating the pointer, and it's the dropping of the temporary which makes the pointer dangling.

The previously wording was clearer IMO on this point. Not sure how to best improve that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I thought that detail did not matter. Do you have a reference for your claim that that would be insta UB?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a reference for your claim that that would be insta UB?

For String::new().DROPPED.as_ptr() it's apparently a grey area rust-lang/unsafe-code-guidelines#188 where use-after-dropped and use-after-free are different regarding opsem. I was told that it should probably be UB and is heavily discouraged.

But for allocation like "".to_string().DROPPED.as_ptr() Miri flags it as UB1 on the as_ptr() call, so 🤷

Footnotes

  1. https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=1c4f3446888f2b92d486a5f478722640

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created rust-lang/miri#4668 to hopefully clarify this...

Copy link
Member Author

@hkBst hkBst Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so this is UB because of a dangling reference (which miri calls a pointer confusingly). I think the existence of https://doc.rust-lang.org/nightly/std/ptr/fn.dangling.html proves that just a dangling pointer is not UB.

I also think that means that it does not really matter whether a now-dangling pointer was temporarily valid at its point of creation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My un-easeness is not on whenever the pointer was temporarily valid, it is, but as you said it's not relevant. What I'm having problem with that your proposed message makes it ambiguous as to whenever or not the pointer was created from an invalid temporary, which rust-lang/miri#4668 made it clear that it's UB (lang or libs).

It would be IMO better if the message made it clear / obvious that the pointer is dangling precisely because it points to a temporary that's dropped too early, which the previous message was clearer about.

Maybe we could say something like this:

dropped temporary makes the pointer to {$ty} dangling

Copy link
Member

@Urgau Urgau Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe something like this:
warning: this pointer becomes dangling as the temporary {$ty} is dropped at statement end

After reflection I think I like this one the best, with the caveat that we should change the primary span to the as_ptr() part

Copy link
Member Author

@hkBst hkBst Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which rust-lang/miri#4668 made it clear that it's UB (lang or libs).

My interpretation is that it makes it clear that a dangling reference is UB, but a dangling pointer is fine either way...

The first example calls str::as_ptr, so an &str is created pointing to the deallocated buffer, hence the UB. Maybe the error message is a bit confusing -- what it says is that for this operation (creating a reference), the pointer needs to be dereferenceable. We use "pointer" as a term that encompasses both references and raw pointers -- everything that has pointer values.

"an &str is created pointing to the deallocated buffer, " -> UB dangling reference (We use "pointer" as a term that encompasses both references and raw pointers )

The second example is library UB (using a reference to a dropped string) but at the moment happens to not be language UB. This is not a stable property, just an artifact of the current (WIP) UB rules and library implementation. Also see rust-lang/unsafe-code-guidelines#188.

"using a reference to a dropped string" -> UB (not sure why it should be library UB and not language UB)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My interpretation is that it makes it clear that a dangling reference is UB, but a dangling pointer is fine either way

I don't think I ever disputed the fact that a dangling pointer is fine unless dereferenced.


As I said earlier the wording should not in anyway make it seems like the dangling pointer is created from a dropped temporary, the pointer is created from a valid (not-dropped) temporary, it's only after the temporary is dropped (so after a call to as_ptr) that the pointer becomes dangling.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still seem to be arguing as if a dangling pointer created from a previously dropped temporary is UB, but that is like saying that creating a pointer from a random address is UB, which it clearly isn't, since there is no unsafe here and it compiles fine:

fn main() {
    let p = 0x1234 as *const ();
}

So if a dangling pointer is fine either way (unless deref'ed), then why bother making the distinction?

.label_ptr = dangling pointer created here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also is misleading IMO, it's not yet dangling but it will be.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.label_ptr = dangling pointer created here
.label_ptr = pointer created here

.label_temporary = this `{$ty}` is dropped at the end of the statement
.help_bind = bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}`
.note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior
.note_return = returning a pointer to a local variable will always result in a dangling pointer
.note_more_info = for more information, see <https://doc.rust-lang.org/reference/destructors.html>


lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance
.note = a `use rustc_data_structures::fx::{$preferred}` may be necessary
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,10 +1106,10 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> {
// dangling.rs
#[derive(LintDiagnostic)]
#[diag(lint_dangling_pointers_from_temporaries)]
#[note]
#[help(lint_help_bind)]
#[help(lint_help_returned)]
#[help(lint_help_visit)]
#[note(lint_note_safe)]
#[note(lint_note_return)]
#[note(lint_note_more_info)]
// FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts
pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
pub callee: Ident,
Expand All @@ -1122,7 +1122,8 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {

#[derive(LintDiagnostic)]
#[diag(lint_dangling_pointers_from_locals)]
#[note]
#[note(lint_note_safe)]
#[note(lint_note_more_info)]
pub(crate) struct DanglingPointersFromLocals<'tcx> {
pub ret_ty: Ty<'tcx>,
#[label(lint_ret_ty)]
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/lint/dangling-pointers-from-locals.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ LL | &x
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
= note: `#[warn(dangling_pointers_from_locals)]` on by default

warning: function returns a dangling pointer to dropped local variable `x`
Expand All @@ -24,6 +25,7 @@ LL | x
| ^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:24:12
Expand All @@ -38,6 +40,7 @@ LL | return y;
| ^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:30:5
Expand All @@ -52,6 +55,7 @@ LL | &x as *const u8
| dangling pointer created here
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:37:5
Expand All @@ -66,6 +70,7 @@ LL | x as *const u8
| ^^^^^^^^^^^^^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:43:12
Expand All @@ -80,6 +85,7 @@ LL | return &mut x as *mut u8 as *const u8 as *mut u8;
| dangling pointer created here
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:49:5
Expand All @@ -92,6 +98,7 @@ LL | &{ x }
| ^^^^^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:57:13
Expand All @@ -108,6 +115,7 @@ LL | | }
| |_____________^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:67:12
Expand All @@ -120,6 +128,7 @@ LL | return &x;
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:73:12
Expand All @@ -132,6 +141,7 @@ LL | return &mut x;
| ^^^^^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:80:16
Expand All @@ -145,6 +155,7 @@ LL | return &x;
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:88:5
Expand All @@ -157,6 +168,7 @@ LL | &x
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:94:12
Expand All @@ -169,6 +181,7 @@ LL | return &x;
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: closure returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:101:16
Expand All @@ -181,6 +194,7 @@ LL | return &x;
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `x`
--> $DIR/dangling-pointers-from-locals.rs:113:5
Expand All @@ -194,6 +208,7 @@ LL | &x
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `a`
--> $DIR/dangling-pointers-from-locals.rs:118:5
Expand All @@ -206,6 +221,7 @@ LL | &a
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `a`
--> $DIR/dangling-pointers-from-locals.rs:123:5
Expand All @@ -218,6 +234,7 @@ LL | &a
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `a`
--> $DIR/dangling-pointers-from-locals.rs:128:5
Expand All @@ -230,6 +247,7 @@ LL | &a
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: function returns a dangling pointer to dropped local variable `a`
--> $DIR/dangling-pointers-from-locals.rs:133:5
Expand All @@ -242,6 +260,7 @@ LL | &a
| ^^
|
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>

warning: 19 warnings emitted

4 changes: 2 additions & 2 deletions tests/ui/lint/dangling-pointers-from-temporaries/allow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn main() {
#[deny(dangling_pointers_from_temporaries)]
{
dbg!(String::new().as_ptr());
//~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped
//~^ ERROR dangling pointer
}
S.foo()
}
Expand All @@ -18,6 +18,6 @@ impl S {
#[warn(dangling_pointers_from_temporaries)]
fn foo(self) {
dbg!(String::new().as_ptr());
//~^ WARNING a dangling pointer will be produced because the temporary `String` will be dropped
//~^ WARNING dangling pointer
}
}
28 changes: 14 additions & 14 deletions tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
error: a dangling pointer will be produced because the temporary `String` will be dropped
error: expression creates a dangling pointer to dropped temporary `String`
--> $DIR/allow.rs:9:28
|
LL | dbg!(String::new().as_ptr());
| ------------- ^^^^^^ this pointer will immediately be invalid
| ------------- ^^^^^^ dangling pointer created here
| |
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
| this `String` is dropped at the end of the statement
|
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
= help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr`
= help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
= help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr`
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: returning a pointer to a local variable will always result in a dangling pointer
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
note: the lint level is defined here
--> $DIR/allow.rs:7:12
|
LL | #[deny(dangling_pointers_from_temporaries)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: a dangling pointer will be produced because the temporary `String` will be dropped
warning: expression creates a dangling pointer to dropped temporary `String`
--> $DIR/allow.rs:20:28
|
LL | dbg!(String::new().as_ptr());
| ------------- ^^^^^^ this pointer will immediately be invalid
| ------------- ^^^^^^ dangling pointer created here
| |
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
| this `String` is dropped at the end of the statement
|
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
= help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr`
= help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
= help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr`
= note: a dangling pointer is safe, but dereferencing one is undefined behavior
= note: returning a pointer to a local variable will always result in a dangling pointer
= note: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
note: the lint level is defined here
--> $DIR/allow.rs:18:12
|
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/lint/dangling-pointers-from-temporaries/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ fn ok() {
fn not_ok() {
{
let ptr = cstring().as_ptr();
//~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped
//~^ ERROR dangling pointer
consume(ptr);
}
consume({
let ptr = cstring().as_ptr();
//~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped
//~^ ERROR dangling pointer
ptr
});
consume({
Expand All @@ -39,11 +39,11 @@ fn not_ok() {
//^ FIXME: should error
});
let _ptr: *const u8 = cstring().as_ptr().cast();
//~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped
//~^ ERROR dangling pointer
let _ptr: *const u8 = { cstring() }.as_ptr().cast();
//~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped
//~^ ERROR dangling pointer
let _ptr: *const u8 = { cstring().as_ptr() }.cast();
//~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped
//~^ ERROR dangling pointer
}

fn main() {
Expand Down
Loading
Loading