about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-05-28 11:50:14 +0000
committerbors <bors@rust-lang.org>2019-05-28 11:50:14 +0000
commit7da118581c9dc839c8bf3fbb622bab9ce32bbf38 (patch)
tree6afa790e230d5ecc9d3ee1849652e00c74688137
parent837b72c805f98d95a44ad4fc2b43ba6a8acb108a (diff)
parent149c53fc442a690ea6a5c309f408cb2160039b9b (diff)
downloadrust-7da118581c9dc839c8bf3fbb622bab9ce32bbf38.tar.gz
rust-7da118581c9dc839c8bf3fbb622bab9ce32bbf38.zip
Auto merge of #61258 - Centril:rollup-l2mof9t, r=Centril
Rollup of 9 pull requests

Successful merges:

 - #61084 (Clarify docs for unreachable! macro)
 - #61220 (Added error message for E0284)
 - #61227 (Use .await syntax instead of await!)
 - #61230 (avoid creating Boxes of uninitalized values in RawVec)
 - #61237 (Updated the Iterator docs with information about overriding methods.)
 - #61241 (Check place iterative)
 - #61242 (Make dest_needs_borrow iterate instead of recurse)
 - #61247 (Make eval_place iterate instead of recurse)
 - #61248 (Use Place::local)

Failed merges:

