RFC 2056: allow-trivial-where-clause-constraints

lang (traits | typesystem)

Summary

Allow constraints to appear in where clauses which are trivially known to either always hold or never hold. This would mean that impl Foo for Bar where i32: Iterator would become valid, and the impl would never be satisfied.

Motivation

It may seem strange to ever want to include a constraint that is always known to hold or not hold. However, as with many of these cases, allowing this would be useful for macros. For example, a custom derive may want to add additional functionality if two derives are used together. As another more concrete example, Diesel allows the use of normal Rust operators to generate the equivalent SQL. Due to coherence rules, we can't actually provide a blanket impl, but we'd like to automatically implement std::ops::Add for columns when they are of a type for which + is a valid operator. The generated impl would look like:

impl<T> std::ops::Add<T> for my_column
where
    my_column::SqlType: diesel::types::ops::Add,
    T: AsExpression<<my_column::SqlType as diesel::types::ops::Add>::Rhs>,
{
    // ...
}

One would never write this impl normally since we always know the type of my_column::SqlType. However, when you consider the use case of a macro, we can't always easily know whether that constraint would hold or not at the time when we're generating code.

Detailed design

Concretely implementing this means the removal of E0193. Interestingly, as of Rust 1.7, that error never actually appears. Instead the current behavior is that something like impl Foo for Bar where i32: Copy (e.g. anywhere that the constraint always holds) compiles fine, and impl Foo for Bar where i32: Iterator fails to compile by complaining that i32 does not implement Iterator. The original error message explicitly forbidding this case does not seem to ever appear.

The obvious complication that comes to mind when implementing this feature is that it would allow nonsensical projections to appear in the where clause as well. For example, when i32: IntoIterator appears in a where clause, we would also need to allow i32::Item: SomeTrait to appear in the same clause, and even allow for _ in 1 to appear in item bodies, and have it all successfully compile.

Since code that was caught by this error is usually nonsense outside of macros, it would be valuable for the error to continue to live on as a lint. The lint trivial_constraints would be added, matching the pre-1.7 semantics of E0193, and would be set to warn by default.

How We Teach This

This feature does not need to be taught explicitly. Knowing the basic rules of where clauses, one would naturally already expect this to work.

Drawbacks

Alternatives

n/a

Unresolved questions

Should the lint error by default instead of warn?