Skip to content

Document that Unique::empty() and NonNull::dangling() aren't sentinel values #52508

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
Jul 22, 2018

Conversation

joshtriplett
Copy link
Member

The documentation of Unique::empty() and NonNull::dangling() could
potentially suggest that they work as sentinel values indicating a
not-yet-initialized pointer. However, they both declare a non-null
pointer equal to the alignment of the type, which could potentially
reference a valid value of that type (specifically, the first such valid
value in memory). Explicitly document that the return value of these
functions does not work as a sentinel value.

@rust-highfive
Copy link
Contributor

r? @Mark-Simulacrum

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 18, 2018
Copy link
Member

@Mark-Simulacrum Mark-Simulacrum left a comment

Choose a reason for hiding this comment

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

r=me with the nit fixed

///
/// Note that the pointer value may potentially represent a valid pointer to
/// a `T`, which means this must not be used as a "not yet initialized"
/// sentinel value. Types that lazily allocate must track initialized cells
Copy link
Member

Choose a reason for hiding this comment

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

nit: s/initialized cells/initialization

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

… values

The documentation of Unique::empty() and NonNull::dangling() could
potentially suggest that they work as sentinel values indicating a
not-yet-initialized pointer. However, they both declare a non-null
pointer equal to the alignment of the type, which could potentially
reference a valid value of that type (specifically, the first such valid
value in memory). Explicitly document that the return value of these
functions does not work as a sentinel value.
@joshtriplett joshtriplett force-pushed the dangling-not-sentinel branch from 31e97d1 to ce75632 Compare July 18, 2018 20:01
@kennytm
Copy link
Member

kennytm commented Jul 18, 2018

@kennytm kennytm added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Jul 18, 2018
@joshtriplett
Copy link
Member Author

@kennytm is_dangling looks like a bug.

@kennytm
Copy link
Member

kennytm commented Jul 18, 2018

Should we revert #50357 and #51901, or just // FIXME them at the moment?

@joshtriplett
Copy link
Member Author

@kennytm If we don't have an immediate fix, we should revert them.

@hanna-kruppe
Copy link
Contributor

I disagree with reverting, at least #51901 fixed a much more real bug.

@joshtriplett
Copy link
Member Author

Ah, fair enough. But we do need a fix for them.

@kennytm Would the solution I proposed of using usize::MAX work for now?

@kennytm
Copy link
Member

kennytm commented Jul 18, 2018

@joshtriplett If the compiler doesn't assume NonNull<RcBox<T>> must be aligned then it should work.

(A properly allocated RcBox<T> obtained from Rc::downgrade must be aligned to usize-boundary, so usize::MAX being an odd number will never point to valid RcBox<T>, making it usable as sentinel)

@kennytm
Copy link
Member

kennytm commented Jul 21, 2018

@joshtriplett Ping, are you going to do this usize::MAX thing in this PR or in a different PR? If latter I think you could r+.

@joshtriplett
Copy link
Member Author

@kennytm I'd be happy to review someone's PR for that.

@bors r=Mark-Simulacrum

@bors
Copy link
Collaborator

bors commented Jul 21, 2018

📌 Commit ce75632 has been approved by Mark-Simulacrum

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 21, 2018
@kennytm
Copy link
Member

kennytm commented Jul 21, 2018

@bors rollup

kennytm added a commit to kennytm/rust that referenced this pull request Jul 21, 2018
… r=Mark-Simulacrum

Document that Unique::empty() and NonNull::dangling() aren't sentinel values

The documentation of Unique::empty() and NonNull::dangling() could
potentially suggest that they work as sentinel values indicating a
not-yet-initialized pointer. However, they both declare a non-null
pointer equal to the alignment of the type, which could potentially
reference a valid value of that type (specifically, the first such valid
value in memory). Explicitly document that the return value of these
functions does not work as a sentinel value.
@RalfJung
Copy link
Member

Which advantage does usize::MAX have over using 1?

@joshtriplett
Copy link
Member Author

joshtriplett commented Jul 21, 2018

@RalfJung Weak (and perhaps other things) contains a NonNull<RcBox<T>>, and RcBox has an alignment of at least the alignment of usize, which will typically be 4 or 8. Given that, there's a niche in a NonNull<RcBox<T>> that extends from 0..=3 or 0..=7. Having Weak use usize::MAX to represent the dangling value avoids using up that niche, and allows Weak to have the same niche.

@RalfJung
Copy link
Member

@joshtriplett I assume you mean 0..4 and 0..8?
AFAIK we only consider NonNull to have a niche of 0..1. It seems unlikely to me that we can ever change that.
OTOH using usize::MAX is probably not more complicated either.

Cc @eddyb

@hanna-kruppe
Copy link
Contributor

We definitely can't extend the niche of NonNull, but we can make Weak specifically (and some other types) opt into a larger niche.

@RalfJung
Copy link
Member

I'd be happy to review someone's PR for that.

I can prepare a PR today or tmr.

kennytm added a commit to kennytm/rust that referenced this pull request Jul 22, 2018
… r=Mark-Simulacrum

Document that Unique::empty() and NonNull::dangling() aren't sentinel values

The documentation of Unique::empty() and NonNull::dangling() could
potentially suggest that they work as sentinel values indicating a
not-yet-initialized pointer. However, they both declare a non-null
pointer equal to the alignment of the type, which could potentially
reference a valid value of that type (specifically, the first such valid
value in memory). Explicitly document that the return value of these
functions does not work as a sentinel value.
bors added a commit that referenced this pull request Jul 22, 2018
Rollup of 11 pull requests

Successful merges:

 - #51807 (Deprecation of str::slice_unchecked(_mut))
 - #52051 (mem::swap the obvious way for types smaller than the SIMD optimization's block size)
 - #52465 (Add CI test harness for `thumb*` targets. [IRR-2018-embedded])
 - #52507 (Reword when `_` couldn't be inferred)
 - #52508 (Document that Unique::empty() and NonNull::dangling() aren't sentinel values)
 - #52521 (Fix links in rustdoc book.)
 - #52581 (Avoid using `#[macro_export]` for documenting builtin macros)
 - #52582 (Typo)
 - #52587 (Add missing backtick in UniversalRegions doc comment)
 - #52594 (Run the error index tool against the sysroot libdir)
 - #52615 (Added new lines to .gitignore.)
@bors bors merged commit ce75632 into rust-lang:master Jul 22, 2018
@RalfJung
Copy link
Member

Done: #52637

@cramertj
Copy link
Member

We definitely can't extend the niche of NonNull, but we can make Weak specifically (and some other types) opt into a larger niche.

Ah-- I had been under the assumption that NonNull was guaranteed to be aligned. To clarify: this isn't the case?

@cramertj
Copy link
Member

Yes, this isn't the case: NonNull::new is documented to return Some for non-zero values.

kennytm added a commit to kennytm/rust that referenced this pull request Jul 24, 2018
Don't use NonNull::dangling as sentinel value in Rc, Arc

Instead, rely on alignment and use usize::MAX as sentinel.

Cc rust-lang#52508

r? @joshtriplett
@SimonSapin
Copy link
Contributor

Sorry I’m only seeing this now, but I’m surprised by this change and not sure I agree with it. Vec has ~always used Unique::empty() as a sentinel for empty vectors. What changed?

@Mark-Simulacrum
Copy link
Member

@SimonSapin A discussion on Discord led us to the conclusion that 0x(alignment) is potentially a valid pointer, so using it as a sentinel value may lead to incorrect behavior when such a pointer is legitimately passed.

@RalfJung
Copy link
Member

RalfJung commented Aug 1, 2018

So should we open an issue for Vec to also use usize::MAX like weak references now do?

If this is reoccurring, should Unique/NonNull have support for this?

@hanna-kruppe
Copy link
Contributor

Vec never does something like if self.ptr == NonNull::dangling(), does it? It has the capacity field for that.

@eddyb
Copy link
Member

eddyb commented Aug 1, 2018

Yes, because of the separate capacity you can use any non-null aligned(?) pointer.
Pretty much like how &[T; 0] can point (almost) anywhere.

@SimonSapin
Copy link
Contributor

Ah ok. Branching on equality to dangling() rather than only using it to "fill in the blanks" in a struct is the difference that I’d missed. Vec has its capacity field, and Box has size_of::<T>() == 0.

Still, does the pointer in rc::Weak not need to be aligned? (This would rather be a question for #52637 but it feels related enough.)

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants