diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 31e054ec1cb93..8b4b9aaeac848 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -175,6 +175,10 @@ fn configure_main(this: &mut EntryContext) { err.emit(); this.session.abort_if_errors(); } else { + if this.session.teach(&err.get_code().unwrap()) { + err.note("If you don't know the basics of Rust, you can go look to the Rust Book \ + to get started: https://doc.rust-lang.org/book/"); + } err.emit(); } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 297e0e491f694..ed1e1ebfc9f0a 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -170,8 +170,20 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn not_const(&mut self) { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { - span_err!(self.tcx.sess, self.span, E0019, - "{} contains unimplemented expression type", self.mode); + let mut err = struct_span_err!( + self.tcx.sess, + self.span, + E0019, + "{} contains unimplemented expression type", + self.mode + ); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("A function call isn't allowed in the const's initialization expression \ + because the expression's value must be known at compile-time."); + err.note("Remember: you can't use a function call inside a const's initialization \ + expression! However, you can use it anywhere else."); + } + err.emit(); } } @@ -179,9 +191,19 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn statement_like(&mut self) { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { - span_err!(self.tcx.sess, self.span, E0016, - "blocks in {}s are limited to items and tail expressions", - self.mode); + let mut err = struct_span_err!( + self.tcx.sess, + self.span, + E0016, + "blocks in {}s are limited to items and tail expressions", + self.mode + ); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("Blocks in constants may only contain items (such as constant, function \ + definition, etc...) and a tail expression."); + err.help("To avoid it, you have to replace the non-item object."); + } + err.emit(); } } @@ -474,9 +496,19 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if self.mode == Mode::Const || self.mode == Mode::ConstFn { - span_err!(self.tcx.sess, self.span, E0013, - "{}s cannot refer to statics, use \ - a constant instead", self.mode); + let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, + "{}s cannot refer to statics, use \ + a constant instead", self.mode); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "Static and const variables can refer to other const variables. But a \ + const variable cannot refer to a static variable." + ); + err.help( + "To fix this, the value can be extracted as a const and then used." + ); + } + err.emit() } } Place::Projection(ref proj) => { @@ -497,13 +529,25 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if let ty::TyRawPtr(_) = base_ty.sty { this.add(Qualif::NOT_CONST); if this.mode != Mode::Fn { - struct_span_err!(this.tcx.sess, - this.span, E0396, + let mut err = struct_span_err!( + this.tcx.sess, + this.span, + E0396, "raw pointers cannot be dereferenced in {}s", - this.mode) - .span_label(this.span, - "dereference of raw pointer in constant") - .emit(); + this.mode + ); + err.span_label(this.span, + "dereference of raw pointer in constant"); + if this.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "The value behind a raw pointer can't be determined \ + at compile-time (or even link-time), which means it \ + can't be used in a constant expression." + ); + err.help("A possible fix is to dereference your pointer \ + at some point in run-time."); + } + err.emit(); } } } @@ -622,12 +666,22 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if !allow { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { - struct_span_err!(self.tcx.sess, self.span, E0017, - "references in {}s may only refer \ - to immutable values", self.mode) - .span_label(self.span, format!("{}s require immutable values", - self.mode)) - .emit(); + let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, + "references in {}s may only refer \ + to immutable values", self.mode); + err.span_label(self.span, format!("{}s require immutable values", + self.mode)); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("References in statics and constants may only refer to \ + immutable values.\n\n\ + 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.\n\n\ + If you really want global mutable state, try using \ + static mut or a global UnsafeCell."); + } + err.emit(); } } } else { @@ -668,9 +722,42 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { (CastTy::FnPtr, CastTy::Int(_)) => { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { - span_err!(self.tcx.sess, self.span, E0018, - "raw pointers cannot be cast to integers in {}s", - self.mode); + let mut err = struct_span_err!( + self.tcx.sess, + self.span, + E0018, + "raw pointers cannot be cast to integers in {}s", + self.mode + ); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("\ +The value of static and constant integers must be known at compile time. You can't cast a pointer \ +to an integer because the address of a pointer can vary. + +For example, if you write: + +``` +static MY_STATIC: u32 = 42; +static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize; +static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR; +``` + +Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \ +when the program is linked, as well as change between different executions due to ASLR, and many \ +linkers would not be able to calculate the value of `WHAT`. + +On the other hand, static and constant pointers can point either to a known numeric address or to \ +the address of a symbol. + +``` +static MY_STATIC: u32 = 42; +static MY_STATIC_ADDR: &'static u32 = &MY_STATIC; +const CONST_ADDR: *const u8 = 0x5f3759df as *const u8; +``` + +This does not pose a problem by itself because they can't be accessed directly."); + } + err.emit(); } } _ => {} @@ -701,10 +788,18 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::NullaryOp(NullOp::Box, _) => { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { - struct_span_err!(self.tcx.sess, self.span, E0010, - "allocations are not allowed in {}s", self.mode) - .span_label(self.span, format!("allocation not allowed in {}s", self.mode)) - .emit(); + let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, + "allocations are not allowed in {}s", self.mode); + err.span_label(self.span, format!("allocation not allowed in {}s", self.mode)); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "The value of statics and constants must be known at compile time, \ + and they live for the entire lifetime of a program. Creating a boxed \ + value allocates memory on the heap at runtime, and therefore cannot \ + be done at compile time." + ); + } + err.emit(); } } @@ -930,9 +1025,22 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Avoid a generic error for other uses of arguments. if self.qualif.intersects(Qualif::FN_ARGUMENT) { let decl = &self.mir.local_decls[index]; - span_err!(self.tcx.sess, decl.source_info.span, E0022, - "arguments of constant functions can only \ - be immutable by-value bindings"); + let mut err = struct_span_err!( + self.tcx.sess, + decl.source_info.span, + E0022, + "arguments of constant functions can only be immutable by-value bindings" + ); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("Constant functions are not allowed to mutate anything. Thus, \ + binding to an argument with a mutable pattern is not allowed."); + err.note("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."); + } + err.emit(); return; } } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 6df860492f05a..59864182a7e40 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -237,10 +237,20 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { Ok(Ordering::Less) | Ok(Ordering::Equal) => {} Ok(Ordering::Greater) => { - struct_span_err!(self.tcx.sess, start.span, E0030, - "lower range bound must be less than or equal to upper") - .span_label(start.span, "lower bound larger than upper bound") - .emit(); + let mut err = struct_span_err!( + self.tcx.sess, + start.span, + E0030, + "lower range bound must be less than or equal to upper" + ); + err.span_label(start.span, "lower bound larger than upper bound"); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("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 requiring the start of \ + the range to be less than or equal to the end of the range."); + } + err.emit(); } Err(ErrorReported) => {} } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 1a285cd869aec..bf253a88d27c2 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -214,12 +214,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { end.span }; - struct_span_err!(tcx.sess, span, E0029, - "only char and numeric types are allowed in range patterns") - .span_label(span, "ranges require char or numeric types") - .note(&format!("start type: {}", self.ty_to_string(lhs_ty))) - .note(&format!("end type: {}", self.ty_to_string(rhs_ty))) - .emit(); + let mut err = struct_span_err!( + tcx.sess, + span, + E0029, + "only char and numeric types are allowed in range patterns" + ); + err.span_label(span, "ranges require char or numeric types"); + err.note(&format!("start type: {}", self.ty_to_string(lhs_ty))); + err.note(&format!("end type: {}", self.ty_to_string(rhs_ty))); + if tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "In a match expression, only numbers and characters can be matched \ + against a range. This is because the compiler checks that the range \ + is non-empty at compile-time, and is unable to evaluate arbitrary \ + comparison functions. If you want to capture values of an orderable \ + type between two end-points, you can use a guard." + ); + } + err.emit(); return; } @@ -505,10 +518,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // This is "x = SomeTrait" being reduced from // "let &x = &SomeTrait" or "let box x = Box", an error. let type_str = self.ty_to_string(expected); - struct_span_err!(self.tcx.sess, span, E0033, - "type `{}` cannot be dereferenced", type_str) - .span_label(span, format!("type `{}` cannot be dereferenced", type_str)) - .emit(); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0033, + "type `{}` cannot be dereferenced", + type_str + ); + err.span_label(span, format!("type `{}` cannot be dereferenced", type_str)); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note("\ +This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ +pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ +this type has no compile-time size. Therefore, all accesses to trait types must be through \ +pointers. If you encounter this error you should try to avoid dereferencing the pointer. + +You can read more about trait objects in the Trait Objects section of the Reference: \ +https://doc.rust-lang.org/reference/types.html#trait-objects"); + } + err.emit(); return false } } @@ -881,17 +909,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.field_ty(span, f, substs) }) .unwrap_or_else(|| { - struct_span_err!(tcx.sess, span, E0026, - "{} `{}` does not have a field named `{}`", - kind_name, - tcx.item_path_str(variant.did), - field.name) - .span_label(span, - format!("{} `{}` does not have field `{}`", - kind_name, - tcx.item_path_str(variant.did), - field.name)) - .emit(); + let mut err = struct_span_err!( + tcx.sess, + span, + E0026, + "{} `{}` does not have a field named `{}`", + kind_name, + tcx.item_path_str(variant.did), + field.name + ); + err.span_label(span, + format!("{} `{}` does not have field `{}`", + kind_name, + tcx.item_path_str(variant.did), + field.name)); + if tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "This error indicates that a struct pattern attempted to \ + extract a non-existent field from a struct. Struct fields \ + are identified by the name used before the colon : so struct \ + patterns should resemble the declaration of the struct type \ + being matched.\n\n\ + If you are using shorthand field patterns but want to refer \ + to the struct field by a different name, you should rename \ + it explicitly." + ); + } + err.emit(); tcx.types.err }) @@ -927,6 +971,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if variant.ctor_kind == CtorKind::Fn { diag.note("trying to match a tuple variant with a struct variant pattern"); } + if tcx.sess.teach(&diag.get_code().unwrap()) { + diag.note( + "This error indicates that a pattern for a struct fails to specify a \ + sub-pattern for every one of the struct's fields. Ensure that each field \ + from the struct's definition is mentioned in the pattern, or use `..` to \ + ignore unwanted fields." + ); + } diag.emit(); } }