Skip to content

Commit 58fbcd7

Browse files
committed
Address review comments
1 parent d424f1d commit 58fbcd7

File tree

1 file changed

+57
-25
lines changed

1 file changed

+57
-25
lines changed

const-generic-const-fn-bounds.md

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
Allow `const impl`s for trait impls where all method impls are checked as const fn.
1010

11-
Make it legal to declare trait bounds on generic parameters of `const fn` and allow
12-
the body of the const fn to call methods on these generic parameters.
11+
Make it legal to declare trait bounds on generic parameters of const functions and allow
12+
the body of the const fn to call methods on the generic parameters that have a `const` modifier
13+
on their bound.
1314

1415
# Motivation
1516
[motivation]: #motivation
@@ -21,26 +22,26 @@ generic parameter type), because they are fully unconstrained.
2122
# Guide-level explanation
2223
[guide-level-explanation]: #guide-level-explanation
2324

24-
You can call call methods of generic parameters of `const fn`, because they are implicitly assumed to be
25-
`const fn`. For example, even though the `Add` trait declaration does not contain any mention of `const`,
25+
You can call call methods of generic parameters of a const function, because they are implicitly assumed to be
26+
`const fn`. For example, the `Add` trait declaration has an additional `const` before the trait name, so
2627
you can use it as a trait bound on your generic parameters:
2728

2829
```rust
29-
const fn triple_add<T: Add>(a: T, b: T, c: T) -> T {
30+
const fn triple_add<T: const Add>(a: T, b: T, c: T) -> T {
3031
a + b + c
3132
}
3233
```
3334

3435
The obligation is passed to the caller of your `triple_add` function to supply a type whose `Add` impl is fully
3536
`const`. Since `Add` only has `add` as a method, in this case one only needs to ensure that the `add` method is
36-
`const fn`. Instead of adding a `const` modifier to all methods of a trait impl, the modifier is added to the entire
37+
`const`. Instead of adding a `const` modifier to all methods of a trait impl, the modifier is added to the entire
3738
`impl` block:
3839

3940
```rust
4041
struct MyInt(i8);
4142
const impl Add for MyInt {
4243
fn add(self, other: Self) -> Self {
43-
MyInt(self.0, other.0)
44+
MyInt(self.0 + other.0)
4445
}
4546
}
4647
```
@@ -65,23 +66,17 @@ const impl Hash for MyInt {
6566
# Reference-level explanation
6667
[reference-level-explanation]: #reference-level-explanation
6768

68-
This is the technical portion of the RFC. Explain the design in sufficient detail that:
69-
70-
- Its interaction with other features is clear.
71-
- It is reasonably clear how the feature would be implemented.
72-
- Corner cases are dissected by example.
73-
74-
The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work.
69+
The implementation of this RFC is (in contrast to some of its alternatives) mostly
70+
changes around the syntax of the language (adding `const` modifiers in a few places)
71+
and ensuring that lowering to HIR and MIR keeps track of that.
72+
The miri engine already fully supports calling methods on generic
73+
bounds, there's just no way of declaring them.
7574

7675
# Drawbacks
7776
[drawbacks]: #drawbacks
7877

79-
One cannot add trait bounds to `const fn` without them automatically
80-
requiring `const impl`s for all monomorphizations. Even if one does not
81-
call any method on the generic parameter, the methods are still required to be constant.
82-
8378
It is not a fully general design that supports every possible use case,
84-
but only covers the most common cases. See also the alternatives.
79+
but it covers the most common cases. See also the alternatives.
8580

8681
# Rationale and alternatives
8782
[rationale-and-alternatives]: #rationale-and-alternatives
@@ -97,13 +92,17 @@ and this RFC is forward compatible to have its background impl be an effect syst
9792
One could annotate methods instead of impls, allowing just marking some method impls
9893
as const fn. This would require some sort of "const bounds" in generic functions that
9994
can be applied to specific methods. E.g. `where <T as Add>::add: const` or something of
100-
the sort.
95+
the sort. This design is more complex than the current one and we'd probably want the
96+
current one as sugar anyway
10197

102-
## Explicit `const` bounds
98+
## No explicit `const` bounds
10399

104-
One could require `T: const Trait` bounds to differentiate between bounds on which methods
105-
can be called and bounds on which no methods can be called. This can backwards compatibly be
106-
added as an opt-out via `T: ?const Trait` if the desire for such differences is strong.
100+
One could require no `const` on the bounds (e.g. `T: Trait`) and assume constness for all
101+
bounds. An opt-out via `T: ?const Trait` would then allow declaring bounds that cannot be
102+
used for calling methods. This design causes discrepancies with `const fn` pointers as
103+
arguments (where the constness would be needed, as normal function pointers already exist
104+
as the type of constants). Also it is not forward compatible to allowing `const` trait bounds
105+
on non-const functions
107106

108107
## Infer all the things
109108

@@ -117,7 +116,40 @@ This is strictly the most powerful and generic variant, but is an enormous backw
117116
hazard as changing a const fn's body to suddenly call a method that it did not before can break
118117
users of the function.
119118

119+
# Future work
120+
121+
This design is explicitly forward compatible to all future extensions the author could think
122+
about. Notable mentions (see also the alternatives section):
123+
124+
* an effect system with a "notconst" effect
125+
* const trait bounds on non-const functions allowing the use of the generic parameter in
126+
constant expressions in the body of the function or maybe even for array lenghts in the
127+
signature of the function
128+
* fine grained bounds for single methods and their bounds
129+
130+
It might also be desirable to make the automatic `Fn*` impls on function types and pointers `const`.
131+
This change should probably go in hand with allowing `const fn` pointers on const functions
132+
that support being called (in contrast to regular function pointers).
133+
120134
# Unresolved questions
121135
[unresolved-questions]: #unresolved-questions
122136

123-
None
137+
Should `const impl` blocks additionally generate impls that are not const if any generic
138+
parameters are not const?
139+
140+
E.g.
141+
142+
```rust
143+
const impl<T: Add> Add for Foo<T> {
144+
fn add(self, other: Self) -> Self {
145+
Foo(self.0 + other.0)
146+
}
147+
}
148+
```
149+
150+
would allow calling `Foo(String::new()) + Foo(String::new())` even though that is (at the time
151+
of writing this RFC) most definitely not const.
152+
153+
This would go in hand with the current scheme for const functions, which may also be called
154+
at runtime with runtime arguments, but are checked for soundness as if they were called in
155+
a const context.

0 commit comments

Comments
 (0)