-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Closed
Labels
C-bugCategory: This is a bug.Category: This is a bug.F-never_type`#![feature(never_type)]``#![feature(never_type)]`T-langRelevant to the language teamRelevant to the language teamfixed-by-thir-unsafeck`-Z thir-unsafeck` handles this correctly.`-Z thir-unsafeck` handles this correctly.
Description
This code compiles:
fn foo(ptr: *const bool) {
let _ = *ptr;
}
The MIR is empty, i.e., no ptr deref happens.
But this does not:
fn foo(ptr: *const !) {
let _ = *ptr;
}
The MIR is unreachable;
, i.e., the ptr deref conceptually somehow did actually happen.
That is a very strange inconsistency -- the !
type is just yet another type, why does it behave differently here?
Curiously, this does not yield any unreachable
:
fn bar(ptr: *const (i32, !)) { unsafe {
let (x, _) = *ptr;
} }
tesuji, 197g and kornelski
Metadata
Metadata
Assignees
Labels
C-bugCategory: This is a bug.Category: This is a bug.F-never_type`#![feature(never_type)]``#![feature(never_type)]`T-langRelevant to the language teamRelevant to the language teamfixed-by-thir-unsafeck`-Z thir-unsafeck` handles this correctly.`-Z thir-unsafeck` handles this correctly.
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
RalfJung commentedon Dec 5, 2020
One possible explanation of this behavior is that "it is UB to create a place of an uninhabited type". That would be a rather surprising special case, I think, but I could get behind this since I am actually proposing to similarly treat uninhabites types specially for
&T
validity (see rust-lang/unsafe-code-guidelines#77).However, under that explanation, the following code has UB:
And yet this is all accepted as safe code. So I think something is very wrong here.
camelid commentedon Dec 7, 2020
Should this be nominated?
DutchGhost commentedon Dec 7, 2020
Is this what is happening with #77694 ?
RalfJung commentedon Dec 7, 2020
@DutchGhost I don't think so, there is no
_
pattern there.nikomatsakis commentedon Dec 15, 2020
We discussed this in our @rust-lang/lang meeting today. Clearly the current behavior is unsound. The question is what behavior we expect. There seem to be two related questions:
let _ = *foo
be unsafe whenfoo
is a raw pointer, independent of everything else?let _ = *foo
be UB iffoo
is not a valid pointer?let _ = place
is generally a no-op, but it does evaluateplace
for side-effects*foo
(wherefoo
is a raw pointer) to a place has the side effect of asserting thatfoo
is a valid pointer which could be loaded, even though we are not currently loadingDoes that analysis sound about right to you, @RalfJung? (It doesn't yet say the answers to those questions, especially the second one)
RalfJung commentedon Dec 16, 2020
Yes that sounds about right, modulo the vagueness of the term "valid pointer". ;)
One way to look at this is by being explicit about place-to-value coercions (
p2v
). The regularlet x = *ptr
then becomesNow there are two axes we can adjust:
let _ = *ptr
perform a place-to-value coercion or not? If yes, certainly the ptr needs to be as valid as inlet x = *ptr
as the same things are happening.*ptr
, without being followed by a p2v, require in terms of validity? Does the ptr need to be dereferencable (so far I always assumed the answer is yes), does it need to point to data that is valid for the place's type (so far I always assumed the answer is no)? Does this depend on the type ofptr
, i.e., do we have "raw places" whose construction is governed by different rules?One aspect to consider for the second question is expressions like
&(*ptr).field
and&raw (*ptr).field
, where there is no implicit place-to-value coercion (assuming here thatptr
is a value expression). Any validity requirement we impose on place construction will also be in force here.&raw (*ptr).field
currently compiles togetelementptr inbounds
, and if we want to stick to that, some amount of dereferencability needs to be required even when a "raw place" is being constructed. But there's also some demand for relaxing this to e.g. makeoffset_of!
easier to implement -- there are proposals to instead use a currently-hypotheticalgetelementptr nw
for&raw (*ptr).field
that ensures no-overflow but does not say anything about allocation bounds. This would correspond to (raw) place construction only requiring that the place (viewed as a base address and a size) does not overflow the high end of memory, so this is another option that's on the table.RalfJung commentedon Dec 28, 2020
Cc #80059
nikomatsakis commentedon Feb 10, 2021
We discussed this in the @rust-lang/lang meeting on 2021-02-09 and we concluded that:
unsafe
to be required here, but that is covered by Unsafe checking skips pointer dereferences in unused places #80059 specifically.let _ = *ptr
is UB or not at this time, it doesn't seem urgent. In other words, it is presently considered UB, and we're ok with that, but we would also definitely consider changing that in the future.I'm going to remove nomination as there isn't anything urgent to solve at this time.
RalfJung commentedon Feb 13, 2021
FWIW, Miri cannot detect this UB since it is not represented in the MIR.
nikomatsakis commentedon Feb 15, 2021
In truth, my opinion is that
let _ = *ptr
should not be UB, regardless of the type ofptr
, but it should be unsafe ifptr
is a raw pointer.syvb commentedon Jun 21, 2021
It seems that the THIR unsafety checker now disallows (while the MIR checker allows) dereferencing pointers to wildcards:
let _ = x
statement is used #90524PlaceMention
statement forlet _ =
. #102256!
place is constructed (but not loaded!) #117288RalfJung commentedon Oct 30, 2023
This does not compile any more. I think @cjgillot fixed it? So the issue can be closed if we have a test.
That is indeed the semantics we are going for. For
bool
andenum Void {}
, this is already the case. For!
it is not but that's a bug: #117288.RalfJung commentedon Oct 30, 2023
#102256 added a test so this can be closed.