r? @ghost
-rw-r--r--src/liballoc/boxed.rs16
-rw-r--r--src/liballoc/raw_vec.rs10
-rw-r--r--src/libcore/future/future.rs2
-rw-r--r--src/libcore/iter/mod.rs5
-rw-r--r--src/libcore/iter/traits/iterator.rs1
-rw-r--r--src/libcore/macros.rs7
-rw-r--r--src/librustc/error_codes.rs46
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs10
-rw-r--r--src/librustc_mir/transform/const_prop.rs79
-rw-r--r--src/librustc_mir/transform/inline.rs23
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs75
-rw-r--r--src/test/ui/associated-types/associated-types-overridden-binding.stderr1
-rw-r--r--src/test/ui/associated-types/associated-types-unconstrained.stderr1
-rw-r--r--src/test/ui/issues/issue-12028.stderr1
-rw-r--r--src/test/ui/question-mark-type-infer.stderr1
15 files changed, 168 insertions, 110 deletions
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs
index bf8f5b8b91a..76b660fba68 100644
--- a/src/liballoc/boxed.rs
+++ b/src/liballoc/boxed.rs
@@ -395,11 +395,10 @@ impl<T: Clone> Clone for Box<T> {
 #[stable(feature = "box_slice_clone", since = "1.3.0")]
 impl Clone for Box<str> {
     fn clone(&self) -> Self {
-        let len = self.len();
-        let buf = RawVec::with_capacity(len);
+        // this makes a copy of the data
+        let buf: Box<[u8]> = self.as_bytes().into();
         unsafe {
-            ptr::copy_nonoverlapping(self.as_ptr(), buf.ptr(), len);
-            from_boxed_utf8_unchecked(buf.into_box())
+            from_boxed_utf8_unchecked(buf)
         }
     }
 }
@@ -546,9 +545,12 @@ impl<T: Copy> From<&[T]> for Box<[T]> {
     /// println!("{:?}", boxed_slice);
     /// ```
     fn from(slice: &[T]) -> Box<[T]> {
-        let mut boxed = unsafe { RawVec::with_capacity(slice.len()).into_box() };
-        boxed.copy_from_slice(slice);
-        boxed
+        let len = slice.len();
+        let buf = RawVec::with_capacity(len);
+        unsafe {
+            ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
+            buf.into_box()
+        }
     }
 }
 
diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs
index d1fc5ac3b30..0454a564435 100644
--- a/src/liballoc/raw_vec.rs
+++ b/src/liballoc/raw_vec.rs
@@ -685,12 +685,14 @@ impl<T, A: Alloc> RawVec<T, A> {
 impl<T> RawVec<T, Global> {
     /// Converts the entire buffer into `Box<[T]>`.
     ///
-    /// While it is not *strictly* Undefined Behavior to call
-    /// this procedure while some of the RawVec is uninitialized,
-    /// it certainly makes it trivial to trigger it.
-    ///
     /// Note that this will correctly reconstitute any `cap` changes
     /// that may have been performed. (see description of type for details)
+    ///
+    /// # Undefined Behavior
+    ///
+    /// All elements of `RawVec<T, Global>` must be initialized. Notice that
+    /// the rules around uninitialized boxed values are not finalized yet,
+    /// but until they are, it is advisable to avoid them.
     pub unsafe fn into_box(self) -> Box<[T]> {
         // NOTE: not calling `cap()` here, actually using the real `cap` field!
         let slice = slice::from_raw_parts_mut(self.ptr(), self.cap);
diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs
index 3f76ac20192..0492fd709b8 100644
--- a/src/libcore/future/future.rs
+++ b/src/libcore/future/future.rs
@@ -21,7 +21,7 @@ use crate::task::{Context, Poll};
 /// task.
 ///
 /// When using a future, you generally won't call `poll` directly, but instead
-/// `await!` the value.
+/// `.await` the value.
 #[doc(spotlight)]
 #[must_use = "futures do nothing unless you `.await` or poll them"]
 #[stable(feature = "futures_api", since = "1.36.0")]
diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs
index 1601357d3b0..6eccb9d1ea8 100644
--- a/src/libcore/iter/mod.rs
+++ b/src/libcore/iter/mod.rs
@@ -140,6 +140,11 @@
 //! call `next()` on your iterator, until it reaches `None`. Let's go over that
 //! next.
 //!
+//! Also note that `Iterator` provides a default implementation of methods such as `nth` and `fold`
+//! which call `next` internally. However, it is also possible to write a custom implementation of
+//! methods like `nth` and `fold` if an iterator can compute them more efficiently without calling
+//! `next`.
+//!
 //! # for Loops and IntoIterator
 //!
 //! Rust's `for` loop syntax is actually sugar for iterators. Here's a basic
diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs
index 38c7c9bc4d0..d0fdd79473e 100644
--- a/src/libcore/iter/traits/iterator.rs
+++ b/src/libcore/iter/traits/iterator.rs
@@ -964,6 +964,7 @@ pub trait Iterator {
     /// Creates an iterator that skips the first `n` elements.
     ///
     /// After they have been consumed, the rest of the elements are yielded.
+    /// Rather than overriding this method directly, instead override the `nth` method.
     ///
     /// # Examples
     ///
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index d2ee9b11b36..9dfa09cf8a5 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -445,9 +445,10 @@ macro_rules! writeln {
 /// * Iterators that dynamically terminate.
 ///
 /// If the determination that the code is unreachable proves incorrect, the
-/// program immediately terminates with a [`panic!`]. The function [`unreachable_unchecked`],
-/// which belongs to the [`std::hint`] module, informs the compiler to
-/// optimize the code out of the release version entirely.
+/// program immediately terminates with a [`panic!`].
+///
+/// The unsafe counterpart of this macro is the [`unreachable_unchecked`] function, which
+/// will cause undefined behavior if the code is reached.
 ///
 /// [`panic!`]:  ../std/macro.panic.html
 /// [`unreachable_unchecked`]: ../std/hint/fn.unreachable_unchecked.html
diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs
index a1bfd417566..6243e911bd5 100644
--- a/src/librustc/error_codes.rs
+++ b/src/librustc/error_codes.rs
@@ -1207,6 +1207,51 @@ fn main() {
 ```
 "##,
 
+E0284: r##"
+This error occurs when the compiler is unable to unambiguously infer the
+return type of a function or method which is generic on return type, such
+as the `collect` method for `Iterator`s.
+
+For example:
+
+```compile_fail,E0284
+fn foo() -> Result<bool, ()> {
+    let results = [Ok(true), Ok(false), Err(())].iter().cloned();
+    let v: Vec<bool> = results.collect()?;
+    // Do things with v...
+    Ok(true)
+}
+```
+
+Here we have an iterator `results` over `Result<bool, ()>`.
+Hence, `results.collect()` can return any type implementing
+`FromIterator<Result<bool, ()>>`. On the other hand, the
+`?` operator can accept any type implementing `Try`.
+
+The author of this code probably wants `collect()` to return a
+`Result<Vec<bool>, ()>`, but the compiler can't be sure
+that there isn't another type `T` implementing both `Try` and
+`FromIterator<Result<bool, ()>>` in scope such that
+`T::Ok == Vec<bool>`. Hence, this code is ambiguous and an error
+is returned.
+
+To resolve this error, use a concrete type for the intermediate expression:
+
+```
+fn foo() -> Result<bool, ()> {
+    let results = [Ok(true), Ok(false), Err(())].iter().cloned();
+    let v = {
+        let temp: Result<Vec<bool>, ()> = results.collect();
+        temp?
+    };
+    // Do things with v...
+    Ok(true)
+}
+```
+
+Note that the type of `v` can now be inferred from the type of `temp`.
+"##,
+
 E0308: r##"
 This error occurs when the compiler was unable to infer the concrete type of a
 variable. It can occur for several cases, the most common of which is a
@@ -2158,7 +2203,6 @@ register_diagnostics! {
     E0278, // requirement is not satisfied
     E0279, // requirement is not satisfied
     E0280, // requirement is not satisfied
-    E0284, // cannot resolve type
 //  E0285, // overflow evaluation builtin bounds
 //  E0296, // replaced with a generic attribute input check
 //  E0300, // unexpanded macro
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index a0b504a99de..07a9f294fb6 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -528,13 +528,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }) => {
                 // Not projected from the implicit `self` in a closure.
                 debug_assert!(
-                    match *base {
-                        Place::Base(PlaceBase::Local(local)) => local == Local::new(1),
-                        Place::Projection(box Projection {
-                            ref base,
-                            elem: ProjectionElem::Deref,
-                        }) => *base == Place::Base(PlaceBase::Local(Local::new(1))),
-                        _ => false,
+                    match base.local() {
+                        Some(local) => local == Local::new(1),
+                        None => false,
                     },
                     "Unexpected capture place"
                 );
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index ce854518db3..11022be097c 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -296,47 +296,50 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
 
     fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
         trace!("eval_place(place={:?})", place);
-        match *place {
-            Place::Base(PlaceBase::Local(loc)) => self.places[loc].clone(),
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _) => {
-                    trace!("field proj on {:?}", proj.base);
-                    let base = self.eval_place(&proj.base, source_info)?;
+        place.iterate(|place_base, place_projection| {
+            let mut eval = match place_base {
+                PlaceBase::Local(loc) => self.places[*loc].clone()?,
+                PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted), ..}) => {
+                    let generics = self.tcx.generics_of(self.source.def_id());
+                    if generics.requires_monomorphization(self.tcx) {
+                        // FIXME: can't handle code with generics
+                        return None;
+                    }
+                    let substs = InternalSubsts::identity_for_item(self.tcx, self.source.def_id());
+                    let instance = Instance::new(self.source.def_id(), substs);
+                    let cid = GlobalId {
+                        instance,
+                        promoted: Some(*promoted),
+                    };
+                    // cannot use `const_eval` here, because that would require having the MIR
+                    // for the current function available, but we're producing said MIR right now
                     let res = self.use_ecx(source_info, |this| {
-                        this.ecx.operand_field(base, field.index() as u64)
+                        let mir = &this.promoted[*promoted];
+                        eval_promoted(this.tcx, cid, mir, this.param_env)
                     })?;
-                    Some(res)
-                },
-                // We could get more projections by using e.g., `operand_projection`,
-                // but we do not even have the stack frame set up properly so
-                // an `Index` projection would throw us off-track.
-                _ => None,
-            },
-            Place::Base(
-                PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted), ..})
-            ) => {
-                let generics = self.tcx.generics_of(self.source.def_id());
-                if generics.requires_monomorphization(self.tcx) {
-                    // FIXME: can't handle code with generics
-                    return None;
+                    trace!("evaluated promoted {:?} to {:?}", promoted, res);
+                    res.into()
                 }
-                let substs = InternalSubsts::identity_for_item(self.tcx, self.source.def_id());
-                let instance = Instance::new(self.source.def_id(), substs);
-                let cid = GlobalId {
-                    instance,
-                    promoted: Some(promoted),
-                };
-                // cannot use `const_eval` here, because that would require having the MIR
-                // for the current function available, but we're producing said MIR right now
-                let res = self.use_ecx(source_info, |this| {
-                    let mir = &this.promoted[promoted];
-                    eval_promoted(this.tcx, cid, mir, this.param_env)
-                })?;
-                trace!("evaluated promoted {:?} to {:?}", promoted, res);
-                Some(res.into())
-            },
-            _ => None,
-        }
+                _ => return None,
+            };
+
+            for proj in place_projection {
+                match proj.elem {
+                    ProjectionElem::Field(field, _) => {
+                        trace!("field proj on {:?}", proj.base);
+                        eval = self.use_ecx(source_info, |this| {
+                            this.ecx.operand_field(eval, field.index() as u64)
+                        })?;
+                    },
+                    // We could get more projections by using e.g., `operand_projection`,
+                    // but we do not even have the stack frame set up properly so
+                    // an `Index` projection would throw us off-track.
+                    _ => return None,
+                }
+            }
+
+            Some(eval)
+        })
     }
 
     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 24ec3219a2e..782af3024ad 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -440,19 +440,22 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                 // writes to `i`. To prevent this we need to create a temporary
                 // borrow of the place and pass the destination as `*temp` instead.
                 fn dest_needs_borrow(place: &Place<'_>) -> bool {
-                    match *place {
-                        Place::Projection(ref p) => {
-                            match p.elem {
+                    place.iterate(|place_base, place_projection| {
+                        for proj in place_projection {
+                            match proj.elem {
                                 ProjectionElem::Deref |
-                                ProjectionElem::Index(_) => true,
-                                _ => dest_needs_borrow(&p.base)
+                                ProjectionElem::Index(_) => return true,
+                                _ => {}
                             }
                         }
-                        // Static variables need a borrow because the callee
-                        // might modify the same static.
-                        Place::Base(PlaceBase::Static(_)) => true,
-                        _ => false
-                    }
+
+                        match place_base {
+                            // Static variables need a borrow because the callee
+                            // might modify the same static.
+                            PlaceBase::Static(_) => true,
+                            _ => false
+                        }
+                    })
                 }
 
                 let dest = if dest_needs_borrow(&destination.0) {
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index e1d41ba4fc5..a1e2d0683d3 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -136,10 +136,10 @@ fn check_rvalue(
 ) -> McfResult {
     match rvalue {
         Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
-            check_operand(tcx, mir, operand, span)
+            check_operand(operand, span)
         }
         Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
-            check_place(tcx, mir, place, span)
+            check_place(place, span)
         }
         Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
             use rustc::ty::cast::CastTy;
@@ -153,11 +153,11 @@ fn check_rvalue(
                 (CastTy::RPtr(_), CastTy::Float) => bug!(),
                 (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                 (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
-                _ => check_operand(tcx, mir, operand, span),
+                _ => check_operand(operand, span),
             }
         }
         Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => {
-            check_operand(tcx, mir, operand, span)
+            check_operand(operand, span)
         }
         Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
         Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
@@ -171,8 +171,8 @@ fn check_rvalue(
         )),
         // binops are fine on integers
         Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
-            check_operand(tcx, mir, lhs, span)?;
-            check_operand(tcx, mir, rhs, span)?;
+            check_operand(lhs, span)?;
+            check_operand(rhs, span)?;
             let ty = lhs.ty(mir, tcx);
             if ty.is_integral() || ty.is_bool() || ty.is_char() {
                 Ok(())
@@ -191,7 +191,7 @@ fn check_rvalue(
         Rvalue::UnaryOp(_, operand) => {
             let ty = operand.ty(mir, tcx);
             if ty.is_integral() || ty.is_bool() {
-                check_operand(tcx, mir, operand, span)
+                check_operand(operand, span)
             } else {
                 Err((
                     span,
@@ -201,7 +201,7 @@ fn check_rvalue(
         }
         Rvalue::Aggregate(_, operands) => {
             for operand in operands {
-                check_operand(tcx, mir, operand, span)?;
+                check_operand(operand, span)?;
             }
             Ok(())
         }
@@ -216,11 +216,11 @@ fn check_statement(
     let span = statement.source_info.span;
     match &statement.kind {
         StatementKind::Assign(place, rval) => {
-            check_place(tcx, mir, place, span)?;
+            check_place(place, span)?;
             check_rvalue(tcx, mir, rval, span)
         }
 
-        StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span),
+        StatementKind::FakeRead(_, place) => check_place(place, span),
 
         // just an assignment
         StatementKind::SetDiscriminant { .. } => Ok(()),
@@ -239,43 +239,40 @@ fn check_statement(
 }
 
 fn check_operand(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    mir: &'a Mir<'tcx>,
     operand: &Operand<'tcx>,
     span: Span,
 ) -> McfResult {
     match operand {
         Operand::Move(place) | Operand::Copy(place) => {
-            check_place(tcx, mir, place, span)
+            check_place(place, span)
         }
         Operand::Constant(_) => Ok(()),
     }
 }
 
-fn check_place(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    mir: &'a Mir<'tcx>,
-    place: &Place<'tcx>,
-    span: Span,
-) -> McfResult {
-    match place {
-        Place::Base(PlaceBase::Local(_)) => Ok(()),
-        // promoteds are always fine, they are essentially constants
-        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) => Ok(()),
-        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Static(_), .. })) =>
-            Err((span, "cannot access `static` items in const fn".into())),
-        Place::Projection(proj) => {
+fn check_place(place: &Place<'tcx>, span: Span) -> McfResult {
+    place.iterate(|place_base, place_projection| {
+        for proj in place_projection {
             match proj.elem {
-                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
-                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
-                    check_place(tcx, mir, &proj.base, span)
-                }
-                | ProjectionElem::Downcast(..) => {
-                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
+                ProjectionElem::Downcast(..) => {
+                    return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
                 }
+                ProjectionElem::ConstantIndex { .. }
+                | ProjectionElem::Subslice { .. }
+                | ProjectionElem::Deref
+                | ProjectionElem::Field(..)
+                | ProjectionElem::Index(_) => {}
             }
         }
-    }
+
+        match place_base {
+            PlaceBase::Static(box Static { kind: StaticKind::Static(_), .. }) => {
+                Err((span, "cannot access `static` items in const fn".into()))
+            }
+            PlaceBase::Local(_)
+            | PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. }) => Ok(()),
+        }
+    })
 }
 
 fn check_terminator(
@@ -290,11 +287,11 @@ fn check_terminator(
         | TerminatorKind::Resume => Ok(()),
 
         TerminatorKind::Drop { location, .. } => {
-            check_place(tcx, mir, location, span)
+            check_place(location, span)
         }
         TerminatorKind::DropAndReplace { location, value, .. } => {
-            check_place(tcx, mir, location, span)?;
-            check_operand(tcx, mir, value, span)
+            check_place(location, span)?;
+            check_operand(value, span)
         },
 
         TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
@@ -346,10 +343,10 @@ fn check_terminator(
                     )),
                 }
 
-                check_operand(tcx, mir, func, span)?;
+                check_operand(func, span)?;
 
                 for arg in args {
-                    check_operand(tcx, mir, arg, span)?;
+                    check_operand(arg, span)?;
                 }
                 Ok(())
             } else {
@@ -363,7 +360,7 @@ fn check_terminator(
             msg: _,
             target: _,
             cleanup: _,
-        } => check_operand(tcx, mir, cond, span),
+        } => check_operand(cond, span),
 
         TerminatorKind::FalseUnwind { .. } => {
             Err((span, "loops are not allowed in const fn".into()))
diff --git a/src/test/ui/associated-types/associated-types-overridden-binding.stderr b/src/test/ui/associated-types/associated-types-overridden-binding.stderr
index fced38caaba..a26ee23894f 100644
--- a/src/test/ui/associated-types/associated-types-overridden-binding.stderr
+++ b/src/test/ui/associated-types/associated-types-overridden-binding.stderr
@@ -12,3 +12,4 @@ LL | trait Foo: Iterator<Item = i32> {}
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0284`.
diff --git a/src/test/ui/associated-types/associated-types-unconstrained.stderr b/src/test/ui/associated-types/associated-types-unconstrained.stderr
index 26e5a6a503c..da14a69ae30 100644
--- a/src/test/ui/associated-types/associated-types-unconstrained.stderr
+++ b/src/test/ui/associated-types/associated-types-unconstrained.stderr
@@ -6,3 +6,4 @@ LL |     let x: isize = Foo::bar();
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0284`.
diff --git a/src/test/ui/issues/issue-12028.stderr b/src/test/ui/issues/issue-12028.stderr
index b9e2e80492b..64694c7a8d0 100644
--- a/src/test/ui/issues/issue-12028.stderr
+++ b/src/test/ui/issues/issue-12028.stderr
@@ -6,3 +6,4 @@ LL |         self.input_stream(&mut stream);
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0284`.
diff --git a/src/test/ui/question-mark-type-infer.stderr b/src/test/ui/question-mark-type-infer.stderr
index 2a1bdf57a88..f62a540572c 100644
--- a/src/test/ui/question-mark-type-infer.stderr
+++ b/src/test/ui/question-mark-type-infer.stderr
@@ -6,3 +6,4 @@ LL |     l.iter().map(f).collect()?
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0284`.