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

Rebalancing coherence. #1023

Merged
merged 1 commit into from Mar 31, 2015

Conversation

Projects
None yet
7 participants
@nikomatsakis
Contributor

nikomatsakis commented Mar 27, 2015

I recently realized that our current trait system contains a forward compatibility hazard concerned with negative reasoning. The TL;DR is that negative reasoning (that is, the compiler saying that "doing X is legal if the trait T is NOT implemented for some type U") can easily make it impossible to add impls of any traits in a backwards compatible way. The most obvious example of negative reasoning are negative trait bounds, which have been proposed in a rather nicely written RFC. However, even without negative bounds, the trait system as currently implemented already has some amount of negative reasoning, in the form of the coherence system.

This RFC is fairly simple proposal that tries to strike a good balance between parent and child crates, in terms of permitting parent crates to expand but also giving child crates lots of freedom to define the impls they need. However, it does involve tightening coherence so it is somewhat more restrictive (the current rules are designed to permit as much as possible in the child crates; but this winds up limiting the parent crates).

Some prior discussion is on this internals thread, although it's mostly me talking to myself.

Rendered view

[edited to link to final rendered version]

## Summary
This RFC proposes two rule changes:

This comment has been minimized.

@quantheory

quantheory Mar 28, 2015

Contributor

You list three things after this.

When you first define a trait, you must also decide whether that trait
should have (a) a blanket impls for all `T` and (b) any blanket impls
over references. These blanket impls cannot be added later without a
major vesion bump, for fear of breaking downstream clients.

This comment has been minimized.

@quantheory

quantheory Mar 28, 2015

Contributor

s/vesion/version/

@freebroccolo

This comment has been minimized.

Contributor

freebroccolo commented Mar 28, 2015

cc me

@quantheory

This comment has been minimized.

Contributor

quantheory commented Mar 28, 2015

I don't know that I have thought through the implications enough to be strongly for or against, but I do want to point out that without higher-kinded types, it's sometimes hard to work generically over types of references. This encourages library developers to define interfaces that use mainly &T and &mut T, but if/when HKT lands, using some generic Ref<'a, T> would become more feasible, and possibly more popular for presenting "views" of data. Those objects would presumably have to be fundamental in order to be used similarly to &T.

Question on a different topic: Should Deref be fundamental purely due to the special status it has in the language? I believe that the current rules regarding coercion, autoref, and autoderef for method calls are such that adding a Deref impl is not a breaking change (e.g. we don't bother with an autoderef for a method call if we've already found an applicable impl). But it's not completely obvious to me that this is always the case (or that we want to require this into the indefinite future).

@theemathas

This comment has been minimized.

theemathas commented Mar 29, 2015

I like the general idea, but there are some corner cases, especially with #[fundamental]. I will add some line notes for those.

BTW, I believe that the specifics of #[fundamental] should be moved to the Detailed Design section.

with the following meaning:
- A `#[fundamental]` type `Foo` is one where implementing a blanket
impl over `Foo` is a breaking change. As described, `&` and `&mut` are

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

What exactly is a "blanket impl"? Does something like impl<T> SomeTrait for Foo<T, Bar> {...} count?

behave the same as `&` and `&mut` with respect to coherence.
- A `#[fundamental]` trait `Foo` is one where adding an impl of `Foo`
for an existing type is a breaking change. For now, the `Fn` traits
and `Sized` would be marked fundamental, though we may want to

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

Why Sized, but not Deref, DerefMut, Index, or IndexMut?

### Proposed orphan rules
Given an impl `impl<P1...Pn> Trait<T1...Tn> for T0`, either `Trait`

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

What about impl<P1...Pn> Trait<T1...Tn> for T0<U1...Un>?

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

Does this allow things like impl<A, B, T: SomeTrait<A, B>> ReverseTrait<B, A> for T?

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

@theemathas

Does this allow things like impl<A, B, T: SomeTrait<A, B>> ReverseTrait<B, A> for T?

That is allowed if ReverseTrait is defined in the current crate, but not otherwise. (As today.)

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

@theemathas

What about impl<P1...Pn> Trait<T1...Tn> for T0<U1...Un>?

I'm not really sure what you're getting at exactly. In particular, T0 is any type, so it could be things like Box<Foo> and so on as well as simple types like i32.

The main effect of this RFC is to limit the compound types permitted in T0, however. For example, today, something like (Box<LocalType>, i32) would be accepted. Under this RFC, that is not permitted, because tuples are not considered "fundamental". However, Box<LocalType> or &Box<LocalType> or &LocalType is ok, because & and Box are fundamental.

1. Modify the orphan rules so that impls of remote traits require a
local type that is either a struct/enum/trait defined in the
current crate `LT = LocalTypeConstructor<...>` or a reference to a
local type `LT = ... | &LT | &mut LT`.

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

What about *const LT and *mut LT?

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

They are not included in the proposed set of fundamental types.

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

Sorry, let me expound. I do not think it is terribly common to need to implement traits for "naked" unsafe pointer types like that. It certainly didn't arise even once in any of the code I looked at. If you wanted to do so, you could make a newtype (which is probably a good idea, because unsafe pointers aren't really a complete abstraction on their own).

This RFC proposes two rule changes:
1. Modify the orphan rules so that impls of remote traits require a
local type that is either a struct/enum/trait defined in the

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

What about type and use items? What about primitive types?

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

type and use introduce pure aliases and are irrelevant here. This is focusing on the core type system, in which no aliases exist.

1. At least one type must meet the `LT` pattern defined above. Let
`Ti` be the first such type.
2. No type parameters `P1...Pn` may appear in the type parameters that
precede `Ti` (that is, `Tj` where `j < i`).

This comment has been minimized.

@theemathas

theemathas Mar 29, 2015

Does this allow where Tj: SomeTrait<P1>?

This comment has been minimized.

@nikomatsakis

nikomatsakis Mar 30, 2015

Contributor

where clauses are not restricted in any way. What is restricted is the use of a where-clauses to permit otherwise overlapping impls. So if you had:

impl<T> Foo for T
    where T: SomeTrait<i32>
{ }

impl Foo for SomeType { }

this is legal only if SomeType: !SomeTrait<i32>. This RFC requires that this negative constraint meet orphan requirements before we consider it satisfied, to take into account the possibility of future impls being added. Basically that means that SomeType or SomeTrait must be local to the current crate (though if SomeTrait is fundamental, the rules are a bit more lenient).

@pnkfelix

This comment has been minimized.

Member

pnkfelix commented Mar 29, 2015

cc me

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Mar 30, 2015

Just to answer the question that was asked a few times as to why the specific set of "fundamental" traits was chosen:

  1. I'm not sure if Sized is actually needed. I added it early on in the code and I forget why. I'll try taking it out to see what happens.
  2. I was trying to resist the temptation to mark a lot of traits as fundamental. I'd prefer to make none. The reason is that every trait we mark as fundamental is an additional constraint that any future generalization has to support. It might be that we find #[fundamental] completely adequate as a scheme for allowing one to balance negative reasoning and possible future expansion, but we may also find some other nifty approach, and I wanted to minimize the set of things that any possible future approach would have to support.

That said, I could certainly see some logic for marking all the "overload" traits (Deref, Fn, Index, Add, etc) as fundamental just because it's a coherent set that should be easy to remember. The fact is that the big step is going from zero fundamental traits to one; going from one to N is somewhat less scary. Still, we can easily expand the set of fundamental traits in a backwards compatible way, so I don't feel a ton of pressure to figure this out this second.

@cristicbz

This comment has been minimized.

cristicbz commented Mar 30, 2015

I noticed in the alternative sections, you talk about specialization and bring up the problem of associated types (brilliant catch by the way). I'm curious about this paragraph (my emphasis):

For example, I experimented with a "forward proofing" rule which required that, if one impl is more specialized than another according to this definition, then all of their associated type bindings must match. This was an experiment to see whether it was possible to write some kind of "rules" for how associated types should be based on their inputs. Unfortunately, this proved to be untenable. There are numerous trait patterns that we use all the time where this constraint does not hold.

My use case of specialisation, like you mention earlier in that doc, would be to provide optimised implementations, like your zip example. In that case we would certainly want associated types to match (or at least be covariant). Essentially we want it to be transparent that you're using a specialised impl and adding specialisations should not be a breaking change.

I can't think of any of the "numerous trait patterns" that you mention. I'm sure you're actually right, but could you please elaborate for posterity? The specialisation solution would have made me really happy as it seems both less ad-hoc and less restrictive than the solution proposed in the RFC.

@theemathas

This comment has been minimized.

theemathas commented Mar 31, 2015

@nikomatsakis The main reason I asked about the overloading traits is that some operators perform autoderef / autoref (to be precise, Index, IndexMut, Fn, FnMut, FnOnce), while Deref and DerefMut control autoderef. I am not sure about the impact of this on method resolution.

PS You still have not answered about "what is a blanket impl".

@aturon

This comment has been minimized.

Member

aturon commented Mar 31, 2015

