From 37b7eff9484c90dc4d60de456dfb78de22bae572 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Sun, 27 Oct 2019 11:32:55 +0100 Subject: [PATCH 1/5] Moved coherence and orphan rules to a separate file, and updated to match RFC#2451 --- src/items/coherence.md | 79 +++++++++++++++++++++++++++++++++ src/items/implementations.md | 25 +---------- src/special-types-and-traits.md | 2 +- 3 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 src/items/coherence.md diff --git a/src/items/coherence.md b/src/items/coherence.md new file mode 100644 index 000000000..6f253d65d --- /dev/null +++ b/src/items/coherence.md @@ -0,0 +1,79 @@ +## What is coherence and why do we care? + +Coherence means that for any given trait and type, there is one specific +implementation that applies. This is important for Rust to be easy to reason +about. When you write `::trait_method`, the compiler needs to know +what actual implementation to use. + +In languages without coherence, the compiler has to have some way to choose +which implementation to use when multiple implementations could apply. Scala +does this by having complex scope resolution rules for "implicit" parameters. +Haskell (when a discouraged flag is enabled) does this by picking an impl +arbitrarily. + +Rust's solution is to enforce that there is only one impl to choose from at all. +While the rules required to enforce this are quite complex, the result is easy +to reason about, and is generally considered to be quite important for Rust. +New features like specialization allow more than one impl to apply, but for any +given type and trait, there will always be exactly one which is most specific, +and deterministically be chosen. + +An important piece of enforcing coherence is restricting "orphan impls". An impl +is orphaned if it is implementing a trait you don't own for a type you don't +own. Rust's rules around this balance two separate, but related goals: + +- Ensuring that two crates can't write impls that would overlap (e.g. no crate + other than `std` can write `impl From for Vec`. If they could, + your program might stop compiling just by using two crates with an overlapping + impl). +- Restricting the impls that can be written so crates can add implementations + for traits/types they do own without worrying about breaking downstream + crates. + + +## Definitions + +Local Trait: A trait which was defined in the current crate. Whether a trait is +local or not has nothing to do with type parameters. Given `trait Foo`, +`Foo` is always local, regardless of the types used for `T` or `U`. + +Local Type: A struct, enum, or union which was defined in the current crate. +This is not affected by type parameters. `struct Foo` is considered local, but +`Vec` is not. `LocalType` is local. Type aliases and trait +aliases do not affect locality. + +Covered Type: A type which appears as a parameter to another type. For example, +`T` is uncovered, but the `T` in `Vec` is covered. This is only relevant for +type parameters. + +Blanket Impl: Any implementation where a type appears uncovered. `impl Foo +for T`, `impl Bar for T`, `impl Bar> for T`, and `impl Bar +for Vec` are considered blanket impls. However, `impl Bar> for +Vec` is not a blanket impl, as all instances of `T` which appear in this impl +are covered by `Vec`. + +Fundamental Type: A type for which you cannot add a blanket impl backwards +compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is +considered local, `&T`, `&mut T`, and `Box` are also considered local. +Fundamental types cannot cover other types. Any time the term "covered type" is +used, the `T` in `&T`, `&mut T`, and `Box` is not considered covered. + + +## Concrete orphan rules + +Assumes the same definitions [as above](#definitions). + +Given `impl Trait for T0`, an impl is valid only if at +least one of the following is true: + +- `Trait` is a local trait +- All of + - At least one of the types `T0..=Tn` must be a local type. Let `Ti` be the + first such type. + - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding + `Ti`) + +We only restrict the appearance of *uncovered* type parameters. Once again, it is +important to note that for the purposes of coherence, `#[fundamental]` types are +special. The `T` in `Box` is not considered covered, and `Box` +is considered local. diff --git a/src/items/implementations.md b/src/items/implementations.md index fedc4c18c..372c6f4e7 100644 --- a/src/items/implementations.md +++ b/src/items/implementations.md @@ -152,29 +152,7 @@ impl Shape for Circle { ### Trait Implementation Coherence -A trait implementation is considered incoherent if either the orphan check fails -or there are overlapping implementation instances. - -Two trait implementations overlap when there is a non-empty intersection of the -traits the implementation is for, the implementations can be instantiated with -the same type. - -The `Orphan Check` states that every trait implementation must meet either of -the following conditions: - -1. The trait being implemented is defined in the same crate. - -2. At least one of either `Self` or a generic type parameter of the trait must - meet the following grammar, where `C` is a nominal type defined - within the containing crate: - - ```ignore - T = C - | &C - | &mut C - | Box - ``` +A trait implementation must follow the [coherence] rules. ## Generic Implementations @@ -224,3 +202,4 @@ attributes]. [path]: ../paths.md [the lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes [Unsafe traits]: traits.md#unsafe-traits +[coherence]: coherence.md \ No newline at end of file diff --git a/src/special-types-and-traits.md b/src/special-types-and-traits.md index 9c2e0b1ff..432471e22 100644 --- a/src/special-types-and-traits.md +++ b/src/special-types-and-traits.md @@ -170,7 +170,7 @@ compiler, not by [implementation items]. [Methods]: items/associated-items.md#associated-functions-and-methods [method resolution]: expressions/method-call-expr.md [operators]: expressions/operator-expr.md -[orphan rules]: items/implementations.md#trait-implementation-coherence +[orphan rules]: items/coherence.md [Raw pointers]: types/pointer.md#raw-pointers-const-and-mut [`static` items]: items/static-items.md [Shared references]: types/pointer.md#shared-references- From 66cc1bd30028dac0e4f2b39a310653250268d267 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Mon, 28 Oct 2019 09:59:02 +0100 Subject: [PATCH 2/5] Use the reference POV and remove motivation and irrelevant info --- src/glossary.md | 34 ++++++++++++++ src/items/coherence.md | 79 --------------------------------- src/items/implementations.md | 31 ++++++++++++- src/special-types-and-traits.md | 2 +- 4 files changed, 64 insertions(+), 82 deletions(-) delete mode 100644 src/items/coherence.md diff --git a/src/glossary.md b/src/glossary.md index 36c0c9314..760ebb574 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -30,6 +30,14 @@ items are defined in [implementations] and declared in [traits]. Only functions, constants, and type aliases can be associated. Contrast to a [free item]. +### Blanket Implementation + +Any implementation where a type appears [uncovered](#uncovered-type). `impl Foo +for T`, `impl Bar for T`, `impl Bar> for T`, and `impl Bar +for Vec` are considered blanket impls. However, `impl Bar> for +Vec` is not a blanket impl, as all instances of `T` which appear in this impl +are covered by `Vec`. + ### Bound Bounds are constraints on a type or trait. For example, if a bound @@ -65,6 +73,13 @@ For example, `2 + (3 * 4)` is an expression that returns the value 14. An [item] that is not a member of an [implementation], such as a *free function* or a *free const*. Contrast to an [associated item]. +### Fundamental Type + +Includes `&`, `&mut`, `Box` and `Pin`. Any time a type `T` is +considered [local](#local-type), `&T`, `&mut T`, `Box` and `Pin` are also considered local. +Fundamental types cannot [cover](#uncovered-type) other types. Any time the term "covered type" is +used, the `T` in `&T`, `&mut T`, `Box` and `Pin` is not considered covered. + ### Inhabited A type is inhabited if it has constructors and therefore can be instantiated. An inhabited type is @@ -87,6 +102,19 @@ A variable is initialized if it has been assigned a value and hasn't since been moved from. All other memory locations are assumed to be uninitialized. Only unsafe Rust can create such a memory without initializing it. +### Local Trait + +A trait which was defined in the current crate. Whether a trait is +local or not has nothing to do with type parameters. Given `trait Foo`, +`Foo` is always local, regardless of the types used for `T` or `U`. + +### Local Type + +A struct, enum, or union which was defined in the current crate. +This is not affected by type parameters. `struct Foo` is considered local, but +`Vec` is not. `LocalType` is local. Type aliases and trait +aliases do not affect locality. + ### Nominal types Types that can be referred to by a path directly. Specifically [enums], @@ -158,6 +186,12 @@ It allows a type to make certain promises about its behavior. Generic functions and generic structs can use traits to constrain, or bound, the types they accept. +### Uncovered Type + +A type which does not appear as a parameter to another type. For example, +`T` is uncovered, but the `T` in `Vec` is covered. This is only relevant for +type parameters. + ### Undefined behavior Compile-time or run-time behavior that is not specified. This may result in, diff --git a/src/items/coherence.md b/src/items/coherence.md deleted file mode 100644 index 6f253d65d..000000000 --- a/src/items/coherence.md +++ /dev/null @@ -1,79 +0,0 @@ -## What is coherence and why do we care? - -Coherence means that for any given trait and type, there is one specific -implementation that applies. This is important for Rust to be easy to reason -about. When you write `::trait_method`, the compiler needs to know -what actual implementation to use. - -In languages without coherence, the compiler has to have some way to choose -which implementation to use when multiple implementations could apply. Scala -does this by having complex scope resolution rules for "implicit" parameters. -Haskell (when a discouraged flag is enabled) does this by picking an impl -arbitrarily. - -Rust's solution is to enforce that there is only one impl to choose from at all. -While the rules required to enforce this are quite complex, the result is easy -to reason about, and is generally considered to be quite important for Rust. -New features like specialization allow more than one impl to apply, but for any -given type and trait, there will always be exactly one which is most specific, -and deterministically be chosen. - -An important piece of enforcing coherence is restricting "orphan impls". An impl -is orphaned if it is implementing a trait you don't own for a type you don't -own. Rust's rules around this balance two separate, but related goals: - -- Ensuring that two crates can't write impls that would overlap (e.g. no crate - other than `std` can write `impl From for Vec`. If they could, - your program might stop compiling just by using two crates with an overlapping - impl). -- Restricting the impls that can be written so crates can add implementations - for traits/types they do own without worrying about breaking downstream - crates. - - -## Definitions - -Local Trait: A trait which was defined in the current crate. Whether a trait is -local or not has nothing to do with type parameters. Given `trait Foo`, -`Foo` is always local, regardless of the types used for `T` or `U`. - -Local Type: A struct, enum, or union which was defined in the current crate. -This is not affected by type parameters. `struct Foo` is considered local, but -`Vec` is not. `LocalType` is local. Type aliases and trait -aliases do not affect locality. - -Covered Type: A type which appears as a parameter to another type. For example, -`T` is uncovered, but the `T` in `Vec` is covered. This is only relevant for -type parameters. - -Blanket Impl: Any implementation where a type appears uncovered. `impl Foo -for T`, `impl Bar for T`, `impl Bar> for T`, and `impl Bar -for Vec` are considered blanket impls. However, `impl Bar> for -Vec` is not a blanket impl, as all instances of `T` which appear in this impl -are covered by `Vec`. - -Fundamental Type: A type for which you cannot add a blanket impl backwards -compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is -considered local, `&T`, `&mut T`, and `Box` are also considered local. -Fundamental types cannot cover other types. Any time the term "covered type" is -used, the `T` in `&T`, `&mut T`, and `Box` is not considered covered. - - -## Concrete orphan rules - -Assumes the same definitions [as above](#definitions). - -Given `impl Trait for T0`, an impl is valid only if at -least one of the following is true: - -- `Trait` is a local trait -- All of - - At least one of the types `T0..=Tn` must be a local type. Let `Ti` be the - first such type. - - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding - `Ti`) - -We only restrict the appearance of *uncovered* type parameters. Once again, it is -important to note that for the purposes of coherence, `#[fundamental]` types are -special. The `T` in `Box` is not considered covered, and `Box` -is considered local. diff --git a/src/items/implementations.md b/src/items/implementations.md index 372c6f4e7..89f209396 100644 --- a/src/items/implementations.md +++ b/src/items/implementations.md @@ -152,7 +152,31 @@ impl Shape for Circle { ### Trait Implementation Coherence -A trait implementation must follow the [coherence] rules. +A trait implementation is considered incoherent if either the orphan rules check fails +or there are overlapping implementation instances. + +Two trait implementations overlap when there is a non-empty intersection of the +traits the implementation is for, the implementations can be instantiated with +the same type. + +#### Orphan rules + +Given `impl Trait for T0`, an impl is valid only if at +least one of the following is true: + +- `Trait` is a [local trait] +- All of + - At least one of the types `T0..=Tn` must be a [local type]. Let `Ti` be the + first such type. + - No [uncovered type] parameters `P1..=Pn` may appear in `T0..Ti` (excluding + `Ti`) + +We only restrict the appearance of *uncovered* type parameters. Once again, it is +important to note that for the purposes of coherence, [fundamental types] are +special. The `T` in `Box` is not considered covered, and `Box` +is considered local. + ## Generic Implementations @@ -202,4 +226,7 @@ attributes]. [path]: ../paths.md [the lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes [Unsafe traits]: traits.md#unsafe-traits -[coherence]: coherence.md \ No newline at end of file +[local trait]: ../glossary.md#local-trait +[local type]: ../glossary.md#local-type +[fundamental types]: ../glossary.md#fundamental-type +[uncovered type]: ../glossary.md#uncovered-type \ No newline at end of file diff --git a/src/special-types-and-traits.md b/src/special-types-and-traits.md index 432471e22..9c2e0b1ff 100644 --- a/src/special-types-and-traits.md +++ b/src/special-types-and-traits.md @@ -170,7 +170,7 @@ compiler, not by [implementation items]. [Methods]: items/associated-items.md#associated-functions-and-methods [method resolution]: expressions/method-call-expr.md [operators]: expressions/operator-expr.md -[orphan rules]: items/coherence.md +[orphan rules]: items/implementations.md#trait-implementation-coherence [Raw pointers]: types/pointer.md#raw-pointers-const-and-mut [`static` items]: items/static-items.md [Shared references]: types/pointer.md#shared-references- From 398ef3ee041e1302b82957ef2a6fc64783ac3d66 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Mon, 28 Oct 2019 21:55:02 +0100 Subject: [PATCH 3/5] Update glossary and implementations Co-Authored-By: Mazdak Farrokhzad --- src/glossary.md | 34 +++++++++++++++++----------------- src/items/implementations.md | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/glossary.md b/src/glossary.md index 760ebb574..30988d1d7 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -30,12 +30,12 @@ items are defined in [implementations] and declared in [traits]. Only functions, constants, and type aliases can be associated. Contrast to a [free item]. -### Blanket Implementation +### Blanket implementation Any implementation where a type appears [uncovered](#uncovered-type). `impl Foo for T`, `impl Bar for T`, `impl Bar> for T`, and `impl Bar for Vec` are considered blanket impls. However, `impl Bar> for -Vec` is not a blanket impl, as all instances of `T` which appear in this impl +Vec` is not a blanket impl, as all instances of `T` which appear in this `impl` are covered by `Vec`. ### Bound @@ -73,12 +73,12 @@ For example, `2 + (3 * 4)` is an expression that returns the value 14. An [item] that is not a member of an [implementation], such as a *free function* or a *free const*. Contrast to an [associated item]. -### Fundamental Type +### Fundamental type Includes `&`, `&mut`, `Box` and `Pin`. Any time a type `T` is -considered [local](#local-type), `&T`, `&mut T`, `Box` and `Pin` are also considered local. +considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` are also considered local. Fundamental types cannot [cover](#uncovered-type) other types. Any time the term "covered type" is -used, the `T` in `&T`, `&mut T`, `Box` and `Pin` is not considered covered. +used, the `T` in `&T`, `&mut T`, `Box`, and `Pin` is not considered covered. ### Inhabited @@ -102,18 +102,18 @@ A variable is initialized if it has been assigned a value and hasn't since been moved from. All other memory locations are assumed to be uninitialized. Only unsafe Rust can create such a memory without initializing it. -### Local Trait +### Local trait -A trait which was defined in the current crate. Whether a trait is -local or not has nothing to do with type parameters. Given `trait Foo`, -`Foo` is always local, regardless of the types used for `T` or `U`. +A `trait` which was defined in the current crate. A trait definition is local +or not independent of applied type arguments. Given `trait Foo`, +`Foo` is always local, regardless of the types substituted for `T` and `U`. -### Local Type +### Local type -A struct, enum, or union which was defined in the current crate. -This is not affected by type parameters. `struct Foo` is considered local, but -`Vec` is not. `LocalType` is local. Type aliases and trait -aliases do not affect locality. +A `struct`, `enum`, or `union` which was defined in the current crate. +This is not affected by applied type arguments. `struct Foo` is considered local, but +`Vec` is not. `LocalType` is local. Type aliases do not +affect locality. ### Nominal types @@ -186,11 +186,11 @@ It allows a type to make certain promises about its behavior. Generic functions and generic structs can use traits to constrain, or bound, the types they accept. -### Uncovered Type +### Uncovered type -A type which does not appear as a parameter to another type. For example, +A type which does not appear as an argument to another type. For example, `T` is uncovered, but the `T` in `Vec` is covered. This is only relevant for -type parameters. +type arguments. ### Undefined behavior diff --git a/src/items/implementations.md b/src/items/implementations.md index 89f209396..39c796eba 100644 --- a/src/items/implementations.md +++ b/src/items/implementations.md @@ -162,7 +162,7 @@ be instantiable with the same set of types for the input type parameters. --> #### Orphan rules -Given `impl Trait for T0`, an impl is valid only if at +Given `impl Trait for T0`, an `impl` is valid only if at least one of the following is true: - `Trait` is a [local trait] @@ -172,8 +172,8 @@ least one of the following is true: - No [uncovered type] parameters `P1..=Pn` may appear in `T0..Ti` (excluding `Ti`) -We only restrict the appearance of *uncovered* type parameters. Once again, it is -important to note that for the purposes of coherence, [fundamental types] are +Only the appearance of *uncovered* type parameters is restricted. +Note that for the purposes of coherence, [fundamental types] are special. The `T` in `Box` is not considered covered, and `Box` is considered local. @@ -229,4 +229,4 @@ attributes]. [local trait]: ../glossary.md#local-trait [local type]: ../glossary.md#local-type [fundamental types]: ../glossary.md#fundamental-type -[uncovered type]: ../glossary.md#uncovered-type \ No newline at end of file +[uncovered type]: ../glossary.md#uncovered-type From 6a78aa5b0b09f78a6fa28b9dc3078c9b134785a9 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Mon, 28 Oct 2019 22:33:55 +0100 Subject: [PATCH 4/5] Type constructors, not types --- src/glossary.md | 21 ++++++++++++++++----- src/items/implementations.md | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/glossary.md b/src/glossary.md index 30988d1d7..b603faf57 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -73,12 +73,23 @@ For example, `2 + (3 * 4)` is an expression that returns the value 14. An [item] that is not a member of an [implementation], such as a *free function* or a *free const*. Contrast to an [associated item]. -### Fundamental type +### Fundamental type constructors -Includes `&`, `&mut`, `Box` and `Pin`. Any time a type `T` is -considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` are also considered local. -Fundamental types cannot [cover](#uncovered-type) other types. Any time the term "covered type" is -used, the `T` in `&T`, `&mut T`, `Box`, and `Pin` is not considered covered. +Fundamental type constructors are XXXX. + +All of + +- `&` +- `&mut` +- `Box` +- `Pin` + +are fundamental type constructors. + +Any time a type `T` is considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` +are also considered local. Fundamental type constructors cannot [cover](#uncovered-type) other types. +Any time the term "covered type" is used, +the `T` in `&T`, `&mut T`, `Box`, and `Pin` is not considered covered. ### Inhabited diff --git a/src/items/implementations.md b/src/items/implementations.md index 39c796eba..c4e63b2fa 100644 --- a/src/items/implementations.md +++ b/src/items/implementations.md @@ -228,5 +228,5 @@ attributes]. [Unsafe traits]: traits.md#unsafe-traits [local trait]: ../glossary.md#local-trait [local type]: ../glossary.md#local-type -[fundamental types]: ../glossary.md#fundamental-type +[fundamental types]: ../glossary.md#fundamental-type-constructors [uncovered type]: ../glossary.md#uncovered-type From eadbdafec0c4e302c2e564a773fe5bff60ddac1d Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Thu, 31 Oct 2019 10:06:55 +0100 Subject: [PATCH 5/5] Use definitions from RFC 1023 --- src/glossary.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/glossary.md b/src/glossary.md index b603faf57..65b1978d0 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -73,18 +73,15 @@ For example, `2 + (3 * 4)` is an expression that returns the value 14. An [item] that is not a member of an [implementation], such as a *free function* or a *free const*. Contrast to an [associated item]. -### Fundamental type constructors - -Fundamental type constructors are XXXX. +### Fundamental traits -All of +A fundamental trait is one where adding an impl of it for an existing type is a breaking change. +The `Fn` traits and `Sized` are fundamental. -- `&` -- `&mut` -- `Box` -- `Pin` +### Fundamental type constructors -are fundamental type constructors. +A fundamental type constructor is a type where implementing a [blanket implementation](#blanket-implementation) over it +is a breaking change. `&`, `&mut`, `Box`, and `Pin` are fundamental. Any time a type `T` is considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` are also considered local. Fundamental type constructors cannot [cover](#uncovered-type) other types.