From 36d852918f5de255a706667af9373edea7429d3f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 29 Jun 2015 22:54:09 +0530 Subject: [PATCH 01/16] Improve E0001-E0003 --- src/librustc/diagnostics.rs | 47 +++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 4e21efcf9eb6b..0c3c9b2ec8011 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -22,6 +22,22 @@ matched, one of the preceding patterns will match. This means that perhaps some of the preceding patterns are too general, this one is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +``` +match foo { + Some(bar) => {/* ... */} + None => {/* ... */} + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +checks. "##, E0002: r##" @@ -31,13 +47,40 @@ it is impossible to create an instance of an empty type, so empty match expressions are almost never desired. This error is typically fixed by adding one or more cases to the match expression. -An example of an empty type is `enum Empty { }`. +An example of an empty type is `enum Empty { }`. So, the following will work: + +``` +fn foo(x: Empty) { + match x { + // empty + } +} + +``` + +but this won't: + +``` +fn foo(x: Option) { + match x { + // empty + } +} +``` "##, E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never match the input to a match expression. To match against NaN values, you should -instead use the `is_nan` method in a guard, as in: `x if x.is_nan() => ...` +instead use the `is_nan()` method in a guard, like so: + +``` +match number { + // ... + x if x.is_nan() => { /* ... */ } + // ... +} +``` "##, E0004: r##" From 688a09910a01800bfa031892540f29bb0c3b4095 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 29 Jun 2015 22:57:35 +0530 Subject: [PATCH 02/16] Move E0006 into E0005 --- src/librustc/diagnostics.rs | 11 ++--------- src/librustc/middle/check_match.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 0c3c9b2ec8011..75d94cdffb13d 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -72,7 +72,7 @@ fn foo(x: Option) { E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never match the input to a match expression. To match against NaN values, you should -instead use the `is_nan()` method in a guard, like so: +instead use the `is_nan()` method in a guard, like so: ``` match number { @@ -96,7 +96,6 @@ underscore `_` wildcard pattern can be added after all other patterns to match "anything else". "##, -// FIXME: Remove duplication here? E0005: r##" Patterns used to bind names must be irrefutable, that is, they must guarantee that a name will be extracted in all cases. If you encounter this error you @@ -104,13 +103,6 @@ probably need to use a `match` or `if let` to deal with the possibility of failure. "##, -E0006: r##" -Patterns used to bind names must be irrefutable, that is, they must guarantee -that a name will be extracted in all cases. If you encounter this error you -probably need to use a `match` or `if let` to deal with the possibility of -failure. -"##, - E0007: r##" This error indicates that the bindings in a match arm would require a value to be moved into more than one location, thus violating unique ownership. Code like @@ -1262,6 +1254,7 @@ contain references (with a maximum lifetime of `'a`). register_diagnostics! { + // E0006 // merged with E0005 E0017, E0022, E0038, diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d8c2341df2d9f..ea86fa318b45d 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -1049,7 +1049,7 @@ fn check_fn(cx: &mut MatchCheckCtxt, for input in &decl.inputs { is_refutable(cx, &*input.pat, |pat| { - span_err!(cx.tcx.sess, input.pat.span, E0006, + span_err!(cx.tcx.sess, input.pat.span, E0005, "refutable pattern in function argument: `{}` not covered", pat_to_string(pat) ); From 5d31deeae43e8f3803604f837649bbaeb625e726 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Jul 2015 14:05:56 +0530 Subject: [PATCH 03/16] Add error explanation for E0017 --- src/librustc/diagnostics.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 75d94cdffb13d..4aac09e02b7fb 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -306,6 +306,28 @@ const FOO: i32 = { const X : i32 = 0; X }; ``` "##, +E0017: r##" +References in statics and constants may only refer to immutable values. Example: + +``` +static X: i32 = 1; +const C: i32 = 2; + +// these three are not allowed: +const CR: &'static mut i32 = &mut C; +static STATIC_REF: &'static mut i32 = &mut X; +static CONST_REF: &'static mut i32 = &mut C; +``` + +Statics are shared everywhere, and if they refer to mutable data one might +violate memory safety since holding multiple mutable references to shared data +is not allowed. + +If you really want global mutable state, try using a global `UnsafeCell` or +`static mut`. + +"##, + E0018: r##" The value of static and const variables must be known at compile time. You can't cast a pointer as an integer because we can't know what value the @@ -1255,7 +1277,6 @@ contain references (with a maximum lifetime of `'a`). register_diagnostics! { // E0006 // merged with E0005 - E0017, E0022, E0038, // E0134, From 37a84bc82169bfdd0c67c519e710ba4488b82d55 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Jul 2015 14:13:25 +0530 Subject: [PATCH 04/16] Add error explanation for E0022 --- src/librustc/diagnostics.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 4aac09e02b7fb..0375f64d7a505 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -375,7 +375,7 @@ fn main() { ``` Remember: you can't use a function call inside a const's initialization -expression! However, you can totally use it elsewhere you want: +expression! However, you can totally use it anywhere else: ``` fn main() { @@ -392,6 +392,24 @@ This error indicates that an attempt was made to divide by zero (or take the remainder of a zero divisor) in a static or constant expression. "##, +E0022: r##" +Constant functions are not allowed to mutate anything. Thus, binding to an +argument with a mutable pattern is not allowed. For example, + +``` +const fn foo(mut x: u8) { + // do stuff +} +``` + +is bad because the function body may not mutate `x`. + +Remove any mutable bindings from the argument list to fix this error. In case +you need to mutate the argument, try lazily initializing a global variable +instead of using a const fn, or refactoring the code to a functional style to +avoid mutation if possible. +"##, + E0030: r##" When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to @@ -1277,7 +1295,6 @@ contain references (with a maximum lifetime of `'a`). register_diagnostics! { // E0006 // merged with E0005 - E0022, E0038, // E0134, // E0135, From 950c2d813571ad98730a7f4b1b7c0b679315bd69 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 11 Jul 2015 16:11:02 +0530 Subject: [PATCH 05/16] Add huge explanation for E0038 (object safety) --- src/librustc/diagnostics.rs | 251 +++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 0375f64d7a505..949f4457aa324 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -428,6 +428,256 @@ match 5u32 { ``` "##, +E0038: r####" + +Trait objects like `Box`, can only be constructed when certain +requirements are obeyed by the trait in question. + +Trait objects are a form of dynamic dispatch and use dynamically sized types. +So, for a given trait `Trait`, when `Trait` is treated as a type, as in +`Box`, the inner type is "unsized". In such cases the boxed pointer is a +"fat pointer" and contains an extra pointer to a method table for dynamic +dispatch. This design mandates some restrictions on the types of traits that are +allowed to be used in trait objects, which are collectively termed as "object +safety" rules. + +Attempting to create a trait object for a non object-safe trait will trigger +this error. + + +There are various rules: + +### The trait cannot require `Self: Sized` + +When `Trait` is treated as a type, the type does not implement the special +`Sized` trait, because the type does not have a known size at compile time and +can only be accessed behind a pointer. Thus, if we have a trait like the +following: + +``` +trait Foo where Self: Sized { + +} +``` + +we cannot create an object of type `Box` or `&Foo` since in this case +`Self` would not be `Sized`. + +Generally `Self : Sized` is used to indicate that the trait should not be used +as a trait object. If the trait comes from your own crate, consider removing +this restriction. + +### Method references the `Self` type in its arguments or return type + +This happens when a trait has a method like the following: + +``` +trait Trait { + fn foo(&self) -> Self; +} +impl Trait for String { + fn foo(&self) -> Self { + "hi".to_owned() + } +} + +impl Trait for u8 { + fn foo(&self) -> Self { + 1 + } +} +``` + +In such a case, the compiler cannot predict the return type of `foo()` in a case +like the following: + +``` +fn call_foo(x: Box) { + let y = x.foo(); // What type is y? + // ... +} +``` + +If the offending method isn't actually being called on the trait object, you can +add a `where Self: Sized` bound on the method: + +``` +trait Trait { + fn foo(&self) -> Self where Self: Sized; + // more functions +} +``` + +Now, `foo()` can no longer be called on the trait object, but you will be +allowed to call other trait methods and construct the trait objects. With such a +bound, one can still call `foo()` on types implementing that trait that aren't +behind trait objects. + +### Method has generic type parameters + +As mentioned before, trait objects contain pointers to method tables. So, if we +have + +``` +trait Trait { + fn foo(&self); +} +impl Trait for String { + fn foo(&self) { + // implementation 1 + } +} +impl Trait for u8 { + fn foo(&self) { + // implementation 2 + } +} +// ... +``` + +at compile time a table of all implementations of `Trait`, containing pointers +to the implementation of `foo()` would be generated. + +This works fine, but when we the method gains generic parameters, we can have a +problem. + +Usually, generic parameters get _monomorphized_. For example, if I have + +``` +fn foo(x: T) { + // ... +} +``` + +the machine code for `foo::()`, `foo::()`, `foo::()`, or any +other type substitution is different. Hence the compiler generates the +implementation on-demand. If you call `foo()` with a `bool` parameter, the +compiler will only generate code for `foo::()`. When we have additional +type parameters, the number of monomorphized implementations the compiler +generates does not grow drastically, since the compiler will only generate an +implementation if the function is called with hard substitutions. + +However, with trait objects we have to make a table containing _every object +that implements the trait_. Now, if it has type parameters, we need to add +implementations for every type that implements the trait, bloating the table +quickly. + +For example, with + +``` +trait Trait { + fn foo(&self, on: T); + // more methods +} +impl Trait for String { + fn foo(&self, on: T) { + // implementation 1 + } +} +impl Trait for u8 { + fn foo(&self, on: T) { + // implementation 2 + } +} +// 8 more implementations +``` + +Now, if I have the following code: + +``` +fn call_foo(thing: Box) { + thing.foo(true); // this could be any one of the 8 types above + thing.foo(1); + thing.foo("hello"); +} +``` + +we don't just need to create a table of all implementations of all methods of +`Trait`, we need to create a table of all implementations of `foo()`, _for each +different type fed to `foo()`_. In this case this turns out to be (10 types +implementing `Trait`)*(3 types being fed to `foo()`) = 30 implementations! + +With real world traits these numbers can grow drastically. + +To fix this, it is suggested to use a `where Self: Sized` bound similar to the +fix for the sub-error above if you do not intend to call the method with type +parameters: + +``` +trait Trait { + fn foo(&self, on: T) where Self: Sized; + // more methods +} +``` + +If this is not an option, consider replacing the type parameter with another +trait object (e.g. if `T: OtherTrait`, use `on: Box`). If the number +of types you intend to feed to this method is limited, consider manually listing +out the methods of different types. + +### Method has no receiver + +Methods that do not take a `self` parameter can't be called since there won't be +a way to get a pointer to the method table for them + +``` +trait Foo { + fn foo() -> u8; +} +``` + +This could be called as `::foo()`, which would not be able to pick +an implementation. + +Adding a `Self: Sized` bound to these methods will generally make this compile. + + +``` +trait Foo { + fn foo() -> u8 where Self: Sized; +} +``` + +### The trait cannot use `Self` as a type parameter in the supertrait listing + +This is similar to the second sub-error, but subtler. It happens in situations +like the following: + +``` +trait Super {} + +trait Trait: Super { +} + +struct Foo; + +impl Super for Foo{} + +impl Trait for Foo {} +``` + +Here, the supertrait might have methods as follows: + +``` +trait Super { + fn get_a(&self) -> A; // note that this is object safe! +} +``` + +If the trait `Foo` was deriving from something like `Super` or +`Super` (where `Foo` itself is `Foo`), this is okay, because given a type +`get_a()` will definitely return an object of that type. + +However, if it derives from `Super`, the method `get_a()` would return an +object of unknown type when called on the function, _even though `Super` is +object safe_. `Self` type parameters let us make object safe traits no longer +safe, so they are forbidden when specifying supertraits. + +There's no easy fix for this, generally code will need to be refactored so that +you no longer need to derive from `Super`. + +"####, + E0079: r##" Enum variants which contain no data can be given a custom integer representation. This error indicates that the value provided is not an @@ -1295,7 +1545,6 @@ contain references (with a maximum lifetime of `'a`). register_diagnostics! { // E0006 // merged with E0005 - E0038, // E0134, // E0135, E0136, From 29d7147dda7f7fbca1341a7cad2f67107db49863 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 12 Jul 2015 02:00:35 +0530 Subject: [PATCH 06/16] Review fixes --- src/librustc/diagnostics.rs | 123 +++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 949f4457aa324..c7a20f243b0c1 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -55,10 +55,9 @@ fn foo(x: Empty) { // empty } } - ``` -but this won't: +However, this won't: ``` fn foo(x: Option) { @@ -71,7 +70,18 @@ fn foo(x: Option) { E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. To match against NaN values, you should +match the input to a match expression. So, the following will not compile: + +``` +const NAN: f32 = 0.0 / 0.0; + +match number { + NAN => { /* ... */ }, + // ... +} +``` + +To match against NaN values, you should instead use the `is_nan()` method in a guard, like so: ``` @@ -429,22 +439,20 @@ match 5u32 { "##, E0038: r####" +Trait objects like `Box` can only be constructed when certain +requirements are satisfied by the trait in question. -Trait objects like `Box`, can only be constructed when certain -requirements are obeyed by the trait in question. - -Trait objects are a form of dynamic dispatch and use dynamically sized types. -So, for a given trait `Trait`, when `Trait` is treated as a type, as in -`Box`, the inner type is "unsized". In such cases the boxed pointer is a -"fat pointer" and contains an extra pointer to a method table for dynamic -dispatch. This design mandates some restrictions on the types of traits that are -allowed to be used in trait objects, which are collectively termed as "object -safety" rules. +Trait objects are a form of dynamic dispatch and use a dynamically sized type +for the inner type. So, for a given trait `Trait`, when `Trait` is treated as a +type, as in `Box`, the inner type is "unsized". In such cases the boxed +pointer is a "fat pointer" that contains an extra pointer to a table of methods +(among other things) for dynamic dispatch. This design mandates some +restrictions on the types of traits that are allowed to be used in trait +objects, which are collectively termed as "object safety" rules. Attempting to create a trait object for a non object-safe trait will trigger this error. - There are various rules: ### The trait cannot require `Self: Sized` @@ -463,7 +471,7 @@ trait Foo where Self: Sized { we cannot create an object of type `Box` or `&Foo` since in this case `Self` would not be `Sized`. -Generally `Self : Sized` is used to indicate that the trait should not be used +Generally, `Self : Sized` is used to indicate that the trait should not be used as a trait object. If the trait comes from your own crate, consider removing this restriction. @@ -475,6 +483,7 @@ This happens when a trait has a method like the following: trait Trait { fn foo(&self) -> Self; } + impl Trait for String { fn foo(&self) -> Self { "hi".to_owned() @@ -488,8 +497,11 @@ impl Trait for u8 { } ``` -In such a case, the compiler cannot predict the return type of `foo()` in a case -like the following: +(Note that `&self` and `&mut self` are okay, it's additional `Self` types which +cause this problem) + +In such a case, the compiler cannot predict the return type of `foo()` in a +situation like the following: ``` fn call_foo(x: Box) { @@ -498,8 +510,10 @@ fn call_foo(x: Box) { } ``` -If the offending method isn't actually being called on the trait object, you can -add a `where Self: Sized` bound on the method: +If only some methods aren't object-safe, you can add a `where Self: Sized` bound +on them to mark them as explicitly unavailable to trait objects. The +functionality will still be available to all other implementers, including +`Box` which is itself sized (assuming you `impl Trait for Box`) ``` trait Trait { @@ -508,10 +522,10 @@ trait Trait { } ``` -Now, `foo()` can no longer be called on the trait object, but you will be -allowed to call other trait methods and construct the trait objects. With such a -bound, one can still call `foo()` on types implementing that trait that aren't -behind trait objects. +Now, `foo()` can no longer be called on a trait object, but you will now be +allowed to make a trait object, and that will be able to call any object-safe +methods". With such a bound, one can still call `foo()` on types implementing +that trait that aren't behind trait objects. ### Method has generic type parameters @@ -535,10 +549,10 @@ impl Trait for u8 { // ... ``` -at compile time a table of all implementations of `Trait`, containing pointers -to the implementation of `foo()` would be generated. +at compile time each implementation of `Trait` will produce a table containing +the various methods (and other items) related to the implementation. -This works fine, but when we the method gains generic parameters, we can have a +This works fine, but when the method gains generic parameters, we can have a problem. Usually, generic parameters get _monomorphized_. For example, if I have @@ -555,12 +569,14 @@ implementation on-demand. If you call `foo()` with a `bool` parameter, the compiler will only generate code for `foo::()`. When we have additional type parameters, the number of monomorphized implementations the compiler generates does not grow drastically, since the compiler will only generate an -implementation if the function is called with hard substitutions. +implementation if the function is called with unparametrized substitutions +(i.e., substitutions where none of the substituted types are themselves +parametrized). However, with trait objects we have to make a table containing _every object that implements the trait_. Now, if it has type parameters, we need to add -implementations for every type that implements the trait, bloating the table -quickly. +implementations for every type that implements the trait, and there could +theoretically be an infinite number of types. For example, with @@ -675,13 +691,46 @@ safe, so they are forbidden when specifying supertraits. There's no easy fix for this, generally code will need to be refactored so that you no longer need to derive from `Super`. - "####, E0079: r##" Enum variants which contain no data can be given a custom integer representation. This error indicates that the value provided is not an integer literal and is therefore invalid. + +For example, in the following code, + +``` +enum Foo { + Q = "32" +} +``` + +we try to set the representation to a string. + +There's no general fix for this; if you can work with an integer +then just set it to one: + +``` +enum Foo { + Q = 32 +} +``` + +however if you actually wanted a mapping between variants +and non-integer objects, it may be preferable to use a method with +a match instead: + +``` +enum Foo { Q } +impl Foo { + fn get_str(&self) -> &'static str { + match *self { + Foo::Q => "32", + } + } +} +``` "##, E0080: r##" @@ -704,8 +753,7 @@ https://doc.rust-lang.org/reference.html#ffi-attributes "##, E0109: r##" -You tried to give a type parameter to a type which doesn't need it. Erroneous -code example: +You tried to give a type parameter to a type which doesn't need it; for example: ``` type X = u32; // error: type parameters are not allowed on this type @@ -713,24 +761,25 @@ type X = u32; // error: type parameters are not allowed on this type Please check that you used the correct type and recheck its definition. Perhaps it doesn't need the type parameter. + Example: ``` -type X = u32; // ok! +type X = u32; // this compiles ``` "##, E0110: r##" -You tried to give a lifetime parameter to a type which doesn't need it. -Erroneous code example: +You tried to give a lifetime parameter to a type which doesn't need it; for +example: ``` type X = u32<'static>; // error: lifetime parameters are not allowed on // this type ``` -Please check that you used the correct type and recheck its definition, -perhaps it doesn't need the lifetime parameter. Example: +Please check that the correct type was used and recheck its definition; perhaps +it doesn't need the lifetime parameter. Example: ``` type X = u32; // ok! From e9c15307a97a4389d12a29ec8d0d42171710d71f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 18 Jul 2015 13:09:01 +0530 Subject: [PATCH 07/16] Add long diagnostic for E0136 --- src/librustc/diagnostics.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index c7a20f243b0c1..27a0ba31f03d2 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -803,6 +803,12 @@ fn main() { See also https://doc.rust-lang.org/book/unsafe.html "##, +// This shouldn't really ever trigger since the repeated value error comes first +E0136: r##" +A binary can only have one entry point, and by default that entry point is the +function `main()`. If there are multiple such functions, please rename one. +"##, + E0137: r##" This error indicates that the compiler found multiple functions with the `#[main]` attribute. This is an error because there must be a unique entry @@ -1596,7 +1602,6 @@ register_diagnostics! { // E0006 // merged with E0005 // E0134, // E0135, - E0136, E0138, E0139, E0264, // unknown external lang item From 3080df36e27ed74399f9fe22e4796e209abd2b69 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 18 Jul 2015 13:12:09 +0530 Subject: [PATCH 08/16] Add long diagnostic for E0138 --- src/librustc/diagnostics.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 27a0ba31f03d2..a409ba62f91cb 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -815,6 +815,12 @@ This error indicates that the compiler found multiple functions with the point into a Rust program. "##, +E0138: r##" +This error indicates that the compiler found multiple functions with the +`#[start]` attribute. This is an error because there must be a unique entry +point into a Rust program. +"##, + E0152: r##" Lang items are already implemented in the standard library. Unless you are writing a free-standing application (e.g. a kernel), you do not need to provide @@ -1602,7 +1608,6 @@ register_diagnostics! { // E0006 // merged with E0005 // E0134, // E0135, - E0138, E0139, E0264, // unknown external lang item E0269, // not all control paths return a value From 74768e9a6ccac85f813e4125c76f9cb52a2ac618 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 18 Jul 2015 13:41:44 +0530 Subject: [PATCH 09/16] Add long diagnostics for E0139 (type parameters in transmute) --- src/librustc/diagnostics.rs | 65 ++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index a409ba62f91cb..02f4cea87ca5f 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -821,6 +821,70 @@ This error indicates that the compiler found multiple functions with the point into a Rust program. "##, +// FIXME link this to the relevant turpl chapters for instilling fear of the +// transmute gods in the user +E0139: r##" +There are various restrictions on transmuting between types in Rust; for example +types being transmuted must have the same size. To apply all these restrictions, +the compiler must know the exact types that may be transmuted. When type +parameters are involved, this cannot always be done. + +So, for example, the following is not allowed: + +``` +struct Foo(Vec) + +fn foo(x: Vec) { + // we are transmuting between Vec and Foo here + let y: Foo = unsafe { transmute(x) }; + // do something with y +} +``` + +In this specific case there's a good chance that the transmute is harmless (but +this is not guaranteed by Rust). However, when alignment and enum optimizations +come into the picture, it's quite likely that the sizes may or may not match +with different type parameter substitutions. It's not possible to check this for +_all_ possible types, so `transmute()` simply only accepts types without any +unsubstituted type parameters. + +If you need this, there's a good chance you're doing something wrong. Keep in +mind that Rust doesn't guarantee much about the layout of different structs +(even two structs with identical declarations may have different layouts). If +there is a solution that avoids the transmute entirely, try it instead. + +If it's possible, hand-monomorphize the code by writing the function for each +possible type substitution. It's possible to use traits to do this cleanly, +for example: + +``` +trait MyTransmutableType { + fn transmute(Vec) -> Foo +} + +impl MyTransmutableType for u8 { + fn transmute(x: Foo) -> Vec { + transmute(x) + } +} +impl MyTransmutableType for String { + fn transmute(x: Foo) -> Vec { + transmute(x) + } +} +// ... more impls for the types you intend to transmute + +fn foo(x: Vec) { + let y: Foo = ::transmute(x); + // do something with y +} +``` + +Each impl will be checked for a size match in the transmute as usual, and since +there are no unbound type parameters involved, this should compile unless there +is a size mismatch in one of the impls. +"##, + E0152: r##" Lang items are already implemented in the standard library. Unless you are writing a free-standing application (e.g. a kernel), you do not need to provide @@ -1608,7 +1672,6 @@ register_diagnostics! { // E0006 // merged with E0005 // E0134, // E0135, - E0139, E0264, // unknown external lang item E0269, // not all control paths return a value E0270, // computation may converge in a function marked as diverging From d6be183af1d76a2716f1a3dc00b3ad3a2b746b37 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 19 Jul 2015 18:34:06 +0530 Subject: [PATCH 10/16] Improve E0260 --- src/librustc/diagnostics.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 02f4cea87ca5f..0d949c98023c6 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1002,6 +1002,14 @@ you prefer them unqualified, you can import the variants into scope: use Method::*; enum Method { GET, POST } ``` + +If you want others to be able to import variants from your module directly, use +`pub use`: + +``` +pub use Method::*; +enum Method { GET, POST } +``` "##, E0261: r##" From 4337e822fb043e3e3abd085ca6483f15f11d913a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 19 Jul 2015 19:13:25 +0530 Subject: [PATCH 11/16] Add long diagnostic for E0269 --- src/librustc/diagnostics.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 0d949c98023c6..969293800f1b1 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1121,6 +1121,41 @@ fn some_func() { ``` "##, +E0269: r##" +Functions must eventually return a value of their return type. For example, in +the following function + +``` +fn foo(x: u8) -> u8 { + if x > 0 { + x // alternatively, `return x` + } + // nothing here +} +``` + +if the condition is true, the value `x` is returned, but if the condition is +false, control exits the `if` block and reaches a place where nothing is being +returned. All possible control paths must eventually return a `u8`, which is not +happening here. + +An easy fix for this in a complicated function is to specify a default return +value, if possible: + +``` +fn foo(x: u8) -> u8 { + if x > 0 { + x // alternatively, `return x` + } + // lots of other if branches + 0 // return 0 if all else fails +} +``` + +It is advisable to find out what the unhandled cases are and check for them, +returning an appropriate value or panicking if necessary. +"##, + E0271: r##" This is because of a type mismatch between the associated type of some trait (e.g. `T::Bar`, where `T` implements `trait Quux { type Bar; }`) @@ -1681,7 +1716,6 @@ register_diagnostics! { // E0134, // E0135, E0264, // unknown external lang item - E0269, // not all control paths return a value E0270, // computation may converge in a function marked as diverging E0272, // rustc_on_unimplemented attribute refers to non-existent type parameter E0273, // rustc_on_unimplemented must have named format arguments From 8590501b31f4ab7d6cc1a818aa717afb5a3d4d96 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 19 Jul 2015 19:32:48 +0530 Subject: [PATCH 12/16] Add E0270 --- src/librustc/diagnostics.rs | 104 ++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 17 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 969293800f1b1..2aa31248503c1 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -573,8 +573,8 @@ implementation if the function is called with unparametrized substitutions (i.e., substitutions where none of the substituted types are themselves parametrized). -However, with trait objects we have to make a table containing _every object -that implements the trait_. Now, if it has type parameters, we need to add +However, with trait objects we have to make a table containing _every_ object +that implements the trait. Now, if it has type parameters, we need to add implementations for every type that implements the trait, and there could theoretically be an infinite number of types. @@ -609,9 +609,9 @@ fn call_foo(thing: Box) { ``` we don't just need to create a table of all implementations of all methods of -`Trait`, we need to create a table of all implementations of `foo()`, _for each -different type fed to `foo()`_. In this case this turns out to be (10 types -implementing `Trait`)*(3 types being fed to `foo()`) = 30 implementations! +`Trait`, we need to create such a table, for each different type fed to +`foo()`. In this case this turns out to be (10 types implementing `Trait`)*(3 +types being fed to `foo()`) = 30 implementations! With real world traits these numbers can grow drastically. @@ -684,10 +684,10 @@ If the trait `Foo` was deriving from something like `Super` or `Super` (where `Foo` itself is `Foo`), this is okay, because given a type `get_a()` will definitely return an object of that type. -However, if it derives from `Super`, the method `get_a()` would return an -object of unknown type when called on the function, _even though `Super` is -object safe_. `Self` type parameters let us make object safe traits no longer -safe, so they are forbidden when specifying supertraits. +However, if it derives from `Super`, even though `Super` is object safe, +the method `get_a()` would return an object of unknown type when called on the +function. `Self` type parameters let us make object safe traits no longer safe, +so they are forbidden when specifying supertraits. There's no easy fix for this, generally code will need to be refactored so that you no longer need to derive from `Super`. @@ -695,8 +695,8 @@ you no longer need to derive from `Super`. E0079: r##" Enum variants which contain no data can be given a custom integer -representation. This error indicates that the value provided is not an -integer literal and is therefore invalid. +representation. This error indicates that the value provided is not an integer +literal and is therefore invalid. For example, in the following code, @@ -708,8 +708,8 @@ enum Foo { we try to set the representation to a string. -There's no general fix for this; if you can work with an integer -then just set it to one: +There's no general fix for this; if you can work with an integer then just set +it to one: ``` enum Foo { @@ -717,9 +717,8 @@ enum Foo { } ``` -however if you actually wanted a mapping between variants -and non-integer objects, it may be preferable to use a method with -a match instead: +however if you actually wanted a mapping between variants and non-integer +objects, it may be preferable to use a method with a match instead: ``` enum Foo { Q } @@ -1156,6 +1155,73 @@ It is advisable to find out what the unhandled cases are and check for them, returning an appropriate value or panicking if necessary. "##, +E0270: r##" +Rust lets you define functions which are known to never return, i.e. are +"diverging", by marking its return type as `!`. + +For example, the following functions never return: + +``` +fn foo() -> ! { + loop {} +} + +fn bar() -> ! { + foo() // foo() is diverging, so this will diverge too +} + +fn baz() -> ! { + panic!(); // this macro internally expands to a call to a diverging function +} + +``` + +Such functions can be used in a place where a value is expected without +returning a value of that type, for instance: + +``` +let y = match x { + 1 => 1, + 2 => 4, + _ => foo() // diverging function called here +}; +println!("{}", y) +``` + +If the third arm of the match block is reached, since `foo()` doesn't ever +return control to the match block, it is fine to use it in a place where an +integer was expected. The `match` block will never finish executing, and any +point where `y` (like the print statement) is needed will not be reached. + +However, if we had a diverging function that actually does finish execution + +``` +fn foo() -> { + loop {break;} +} +``` + +then we would have an unknown value for `y` in the following code: + +``` +let y = match x { + 1 => 1, + 2 => 4, + _ => foo() +}; +println!("{}", y); +``` + +In the previous example, the print statement was never reached when the wildcard +match arm was hit, so we were okay with `foo()` not returning an integer that we +could set to `y`. But in this example, `foo()` actually does return control, so +the print statement will be executed with an uninitialized value. + +Obviously we cannot have functions which are allowed to be used in such +positions and yet can return control. So, if you are defining a function that +returns `!`, make sure that there is no way for it to actually finish executing. +"##, + E0271: r##" This is because of a type mismatch between the associated type of some trait (e.g. `T::Bar`, where `T` implements `trait Quux { type Bar; }`) @@ -1292,6 +1358,11 @@ for v in &vs { ``` "##, +E0272: r##" + +The `#[rustc_on_unimplemented]` attribute lets you specify +"##, + E0277: r##" You tried to use a type which doesn't implement some trait in a place which expected that trait. Erroneous code example: @@ -1716,7 +1787,6 @@ register_diagnostics! { // E0134, // E0135, E0264, // unknown external lang item - E0270, // computation may converge in a function marked as diverging E0272, // rustc_on_unimplemented attribute refers to non-existent type parameter E0273, // rustc_on_unimplemented must have named format arguments E0274, // rustc_on_unimplemented must have a value From dc556bef4313b59f082a1a0f7f2d30ef9cc88c0c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 22 Jul 2015 21:01:19 +0530 Subject: [PATCH 13/16] Add long diagnostics for E0272-274 (on_unimplemented) --- src/librustc/diagnostics.rs | 81 +++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 2aa31248503c1..cb29f01776578 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1359,8 +1359,84 @@ for v in &vs { "##, E0272: r##" +The `#[rustc_on_unimplemented]` attribute lets you specify a custom error +message for when a particular trait isn't implemented on a type placed in a +position that needs that trait. For example, when the following code is +compiled: -The `#[rustc_on_unimplemented]` attribute lets you specify +``` +fn foo>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { ... } + +foo(true); // `bool` does not implement `Index` +``` + +there will be an error about `bool` not implementing `Index`, followed by a +note saying "the type `bool` cannot be indexed by `u8`". + +As you can see, you can specify type parameters in curly braces for substitution +with the actual types (using the regular format string syntax) in a given +situation. Furthermore, `{Self}` will substitute to the type (in this case, +`bool`) that we tried to use. + +This error appears when the curly braces contain an identifier which doesn't +match with any of the type parameters or the string `Self`. This might happen if +you misspelled a type parameter, or if you intended to use literal curly braces. +If it is the latter, escape the curly braces with a second curly brace of the +same type; e.g. a literal `{` is `{{` +"##, + +E0273: r##" +The `#[rustc_on_unimplemented]` attribute lets you specify a custom error +message for when a particular trait isn't implemented on a type placed in a +position that needs that trait. For example, when the following code is +compiled: + +``` +fn foo>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { ... } + +foo(true); // `bool` does not implement `Index` +``` + +there will be an error about `bool` not implementing `Index`, followed by a +note saying "the type `bool` cannot be indexed by `u8`". + +As you can see, you can specify type parameters in curly braces for substitution +with the actual types (using the regular format string syntax) in a given +situation. Furthermore, `{Self}` will substitute to the type (in this case, +`bool`) that we tried to use. + +This error appears when the curly braces do not contain an identifier. Please +add one of the same name as a type parameter. If you intended to use literal +braces, use `{{` and `}}` to escape them. +"##, + +E0273: r##" +The `#[rustc_on_unimplemented]` attribute lets you specify a custom error +message for when a particular trait isn't implemented on a type placed in a +position that needs that trait. For example, when the following code is +compiled: + +``` +fn foo>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { ... } + +foo(true); // `bool` does not implement `Index` +``` + +there will be an error about `bool` not implementing `Index`, followed by a +note saying "the type `bool` cannot be indexed by `u8`". + +For this to work, some note must be specified. An empty attribute will not do +anything, please remove the attribute or add some helpful note for users of the +trait. "##, E0277: r##" @@ -1787,9 +1863,6 @@ register_diagnostics! { // E0134, // E0135, E0264, // unknown external lang item - E0272, // rustc_on_unimplemented attribute refers to non-existent type parameter - E0273, // rustc_on_unimplemented must have named format arguments - E0274, // rustc_on_unimplemented must have a value E0275, // overflow evaluating requirement E0276, // requirement appears on impl method but not on corresponding trait method E0278, // requirement is not satisfied From b5317761b8202affde7493d9b81a049b4727c29e Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 22 Jul 2015 21:13:22 +0530 Subject: [PATCH 14/16] Add long diagnostic explanation for E0275 --- src/librustc/diagnostics.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index cb29f01776578..5bd03ce598a3d 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1439,6 +1439,29 @@ anything, please remove the attribute or add some helpful note for users of the trait. "##, +E0275: r##" +This error occurs when there was a recursive trait requirement that overflowed +before it could be evaluated. Often this means that there is unbounded recursion +in resolving some type bounds. + +For example, in the following code + +``` +trait Foo {} + +struct Bar(T); + +impl Foo for T where Bar: Foo {} +``` + +to determine if a `T` is `Foo`, we need to check if `Bar` is `Foo`. However, +to do this check, we need to determine that `Bar>` is `Foo`. To determine +this, we check if `Bar>>` is `Foo`, and so on. This is clearly a +recursive requirement that can't be resolved directly. + +Consider changing your trait bounds so that they're less self-referential. +"##, + E0277: r##" You tried to use a type which doesn't implement some trait in a place which expected that trait. Erroneous code example: @@ -1863,7 +1886,6 @@ register_diagnostics! { // E0134, // E0135, E0264, // unknown external lang item - E0275, // overflow evaluating requirement E0276, // requirement appears on impl method but not on corresponding trait method E0278, // requirement is not satisfied E0279, // requirement is not satisfied From 522a9785e4866d7f6872f7d6c262ddcfc425a1f3 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 22 Jul 2015 21:19:24 +0530 Subject: [PATCH 15/16] Add long diagnostic for E0276 --- src/librustc/diagnostics.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 5bd03ce598a3d..3665512392787 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1462,6 +1462,28 @@ recursive requirement that can't be resolved directly. Consider changing your trait bounds so that they're less self-referential. "##, +E0276: r##" +This error occurs when a bound in an implementation of a trait does not match +the bounds specified in the original trait. For example: + +``` +trait Foo { + fn foo(x: T); +} + +impl Foo for bool { + fn foo(x: T) where T: Copy {} +} +``` + +Here, all types implementing `Foo` must have a method `foo(x: T)` which can +take any type `T`. However, in the `impl` for `bool`, we have added an extra +bound that `T` is `Copy`, which isn't compatible with the original trait. + +Consider removing the bound from the method or adding the bound to the original +method definition in the trait. +"##, + E0277: r##" You tried to use a type which doesn't implement some trait in a place which expected that trait. Erroneous code example: @@ -1886,7 +1908,6 @@ register_diagnostics! { // E0134, // E0135, E0264, // unknown external lang item - E0276, // requirement appears on impl method but not on corresponding trait method E0278, // requirement is not satisfied E0279, // requirement is not satisfied E0280, // requirement is not satisfied From c5889358f285aac3bc94aa856d15af682f420e69 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 23 Jul 2015 12:11:04 +0530 Subject: [PATCH 16/16] Address comments --- src/librustc/diagnostics.rs | 48 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 3665512392787..879a2d740cb0b 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -37,7 +37,7 @@ match foo { a wildcard arm above a more specific arm will make the latter arm irrelevant. Ensure the ordering of the match arm is correct and remove any superfluous -checks. +arms. "##, E0002: r##" @@ -81,8 +81,8 @@ match number { } ``` -To match against NaN values, you should -instead use the `is_nan()` method in a guard, like so: +To match against NaN values, you should instead use the `is_nan()` method in a +guard, like so: ``` match number { @@ -333,8 +333,8 @@ Statics are shared everywhere, and if they refer to mutable data one might violate memory safety since holding multiple mutable references to shared data is not allowed. -If you really want global mutable state, try using a global `UnsafeCell` or -`static mut`. +If you really want global mutable state, try using `static mut` or a global +`UnsafeCell`. "##, @@ -416,7 +416,7 @@ is bad because the function body may not mutate `x`. Remove any mutable bindings from the argument list to fix this error. In case you need to mutate the argument, try lazily initializing a global variable -instead of using a const fn, or refactoring the code to a functional style to +instead of using a `const fn`, or refactoring the code to a functional style to avoid mutation if possible. "##, @@ -444,11 +444,11 @@ requirements are satisfied by the trait in question. Trait objects are a form of dynamic dispatch and use a dynamically sized type for the inner type. So, for a given trait `Trait`, when `Trait` is treated as a -type, as in `Box`, the inner type is "unsized". In such cases the boxed -pointer is a "fat pointer" that contains an extra pointer to a table of methods +type, as in `Box`, the inner type is 'unsized'. In such cases the boxed +pointer is a 'fat pointer' that contains an extra pointer to a table of methods (among other things) for dynamic dispatch. This design mandates some restrictions on the types of traits that are allowed to be used in trait -objects, which are collectively termed as "object safety" rules. +objects, which are collectively termed as 'object safety' rules. Attempting to create a trait object for a non object-safe trait will trigger this error. @@ -513,7 +513,7 @@ fn call_foo(x: Box) { If only some methods aren't object-safe, you can add a `where Self: Sized` bound on them to mark them as explicitly unavailable to trait objects. The functionality will still be available to all other implementers, including -`Box` which is itself sized (assuming you `impl Trait for Box`) +`Box` which is itself sized (assuming you `impl Trait for Box`). ``` trait Trait { @@ -530,7 +530,7 @@ that trait that aren't behind trait objects. ### Method has generic type parameters As mentioned before, trait objects contain pointers to method tables. So, if we -have +have: ``` trait Trait { @@ -549,7 +549,7 @@ impl Trait for u8 { // ... ``` -at compile time each implementation of `Trait` will produce a table containing +At compile time each implementation of `Trait` will produce a table containing the various methods (and other items) related to the implementation. This works fine, but when the method gains generic parameters, we can have a @@ -578,7 +578,7 @@ that implements the trait. Now, if it has type parameters, we need to add implementations for every type that implements the trait, and there could theoretically be an infinite number of types. -For example, with +For example, with: ``` trait Trait { @@ -598,7 +598,7 @@ impl Trait for u8 { // 8 more implementations ``` -Now, if I have the following code: +Now, if we have the following code: ``` fn call_foo(thing: Box) { @@ -647,7 +647,6 @@ an implementation. Adding a `Self: Sized` bound to these methods will generally make this compile. - ``` trait Foo { fn foo() -> u8 where Self: Sized; @@ -752,7 +751,8 @@ https://doc.rust-lang.org/reference.html#ffi-attributes "##, E0109: r##" -You tried to give a type parameter to a type which doesn't need it; for example: +You tried to give a type parameter to a type which doesn't need it. Erroneous +code example: ``` type X = u32; // error: type parameters are not allowed on this type @@ -769,8 +769,8 @@ type X = u32; // this compiles "##, E0110: r##" -You tried to give a lifetime parameter to a type which doesn't need it; for -example: +You tried to give a lifetime parameter to a type which doesn't need it. +Erroneous code example: ``` type X = u32<'static>; // error: lifetime parameters are not allowed on @@ -882,6 +882,14 @@ fn foo(x: Vec) { Each impl will be checked for a size match in the transmute as usual, and since there are no unbound type parameters involved, this should compile unless there is a size mismatch in one of the impls. + +It is also possible to manually transmute: + +``` +let result: SomeType = mem::uninitialized(); +unsafe { copy_nonoverlapping(&v, &result) }; +result // `v` transmuted to type `SomeType` +``` "##, E0152: r##" @@ -1157,7 +1165,7 @@ returning an appropriate value or panicking if necessary. E0270: r##" Rust lets you define functions which are known to never return, i.e. are -"diverging", by marking its return type as `!`. +'diverging', by marking its return type as `!`. For example, the following functions never return: @@ -1416,7 +1424,7 @@ add one of the same name as a type parameter. If you intended to use literal braces, use `{{` and `}}` to escape them. "##, -E0273: r##" +E0274: r##" The `#[rustc_on_unimplemented]` attribute lets you specify a custom error message for when a particular trait isn't implemented on a type placed in a position that needs that trait. For example, when the following code is