or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Syncing
xxxxxxxxxx
https://github.com/rust-lang/types-team/issues/92
What do I mean with dropck
We generally require the type of locals to be well-formed whenever the local is used. This includes proving the where-bounds of the local and also requires all regions used by it to be live.
The only exception to this is the implicitly dropping values when they go out of scope. This does not necessarily require the value to be live:
This is only sound if dropping the value does not try to access any dead region. The code responsible for this is
dropck_outlives
.The rest of this document uses the following type definition for a type which requires its region parameter to be live:
How values are dropped
I mostly got the terminology and implementation details here from looking at the source. Please tell me if something feels off.
At its core, a value of type
T
is dropped by executing its "drop glue". Drop glue is compiler generated and first calls<T as Drop>::drop
and then recursively calls the drop glue of any recursively owned values.T
has an explicitDrop
impl, call<T as Drop>::drop
.T
implementsDrop
, recurse into all values owned byT
:ManuallyDrop<U>
which is not considered to ownU
.PhantomData<U>
also does not own anything.Whether a type has drop glue is returned by
fn Ty::needs_drop
.Partially dropping a local
For types which do not implement
Drop
themselves, we can also partially move parts of the value before dropping the rest. In this case only the drop glue for the not-yet moved values is called, e.g.During MIR building we assume that a local may get dropped whenever it goes out of scope as long as its type needs drop. Computing the exact drop glue for a variable happens after borrowck in the
ElaborateDrops
pass. This means that even if some part of the local have been dropped previously, dropck still requires this value to be live. This is the case even if we completely moved a local.I think that it should be possible to add some amount of drop elaboration before borrowck, allowing this example to compile. There is an unstable feature to move drop elaboration before const checking: #73255. As far as I know such a feature gate does not exist for doing some drop elaboration before borrowck.
dropck_outlives
During borrowck, we require a local to be valid for drop at all locations before it is dropped[2] by adding the region constraints returned by
dropck_outlives
. For locals whose type does not need drop, i.e.Ty::needs_drop
returned false, we do not emit drop statements during mir building, so these locals are never required to be valid wrt to drop.The constraints computed by
dropck_outlives
for a type closely match the generated drop glue for that type. Unlike drop glue,dropck_outlives
cares about the types of owned values, not the values itself. For a value of typeT
T
has an explicitDrop
, require all generic arguments to be live, unless they are marked with#[may_dangle]
in which case they are fully ignoredT
has an explicitDrop
, recurse into all types owned byT
ManuallyDrop<U>
which is not considered to ownU
. We considerPhantomData<U>
to ownU
.The sections marked in bold are cases where
dropck_outlives
considers types to be owned which are ignored byTy::needs_drop
. We only rely ondropck_outlives
ifTy::needs_drop
for the containing local returnedtrue
.This means liveness requirements can change depending on whether a type is contained in a larger local. This is inconsistent, and should be fixed: an example for arrays and forPhantomData
.[3]I believe the only ways these inconsistencies can be fixed is by MIR building to be more pessimistic, probably by making
Ty::needs_drop
weaker, or alternatively, changingdropck_outlives
to be more precise, requiring fewer regions to be live.Fixing the
[T; 0]
inconsistency (#110288)I propose to change
dropck_outlives
to not add outlives requirements for the element type of zero length arrays. Summarizing the discussion in that issue (full summary):[T; 0]: Default
and also inside of the compiler itself.dropck_outlives
behavior. Making rustc more precise is generally preferable over making it "weaker".needs_drop
to be more pessimistic causes actual breakage found via crater.Fixing the
PhantomData<U>
inconsistency (RFC 3417)I propose to change
dropck_outlives
to not add outlives requirements forU
. I still have to update the RFC, the main motivation is thatPhantomData
isCopy
, returningtrue
inneeds_drop
would also be inconsistent.We have the issue that
PhantomData
is currently used together with#[may_dangle]
to specify the exactdropck_outlives
requirements.#[may_dangle]
unsafely states "this parameter is not accessed directly in theDrop
implementation". Parameters with#[may_dangle]
can still be required to be live by recursively owned types. This is incredibly subtle if theDrop
implementation manually drops values of the parameter type.For this to be sound, the parameter has to be considered live because it is recursively owned. This is often not the case when manually dropping values. We therefore currently add a
PhantomData
field owning the parameter to the type. This is subtle and easy to miss and we have already gotten this wrong multiple times: #76367 and #99408.RFC 3417 proposes to change may dangle to stop relying on recursive ownership by explicitly stating whether a parameter is completely unused or only dropped. I propose the syntax
#[may_dangle(must_not_use)]
and#[may_dangle(can_drop)]
for this. We can then use a recursive ownership check to warn against unnecessarily strong#[may_dangle]
attributes onDrop
impls.The bright future
We can add assertions that
!dropck_outlives.is_empty()
impliesTy::needs_drop
.dropck_outlives
andTy::needs_drop
agree on the definitation of recursive ownership.For a given type
T
, we have the following ownership rules:ManuallyDrop<U>
which is not considered to ownU
.PhantomData<U>
does not own anything.Drop glue is generated as follows:
T
has an explicitDrop
impl, call<T as Drop>::drop
[1:3].T
has aDrop
impl, recusively drop all values owned byT
The outlives requirements are computed as follows:
T
has an explicitDrop
impl, require all parameters to be live unless they are marked with#[may_dangle]
:#[may_dangle]
on lifetime parameters and#[may_dangle(must_not_use)]
on type parameters cause us to fully ignore the parameter#[may_dangle(may_drop)]
recurses into the type parameter as if it were recursively ownedT
has aDrop
impl, recursively add the outlives requirements for types owned byT
question section
@lcnr: how do i ask a question
i want to ask a question but don't know how, how do I do it?
@lcnr: like this :3
"trait objects do not own anything"
pnkfelixs: I assume "trait object" there means
&dyn Trait
/&mut Trait
/*const Trait
…@lcnr: I mean
dyn Trait<'a>
, I consider it easier to think about trait objects as having an explicit drop, but not owning anything. Because that's what is actually going on, even though its currently not really represented by the type system.pnkfelix: but can't
Box<dyn Trait>
own things … ah I see, you treat the embedded ownership as being tied to an explicit<T as Drop>::drop
method, even if the underlying implementing type does not actually implement Drop?lcnr:
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →vtable.drop_in_place
, while not recursing into anything.nikomatsakis: I always say it as more like "vtables have drop, which is defined by calling a method from the vtable". I'm not sure if this is an important distinction though.
dropck_outlives vs needs_drop inconsistency
pnkfelix: What are the concrete negative outcomes, from a Rust programmer's perspective (vs a rustc dev's perspective) of the inconsistency here? Is it unsoundnes? Or surprising cases where code is rejected by the compiler? Or difficulty in reasoning about unsafe code soundness? Or something else?
pnkfelix: Specifically, with respect to the PhantomData inconsistency: Are all the negative outcomes there surfacing as instances of bad interactions with
#[may_dangle]
? (Note the text here does provide pointers to examples of the#[may_dangle]
problems.) Or are there other issues (that I don't see documented here)?Discussion on Zulip
elaboration before dropck
nikomatsakis: There was work on this. It was meant to unblock polonius. I am not sure the current state.
Relevant MCP: https://github.com/rust-lang/compiler-team/issues/558
"drop-live"
nikomatsakis: This is accurate, I just want to add a bit of "color" to the description. The way I think about it is that there are two distinct "liveness" computations that we perform:
v
is use-live at locationL
if it maybe "used" later; a use here is basically anything that is not a dropv
is drop-live at locationL
if it maybe dropped laterWhen things are use-live, their entire type must be valid at
L
. When they are drop-live, all regions that are required by dropck must be valid atL
.These "values" would be better thought of as places.
What are the "cycle" implications of fixing the
[T;0]
inconsistency?nikomatsakis: This implies that we perform const eval for the array length, right? Is that an issue? We would have to be able to do that without access to borrow check, I think that's the primary implication?
lcnr: good catch
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →feature(generic_const_exprs)
.What does "may dangle" mean?
nikomatsakis: I don't think this is the right way to describe it. Well, maybe it is! I was going to say that "may dangle" meant: "may be dropped", but actually I think that's untrue, and I guess this is the point of the text that follows? i.e., we basically want to characterize in one of 3 ways:
T
should be use-live when the drop executes (chosen if there is aDrop
impl unless there is a#[may_dangle]
attribute)T
should be drop-live when the drop executes (chosen if the type owns a value of typeT
or (today) includes aPhantomData<T>
, even if there is a#[may_dangle]
attribute)T
don't need to be live at all when the drop executes (chosen if the type is only borrowed)Is this an accurate summary?
lcnr: yes, though "chosen if the type owns a value of type
T
or (today) includes aPhantomData<T>
, even if there is a#[may_dangle]
attribute" feels less clear to me then saying "Drop
impls requires all parameters to be live, unless they are marked with#[may_dangle]
. In this case we only consider the parameter if it is recursively owned" which is what I wanted to state in the doc ^^examples of finer-grained may_dangle?
pnkfelix: Regarding the extensions
#[may_dangle(must_not_use)]
and#[may_dangle(can_drop)]
: do we know how the existing uses of#[may_dangle]
are going to map to each of those two new forms?pnkfelix: E.g. what will
unsafe impl<#[may_dangle] T> Drop for Vec<T>
map to?you can consider trait objects to have a builtin
Drop
implementation which directly uses thedrop_in_place
provided by the vtable. ThisDrop
implementation requires all its generic parameters to be live.- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →I think should be equivalent to require the value to be valid for drop only when actually dropping it? With polonius we add additional constraints for exactly the locations where drop is used I think? Unsure, should be fine to think about this as: borrowck requires a value to be valid for drop whenever it could get dropped. ↩︎
This is the core assumption of #110288 and RFC 3417. ↩︎