After much discussion internally, on the internals post, this thread, and the weekly meeting, this RFC has been approved. It is unfortunate to have to move so quickly on a deep change like this, but the existing system of coherence was simply not in a shippable state without it. The design presented here manages to avoid breaking code while not making strong commitments -- the proposed attribute can be left feature-gated, and eventually replaced with an entirely different mechanism or stabilized as-is, depending on our experience.

Tracking issue

Manishearth added a commit to Manishearth/rust that referenced this pull request Apr 1, 2015

Rollup merge of rust-lang#23895 - nikomatsakis:fn-trait-inheritance-a…
…dd-impls, r=pnkfelix

The primary purpose of this PR is to add blanket impls for the `Fn` traits of the following (simplified) form:

    impl<F:Fn> Fn for &F
    impl<F:FnMut> FnMut for &mut F

However, this wound up requiring two changes:

1. A slight hack so that `x()` where `x: &mut F` is translated to `FnMut::call_mut(&mut *x, ())` vs `FnMut::call_mut(&mut x, ())`. This is achieved by just autoderef'ing one time when calling something whose type is `&F` or `&mut F`.
2. Making the infinite recursion test in trait matching a bit more tailored. This involves adding a notion of "matching" types that looks to see if types are potentially unifiable (it's an approximation).

The PR also includes various small refactorings to the inference code that are aimed at moving the unification and other code into a library (I've got that particular change in a branch, these changes just lead the way there by removing unnecessary dependencies between the compiler and the more general unification code). 

Note that per rust-lang/rfcs#1023, adding impls like these would be a breaking change in the future. 

cc @japaric
cc @alexcrichton 
cc @aturon 

Fixes rust-lang#23015.

Manishearth added a commit to Manishearth/rust that referenced this pull request Apr 1, 2015

Rollup merge of rust-lang#23895 - nikomatsakis:fn-trait-inheritance-a…
…dd-impls, r=pnkfelix

The primary purpose of this PR is to add blanket impls for the `Fn` traits of the following (simplified) form:

    impl<F:Fn> Fn for &F
    impl<F:FnMut> FnMut for &mut F

However, this wound up requiring two changes:

1. A slight hack so that `x()` where `x: &mut F` is translated to `FnMut::call_mut(&mut *x, ())` vs `FnMut::call_mut(&mut x, ())`. This is achieved by just autoderef'ing one time when calling something whose type is `&F` or `&mut F`.
2. Making the infinite recursion test in trait matching a bit more tailored. This involves adding a notion of "matching" types that looks to see if types are potentially unifiable (it's an approximation).

The PR also includes various small refactorings to the inference code that are aimed at moving the unification and other code into a library (I've got that particular change in a branch, these changes just lead the way there by removing unnecessary dependencies between the compiler and the more general unification code). 

Note that per rust-lang/rfcs#1023, adding impls like these would be a breaking change in the future. 

cc @japaric
cc @alexcrichton 
cc @aturon 

Fixes rust-lang#23015.

Manishearth added a commit to Manishearth/rust that referenced this pull request Apr 1, 2015

Rollup merge of rust-lang#23867 - nikomatsakis:issue-23086-take-3, r=…
…pnkfelix

This PR implements rust-lang/rfcs#1023. In the process it fixes rust-lang#23086 and rust-lang#23516. A few impls in libcore had to be updated, but the impact is generally pretty minimal. Most of the fallout is in the tests that probed the limits of today's coherence.

I tested and we were able to build the most popular crates along with iron (modulo errors around errors being sendable).

Fixes rust-lang#23918.

Manishearth added a commit to Manishearth/rust that referenced this pull request Apr 1, 2015

Rollup merge of rust-lang#23895 - nikomatsakis:fn-trait-inheritance-a…
…dd-impls, r=pnkfelix

The primary purpose of this PR is to add blanket impls for the `Fn` traits of the following (simplified) form:

    impl<F:Fn> Fn for &F
    impl<F:FnMut> FnMut for &mut F

However, this wound up requiring two changes:

1. A slight hack so that `x()` where `x: &mut F` is translated to `FnMut::call_mut(&mut *x, ())` vs `FnMut::call_mut(&mut x, ())`. This is achieved by just autoderef'ing one time when calling something whose type is `&F` or `&mut F`.
2. Making the infinite recursion test in trait matching a bit more tailored. This involves adding a notion of "matching" types that looks to see if types are potentially unifiable (it's an approximation).

The PR also includes various small refactorings to the inference code that are aimed at moving the unification and other code into a library (I've got that particular change in a branch, these changes just lead the way there by removing unnecessary dependencies between the compiler and the more general unification code). 

Note that per rust-lang/rfcs#1023, adding impls like these would be a breaking change in the future. 

cc @japaric
cc @alexcrichton 
cc @aturon 

Fixes rust-lang#23015.

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Apr 1, 2015

rollup merge of rust-lang#23867: nikomatsakis/issue-23086-take-3
This PR implements rust-lang/rfcs#1023. In the process it fixes rust-lang#23086 and rust-lang#23516. A few impls in libcore had to be updated, but the impact is generally pretty minimal. Most of the fallout is in the tests that probed the limits of today's coherence.

I tested and we were able to build the most popular crates along with iron (modulo errors around errors being sendable).

Fixes rust-lang#23918.

bors added a commit to rust-lang/rust that referenced this pull request Apr 2, 2015

Auto merge of #23867 - nikomatsakis:issue-23086-take-3, r=aturon
This PR implements rust-lang/rfcs#1023. In the process it fixes #23086 and #23516. A few impls in libcore had to be updated, but the impact is generally pretty minimal. Most of the fallout is in the tests that probed the limits of today's coherence.

I tested and we were able to build the most popular crates along with iron (modulo errors around errors being sendable).

Fixes #23918.

@dcampbell24 dcampbell24 referenced this pull request Apr 3, 2015

Closed

Rewrite #54

barosl added a commit to barosl/rust that referenced this pull request Jun 25, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the MarkDown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [thread::scoped](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

barosl added a commit to barosl/rust that referenced this pull request Jun 25, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the MarkDown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [thread::scoped](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

barosl added a commit to barosl/rust that referenced this pull request Jun 25, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [thread::scoped](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

barosl added a commit to barosl/rust that referenced this pull request Jun 25, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [`thread::scoped`](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

bors added a commit to rust-lang/rust that referenced this pull request Jun 26, 2015

Auto merge of #26568 - barosl:rel-notes-refs, r=alexcrichton
I found some typos in the upcoming 1.1 release note. I corrected them, but I wanted to go further. So I wrote a script that checks the integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following "unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [`thread::scoped`](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to get descriptions as well. How do you feel about it?

bors added a commit to rust-lang/rust that referenced this pull request Jun 26, 2015

Auto merge of #26568 - barosl:rel-notes-refs, r=alexcrichton
I found some typos in the upcoming 1.1 release note. I corrected them, but I wanted to go further. So I wrote a script that checks the integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following "unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [`thread::scoped`](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to get descriptions as well. How do you feel about it?

jroesch added a commit to jroesch/rust that referenced this pull request Jul 21, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [`thread::scoped`](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

thepowersgang added a commit to thepowersgang/rust that referenced this pull request Jul 25, 2015

Correct typos and remove unused references from RELEASES.md
I found some typos in the upcoming 1.1 release note. I corrected them,
but I wanted to go further. So I wrote a script that checks the
integrity of the Markdown references, and ran it against `RELEASES.md`.

This commit fixes some trivial cases, but also removes the following
"unused" references:

- [`Iterator::cloned`](http://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.cloned)
- [`thread::scoped`](http://static.rust-lang.org/doc/master/std/thread/fn.scoped.html)
- [`Debug` improvements](https://github.com/rust-lang/rfcs/blob/master/text/0640-debug-improvements.md)
- [Rebalancing coherence.](rust-lang/rfcs#1023)

However, I think there's a possibility that these features might need to
get descriptions as well. How do you feel about it?

@aturon aturon referenced this pull request Mar 23, 2016

Open

Tracking issue for specialization (RFC 1210) #31844

1 of 7 tasks complete

sgrif added a commit to sgrif/rfcs that referenced this pull request May 30, 2018

Re-Rebalancing Coherence
RFC rust-lang#1023 introduced rules that exclude impls which should clearly be
valid. It also used some ambiguous language around what is a breaking
change, that ended up being completely ignored and contradicted by rust-lang#1105.
This RFC seeks to clarify what is or isn't a breaking change when it
comes to implementing an existing trait, and conservatively expands the
orphan rules to allow impls which do not violate coherence, and fit
within the original goals of rust-lang#1023.

[Rendered]

sgrif added a commit to sgrif/rfcs that referenced this pull request May 30, 2018

Re-Rebalancing Coherence
RFC rust-lang#1023 introduced rules that exclude impls which should clearly be
valid. It also used some ambiguous language around what is a breaking
change, that ended up being completely ignored and contradicted by rust-lang#1105.
This RFC seeks to clarify what is or isn't a breaking change when it
comes to implementing an existing trait, and conservatively expands the
orphan rules to allow impls which do not violate coherence, and fit
within the original goals of rust-lang#1023.

@sgrif sgrif referenced this pull request May 30, 2018

Merged

Re-Rebalancing Coherence #2451

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment