Skip to content

Commit 97452ca

Browse files
committed
Remove newtype coercions, make CoerceUnsized coercions more general
1 parent 4d803c1 commit 97452ca

File tree

1 file changed

+66
-58
lines changed

1 file changed

+66
-58
lines changed

text/0000-dst-coercion.md

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -51,34 +51,60 @@ An example implementation:
5151

5252
```
5353
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<Rc<U>> for Rc<T> {}
54+
impl<T: ?Sized+CoerceUnsized<U>, U: ?Sized> NonZero<U> for NonZero<T> {}
5455
55-
// For reference, the definition of Rc:
56+
// For reference, the definitions of Rc and NonZero:
5657
pub struct Rc<T: ?Sized> {
5758
_ptr: NonZero<*mut RcBox<T>>,
5859
}
60+
pub struct NonZero<T: Zeroable+?Sized>(T);
5961
```
6062

6163
Implementing `CoerceUnsized` indicates that the self type should be able to be
6264
coerced to the `Target` type. E.g., the above implementation means that
63-
`Rc<[i32; 42]>` can be coerced to `Rc<[i32]>`.
65+
`Rc<[i32; 42]>` can be coerced to `Rc<[i32]>`. There will be `CoerceUnsized` impls
66+
for the various pointer kinds available in Rust and which allow coercions, therefore
67+
`CoerceUnsized` when used as a bound indicates coercible types. E.g.,
6468

69+
```
70+
fn foo<T: CoerceUnsized<U>, U>(x: T) -> U {
71+
x
72+
}
73+
```
74+
75+
Built-in pointer impls:
6576

66-
## Newtype coercions
77+
```
78+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
79+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a> CoerceUnsized<&'a U> for Box<T> {}
80+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a> CoerceUnsized<&mut 'a U> for Box<T> {}
81+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for Box<T> {}
82+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for Box<T> {}
83+
84+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a, 'b: 'a> CoerceUnsized<&'a U> for &mut 'b U {}
85+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a> CoerceUnsized<&mut 'a U> for &mut 'a U {}
86+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a> CoerceUnsized<*const U> for &mut 'a U {}
87+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a> CoerceUnsized<*mut U> for &mut 'a U {}
6788
68-
We also add a new built-in coercion for 'newtype's. If `Foo<T>` is a tuple
69-
struct with a single field with type `T`, then coerce_inner(`Foo<T>`) = `Foo<U>`
70-
holds for any `T` and `U` where `T` coerces to `U`.
89+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'a, 'b> CoerceUnsized<&'a U> for &'b U {}
90+
impl<T: ?Sized+Unsize<U>, U: ?Sized, 'b> CoerceUnsized<*const U> for &'b U {}
7191
72-
This coercion is not opt-in. It is best thought of as an extension to the
73-
coercion rule for structs with an unsized field, the extension is that here the
74-
field conversion is a proper coercion, not an application of `coerce_inner`.
75-
Note that this coercion can be recursively applied.
92+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut U {}
93+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut U {}
94+
95+
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const U {}
96+
```
97+
98+
Note that there are some coercions which are not given by `CoerceUnsized`, e.g.,
99+
from safe to unsafe function pointers, so it really is a `CoerceUnsized` trait,
100+
not a general `Coerce` trait.
76101

77102

78103
## Compiler checking
79104

80105
### On encountering an implementation of `CoerceUnsized` (type collection phase)
81106

107+
* If the impl is for a built-in pointer type, we check nothing, otherwise...
82108
* The compiler checks that the `Self` type is a struct or tuple struct and that
83109
the `Target` type is a simple substitution of type parameters from the `Self`
84110
type (one day, with HKT, this could be a regular part of type checking, for now
@@ -87,63 +113,46 @@ form `X/Y` where `X` and `Y` are both formal type parameters of the
87113
implementation (I don't think this is necessary, but it makes checking coercions
88114
easier and is satisfied for all smart pointers).
89115
* The compiler checks each field in the `Self` type against the corresponding field
90-
in the `Target` type. Either the field types must be subtypes or be coercible from the
91-
`Self` field to the `Target` field (this is checked taking into account any
92-
`Unsize` bounds in the environment which indicate that some coercion can take
93-
place). Note that this per-field check uses only the built-in coercion
94-
mechanics. It does not take into account `CoerceUnsized` impls (although we
95-
might allow this in the future).
116+
in the `Target` type. Assuming `Fs` is the type of a field in `Self` and `Ft` is
117+
the type of the corresponding field in `Target`, then either `Ft <: Fs` or
118+
`Fs: CoerceUnsized<Ft>` (note that this includes built-in coercions).
96119
* There must be only one field that is coerced.
97-
* We record in a side table a mapping from the impl to an adjustment. The
98-
adjustment will contain the field which is coerced and a nested adjustment
99-
representing that coercion. The nested adjustment will have a placeholder for
100-
any use of the `Unsize` bound (we should require that there is exactly one such use).
120+
* We record for each impl, the index of the field in the `Self` type which is
121+
coerced.
101122

102-
### On encountering a potential coercion
123+
### On encountering a potential coercion (type checking phase)
103124

104125
* If we have an expression with type `E` where the type `F` is required during
105126
type checking and `E` is not a subtype of `F`, nor is it coercible using the
106-
built-in coercions, then we search for an implementation of `CoerceUnsized<F>`
107-
for `E`. A match will give us a substitution of the formal type parameters of
108-
the impl by some actual types.
109-
* We look up the impl in the side table described above. The substitution is used
110-
with the placeholder in the recorded adjustment to create a new coercion which
111-
will map one field of the struct being coerced. That coercion should always be
112-
valid (if it is not, there is a compiler bug).
113-
* We create a new adjustment for the coerced expression. This will include the
114-
index of the field which is deeply coerced and the adjustment for the coercion
115-
described in the previous step.
116-
* In trans, the adjustment is used to codegen a coercion by moving the coerced
117-
value and changing the indicated field to a new type according to the nested
118-
adjustment.
127+
built-in coercions, then we search for a bound of `E: CoerceUnsized<F>`. Note
128+
that we may not at this stage find the actual impl, but finding the bound is
129+
good enough for type checking.
119130

120-
### Adjustment types
131+
* If we require a coercion in the receiver of a method call or field lookup, we
132+
perform the same search that we currently do, except that where we currently
133+
check for coercions, we check for built-in coercions and then for `CoerceUnsized`
134+
bounds. We must also check for `Unsize` bounds for the case where the receiver
135+
is auto-deref'ed, but not autoref'ed.
121136

122-
We add `AdjustCustom(usize, Box<AutoAdjustment>)` and
123-
`AdjustNewtype(Box<AutoDerefRef>)` to the `AutoAdjustment` enum. These
124-
represent the new custom and newtype coercions, respectively. We add
125-
`UnsizePlaceHolder(Ty, Ty)` to the `UnsizeKind` enum to represent a placeholder
126-
adjustment due to an `Unsize` bound.
127137

128-
### Example
138+
### On encountering an adjustment (translation phase)
129139

130-
For the above `Rc` impl, we record the following adjustment (with some trivial
131-
bits and pieces elided):
140+
* In trans (which is post-monomorphisation) we should always be able to find an
141+
impl for any `CoerceUnsized` bound.
142+
* If the impl is for a built-in pointer type, then we use the current coercion
143+
code for the various pointer kinds (`Box<T>` has different behaviour than `&` and
144+
`*` pointers).
145+
* Otherwise, we lookup which field is coerced due to the opt-in coercion, move
146+
the object being coerced and coerce the field in question by recursing (the
147+
built-in pointers are the base cases).
132148

133-
```
134-
AdjustCustom(0, AdjustNewType(
135-
AutoDerefRef {
136-
autoderefs: 1,
137-
autoref: AutoUnsafe(mut, AutoUnsize(
138-
UnsizeStruct(UnsizePlaceholder(T, U))))
139-
}))
140-
```
141149

142-
When we need to coerce `Rc<[i32; 42]>` to `Rc<[i32]>`, we look up the impl and
143-
find `T = [i32; 42]` and `U = [i32]` (note that we automatically require that
144-
`Unsize` is satisfied when looking up the impl). We can therefore replace the
145-
placeholder in the above adjustment with `UnsizeLength(42)`. That gives us the
146-
real adjustment to store for trans.
150+
### Adjustment types
151+
152+
We add `AdjustCustom` to the `AutoAdjustment` enum as a placeholder for coercions
153+
due to a `CoerceUnsized` bound. I don't think we need the `UnsizeKind` enum at
154+
all now, since all checking is postponed until trans or relies on traits and impls.
155+
147156

148157
# Drawbacks
149158

@@ -164,8 +173,7 @@ intrinsics. Although more flexible, this allows for implcicit excecution of
164173
arbitrary code. If we need the increased flexibility, I believe we can add a
165174
manual option to the `CoerceUnsized` trait backwards compatibly.
166175

167-
The proposed design could be tweaked: we could make newtype coercions opt-in
168-
(this would complicate other parts of the proposal though). We could change the
176+
The proposed design could be tweaked: for example, we could change the
169177
`CoerceUnsized` trait in many ways (we experimented with an associated type to
170178
indicate the field type which is coerced, for example).
171179

0 commit comments

Comments
 (0)