Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: ForbiddenValues trait to enable more optimizations #2888

Closed
wants to merge 13 commits into from
52 changes: 52 additions & 0 deletions text/0000-forbidden-value-trait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
- Start Date: 2020-03-24
- RFC PR #:
- Rust Issue #:

# Summary

Some types have one or more forbidden values. Such values can be sometimes used to perform [optimizations](https://github.com/rust-lang/rust/pull/45225). For example, rustc currently uses a known forbidden value of a certain type `A` (like `&T`, `Box<T>`, `NonNull<T>`, `NonZeroI8` and some enum types) to represent the `None` enum variant of `Option<A>`. I propose to give users the ability to enable the same optimizations for types rustc currently doesn't know a forbidden value exists for.

# Motivation

This would increase the number of situations where the compiler can do the aforementioned, genuinely useful optimizations.

# Detailed design

Add the following trait somewhere in the standard library:
```rust
use std::{array::FixedSizeArray, mem::size_of};

unsafe trait ForbiddenValues {
type Forbidden: FixedSizeArray<[u8; size_of::<Self>()]>;

const FORBIDDEN: Self::Forbidden;
}
```

To implement `ForbiddenValues` for type `T`, the following conditions must be met:

1. For each value `t` of type `T`, and for each item `f` in `T::FORBIDDEN`:
```rust
*(&raw const t as *const [u8; size_of::<T>()]) != f
```
2. `T` must not be a type that has forbidden values the compiler already knows about (e.g. `char`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. char

How about other types, are there any other types that the compiler already knows about? Is it possible to lists them out?

Copy link
Contributor

@Ixrec Ixrec May 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how relevant this is now that the issue's closed, but https://doc.rust-lang.org/std/#primitives is a list of primitive types in the language. Which of these counts as "having forbidden values" depends on what you mean by that. For example, "uninitialized" is a forbidden / insta-UB-causing value for all of them, and str has to be valid UTF-8 within safe code but unsafe code can violate that without triggering UB, and I don't know if you'd want to include library types like NonZero*, and so on and so forth. But if we try to just ignore all of this crippling scope vagueness, I think bool is the only other primitive type that is "like char" insofar as it clearly has to be implemented as one of the integer types with certain values excluded (in bool's case, every value except 0 or 1).


Then compilers would be allowed to use `T::FORBIDDEN` to represent forbidden values of type `T` in whatever optimizations they decide to perform.

# Alternatives

This is a simple proposal, but a step further would be to make all primitive and standard library types that have forbidden value(s) implement `ForbiddenValues`. And an even further step would be have Rust (the language) specify which optimizations are guaranteed to happen.

Presumably the following syntax should work in the future:
```rust
unsafe trait ForbiddenValues {
const COUNT: usize;
const FORBIDDEN_VALUES: [[u8; size_of::<Self>()]; Self::COUNT];
}
```

# Unresolved questions

Are there some alignment issues with `ForbiddenValues::FORBIDDEN`? I don't think so, because the compiler is free to use those bytes however it likes.

Given that producing a forbidden value of a `bool`, `char`, or an enum type is considered undefined behaviour, I suppose it makes sense to ask if it should be considered undefined behaviour to produce a value `t` of type `T` such that for some item `f` in `T::FORBIDDEN`, `*(&raw const t as *const [u8; size_of::<T>()]) == f`.