From 5c411de62e73ee911df3735bc1eaef61c5b74cc2 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 28 Jul 2017 15:33:10 +0200 Subject: [PATCH 1/6] Update to new template. --- text/0000-implied-bounds.md | 736 ++++++++++++++++++++++++++++++++++++ 1 file changed, 736 insertions(+) create mode 100644 text/0000-implied-bounds.md diff --git a/text/0000-implied-bounds.md b/text/0000-implied-bounds.md new file mode 100644 index 00000000000..8b66143e816 --- /dev/null +++ b/text/0000-implied-bounds.md @@ -0,0 +1,736 @@ +- Feature Name: `implied-bounds` +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Eliminate the need for “redundant” bounds on functions and impls where those bounds can be inferred from the input types. For example, in this simple program, the impl would no longer require a bound, because it can be inferred from the `Foo` type: + +```rust +struct Foo { .. } +impl Foo { + // ^^^^^ this bound is redundant + ... +} +``` +Hence, simply writing `impl Foo { ... }` would suffice. We currently support implied bounds for super traits, projections and lifetimes. We propose to extend this to all where clauses on traits and types, as was already discussed [here][niko]. + +# Motivation +[motivation]: #motivation + +## Types + +Let's take an example from the standard library where trait bounds are actually expressed on a type¹. +```rust +pub enum Cow<'a, B: ?Sized + 'a> + where B: ToOwned +{ + Borrowed(&'a B), + Owned(::Owned), +} +``` +The `ToOwned` bound has then to be carried everywhere: +```rust +impl<'a, B: ?Sized> Cow<'a, B> + where B: ToOwned +{ + ... +} + +impl<'a, B: ?Sized> Clone for Cow<'a, B> + where B: ToOwned +{ + ... +} + +impl<'a, B: ?Sized> Eq for Cow<'a, B: Eq> + where B: ToOwned +{ + ... +} +``` +even if one does not actually care about the semantics implied by `ToOwned`: +```rust + fn panic_if_not_borrowed<'a, B>(cow: Cow<'a, B>) -> &'a B +// where B: ToOwned + { + match cow { + Cow::Borrowed(b) => b, + Cow::Owned(_) => panic!(), + } + } +// ^ the trait `std::borrow::ToOwned` is not implemented for `B` +``` +However what we know is that if `Cow<'a, B>` is well-formed, then `B` *has* to implement `ToOwned`. We would say that such a bound is *implied* by the well-formedness of `Cow<'a, B>`. + +Currently, impls and functions have to prove that their arguments are well-formed. Under this proposal, they would *assume* that their arguments are well-formed, leaving the responsibility for proving well-formedness to the caller. Hence we would be able to drop the `B: ToOwned` bounds in the previous examples. + +Beside reducing repeated constraints, it would also provide a clearer separation between what bounds a type needs so that it is is well-formed, and what additional bounds an `fn` or an `impl` actually needs: + +```rust +struct Set where K: Hash + Eq { ... } + +fn only_clonable_set(set: Set) { ... } + +// VS + +fn only_clonable_set(set: Set) { ... } +``` + +Moreover, we already support implied lifetime bounds on types: +```rust +pub struct DebugStruct<'a, 'b> where 'b: 'a { + fmt: &'a mut fmt::Formatter<'b>, + ... +} + +pub fn debug_struct_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> DebugStruct<'a, 'b> +// where 'b: 'a +// ^^^^^^^^^^^^ this is not needed +{ + /* inside here: assume that `'b: 'a` */ +} +``` +This RFC proposes to extend this sort of logic beyond these special cases and use it uniformly for both trait bounds and lifetime bounds. + +¹Actually only a few types in the standard library have bounds, for example `HashSet` does not have a `T: Hash + Eq` on the type declaration, but on the impl declaration rather. Wether we should prefer bounds on types or on impls is related, but beyond the scope of this RFC. + +## Traits + +Traits also currently support some form of implied bounds, namely super traits bounds: +```rust +// Equivalent to `trait Foo where Self: From`. +trait Foo: From { } + +pub fn from_bar(bar: Bar) -> T { + // `T: From` is implied by `T: Foo`. + T::from(bar) +} +``` +and bounds on projections: +```rust +// Equivalent to `trait Foo where Self::Item: Eq`. +trait Foo { + type Item: Eq; +} + +fn only_eq() { } + +fn foo() { + // `T::Item: Eq` is implied by `T: Foo`. + only_eq::() +} +``` +However, this example does not compile: +```rust + trait Foo where U: Eq { } + + fn only_eq() { } + + fn foo>() { + only_eq::() + } +// ^ the trait `std::cmp::Eq` is not implemented for `U` +``` +Again we propose to uniformly support implied bounds for all where clauses on trait definitions. + +# Guide-Level Explanation +[guide]: #guide + +When you declare bounds on a type, you don't have to repeat them when writing impls and functions as soon as the type appear in the signature or the impl header: +```rust +struct Set where T: Hash + Eq { + ... +} + +impl Set { + // You can rely on the fact that `T: Hash + Eq` inside here. + ... +} + +impl Clone for Set where T: Clone { + // Same here, and you can also rely on the `T: Clone` bound of course. + ... +} + +fn only_eq() { } + +fn use_my_set(arg: Set) { + // We know that `T: Eq` because we have a `Set` as an argument, and there already is a + // `T: Eq` bound on the declaration of `Set`. + only_eq::(); +} + +// This also works for the return type: no need to repeat bounds. +fn return_a_set() -> Set { + Set::new() +} +``` + +Lifetime bounds are supported as well (this is already the case today): +```rust +struct MyStruct<'a, 'b> where 'b: 'a { + reference: &'a &'b i32, +} + +fn use_my_struct<'a, 'b>(arg: MyStruct<'a, 'b>) { + // No need to repeat `where 'b: 'a`, it is assumed. +} +``` + +However, you still have to write the bounds explicitly if the type does not appear in the function signature or the impl header: +```rust +// `Set` does not appear in the fn signature: we need to explicitly write the bounds. +fn declare_a_set() { + let set = Set::::new(); +} +``` + +Similarly, you don't have to repeat bounds that you write on a trait declaration as soon as you know that the trait reference holds: +```rust +trait Foo where Bar: Into { + ... +} + +fn into_foo(bar: Bar) -> T { + // We know that `T: Foo` holds so given the trait declaration, we know that `Bar: Into`. + bar.into() +} +``` + +Note that this is transitive: +```rust +trait Foo { } +trait Bar where Self: Foo { } +trait Baz where Self: Bar { } + +fn only_foo() { } + +fn use_baz() { + // We know that `T: Baz`, hence we know that `T: Bar`, hence we know that `T: Foo`. + only_foo::() +} +``` + +This also works for bounds on associated types: +```rust +trait Foo { + type Item: Debug; +} + +fn debug_foo>(arg: U) { + // We know that `::Item` implements `Debug` because of the trait declaration. + // Moreover, we know that `::Item` is `U`. + // Hence, we know that `U` implements `Debug`. + println!("{:?}", arg); + + /* do something else with `T` and `U`... */ +} +``` + +# Reference-Level Explanation +[reference]: #reference + +This design has already been experimented on [Chalk](https://github.com/nikomatsakis/chalk), to some extent. The current design has been driven by issue [#12], it is a good read to understand why we *need* to expand where clauses as described below. + +We'll use the grammar from [RFC 1214] to detail the rules: +``` +T = scalar (i32, u32, ...) // Boring stuff + | X // Type variable + | Id // Nominal type (struct, enum) + | &r T // Reference (mut doesn't matter here) + | O0 + ... + On + r // Object type + | [T] // Slice type + | for fn(T1, ..., Tn) -> T0 // Function pointer + | >::Id // Projection +P = r // Region name + | T // Type +O = for TraitId // Object type fragment +r = 'x // Region name +``` + +We'll use the same notations as [RFC 1214] for the set `R = ` denoting the set of lifetimes currently bound. + +## Well-formedness rules +Basically, we say that something (type or trait reference) is well-formed if the bounds declared on it are met, *regardless of the well-formedness of its parameters*: this is the main difference with [RFC 1214]. + +We will write: +* `WF(T: Trait)` for a trait reference `T: Trait` being well-formed +* `WF(T)` for a reference to the type `T` being well-formed + +### **Trait refs** +We'll start with well-formedness for trait references. The important thing is that we distinguish between `T: Trait` and `WF(T: Trait)`. The former means that an impl for `T` has been found while the latter means that `T` meets the bounds on trait `Trait`. + +We'll also consider a function `Expanded` applying on where clauses like this: +``` +Expanded((T: Trait)) = { (T: Trait), WF(T: Trait) } +Expanded((T: Trait)) = { (T: Trait), WF(T: Trait) } +Expanded(OtherWhereClause) = { OtherWhereClause } +``` +We naturally extend `Expanded` so that it applies on a finite set of where clauses: +``` +Expanded({ WhereClause1, ..., WhereClauseN }) = Union(Expanded(WhereClause1), ..., Expanded(WhereClauseN)) +``` +***Every where clause*** a user writes will be expanded through the `Expanded` function. This means that the following impl: +```rust +impl Into for U where T: From { ... } +``` +will give the following rule: +``` + T: From, WF(T: From) +-------------------------------------------------- + U: Into +``` + +Now let's see the actual rule for a trait reference being well-formed: +``` +WfTraitReference: + C = Expanded(WhereClauses(TraitId)) // the conditions declared on TraitId must hold... + R, r... ⊢ [P0, ..., Pn] C // ...after substituting parameters, of course + -------------------------------------------------- + R ⊢ WF(for P0: TraitId) +``` + + And here is an example: +```rust +// `WF(Self: SuperTrait)` holds. +trait SuperTrait { } + +// `WF(Self: Trait)` holds if `Self: SuperTrait`, `WF(Self: Supertrait)`. +trait Trait: SuperTrait { } + +// `i32: Trait` holds but not `WF(i32: Trait)`. +// This would be flagged as an error. +impl Trait for i32 { } + +// Both `f32: Trait` and `WF(f32: Trait)` hold. +impl SuperTrait for f32 { } +impl Trait for f32 { } +``` + +### **Types** + +The well-formedness rules for types are given by: +``` +WfScalar: + -------------------------------------------------- + R ⊢ WF(scalar) + +WfFn: // an fn pointer is always WF since it only carries parameters + -------------------------------------------------- + R ⊢ WF(for fn(T1, ..., Tn) -> T0) + +WfObject: + rᵢ = union of implied region bounds from Oi + ∀i. rᵢ: r + -------------------------------------------------- + R ⊢ WF(O0 + ... + On + r) + +WfObjectFragment: + TraitId is object safe + -------------------------------------------------- + R ⊢ WF(for TraitId) + +WfTuple: + ∀i) + +WfReference: + R ⊢ T: 'x // T must outlive 'x + -------------------------------------------------- + R ⊢ WF(&'x T) + +WfSlice: + R ⊢ T: Sized + -------------------------------------------------- + R ⊢ WF([T]) + +WfProjection: + R ⊢ P0: Trait // the trait reference holds + R ⊢ WF(P0: Trait) // the trait reference is well-formed + -------------------------------------------------- + R ⊢ WF(>::Id) +``` +Taking again our `SuperTrait` and `Trait` from above, here is an example: +```rust +// `WF(Struct)` holds if `T: Trait`, `WF(T: Trait)`. +struct Struct where T: Trait { + field: T, +} + +// `WF(Struct)` would not hold since `WF(i32: Trait)` doesn't. +// But `WF(Struct)` does hold. +``` + +## Reverse rules +This is a core element of this RFC. Morally, the well-formedness rules are "if and only if" rules. We thus add reverse rules for each relevant WF rule: +``` +ReverseWfTraitReferenceᵢ + // Substitute parameters + { WhereClause1, ..., WhereClauseN } = [P0, ..., Pn] Expanded(WhereClauses(TraitId)) + R ⊢ WF(for P0: TraitId) + -------------------------------------------------- + R, r... ⊢ WhereClauseᵢ + +ReverseWfTupleᵢ, i < n: + R ⊢ WF((T1, ..., Tn)) + -------------------------------------------------- + R ⊢ Ti: Sized // not very useful since this bound is often implicit + +ReverseWfNominalTypeᵢ: + // Substitute parameters + { WhereClause1, ..., WhereClauseN } = [P1, ..., Pn] Expanded(WhereClauses(id)) + R ⊢ WF(Id) + -------------------------------------------------- + R ⊢ WhereClauseᵢ + +ReverseWfReference: + R ⊢ WF(&'x T) + -------------------------------------------------- + R ⊢ T: 'x + +ReverseWfSlice: + R ⊢ WF([T]) + -------------------------------------------------- + R ⊢ T: Sized // same as above +``` + +Note that we add reverse rules for all ***expanded*** where clauses, this means that given: +```rust +// Expands to `trait Foo where Self: Bar, WF(Self: Bar)` +trait Bar where Self: Foo { } +``` +we have two reverse rules given by: +``` +WF(T: Bar) +-------------------------------------------------- +T: Foo + +WF(T: Bar) +-------------------------------------------------- +WF(T: Foo) +``` + +## Input types +We define the notion of input types of a type. Basically, input types refer to all types that are accessible from referencing to a specific type. For example, a function will assume that the input types of its arguments are well-formed, hence in the body of that function we'll be able to derive implied bounds thanks to the reverse rules described earlier. + +We'll denote by `InputTypes` the function which maps a type to its input types, defined by: +``` +// Scalar +InputTypes(scalar) = { scalar } + +// Type variable +InputTypes(X) = { X } + +// Region name +InputTypes(r) = { } + +// Reference +InputTypes(&r T) = Union({ &r T }, InputTypes(T)) + +// Slice type +InputTypes([T]) = Union({ [T] }, InputTypes(T)) + +// Nominal type +InputTypes(Id) = Union({ Id }, InputTypes(P0), ..., InputTypes(Pn)) + +// Object type +InputTypes(O0 + ... + On + r) = Union({ O0 + ... + On + r }, InputTypes(O0), ..., InputTypes(On)) + +// Object type fragment +InputTypes(for TraitId) = { for TraitId } + +// Function pointer +InputTypes(for fn(T1, ..., Tn) -> T0) = { for fn(T1, ..., Tn) -> T0 } + +// Projection +InputTypes(>::Id) = Union( + { >::Id }, + InputTypes(P0), + InputTypes(P1), + ..., + InputTypes(Pn) +) +``` + +Note that higher-ranked types (functions, object type fragments) do not carry input types other than themselves. This is because they are unusable *as such*, one will have to use them in a lower-ranked way at some point (e.g. calling a function) and will thus rely on `InputTypes` for normal types. + +## Assumptions and checking well-formedness +This is the other core element: how to use reverse rules. Basically, functions and impls will assume that their input types are well-formed, and that (expanded) where clauses hold. + +### **Functions** +Given a function declaration: +```rust +fn F(arg1: T1, ..., argm: Tm) -> T0 where WhereClause1, ..., WhereClausek { + /* body of the function inside here */ +} +``` +We rely on the following assumptions inside the body of `F`: +* `Expanded({ WhereClause1, ..., WhereClausek })` +* `WF(T)` for all `T ∈ Union(InputTypes(T0), InputTypes(T1), ..., InputTypes(Tm))` +* `WF(Xi)` for all `i` + +Note that we assume that the input types of the return type `T0` are well-formed. + +With these assumptions, the function must be able to prove that everything that appears in its body is well-formed (e.g. every type appearing in the body, projections, etc). + +Moreover, a caller of `F` would have to prove that the where clauses on `F` hold, after having substituted parameters. + +**Remark**: Notice that we assume that the type variables `Xi` are well-formed for all `i`. This way, type variables don't need a special treatment regarding well-formedness. See example below. + +Examples: + +```rust +trait Bar { } +trait Foo where Box: Bar { } + +fn only_bar() { } + +fn foo() { + // Inside the body, we have to prove `WF(T)`, `WF(Box)`, and `Box: Bar`. + // Because we assume that `WF(T: Foo)`, we indeed have `Box: Bar`. + only_bar::>() +} + +fn main() { + // We have to prove `WF(i32)`, `i32: Foo`. + foo::(); +} +``` + +```rust +/// Illustrate remark 2: no need for a special treatment for type variables. + +struct Set { ... } + +fn two_variables() { } + +fn one_variable() { + // We have to prove `WF(T)`, `WF(Set)`. `WF(T)` trivially holds because of the assumption + // made by the function `one_variable`. `WF(Set)` holds because of the `T: Hash` bound. + two_variables>() +} + +fn main() { + // We have to prove `WF(i32)`. + one_variable::(); +} +``` + +```rust +/// Illustrate "inner" input types and transitivity + +trait Bar where Box: Eq { } +trait Baz: Bar { } + +struct Struct { ... } + +fn only_eq() { } + +fn dummy(arg: Option>) { + /* do something with arg */ + + // Since `Struct` is an input type, we assume that `WF(Struct)` hence `WF(T: Baz)` + // hence `WF(T: Bar)` hence `Box: Eq` + only_eq::>() +} +``` + +### **Trait impls** +Given a trait impl: +```rust +impl Trait for T0 where WhereClause1, ..., WhereClausek { + // body of the impl inside here + + type Assoc = AssocTyValue; + + /* ... */ +} +``` +We rely on the following assumptions inside the body of the impl: +* `Expanded({ WhereClause1, ..., WhereClausek })` +* `WF(T)` for all `T ∈ Union(InputTypes(T0), InputTypes(T1), ..., InputTypes(Tn))` +* `WF(Xi)` for all `i` + +Based on these assumptions, the impl declaration has to prove `WF(T0: Trait)` and `WF(T)` for all `T ∈ InputTypes(AssocTyValue)`. Note that associated fns can be seen as (higher-kinded) associated types, but since fn pointers are always well-formed and do not carry input types other than themselves, this is fine. + +Associated fns make their normal assumptions + the set of assumptions made by the impl. Things to prove inside associated fns do not differ from normal fns. + +Note that when projecting out of a type, one must automatically prove that the trait reference holds because of the `WfProjection` rule. + +Examples: + +```rust +struct Set { ... } + +trait Foo where Self: Clone { + fn foo(); +} + +fn only_hash() { } + +impl Foo for Set { + // Inside here: we assume `WF(Set)`, `K: Clone`, `WF(K: Clone)`, `WF(K)`. + // Also, we must prove `WF(Set: Foo)`. + + fn foo() { + only_hash::() + } +} +``` + +```rust +struct Set { ... } + +trait Foo { + type Item; +} + +// We need an explicit `K: Hash` bound in order to prove that the associated type value `Set` is WF. +impl Foo for K { + type Item = Set; +} +``` + +```rust +trait Foo { + type Item; +} + +impl Foo for T where T: Clone { + type Item = f32; +} + +fn foo(arg: T) { + // We must prove `WF(::Item)` hence prove that `T: Foo`: ok this is in our assumptions. + let a = ::Item; +} + +fn bar(arg: T) { + // We must prove `WF(::Item)` hence prove that `T: Foo`: ok, use the impl. + let a = ::Item; +} +``` + +### **Inherent impls** +Given an inherent impl: +```rust +impl SelfTy where WhereClause1, ..., WhereClausek { + /* body of the impl inside here */ +} +``` +We rely on the following assumptions inside the body of the impl: +* `Expanded({ WhereClause1, ..., WhereClausek })` +* `WF(T)` for all `T ∈ InputTypes(SelfTy)` +* `WF(Xi)` for all `i` + +Methods make their normal assumptions + the set of assumptions made by the impl. Things to prove inside methods do not differ from normal fns. + +A caller of a method has to prove that the where clauses defined on the impl hold, in addition to the requirements for calling general fns. + +## Proving well-formedness for input types +[proving-wf-input-types]: #proving-well-formedness-for-input-types + +One would have noticed that we only prove well-formedness for input types in a lazy way (e.g., inside function bodies). This means that if we have a function: +```rust +struct Set { ... } +struct NotHash; + +fn foo(arg: Set) { ... } +``` +then no error will be caught until someone actually tries to call `foo`. Same thing for an impl: +```rust +impl Set { ... } +``` +the error will not be caught until someone actually uses `Set`. + +The idea is, when encountering an fn/trait impl/inherent impl, retrieve all input types that appear in the signature / header and for each input type `T`, do the following: retrieve type variables `X1, ..., Xn` bound by the declaration and ask for `∃X1, ..., ∃Xn; WF(T)` in an empty enviromnent (in Chalk terms). If there is no possible substitution for the existentials, output a warning. + +Example: +```rust +struct Set { ... } + +// `NotHash` is local to this crate, so we know that there exists no `T` +// such that `NotHash: Hash`. +struct NotHash { ... } + +// Warning: `foo` cannot be called whatever the value of `T` +fn foo(arg: Set>) { ... } +``` + +## Cycle detection +In Chalk this design often leads to cycles in the proof tree. Example: +```rust +trait Foo { } +// `WF(Self: Foo)` holds. + +impl Foo for u8 { } + +// Expanded to `trait Bar where Self: Foo, WF(Self: Foo)` +trait Bar where Self: Foo { } + +// WF rule: +// `WF(Self: Bar)` holds if `Self: Foo`, `WF(Self: Foo)`. + +// Reverse WF rules: +// `Self: Foo` holds if `WF(Self: Bar)` +// `WF(Self: Foo)` holds if `WF(Self: Bar)` +``` +Now suppose we are asking wether `u8: Foo` holds. The following branch exists in the proof tree: +`u8: Foo` holds if `WF(u8: Bar)` holds if `u8: Foo` holds. + +I *think* rustc would have the right behavior currently: just dismiss this branch since it only leads to the tautological rule `(u8: Foo) if (u8: Foo)`. + +In Chalk we have a more sophisticated cycle detection strategy based on tabling, which basically enables us to correctly answer "multiple solutions", instead of "unique solution" if a simple *error-on-cycle* strategy were used. Would rustc need such a thing? + +# Drawbacks +[drawbacks]: #drawbacks + +* Implied bounds on types can feel like "implicit bounds" (although they are not: the types appear in the signature of a function / impl header, so it's self-documenting). +* Removing a bound from a struct becomes a breaking change (note: this can already be the case for functions and traits). + +# Rationale and Alternatives +[alternatives]: #alternatives + +## Including parameters in well-formedness rules + +Specific to this design: instead of disregarding parameters in well-formedness checks, we could have included them, and added reverse rules of the form: "`WF(T)` holds if `WF(Struct)` holds". From a theoretical point of view, this would have had the same effects as the current design, and would have avoided the whole `InputTypes` thing. However, implementation in Chalk revelead some tricky issues. Writing in Chalk-style, suppose we have rules like: +``` +WF(Struct) :- WF(T) +WF(T) :- WF(Struct) +``` +then trying to prove `WF(i32)` gives birth to an infinite branch `WF(i32) :- WF(Struct) :- WF(Struct>) :- ...` in the proof tree, which is hard (at least that's what we believe) to dismiss. + +## Trait aliases + +Trait aliases offer a way to factorize repeated constraints ([RFC 1733]), it's useful especially for bounds on types, but it does not overcome the limitations for implied bounds on traits (the `where Bar: Into` example is a good one). + +## Limiting the scope of implied bounds + +These essentially try to address the breaking change when removing a bound on a type: +* do not derive implied bounds for types +* limit the use of implied bounds for types that are in your current crate only +* derive implied bounds in impl bodys only +* two distinct feature-gates, one for implied bounds on traits and another one for types + +# Unresolved questions +[unresolved]: #unresolved-questions + +@nikomatsakis pointed [here][niko] that implied bounds can interact badly with current inference rules. + +[#12]: https://github.com/nikomatsakis/chalk/issues/12 + +[RFC 1214]: https://github.com/rust-lang/rfcs/blob/master/text/1214-projections-lifetimes-and-wf.md + +[RFC 1733]: https://github.com/rust-lang/rfcs/blob/master/text/1733-trait-alias.md + +[niko]: https://internals.rust-lang.org/t/lang-team-minutes-implied-bounds/4905 From f600f0c27250c9095ca6831b60ba82a9f29d5ac0 Mon Sep 17 00:00:00 2001 From: Alexandre Martin Date: Fri, 28 Jul 2017 23:34:48 +0200 Subject: [PATCH 2/6] Update 0000-implied-bounds.md --- text/0000-implied-bounds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-implied-bounds.md b/text/0000-implied-bounds.md index 8b66143e816..9798db38363 100644 --- a/text/0000-implied-bounds.md +++ b/text/0000-implied-bounds.md @@ -233,7 +233,7 @@ fn debug_foo>(arg: U) { # Reference-Level Explanation [reference]: #reference -This design has already been experimented on [Chalk](https://github.com/nikomatsakis/chalk), to some extent. The current design has been driven by issue [#12], it is a good read to understand why we *need* to expand where clauses as described below. +This is the fully-detailed design and you probably don't need to read everything. This design has already been experimented on [Chalk](https://github.com/nikomatsakis/chalk), to some extent. The current design has been driven by issue [#12], it is a good read to understand why we *need* to expand where clauses as described below. We'll use the grammar from [RFC 1214] to detail the rules: ``` From 1feb567b56f5e5e4f12f8fb95caa4ab818a5916b Mon Sep 17 00:00:00 2001 From: Alexandre Martin Date: Fri, 28 Jul 2017 23:50:14 +0200 Subject: [PATCH 3/6] Update summary --- text/0000-implied-bounds.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-implied-bounds.md b/text/0000-implied-bounds.md index 9798db38363..a7aa7267376 100644 --- a/text/0000-implied-bounds.md +++ b/text/0000-implied-bounds.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Eliminate the need for “redundant” bounds on functions and impls where those bounds can be inferred from the input types. For example, in this simple program, the impl would no longer require a bound, because it can be inferred from the `Foo` type: +Eliminate the need for “redundant” bounds on functions and impls where those bounds can be inferred from the input types and other trait bounds. For example, in this simple program, the impl would no longer require a bound, because it can be inferred from the `Foo` type: ```rust struct Foo { .. } @@ -15,7 +15,7 @@ impl Foo { ... } ``` -Hence, simply writing `impl Foo { ... }` would suffice. We currently support implied bounds for super traits, projections and lifetimes. We propose to extend this to all where clauses on traits and types, as was already discussed [here][niko]. +Hence, simply writing `impl Foo { ... }` would suffice. We currently support implied bounds for lifetime bounds, super traits and projections. We propose to extend this to all where clauses on traits and types, as was already discussed [here][niko]. # Motivation [motivation]: #motivation From 2aa7990f7e95a4ece7dfbb14f502fb27259930b3 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 25 Aug 2017 17:27:39 +0200 Subject: [PATCH 4/6] Add an unresolved question --- text/0000-implied-bounds.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-implied-bounds.md b/text/0000-implied-bounds.md index a7aa7267376..17f7135b269 100644 --- a/text/0000-implied-bounds.md +++ b/text/0000-implied-bounds.md @@ -725,7 +725,8 @@ These essentially try to address the breaking change when removing a bound on a # Unresolved questions [unresolved]: #unresolved-questions -@nikomatsakis pointed [here][niko] that implied bounds can interact badly with current inference rules. +* Should we try to limit the range of implied bounds to be crate-local (or module-local, etc)? +* @nikomatsakis pointed [here][niko] that implied bounds can interact badly with current inference rules. [#12]: https://github.com/nikomatsakis/chalk/issues/12 From df02a2adc4bcd0ac9456be7b9f0ebcc3a8b32d40 Mon Sep 17 00:00:00 2001 From: Alexandre Martin Date: Thu, 7 Sep 2017 19:31:12 +0200 Subject: [PATCH 5/6] Add a remark about `Sized` bounds --- text/0000-implied-bounds.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-implied-bounds.md b/text/0000-implied-bounds.md index 17f7135b269..b5310efc2c1 100644 --- a/text/0000-implied-bounds.md +++ b/text/0000-implied-bounds.md @@ -420,6 +420,8 @@ WF(T: Bar) WF(T: Foo) ``` +**Remark**: Reverse rules include implicit `Sized` bounds on type declarations. However, they do not include (explicit) `?Sized` bounds since those are not *real* trait bounds, but only a way to disable the implicit `Sized` bound. + ## Input types We define the notion of input types of a type. Basically, input types refer to all types that are accessible from referencing to a specific type. For example, a function will assume that the input types of its arguments are well-formed, hence in the body of that function we'll be able to derive implied bounds thanks to the reverse rules described earlier. From 018e9b38606b0a57aea8c9712f548ef3fd47a9f6 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 11 Sep 2017 08:56:55 -0700 Subject: [PATCH 6/6] RFC 2089: Implied bounds --- text/{0000-implied-bounds.md => 2089-implied-bounds.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-implied-bounds.md => 2089-implied-bounds.md} (99%) diff --git a/text/0000-implied-bounds.md b/text/2089-implied-bounds.md similarity index 99% rename from text/0000-implied-bounds.md rename to text/2089-implied-bounds.md index b5310efc2c1..8abe4a80914 100644 --- a/text/0000-implied-bounds.md +++ b/text/2089-implied-bounds.md @@ -1,7 +1,7 @@ - Feature Name: `implied-bounds` - Start Date: (fill me in with today's date, YYYY-MM-DD) -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: https://github.com/rust-lang/rfcs/pull/2089 +- Rust Issue: https://github.com/rust-lang/rust/issues/44491 # Summary [summary]: #summary