Skip to content

Commit 56f394b

Browse files
committed
Integrate centril's review
1 parent 9edf8ec commit 56f394b

File tree

1 file changed

+87
-22
lines changed

1 file changed

+87
-22
lines changed

const-generic-const-fn-bounds.md

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ impl const Add for MyInt {
4646
}
4747
```
4848

49+
You cannot implement both `const Add` and `Add` for any type, since the `const Add`
50+
impl is used as a regular impl outside of const contexts.
51+
4952
The const requirement is inferred on all bounds of the impl and its methods,
5053
so in the following `H` is required to have a const impl of `Hasher`, so that
5154
methods on `state` are callable.
@@ -102,9 +105,16 @@ bounds for types substituted for `T`.
102105

103106
## Drop
104107

105-
A notable use case of `impl const` is defining `Drop` impls. If you write
108+
A notable use case of `impl const` is defining `Drop` impls.
109+
Since const evaluation has no side effects, there is no simple example that
110+
showcases `const Drop` in any useful way. Instead we create a `Drop` impl that
111+
has user visible side effects:
106112

107113
```rust
114+
let x = Cell::new(42);
115+
SomeDropType(&x);
116+
// x is now 41
117+
108118
struct SomeDropType<'a>(&'a Cell<u32>);
109119
impl const Drop for SomeDropType {
110120
fn drop(&mut self) {
@@ -113,8 +123,14 @@ impl const Drop for SomeDropType {
113123
}
114124
```
115125

116-
Then you are allowed to actually let a value of `SomeDropType` get dropped within a constant
117-
evaluation. This means `(SomeDropType(&Cell::new(42)), 42).1` is now allowed, because we can prove
126+
You are now allowed to actually let a value of `SomeDropType` get dropped within a constant
127+
evaluation. This means
128+
129+
```rust
130+
(SomeDropType(&Cell::new(42)), 42).1
131+
```
132+
133+
is now allowed, because we can prove
118134
that everything from the creation of the value to the destruction is const evaluable.
119135

120136
Note that all fields of types with a `const Drop` impl must have `const Drop` impls, too, as the
@@ -140,11 +156,24 @@ impl<T: Add> const Add for Foo<T> {
140156
Foo(self.0 + other.0)
141157
}
142158
}
159+
#[derive(Debug)]
160+
struct Bar;
161+
impl Add for Bar {
162+
fn add(self, other: Self) -> Self {
163+
println!("hello from the otter side: {:?}", other);
164+
self
165+
}
166+
}
167+
impl Neg for Bar {
168+
fn neg(self) -> Self {
169+
self
170+
}
171+
}
143172
```
144173

145-
allows calling `Foo(String::from("foo")) + Foo(String::from("bar"))` even though that is (at the time
146-
of writing this RFC) most definitely not const, because `String` only has an `impl Add for String`
147-
and not an `impl const Add for String`. Expressed in some sort of effect system syntax (neither
174+
allows calling `Foo(Bar) + Foo(Bar)` even though that is most definitely not const,
175+
because `Bar` only has an `impl Add for Bar`
176+
and not an `impl const Add for Bar`. Expressed in some sort of effect system syntax (neither
148177
effect syntax nor effect semantics are proposed by this RFC, the following is just for demonstration
149178
purposes):
150179

@@ -159,25 +188,25 @@ impl<c: constness, T: const(c) Add> const(c) Add for Foo<T> {
159188
In this scheme on can see that if the `c` parameter is set to `const`, the `T` parameter requires a
160189
`const Add` bound, and creates a `const Add` impl for `Foo<T>` which then has a `const fn add`
161190
method. On the other hand, if `c` is `?const`, we get a regular impl without any constness anywhere.
162-
Of course for regular impls one can still pass a `T` which has a `const Add` impl, but that won't
191+
For regular impls one can still pass a `T` which has a `const Add` impl, but that won't
163192
cause any constness for `Foo<T>`.
164193

165194
This goes in hand with the current scheme for const functions, which may also be called
166195
at runtime with runtime arguments, but are checked for soundness as if they were called in
167196
a const context. E.g. the following function may be called as
168-
`add(String::from("foo"), String::from("bar"))` at runtime.
197+
`add(Bar, Bar)` at runtime.
169198

170199
```rust
171-
const fn add<T: Add>(a: T, b: T) -> T {
172-
a + b
200+
const fn add<T: Neg, U: Add<T>>(a: T, b: U) -> T {
201+
-a + b
173202
}
174203
```
175204

176205
Using the same effect syntax from above:
177206

178207
```rust
179-
<c: constness> const(c) fn add<T: const(c) Add>(a: T, b: T) -> T {
180-
a + b
208+
<c: constness> const(c) fn add<T: const(c) Neg, U: const(c) Add<T>>(a: T, b: U) -> T {
209+
-a + b
181210
}
182211
```
183212

@@ -225,7 +254,7 @@ in an `impl`. This has several uses, most notably
225254
In order to keep both advantages in the presence of `impl const`s, we need a way to declare the
226255
method default body as being `const`. The exact syntax for doing so is left as an open question to
227256
be decided during the implementation and following final comment period. For now one can add the
228-
`#[default_method_body_is_const]` attribute to the method.
257+
placeholder `#[default_method_body_is_const]` attribute to the method.
229258

230259
```rust
231260
trait Foo {
@@ -286,7 +315,7 @@ but it covers the most common cases. See also the alternatives.
286315
## Effect system
287316

288317
A fully powered effect system can allow us to do fine grained constness propagation
289-
(or no propagation where undesirable). This is way out of scope in the near future
318+
(or no propagation where undesirable). This is out of scope in the near future
290319
and this RFC is forward compatible to have its background impl be an effect system.
291320

292321
## Fine grained `const` annotations
@@ -295,13 +324,13 @@ One could annotate methods instead of impls, allowing just marking some method i
295324
as const fn. This would require some sort of "const bounds" in generic functions that
296325
can be applied to specific methods. E.g. `where <T as Add>::add: const` or something of
297326
the sort. This design is more complex than the current one and we'd probably want the
298-
current one as sugar anyway
327+
current one as sugar anyway.
299328

300329
## Require `const` bounds everywhere
301330

302331
One could require `const` on the bounds (e.g. `T: const Trait`) instead of assuming constness for all
303332
bounds. That design would not be forward compatible to allowing `const` trait bounds
304-
on non-const functions, e.g. in
333+
on non-const functions, e.g. in:
305334

306335
```rust
307336
fn foo<T: const Bar>() -> i32 {
@@ -318,7 +347,7 @@ annotate methods in trait impls, but we would not block calling a function on wh
318347
generic parameters fulfill some sort of constness rules. Instead we'd catch this during
319348
const evaluation.
320349

321-
This is strictly the most powerful and generic variant, but is an enormous backwards compatibility
350+
This is strictly the least restrictive and generic variant, but is a semver
322351
hazard as changing a const fn's body to suddenly call a method that it did not before can break
323352
users of the function.
324353

@@ -331,7 +360,8 @@ about. Notable mentions (see also the alternatives section):
331360
* const trait bounds on non-const functions allowing the use of the generic parameter in
332361
constant expressions in the body of the function or maybe even for array lenghts in the
333362
signature of the function
334-
* fine grained bounds for single methods and their bounds
363+
* fine grained bounds for single methods and their bounds (e.g. stating that a single method
364+
is const)
335365

336366
It might also be desirable to make the automatic `Fn*` impls on function types and pointers `const`.
337367
This change should probably go in hand with allowing `const fn` pointers on const functions
@@ -483,17 +513,18 @@ themselves known:
483513

484514
is already legal in Rust today, even though the `F` doesn't need to be a `const` function.
485515

486-
2. Opt out bounds are ugly
487-
488-
I don't think it's either intuitive nor readable to write the following
516+
2. Opt out bounds might seem unintuitive?
489517

490518
```rust
491519
const fn foo(f: ?const fn() -> i32) -> i32 {
492520
// not allowed to call `f` here, because we can't guarantee that it points to a `const fn`
493521
}
522+
const fn foo(f: fn() -> i32) -> i32 {
523+
f()
524+
}
494525
```
495526

496-
Thus it seems useful to prefix function pointers to `const` functions with `const`:
527+
Alternatively one can prefix function pointers to `const` functions with `const`:
497528

498529
```rust
499530
const fn foo(f: const fn() -> i32) -> i32 {
@@ -530,8 +561,42 @@ fn foo<T: const Bar>() -> i32 {
530561
Which, once `const` items and array lengths inside of functions can make use of the generics of
531562
the function, would allow the above function to actually exist.
532563

564+
## `dyn Trait`
565+
566+
A natural extension to this RFC is to allow
567+
568+
```rust
569+
const fn foo(bar: &dyn Trait) -> SomeType {
570+
bar.some_method()
571+
}
572+
```
573+
574+
with an opt out via `?const`
575+
576+
```rust
577+
const fn foo(bar: &dyn ?const Trait) -> SomeType {
578+
bar.some_method() // ERROR
579+
}
580+
```
581+
533582
# Unresolved questions
534583
[unresolved-questions]: #unresolved-questions
535584

536585
The syntax for specifying that a trait method's default body is `const` is left unspecified and uses
537586
the `#[default_method_body_is_const]` attribute as the placeholder syntax.
587+
588+
## Implied bounds
589+
590+
Assuming we have implied bounds on functions or impl blocks, will the following compile?
591+
592+
```rust
593+
struct Foo<T: Add> {
594+
t: T,
595+
u: u32,
596+
}
597+
598+
/// T has implied bound `Add`, but is that `const Add` or `?const Add` or `!const Add`?
599+
const fn foo<T>(foo: Foo<T>, bar: Foo<T>) -> T {
600+
foo.t + bar.t
601+
}
602+
```

0 commit comments

Comments
 (0)