Rust Lang Team
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee
  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: "Design meeting 2023-09-27: MaybeDangling RFC read" date: 2023-09-27 tags: T-lang, design-meeting, minutes url: https://hackmd.io/ifgF0ThcTSWvKnYRWEM8nw --- # `maybe_dangling` - Feature Name: `maybe_dangling` - Start Date: 2022-09-30 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary [summary]: #summary Declare that references and `Box` inside a new `MaybeDangling` type do not need to satisfy any memory-dependent validity properties (such as `dereferenceable` and `noalias`). # Motivation [motivation]: #motivation ### Example 1 Sometimes one has to work with references or boxes that either are already deallocated, or might get deallocated too early. This comes up particularly often with `ManuallyDrop`. For example, the following code is UB at the time of writing this RFC: ```rust fn id<T>(x: T) -> T { x } fn unsound(x: Box<i32>) { let mut x = ManuallyDrop::new(x); unsafe { x.drop() }; id(x); // or `let y = x;` or `mem::forget(x);`. } unsound(Box::new(42)); ``` It is unsound because we are passing a dangling `ManuallyDrop<Box<i32>>` to `id`. In terms of invariants required by the language ("validity invariants"), `ManuallyDrop` is a regular `struct`, so all its fields have to be valid, but that means the `Box` needs to valid, so in particular it must point to allocated memory -- but when `id` is invoked, the `Box` has already been deallocated. Given that `ManuallyDrop` is specifically designed to allow dropping the `Box` early, this is a big footgun (that people do [run into in practice](https://github.com/rust-lang/miri/issues/1508)). ### Example 2 There exist more complex versions of this problem, relating to a subtle aspect of the (currently poorly documented) aliasing requirements of Rust: when a reference is passed to a function as an argument (including nested in a struct), then that reference must remain live throughout the function. (In LLVM terms: we are annotating that reference with `dereferenceable`, which means "dereferenceable for the entire duration of this function call"). In [issue #101983](https://github.com/rust-lang/rust/issues/101983), this leads to a bug in `scoped_thread`. There we have a function that invokes a user-supplied `impl FnOnce` closure, roughly like this: ```rust // Not showing all the `'lifetime` tracking, the point is that // this closure might live shorter than `thread`. fn thread(control: ..., closure: impl FnOnce() + 'lifetime) { closure(); control.signal_done(); // A lot of time can pass here. } ``` The closure has a non-`'static` lifetime, meaning clients can capture references to on-stack data. The surrounding code ensure that `'lifetime` lasts at least until `signal_done` is triggered, which ensures that the closure never accesses dangling data. However, note that `thread` continues to run even after `signal_done`! Now consider what happens if the closure captures a reference of lifetime `'lifetime`: - The type of `closure` is a struct (the implicit unnameable closure type) with a `&'lifetime mut T` field. References passed to a function must be live for the entire duration of the call. - The closure runs, `signal_done` runs. Then -- potentially -- this thread gets scheduled away and the main thread runs, seeing the signal and returning to the user. Now `'lifetime` ends and the memory the reference points to might be deallocated. - Now we have UB! The reference that as passed to `thread` with the promise of remaining live for the entire duration of the function, actually got deallocated while the function still runs. Oops. ### Example 3 As a third example, consider a type that wants to store a "pointer together with some data borrowed from that pointer", like the `owning_ref` crate. This will usually boil down to something like this: ```rust unsafe trait StableDeref: Deref {} struct OwningRef<U, T: StableDeref<Target=U>> { buffer: T, ref_: NonNull<U>, // conceptually borrows from `buffer`. } ``` Such a type is unsound when `T` is `&mut U` or `Box<U>` because those types are assumed by the compiler to be unique, so any time `OwningRef` is passed around, the compiler can assume that `buffer` is a unique pointer -- an assumption that this code breaks because `ref_` points to the same memory! ### Goal of this RFC The goal of this RFC is to - make the first example UB-free without code changes - make the second example UB-free without needing to add `unsafe` code - make it possible to define a type like the third example (Making the 2nd example UB-free without code changes would incur cost across the ecosystem, see the alternatives discussed below.) The examples described above are far from artificial, here are some real-world crates that need `MaybeDangling` to ensure their soundness (some currently crudely work-around that problem with `MaybeUninit` but that is really not satisfying): - [Yoke](https://github.com/unicode-org/icu4x/issues/3696) and [Yoke again](https://github.com/unicode-org/icu4x/issues/2095) (the first needs opting-out of `dereferenceable` for the yoke, the latter needs opting-out of `noalias` for both yoke and cart) - [ouroboros](https://github.com/joshua-maros/ouroboros/issues/88) # Guide-level explanation [guide-level-explanation]: #guide-level-explanation To handle situations like this, Rust has a special type called `MaybeDangling<P>`: references and boxes in `P` do *not* have to be dereferenceable or follow aliasing guarantees. This applies inside nested references/boxes inside `P` as well. They still have to be non-null and aligned, and it has to at least be *possible* that there exists valid data behind that reference (i.e., `MaybeDangling<&!>` is still invalid). Also note that safe code can still generally assume that every `MaybeDangling<P>` it encounters is a valid `P`, but within unsafe code this makes it possible to store data of arbitrary type without making reference guarantees (this is similar to `ManuallyDrop`). In other words, `MaybeDangling<P>` is entirely like `P`, except that the rules that relate to the contents of memory that pointers in `P` point to (dereferencability and aliasing restrictions) are suspended when the pointers are not being actively used. You can think of the `P` as being "suspended" or "inert". The `ManuallyDrop<T>` type internally wraps `T` in a `MaybeDangling`. This means that the first example is actually fine: the dangling `Box` was passed inside a `ManuallyDrop`, so there is no UB. The 2nd example can be fixed by passing the closure in a `MaybeDangling`: ```rust // Argument is passed as `MaybeDangling` since we might actually keep // it around after its lifetime ends (at which point the caller can // start dropping memory it points to). fn thread(control: ..., closure: MaybeDangling<impl FnOnce() + 'lifetime>) { closure.into_inner()(); control.signal_done(); // A lot of time can pass here. } ``` The 3rd example can be fixed by storing the `buffer` inside a `MaybeDangling`, which disables its aliasing requirements: ```rust struct OwningRef<U, T: StableDeref<Target=U>> { buffer: MaybeDangling<T>, ref_: NonNull<U>, // conceptually borrows from `buffer`. } ``` As long as the `buffer` field is not used, the pointer stored in `ref_` will remain valid. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation The standard library contains a type `MaybeDangling<P>` that is safely convertible with `P` (i.e., the safety invariant is the same), and that has all the same niches as `P`, but that does allow passing around dangling boxes and references within unsafe code. `MaybeDangling<P>` propagates auto traits, drops the `P` when it is dropped, and has (at least) `derive(Copy, Clone, Debug)`. "Behavior considered undefined" is adjusted as follows: ```diff * Breaking the [pointer aliasing rules]. `Box<T>`, `&mut T` and `&T` follow LLVM’s scoped noalias model, except if the `&T` contains an [`UnsafeCell<U>`]. References must not be dangling while they are live. (The exact liveness duration is not specified, but it is certainly upper-bounded by the syntactic lifetime assigned by the borrow checker. When a reference is passed to a function, it is live at least as long as that function call, again except if the `&T` contains an [`UnsafeCell<U>`].) All this also applies when values of these types are passed in a (nested) field of a compound type, but not behind - pointer indirections. + pointer indirections and also not for values inside a `MaybeDangling<_>`. [...] * Producing an invalid value, even in private fields and locals. "Producing" a value happens any time a value is assigned to or read from a place, passed to a function/primitive operation or returned from a function/primitive operation. The following values are invalid (at their respective type): [...] - * A reference or Box<T> that is dangling, unaligned, or points to an - invalid value. + * A reference or `Box<T>` that is unaligned or null, or whose pointee + type `T` is uninhabited. Furthermore, except when this value occurs + inside a `MaybeDangling`, if the reference/`Box<T>` is dangling or points + to an invalid value, it is itself invalid. ``` *Note: this diff is based on [an updated version of the reference](https://github.com/rust-lang/reference/pull/1290).* Another way to think about this is: most types only have "by-value" requirements for their validity, i.e., they only require that the bit pattern be of a certain shape. References and boxes are the sole exception, they also require some properties of the memory they point to (e.g., they need to be dereferenceable). `MaybeDangling<T>` is a way to "truncate" `T` to its by-value invariant, which changes nothing for most types, but means that references and boxes are allowed as long as their bit patterns are fine (aligned and non-null) and as long as there *conceivably could be* a state of memory that makes them valid (`T` is inhabited). codegen is adjusted as follows: - When computing LLVM attributes, we traverse through newtypes such that `Newtype<&mut i32>` is marked as `dereferenceable(4) noalias aligned(4)`. When traversing below `MaybeDangling`, no memory-related attributes such as `dereferenceable` or `noalias` are emitted. Other value-related attributes such as `aligned` are still emitted. (Really this happens as part of computing the `ArgAttributes` in the function ABI, and that is the code that needs to be adjusted.) Miri is adjusted as follows: - During Stacked Borrows retagging, when recursively traversing the value to search for references and boxes to retag, we stop the traversal when encountering a `MaybeDangling`. (Note that by default, Miri will not do any such recursion, and only retag bare references. But that is not sound, given that we do emit `noalias` for newtyped references and boxes. The `-Zmiri-retag-fields` flag makes retagging "peer into" compound types to retag all references it can find. This flag needs to become the default to make Miri actually detect all UB in the LLVM IR we generate. This RFC says that that traversal stops at `MaybeDangling`.) ### Comparison with some other types that affect aliasing - `UnsafeCell`: disables aliasing (and affects but does not fully disable dereferenceable) behind shared refs, i.e. `&UnsafeCell<T>` is special. `UnsafeCell<&T>` (by-val, fully owned) is not special at all and basically like `&T`; `&mut UnsafeCell<T>` is also not special. - [`UnsafeAliased`](https://github.com/rust-lang/rfcs/pull/3467): disables aliasing (and affects but does not fully disable dereferenceable) behind mutable refs, i.e. `&mut UnsafeAliased<T>` is special. `UnsafeAliased<&mut T>` (by-val, fully owned) is not special at all and basically like `&mut T`; `&UnsafeAliased<T>` is also not special. - `MaybeDangling`: disables aliasing and dereferencable *of all references (and boxes) directly inside it*, i.e. `MaybeDangling<&[mut] T>` is special. `&[mut] MaybeDangling<T>` is not special at all and basically like `&[mut] T`. # Drawbacks [drawbacks]: #drawbacks - For users of `ManuallyDrop` that don't need this exceptions, we might miss optimizations if we start allowing example 1. - We are accumulating quite a few of these marker types to control various aspect of Rust's validity and aliasing rules: we already have `UnsafeCell` and `MaybeUninit`, and we are likely going to need a "mutable reference version" of `UnsafeCell` to properly treat self-referential types. It's easy to get lost in this sea of types and mix up what exactly they are acting on and how. In particular, it is easy to think that one should do `&mut MaybeDangling<T>` (which is useless, it should be `MaybeDangling<&mut T>`) -- this type applies in the exact opposite way compared to `UnsafeCell` (where one uses `&UnsafeCell<T>`, and `UnsafeCell<&T>` is useless). # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives - The most obvious alternative is to declare `ManuallyDrop` to be that magic type with the memory model exception. This has the disadvantage that one risks memory leaks when all one wants to do is pass around data of some `T` without upholding reference liveness. For instance, the third example would have to remember to call `drop` on the `buffer`. This alternative has the advantage that we avoid introducing another type, and it is future-compatible with factoring that aspect of `ManuallyDrop` into a dedicated type in the future. - Another tempting alternative is to attach the special meaning not to a type, but an attribute. We could have a `#[maybe_dangling]` attribute that can be attached to ADTs, such that references and `Box` inside that type are not required to be dereferenceable or non-aliasing as the type gets moved around. This has the advantage that user can attach the attribute to their own type and directly access the fields, so e.g. `MyType` can have a `Box<T>` field and all of the magic of `Box` is still available, but the type can be moved around freely without worrying about aliasing. For the compiler and Miri implementation this would barely make a difference; we would simply stop recursing into fields when encountering any type with that attribute (rather than only stopping when encountering the magic `MaybeDangling` type). - Another alternative is to change the memory model such that the example code is fine as-is. There are several variants of this: - [Make all examples legal] All newtype wrappers behave the way `MaybeDangling` is specified in this RFC. This means it is impossible to do zero-cost newtype-wrapping of references and boxes, which is against the Rust value of zero-cost abstractions. It is also a non-compositional surprise for type semantics to be altered through a newtype wrapper. - [Make examples 1+2 legal] Or we leave newtype wrappers untouched, but rule that boxes (and references) don't actually have to be dereferenceable. This is just listed for completeness' sake, removing all those optimizations is unlikely to make our codegen folks happy. It is also insufficient for example 3, which is about aliasing, not dereferencability. - [Make only the 2nd example legal] We could remove the part about references always being live for at least as long as the functions they are passed to. This corresponds to replacing the LLVM `dereferenceable` attribute by a (planned by not yet implemented) `dereferenceable-on-entry`, which matches the semantics of references in C++. But that does not solve the problem of the `MaybeUninit<Box<_>>` footgun, i.e., the first example. (We would have to change the rules for `Box` for that, saying it does not need to be dereferenceable at all.) Nor does it help the 3rd example. Also this loses some very desirable optimizations, such as ```rust fn foo(x: &i32) -> i32 { let val = *x; bar(); return val; // optimize to `*x`, avoid saving `val` across the call. } ``` Under the adjusted rules, `x` could stop being live in the middle of the execution of `foo`, so it might not be live any more when the `return` is executed. Therefore the compiler is not allowed to insert a new use of `x` there. - We could more directly expose ways to manipulate the underlying LLVM attributes (`dereferenceable`, `noalias`) using by-value wrappers. (When adjusting the pointee type, such as in `&UnsafeCell<T>`, we already provide a bunch of fine-grained control.) However there exist other backends, and LLVM attributes were designed for C/C++/Swift, not Rust. The author would argue that we should first think of the semantics we want, and then find ways to best express them in LLVM, not the other way around. And while situations are conceivable where one wants to disable only `noalias` or only `dereferenceable`, it is unclear whether they are worth the extra complexity. (On the pointee side, Rust used to have a `Unique` type, that still exists internally in the standard library, which was intended to provide `noalias` without any form of `dereferenceable`. It was deemed better to not expose this.) - Instead of saying that all fields of all compound types still must abide by the aliasing rules, we could restrict this to fields of `repr(transparent)` types. That would solve the 2nd and 3rd example without any code changes. It would make it impossible to package up multiple references (in a struct with multiple reference-typed fields) in a way that their aliasing guarantees are still in full force. Right now, we actually *do* emit `noalias` for the 2nd and 3rd example, so codegen of existing types would have to be changed under this alternative. It would not help for the first example. - Finally we could do nothing and declare all examples as intentional UB. The 2nd and 3rd example could use `MaybeUninit` to pass around the closure / the buffer in a UB-free way. That will however require `unsafe` code, and leaves `ManuallyDrop<Box<T>>` with its footgun (1st example). # Prior art [prior-art]: #prior-art The author cannot think of prior art in other languages; the issue arises because of Rust's unique combination of strong safety guarantees with low-level types such as `ManuallyDrop` that manage memory allocation in a very precise way. Inside Rust, we do have precedent for wrapper types altering language semantics; most prominently, there are `UnsafeCell` and `MaybeUninit`. Notice that `UnsafeCell` acts "behind references" while `MaybeDangling`, like `MaybeUninit`, acts "around references": `MaybeDangling<&T>` vs `&UnsafeCell<T>`. # Unresolved questions [unresolved-questions]: #unresolved-questions - What should the type be called? `MaybeDangling` is somewhat misleading since the safety invariant still requires everything to be dereferenceable, only the requirement of dereferencability and noalias is relaxed. This is a bit like `ManuallyDrop` which supports dropping via an `unsafe` function but its safety invariant says that the data is not dropped (so that it can implement `Deref` and `DerefMut` and a safe `into_inner`). Furthermore, the type also allows maybe-aliasing references, not just maybe-dangling references. Other possible names might be things like `InertPointers` or `SuspendedPointers`. - Should `MaybeDangling` implement `Deref` and `DerefMut` like `ManuallyDrop` does, or should accessing the inner data be more explicit since that is when the aliasing and dereferencability requirements do come back in full force? # Future possibilities [future-possibilities]: #future-possibilities - None that the author can think of -- this arguably closes a gap in our ability to express and manipulate the aliasing guarantees of types that are being passed around. --- # Design meeting minutes Attendance: TC, pnkfelix, scottmcm, waffle, yosh, RalfJ, tmandry Minutes, driver: TC ## Not-a-question scottmcm: I started thinking about changing validity rules, and if the "references don't need valid things behind them" affects this, but that's irrelevant because this is more about things we definitely want like the uniqueness requirements on `&mut`, so I don't think we'd want to solve this by weakening the validity rules. ## Question: Pondering the "implement `Vec` with `Box`" problems scottmcm: `Box`'s aliasing rules have made it disallowed to make `Vec<T>` be a wrapper around `Box<[MaybeUninit<T>]>`. I guess with this RFC it would be possible to do that by making it a wrapper around `MaybeDangling<Box<[MaybeUninit<T>]>>` and doing some careful pointer access to the internals? It still drops, which is one of the important gains from having the field be a `Box` instead of a pointer. But maybe the extra level of wrapping would lose basically all of the advantages of having it being a Box, since the normal Box helpers wouldn't be usable without re-introducing the aliasing issue. Ralf: Yes that sounds accurate. Almost all of `Vec` only needs `as_ptr`/`as_mut_ptr` which shouldn't be too hard to implement without re-introducing aliasing... I hope. scottmcm: Vec's implementation would never make a Box directly maybe. If I have a reference to a Box, does that introduce the problems here? RalfJ: Not under any of the aliasing semantics we currently have. SB and TB are only on the outer pointers. That's for deep reasons in how the models work. We shouldn't have aliasing for deeper references in pointers, but we maybe haven't made an official call there. scottmcm: It sounds like this would still be elegant. RalfJ: ... RalfJ: If we put Box into Vec, we still want this. scottmcm: +1. ## Question: The purpose of `ManuallyDrop`? (resolved) > Given that `ManuallyDrop` is specifically designed to allow dropping the `Box` early, this is a big footgun (that people do [run into in practice](https://github.com/rust-lang/miri/issues/1508)) yosh: Wait, is that what `ManuallyDrop` was specifically designed for? I'm a little confused about the motivating example here I guess? Ralf: I do think that calling `drop` on a `ManuallyDrop<Box<T>>`, and then moving that variable, was meant to be allowed, yes. yosh: Oh ok, it's to show that merely observing the bytes by another function after dropping is UB. Which should be fine since none of this becomes part of the public API. I had to go through the `ManuallyDrop` docs to fully get that. Ralf: It's not just "observing in another function", a mere "let x = y;" will trip Miri. ## Question: Consuming values as in Example 2 scott: That example is calling as by-`self` method, so if I'm understanding properly the value has already been consumed before the "time passes here" part? Can we do something to say that already-moved-out-of variables never need to meet these rules? (I guess that gets into the whole "what does moving even mean" question, and things like whether pointers to moved-from values can be read from to get bitvalid-but-maybe-not-safe values...) Ralf: For references we have `dereferenceable noalias` that apply throughout the entire function. If we want to say that a mutable reference that was moved out no longer has such restrictions, we'll have to stop emitting at least `dereferenceable` to LLVM. (`Box` is not a problem for this example.) scott: Ah, because LLVM doesn't have it for scopes, I see. Thanks. ## Question: Is the recursive descent based on pre-monomorphized type or post-monomorphization? pnkfelix: I'm not sure I understand all the implications of "directly inside it" in the following: > - `MaybeDangling`: disables aliasing and dereferencable *of all references (and boxes) directly inside it*, i.e. `MaybeDangling<&[mut] T>` is special. `&[mut] MaybeDangling<T>` is not special at all and basically like `&[mut] T`. In part because I'm not sure whether I can read the `T` there as an uninstantiated type-parameter, or as a place-holder for a concrete type expression that may have its own substructure. pnkfelix: For example: what about `MaybeDangling<Box<Vec<&mut i32>>>` ? Does the `Vec` there act just like a `Box` since it owns its contents? Or do `Box` and `Vec` differ here in some way that I'm not understanding yet? scottmcm: Based on this paragraph, I interpret it as the monomorphized type, like when you use `UnsafeCell<T>`: > When computing LLVM attributes, we traverse through newtypes such that `Newtype<&mut i32>` is marked as `dereferenceable(4) noalias aligned(4)`. When traversing below `MaybeDangling`, no memory-related attributes such as `dereferenceable` or `noalias` are emitted. Ralf: Yes everything in opsem is post-monomorphization. For the example, `T` is universally quantified, i.e. the statement holds for any choice of `T`. pnkfelix: Is it correct that under this, `Box<T>` is special, but for `Vec<T>`, this specialness does not descent into the elements of the `Vec`? RalfJ: Yes, that's correct. But this gets into another question, which is what are the aliasing requirements on `Vec`. pnkfelix: I'd like it if people could predict how deep the recursion go. RalfJ: We don't recurse into pointer indirections. Whether we do that is orthogonal to this RFC. RalfJ: Here, when we hit a `MaybeDangling` we stop recursing. RalfJ: In this document, "directly" means "not through a pointer indirection". waffle: One way to think about it is that we flatten the types. We have a list of fields, then we turn those into boxes and references. RalfJ: Aliasing and dereferencable don't apply to indirect elements, i.e. things behind pointers. RalfJ: MaybeUninit is a crude workaround for the lack of MaybeDangling right now because it too stops the recursion, but it does other things. ## Question: Should `MaybeDangling` implement `Deref[Mut]`? > This \[the attribute version of maybe dangling\] has the advantage that user can attach the attribute to their own type and directly access the fields, so e.g. `MyType` can have a `Box<T>` field and all of the magic of `Box` is still available, but the type can be moved around freely without worrying about aliasing. waffle: wouldn't we have the same benefits by just implementing `Deref<Target = T>` & `DerefMut` for `MaybeDangling<T>`? (similarly to how we do with `ManuallyDrop`) Ralf: No, that would lose some `Box` magic, such as partial moves. waffle: Is this magic important? Deref and DerefMut provide us most of the benefits that we would get with an attribute. RalfJ: Porting from Box to MaybeDangling of Box could be tricky because of this magic. waffle: Can we implement the same magic for MaybeDangling that we do for Box? RalfJ: There's probably not much desire for more magic like Box. ## Question: MaybeDangling vs #[maybe_dangling] TC: RalfJ brings up in the document that this may be better as an attribute. This was discussed during a T-opsem meeting and the feeling there was that an attribute had some advantages. What do we think? TC: The main point in favor of the attribute is that, since we have a number of these marker types, the order in which they are layered becomes somewhat arbitrary. These properties are actually commutative, but of course the type system doesn't know that, and it cares about the order in which these are composed. RalfJ: Mario suggested this. Those were the arguments. ... RalfJ: We'd still have the MaybeDangling type, but we'd also have the attribute. waffle: Would an attribute have the same problems as the types we currently have in terms of being.... Currently there are a lot of wrapper types. If we add attributes for all of them, maybe that's the same problem. RalfJ: If you have two wrapper types, you have to layer them in some order. But with attributes, you just add the attributes. scottmcm: If we're going to have the type anyway, we can always choose later whether to expose an attribute on a type for it. So I think if we're sure we need the type regardless, then we can punt the attribute question to later after we have stable experience with the type. pnkfelix: If we did an attribute, we should consider adding such attributes for the others. TC: If we had the attribute, why would we have the type? RalfJ: It's for example 2. We wouldn't actually have to have the type, but then you'd have to define it for example 2. waffle: How would this affect learning? RalfJ: The documentation tooling is better for types, but maybe we shouldn't design the language around the current documentation tooling. (Discussion about how the documentation tools could better handle this.) waffle: Maybe I was asking more about how we teach this? RalfJ: This is maybe orthogonal to the MaybeDangling semantic question. ## Question: What other squares are we missing on this matrix? TC: As RalfJ brings up in the document, we have a number of these marker traits. It'd be nice if we could ensure these are orthogonal. What does the full matrix look like (including the others under consideration), and what squares are we possibly missing? RalfJ: NoAlias and Dereferencable are themselves not orthogonal, so that's one thing. Dimensions: - Alias, noalias - deferencable at function entry, dereferencable for the entire function, and not dereferencable E.g., Box only gets dereferencable at entry. TC: We should also write out a matrix of what we have covered with all of `MaybeDangling`, `UnsafeAliased`, `ManuallyDrop`, `MaybeUninit`, `UnsafeCell`, etc. ## Question: What does the first unresolved question mean? waffle: I'm not sure what this means: > What should the type be called? `MaybeDangling` is somewhat misleading since the safety invariant still requires everything to be **dereferenceable**, only the requirement of **dereferencability** and noalias is relaxed. The highlighted parts seem to contradict each other. Ralf: That was indeed not worded properly. The *validity* requirement is relaxed, the *safety* requirement is not. ## Question: Missed optimizations on `ManuallyDrop` TC: The document notes: > - For users of `ManuallyDrop` that don't need this exceptions, we might miss optimizations if we start allowing example 1. Do we have any options that let users keep these optimizations when wanted? I.e., is there any way to make this more orthogonal? RalfJ: If ManuallyDrop were an attribute an attribute, that would do it. ## Question: What would the API look like? TC: The document doesn't propose any API for this type. RalfJ, what do you have in mind? RalfJ: It'd be mostly the same as `ManuallyDrop`, but without the unsafe drop. ## Question: How are we feeling? TC: How are we feeling about this? scottmcm: It's well motivated. We should do it. pnkfelix: Agreed. I'm wondering about the attribute alternative. scottmcm: Maybe the type would be more OK because of Deref. scottmcm: We can FCP the semantic. We could always rename it or do the attribute. scottmcm: I'll propose FCP merge. (The meeting ended here.)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    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.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully