Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a458996
first draft of pointer to fields
RustyYato Jun 5, 2019
7eb7ea4
added to the drawbacks and later sections
RustyYato Jun 5, 2019
7ada603
expanded up reference level explanation
RustyYato Jun 5, 2019
6a9405f
updated formatting and added reference to InitPtr
RustyYato Jun 5, 2019
9b98d97
updated parts about field types, updated drawbacks
RustyYato Jun 5, 2019
b816798
added note about the `Copy` trait.
RustyYato Jun 5, 2019
0eb3d5c
added unsafe to `Field` trait
RustyYato Jun 6, 2019
9d82646
added unresolved question: minimizing proposal
RustyYato Jun 6, 2019
a1b545e
Added `?Sized` bounds
RustyYato Jun 6, 2019
8b5e4e2
added some details abot field types
RustyYato Jun 7, 2019
14fc49f
reworked the RFC's presentation
RustyYato Jun 7, 2019
d3cac45
fixed the pin-projection example in guid-level
RustyYato Jun 7, 2019
d001442
fixed typo
RustyYato Jun 7, 2019
d671633
removed `trait Project` and `trait PinProjectable`
RustyYato Jun 24, 2019
08d9ec2
added inverse projections
RustyYato Jun 24, 2019
00de06a
updated motivation
RustyYato Jun 24, 2019
43d559b
reason for removing `Project` and `PinProjectable`
RustyYato Jun 24, 2019
7c5161a
merge
RustyYato Jun 24, 2019
a83911c
moved from `std` to `core`
RustyYato Jun 24, 2019
6da5da2
Update text/0000-ptr-to-field.md
RustyYato Jun 25, 2019
97e2a7f
Update text/0000-ptr-to-field.md
RustyYato Jun 25, 2019
9d78b37
updated wording based off of @CAD97's suggestions
RustyYato Jun 25, 2019
f362e75
Merge branch 'ptr-to-field' of https://github.com/KrishnaSannasi/rfcs…
RustyYato Jun 25, 2019
dece0b1
editted note about pointer metadata
RustyYato Jun 25, 2019
2e77eef
Update text/0000-ptr-to-field.md
RustyYato Jun 25, 2019
e0e7e80
Apply suggestions from code review
RustyYato Jun 25, 2019
da3afb6
Some more changes from @CAD97's review
RustyYato Jun 25, 2019
21cf26b
added note about enum variants as types
RustyYato Jun 25, 2019
6b31b65
Merge branch 'ptr-to-field' of https://github.com/KrishnaSannasi/rfcs…
RustyYato Jun 25, 2019
00b4d4b
fixed inverse projecting UB examples
RustyYato Jun 25, 2019
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
Prev Previous commit
fixed inverse projecting UB examples
formatting
added note about the future possiblity of the Project trait.
  • Loading branch information
RustyYato committed Jun 25, 2019
commit 00b4d4bfc1ac490eb60162e302a39bbb0ad786f6
27 changes: 19 additions & 8 deletions text/0000-ptr-to-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ impl<T: ?Sized> *mut T {
These will allowing projections through raw pointers without dereferencing the raw pointer. This is useful for building projections through other abstractions like smart pointers (`Rc<T>`, `Pin<&T>`)!

Using this we can do something like this

```rust
struct Foo {
bar: Bar,
Expand All @@ -111,7 +112,7 @@ let y_bar_name: *const String = unsafe { y.project_unchecked(Foo.bar).project_un

In the end `y_bar_name` will contain a pointer to `x.bar.name`, all without dereferencing a single pointer! (Given that this is a verbose, we may want some syntax for this, but that is out of scope for this RFC)

But, we can build on this foundation and create a more power abstraction, to generalize this project notion to smart pointers.
But, we can build on this foundation and create a more power abstraction, to generalize this project notion to smart pointers. See future possibilities section for some ideas about how this could work, via the `Project` trait.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation
Expand All @@ -123,6 +124,7 @@ This section will go in detail about the semantics and implementation of the typ
Field types are just sugar for unit types (`struct Foo;`) that are declared right next to the `Parent` type.

Like so,

```rust
struct Person {
pub name: String,
Expand Down Expand Up @@ -156,7 +158,7 @@ The compiler can decide whether to actual generate a field type, this is to help

`project_unchecked`, `wrapping_project`, `inverse_project_unchecked`, and `inverse_wrapping_project` will all be inherent methods on `*const T` and `*mut T`. They will be implemented on top of intrinsics (names of which don't matter), and those intrinsics will live in `core::intrinsics` (as all intrinsics do).

raw pointer projections need to be implemented as intrinsics because there is no way to assert that the pointer metadata for fat pointers of `Field::Parent` and `Field::Type` will always match in general without some other compiler support. This is necessary to allow unsized types to be used transparently with this scheme. (See more details in next section).
Raw pointer projections need to be implemented as intrinsics because there is no way to assert that the pointer metadata for fat pointers of `Field::Parent` and `Field::Type` will always match in general without some other compiler support. This is necessary to allow unsized types to be used transparently with this scheme. (See more details in next section).

We need both `project_unchecked` and `wrapping_project` because there are some important optimization available inside of LLVM related to aliasing and escape analysis. In particular the LLVM `inbounds` assertion tells LLVM that a pointer offset stays within the same allocation and if the pointer is invalid or the offset does not stay within the same allocation it is considered UB. This behaviour is exposed via `project_unchecked`. This can be used when the pointer is known without a doubt to be valid, such as when derived from `&T`, to enable better codegen. `wrapping_project` on the other hand will not assert `inbounds`, and will just wrap around if the pointer offset is larger than `usize::max_value()`. This safe defined behaviour, even if it is almost always a bug, unlike `project_unchecked` which is UB on invalid pointers or offsets.

Expand Down Expand Up @@ -192,21 +194,21 @@ struct Foo {
bar: Bar
}

let x : *const Foo = 2usize as *const Bar;
let y : *const Bar = x.inverse_project_unchecked(Foo.bar); // UB, x does not point to a valid instance of Bar
let x : *const Bar = 2usize as *const Bar;
let y : *const Foo = x.inverse_project_unchecked(Foo.bar); // UB, x does not point to a valid instance of Bar

let v : Bar = Bar(...);
let v : *const Bar = &v;
let y : *const Bar = v.inverse_project_unchecked(Foo.bar); // UB, v does not point to a field of Foo
let y : *const Foo = v.inverse_project_unchecked(Foo.bar); // UB, v does not point to a field of Foo
```

With `inverse_wrapping_project`

```rust

let y : *const Bar = x.inverse_wrapping_project(Foo.bar); // not UB, but y is invalid
let y : *const Foo = x.inverse_wrapping_project(Foo.bar); // not UB, but y is invalid

let y : *const Bar = v.inverse_wrapping_project(Foo.bar); // not UB, but y is invalid
let y : *const Foo = v.inverse_wrapping_project(Foo.bar); // not UB, but y is invalid
```

If the raw pointer is valid, then the result of both `project_unchecked` and `wrapping_project` is a raw pointer to the given field.
Expand All @@ -232,13 +234,15 @@ The `Field` trait will only be implemented by the compiler, and it compiler shou
[rationale-and-alternatives]: #rationale-and-alternatives

- The `&[mut] raw T` could solve some of the problems, but only for raw pointers. It doesn't help with abstractions.

- Somehow expand on `Deref` to allow dereferencing to a smart pointer
- This would require Generic Associated Types at the very least, and maybe some other features like assocaited traits

# Prior art
[prior-art]: #prior-art

- C++'s pointer to members `Parent::*field`

- Java's `class Field`
- Similar reflection capabilies in other languages

Expand All @@ -250,16 +254,20 @@ The `Field` trait will only be implemented by the compiler, and it compiler shou
- Some other variations of the syntax are ...
- `Type::field` // This is bad because it conflicts with associated method, and there isn't a way to disambiguate them easily
- `Type~field` // This adds a new sigil to the language

- How will pointer metadata be handled for more exotic Custom DSTs?
- see @CAD97's example [here](https://github.com/rust-lang/rfcs/pull/2708#discussion_r296933269)
> Note that this doesn't require equivalent metadata: a theoretical pointer with two metadatas could be split into two child fat pointers with one or the other metadata.

# Future possibilities
[future-possibilities]: #future-possibilities

- [`InitPtr`](https://internals.rust-lang.org/t/idea-pointer-to-field/10061/72), which encapsulates all of the safety requirements of `project_unchecked` into `InitPtr::new` and safely implements `Project`
- [`InitPtr`](https://internals.rust-lang.org/t/idea-pointer-to-field/10061/72), which encapsulates all of the safety requirements of `project_unchecked` into `InitPtr::new` and safely implements `Project` (see below for `Project`)

- Distant Future, we could reformulate `Copy` based on the `Field` trait so that it enforces that all of the fields of a type must be `Copy` in order to be the type to be `Copy`, and thus reduce the amount of magic in the compiler.

- Integration with Custom DSTs

- Integration with a possible Enum Variants as Types feature
- This will allow projecting to enum variants safely

Expand Down Expand Up @@ -303,6 +311,7 @@ unsafe trait PinProjectable {}
Due to some safety requirements that will be detailed in the reference-level explanation, we can't just freely hand out pin projections to every type (sad as it is). To enable pin projections to a field, a field type must implement `PinProjectable`.

Like so,

```rust
unsafe impl PinProjectable for Foo.field {}
```
Expand All @@ -311,6 +320,7 @@ unsafe impl PinProjectable for Foo.field {}

Here is a toy example of how to use this api:
Given the implementation of `Project for Pin<&mut T>`

```rust
/// Some other crate foo

Expand Down Expand Up @@ -365,6 +375,7 @@ struct Foo(core::marker::PhantomPinned);

unsafe impl PinProjectable for Foo.0 {}
```

[Proof](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b0796a8b631e0fec1804318caef162c7)

I think making `PhantomPinned` a lang-item that is known to always implment `!Unpin` would solve this. This way only those who implment `!Unpin` types need to worry about implementing `PinProjectable`. Another way to solve this would be to somehow make `PinProjectable` a lang-item that allows this one case of conflicting impls. But I am unsure of how to properly handle this, both way s that I showed seem unsatisfactory. The blanket impl is highly desirable, because it enables those who don't write `!Unpin` types to ignore safe pin projections, and still have them available.
Expand Down