about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-09-06 07:37:41 +0000
committerbors <bors@rust-lang.org>2019-09-06 07:37:41 +0000
commit1fb3c4ec7ca37d33bd1e68cce669d171c2752615 (patch)
tree48ea5150538790309c68b9f515c4e958724a82a5
parent6b5f9b2e973e438fc1726a2d164d046acd80b170 (diff)
parent61fcd057d2524175a21e2eb1d49c1d4aec29a0be (diff)
Auto merge of #64209 - Centril:rollup-x9kvjb7, r=Centril
Rollup of 10 pull requests

Successful merges:

 - #63676 (Use wasi crate for Core API)
 - #64094 (Improve searching in rustdoc and add tests)
 - #64111 (or-patterns: Uniformly use `PatKind::Or` in AST & Fix/Cleanup resolve)
 - #64156 (Assume non-git LLVM is fresh if the stamp file exists)
 - #64161 (Point at variant on pattern field count mismatch)
 - #64174 (Add missing code examples on Iterator trait)
 - #64175 (Fix invalid span generation when it should be div)
 - #64186 (std: Improve downstream codegen in `Command::env`)
 - #64190 (fill metadata in rustc_lexer's Cargo.toml)
 - #64198 (Add Fuchsia to actually_monotonic)

Failed merges:

r? @ghost
-rw-r--r--Cargo.lock12
-rw-r--r--src/bootstrap/native.rs27
-rw-r--r--src/libcore/iter/traits/iterator.rs80
-rw-r--r--src/librustc/hir/lowering.rs41
-rw-r--r--src/librustc/hir/lowering/expr.rs77
-rw-r--r--src/librustc_lexer/Cargo.toml6
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_lint/unused.rs10
-rw-r--r--src/librustc_resolve/late.rs812
-rw-r--r--src/librustc_resolve/lib.rs1
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs97
-rw-r--r--src/librustc_save_analysis/lib.rs1
-rw-r--r--src/librustc_typeck/check/pat.rs36
-rw-r--r--src/librustdoc/html/render.rs8
-rw-r--r--src/librustdoc/html/static/main.js16
-rw-r--r--src/libstd/Cargo.toml3
-rw-r--r--src/libstd/sys/cloudabi/shims/process.rs8
-rw-r--r--src/libstd/sys/sgx/process.rs8
-rw-r--r--src/libstd/sys/unix/process/mod.rs1
-rw-r--r--src/libstd/sys/unix/process/process_common.rs8
-rw-r--r--src/libstd/sys/unix/time.rs1
-rw-r--r--src/libstd/sys/vxworks/process/process_common.rs10
-rw-r--r--src/libstd/sys/wasi/args.rs41
-rw-r--r--src/libstd/sys/wasi/ext/fs.rs10
-rw-r--r--src/libstd/sys/wasi/ext/io.rs8
-rw-r--r--src/libstd/sys/wasi/fd.rs280
-rw-r--r--src/libstd/sys/wasi/fs.rs186
-rw-r--r--src/libstd/sys/wasi/io.rs11
-rw-r--r--src/libstd/sys/wasi/mod.rs93
-rw-r--r--src/libstd/sys/wasi/os.rs33
-rw-r--r--src/libstd/sys/wasi/process.rs8
-rw-r--r--src/libstd/sys/wasi/stdio.rs11
-rw-r--r--src/libstd/sys/wasi/thread.rs54
-rw-r--r--src/libstd/sys/wasi/time.rs30
-rw-r--r--src/libstd/sys/wasm/process.rs8
-rw-r--r--src/libstd/sys/windows/process.rs24
-rw-r--r--src/libstd/sys_common/process.rs51
-rw-r--r--src/libsyntax/ast.rs29
-rw-r--r--src/libsyntax/ext/build.rs10
-rw-r--r--src/libsyntax/mut_visit.rs13
-rw-r--r--src/libsyntax/parse/parser/expr.rs8
-rw-r--r--src/libsyntax/parse/parser/pat.rs10
-rw-r--r--src/libsyntax/print/pprust.rs21
-rw-r--r--src/libsyntax/visit.rs17
-rw-r--r--src/libsyntax_ext/deriving/cmp/ord.rs6
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_ord.rs4
-rw-r--r--src/libsyntax_ext/deriving/decodable.rs4
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs6
-rw-r--r--src/libsyntax_ext/format.rs2
-rw-r--r--src/test/rustdoc-js-std/vec-new.js1
-rw-r--r--src/test/rustdoc-js/exact-match.js9
-rw-r--r--src/test/rustdoc-js/exact-match.rs68
-rw-r--r--src/test/rustdoc-js/module-substring.js9
-rw-r--r--src/test/rustdoc-js/module-substring.rs68
-rw-r--r--src/test/rustdoc-js/search-short-types.js2
-rw-r--r--src/test/rustdoc-js/search-short-types.rs6
-rw-r--r--src/test/ui-fulldeps/pprust-expr-roundtrip.rs6
-rw-r--r--src/test/ui/error-codes/E0023.stderr9
-rw-r--r--src/test/ui/lint/issue-54538-unused-parens-lint.rs2
-rw-r--r--src/test/ui/lint/issue-54538-unused-parens-lint.stderr51
-rw-r--r--src/test/ui/match/match-pattern-field-mismatch.stderr3
-rw-r--r--src/test/ui/or-patterns/already-bound-name.rs46
-rw-r--r--src/test/ui/or-patterns/already-bound-name.stderr105
-rw-r--r--src/test/ui/or-patterns/consistent-bindings.rs46
-rw-r--r--src/test/ui/or-patterns/consistent-bindings.stderr20
-rw-r--r--src/test/ui/or-patterns/inconsistent-modes.rs28
-rw-r--r--src/test/ui/or-patterns/inconsistent-modes.stderr80
-rw-r--r--src/test/ui/or-patterns/missing-bindings.rs84
-rw-r--r--src/test/ui/or-patterns/missing-bindings.stderr250
-rw-r--r--src/test/ui/pattern/pat-tuple-overfield.stderr6
-rw-r--r--src/test/ui/pattern/pattern-error-continue.stderr3
-rw-r--r--src/test/ui/shadowed/shadowing-in-the-same-pattern.stderr7
-rw-r--r--src/tools/tidy/src/deps.rs2
73 files changed, 1991 insertions, 1163 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 243a326646c..313fef1c086 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3870,6 +3870,7 @@ dependencies = [
  "rustc_msan",
  "rustc_tsan",
  "unwind",
+ "wasi",
 ]
 
 [[package]]
@@ -4687,6 +4688,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "wasi"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
+dependencies = [
+ "compiler_builtins",
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
 name = "winapi"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index f02def3e1b0..7bf9ea2688f 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -81,26 +81,29 @@ impl Step for Llvm {
             (info, "src/llvm-project/llvm", builder.llvm_out(target), dir.join("bin"))
         };
 
-        if !llvm_info.is_git() {
-            println!(
-                "git could not determine the LLVM submodule commit hash. \
-                Assuming that an LLVM build is necessary.",
-            );
-        }
-
         let build_llvm_config = llvm_config_ret_dir
             .join(exe("llvm-config", &*builder.config.build));
         let done_stamp = out_dir.join("llvm-finished-building");
 
-        if let Some(llvm_commit) = llvm_info.sha() {
-            if done_stamp.exists() {
+        if done_stamp.exists() {
+            if let Some(llvm_commit) = llvm_info.sha() {
                 let done_contents = t!(fs::read(&done_stamp));
 
                 // If LLVM was already built previously and the submodule's commit didn't change
                 // from the previous build, then no action is required.
                 if done_contents == llvm_commit.as_bytes() {
-                    return build_llvm_config
+                    return build_llvm_config;
                 }
+            } else {
+                builder.info(
+                    "Could not determine the LLVM submodule commit hash. \
+                     Assuming that an LLVM rebuild is not necessary.",
+                );
+                builder.info(&format!(
+                    "To force LLVM to rebuild, remove the file `{}`",
+                    done_stamp.display()
+                ));
+                return build_llvm_config;
             }
         }
 
@@ -303,9 +306,7 @@ impl Step for Llvm {
 
         cfg.build();
 
-        if let Some(llvm_commit) = llvm_info.sha() {
-            t!(fs::write(&done_stamp, llvm_commit));
-        }
+        t!(fs::write(&done_stamp, llvm_info.sha().unwrap_or("")));
 
         build_llvm_config
     }
diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs
index d644787d2c4..8fd5fe0653e 100644
--- a/src/libcore/iter/traits/iterator.rs
+++ b/src/libcore/iter/traits/iterator.rs
@@ -2546,6 +2546,16 @@ pub trait Iterator {
 
     /// Lexicographically compares the elements of this `Iterator` with those
     /// of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::cmp::Ordering;
+    ///
+    /// assert_eq!([1].iter().cmp([1].iter()), Ordering::Equal);
+    /// assert_eq!([1].iter().cmp([1, 2].iter()), Ordering::Less);
+    /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn cmp<I>(mut self, other: I) -> Ordering where
         I: IntoIterator<Item = Self::Item>,
@@ -2578,6 +2588,18 @@ pub trait Iterator {
 
     /// Lexicographically compares the elements of this `Iterator` with those
     /// of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::cmp::Ordering;
+    ///
+    /// assert_eq!([1.].iter().partial_cmp([1.].iter()), Some(Ordering::Equal));
+    /// assert_eq!([1.].iter().partial_cmp([1., 2.].iter()), Some(Ordering::Less));
+    /// assert_eq!([1., 2.].iter().partial_cmp([1.].iter()), Some(Ordering::Greater));
+    ///
+    /// assert_eq!([std::f64::NAN].iter().partial_cmp([1.].iter()), None);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn partial_cmp<I>(mut self, other: I) -> Option<Ordering> where
         I: IntoIterator,
@@ -2610,6 +2632,13 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are equal to those of
     /// another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().eq([1].iter()), true);
+    /// assert_eq!([1].iter().eq([1, 2].iter()), false);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn eq<I>(mut self, other: I) -> bool where
         I: IntoIterator,
@@ -2635,6 +2664,13 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are unequal to those of
     /// another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().ne([1].iter()), false);
+    /// assert_eq!([1].iter().ne([1, 2].iter()), true);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn ne<I>(self, other: I) -> bool where
         I: IntoIterator,
@@ -2646,6 +2682,14 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are lexicographically
     /// less than those of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().lt([1].iter()), false);
+    /// assert_eq!([1].iter().lt([1, 2].iter()), true);
+    /// assert_eq!([1, 2].iter().lt([1].iter()), false);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn lt<I>(self, other: I) -> bool where
         I: IntoIterator,
@@ -2657,6 +2701,14 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are lexicographically
     /// less or equal to those of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().le([1].iter()), true);
+    /// assert_eq!([1].iter().le([1, 2].iter()), true);
+    /// assert_eq!([1, 2].iter().le([1].iter()), false);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn le<I>(self, other: I) -> bool where
         I: IntoIterator,
@@ -2671,6 +2723,14 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are lexicographically
     /// greater than those of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().gt([1].iter()), false);
+    /// assert_eq!([1].iter().gt([1, 2].iter()), false);
+    /// assert_eq!([1, 2].iter().gt([1].iter()), true);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn gt<I>(self, other: I) -> bool where
         I: IntoIterator,
@@ -2682,6 +2742,14 @@ pub trait Iterator {
 
     /// Determines if the elements of this `Iterator` are lexicographically
     /// greater than or equal to those of another.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert_eq!([1].iter().ge([1].iter()), true);
+    /// assert_eq!([1].iter().ge([1, 2].iter()), false);
+    /// assert_eq!([1, 2].iter().ge([1].iter()), true);
+    /// ```
     #[stable(feature = "iter_order", since = "1.5.0")]
     fn ge<I>(self, other: I) -> bool where
         I: IntoIterator,
@@ -2730,6 +2798,18 @@ pub trait Iterator {
     /// function to determine the ordering of two elements. Apart from that, it's equivalent to
     /// [`is_sorted`]; see its documentation for more information.
     ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(is_sorted)]
+    ///
+    /// assert!([1, 2, 2, 9].iter().is_sorted_by(|a, b| a.partial_cmp(b)));
+    /// assert!(![1, 3, 2, 4].iter().is_sorted_by(|a, b| a.partial_cmp(b)));
+    /// assert!([0].iter().is_sorted_by(|a, b| a.partial_cmp(b)));
+    /// assert!(std::iter::empty::<i32>().is_sorted_by(|a, b| a.partial_cmp(b)));
+    /// assert!(![0.0, 1.0, std::f32::NAN].iter().is_sorted_by(|a, b| a.partial_cmp(b)));
+    /// ```
+    ///
     /// [`is_sorted`]: trait.Iterator.html#method.is_sorted
     #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")]
     fn is_sorted_by<F>(mut self, mut compare: F) -> bool
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 5e2aebfd318..190b50b10b2 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -425,19 +425,44 @@ impl<'a> LoweringContext<'a> {
 
         impl<'tcx, 'interner> Visitor<'tcx> for MiscCollector<'tcx, 'interner> {
             fn visit_pat(&mut self, p: &'tcx Pat) {
-                match p.node {
+                if let PatKind::Paren(..) | PatKind::Rest = p.node {
                     // Doesn't generate a HIR node
-                    PatKind::Paren(..) | PatKind::Rest => {},
-                    _ => {
-                        if let Some(owner) = self.hir_id_owner {
-                            self.lctx.lower_node_id_with_owner(p.id, owner);
-                        }
-                    }
-                };
+                } else if let Some(owner) = self.hir_id_owner {
+                    self.lctx.lower_node_id_with_owner(p.id, owner);
+                }
 
                 visit::walk_pat(self, p)
             }
 
+            // HACK(or_patterns; Centril | dlrobertson): Avoid creating
+            // HIR  nodes for `PatKind::Or` for the top level of a `ast::Arm`.
+            // This is a temporary hack that should go away once we push down
+            // `arm.pats: HirVec<P<Pat>>` -> `arm.pat: P<Pat>` to HIR. // Centril
+            fn visit_arm(&mut self, arm: &'tcx Arm) {
+                match &arm.pat.node {
+                    PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)),
+                    _ => self.visit_pat(&arm.pat),
+                }
+                walk_list!(self, visit_expr, &arm.guard);
+                self.visit_expr(&arm.body);
+                walk_list!(self, visit_attribute, &arm.attrs);
+            }
+
+            // HACK(or_patterns; Centril | dlrobertson): Same as above. // Centril
+            fn visit_expr(&mut self, e: &'tcx Expr) {
+                if let ExprKind::Let(pat, scrutinee) = &e.node {
+                    walk_list!(self, visit_attribute, e.attrs.iter());
+                    match &pat.node {
+                        PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)),
+                        _ => self.visit_pat(&pat),
+                    }
+                    self.visit_expr(scrutinee);
+                    self.visit_expr_post(e);
+                    return;
+                }
+                visit::walk_expr(self, e)
+            }
+
             fn visit_item(&mut self, item: &'tcx Item) {
                 let hir_id = self.lctx.allocate_hir_id_counter(item.id);
 
diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs
index bd217831faa..0d8986ddec3 100644
--- a/src/librustc/hir/lowering/expr.rs
+++ b/src/librustc/hir/lowering/expr.rs
@@ -68,7 +68,7 @@ impl LoweringContext<'_> {
                 let ohs = P(self.lower_expr(ohs));
                 hir::ExprKind::AddrOf(m, ohs)
             }
-            ExprKind::Let(ref pats, ref scrutinee) => self.lower_expr_let(e.span, pats, scrutinee),
+            ExprKind::Let(ref pat, ref scrutinee) => self.lower_expr_let(e.span, pat, scrutinee),
             ExprKind::If(ref cond, ref then, ref else_opt) => {
                 self.lower_expr_if(e.span, cond, then, else_opt.as_deref())
             }
@@ -227,16 +227,11 @@ impl LoweringContext<'_> {
         }
     }
 
-    /// Emit an error and lower `ast::ExprKind::Let(pats, scrutinee)` into:
+    /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
     /// ```rust
     /// match scrutinee { pats => true, _ => false }
     /// ```
-    fn lower_expr_let(
-        &mut self,
-        span: Span,
-        pats: &[AstP<Pat>],
-        scrutinee: &Expr
-    ) -> hir::ExprKind {
+    fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind {
         // If we got here, the `let` expression is not allowed.
         self.sess
             .struct_span_err(span, "`let` expressions are not supported here")
@@ -246,23 +241,23 @@ impl LoweringContext<'_> {
 
         // For better recovery, we emit:
         // ```
-        // match scrutinee { pats => true, _ => false }
+        // match scrutinee { pat => true, _ => false }
         // ```
         // While this doesn't fully match the user's intent, it has key advantages:
         // 1. We can avoid using `abort_if_errors`.
-        // 2. We can typeck both `pats` and `scrutinee`.
-        // 3. `pats` is allowed to be refutable.
+        // 2. We can typeck both `pat` and `scrutinee`.
+        // 3. `pat` is allowed to be refutable.
         // 4. The return type of the block is `bool` which seems like what the user wanted.
         let scrutinee = self.lower_expr(scrutinee);
         let then_arm = {
-            let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
+            let pat = self.lower_pat_top_hack(pat);
             let expr = self.expr_bool(span, true);
-            self.arm(pats, P(expr))
+            self.arm(pat, P(expr))
         };
         let else_arm = {
-            let pats = hir_vec![self.pat_wild(span)];
+            let pat = self.pat_wild(span);
             let expr = self.expr_bool(span, false);
-            self.arm(pats, P(expr))
+            self.arm(hir_vec![pat], P(expr))
         };
         hir::ExprKind::Match(
             P(scrutinee),
@@ -291,13 +286,12 @@ impl LoweringContext<'_> {
         // Handle then + scrutinee:
         let then_blk = self.lower_block(then, false);
         let then_expr = self.expr_block(then_blk, ThinVec::new());
-        let (then_pats, scrutinee, desugar) = match cond.node {
+        let (then_pat, scrutinee, desugar) = match cond.node {
             // `<pat> => <then>`:
-            ExprKind::Let(ref pats, ref scrutinee) => {
+            ExprKind::Let(ref pat, ref scrutinee) => {
                 let scrutinee = self.lower_expr(scrutinee);
-                let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
-                let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
-                (pats, scrutinee, desugar)
+                let pat = self.lower_pat_top_hack(pat);
+                (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause })
             }
             // `true => <then>`:
             _ => {
@@ -312,13 +306,11 @@ impl LoweringContext<'_> {
                 // to preserve drop semantics since `if cond { ... }` does not
                 // let temporaries live outside of `cond`.
                 let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
-
-                let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
-                let pats = hir_vec![self.pat_bool(span, true)];
-                (pats, cond, desugar)
+                let pat = self.pat_bool(span, true);
+                (hir_vec![pat], cond, hir::MatchSource::IfDesugar { contains_else_clause })
             }
         };
-        let then_arm = self.arm(then_pats, P(then_expr));
+        let then_arm = self.arm(then_pat, P(then_expr));
 
         hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
     }
@@ -345,8 +337,8 @@ impl LoweringContext<'_> {
         // Handle then + scrutinee:
         let then_blk = self.lower_block(body, false);
         let then_expr = self.expr_block(then_blk, ThinVec::new());
-        let (then_pats, scrutinee, desugar, source) = match cond.node {
-            ExprKind::Let(ref pats, ref scrutinee) => {
+        let (then_pat, scrutinee, desugar, source) = match cond.node {
+            ExprKind::Let(ref pat, ref scrutinee) => {
                 // to:
                 //
                 //   [opt_ident]: loop {
@@ -356,9 +348,8 @@ impl LoweringContext<'_> {
                 //     }
                 //   }
                 let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
-                let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
-                let desugar = hir::MatchSource::WhileLetDesugar;
-                (pats, scrutinee, desugar, hir::LoopSource::WhileLet)
+                let pat = self.lower_pat_top_hack(pat);
+                (pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
             }
             _ => {
                 // We desugar: `'label: while $cond $body` into:
@@ -383,14 +374,12 @@ impl LoweringContext<'_> {
                 // to preserve drop semantics since `while cond { ... }` does not
                 // let temporaries live outside of `cond`.
                 let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
-
-                let desugar = hir::MatchSource::WhileDesugar;
                 // `true => <then>`:
-                let pats = hir_vec![self.pat_bool(span, true)];
-                (pats, cond, desugar, hir::LoopSource::While)
+                let pat = self.pat_bool(span, true);
+                (hir_vec![pat], cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
             }
         };
-        let then_arm = self.arm(then_pats, P(then_expr));
+        let then_arm = self.arm(then_pat, P(then_expr));
 
         // `match <scrutinee> { ... }`
         let match_expr = self.expr_match(
@@ -440,7 +429,7 @@ impl LoweringContext<'_> {
         hir::Arm {
             hir_id: self.next_id(),
             attrs: self.lower_attrs(&arm.attrs),
-            pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
+            pats: self.lower_pat_top_hack(&arm.pat),
             guard: match arm.guard {
                 Some(ref x) => Some(hir::Guard::If(P(self.lower_expr(x)))),
                 _ => None,
@@ -450,6 +439,16 @@ impl LoweringContext<'_> {
         }
     }
 
+    /// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns
+    /// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready
+    /// to deal with it. This should by fixed by pushing it down to HIR and then HAIR.
+    fn lower_pat_top_hack(&mut self, pat: &Pat) -> HirVec<P<hir::Pat>> {
+        match pat.node {
+            PatKind::Or(ref ps) => ps.iter().map(|x| self.lower_pat(x)).collect(),
+            _ => hir_vec![self.lower_pat(pat)],
+        }
+    }
+
     pub(super) fn make_async_expr(
         &mut self,
         capture_clause: CaptureBy,
@@ -1255,7 +1254,6 @@ impl LoweringContext<'_> {
                 ThinVec::from(attrs.clone()),
             ));
             let ok_pat = self.pat_ok(span, val_pat);
-
             self.arm(hir_vec![ok_pat], val_expr)
         };
 
@@ -1486,7 +1484,10 @@ impl LoweringContext<'_> {
         }
     }
 
-    fn arm(&mut self, pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
+    /// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns
+    /// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready
+    /// to deal with it. This should by fixed by pushing it down to HIR and then HAIR.
+    fn arm(&mut self, pats: HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
         hir::Arm {
             hir_id: self.next_id(),
             attrs: hir_vec![],
diff --git a/src/librustc_lexer/Cargo.toml b/src/librustc_lexer/Cargo.toml
index 675d3065c5b..950771f0a69 100644
--- a/src/librustc_lexer/Cargo.toml
+++ b/src/librustc_lexer/Cargo.toml
@@ -2,8 +2,14 @@
 authors = ["The Rust Project Developers"]
 name = "rustc_lexer"
 version = "0.1.0"
+license = "MIT OR Apache-2.0"
 edition = "2018"
 
+repository = "https://github.com/rust-lang/rust/"
+description = """
+Rust lexer used by rustc. No stability guarantees are provided.
+"""
+
 # Note: do not remove this blank `[lib]` section.
 # This will be used when publishing this crate as `rustc-ap-rustc_lexer`.
 [lib]
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 26e7b789f8f..aecf5c5b52d 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -772,7 +772,7 @@ impl EarlyLintPass for UnusedDocComment {
     }
 
     fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
-        let arm_span = arm.pats[0].span.with_hi(arm.body.span.hi());
+        let arm_span = arm.pat.span.with_hi(arm.body.span.hi());
         self.warn_if_doc(cx, arm_span, "match arms", false, &arm.attrs);
     }
 
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 39c0698aeec..561bf202dfe 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -493,10 +493,8 @@ impl EarlyLintPass for UnusedParens {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         use syntax::ast::ExprKind::*;
         let (value, msg, followed_by_block, left_pos, right_pos) = match e.node {
-            Let(ref pats, ..) => {
-                for p in pats {
-                    self.check_unused_parens_pat(cx, p, false, false);
-                }
+            Let(ref pat, ..) => {
+                self.check_unused_parens_pat(cx, pat, false, false);
                 return;
             }
 
@@ -594,9 +592,7 @@ impl EarlyLintPass for UnusedParens {
     }
 
     fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
-        for p in &arm.pats {
-            self.check_unused_parens_pat(cx, p, false, false);
-        }
+        self.check_unused_parens_pat(cx, &arm.pat, false, false);
     }
 }
 
diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs
index e15d02a9f7e..3ddaf2d94f9 100644
--- a/src/librustc_resolve/late.rs
+++ b/src/librustc_resolve/late.rs
@@ -9,7 +9,7 @@ use GenericParameters::*;
 use RibKind::*;
 
 use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding};
-use crate::{Module, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult};
+use crate::{Module, ModuleOrUniformRoot, NameBindingKind, ParentScope, PathResult};
 use crate::{ResolutionError, Resolver, Segment, UseError};
 
 use log::debug;
@@ -18,7 +18,7 @@ use rustc::hir::def::{self, PartialRes, DefKind, CtorKind, PerNS};
 use rustc::hir::def::Namespace::{self, *};
 use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc::hir::TraitCandidate;
-use rustc::util::nodemap::FxHashMap;
+use rustc::util::nodemap::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 use syntax::{unwrap_or, walk_list};
 use syntax::ast::*;
@@ -35,8 +35,10 @@ mod diagnostics;
 
 type Res = def::Res<NodeId>;
 
+type IdentMap<T> = FxHashMap<Ident, T>;
+
 /// Map from the name in a pattern to its binding mode.
-type BindingMap = FxHashMap<Ident, BindingInfo>;
+type BindingMap = IdentMap<BindingInfo>;
 
 #[derive(Copy, Clone, Debug)]
 struct BindingInfo {
@@ -73,6 +75,16 @@ impl PatternSource {
     }
 }
 
+/// Denotes whether the context for the set of already bound bindings is a `Product`
+/// or `Or` context. This is used in e.g., `fresh_binding` and `resolve_pattern_inner`.
+/// See those functions for more information.
+enum PatBoundCtx {
+    /// A product pattern context, e.g., `Variant(a, b)`.
+    Product,
+    /// An or-pattern context, e.g., `p_0 | ... | p_n`.
+    Or,
+}
+
 /// The rib kind restricts certain accesses,
 /// e.g. to a `Res::Local` of an outer item.
 #[derive(Copy, Clone, Debug)]
@@ -143,7 +155,7 @@ impl RibKind<'_> {
 /// resolving, the name is looked up from inside out.
 #[derive(Debug)]
 crate struct Rib<'a, R = Res> {
-    pub bindings: FxHashMap<Ident, R>,
+    pub bindings: IdentMap<R>,
     pub kind: RibKind<'a>,
 }
 
@@ -406,50 +418,32 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
             visit::walk_foreign_item(this, foreign_item);
         });
     }
-    fn visit_fn(&mut self,
-                function_kind: FnKind<'tcx>,
-                declaration: &'tcx FnDecl,
-                _: Span,
-                _: NodeId)
-    {
+    fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, _: NodeId) {
         debug!("(resolving function) entering function");
-        let rib_kind = match function_kind {
+        let rib_kind = match fn_kind {
             FnKind::ItemFn(..) => FnItemRibKind,
             FnKind::Method(..) | FnKind::Closure(_) => NormalRibKind,
         };
 
         // Create a value rib for the function.
-        self.ribs[ValueNS].push(Rib::new(rib_kind));
-
-        // Create a label rib for the function.
-        self.label_ribs.push(Rib::new(rib_kind));
-
-        // Add each argument to the rib.
-        let mut bindings_list = FxHashMap::default();
-        for argument in &declaration.inputs {
-            self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
-
-            self.visit_ty(&argument.ty);
-
-            debug!("(resolving function) recorded argument");
-        }
-        visit::walk_fn_ret_ty(self, &declaration.output);
-
-        // Resolve the function body, potentially inside the body of an async closure
-        match function_kind {
-            FnKind::ItemFn(.., body) |
-            FnKind::Method(.., body) => {
-                self.visit_block(body);
-            }
-            FnKind::Closure(body) => {
-                self.visit_expr(body);
-            }
-        };
-
-        debug!("(resolving function) leaving function");
-
-        self.label_ribs.pop();
-        self.ribs[ValueNS].pop();
+        self.with_rib(ValueNS, rib_kind, |this| {
+            // Create a label rib for the function.
+            this.with_label_rib(rib_kind, |this| {
+                // Add each argument to the rib.
+                this.resolve_params(&declaration.inputs);
+
+                visit::walk_fn_ret_ty(this, &declaration.output);
+
+                // Resolve the function body, potentially inside the body of an async closure
+                match fn_kind {
+                    FnKind::ItemFn(.., body) |
+                    FnKind::Method(.., body) => this.visit_block(body),
+                    FnKind::Closure(body) => this.visit_expr(body),
+                };
+
+                debug!("(resolving function) leaving function");
+            })
+        });
     }
 
     fn visit_generics(&mut self, generics: &'tcx Generics) {
@@ -528,13 +522,14 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         // although it may be useful to track other components as well for diagnostics.
         let graph_root = resolver.graph_root;
         let parent_scope = ParentScope::module(graph_root);
+        let start_rib_kind = ModuleRibKind(graph_root);
         LateResolutionVisitor {
             r: resolver,
             parent_scope,
             ribs: PerNS {
-                value_ns: vec![Rib::new(ModuleRibKind(graph_root))],
-                type_ns: vec![Rib::new(ModuleRibKind(graph_root))],
-                macro_ns: vec![Rib::new(ModuleRibKind(graph_root))],
+                value_ns: vec![Rib::new(start_rib_kind)],
+                type_ns: vec![Rib::new(start_rib_kind)],
+                macro_ns: vec![Rib::new(start_rib_kind)],
             },
             label_ribs: Vec::new(),
             current_trait_ref: None,
@@ -588,23 +583,32 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
     // generate a fake "implementation scope" containing all the
     // implementations thus found, for compatibility with old resolve pass.
 
-    fn with_scope<F, T>(&mut self, id: NodeId, f: F) -> T
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>) -> T
-    {
+    /// Do some `work` within a new innermost rib of the given `kind` in the given namespace (`ns`).
+    fn with_rib<T>(
+        &mut self,
+        ns: Namespace,
+        kind: RibKind<'a>,
+        work: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        self.ribs[ns].push(Rib::new(kind));
+        let ret = work(self);
+        self.ribs[ns].pop();
+        ret
+    }
+
+    fn with_scope<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
         let id = self.r.definitions.local_def_id(id);
         let module = self.r.module_map.get(&id).cloned(); // clones a reference
         if let Some(module) = module {
             // Move down in the graph.
             let orig_module = replace(&mut self.parent_scope.module, module);
-            self.ribs[ValueNS].push(Rib::new(ModuleRibKind(module)));
-            self.ribs[TypeNS].push(Rib::new(ModuleRibKind(module)));
-
-            let ret = f(self);
-
-            self.parent_scope.module = orig_module;
-            self.ribs[ValueNS].pop();
-            self.ribs[TypeNS].pop();
-            ret
+            self.with_rib(ValueNS, ModuleRibKind(module), |this| {
+                this.with_rib(TypeNS, ModuleRibKind(module), |this| {
+                    let ret = f(this);
+                    this.parent_scope.module = orig_module;
+                    ret
+                })
+            })
         } else {
             f(self)
         }
@@ -808,7 +812,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
     }
 
     fn with_generic_param_rib<'c, F>(&'c mut self, generic_params: GenericParameters<'a, 'c>, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
+        where F: FnOnce(&mut Self)
     {
         debug!("with_generic_param_rib");
         match generic_params {
@@ -894,38 +898,24 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         }
     }
 
-    fn with_label_rib<F>(&mut self, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
-        self.label_ribs.push(Rib::new(NormalRibKind));
+    fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) {
+        self.label_ribs.push(Rib::new(kind));
         f(self);
         self.label_ribs.pop();
     }
 
-    fn with_item_rib<F>(&mut self, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
-        self.ribs[ValueNS].push(Rib::new(ItemRibKind));
-        self.ribs[TypeNS].push(Rib::new(ItemRibKind));
-        f(self);
-        self.ribs[TypeNS].pop();
-        self.ribs[ValueNS].pop();
+    fn with_item_rib(&mut self, f: impl FnOnce(&mut Self)) {
+        self.with_rib(ValueNS, ItemRibKind, |this| this.with_rib(TypeNS, ItemRibKind, f))
     }
 
-    fn with_constant_rib<F>(&mut self, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
+    fn with_constant_rib(&mut self, f: impl FnOnce(&mut Self)) {
         debug!("with_constant_rib");
-        self.ribs[ValueNS].push(Rib::new(ConstantItemRibKind));
-        self.label_ribs.push(Rib::new(ConstantItemRibKind));
-        f(self);
-        self.label_ribs.pop();
-        self.ribs[ValueNS].pop();
+        self.with_rib(ValueNS, ConstantItemRibKind, |this| {
+            this.with_label_rib(ConstantItemRibKind, f);
+        });
     }
 
-    fn with_current_self_type<T, F>(&mut self, self_type: &Ty, f: F) -> T
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>) -> T
-    {
+    fn with_current_self_type<T>(&mut self, self_type: &Ty, f: impl FnOnce(&mut Self) -> T) -> T {
         // Handle nested impls (inside fn bodies)
         let previous_value = replace(&mut self.current_self_type, Some(self_type.clone()));
         let result = f(self);
@@ -933,9 +923,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         result
     }
 
-    fn with_current_self_item<T, F>(&mut self, self_item: &Item, f: F) -> T
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>) -> T
-    {
+    fn with_current_self_item<T>(&mut self, self_item: &Item, f: impl FnOnce(&mut Self) -> T) -> T {
         let previous_value = replace(&mut self.current_self_item, Some(self_item.id));
         let result = f(self);
         self.current_self_item = previous_value;
@@ -943,9 +931,11 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
     }
 
     /// When evaluating a `trait` use its associated types' idents for suggestionsa in E0412.
-    fn with_trait_items<T, F>(&mut self, trait_items: &Vec<TraitItem>, f: F) -> T
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>) -> T
-    {
+    fn with_trait_items<T>(
+        &mut self,
+        trait_items: &Vec<TraitItem>,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
         let trait_assoc_types = replace(
             &mut self.current_trait_assoc_types,
             trait_items.iter().filter_map(|item| match &item.node {
@@ -959,9 +949,11 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
     }
 
     /// This is called to resolve a trait reference from an `impl` (i.e., `impl Trait for Foo`).
-    fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>, Option<DefId>) -> T
-    {
+    fn with_optional_trait_ref<T>(
+        &mut self,
+        opt_trait_ref: Option<&TraitRef>,
+        f: impl FnOnce(&mut Self, Option<DefId>) -> T
+    ) -> T {
         let mut new_val = None;
         let mut new_id = None;
         if let Some(trait_ref) = opt_trait_ref {
@@ -996,27 +988,18 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         result
     }
 
-    fn with_self_rib<F>(&mut self, self_res: Res, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
+    fn with_self_rib_ns(&mut self, ns: Namespace, self_res: Res, f: impl FnOnce(&mut Self)) {
         let mut self_type_rib = Rib::new(NormalRibKind);
 
         // Plain insert (no renaming, since types are not currently hygienic)
         self_type_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), self_res);
-        self.ribs[TypeNS].push(self_type_rib);
+        self.ribs[ns].push(self_type_rib);
         f(self);
-        self.ribs[TypeNS].pop();
+        self.ribs[ns].pop();
     }
 
-    fn with_self_struct_ctor_rib<F>(&mut self, impl_id: DefId, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
-        let self_res = Res::SelfCtor(impl_id);
-        let mut self_type_rib = Rib::new(NormalRibKind);
-        self_type_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), self_res);
-        self.ribs[ValueNS].push(self_type_rib);
-        f(self);
-        self.ribs[ValueNS].pop();
+    fn with_self_rib(&mut self, self_res: Res, f: impl FnOnce(&mut Self)) {
+        self.with_self_rib_ns(TypeNS, self_res, f)
     }
 
     fn resolve_implementation(&mut self,
@@ -1044,8 +1027,8 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
                         this.visit_generics(generics);
                         // Resolve the items within the impl.
                         this.with_current_self_type(self_type, |this| {
-                            this.with_self_struct_ctor_rib(item_def_id, |this| {
-                                debug!("resolve_implementation with_self_struct_ctor_rib");
+                            this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
+                                debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
                                 for impl_item in impl_items {
                                     // We also need a new scope for the impl item type parameters.
                                     let generic_params = HasGenericParams(&impl_item.generics,
@@ -1135,6 +1118,15 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         }
     }
 
+    fn resolve_params(&mut self, params: &[Param]) {
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        for Param { pat, ty, .. } in params {
+            self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
+            self.visit_ty(ty);
+            debug!("(resolving function / closure) recorded parameter");
+        }
+    }
+
     fn resolve_local(&mut self, local: &Local) {
         // Resolve the type.
         walk_list!(self, visit_ty, &local.ty);
@@ -1143,72 +1135,93 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         walk_list!(self, visit_expr, &local.init);
 
         // Resolve the pattern.
-        self.resolve_pattern(&local.pat, PatternSource::Let, &mut FxHashMap::default());
+        self.resolve_pattern_top(&local.pat, PatternSource::Let);
     }
 
-    // build a map from pattern identifiers to binding-info's.
-    // this is done hygienically. This could arise for a macro
-    // that expands into an or-pattern where one 'x' was from the
-    // user and one 'x' came from the macro.
+    /// build a map from pattern identifiers to binding-info's.
+    /// this is done hygienically. This could arise for a macro
+    /// that expands into an or-pattern where one 'x' was from the
+    /// user and one 'x' came from the macro.
     fn binding_mode_map(&mut self, pat: &Pat) -> BindingMap {
         let mut binding_map = FxHashMap::default();
 
         pat.walk(&mut |pat| {
-            if let PatKind::Ident(binding_mode, ident, ref sub_pat) = pat.node {
-                if sub_pat.is_some() || match self.r.partial_res_map.get(&pat.id)
-                                                                  .map(|res| res.base_res()) {
-                    Some(Res::Local(..)) => true,
-                    _ => false,
-                } {
-                    let binding_info = BindingInfo { span: ident.span, binding_mode: binding_mode };
-                    binding_map.insert(ident, binding_info);
+            match pat.node {
+                PatKind::Ident(binding_mode, ident, ref sub_pat)
+                    if sub_pat.is_some() || self.is_base_res_local(pat.id) =>
+                {
+                    binding_map.insert(ident, BindingInfo { span: ident.span, binding_mode });
+                }
+                PatKind::Or(ref ps) => {
+                    // Check the consistency of this or-pattern and
+                    // then add all bindings to the larger map.
+                    for bm in self.check_consistent_bindings(ps) {
+                        binding_map.extend(bm);
+                    }
+                    return false;
                 }
+                _ => {}
             }
+
             true
         });
 
         binding_map
     }
 
-    // Checks that all of the arms in an or-pattern have exactly the
-    // same set of bindings, with the same binding modes for each.
-    fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) {
+    fn is_base_res_local(&self, nid: NodeId) -> bool {
+        match self.r.partial_res_map.get(&nid).map(|res| res.base_res()) {
+            Some(Res::Local(..)) => true,
+            _ => false,
+        }
+    }
+
+    /// Checks that all of the arms in an or-pattern have exactly the
+    /// same set of bindings, with the same binding modes for each.
+    fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) -> Vec<BindingMap> {
         let mut missing_vars = FxHashMap::default();
         let mut inconsistent_vars = FxHashMap::default();
 
-        for pat_outer in pats.iter() {
-            let map_outer = self.binding_mode_map(&pat_outer);
-
-            for pat_inner in pats.iter().filter(|pat| pat.id != pat_outer.id) {
-                let map_inner = self.binding_mode_map(&pat_inner);
-
-                for (&key_inner, &binding_inner) in map_inner.iter() {
-                    match map_outer.get(&key_inner) {
-                        None => {  // missing binding
-                            let binding_error = missing_vars
-                                .entry(key_inner.name)
-                                .or_insert(BindingError {
-                                    name: key_inner.name,
-                                    origin: BTreeSet::new(),
-                                    target: BTreeSet::new(),
-                                    could_be_path:
-                                        key_inner.name.as_str().starts_with(char::is_uppercase)
-                                });
-                            binding_error.origin.insert(binding_inner.span);
-                            binding_error.target.insert(pat_outer.span);
-                        }
-                        Some(binding_outer) => {  // check consistent binding
-                            if binding_outer.binding_mode != binding_inner.binding_mode {
-                                inconsistent_vars
-                                    .entry(key_inner.name)
-                                    .or_insert((binding_inner.span, binding_outer.span));
-                            }
+        // 1) Compute the binding maps of all arms.
+        let maps = pats.iter()
+            .map(|pat| self.binding_mode_map(pat))
+            .collect::<Vec<_>>();
+
+        // 2) Record any missing bindings or binding mode inconsistencies.
+        for (map_outer, pat_outer) in pats.iter().enumerate().map(|(idx, pat)| (&maps[idx], pat)) {
+            // Check against all arms except for the same pattern which is always self-consistent.
+            let inners = pats.iter().enumerate()
+                .filter(|(_, pat)| pat.id != pat_outer.id)
+                .flat_map(|(idx, _)| maps[idx].iter())
+                .map(|(key, binding)| (key.name, map_outer.get(&key), binding));
+
+            for (name, info, &binding_inner) in inners {
+                match info {
+                    None => { // The inner binding is missing in the outer.
+                        let binding_error = missing_vars
+                            .entry(name)
+                            .or_insert_with(|| BindingError {
+                                name,
+                                origin: BTreeSet::new(),
+                                target: BTreeSet::new(),
+                                could_be_path: name.as_str().starts_with(char::is_uppercase),
+                            });
+                        binding_error.origin.insert(binding_inner.span);
+                        binding_error.target.insert(pat_outer.span);
+                    }
+                    Some(binding_outer) => {
+                        if binding_outer.binding_mode != binding_inner.binding_mode {
+                            // The binding modes in the outer and inner bindings differ.
+                            inconsistent_vars
+                                .entry(name)
+                                .or_insert((binding_inner.span, binding_outer.span));
                         }
                     }
                 }
             }
         }
 
+        // 3) Report all missing variables we found.
         let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
         missing_vars.sort();
         for (name, mut v) in missing_vars {
@@ -1220,212 +1233,245 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
                 ResolutionError::VariableNotBoundInPattern(v));
         }
 
+        // 4) Report all inconsistencies in binding modes we found.
         let mut inconsistent_vars = inconsistent_vars.iter().collect::<Vec<_>>();
         inconsistent_vars.sort();
         for (name, v) in inconsistent_vars {
             self.r.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1));
         }
-    }
 
-    fn resolve_arm(&mut self, arm: &Arm) {
-        self.ribs[ValueNS].push(Rib::new(NormalRibKind));
-
-        self.resolve_pats(&arm.pats, PatternSource::Match);
-
-        if let Some(ref expr) = arm.guard {
-            self.visit_expr(expr)
-        }
-        self.visit_expr(&arm.body);
-
-        self.ribs[ValueNS].pop();
+        // 5) Finally bubble up all the binding maps.
+        maps
     }
 
-    /// Arising from `source`, resolve a sequence of patterns (top level or-patterns).
-    fn resolve_pats(&mut self, pats: &[P<Pat>], source: PatternSource) {
-        let mut bindings_list = FxHashMap::default();
-        for pat in pats {
-            self.resolve_pattern(pat, source, &mut bindings_list);
-        }
-        // This has to happen *after* we determine which pat_idents are variants
-        if pats.len() > 1 {
-            self.check_consistent_bindings(pats);
-        }
+    /// Check the consistency of the outermost or-patterns.
+    fn check_consistent_bindings_top(&mut self, pat: &Pat) {
+        pat.walk(&mut |pat| match pat.node {
+            PatKind::Or(ref ps) => {
+                self.check_consistent_bindings(ps);
+                false
+            },
+            _ => true,
+        })
     }
 
-    fn resolve_block(&mut self, block: &Block) {
-        debug!("(resolving block) entering block");
-        // Move down in the graph, if there's an anonymous module rooted here.
-        let orig_module = self.parent_scope.module;
-        let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference
-
-        let mut num_macro_definition_ribs = 0;
-        if let Some(anonymous_module) = anonymous_module {
-            debug!("(resolving block) found anonymous module, moving down");
-            self.ribs[ValueNS].push(Rib::new(ModuleRibKind(anonymous_module)));
-            self.ribs[TypeNS].push(Rib::new(ModuleRibKind(anonymous_module)));
-            self.parent_scope.module = anonymous_module;
-        } else {
-            self.ribs[ValueNS].push(Rib::new(NormalRibKind));
-        }
-
-        // Descend into the block.
-        for stmt in &block.stmts {
-            if let StmtKind::Item(ref item) = stmt.node {
-                if let ItemKind::MacroDef(..) = item.node {
-                    num_macro_definition_ribs += 1;
-                    let res = self.r.definitions.local_def_id(item.id);
-                    self.ribs[ValueNS].push(Rib::new(MacroDefinition(res)));
-                    self.label_ribs.push(Rib::new(MacroDefinition(res)));
-                }
-            }
-
-            self.visit_stmt(stmt);
-        }
-
-        // Move back up.
-        self.parent_scope.module = orig_module;
-        for _ in 0 .. num_macro_definition_ribs {
-            self.ribs[ValueNS].pop();
-            self.label_ribs.pop();
-        }
-        self.ribs[ValueNS].pop();
-        if anonymous_module.is_some() {
-            self.ribs[TypeNS].pop();
-        }
-        debug!("(resolving block) leaving block");
+    fn resolve_arm(&mut self, arm: &Arm) {
+        self.with_rib(ValueNS, NormalRibKind, |this| {
+            this.resolve_pattern_top(&arm.pat, PatternSource::Match);
+            walk_list!(this, visit_expr, &arm.guard);
+            this.visit_expr(&arm.body);
+        });
     }
 
-    fn fresh_binding(&mut self,
-                     ident: Ident,
-                     pat_id: NodeId,
-                     outer_pat_id: NodeId,
-                     pat_src: PatternSource,
-                     bindings: &mut FxHashMap<Ident, NodeId>)
-                     -> Res {
-        // Add the binding to the local ribs, if it
-        // doesn't already exist in the bindings map. (We
-        // must not add it if it's in the bindings map
-        // because that breaks the assumptions later
-        // passes make about or-patterns.)
-        let ident = ident.modern_and_legacy();
-        let mut res = Res::Local(pat_id);
-        match bindings.get(&ident).cloned() {
-            Some(id) if id == outer_pat_id => {
-                // `Variant(a, a)`, error
-                self.r.report_error(
-                    ident.span,
-                    ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(
-                        &ident.as_str())
-                );
-            }
-            Some(..) if pat_src == PatternSource::FnParam => {
-                // `fn f(a: u8, a: u8)`, error
-                self.r.report_error(
-                    ident.span,
-                    ResolutionError::IdentifierBoundMoreThanOnceInParameterList(
-                        &ident.as_str())
-                );
-            }
-            Some(..) if pat_src == PatternSource::Match ||
-                        pat_src == PatternSource::Let => {
-                // `Variant1(a) | Variant2(a)`, ok
-                // Reuse definition from the first `a`.
-                res = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident];
-            }
-            Some(..) => {
-                span_bug!(ident.span, "two bindings with the same name from \
-                                       unexpected pattern source {:?}", pat_src);
-            }
-            None => {
-                // A completely fresh binding, add to the lists if it's valid.
-                if ident.name != kw::Invalid {
-                    bindings.insert(ident, outer_pat_id);
-                    self.ribs[ValueNS].last_mut().unwrap().bindings.insert(ident, res);
-                }
-            }
-        }
+    /// Arising from `source`, resolve a top level pattern.
+    fn resolve_pattern_top(&mut self, pat: &Pat, pat_src: PatternSource) {
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        self.resolve_pattern(pat, pat_src, &mut bindings);
+    }
 
-        res
+    fn resolve_pattern(
+        &mut self,
+        pat: &Pat,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) {
+        self.resolve_pattern_inner(pat, pat_src, bindings);
+        // This has to happen *after* we determine which pat_idents are variants:
+        self.check_consistent_bindings_top(pat);
+        visit::walk_pat(self, pat);
     }
 
-    fn resolve_pattern(&mut self,
-                       pat: &Pat,
-                       pat_src: PatternSource,
-                       // Maps idents to the node ID for the
-                       // outermost pattern that binds them.
-                       bindings: &mut FxHashMap<Ident, NodeId>) {
+    /// Resolve bindings in a pattern. This is a helper to `resolve_pattern`.
+    ///
+    /// ### `bindings`
+    ///
+    /// A stack of sets of bindings accumulated.
+    ///
+    /// In each set, `PatBoundCtx::Product` denotes that a found binding in it should
+    /// be interpreted as re-binding an already bound binding. This results in an error.
+    /// Meanwhile, `PatBound::Or` denotes that a found binding in the set should result
+    /// in reusing this binding rather than creating a fresh one.
+    ///
+    /// When called at the top level, the stack must have a single element
+    /// with `PatBound::Product`. Otherwise, pushing to the stack happens as
+    /// or-patterns (`p_0 | ... | p_n`) are encountered and the context needs
+    /// to be switched to `PatBoundCtx::Or` and then `PatBoundCtx::Product` for each `p_i`.
+    /// When each `p_i` has been dealt with, the top set is merged with its parent.
+    /// When a whole or-pattern has been dealt with, the thing happens.
+    ///
+    /// See the implementation and `fresh_binding` for more details.
+    fn resolve_pattern_inner(
+        &mut self,
+        pat: &Pat,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) {
         // Visit all direct subpatterns of this pattern.
-        let outer_pat_id = pat.id;
         pat.walk(&mut |pat| {
             debug!("resolve_pattern pat={:?} node={:?}", pat, pat.node);
             match pat.node {
-                PatKind::Ident(bmode, ident, ref opt_pat) => {
-                    // First try to resolve the identifier as some existing
-                    // entity, then fall back to a fresh binding.
-                    let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS,
-                                                                      None, pat.span)
-                                      .and_then(LexicalScopeBinding::item);
-                    let res = binding.map(NameBinding::res).and_then(|res| {
-                        let is_syntactic_ambiguity = opt_pat.is_none() &&
-                            bmode == BindingMode::ByValue(Mutability::Immutable);
-                        match res {
-                            Res::Def(DefKind::Ctor(_, CtorKind::Const), _) |
-                            Res::Def(DefKind::Const, _) if is_syntactic_ambiguity => {
-                                // Disambiguate in favor of a unit struct/variant
-                                // or constant pattern.
-                                self.r.record_use(ident, ValueNS, binding.unwrap(), false);
-                                Some(res)
-                            }
-                            Res::Def(DefKind::Ctor(..), _)
-                            | Res::Def(DefKind::Const, _)
-                            | Res::Def(DefKind::Static, _) => {
-                                // This is unambiguously a fresh binding, either syntactically
-                                // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
-                                // to something unusable as a pattern (e.g., constructor function),
-                                // but we still conservatively report an error, see
-                                // issues/33118#issuecomment-233962221 for one reason why.
-                                self.r.report_error(
-                                    ident.span,
-                                    ResolutionError::BindingShadowsSomethingUnacceptable(
-                                        pat_src.descr(), ident.name, binding.unwrap())
-                                );
-                                None
-                            }
-                            Res::Def(DefKind::Fn, _) | Res::Err => {
-                                // These entities are explicitly allowed
-                                // to be shadowed by fresh bindings.
-                                None
-                            }
-                            res => {
-                                span_bug!(ident.span, "unexpected resolution for an \
-                                                       identifier in pattern: {:?}", res);
-                            }
-                        }
-                    }).unwrap_or_else(|| {
-                        self.fresh_binding(ident, pat.id, outer_pat_id, pat_src, bindings)
-                    });
-
+                PatKind::Ident(bmode, ident, ref sub) => {
+                    // First try to resolve the identifier as some existing entity,
+                    // then fall back to a fresh binding.
+                    let has_sub = sub.is_some();
+                    let res = self.try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
+                        .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
                     self.r.record_partial_res(pat.id, PartialRes::new(res));
                 }
-
                 PatKind::TupleStruct(ref path, ..) => {
                     self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct);
                 }
-
                 PatKind::Path(ref qself, ref path) => {
                     self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);
                 }
-
                 PatKind::Struct(ref path, ..) => {
                     self.smart_resolve_path(pat.id, None, path, PathSource::Struct);
                 }
-
+                PatKind::Or(ref ps) => {
+                    // Add a new set of bindings to the stack. `Or` here records that when a
+                    // binding already exists in this set, it should not result in an error because
+                    // `V1(a) | V2(a)` must be allowed and are checked for consistency later.
+                    bindings.push((PatBoundCtx::Or, Default::default()));
+                    for p in ps {
+                        // Now we need to switch back to a product context so that each
+                        // part of the or-pattern internally rejects already bound names.
+                        // For example, `V1(a) | V2(a, a)` and `V1(a, a) | V2(a)` are bad.
+                        bindings.push((PatBoundCtx::Product, Default::default()));
+                        self.resolve_pattern_inner(p, pat_src, bindings);
+                        // Move up the non-overlapping bindings to the or-pattern.
+                        // Existing bindings just get "merged".
+                        let collected = bindings.pop().unwrap().1;
+                        bindings.last_mut().unwrap().1.extend(collected);
+                    }
+                    // This or-pattern itself can itself be part of a product,
+                    // e.g. `(V1(a) | V2(a), a)` or `(a, V1(a) | V2(a))`.
+                    // Both cases bind `a` again in a product pattern and must be rejected.
+                    let collected = bindings.pop().unwrap().1;
+                    bindings.last_mut().unwrap().1.extend(collected);
+
+                    // Prevent visiting `ps` as we've already done so above.
+                    return false;
+                }
                 _ => {}
             }
             true
         });
+    }
 
-        visit::walk_pat(self, pat);
+    fn fresh_binding(
+        &mut self,
+        ident: Ident,
+        pat_id: NodeId,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) -> Res {
+        // Add the binding to the local ribs, if it doesn't already exist in the bindings map.
+        // (We must not add it if it's in the bindings map because that breaks the assumptions
+        // later passes make about or-patterns.)
+        let ident = ident.modern_and_legacy();
+
+        // Walk outwards the stack of products / or-patterns and
+        // find out if the identifier has been bound in any of these.
+        let mut already_bound_and = false;
+        let mut already_bound_or = false;
+        for (is_sum, set) in bindings.iter_mut().rev() {
+            match (is_sum, set.get(&ident).cloned()) {
+                // Already bound in a product pattern, e.g. `(a, a)` which is not allowed.
+                (PatBoundCtx::Product, Some(..)) => already_bound_and = true,
+                // Already bound in an or-pattern, e.g. `V1(a) | V2(a)`.
+                // This is *required* for consistency which is checked later.
+                (PatBoundCtx::Or, Some(..)) => already_bound_or = true,
+                // Not already bound here.
+                _ => {}
+            }
+        }
+
+        if already_bound_and {
+            // Overlap in a product pattern somewhere; report an error.
+            use ResolutionError::*;
+            let error = match pat_src {
+                // `fn f(a: u8, a: u8)`:
+                PatternSource::FnParam => IdentifierBoundMoreThanOnceInParameterList,
+                // `Variant(a, a)`:
+                _ => IdentifierBoundMoreThanOnceInSamePattern,
+            };
+            self.r.report_error(ident.span, error(&ident.as_str()));
+        }
+
+        // Record as bound if it's valid:
+        let ident_valid = ident.name != kw::Invalid;
+        if ident_valid {
+            bindings.last_mut().unwrap().1.insert(ident);
+        }
+
+        if already_bound_or {
+            // `Variant1(a) | Variant2(a)`, ok
+            // Reuse definition from the first `a`.
+            self.innermost_rib_bindings(ValueNS)[&ident]
+        } else {
+            let res = Res::Local(pat_id);
+            if ident_valid {
+                // A completely fresh binding add to the set if it's valid.
+                self.innermost_rib_bindings(ValueNS).insert(ident, res);
+            }
+            res
+        }
+    }
+
+    fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut IdentMap<Res> {
+        &mut self.ribs[ns].last_mut().unwrap().bindings
+    }
+
+    fn try_resolve_as_non_binding(
+        &mut self,
+        pat_src: PatternSource,
+        pat: &Pat,
+        bm: BindingMode,
+        ident: Ident,
+        has_sub: bool,
+    ) -> Option<Res> {
+        let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None, pat.span)?.item()?;
+        let res = binding.res();
+
+        // An immutable (no `mut`) by-value (no `ref`) binding pattern without
+        // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could
+        // also be interpreted as a path to e.g. a constant, variant, etc.
+        let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Immutable);
+
+        match res {
+            Res::Def(DefKind::Ctor(_, CtorKind::Const), _) |
+            Res::Def(DefKind::Const, _) if is_syntactic_ambiguity => {
+                // Disambiguate in favor of a unit struct/variant or constant pattern.
+                self.r.record_use(ident, ValueNS, binding, false);
+                Some(res)
+            }
+            Res::Def(DefKind::Ctor(..), _)
+            | Res::Def(DefKind::Const, _)
+            | Res::Def(DefKind::Static, _) => {
+                // This is unambiguously a fresh binding, either syntactically
+                // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
+                // to something unusable as a pattern (e.g., constructor function),
+                // but we still conservatively report an error, see
+                // issues/33118#issuecomment-233962221 for one reason why.
+                self.r.report_error(
+                    ident.span,
+                    ResolutionError::BindingShadowsSomethingUnacceptable(
+                        pat_src.descr(),
+                        ident.name,
+                        binding,
+                    ),
+                );
+                None
+            }
+            Res::Def(DefKind::Fn, _) | Res::Err => {
+                // These entities are explicitly allowed to be shadowed by fresh bindings.
+                None
+            }
+            res => {
+                span_bug!(ident.span, "unexpected resolution for an \
+                                        identifier in pattern: {:?}", res);
+            }
+        }
     }
 
     // High-level and context dependent path resolution routine.
@@ -1723,12 +1769,10 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         Some(result)
     }
 
-    fn with_resolved_label<F>(&mut self, label: Option<Label>, id: NodeId, f: F)
-        where F: FnOnce(&mut LateResolutionVisitor<'_, '_>)
-    {
+    fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {
         if let Some(label) = label {
             self.unused_labels.insert(id, label.ident.span);
-            self.with_label_rib(|this| {
+            self.with_label_rib(NormalRibKind, |this| {
                 let ident = label.ident.modern_and_legacy();
                 this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
                 f(this);
@@ -1742,6 +1786,49 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
         self.with_resolved_label(label, id, |this| this.visit_block(block));
     }
 
+    fn resolve_block(&mut self, block: &Block) {
+        debug!("(resolving block) entering block");
+        // Move down in the graph, if there's an anonymous module rooted here.
+        let orig_module = self.parent_scope.module;
+        let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference
+
+        let mut num_macro_definition_ribs = 0;
+        if let Some(anonymous_module) = anonymous_module {
+            debug!("(resolving block) found anonymous module, moving down");
+            self.ribs[ValueNS].push(Rib::new(ModuleRibKind(anonymous_module)));
+            self.ribs[TypeNS].push(Rib::new(ModuleRibKind(anonymous_module)));
+            self.parent_scope.module = anonymous_module;
+        } else {
+            self.ribs[ValueNS].push(Rib::new(NormalRibKind));
+        }
+
+        // Descend into the block.
+        for stmt in &block.stmts {
+            if let StmtKind::Item(ref item) = stmt.node {
+                if let ItemKind::MacroDef(..) = item.node {
+                    num_macro_definition_ribs += 1;
+                    let res = self.r.definitions.local_def_id(item.id);
+                    self.ribs[ValueNS].push(Rib::new(MacroDefinition(res)));
+                    self.label_ribs.push(Rib::new(MacroDefinition(res)));
+                }
+            }
+
+            self.visit_stmt(stmt);
+        }
+
+        // Move back up.
+        self.parent_scope.module = orig_module;
+        for _ in 0 .. num_macro_definition_ribs {
+            self.ribs[ValueNS].pop();
+            self.label_ribs.pop();
+        }
+        self.ribs[ValueNS].pop();
+        if anonymous_module.is_some() {
+            self.ribs[TypeNS].pop();
+        }
+        debug!("(resolving block) leaving block");
+    }
+
     fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) {
         // First, record candidate traits for this expression if it could
         // result in the invocation of a method call.
@@ -1796,39 +1883,36 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
                 visit::walk_expr(self, expr);
             }
 
-            ExprKind::Let(ref pats, ref scrutinee) => {
+            ExprKind::Let(ref pat, ref scrutinee) => {
                 self.visit_expr(scrutinee);
-                self.resolve_pats(pats, PatternSource::Let);
+                self.resolve_pattern_top(pat, PatternSource::Let);
             }
 
             ExprKind::If(ref cond, ref then, ref opt_else) => {
-                self.ribs[ValueNS].push(Rib::new(NormalRibKind));
-                self.visit_expr(cond);
-                self.visit_block(then);
-                self.ribs[ValueNS].pop();
-
+                self.with_rib(ValueNS, NormalRibKind, |this| {
+                    this.visit_expr(cond);
+                    this.visit_block(then);
+                });
                 opt_else.as_ref().map(|expr| self.visit_expr(expr));
             }
 
             ExprKind::Loop(ref block, label) => self.resolve_labeled_block(label, expr.id, &block),
 
-            ExprKind::While(ref subexpression, ref block, label) => {
+            ExprKind::While(ref cond, ref block, label) => {
                 self.with_resolved_label(label, expr.id, |this| {
-                    this.ribs[ValueNS].push(Rib::new(NormalRibKind));
-                    this.visit_expr(subexpression);
-                    this.visit_block(block);
-                    this.ribs[ValueNS].pop();
+                    this.with_rib(ValueNS, NormalRibKind, |this| {
+                        this.visit_expr(cond);
+                        this.visit_block(block);
+                    })
                 });
             }
 
-            ExprKind::ForLoop(ref pattern, ref subexpression, ref block, label) => {
-                self.visit_expr(subexpression);
-                self.ribs[ValueNS].push(Rib::new(NormalRibKind));
-                self.resolve_pattern(pattern, PatternSource::For, &mut FxHashMap::default());
-
-                self.resolve_labeled_block(label, expr.id, block);
-
-                self.ribs[ValueNS].pop();
+            ExprKind::ForLoop(ref pat, ref iter_expr, ref block, label) => {
+                self.visit_expr(iter_expr);
+                self.with_rib(ValueNS, NormalRibKind, |this| {
+                    this.resolve_pattern_top(pat, PatternSource::For);
+                    this.resolve_labeled_block(label, expr.id, block);
+                });
             }
 
             ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block),
@@ -1860,30 +1944,22 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
             // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
             // resolve the arguments within the proper scopes so that usages of them inside the
             // closure are detected as upvars rather than normal closure arg usages.
-            ExprKind::Closure(
-                _, IsAsync::Async { .. }, _,
-                ref fn_decl, ref body, _span,
-            ) => {
-                let rib_kind = NormalRibKind;
-                self.ribs[ValueNS].push(Rib::new(rib_kind));
-                // Resolve arguments:
-                let mut bindings_list = FxHashMap::default();
-                for argument in &fn_decl.inputs {
-                    self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
-                    self.visit_ty(&argument.ty);
-                }
-                // No need to resolve return type-- the outer closure return type is
-                // FunctionRetTy::Default
-
-                // Now resolve the inner closure
-                {
-                    // No need to resolve arguments: the inner closure has none.
-                    // Resolve the return type:
-                    visit::walk_fn_ret_ty(self, &fn_decl.output);
-                    // Resolve the body
-                    self.visit_expr(body);
-                }
-                self.ribs[ValueNS].pop();
+            ExprKind::Closure(_, IsAsync::Async { .. }, _, ref fn_decl, ref body, _span) => {
+                self.with_rib(ValueNS, NormalRibKind, |this| {
+                    // Resolve arguments:
+                    this.resolve_params(&fn_decl.inputs);
+                    // No need to resolve return type --
+                    // the outer closure return type is `FunctionRetTy::Default`.
+
+                    // Now resolve the inner closure
+                    {
+                        // No need to resolve arguments: the inner closure has none.
+                        // Resolve the return type:
+                        visit::walk_fn_ret_ty(this, &fn_decl.output);
+                        // Resolve the body
+                        this.visit_expr(body);
+                    }
+                });
             }
             _ => {
                 visit::walk_expr(self, expr);
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 875ae449d94..9b92bb7698a 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -9,6 +9,7 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 
+#![feature(inner_deref)]
 #![feature(crate_visibility_modifier)]
 #![feature(label_break_value)]
 #![feature(mem_take)]
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index d1fd51a97f6..12c5ce12a0e 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -897,32 +897,23 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
         }
     }
 
-    fn process_var_decl_multi(&mut self, pats: &'l [P<ast::Pat>]) {
+    fn process_var_decl(&mut self, pat: &'l ast::Pat) {
+        // The pattern could declare multiple new vars,
+        // we must walk the pattern and collect them all.
         let mut collector = PathCollector::new();
-        for pattern in pats {
-            // collect paths from the arm's patterns
-            collector.visit_pat(&pattern);
-            self.visit_pat(&pattern);
-        }
+        collector.visit_pat(&pat);
+        self.visit_pat(&pat);
 
-        // process collected paths
-        for (id, ident, immut) in collector.collected_idents {
+        // Process collected paths.
+        for (id, ident, _) in collector.collected_idents {
             match self.save_ctxt.get_path_res(id) {
                 Res::Local(hir_id) => {
-                    let mut value = if immut == ast::Mutability::Immutable {
-                        self.span.snippet(ident.span)
-                    } else {
-                        "<mutable>".to_owned()
-                    };
                     let id = self.tcx.hir().hir_to_node_id(hir_id);
-                    let typ = self.save_ctxt
-                        .tables
-                        .node_type_opt(hir_id)
+                    let typ = self.save_ctxt.tables.node_type_opt(hir_id)
                         .map(|t| t.to_string())
                         .unwrap_or_default();
-                    value.push_str(": ");
-                    value.push_str(&typ);
 
+                    // Rust uses the id of the pattern for var lookups, so we'll use it too.
                     if !self.span.filter_generated(ident.span) {
                         let qualname = format!("{}${}", ident.to_string(), id);
                         let id = id_from_node_id(id, &self.save_ctxt);
@@ -972,61 +963,6 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
         }
     }
 
-    fn process_var_decl(&mut self, p: &'l ast::Pat, value: String) {
-        // The local could declare multiple new vars, we must walk the
-        // pattern and collect them all.
-        let mut collector = PathCollector::new();
-        collector.visit_pat(&p);
-        self.visit_pat(&p);
-
-        for (id, ident, immut) in collector.collected_idents {
-            let mut value = match immut {
-                ast::Mutability::Immutable => value.to_string(),
-                _ => String::new(),
-            };
-            let hir_id = self.tcx.hir().node_to_hir_id(id);
-            let typ = match self.save_ctxt.tables.node_type_opt(hir_id) {
-                Some(typ) => {
-                    let typ = typ.to_string();
-                    if !value.is_empty() {
-                        value.push_str(": ");
-                    }
-                    value.push_str(&typ);
-                    typ
-                }
-                None => String::new(),
-            };
-
-            // Rust uses the id of the pattern for var lookups, so we'll use it too.
-            if !self.span.filter_generated(ident.span) {
-                let qualname = format!("{}${}", ident.to_string(), id);
-                let id = id_from_node_id(id, &self.save_ctxt);
-                let span = self.span_from_span(ident.span);
-
-                self.dumper.dump_def(
-                    &Access {
-                        public: false,
-                        reachable: false,
-                    },
-                    Def {
-                        kind: DefKind::Local,
-                        id,
-                        span,
-                        name: ident.to_string(),
-                        qualname,
-                        value: typ,
-                        parent: None,
-                        children: vec![],
-                        decl_id: None,
-                        docs: String::new(),
-                        sig: None,
-                        attributes: vec![],
-                    },
-                );
-            }
-        }
-    }
-
     /// Extracts macro use and definition information from the AST node defined
     /// by the given NodeId, using the expansion information from the node's
     /// span.
@@ -1565,14 +1501,13 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
                 });
             }
             ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) => {
-                let value = self.span.snippet(subexpression.span);
-                self.process_var_decl(pattern, value);
+                self.process_var_decl(pattern);
                 debug!("for loop, walk sub-expr: {:?}", subexpression.node);
                 self.visit_expr(subexpression);
                 visit::walk_block(self, block);
             }
-            ast::ExprKind::Let(ref pats, ref scrutinee) => {
-                self.process_var_decl_multi(pats);
+            ast::ExprKind::Let(ref pat, ref scrutinee) => {
+                self.process_var_decl(pat);
                 self.visit_expr(scrutinee);
             }
             ast::ExprKind::Repeat(ref element, ref count) => {
@@ -1599,7 +1534,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
     }
 
     fn visit_arm(&mut self, arm: &'l ast::Arm) {
-        self.process_var_decl_multi(&arm.pats);
+        self.process_var_decl(&arm.pat);
         if let Some(expr) = &arm.guard {
             self.visit_expr(expr);
         }
@@ -1617,11 +1552,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
 
     fn visit_local(&mut self, l: &'l ast::Local) {
         self.process_macro_use(l.span);
-        let value = l.init
-            .as_ref()
-            .map(|i| self.span.snippet(i.span))
-            .unwrap_or_default();
-        self.process_var_decl(&l.pat, value);
+        self.process_var_decl(&l.pat);
 
         // Just walk the initialiser and type (don't want to walk the pattern again).
         walk_list!(self, visit_ty, &l.ty);
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 92ccd4f49f6..4bc098db686 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -1,5 +1,6 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![feature(nll)]
+#![feature(inner_deref)]
 
 #![recursion_limit="256"]
 
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 4cf0df308fb..24d0659391b 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -675,21 +675,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span);
             }
         } else {
-            let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
-            let fields_ending = if variant.fields.len() == 1 { "" } else { "s" };
-            struct_span_err!(tcx.sess, pat.span, E0023,
-                             "this pattern has {} field{}, but the corresponding {} has {} field{}",
-                             subpats.len(), subpats_ending, res.descr(),
-                             variant.fields.len(),  fields_ending)
-                .span_label(pat.span, format!("expected {} field{}, found {}",
-                                              variant.fields.len(), fields_ending, subpats.len()))
-                .emit();
+            // Pattern has wrong number of fields.
+            self.e0023(pat.span, res, &subpats, &variant.fields);
             on_error();
             return tcx.types.err;
         }
         pat_ty
     }
 
+    fn e0023(&self, pat_span: Span, res: Res, subpats: &'tcx [P<Pat>], fields: &[ty::FieldDef]) {
+        let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
+        let fields_ending = if fields.len() == 1 { "" } else { "s" };
+        let res_span = self.tcx.def_span(res.def_id());
+        struct_span_err!(
+            self.tcx.sess,
+            pat_span,
+            E0023,
+            "this pattern has {} field{}, but the corresponding {} has {} field{}",
+            subpats.len(),
+            subpats_ending,
+            res.descr(),
+            fields.len(),
+            fields_ending,
+        )
+            .span_label(pat_span, format!(
+                "expected {} field{}, found {}",
+                fields.len(),
+                fields_ending,
+                subpats.len(),
+            ))
+            .span_label(res_span, format!("{} defined here", res.descr()))
+            .emit();
+    }
+
     fn check_pat_tuple(
         &self,
         span: Span,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index d5c47a15948..0e046b8cd44 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -3554,7 +3554,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
             let ns_id = cx.derive_id(format!("{}.{}",
                                           variant.name.as_ref().unwrap(),
                                           ItemType::Variant.name_space()));
-            write!(w, "<span id=\"{id}\" class=\"variant small-section-header\">\
+            write!(w, "<div id=\"{id}\" class=\"variant small-section-header\">\
                        <a href=\"#{id}\" class=\"anchor field\"></a>\
                        <code id='{ns_id}'>{name}",
                    id = id,
@@ -3572,7 +3572,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
                     write!(w, ")")?;
                 }
             }
-            write!(w, "</code></span>")?;
+            write!(w, "</code></div>")?;
             document(w, cx, variant)?;
             document_non_exhaustive(w, variant)?;
 
@@ -3583,7 +3583,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
                 let variant_id = cx.derive_id(format!("{}.{}.fields",
                                                    ItemType::Variant,
                                                    variant.name.as_ref().unwrap()));
-                write!(w, "<span class='autohide sub-variant' id='{id}'>",
+                write!(w, "<div class='autohide sub-variant' id='{id}'>",
                        id = variant_id)?;
                 write!(w, "<h3>Fields of <b>{name}</b></h3><div>",
                        name = variant.name.as_ref().unwrap())?;
@@ -3609,7 +3609,7 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
                         document(w, cx, field)?;
                     }
                 }
-                write!(w, "</div></span>")?;
+                write!(w, "</div></div>")?;
             }
             render_stability_since(w, variant, it)?;
         }
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 309e5575ee4..83b0b78a0ce 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -547,6 +547,11 @@ if (!DOMTokenList.prototype.remove) {
                 results.sort(function(aaa, bbb) {
                     var a, b;
 
+                    // sort by exact match with regard to the last word (mismatch goes later)
+                    a = (aaa.word !== val);
+                    b = (bbb.word !== val);
+                    if (a !== b) { return a - b; }
+
                     // Sort by non levenshtein results and then levenshtein results by the distance
                     // (less changes required to match means higher rankings)
                     a = (aaa.lev);
@@ -558,11 +563,6 @@ if (!DOMTokenList.prototype.remove) {
                     b = (bbb.item.crate !== window.currentCrate);
                     if (a !== b) { return a - b; }
 
-                    // sort by exact match (mismatch goes later)
-                    a = (aaa.word !== valLower);
-                    b = (bbb.word !== valLower);
-                    if (a !== b) { return a - b; }
-
                     // sort by item name length (longer goes later)
                     a = aaa.word.length;
                     b = bbb.word.length;
@@ -1028,7 +1028,7 @@ if (!DOMTokenList.prototype.remove) {
                         if (lev > MAX_LEV_DISTANCE) {
                             continue;
                         } else if (lev > 0) {
-                            lev_add = 1;
+                            lev_add = lev / 10;
                         }
                     }
 
@@ -1099,10 +1099,6 @@ if (!DOMTokenList.prototype.remove) {
                     if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
                         if (index !== -1 && paths.length < 2) {
                             lev = 0;
-                        } else if (searchWords[j] === val) {
-                            // Small trick to fix when you're looking for a one letter type
-                            // and there are other short named types.
-                            lev = -1;
                         }
                         if (results[fullId] === undefined) {
                             results[fullId] = {
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index 157faa0af9b..18a46cf0b52 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -56,6 +56,9 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] }
 [target.x86_64-fortanix-unknown-sgx.dependencies]
 fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] }
 
+[target.wasm32-wasi.dependencies]
+wasi = { version = "0.7.0", features = ['rustc-dep-of-std', 'alloc'] }
+
 [build-dependencies]
 cc = "1.0"
 
diff --git a/src/libstd/sys/cloudabi/shims/process.rs b/src/libstd/sys/cloudabi/shims/process.rs
index e719b362cbf..03a59d6d7c8 100644
--- a/src/libstd/sys/cloudabi/shims/process.rs
+++ b/src/libstd/sys/cloudabi/shims/process.rs
@@ -4,14 +4,16 @@ use crate::io;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
 use crate::sys::{unsupported, Void};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
+
+pub use crate::ffi::OsString as EnvKey;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
 
 pub struct Command {
-    env: CommandEnv<DefaultEnvKey>,
+    env: CommandEnv,
 }
 
 // passed back to std::process with the pipes connected to the child, if any
@@ -37,7 +39,7 @@ impl Command {
 
     pub fn arg(&mut self, _arg: &OsStr) {}
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
diff --git a/src/libstd/sys/sgx/process.rs b/src/libstd/sys/sgx/process.rs
index a02e009d953..edf933d10e0 100644
--- a/src/libstd/sys/sgx/process.rs
+++ b/src/libstd/sys/sgx/process.rs
@@ -4,14 +4,16 @@ use crate::io;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
 use crate::sys::{unsupported, Void};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
+
+pub use crate::ffi::OsString as EnvKey;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
 
 pub struct Command {
-    env: CommandEnv<DefaultEnvKey>
+    env: CommandEnv,
 }
 
 // passed back to std::process with the pipes connected to the child, if any
@@ -38,7 +40,7 @@ impl Command {
     pub fn arg(&mut self, _arg: &OsStr) {
     }
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
diff --git a/src/libstd/sys/unix/process/mod.rs b/src/libstd/sys/unix/process/mod.rs
index bba4b21c462..056a20345f4 100644
--- a/src/libstd/sys/unix/process/mod.rs
+++ b/src/libstd/sys/unix/process/mod.rs
@@ -1,5 +1,6 @@
 pub use self::process_common::{Command, ExitStatus, ExitCode, Stdio, StdioPipes};
 pub use self::process_inner::Process;
+pub use crate::ffi::OsString as EnvKey;
 
 mod process_common;
 #[cfg(not(target_os = "fuchsia"))]
diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs
index 21fca23a8fe..72e66cc8e72 100644
--- a/src/libstd/sys/unix/process/process_common.rs
+++ b/src/libstd/sys/unix/process/process_common.rs
@@ -7,7 +7,7 @@ use crate::ptr;
 use crate::sys::fd::FileDesc;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::pipe::{self, AnonPipe};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
 use crate::collections::BTreeMap;
 
 use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
@@ -69,7 +69,7 @@ pub struct Command {
     program: CString,
     args: Vec<CString>,
     argv: Argv,
-    env: CommandEnv<DefaultEnvKey>,
+    env: CommandEnv,
 
     cwd: Option<CString>,
     uid: Option<uid_t>,
@@ -201,7 +201,7 @@ impl Command {
         self.stderr = Some(stderr);
     }
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
@@ -271,7 +271,7 @@ impl CStringArray {
     }
 }
 
-fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
+fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> CStringArray {
     let mut result = CStringArray::with_capacity(env.len());
     for (k, v) in env {
         let mut k: OsString = k.into();
diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs
index 02f377d55c9..fd6796ad22c 100644
--- a/src/libstd/sys/unix/time.rs
+++ b/src/libstd/sys/unix/time.rs
@@ -311,6 +311,7 @@ mod inner {
         pub fn actually_monotonic() -> bool {
             (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) ||
             (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) ||
+            cfg!(target_os = "fuchsia") ||
             false // last clause, used so `||` is always trailing above
         }
 
diff --git a/src/libstd/sys/vxworks/process/process_common.rs b/src/libstd/sys/vxworks/process/process_common.rs
index ba797354a73..509140229fd 100644
--- a/src/libstd/sys/vxworks/process/process_common.rs
+++ b/src/libstd/sys/vxworks/process/process_common.rs
@@ -7,11 +7,13 @@ use crate::ptr;
 use crate::sys::fd::FileDesc;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::pipe::{self, AnonPipe};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
 use crate::collections::BTreeMap;
 
 use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
 
+pub use crate::ffi::OsString as EnvKey;
+
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
@@ -37,7 +39,7 @@ pub struct Command {
     program: CString,
     args: Vec<CString>,
     argv: Argv,
-    env: CommandEnv<DefaultEnvKey>,
+    env: CommandEnv,
 
     cwd: Option<CString>,
     uid: Option<uid_t>,
@@ -170,7 +172,7 @@ impl Command {
         self.stderr = Some(stderr);
     }
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
@@ -240,7 +242,7 @@ impl CStringArray {
     }
 }
 
-fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
+fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> CStringArray {
     let mut result = CStringArray::with_capacity(env.len());
     for (k, v) in env {
         let mut k: OsString = k.into();
diff --git a/src/libstd/sys/wasi/args.rs b/src/libstd/sys/wasi/args.rs
index 8b4b354d9fc..3280c4990dc 100644
--- a/src/libstd/sys/wasi/args.rs
+++ b/src/libstd/sys/wasi/args.rs
@@ -1,11 +1,10 @@
-use crate::ffi::CStr;
-use crate::io;
-use crate::sys::cvt_wasi;
 use crate::ffi::OsString;
 use crate::marker::PhantomData;
 use crate::os::wasi::ffi::OsStringExt;
 use crate::vec;
 
+use ::wasi::wasi_unstable as wasi;
+
 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
 }
 
@@ -19,31 +18,17 @@ pub struct Args {
 
 /// Returns the command line arguments
 pub fn args() -> Args {
-    maybe_args().unwrap_or_else(|_| {
-        Args {
-            iter: Vec::new().into_iter(),
-            _dont_send_or_sync_me: PhantomData
-        }
-    })
-}
-
-fn maybe_args() -> io::Result<Args> {
-    unsafe {
-        let (mut argc, mut argv_buf_size) = (0, 0);
-        cvt_wasi(libc::__wasi_args_sizes_get(&mut argc, &mut argv_buf_size))?;
-
-        let mut argc = vec![core::ptr::null_mut::<libc::c_char>(); argc];
-        let mut argv_buf = vec![0; argv_buf_size];
-        cvt_wasi(libc::__wasi_args_get(argc.as_mut_ptr(), argv_buf.as_mut_ptr()))?;
-
-        let args = argc.into_iter()
-            .map(|ptr| CStr::from_ptr(ptr).to_bytes().to_vec())
-            .map(|bytes| OsString::from_vec(bytes))
-            .collect::<Vec<_>>();
-        Ok(Args {
-            iter: args.into_iter(),
-            _dont_send_or_sync_me: PhantomData,
-        })
+    let buf = wasi::args_sizes_get().and_then(|args_sizes| {
+        let mut buf = Vec::with_capacity(args_sizes.get_count());
+        wasi::args_get(args_sizes, |arg| {
+            let arg = OsString::from_vec(arg.to_vec());
+            buf.push(arg);
+        })?;
+        Ok(buf)
+    }).unwrap_or(vec![]);
+    Args {
+        iter: buf.into_iter(),
+        _dont_send_or_sync_me: PhantomData
     }
 }
 
diff --git a/src/libstd/sys/wasi/ext/fs.rs b/src/libstd/sys/wasi/ext/fs.rs
index 0ec4122f385..9fa4abfd171 100644
--- a/src/libstd/sys/wasi/ext/fs.rs
+++ b/src/libstd/sys/wasi/ext/fs.rs
@@ -8,6 +8,8 @@ use crate::os::wasi::ffi::OsStrExt;
 use crate::path::{Path, PathBuf};
 use crate::sys_common::{AsInner, AsInnerMut, FromInner};
 
+use ::wasi::wasi_unstable as wasi;
+
 /// WASI-specific extensions to [`File`].
 ///
 /// [`File`]: ../../../../std/fs/struct.File.html
@@ -336,16 +338,16 @@ pub trait FileTypeExt {
 
 impl FileTypeExt for fs::FileType {
     fn is_block_device(&self) -> bool {
-        self.as_inner().bits() == libc::__WASI_FILETYPE_BLOCK_DEVICE
+        self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
     }
     fn is_character_device(&self) -> bool {
-        self.as_inner().bits() == libc::__WASI_FILETYPE_CHARACTER_DEVICE
+        self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
     }
     fn is_socket_dgram(&self) -> bool {
-        self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_DGRAM
+        self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
     }
     fn is_socket_stream(&self) -> bool {
-        self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_STREAM
+        self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
     }
 }
 
diff --git a/src/libstd/sys/wasi/ext/io.rs b/src/libstd/sys/wasi/ext/io.rs
index 12afd1d42dc..f1839df3801 100644
--- a/src/libstd/sys/wasi/ext/io.rs
+++ b/src/libstd/sys/wasi/ext/io.rs
@@ -8,6 +8,8 @@ use crate::sys;
 use crate::net;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 
+use ::wasi::wasi_unstable as wasi;
+
 /// Raw file descriptors.
 pub type RawFd = u32;
 
@@ -125,18 +127,18 @@ impl IntoRawFd for fs::File {
 
 impl AsRawFd for io::Stdin {
     fn as_raw_fd(&self) -> RawFd {
-        libc::STDIN_FILENO as u32
+        wasi::STDIN_FD
     }
 }
 
 impl AsRawFd for io::Stdout {
     fn as_raw_fd(&self) -> RawFd {
-        libc::STDOUT_FILENO as u32
+        wasi::STDOUT_FD
     }
 }
 
 impl AsRawFd for io::Stderr {
     fn as_raw_fd(&self) -> RawFd {
-        libc::STDERR_FILENO as u32
+        wasi::STDERR_FD
     }
 }
diff --git a/src/libstd/sys/wasi/fd.rs b/src/libstd/sys/wasi/fd.rs
index 25692ec0868..5b7a8678b66 100644
--- a/src/libstd/sys/wasi/fd.rs
+++ b/src/libstd/sys/wasi/fd.rs
@@ -3,348 +3,248 @@
 use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
 use crate::mem;
 use crate::net::Shutdown;
-use crate::sys::cvt_wasi;
-use libc::{self, c_char, c_void};
+use super::err2io;
+use ::wasi::wasi_unstable as wasi;
 
 #[derive(Debug)]
 pub struct WasiFd {
-    fd: libc::__wasi_fd_t,
+    fd: wasi::Fd,
 }
 
-// FIXME: these should probably all be fancier structs, builders, enums, etc
-pub type LookupFlags = u32;
-pub type FdFlags = u16;
-pub type Advice = u8;
-pub type Rights = u64;
-pub type Oflags = u16;
-pub type DirCookie = u64;
-pub type Timestamp = u64;
-pub type FstFlags = u16;
-pub type RiFlags = u16;
-pub type RoFlags = u16;
-pub type SiFlags = u16;
-
-fn iovec(a: &mut [IoSliceMut<'_>]) -> (*const libc::__wasi_iovec_t, usize) {
+fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::IoVec] {
     assert_eq!(
         mem::size_of::<IoSliceMut<'_>>(),
-        mem::size_of::<libc::__wasi_iovec_t>()
+        mem::size_of::<wasi::IoVec>()
     );
     assert_eq!(
         mem::align_of::<IoSliceMut<'_>>(),
-        mem::align_of::<libc::__wasi_iovec_t>()
+        mem::align_of::<wasi::IoVec>()
     );
-    (a.as_ptr() as *const libc::__wasi_iovec_t, a.len())
+    /// SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout
+    unsafe { mem::transmute(a) }
 }
 
-fn ciovec(a: &[IoSlice<'_>]) -> (*const libc::__wasi_ciovec_t, usize) {
+fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::CIoVec] {
     assert_eq!(
         mem::size_of::<IoSlice<'_>>(),
-        mem::size_of::<libc::__wasi_ciovec_t>()
+        mem::size_of::<wasi::CIoVec>()
     );
     assert_eq!(
         mem::align_of::<IoSlice<'_>>(),
-        mem::align_of::<libc::__wasi_ciovec_t>()
+        mem::align_of::<wasi::CIoVec>()
     );
-    (a.as_ptr() as *const libc::__wasi_ciovec_t, a.len())
+    /// SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout
+    unsafe { mem::transmute(a) }
 }
 
 impl WasiFd {
-    pub unsafe fn from_raw(fd: libc::__wasi_fd_t) -> WasiFd {
+    pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd {
         WasiFd { fd }
     }
 
-    pub fn into_raw(self) -> libc::__wasi_fd_t {
+    pub fn into_raw(self) -> wasi::Fd {
         let ret = self.fd;
         mem::forget(self);
         ret
     }
 
-    pub fn as_raw(&self) -> libc::__wasi_fd_t {
+    pub fn as_raw(&self) -> wasi::Fd {
         self.fd
     }
 
     pub fn datasync(&self) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_datasync(self.fd) })
+        unsafe { wasi::fd_datasync(self.fd).map_err(err2io) }
     }
 
     pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
-        let mut read = 0;
-        let (ptr, len) = iovec(bufs);
-        cvt_wasi(unsafe { libc::__wasi_fd_pread(self.fd, ptr, len, offset, &mut read) })?;
-        Ok(read)
+        unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) }
     }
 
     pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
-        let mut read = 0;
-        let (ptr, len) = ciovec(bufs);
-        cvt_wasi(unsafe { libc::__wasi_fd_pwrite(self.fd, ptr, len, offset, &mut read) })?;
-        Ok(read)
+        unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) }
     }
 
     pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        let mut read = 0;
-        let (ptr, len) = iovec(bufs);
-        cvt_wasi(unsafe { libc::__wasi_fd_read(self.fd, ptr, len, &mut read) })?;
-        Ok(read)
+        unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) }
     }
 
     pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        let mut read = 0;
-        let (ptr, len) = ciovec(bufs);
-        cvt_wasi(unsafe { libc::__wasi_fd_write(self.fd, ptr, len, &mut read) })?;
-        Ok(read)
+        unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) }
     }
 
     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
         let (whence, offset) = match pos {
-            SeekFrom::Start(pos) => (libc::__WASI_WHENCE_SET, pos as i64),
-            SeekFrom::End(pos) => (libc::__WASI_WHENCE_END, pos),
-            SeekFrom::Current(pos) => (libc::__WASI_WHENCE_CUR, pos),
+            SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64),
+            SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
+            SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
         };
-        let mut pos = 0;
-        cvt_wasi(unsafe { libc::__wasi_fd_seek(self.fd, offset, whence, &mut pos) })?;
-        Ok(pos)
+        unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) }
     }
 
     pub fn tell(&self) -> io::Result<u64> {
-        let mut pos = 0;
-        cvt_wasi(unsafe { libc::__wasi_fd_tell(self.fd, &mut pos) })?;
-        Ok(pos)
+        unsafe { wasi::fd_tell(self.fd).map_err(err2io) }
     }
 
     // FIXME: __wasi_fd_fdstat_get
 
-    pub fn set_flags(&self, flags: FdFlags) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_flags(self.fd, flags) })
+    pub fn set_flags(&self, flags: wasi::FdFlags) -> io::Result<()> {
+        unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) }
     }
 
-    pub fn set_rights(&self, base: Rights, inheriting: Rights) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_rights(self.fd, base, inheriting) })
+    pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
+        unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) }
     }
 
     pub fn sync(&self) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_sync(self.fd) })
+        unsafe { wasi::fd_sync(self.fd).map_err(err2io) }
     }
 
-    pub fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_advise(self.fd, offset, len, advice as u8) })
+    pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
+        unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) }
     }
 
     pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_allocate(self.fd, offset, len) })
+        unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) }
     }
 
     pub fn create_directory(&self, path: &[u8]) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_create_directory(self.fd, path.as_ptr() as *const c_char, path.len())
-        })
+        unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) }
     }
 
     pub fn link(
         &self,
-        old_flags: LookupFlags,
+        old_flags: wasi::LookupFlags,
         old_path: &[u8],
         new_fd: &WasiFd,
         new_path: &[u8],
     ) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_link(
-                self.fd,
-                old_flags,
-                old_path.as_ptr() as *const c_char,
-                old_path.len(),
-                new_fd.fd,
-                new_path.as_ptr() as *const c_char,
-                new_path.len(),
-            )
-        })
+        unsafe {
+            wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path)
+                .map_err(err2io)
+        }
     }
 
     pub fn open(
         &self,
-        dirflags: LookupFlags,
+        dirflags: wasi::LookupFlags,
         path: &[u8],
-        oflags: Oflags,
-        fs_rights_base: Rights,
-        fs_rights_inheriting: Rights,
-        fs_flags: FdFlags,
+        oflags: wasi::OFlags,
+        fs_rights_base: wasi::Rights,
+        fs_rights_inheriting: wasi::Rights,
+        fs_flags: wasi::FdFlags,
     ) -> io::Result<WasiFd> {
         unsafe {
-            let mut fd = 0;
-            cvt_wasi(libc::__wasi_path_open(
+            wasi::path_open(
                 self.fd,
                 dirflags,
-                path.as_ptr() as *const c_char,
-                path.len(),
+                path,
                 oflags,
                 fs_rights_base,
                 fs_rights_inheriting,
                 fs_flags,
-                &mut fd,
-            ))?;
-            Ok(WasiFd::from_raw(fd))
+            ).map(|fd| WasiFd::from_raw(fd)).map_err(err2io)
         }
     }
 
-    pub fn readdir(&self, buf: &mut [u8], cookie: DirCookie) -> io::Result<usize> {
-        let mut used = 0;
-        cvt_wasi(unsafe {
-            libc::__wasi_fd_readdir(
-                self.fd,
-                buf.as_mut_ptr() as *mut c_void,
-                buf.len(),
-                cookie,
-                &mut used,
-            )
-        })?;
-        Ok(used)
+    pub fn readdir(&self, buf: &mut [u8], cookie: wasi::DirCookie) -> io::Result<usize> {
+        unsafe { wasi::fd_readdir(self.fd, buf, cookie).map_err(err2io) }
     }
 
     pub fn readlink(&self, path: &[u8], buf: &mut [u8]) -> io::Result<usize> {
-        let mut used = 0;
-        cvt_wasi(unsafe {
-            libc::__wasi_path_readlink(
-                self.fd,
-                path.as_ptr() as *const c_char,
-                path.len(),
-                buf.as_mut_ptr() as *mut c_char,
-                buf.len(),
-                &mut used,
-            )
-        })?;
-        Ok(used)
+        unsafe { wasi::path_readlink(self.fd, path, buf).map_err(err2io) }
     }
 
     pub fn rename(&self, old_path: &[u8], new_fd: &WasiFd, new_path: &[u8]) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_rename(
-                self.fd,
-                old_path.as_ptr() as *const c_char,
-                old_path.len(),
-                new_fd.fd,
-                new_path.as_ptr() as *const c_char,
-                new_path.len(),
-            )
-        })
+        unsafe {
+            wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io)
+        }
     }
 
-    pub fn filestat_get(&self, buf: *mut libc::__wasi_filestat_t) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_filestat_get(self.fd, buf) })
+    pub fn filestat_get(&self) -> io::Result<wasi::FileStat> {
+        unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) }
     }
 
     pub fn filestat_set_times(
         &self,
-        atim: Timestamp,
-        mtim: Timestamp,
-        fstflags: FstFlags,
+        atim: wasi::Timestamp,
+        mtim: wasi::Timestamp,
+        fstflags: wasi::FstFlags,
     ) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_times(self.fd, atim, mtim, fstflags) })
+        unsafe {
+            wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io)
+        }
     }
 
     pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
-        cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_size(self.fd, size) })
+        unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) }
     }
 
     pub fn path_filestat_get(
         &self,
-        flags: LookupFlags,
+        flags: wasi::LookupFlags,
         path: &[u8],
-        buf: *mut libc::__wasi_filestat_t,
-    ) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_filestat_get(
-                self.fd,
-                flags,
-                path.as_ptr() as *const c_char,
-                path.len(),
-                buf,
-            )
-        })
+    ) -> io::Result<wasi::FileStat> {
+        unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) }
     }
 
     pub fn path_filestat_set_times(
         &self,
-        flags: LookupFlags,
+        flags: wasi::LookupFlags,
         path: &[u8],
-        atim: Timestamp,
-        mtim: Timestamp,
-        fstflags: FstFlags,
+        atim: wasi::Timestamp,
+        mtim: wasi::Timestamp,
+        fstflags: wasi::FstFlags,
     ) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_filestat_set_times(
+        unsafe {
+            wasi::path_filestat_set_times(
                 self.fd,
                 flags,
-                path.as_ptr() as *const c_char,
-                path.len(),
+                path,
                 atim,
                 mtim,
                 fstflags,
-            )
-        })
+            ).map_err(err2io)
+        }
     }
 
     pub fn symlink(&self, old_path: &[u8], new_path: &[u8]) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_symlink(
-                old_path.as_ptr() as *const c_char,
-                old_path.len(),
-                self.fd,
-                new_path.as_ptr() as *const c_char,
-                new_path.len(),
-            )
-        })
+        unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) }
     }
 
     pub fn unlink_file(&self, path: &[u8]) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_unlink_file(self.fd, path.as_ptr() as *const c_char, path.len())
-        })
+        unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) }
     }
 
     pub fn remove_directory(&self, path: &[u8]) -> io::Result<()> {
-        cvt_wasi(unsafe {
-            libc::__wasi_path_remove_directory(self.fd, path.as_ptr() as *const c_char, path.len())
-        })
+        unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) }
     }
 
     pub fn sock_recv(
         &self,
         ri_data: &mut [IoSliceMut<'_>],
-        ri_flags: RiFlags,
-    ) -> io::Result<(usize, RoFlags)> {
-        let mut ro_datalen = 0;
-        let mut ro_flags = 0;
-        let (ptr, len) = iovec(ri_data);
-        cvt_wasi(unsafe {
-            libc::__wasi_sock_recv(self.fd, ptr, len, ri_flags, &mut ro_datalen, &mut ro_flags)
-        })?;
-        Ok((ro_datalen, ro_flags))
+        ri_flags: wasi::RiFlags,
+    ) -> io::Result<(usize, wasi::RoFlags)> {
+        unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) }
     }
 
-    pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: SiFlags) -> io::Result<usize> {
-        let mut so_datalen = 0;
-        let (ptr, len) = ciovec(si_data);
-        cvt_wasi(unsafe { libc::__wasi_sock_send(self.fd, ptr, len, si_flags, &mut so_datalen) })?;
-        Ok(so_datalen)
+    pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::SiFlags) -> io::Result<usize> {
+        unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) }
     }
 
     pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
         let how = match how {
-            Shutdown::Read => libc::__WASI_SHUT_RD,
-            Shutdown::Write => libc::__WASI_SHUT_WR,
-            Shutdown::Both => libc::__WASI_SHUT_WR | libc::__WASI_SHUT_RD,
+            Shutdown::Read => wasi::SHUT_RD,
+            Shutdown::Write => wasi::SHUT_WR,
+            Shutdown::Both => wasi::SHUT_WR | wasi::SHUT_RD,
         };
-        cvt_wasi(unsafe { libc::__wasi_sock_shutdown(self.fd, how) })?;
-        Ok(())
+        unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) }
     }
 }
 
 impl Drop for WasiFd {
     fn drop(&mut self) {
-        unsafe {
-            // FIXME: can we handle the return code here even though we can't on
-            // unix?
-            libc::__wasi_fd_close(self.fd);
-        }
+        // FIXME: can we handle the return code here even though we can't on
+        // unix?
+        let _ = unsafe { wasi::fd_close(self.fd) };
     }
 }
diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs
index 172c60385b3..4113f6a2e09 100644
--- a/src/libstd/sys/wasi/fs.rs
+++ b/src/libstd/sys/wasi/fs.rs
@@ -7,7 +7,7 @@ use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
 use crate::sync::Arc;
-use crate::sys::fd::{DirCookie, WasiFd};
+use crate::sys::fd::WasiFd;
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
 use crate::sys_common::FromInner;
@@ -15,18 +15,20 @@ use crate::sys_common::FromInner;
 pub use crate::sys_common::fs::copy;
 pub use crate::sys_common::fs::remove_dir_all;
 
+use ::wasi::wasi_unstable as wasi;
+
 pub struct File {
     fd: WasiFd,
 }
 
 #[derive(Clone)]
 pub struct FileAttr {
-    meta: libc::__wasi_filestat_t,
+    meta: wasi::FileStat,
 }
 
 pub struct ReadDir {
     inner: Arc<ReadDirInner>,
-    cookie: Option<DirCookie>,
+    cookie: Option<wasi::DirCookie>,
     buf: Vec<u8>,
     offset: usize,
     cap: usize,
@@ -38,7 +40,7 @@ struct ReadDirInner {
 }
 
 pub struct DirEntry {
-    meta: libc::__wasi_dirent_t,
+    meta: wasi::Dirent,
     name: Vec<u8>,
     inner: Arc<ReadDirInner>,
 }
@@ -47,11 +49,11 @@ pub struct DirEntry {
 pub struct OpenOptions {
     read: bool,
     write: bool,
-    dirflags: libc::__wasi_lookupflags_t,
-    fdflags: libc::__wasi_fdflags_t,
-    oflags: libc::__wasi_oflags_t,
-    rights_base: Option<libc::__wasi_rights_t>,
-    rights_inheriting: Option<libc::__wasi_rights_t>,
+    dirflags: wasi::LookupFlags,
+    fdflags: wasi::FdFlags,
+    oflags: wasi::OFlags,
+    rights_base: Option<wasi::Rights>,
+    rights_inheriting: Option<wasi::Rights>,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug)]
@@ -61,19 +63,13 @@ pub struct FilePermissions {
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct FileType {
-    bits: libc::__wasi_filetype_t,
+    bits: wasi::FileType,
 }
 
 #[derive(Debug)]
 pub struct DirBuilder {}
 
 impl FileAttr {
-    fn zero() -> FileAttr {
-        FileAttr {
-            meta: unsafe { mem::zeroed() },
-        }
-    }
-
     pub fn size(&self) -> u64 {
         self.meta.st_size
     }
@@ -101,7 +97,7 @@ impl FileAttr {
         Ok(SystemTime::from_wasi_timestamp(self.meta.st_ctim))
     }
 
-    pub fn as_wasi(&self) -> &libc::__wasi_filestat_t {
+    pub fn as_wasi(&self) -> &wasi::FileStat {
         &self.meta
     }
 }
@@ -118,18 +114,18 @@ impl FilePermissions {
 
 impl FileType {
     pub fn is_dir(&self) -> bool {
-        self.bits == libc::__WASI_FILETYPE_DIRECTORY
+        self.bits == wasi::FILETYPE_DIRECTORY
     }
 
     pub fn is_file(&self) -> bool {
-        self.bits == libc::__WASI_FILETYPE_REGULAR_FILE
+        self.bits == wasi::FILETYPE_REGULAR_FILE
     }
 
     pub fn is_symlink(&self) -> bool {
-        self.bits == libc::__WASI_FILETYPE_SYMBOLIC_LINK
+        self.bits == wasi::FILETYPE_SYMBOLIC_LINK
     }
 
-    pub fn bits(&self) -> libc::__wasi_filetype_t {
+    pub fn bits(&self) -> wasi::FileType {
         self.bits
     }
 }
@@ -173,7 +169,7 @@ impl Iterator for ReadDir {
             // must have been truncated at the end of the buffer, so reset our
             // offset so we can go back and reread into the buffer, picking up
             // where we last left off.
-            let dirent_size = mem::size_of::<libc::__wasi_dirent_t>();
+            let dirent_size = mem::size_of::<wasi::Dirent>();
             if data.len() < dirent_size {
                 assert!(self.cookie.is_some());
                 assert!(self.buf.len() >= dirent_size);
@@ -182,7 +178,7 @@ impl Iterator for ReadDir {
             }
             let (dirent, data) = data.split_at(dirent_size);
             let dirent =
-                unsafe { ptr::read_unaligned(dirent.as_ptr() as *const libc::__wasi_dirent_t) };
+                unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
 
             // If the file name was truncated, then we need to reinvoke
             // `readdir` so we truncate our buffer to start over and reread this
@@ -241,7 +237,7 @@ impl DirEntry {
         })
     }
 
-    pub fn ino(&self) -> libc::__wasi_inode_t {
+    pub fn ino(&self) -> wasi::Inode {
         self.meta.d_ino
     }
 }
@@ -249,7 +245,7 @@ impl DirEntry {
 impl OpenOptions {
     pub fn new() -> OpenOptions {
         let mut base = OpenOptions::default();
-        base.dirflags = libc::__WASI_LOOKUP_SYMLINK_FOLLOW;
+        base.dirflags = wasi::LOOKUP_SYMLINK_FOLLOW;
         return base;
     }
 
@@ -262,23 +258,23 @@ impl OpenOptions {
     }
 
     pub fn truncate(&mut self, truncate: bool) {
-        self.oflag(libc::__WASI_O_TRUNC, truncate);
+        self.oflag(wasi::O_TRUNC, truncate);
     }
 
     pub fn create(&mut self, create: bool) {
-        self.oflag(libc::__WASI_O_CREAT, create);
+        self.oflag(wasi::O_CREAT, create);
     }
 
     pub fn create_new(&mut self, create_new: bool) {
-        self.oflag(libc::__WASI_O_EXCL, create_new);
-        self.oflag(libc::__WASI_O_CREAT, create_new);
+        self.oflag(wasi::O_EXCL, create_new);
+        self.oflag(wasi::O_CREAT, create_new);
     }
 
     pub fn directory(&mut self, directory: bool) {
-        self.oflag(libc::__WASI_O_DIRECTORY, directory);
+        self.oflag(wasi::O_DIRECTORY, directory);
     }
 
-    fn oflag(&mut self, bit: libc::__wasi_oflags_t, set: bool) {
+    fn oflag(&mut self, bit: wasi::OFlags, set: bool) {
         if set {
             self.oflags |= bit;
         } else {
@@ -287,26 +283,26 @@ impl OpenOptions {
     }
 
     pub fn append(&mut self, set: bool) {
-        self.fdflag(libc::__WASI_FDFLAG_APPEND, set);
+        self.fdflag(wasi::FDFLAG_APPEND, set);
     }
 
     pub fn dsync(&mut self, set: bool) {
-        self.fdflag(libc::__WASI_FDFLAG_DSYNC, set);
+        self.fdflag(wasi::FDFLAG_DSYNC, set);
     }
 
     pub fn nonblock(&mut self, set: bool) {
-        self.fdflag(libc::__WASI_FDFLAG_NONBLOCK, set);
+        self.fdflag(wasi::FDFLAG_NONBLOCK, set);
     }
 
     pub fn rsync(&mut self, set: bool) {
-        self.fdflag(libc::__WASI_FDFLAG_RSYNC, set);
+        self.fdflag(wasi::FDFLAG_RSYNC, set);
     }
 
     pub fn sync(&mut self, set: bool) {
-        self.fdflag(libc::__WASI_FDFLAG_SYNC, set);
+        self.fdflag(wasi::FDFLAG_SYNC, set);
     }
 
-    fn fdflag(&mut self, bit: libc::__wasi_fdflags_t, set: bool) {
+    fn fdflag(&mut self, bit: wasi::FdFlags, set: bool) {
         if set {
             self.fdflags |= bit;
         } else {
@@ -314,15 +310,15 @@ impl OpenOptions {
         }
     }
 
-    pub fn fs_rights_base(&mut self, rights: libc::__wasi_rights_t) {
+    pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
         self.rights_base = Some(rights);
     }
 
-    pub fn fs_rights_inheriting(&mut self, rights: libc::__wasi_rights_t) {
+    pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
         self.rights_inheriting = Some(rights);
     }
 
-    fn rights_base(&self) -> libc::__wasi_rights_t {
+    fn rights_base(&self) -> wasi::Rights {
         if let Some(rights) = self.rights_base {
             return rights;
         }
@@ -334,52 +330,52 @@ impl OpenOptions {
         // based on that.
         let mut base = 0;
         if self.read {
-            base |= libc::__WASI_RIGHT_FD_READ;
-            base |= libc::__WASI_RIGHT_FD_READDIR;
+            base |= wasi::RIGHT_FD_READ;
+            base |= wasi::RIGHT_FD_READDIR;
         }
         if self.write {
-            base |= libc::__WASI_RIGHT_FD_WRITE;
-            base |= libc::__WASI_RIGHT_FD_DATASYNC;
-            base |= libc::__WASI_RIGHT_FD_ALLOCATE;
-            base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_SIZE;
+            base |= wasi::RIGHT_FD_WRITE;
+            base |= wasi::RIGHT_FD_DATASYNC;
+            base |= wasi::RIGHT_FD_ALLOCATE;
+            base |= wasi::RIGHT_FD_FILESTAT_SET_SIZE;
         }
 
         // FIXME: some of these should probably be read-only or write-only...
-        base |= libc::__WASI_RIGHT_FD_ADVISE;
-        base |= libc::__WASI_RIGHT_FD_FDSTAT_SET_FLAGS;
-        base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_TIMES;
-        base |= libc::__WASI_RIGHT_FD_SEEK;
-        base |= libc::__WASI_RIGHT_FD_SYNC;
-        base |= libc::__WASI_RIGHT_FD_TELL;
-        base |= libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY;
-        base |= libc::__WASI_RIGHT_PATH_CREATE_FILE;
-        base |= libc::__WASI_RIGHT_PATH_FILESTAT_GET;
-        base |= libc::__WASI_RIGHT_PATH_LINK_SOURCE;
-        base |= libc::__WASI_RIGHT_PATH_LINK_TARGET;
-        base |= libc::__WASI_RIGHT_PATH_OPEN;
-        base |= libc::__WASI_RIGHT_PATH_READLINK;
-        base |= libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY;
-        base |= libc::__WASI_RIGHT_PATH_RENAME_SOURCE;
-        base |= libc::__WASI_RIGHT_PATH_RENAME_TARGET;
-        base |= libc::__WASI_RIGHT_PATH_SYMLINK;
-        base |= libc::__WASI_RIGHT_PATH_UNLINK_FILE;
-        base |= libc::__WASI_RIGHT_POLL_FD_READWRITE;
+        base |= wasi::RIGHT_FD_ADVISE;
+        base |= wasi::RIGHT_FD_FDSTAT_SET_FLAGS;
+        base |= wasi::RIGHT_FD_FILESTAT_SET_TIMES;
+        base |= wasi::RIGHT_FD_SEEK;
+        base |= wasi::RIGHT_FD_SYNC;
+        base |= wasi::RIGHT_FD_TELL;
+        base |= wasi::RIGHT_PATH_CREATE_DIRECTORY;
+        base |= wasi::RIGHT_PATH_CREATE_FILE;
+        base |= wasi::RIGHT_PATH_FILESTAT_GET;
+        base |= wasi::RIGHT_PATH_LINK_SOURCE;
+        base |= wasi::RIGHT_PATH_LINK_TARGET;
+        base |= wasi::RIGHT_PATH_OPEN;
+        base |= wasi::RIGHT_PATH_READLINK;
+        base |= wasi::RIGHT_PATH_REMOVE_DIRECTORY;
+        base |= wasi::RIGHT_PATH_RENAME_SOURCE;
+        base |= wasi::RIGHT_PATH_RENAME_TARGET;
+        base |= wasi::RIGHT_PATH_SYMLINK;
+        base |= wasi::RIGHT_PATH_UNLINK_FILE;
+        base |= wasi::RIGHT_POLL_FD_READWRITE;
 
         return base;
     }
 
-    fn rights_inheriting(&self) -> libc::__wasi_rights_t {
+    fn rights_inheriting(&self) -> wasi::Rights {
         self.rights_inheriting.unwrap_or_else(|| self.rights_base())
     }
 
-    pub fn lookup_flags(&mut self, flags: libc::__wasi_lookupflags_t) {
+    pub fn lookup_flags(&mut self, flags: wasi::LookupFlags) {
         self.dirflags = flags;
     }
 }
 
 impl File {
     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
-        let (dir, file) = open_parent(path, libc::__WASI_RIGHT_PATH_OPEN)?;
+        let (dir, file) = open_parent(path, wasi::RIGHT_PATH_OPEN)?;
         open_at(&dir, &file, opts)
     }
 
@@ -388,14 +384,12 @@ impl File {
     }
 
     pub fn file_attr(&self) -> io::Result<FileAttr> {
-        let mut ret = FileAttr::zero();
-        self.fd.filestat_get(&mut ret.meta)?;
-        Ok(ret)
+        self.fd.filestat_get().map(|meta| FileAttr { meta })
     }
 
     pub fn metadata_at(
         &self,
-        flags: libc::__wasi_lookupflags_t,
+        flags: wasi::LookupFlags,
         path: &Path,
     ) -> io::Result<FileAttr> {
         metadata_at(&self.fd, flags, path)
@@ -477,7 +471,7 @@ impl DirBuilder {
     }
 
     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
-        let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY)?;
+        let (dir, file) = open_parent(p, wasi::RIGHT_PATH_CREATE_DIRECTORY)?;
         dir.create_directory(file.as_os_str().as_bytes())
     }
 }
@@ -508,13 +502,13 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
 }
 
 pub fn unlink(p: &Path) -> io::Result<()> {
-    let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_UNLINK_FILE)?;
+    let (dir, file) = open_parent(p, wasi::RIGHT_PATH_UNLINK_FILE)?;
     dir.unlink_file(file.as_os_str().as_bytes())
 }
 
 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
-    let (old, old_file) = open_parent(old, libc::__WASI_RIGHT_PATH_RENAME_SOURCE)?;
-    let (new, new_file) = open_parent(new, libc::__WASI_RIGHT_PATH_RENAME_TARGET)?;
+    let (old, old_file) = open_parent(old, wasi::RIGHT_PATH_RENAME_SOURCE)?;
+    let (new, new_file) = open_parent(new, wasi::RIGHT_PATH_RENAME_TARGET)?;
     old.rename(
         old_file.as_os_str().as_bytes(),
         &new,
@@ -529,12 +523,12 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
 }
 
 pub fn rmdir(p: &Path) -> io::Result<()> {
-    let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY)?;
+    let (dir, file) = open_parent(p, wasi::RIGHT_PATH_REMOVE_DIRECTORY)?;
     dir.remove_directory(file.as_os_str().as_bytes())
 }
 
 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
-    let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_READLINK)?;
+    let (dir, file) = open_parent(p, wasi::RIGHT_PATH_READLINK)?;
     read_link(&dir, &file)
 }
 
@@ -570,15 +564,15 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
 }
 
 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
-    let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_SYMLINK)?;
+    let (dst, dst_file) = open_parent(dst, wasi::RIGHT_PATH_SYMLINK)?;
     dst.symlink(src.as_os_str().as_bytes(), dst_file.as_os_str().as_bytes())
 }
 
 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
-    let (src, src_file) = open_parent(src, libc::__WASI_RIGHT_PATH_LINK_SOURCE)?;
-    let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_LINK_TARGET)?;
+    let (src, src_file) = open_parent(src, wasi::RIGHT_PATH_LINK_SOURCE)?;
+    let (dst, dst_file) = open_parent(dst, wasi::RIGHT_PATH_LINK_TARGET)?;
     src.link(
-        libc::__WASI_LOOKUP_SYMLINK_FOLLOW,
+        wasi::LOOKUP_SYMLINK_FOLLOW,
         src_file.as_os_str().as_bytes(),
         &dst,
         dst_file.as_os_str().as_bytes(),
@@ -586,23 +580,22 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
 }
 
 pub fn stat(p: &Path) -> io::Result<FileAttr> {
-    let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?;
-    metadata_at(&dir, libc::__WASI_LOOKUP_SYMLINK_FOLLOW, &file)
+    let (dir, file) = open_parent(p, wasi::RIGHT_PATH_FILESTAT_GET)?;
+    metadata_at(&dir, wasi::LOOKUP_SYMLINK_FOLLOW, &file)
 }
 
 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
-    let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?;
+    let (dir, file) = open_parent(p, wasi::RIGHT_PATH_FILESTAT_GET)?;
     metadata_at(&dir, 0, &file)
 }
 
 fn metadata_at(
     fd: &WasiFd,
-    flags: libc::__wasi_lookupflags_t,
+    flags: wasi::LookupFlags,
     path: &Path,
 ) -> io::Result<FileAttr> {
-    let mut ret = FileAttr::zero();
-    fd.path_filestat_get(flags, path.as_os_str().as_bytes(), &mut ret.meta)?;
-    Ok(ret)
+    fd.path_filestat_get(flags, path.as_os_str().as_bytes())
+        .map(|meta| FileAttr { meta })
 }
 
 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
@@ -652,12 +645,12 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
 /// to any preopened file descriptor.
 fn open_parent(
     p: &Path,
-    rights: libc::__wasi_rights_t,
+    rights: wasi::Rights,
 ) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
     let p = CString::new(p.as_os_str().as_bytes())?;
     unsafe {
         let mut ret = ptr::null();
-        let fd = __wasilibc_find_relpath(p.as_ptr(), rights, 0, &mut ret);
+        let fd = libc::__wasilibc_find_relpath(p.as_ptr(), rights, 0, &mut ret);
         if fd == -1 {
             let msg = format!(
                 "failed to find a preopened file descriptor \
@@ -677,15 +670,4 @@ fn open_parent(
 
         return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path));
     }
-
-    // FIXME(rust-lang/libc#1314) use the `libc` crate for this when the API
-    // there is published
-    extern "C" {
-        pub fn __wasilibc_find_relpath(
-            path: *const libc::c_char,
-            rights_base: libc::__wasi_rights_t,
-            rights_inheriting: libc::__wasi_rights_t,
-            relative_path: *mut *const libc::c_char,
-        ) -> libc::c_int;
-    }
 }
diff --git a/src/libstd/sys/wasi/io.rs b/src/libstd/sys/wasi/io.rs
index ffecca5d1b6..4be92faed30 100644
--- a/src/libstd/sys/wasi/io.rs
+++ b/src/libstd/sys/wasi/io.rs
@@ -1,11 +1,12 @@
 use crate::marker::PhantomData;
 use crate::slice;
 
-use libc::{__wasi_ciovec_t, __wasi_iovec_t, c_void};
+use ::wasi::wasi_unstable as wasi;
+use core::ffi::c_void;
 
 #[repr(transparent)]
 pub struct IoSlice<'a> {
-    vec: __wasi_ciovec_t,
+    vec: wasi::CIoVec,
     _p: PhantomData<&'a [u8]>,
 }
 
@@ -13,7 +14,7 @@ impl<'a> IoSlice<'a> {
     #[inline]
     pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
         IoSlice {
-            vec: __wasi_ciovec_t {
+            vec: wasi::CIoVec {
                 buf: buf.as_ptr() as *const c_void,
                 buf_len: buf.len(),
             },
@@ -43,7 +44,7 @@ impl<'a> IoSlice<'a> {
 
 #[repr(transparent)]
 pub struct IoSliceMut<'a> {
-    vec: __wasi_iovec_t,
+    vec: wasi::IoVec,
     _p: PhantomData<&'a mut [u8]>,
 }
 
@@ -51,7 +52,7 @@ impl<'a> IoSliceMut<'a> {
     #[inline]
     pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
         IoSliceMut {
-            vec: __wasi_iovec_t {
+            vec: wasi::IoVec {
                 buf: buf.as_mut_ptr() as *mut c_void,
                 buf_len: buf.len()
             },
diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs
index 57da81b41e7..517e3be9cb5 100644
--- a/src/libstd/sys/wasi/mod.rs
+++ b/src/libstd/sys/wasi/mod.rs
@@ -14,10 +14,10 @@
 //! compiling for wasm. That way it's a compile time error for something that's
 //! guaranteed to be a runtime error!
 
-use libc;
-use crate::io::{Error, ErrorKind};
+use crate::io as std_io;
 use crate::mem;
 use crate::os::raw::c_char;
+use ::wasi::wasi_unstable as wasi;
 
 pub mod alloc;
 pub mod args;
@@ -56,31 +56,42 @@ pub mod ext;
 pub fn init() {
 }
 
-pub fn unsupported<T>() -> crate::io::Result<T> {
+pub fn unsupported<T>() -> std_io::Result<T> {
     Err(unsupported_err())
 }
 
-pub fn unsupported_err() -> Error {
-    Error::new(ErrorKind::Other, "operation not supported on wasm yet")
+pub fn unsupported_err() -> std_io::Error {
+    std_io::Error::new(
+        std_io::ErrorKind::Other,
+        "operation not supported on wasm yet",
+    )
 }
 
-pub fn decode_error_kind(errno: i32) -> ErrorKind {
-    match errno as libc::c_int {
-        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
-        libc::ECONNRESET => ErrorKind::ConnectionReset,
-        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
-        libc::EPIPE => ErrorKind::BrokenPipe,
-        libc::ENOTCONN => ErrorKind::NotConnected,
-        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
-        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
-        libc::EADDRINUSE => ErrorKind::AddrInUse,
-        libc::ENOENT => ErrorKind::NotFound,
-        libc::EINTR => ErrorKind::Interrupted,
-        libc::EINVAL => ErrorKind::InvalidInput,
-        libc::ETIMEDOUT => ErrorKind::TimedOut,
-        libc::EEXIST => ErrorKind::AlreadyExists,
-        libc::EAGAIN => ErrorKind::WouldBlock,
-        _ => ErrorKind::Other,
+pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
+    use std_io::ErrorKind::*;
+    if errno > u16::max_value() as i32 || errno < 0 {
+        return Other;
+    }
+    let code = match wasi::Error::new(errno as u16) {
+        Some(code) => code,
+        None => return Other,
+    };
+    match code {
+        wasi::ECONNREFUSED => ConnectionRefused,
+        wasi::ECONNRESET => ConnectionReset,
+        wasi::EPERM | wasi::EACCES => PermissionDenied,
+        wasi::EPIPE => BrokenPipe,
+        wasi::ENOTCONN => NotConnected,
+        wasi::ECONNABORTED => ConnectionAborted,
+        wasi::EADDRNOTAVAIL => AddrNotAvailable,
+        wasi::EADDRINUSE => AddrInUse,
+        wasi::ENOENT => NotFound,
+        wasi::EINTR => Interrupted,
+        wasi::EINVAL => InvalidInput,
+        wasi::ETIMEDOUT => TimedOut,
+        wasi::EEXIST => AlreadyExists,
+        wasi::EAGAIN => WouldBlock,
+        _ => Other,
     }
 }
 
@@ -105,40 +116,16 @@ pub unsafe fn abort_internal() -> ! {
 pub fn hashmap_random_keys() -> (u64, u64) {
     let mut ret = (0u64, 0u64);
     unsafe {
-        let base = &mut ret as *mut (u64, u64) as *mut libc::c_void;
+        let base = &mut ret as *mut (u64, u64) as *mut core::ffi::c_void;
         let len = mem::size_of_val(&ret);
-        cvt_wasi(libc::__wasi_random_get(base, len)).unwrap();
-    }
-    return ret
-}
-
-#[doc(hidden)]
-pub trait IsMinusOne {
-    fn is_minus_one(&self) -> bool;
-}
-
-macro_rules! impl_is_minus_one {
-    ($($t:ident)*) => ($(impl IsMinusOne for $t {
-        fn is_minus_one(&self) -> bool {
-            *self == -1
+        let ret = wasi::raw::__wasi_random_get(base, len);
+        if ret != 0 {
+            panic!("__wasi_random_get failure")
         }
-    })*)
-}
-
-impl_is_minus_one! { i8 i16 i32 i64 isize }
-
-pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
-    if t.is_minus_one() {
-        Err(Error::last_os_error())
-    } else {
-        Ok(t)
     }
+    return ret
 }
 
-pub fn cvt_wasi(r: u16) -> crate::io::Result<()> {
-    if r != libc::__WASI_ESUCCESS {
-        Err(Error::from_raw_os_error(r as i32))
-    } else {
-        Ok(())
-    }
+fn err2io(err: wasi::Error) -> std_io::Error {
+    std_io::Error::from_raw_os_error(err.get() as i32)
 }
diff --git a/src/libstd/sys/wasi/os.rs b/src/libstd/sys/wasi/os.rs
index 822ea02a11b..feee8407825 100644
--- a/src/libstd/sys/wasi/os.rs
+++ b/src/libstd/sys/wasi/os.rs
@@ -9,7 +9,7 @@ use crate::path::{self, PathBuf};
 use crate::ptr;
 use crate::str;
 use crate::sys::memchr;
-use crate::sys::{cvt, unsupported, Void};
+use crate::sys::{unsupported, Void};
 use crate::vec;
 
 #[cfg(not(target_feature = "atomics"))]
@@ -28,16 +28,11 @@ pub fn errno() -> i32 {
 }
 
 pub fn error_string(errno: i32) -> String {
-    extern {
-        fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char,
-                      buflen: libc::size_t) -> libc::c_int;
-    }
-
     let mut buf = [0 as libc::c_char; 1024];
 
     let p = buf.as_mut_ptr();
     unsafe {
-        if strerror_r(errno as libc::c_int, p, buf.len()) < 0 {
+        if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 {
             panic!("strerror_r failure");
         }
         str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
@@ -89,7 +84,6 @@ impl StdError for JoinPathsError {
 pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
-
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
     _dont_send_or_sync_me: PhantomData<*mut ()>,
@@ -182,3 +176,26 @@ pub fn exit(code: i32) -> ! {
 pub fn getpid() -> u32 {
     panic!("unsupported");
 }
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() {
+        Err(io::Error::last_os_error())
+    } else {
+        Ok(t)
+    }
+}
diff --git a/src/libstd/sys/wasi/process.rs b/src/libstd/sys/wasi/process.rs
index 788b829f4ba..1c4d028b761 100644
--- a/src/libstd/sys/wasi/process.rs
+++ b/src/libstd/sys/wasi/process.rs
@@ -4,14 +4,16 @@ use crate::io;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
 use crate::sys::{unsupported, Void};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
+
+pub use crate::ffi::OsString as EnvKey;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
 
 pub struct Command {
-    env: CommandEnv<DefaultEnvKey>
+    env: CommandEnv
 }
 
 // passed back to std::process with the pipes connected to the child, if any
@@ -38,7 +40,7 @@ impl Command {
     pub fn arg(&mut self, _arg: &OsStr) {
     }
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs
index 2bf8d803c01..1d57b9922e5 100644
--- a/src/libstd/sys/wasi/stdio.rs
+++ b/src/libstd/sys/wasi/stdio.rs
@@ -1,8 +1,9 @@
 use crate::io::{self, IoSlice, IoSliceMut};
-use crate::libc;
 use crate::mem::ManuallyDrop;
 use crate::sys::fd::WasiFd;
 
+use ::wasi::wasi_unstable as wasi;
+
 pub struct Stdin;
 pub struct Stdout;
 pub struct Stderr;
@@ -17,7 +18,7 @@ impl Stdin {
     }
 
     pub fn read_vectored(&self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDIN_FILENO as u32) })
+        ManuallyDrop::new(unsafe { WasiFd::from_raw(wasi::STDIN_FD) })
             .read(data)
     }
 }
@@ -32,7 +33,7 @@ impl Stdout {
     }
 
     pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDOUT_FILENO as u32) })
+        ManuallyDrop::new(unsafe { WasiFd::from_raw(wasi::STDOUT_FD) })
             .write(data)
     }
 
@@ -51,7 +52,7 @@ impl Stderr {
     }
 
     pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDERR_FILENO as u32) })
+        ManuallyDrop::new(unsafe { WasiFd::from_raw(wasi::STDERR_FD) })
             .write(data)
     }
 
@@ -73,7 +74,7 @@ impl io::Write for Stderr {
 pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
 
 pub fn is_ebadf(err: &io::Error) -> bool {
-    err.raw_os_error() == Some(libc::__WASI_EBADF as i32)
+    err.raw_os_error() == Some(wasi::EBADF.get() as i32)
 }
 
 pub fn panic_output() -> Option<impl io::Write> {
diff --git a/src/libstd/sys/wasi/thread.rs b/src/libstd/sys/wasi/thread.rs
index 5e69e4d948f..28a504f1979 100644
--- a/src/libstd/sys/wasi/thread.rs
+++ b/src/libstd/sys/wasi/thread.rs
@@ -1,10 +1,10 @@
-use crate::cmp;
 use crate::ffi::CStr;
 use crate::io;
-use crate::sys::cvt;
+use crate::mem;
 use crate::sys::{unsupported, Void};
 use crate::time::Duration;
-use libc;
+
+use ::wasi::wasi_unstable as wasi;
 
 pub struct Thread(Void);
 
@@ -19,8 +19,8 @@ impl Thread {
     }
 
     pub fn yield_now() {
-        let ret = unsafe { libc::__wasi_sched_yield() };
-        debug_assert_eq!(ret, 0);
+        let ret = wasi::sched_yield();
+        debug_assert_eq!(ret, Ok(()));
     }
 
     pub fn set_name(_name: &CStr) {
@@ -28,19 +28,37 @@ impl Thread {
     }
 
     pub fn sleep(dur: Duration) {
-        let mut secs = dur.as_secs();
-        let mut nsecs = dur.subsec_nanos() as i32;
-
-        unsafe {
-            while secs > 0 || nsecs > 0 {
-                let mut ts = libc::timespec {
-                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
-                    tv_nsec: nsecs,
-                };
-                secs -= ts.tv_sec as u64;
-                cvt(libc::nanosleep(&ts, &mut ts)).unwrap();
-                nsecs = 0;
-            }
+        let nanos = dur.as_nanos();
+        assert!(nanos <= u64::max_value() as u128);
+
+        const CLOCK_ID: wasi::Userdata = 0x0123_45678;
+
+        let clock = wasi::raw::__wasi_subscription_u_clock_t {
+            identifier: CLOCK_ID,
+            clock_id: wasi::CLOCK_MONOTONIC,
+            timeout: nanos as u64,
+            precision: 0,
+            flags: 0,
+        };
+
+        let in_ = [wasi::Subscription {
+            userdata: 0,
+            type_: wasi::EVENTTYPE_CLOCK,
+            u: wasi::raw::__wasi_subscription_u { clock: clock },
+        }];
+        let (res, event) = unsafe {
+            let mut out: [wasi::Event; 1] = mem::zeroed();
+            let res = wasi::poll_oneoff(&in_, &mut out);
+            (res, out[0])
+        };
+        match (res, event) {
+            (Ok(1), wasi::Event {
+                userdata: CLOCK_ID,
+                error: 0,
+                type_: wasi::EVENTTYPE_CLOCK,
+                ..
+            }) => {}
+            _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
         }
     }
 
diff --git a/src/libstd/sys/wasi/time.rs b/src/libstd/sys/wasi/time.rs
index 3f14c80928c..4394a22f9c2 100644
--- a/src/libstd/sys/wasi/time.rs
+++ b/src/libstd/sys/wasi/time.rs
@@ -1,7 +1,5 @@
 use crate::time::Duration;
-use crate::mem;
-use crate::sys::cvt_wasi;
-use libc;
+use ::wasi::wasi_unstable as wasi;
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct Instant(Duration);
@@ -12,23 +10,19 @@ pub struct SystemTime(Duration);
 pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
 
 fn current_time(clock: u32) -> Duration {
-    unsafe {
-        let mut ts = mem::zeroed();
-        cvt_wasi(libc::__wasi_clock_time_get(
-            clock,
-            1, // precision... seems ignored though?
-            &mut ts,
-        )).unwrap();
-        Duration::new(
-            (ts / 1_000_000_000) as u64,
-            (ts % 1_000_000_000) as u32,
-        )
-    }
+    let ts = wasi::clock_time_get(
+        clock,
+        1, // precision... seems ignored though?
+    ).unwrap();
+    Duration::new(
+        (ts / 1_000_000_000) as u64,
+        (ts % 1_000_000_000) as u32,
+    )
 }
 
 impl Instant {
     pub fn now() -> Instant {
-        Instant(current_time(libc::__WASI_CLOCK_MONOTONIC))
+        Instant(current_time(wasi::CLOCK_MONOTONIC))
     }
 
     pub const fn zero() -> Instant {
@@ -54,10 +48,10 @@ impl Instant {
 
 impl SystemTime {
     pub fn now() -> SystemTime {
-        SystemTime(current_time(libc::__WASI_CLOCK_REALTIME))
+        SystemTime(current_time(wasi::CLOCK_REALTIME))
     }
 
-    pub fn from_wasi_timestamp(ts: libc::__wasi_timestamp_t) -> SystemTime {
+    pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime {
         SystemTime(Duration::from_nanos(ts))
     }
 
diff --git a/src/libstd/sys/wasm/process.rs b/src/libstd/sys/wasm/process.rs
index a02e009d953..edf933d10e0 100644
--- a/src/libstd/sys/wasm/process.rs
+++ b/src/libstd/sys/wasm/process.rs
@@ -4,14 +4,16 @@ use crate::io;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
 use crate::sys::{unsupported, Void};
-use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+use crate::sys_common::process::CommandEnv;
+
+pub use crate::ffi::OsString as EnvKey;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
 
 pub struct Command {
-    env: CommandEnv<DefaultEnvKey>
+    env: CommandEnv,
 }
 
 // passed back to std::process with the pipes connected to the child, if any
@@ -38,7 +40,7 @@ impl Command {
     pub fn arg(&mut self, _arg: &OsStr) {
     }
 
-    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
 
diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs
index 05e0ca67064..8658deb8546 100644
--- a/src/libstd/sys/windows/process.rs
+++ b/src/libstd/sys/windows/process.rs
@@ -19,7 +19,7 @@ use crate::sys::pipe::{self, AnonPipe};
 use crate::sys::stdio;
 use crate::sys::cvt;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
-use crate::sys_common::process::{CommandEnv, EnvKey};
+use crate::sys_common::process::CommandEnv;
 use crate::borrow::Borrow;
 
 use libc::{c_void, EXIT_SUCCESS, EXIT_FAILURE};
@@ -30,30 +30,28 @@ use libc::{c_void, EXIT_SUCCESS, EXIT_FAILURE};
 
 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
 #[doc(hidden)]
-pub struct WindowsEnvKey(OsString);
+pub struct EnvKey(OsString);
 
-impl From<OsString> for WindowsEnvKey {
+impl From<OsString> for EnvKey {
     fn from(k: OsString) -> Self {
         let mut buf = k.into_inner().into_inner();
         buf.make_ascii_uppercase();
-        WindowsEnvKey(FromInner::from_inner(FromInner::from_inner(buf)))
+        EnvKey(FromInner::from_inner(FromInner::from_inner(buf)))
     }
 }
 
-impl From<WindowsEnvKey> for OsString {
-    fn from(k: WindowsEnvKey) -> Self { k.0 }
+impl From<EnvKey> for OsString {
+    fn from(k: EnvKey) -> Self { k.0 }
 }
 
-impl Borrow<OsStr> for WindowsEnvKey {
+impl Borrow<OsStr> for EnvKey {
     fn borrow(&self) -> &OsStr { &self.0 }
 }
 
-impl AsRef<OsStr> for WindowsEnvKey {
+impl AsRef<OsStr> for EnvKey {
     fn as_ref(&self) -> &OsStr { &self.0 }
 }
 
-impl EnvKey for WindowsEnvKey {}
-
 
 fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
     if str.as_ref().encode_wide().any(|b| b == 0) {
@@ -66,7 +64,7 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
 pub struct Command {
     program: OsString,
     args: Vec<OsString>,
-    env: CommandEnv<WindowsEnvKey>,
+    env: CommandEnv,
     cwd: Option<OsString>,
     flags: u32,
     detach: bool, // not currently exposed in std::process
@@ -110,7 +108,7 @@ impl Command {
     pub fn arg(&mut self, arg: &OsStr) {
         self.args.push(arg.to_os_string())
     }
-    pub fn env_mut(&mut self) -> &mut CommandEnv<WindowsEnvKey> {
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
         &mut self.env
     }
     pub fn cwd(&mut self, dir: &OsStr) {
@@ -498,7 +496,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
     }
 }
 
-fn make_envp(maybe_env: Option<BTreeMap<WindowsEnvKey, OsString>>)
+fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>)
              -> io::Result<(*mut c_void, Vec<u16>)> {
     // On Windows we pass an "environment block" which is not a char**, but
     // rather a concatenation of null-terminated k=v\0 sequences, with a final
diff --git a/src/libstd/sys_common/process.rs b/src/libstd/sys_common/process.rs
index 4d40dec9724..bdf66fca359 100644
--- a/src/libstd/sys_common/process.rs
+++ b/src/libstd/sys_common/process.rs
@@ -1,47 +1,20 @@
 #![allow(dead_code)]
 #![unstable(feature = "process_internals", issue = "0")]
 
-use crate::ffi::{OsStr, OsString};
-use crate::env;
 use crate::collections::BTreeMap;
-use crate::borrow::Borrow;
-
-pub trait EnvKey:
-    From<OsString> + Into<OsString> +
-    Borrow<OsStr> + Borrow<Self> + AsRef<OsStr> +
-    Ord + Clone {}
-
-// Implement a case-sensitive environment variable key
-#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
-pub struct DefaultEnvKey(OsString);
-
-impl From<OsString> for DefaultEnvKey {
-    fn from(k: OsString) -> Self { DefaultEnvKey(k) }
-}
-
-impl From<DefaultEnvKey> for OsString {
-    fn from(k: DefaultEnvKey) -> Self { k.0 }
-}
-
-impl Borrow<OsStr> for DefaultEnvKey {
-    fn borrow(&self) -> &OsStr { &self.0 }
-}
-
-impl AsRef<OsStr> for DefaultEnvKey {
-    fn as_ref(&self) -> &OsStr { &self.0 }
-}
-
-impl EnvKey for DefaultEnvKey {}
+use crate::env;
+use crate::ffi::{OsStr, OsString};
+use crate::sys::process::EnvKey;
 
 // Stores a set of changes to an environment
 #[derive(Clone, Debug)]
-pub struct CommandEnv<K> {
+pub struct CommandEnv {
     clear: bool,
     saw_path: bool,
-    vars: BTreeMap<K, Option<OsString>>
+    vars: BTreeMap<EnvKey, Option<OsString>>
 }
 
-impl<K: EnvKey> Default for CommandEnv<K> {
+impl Default for CommandEnv {
     fn default() -> Self {
         CommandEnv {
             clear: false,
@@ -51,10 +24,10 @@ impl<K: EnvKey> Default for CommandEnv<K> {
     }
 }
 
-impl<K: EnvKey> CommandEnv<K> {
+impl CommandEnv {
     // Capture the current environment with these changes applied
-    pub fn capture(&self) -> BTreeMap<K, OsString> {
-        let mut result = BTreeMap::<K, OsString>::new();
+    pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
+        let mut result = BTreeMap::<EnvKey, OsString>::new();
         if !self.clear {
             for (k, v) in env::vars_os() {
                 result.insert(k.into(), v);
@@ -90,7 +63,7 @@ impl<K: EnvKey> CommandEnv<K> {
         !self.clear && self.vars.is_empty()
     }
 
-    pub fn capture_if_changed(&self) -> Option<BTreeMap<K, OsString>> {
+    pub fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> {
         if self.is_unchanged() {
             None
         } else {
@@ -103,6 +76,7 @@ impl<K: EnvKey> CommandEnv<K> {
         self.maybe_saw_path(&key);
         self.vars.insert(key.to_owned().into(), Some(value.to_owned()));
     }
+
     pub fn remove(&mut self, key: &OsStr) {
         self.maybe_saw_path(&key);
         if self.clear {
@@ -111,13 +85,16 @@ impl<K: EnvKey> CommandEnv<K> {
             self.vars.insert(key.to_owned().into(), None);
         }
     }
+
     pub fn clear(&mut self) {
         self.clear = true;
         self.vars.clear();
     }
+
     pub fn have_changed_path(&self) -> bool {
         self.saw_path || self.clear
     }
+
     fn maybe_saw_path(&mut self, key: &OsStr) {
         if !self.saw_path && key == "PATH" {
             self.saw_path = true;
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 6be00bcef45..c93e6d11ce7 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -561,29 +561,31 @@ impl Pat {
         }))
     }
 
-    pub fn walk<F>(&self, it: &mut F) -> bool
-    where
-        F: FnMut(&Pat) -> bool,
-    {
+    /// Walk top-down and call `it` in each place where a pattern occurs
+    /// starting with the root pattern `walk` is called on. If `it` returns
+    /// false then we will descend no further but siblings will be processed.
+    pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
         if !it(self) {
-            return false;
+            return;
         }
 
         match &self.node {
             PatKind::Ident(_, _, Some(p)) => p.walk(it),
-            PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk(it)),
+            PatKind::Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk(it)),
             PatKind::TupleStruct(_, s)
             | PatKind::Tuple(s)
             | PatKind::Slice(s)
-            | PatKind::Or(s) => s.iter().all(|p| p.walk(it)),
-            PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
+            | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
+            PatKind::Box(s)
+            | PatKind::Ref(s, _)
+            | PatKind::Paren(s) => s.walk(it),
             PatKind::Wild
             | PatKind::Rest
             | PatKind::Lit(_)
             | PatKind::Range(..)
             | PatKind::Ident(..)
             | PatKind::Path(..)
-            | PatKind::Mac(_) => true,
+            | PatKind::Mac(_) => {},
         }
     }
 
@@ -928,7 +930,7 @@ pub struct Local {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Arm {
     pub attrs: Vec<Attribute>,
-    pub pats: Vec<P<Pat>>,
+    pub pat: P<Pat>,
     pub guard: Option<P<Expr>>,
     pub body: P<Expr>,
     pub span: Span,
@@ -1146,12 +1148,9 @@ pub enum ExprKind {
     Cast(P<Expr>, P<Ty>),
     /// A type ascription (e.g., `42: usize`).
     Type(P<Expr>, P<Ty>),
-    /// A `let pats = expr` expression that is only semantically allowed in the condition
+    /// A `let pat = expr` expression that is only semantically allowed in the condition
     /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
-    ///
-    /// The `Vec<P<Pat>>` is for or-patterns at the top level.
-    /// FIXME(54883): Change this to just `P<Pat>`.
-    Let(Vec<P<Pat>>, P<Expr>),
+    Let(P<Pat>, P<Expr>),
     /// An `if` block, with an optional `else` block.
     ///
     /// `if expr { block } else { expr }`
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index e894fd17ff5..dc6cbfcf6ad 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -537,9 +537,9 @@ impl<'a> ExtCtxt<'a> {
         let err_expr = self.expr(sp, ast::ExprKind::Ret(Some(err_inner_expr)));
 
         // `Ok(__try_var) => __try_var`
-        let ok_arm = self.arm(sp, vec![ok_pat], binding_expr);
+        let ok_arm = self.arm(sp, ok_pat, binding_expr);
         // `Err(__try_var) => return Err(__try_var)`
-        let err_arm = self.arm(sp, vec![err_pat], err_expr);
+        let err_arm = self.arm(sp, err_pat, err_expr);
 
         // `match head { Ok() => ..., Err() => ... }`
         self.expr_match(sp, head, vec![ok_arm, err_arm])
@@ -606,10 +606,10 @@ impl<'a> ExtCtxt<'a> {
         self.pat_tuple_struct(span, path, vec![pat])
     }
 
-    pub fn arm(&self, span: Span, pats: Vec<P<ast::Pat>>, expr: P<ast::Expr>) -> ast::Arm {
+    pub fn arm(&self, span: Span, pat: P<ast::Pat>, expr: P<ast::Expr>) -> ast::Arm {
         ast::Arm {
             attrs: vec![],
-            pats,
+            pat,
             guard: None,
             body: expr,
             span,
@@ -618,7 +618,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn arm_unreachable(&self, span: Span) -> ast::Arm {
-        self.arm(span, vec![self.pat_wild(span)], self.expr_unreachable(span))
+        self.arm(span, self.pat_wild(span), self.expr_unreachable(span))
     }
 
     pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: Vec<ast::Arm>) -> P<Expr> {
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index e14ca4b06a0..6023c5149d0 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -402,14 +402,11 @@ pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_flat_map_arm<T: MutVisitor>(
-    mut arm: Arm,
-    vis: &mut T,
-) -> SmallVec<[Arm; 1]> {
-    let Arm { attrs, pats, guard, body, span, id } = &mut arm;
+pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[Arm; 1]> {
+    let Arm { attrs, pat, guard, body, span, id } = &mut arm;
     visit_attrs(attrs, vis);
     vis.visit_id(id);
-    visit_vec(pats, |pat| vis.visit_pat(pat));
+    vis.visit_pat(pat);
     visit_opt(guard, |guard| vis.visit_expr(guard));
     vis.visit_expr(body);
     vis.visit_span(span);
@@ -1132,8 +1129,8 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr,
             vis.visit_ty(ty);
         }
         ExprKind::AddrOf(_m, ohs) => vis.visit_expr(ohs),
-        ExprKind::Let(pats, scrutinee) => {
-            visit_vec(pats, |pat| vis.visit_pat(pat));
+        ExprKind::Let(pat, scrutinee) => {
+            vis.visit_pat(pat);
             vis.visit_expr(scrutinee);
         }
         ExprKind::If(cond, tr, fl) => {
diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs
index e502a08f4b2..3db9c899dba 100644
--- a/src/libsyntax/parse/parser/expr.rs
+++ b/src/libsyntax/parse/parser/expr.rs
@@ -1250,8 +1250,7 @@ impl<'a> Parser<'a> {
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         let lo = self.prev_span;
-        // FIXME(or_patterns, Centril | dlrobertson): use `parse_top_pat` instead.
-        let pat = self.parse_top_pat_unpack(GateOr::No)?;
+        let pat = self.parse_top_pat(GateOr::No)?;
         self.expect(&token::Eq)?;
         let expr = self.with_res(
             Restrictions::NO_STRUCT_LITERAL,
@@ -1393,8 +1392,7 @@ impl<'a> Parser<'a> {
     crate fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
         let lo = self.token.span;
-        // FIXME(or_patterns, Centril | dlrobertson): use `parse_top_pat` instead.
-        let pat = self.parse_top_pat_unpack(GateOr::No)?;
+        let pat = self.parse_top_pat(GateOr::No)?;
         let guard = if self.eat_keyword(kw::If) {
             Some(self.parse_expr()?)
         } else {
@@ -1455,7 +1453,7 @@ impl<'a> Parser<'a> {
 
         Ok(ast::Arm {
             attrs,
-            pats: pat, // FIXME(or_patterns, Centril | dlrobertson): this should just be `pat,`.
+            pat,
             guard,
             body: expr,
             span: lo.to(hi),
diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index 823f880337d..669f657160b 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -36,16 +36,6 @@ impl<'a> Parser<'a> {
         self.parse_pat_with_range_pat(true, expected)
     }
 
-    // FIXME(or_patterns, Centril | dlrobertson):
-    // remove this and use `parse_top_pat` everywhere it is used instead.
-    pub(super) fn parse_top_pat_unpack(&mut self, gate_or: GateOr) -> PResult<'a, Vec<P<Pat>>> {
-        self.parse_top_pat(gate_or)
-            .map(|pat| pat.and_then(|pat| match pat.node {
-                PatKind::Or(pats) => pats,
-                node => vec![self.mk_pat(pat.span, node)],
-            }))
-    }
-
     /// Entry point to the main pattern parser.
     /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
     pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P<Pat>> {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 37305055e62..6772bbce21b 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1710,11 +1710,11 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::Block(blk))
     }
 
-    /// Print a `let pats = scrutinee` expression.
-    crate fn print_let(&mut self, pats: &[P<ast::Pat>], scrutinee: &ast::Expr) {
+    /// Print a `let pat = scrutinee` expression.
+    crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
         self.s.word("let ");
 
-        self.print_pats(pats);
+        self.print_pat(pat);
         self.s.space();
 
         self.word_space("=");
@@ -2040,8 +2040,8 @@ impl<'a> State<'a> {
                 self.word_space(":");
                 self.print_type(ty);
             }
-            ast::ExprKind::Let(ref pats, ref scrutinee) => {
-                self.print_let(pats, scrutinee);
+            ast::ExprKind::Let(ref pat, ref scrutinee) => {
+                self.print_let(pat, scrutinee);
             }
             ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
                 self.print_if(test, blk, elseopt.as_ref().map(|e| &**e));
@@ -2451,21 +2451,16 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::Pat(pat))
     }
 
-    fn print_pats(&mut self, pats: &[P<ast::Pat>]) {
-        self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
-    }
-
     fn print_arm(&mut self, arm: &ast::Arm) {
-        // I have no idea why this check is necessary, but here it
-        // is :(
+        // I have no idea why this check is necessary, but here it is :(
         if arm.attrs.is_empty() {
             self.s.space();
         }
         self.cbox(INDENT_UNIT);
         self.ibox(0);
-        self.maybe_print_comment(arm.pats[0].span.lo());
+        self.maybe_print_comment(arm.pat.span.lo());
         self.print_outer_attributes(&arm.attrs);
-        self.print_pats(&arm.pats);
+        self.print_pat(&arm.pat);
         self.s.space();
         if let Some(ref e) = arm.guard {
             self.word_space("if");
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index ce1568316f8..421c327aa41 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -678,9 +678,8 @@ pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonCo
 }
 
 pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
-    for attr in expression.attrs.iter() {
-        visitor.visit_attribute(attr);
-    }
+    walk_list!(visitor, visit_attribute, expression.attrs.iter());
+
     match expression.node {
         ExprKind::Box(ref subexpression) => {
             visitor.visit_expr(subexpression)
@@ -719,8 +718,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(subexpression);
             visitor.visit_ty(typ)
         }
-        ExprKind::Let(ref pats, ref scrutinee) => {
-            walk_list!(visitor, visit_pat, pats);
+        ExprKind::Let(ref pat, ref scrutinee) => {
+            visitor.visit_pat(pat);
             visitor.visit_expr(scrutinee);
         }
         ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
@@ -831,10 +830,10 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
 }
 
 pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
-    walk_list!(visitor, visit_pat, &arm.pats);
-    if let Some(ref e) = &arm.guard {
-        visitor.visit_expr(e);
-    }
+    visitor.visit_pat(&arm.pat);
+    // NOTE(or_patterns; Centril | dlrobertson):
+    // If you change this, also change the hack in `lowering.rs`.
+    walk_list!(visitor, visit_expr, &arm.guard);
     visitor.visit_expr(&arm.body);
     walk_list!(visitor, visit_attribute, &arm.attrs);
 }
diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs
index 55687c3175b..1f4f5aa3709 100644
--- a/src/libsyntax_ext/deriving/cmp/ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/ord.rs
@@ -95,11 +95,9 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<
                     cx.expr_call_global(span, cmp_path.clone(), args)
                 };
 
-                let eq_arm = cx.arm(span,
-                                    vec![cx.pat_path(span, equals_path.clone())],
-                                    old);
+                let eq_arm = cx.arm(span, cx.pat_path(span, equals_path.clone()), old);
                 let neq_arm = cx.arm(span,
-                                     vec![cx.pat_ident(span, test_id)],
+                                     cx.pat_ident(span, test_id),
                                      cx.expr_ident(span, test_id));
 
                 cx.expr_match(span, new, vec![eq_arm, neq_arm])
diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
index 740b92a9b79..debdc300e64 100644
--- a/src/libsyntax_ext/deriving/cmp/partial_ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
@@ -160,10 +160,10 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
                 };
 
                 let eq_arm = cx.arm(span,
-                                    vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))],
+                                    cx.pat_some(span, cx.pat_path(span, ordering.clone())),
                                     old);
                 let neq_arm = cx.arm(span,
-                                    vec![cx.pat_ident(span, test_id)],
+                                    cx.pat_ident(span, test_id),
                                     cx.expr_ident(span, test_id));
 
                 cx.expr_match(span, new, vec![eq_arm, neq_arm])
diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs
index 9b6f8518de0..d3d604b7252 100644
--- a/src/libsyntax_ext/deriving/decodable.rs
+++ b/src/libsyntax_ext/deriving/decodable.rs
@@ -119,9 +119,7 @@ fn decodable_substructure(cx: &mut ExtCtxt<'_>,
                                                     vec![idx, exprdecode.clone()]))
                 });
 
-                arms.push(cx.arm(v_span,
-                                 vec![cx.pat_lit(v_span, cx.expr_usize(v_span, i))],
-                                 decoded));
+                arms.push(cx.arm(v_span, cx.pat_lit(v_span, cx.expr_usize(v_span, i)), decoded));
             }
 
             arms.push(cx.arm_unreachable(trait_span));
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 6fd763f5a91..893d89f06a1 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -1071,7 +1071,7 @@ impl<'a> MethodDef<'a> {
         for (arg_expr, pat) in self_args.iter().zip(patterns) {
             body = cx.expr_match(trait_.span,
                                  arg_expr.clone(),
-                                 vec![cx.arm(trait_.span, vec![pat.clone()], body)])
+                                 vec![cx.arm(trait_.span, pat.clone(), body)])
         }
 
         body
@@ -1311,7 +1311,7 @@ impl<'a> MethodDef<'a> {
                                                              nonself_args,
                                                              &substructure);
 
-                cx.arm(sp, vec![single_pat], arm_expr)
+                cx.arm(sp, single_pat, arm_expr)
             })
             .collect();
 
@@ -1337,7 +1337,7 @@ impl<'a> MethodDef<'a> {
             _ => None,
         };
         if let Some(arm) = default {
-            match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], arm));
+            match_arms.push(cx.arm(sp, cx.pat_wild(sp), arm));
         }
 
         // We will usually need the catch-all after matching the
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index ad275f421af..e29f12c50c5 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -716,7 +716,7 @@ impl<'a, 'b> Context<'a, 'b> {
         // But the nested match expression is proved to perform not as well
         // as series of let's; the first approach does.
         let pat = self.ecx.pat_tuple(self.fmtsp, pats);
-        let arm = self.ecx.arm(self.fmtsp, vec![pat], args_array);
+        let arm = self.ecx.arm(self.fmtsp, pat, args_array);
         let head = self.ecx.expr(self.fmtsp, ast::ExprKind::Tup(heads));
         let result = self.ecx.expr_match(self.fmtsp, head, vec![arm]);
 
diff --git a/src/test/rustdoc-js-std/vec-new.js b/src/test/rustdoc-js-std/vec-new.js
index e4daa5065d2..e1a3256876b 100644
--- a/src/test/rustdoc-js-std/vec-new.js
+++ b/src/test/rustdoc-js-std/vec-new.js
@@ -4,5 +4,6 @@ const EXPECTED = {
     'others': [
         { 'path': 'std::vec::Vec', 'name': 'new' },
         { 'path': 'std::vec::Vec', 'name': 'ne' },
+        { 'path': 'std::rc::Rc', 'name': 'ne' },
     ],
 };
diff --git a/src/test/rustdoc-js/exact-match.js b/src/test/rustdoc-js/exact-match.js
new file mode 100644
index 00000000000..b0a411bee58
--- /dev/null
+++ b/src/test/rustdoc-js/exact-match.js
@@ -0,0 +1,9 @@
+const QUERY = 'si::pc';
+
+const EXPECTED = {
+    'others': [
+        { 'path': 'exact_match::Si', 'name': 'pc' },
+        { 'path': 'exact_match::Psi', 'name': 'pc' },
+        { 'path': 'exact_match::Si', 'name': 'pa' },
+    ],
+};
diff --git a/src/test/rustdoc-js/exact-match.rs b/src/test/rustdoc-js/exact-match.rs
new file mode 100644
index 00000000000..2eacc0a3582
--- /dev/null
+++ b/src/test/rustdoc-js/exact-match.rs
@@ -0,0 +1,68 @@
+macro_rules! imp {
+    ($name:ident) => {
+        pub struct $name {
+            pub op: usize,
+        }
+        impl $name {
+            pub fn op() {}
+            pub fn cmp() {}
+            pub fn map() {}
+            pub fn pop() {}
+            pub fn ptr() {}
+            pub fn rpo() {}
+            pub fn drop() {}
+            pub fn copy() {}
+            pub fn zip() {}
+            pub fn sup() {}
+            pub fn pa() {}
+            pub fn pb() {}
+            pub fn pc() {}
+            pub fn pd() {}
+            pub fn pe() {}
+            pub fn pf() {}
+            pub fn pg() {}
+            pub fn ph() {}
+            pub fn pi() {}
+            pub fn pj() {}
+            pub fn pk() {}
+            pub fn pl() {}
+            pub fn pm() {}
+            pub fn pn() {}
+            pub fn po() {}
+        }
+    };
+    ($name:ident, $($names:ident),*) => {
+        imp!($name);
+        imp!($($names),*);
+    };
+}
+macro_rules! en {
+    ($name:ident) => {
+        pub enum $name {
+            Ptr,
+            Rp,
+            Rpo,
+            Pt,
+            Drop,
+            Dr,
+            Dro,
+            Sup,
+            Op,
+            Cmp,
+            Map,
+            Mp,
+        }
+    };
+    ($name:ident, $($names:ident),*) => {
+        en!($name);
+        en!($($names),*);
+    };
+}
+
+imp!(Ot, Foo, Cmp, Map, Loc, Lac, Toc, Si, Sig, Sip, Psy, Psi, Py, Pi, Pa, Pb, Pc, Pd);
+imp!(Pe, Pf, Pg, Ph, Pj, Pk, Pl, Pm, Pn, Po, Pq, Pr, Ps, Pt, Pu, Pv, Pw, Px, Pz, Ap, Bp, Cp);
+imp!(Dp, Ep, Fp, Gp, Hp, Ip, Jp, Kp, Lp, Mp, Np, Op, Pp, Qp, Rp, Sp, Tp, Up, Vp, Wp, Xp, Yp, Zp);
+
+en!(Place, Plac, Plae, Plce, Pace, Scalar, Scalr, Scaar, Sclar, Salar);
+
+pub struct P;
diff --git a/src/test/rustdoc-js/module-substring.js b/src/test/rustdoc-js/module-substring.js
new file mode 100644
index 00000000000..a446c39ebad
--- /dev/null
+++ b/src/test/rustdoc-js/module-substring.js
@@ -0,0 +1,9 @@
+const QUERY = 'ig::pc';
+
+const EXPECTED = {
+    'others': [
+        { 'path': 'module_substring::Sig', 'name': 'pc' },
+        { 'path': 'module_substring::Si', 'name': 'pc' },
+        { 'path': 'module_substring::Si', 'name': 'pa' },
+    ],
+};
diff --git a/src/test/rustdoc-js/module-substring.rs b/src/test/rustdoc-js/module-substring.rs
new file mode 100644
index 00000000000..2eacc0a3582
--- /dev/null
+++ b/src/test/rustdoc-js/module-substring.rs
@@ -0,0 +1,68 @@
+macro_rules! imp {
+    ($name:ident) => {
+        pub struct $name {
+            pub op: usize,
+        }
+        impl $name {
+            pub fn op() {}
+            pub fn cmp() {}
+            pub fn map() {}
+            pub fn pop() {}
+            pub fn ptr() {}
+            pub fn rpo() {}
+            pub fn drop() {}
+            pub fn copy() {}
+            pub fn zip() {}
+            pub fn sup() {}
+            pub fn pa() {}
+            pub fn pb() {}
+            pub fn pc() {}
+            pub fn pd() {}
+            pub fn pe() {}
+            pub fn pf() {}
+            pub fn pg() {}
+            pub fn ph() {}
+            pub fn pi() {}
+            pub fn pj() {}
+            pub fn pk() {}
+            pub fn pl() {}
+            pub fn pm() {}
+            pub fn pn() {}
+            pub fn po() {}
+        }
+    };
+    ($name:ident, $($names:ident),*) => {
+        imp!($name);
+        imp!($($names),*);
+    };
+}
+macro_rules! en {
+    ($name:ident) => {
+        pub enum $name {
+            Ptr,
+            Rp,
+            Rpo,
+            Pt,
+            Drop,
+            Dr,
+            Dro,
+            Sup,
+            Op,
+            Cmp,
+            Map,
+            Mp,
+        }
+    };
+    ($name:ident, $($names:ident),*) => {
+        en!($name);
+        en!($($names),*);
+    };
+}
+
+imp!(Ot, Foo, Cmp, Map, Loc, Lac, Toc, Si, Sig, Sip, Psy, Psi, Py, Pi, Pa, Pb, Pc, Pd);
+imp!(Pe, Pf, Pg, Ph, Pj, Pk, Pl, Pm, Pn, Po, Pq, Pr, Ps, Pt, Pu, Pv, Pw, Px, Pz, Ap, Bp, Cp);
+imp!(Dp, Ep, Fp, Gp, Hp, Ip, Jp, Kp, Lp, Mp, Np, Op, Pp, Qp, Rp, Sp, Tp, Up, Vp, Wp, Xp, Yp, Zp);
+
+en!(Place, Plac, Plae, Plce, Pace, Scalar, Scalr, Scaar, Sclar, Salar);
+
+pub struct P;
diff --git a/src/test/rustdoc-js/search-short-types.js b/src/test/rustdoc-js/search-short-types.js
index 0ebf4860cfa..d14672af71f 100644
--- a/src/test/rustdoc-js/search-short-types.js
+++ b/src/test/rustdoc-js/search-short-types.js
@@ -3,6 +3,8 @@ const QUERY = 'P';
 const EXPECTED = {
     'others': [
         { 'path': 'search_short_types', 'name': 'P' },
+        { 'path': 'search_short_types::VeryLongTypeName', 'name': 'p' },
         { 'path': 'search_short_types', 'name': 'Ap' },
+        { 'path': 'search_short_types::VeryLongTypeName', 'name': 'ap' },
     ],
 };
diff --git a/src/test/rustdoc-js/search-short-types.rs b/src/test/rustdoc-js/search-short-types.rs
index 2eacc0a3582..a4083f9a764 100644
--- a/src/test/rustdoc-js/search-short-types.rs
+++ b/src/test/rustdoc-js/search-short-types.rs
@@ -66,3 +66,9 @@ imp!(Dp, Ep, Fp, Gp, Hp, Ip, Jp, Kp, Lp, Mp, Np, Op, Pp, Qp, Rp, Sp, Tp, Up, Vp,
 en!(Place, Plac, Plae, Plce, Pace, Scalar, Scalr, Scaar, Sclar, Salar);
 
 pub struct P;
+
+pub struct VeryLongTypeName;
+impl VeryLongTypeName {
+    pub fn p() {}
+    pub fn ap() {}
+}
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index 09f58521e5d..b4c154e5d95 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -150,12 +150,12 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e)));
             },
             19 => {
-                let ps = vec![P(Pat {
+                let pat = P(Pat {
                     id: DUMMY_NODE_ID,
                     node: PatKind::Wild,
                     span: DUMMY_SP,
-                })];
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(ps.clone(), e)))
+                });
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e)))
             },
             _ => panic!("bad counter value in iter_exprs"),
         }
diff --git a/src/test/ui/error-codes/E0023.stderr b/src/test/ui/error-codes/E0023.stderr
index 1bc90a995fe..d04e494c258 100644
--- a/src/test/ui/error-codes/E0023.stderr
+++ b/src/test/ui/error-codes/E0023.stderr
@@ -1,18 +1,27 @@
 error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
   --> $DIR/E0023.rs:10:9
    |
+LL |     Apple(String, String),
+   |     --------------------- tuple variant defined here
+...
 LL |         Fruit::Apple(a) => {},
    |         ^^^^^^^^^^^^^^^ expected 2 fields, found 1
 
 error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
   --> $DIR/E0023.rs:11:9
    |
+LL |     Apple(String, String),
+   |     --------------------- tuple variant defined here
+...
 LL |         Fruit::Apple(a, b, c) => {},
    |         ^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
 error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 1 field
   --> $DIR/E0023.rs:12:9
    |
+LL |     Pear(u32),
+   |     --------- tuple variant defined here
+...
 LL |         Fruit::Pear(1, 2) => {},
    |         ^^^^^^^^^^^^^^^^^ expected 1 field, found 2
 
diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.rs b/src/test/ui/lint/issue-54538-unused-parens-lint.rs
index c442c39fe01..1d8cce52281 100644
--- a/src/test/ui/lint/issue-54538-unused-parens-lint.rs
+++ b/src/test/ui/lint/issue-54538-unused-parens-lint.rs
@@ -33,10 +33,8 @@ fn or_patterns_no_lint() {
     if let &mut (0 | 1) = &mut 0 {} // Same.
 
     fn foo((Ok(a) | Err(a)): Result<u8, u8>) {} // Doesn't parse if we remove parens for now.
-    //~^ ERROR identifier `a` is bound more than once
 
     let _ = |(Ok(a) | Err(a)): Result<u8, u8>| 1; // `|Ok(a) | Err(a)| 1` parses as bit-or.
-    //~^ ERROR identifier `a` is bound more than once
 }
 
 fn or_patterns_will_lint() {
diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr
index a3e0fb938b3..7d5e286416f 100644
--- a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr
+++ b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr
@@ -1,15 +1,3 @@
-error[E0416]: identifier `a` is bound more than once in the same pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:35:25
-   |
-LL |     fn foo((Ok(a) | Err(a)): Result<u8, u8>) {} // Doesn't parse if we remove parens for now.
-   |                         ^ used in a pattern more than once
-
-error[E0416]: identifier `a` is bound more than once in the same pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:38:27
-   |
-LL |     let _ = |(Ok(a) | Err(a)): Result<u8, u8>| 1; // `|Ok(a) | Err(a)| 1` parses as bit-or.
-   |                           ^ used in a pattern more than once
-
 warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
   --> $DIR/issue-54538-unused-parens-lint.rs:3:12
    |
@@ -61,113 +49,112 @@ LL |     let _ = |(a): u8| 0;
    |              ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:43:12
+  --> $DIR/issue-54538-unused-parens-lint.rs:41:12
    |
 LL |     if let (0 | 1) = 0 {}
    |            ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:44:13
+  --> $DIR/issue-54538-unused-parens-lint.rs:42:13
    |
 LL |     if let ((0 | 1),) = (0,) {}
    |             ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:45:13
+  --> $DIR/issue-54538-unused-parens-lint.rs:43:13
    |
 LL |     if let [(0 | 1)] = [0] {}
    |             ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:46:16
+  --> $DIR/issue-54538-unused-parens-lint.rs:44:16
    |
 LL |     if let 0 | (1 | 2) = 0 {}
    |                ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:48:15
+  --> $DIR/issue-54538-unused-parens-lint.rs:46:15
    |
 LL |     if let TS((0 | 1)) = TS(0) {}
    |               ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:50:20
+  --> $DIR/issue-54538-unused-parens-lint.rs:48:20
    |
 LL |     if let NS { f: (0 | 1) } = (NS { f: 0 }) {}
    |                    ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:60:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:58:9
    |
 LL |         (_) => {}
    |         ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:61:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:59:9
    |
 LL |         (y) => {}
    |         ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:62:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:60:9
    |
 LL |         (ref r) => {}
    |         ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:63:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:61:9
    |
 LL |         (e @ 1...2) => {}
    |         ^^^^^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:69:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:67:9
    |
 LL |         (e @ &(1...2)) => {}
    |         ^^^^^^^^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:70:10
+  --> $DIR/issue-54538-unused-parens-lint.rs:68:10
    |
 LL |         &(_) => {}
    |          ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:81:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:79:9
    |
 LL |         (_) => {}
    |         ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:82:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:80:9
    |
 LL |         (y) => {}
    |         ^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:83:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:81:9
    |
 LL |         (ref r) => {}
    |         ^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:84:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:82:9
    |
 LL |         (e @ 1..=2) => {}
    |         ^^^^^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:90:9
+  --> $DIR/issue-54538-unused-parens-lint.rs:88:9
    |
 LL |         (e @ &(1..=2)) => {}
    |         ^^^^^^^^^^^^^^ help: remove these parentheses
 
 error: unnecessary parentheses around pattern
-  --> $DIR/issue-54538-unused-parens-lint.rs:91:10
+  --> $DIR/issue-54538-unused-parens-lint.rs:89:10
    |
 LL |         &(_) => {}
    |          ^^^ help: remove these parentheses
 
-error: aborting due to 26 previous errors
+error: aborting due to 24 previous errors
 
-For more information about this error, try `rustc --explain E0416`.
diff --git a/src/test/ui/match/match-pattern-field-mismatch.stderr b/src/test/ui/match/match-pattern-field-mismatch.stderr
index 663cd2cd24d..c2298d6fbbf 100644
--- a/src/test/ui/match/match-pattern-field-mismatch.stderr
+++ b/src/test/ui/match/match-pattern-field-mismatch.stderr
@@ -1,6 +1,9 @@
 error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields
   --> $DIR/match-pattern-field-mismatch.rs:10:11
    |
+LL |         Rgb(usize, usize, usize),
+   |         ------------------------ tuple variant defined here
+...
 LL |           Color::Rgb(_, _) => { }
    |           ^^^^^^^^^^^^^^^^ expected 3 fields, found 2
 
diff --git a/src/test/ui/or-patterns/already-bound-name.rs b/src/test/ui/or-patterns/already-bound-name.rs
new file mode 100644
index 00000000000..3ebf59c6437
--- /dev/null
+++ b/src/test/ui/or-patterns/already-bound-name.rs
@@ -0,0 +1,46 @@
+// This test ensures that the "already bound identifier in a product pattern"
+// correctly accounts for or-patterns.
+
+#![feature(or_patterns)]
+//~^ WARN the feature `or_patterns` is incomplete
+
+enum E<T> { A(T, T), B(T) }
+
+use E::*;
+
+fn main() {
+    let (a, a) = (0, 1); // Standard duplication without an or-pattern.
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+
+    let (a, A(a, _) | B(a)) = (0, A(1, 2));
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+
+    let (A(a, _) | B(a), a) = (A(0, 1), 2);
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+
+    let A(a, a) | B(a) = A(0, 1);
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+
+    let B(a) | A(a, a) = A(0, 1);
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+
+    match A(0, 1) {
+        B(a) | A(a, a) => {} // Let's ensure `match` has no funny business.
+        //~^ ERROR identifier `a` is bound more than once in the same pattern
+    }
+
+    let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR mismatched types
+
+    let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR variable `a` is not bound in all patterns
+
+    let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+}
diff --git a/src/test/ui/or-patterns/already-bound-name.stderr b/src/test/ui/or-patterns/already-bound-name.stderr
new file mode 100644
index 00000000000..360699a8739
--- /dev/null
+++ b/src/test/ui/or-patterns/already-bound-name.stderr
@@ -0,0 +1,105 @@
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:12:13
+   |
+LL |     let (a, a) = (0, 1); // Standard duplication without an or-pattern.
+   |             ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:15:15
+   |
+LL |     let (a, A(a, _) | B(a)) = (0, A(1, 2));
+   |               ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:15:25
+   |
+LL |     let (a, A(a, _) | B(a)) = (0, A(1, 2));
+   |                         ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:19:26
+   |
+LL |     let (A(a, _) | B(a), a) = (A(0, 1), 2);
+   |                          ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:22:14
+   |
+LL |     let A(a, a) | B(a) = A(0, 1);
+   |              ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:25:21
+   |
+LL |     let B(a) | A(a, a) = A(0, 1);
+   |                     ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:29:21
+   |
+LL |         B(a) | A(a, a) => {} // Let's ensure `match` has no funny business.
+   |                     ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:33:36
+   |
+LL |     let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
+   |                                    ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:33:46
+   |
+LL |     let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
+   |                                              ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:38:36
+   |
+LL |     let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+   |                                    ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:38:46
+   |
+LL |     let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+   |                                              ^ used in a pattern more than once
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/already-bound-name.rs:38:9
+   |
+LL |     let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+   |         ^^^^ pattern doesn't bind `a`        - variable not in all patterns
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:43:49
+   |
+LL |     let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+   |                                                 ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/already-bound-name.rs:43:59
+   |
+LL |     let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
+   |                                                           ^ used in a pattern more than once
+
+warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
+  --> $DIR/already-bound-name.rs:4:12
+   |
+LL | #![feature(or_patterns)]
+   |            ^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/already-bound-name.rs:33:31
+   |
+LL |     let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
+   |                               ^ expected integer, found enum `E`
+   |
+   = note: expected type `{integer}`
+              found type `E<{integer}>`
+
+error: aborting due to 15 previous errors
+
+Some errors have detailed explanations: E0308, E0408, E0416.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/consistent-bindings.rs b/src/test/ui/or-patterns/consistent-bindings.rs
new file mode 100644
index 00000000000..0eb539dca4c
--- /dev/null
+++ b/src/test/ui/or-patterns/consistent-bindings.rs
@@ -0,0 +1,46 @@
+// Check that or-patterns with consistent bindings across arms are allowed.
+
+// edition:2018
+
+#![feature(or_patterns)]
+//~^ WARN the feature `or_patterns` is incomplete
+
+fn main() {
+    // One level:
+    let Ok(a) | Err(a) = Ok(0);
+    let Ok(ref a) | Err(ref a) = Ok(0);
+    let Ok(ref mut a) | Err(ref mut a) = Ok(0);
+
+    // Two levels:
+    enum Tri<S, T, U> { V1(S), V2(T), V3(U) }
+    use Tri::*;
+
+    let Ok((V1(a) | V2(a) | V3(a), b)) | Err(Ok((a, b)) | Err((a, b)))
+        : Result<_, Result<_, _>>
+        = Ok((V1(1), 1));
+
+    let Ok((V1(a) | V2(a) | V3(a), ref b)) | Err(Ok((a, ref b)) | Err((a, ref b)))
+        : Result<_, Result<_, _>>
+        = Ok((V1(1), 1));
+
+    // Three levels:
+    let (
+            a,
+            Err((ref mut b, ref c, d)) |
+            Ok((
+                Ok(
+                    V1((ref c, d)) |
+                    V2((d, ref c)) |
+                    V3((ref c, Ok((_, d)) | Err((d, _))))
+                ) |
+                Err((ref c, d)),
+                ref mut b
+            ))
+        ) =
+        (1, Ok((Ok(V3((1, Ok((1, 1))))), 1)));
+
+    // FIXME(or_patterns; Centril | dlrobertson): remove this line below and
+    // change this test to check-pass once MIR can handle or-patterns with bindings.
+    let () = 0;
+    //~^ ERROR mismatched types
+}
diff --git a/src/test/ui/or-patterns/consistent-bindings.stderr b/src/test/ui/or-patterns/consistent-bindings.stderr
new file mode 100644
index 00000000000..7f5e670c257
--- /dev/null
+++ b/src/test/ui/or-patterns/consistent-bindings.stderr
@@ -0,0 +1,20 @@
+warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
+  --> $DIR/consistent-bindings.rs:5:12
+   |
+LL | #![feature(or_patterns)]
+   |            ^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/consistent-bindings.rs:44:9
+   |
+LL |     let () = 0;
+   |         ^^ expected integer, found ()
+   |
+   = note: expected type `{integer}`
+              found type `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/inconsistent-modes.rs b/src/test/ui/or-patterns/inconsistent-modes.rs
new file mode 100644
index 00000000000..44836893ea2
--- /dev/null
+++ b/src/test/ui/or-patterns/inconsistent-modes.rs
@@ -0,0 +1,28 @@
+// This test ensures that or patterns require binding mode consistency across arms.
+
+#![feature(or_patterns)]
+//~^ WARN the feature `or_patterns` is incomplete
+
+#![allow(non_camel_case_types)]
+fn main() {
+    // One level:
+    let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0);
+    //~^ ERROR variable `a` is bound in inconsistent ways
+    let Ok(ref mut a) | Err(a): Result<u8, &mut u8> = Ok(0);
+    //~^ ERROR variable `a` is bound in inconsistent ways
+    let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0);
+    //~^ ERROR variable `a` is bound in inconsistent ways
+    //~| ERROR mismatched types
+    let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0));
+    //~^ ERROR variable `a` is bound in inconsistent ways
+    //~| ERROR variable `b` is bound in inconsistent ways
+    //~| ERROR mismatched types
+
+    // Two levels:
+    let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0);
+    //~^ ERROR variable `a` is bound in inconsistent ways
+
+    // Three levels:
+    let Ok([ Ok((Ok(ref a) | Err(a),)) | Err(a) ]) | Err(a) = Err(&1);
+    //~^ ERROR variable `a` is bound in inconsistent ways
+}
diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr
new file mode 100644
index 00000000000..0a36ed5548e
--- /dev/null
+++ b/src/test/ui/or-patterns/inconsistent-modes.stderr
@@ -0,0 +1,80 @@
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:9:25
+   |
+LL |     let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0);
+   |            -            ^ bound in different ways
+   |            |
+   |            first binding
+
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:11:29
+   |
+LL |     let Ok(ref mut a) | Err(a): Result<u8, &mut u8> = Ok(0);
+   |                    -        ^ bound in different ways
+   |                    |
+   |                    first binding
+
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:13:33
+   |
+LL |     let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0);
+   |                - first binding  ^ bound in different ways
+
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:16:39
+   |
+LL |     let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0));
+   |                 - first binding       ^ bound in different ways
+
+error[E0409]: variable `b` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:16:46
+   |
+LL |     let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0));
+   |                    - first binding           ^ bound in different ways
+
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:22:38
+   |
+LL |     let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0);
+   |                        -             ^ bound in different ways
+   |                        |
+   |                        first binding
+
+error[E0409]: variable `a` is bound in inconsistent ways within the same match arm
+  --> $DIR/inconsistent-modes.rs:26:34
+   |
+LL |     let Ok([ Ok((Ok(ref a) | Err(a),)) | Err(a) ]) | Err(a) = Err(&1);
+   |                         -        ^ bound in different ways
+   |                         |
+   |                         first binding
+
+warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
+  --> $DIR/inconsistent-modes.rs:3:12
+   |
+LL | #![feature(or_patterns)]
+   |            ^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/inconsistent-modes.rs:13:25
+   |
+LL |     let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0);
+   |                         ^^^^^^^^^ types differ in mutability
+   |
+   = note: expected type `&&u8`
+              found type `&mut &mut u8`
+
+error[E0308]: mismatched types
+  --> $DIR/inconsistent-modes.rs:16:31
+   |
+LL |     let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0));
+   |                               ^^^^^^^^^ types differ in mutability
+   |
+   = note: expected type `&{integer}`
+              found type `&mut _`
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0308, E0409.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/missing-bindings.rs b/src/test/ui/or-patterns/missing-bindings.rs
new file mode 100644
index 00000000000..b065028e7a5
--- /dev/null
+++ b/src/test/ui/or-patterns/missing-bindings.rs
@@ -0,0 +1,84 @@
+// This test ensures that or patterns do not allow missing bindings in any of the arms.
+
+// edition:2018
+
+#![feature(or_patterns)]
+//~^ WARN the feature `or_patterns` is incomplete
+
+#![allow(non_camel_case_types)]
+
+fn main() {}
+
+fn check_handling_of_paths() {
+    mod bar {
+        pub enum foo {
+            alpha,
+            beta,
+            charlie
+        }
+    }
+
+    use bar::foo::{alpha, charlie};
+    let alpha | beta | charlie = alpha; //~  ERROR variable `beta` is not bound in all patterns
+    match Some(alpha) {
+        Some(alpha | beta) => {} //~ ERROR variable `beta` is not bound in all patterns
+    }
+}
+
+fn check_misc_nesting() {
+    enum E<T> { A(T, T), B(T) }
+    use E::*;
+    enum Vars3<S, T, U> { V1(S), V2(T), V3(U) }
+    use Vars3::*;
+
+    // One level:
+    const X: E<u8> = B(0);
+    let A(a, _) | _ = X; //~ ERROR variable `a` is not bound in all patterns
+    let _ | B(a) = X; //~ ERROR variable `a` is not bound in all patterns
+    let A(..) | B(a) = X; //~ ERROR variable `a` is not bound in all patterns
+    let A(a, _) | B(_) = X; //~ ERROR variable `a` is not bound in all patterns
+    let A(_, a) | B(_) = X; //~ ERROR variable `a` is not bound in all patterns
+    let A(a, b) | B(a) = X; //~ ERROR variable `b` is not bound in all patterns
+
+    // Two levels:
+    const Y: E<E<u8>> = B(B(0));
+    let A(A(..) | B(_), _) | B(a) = Y; //~ ERROR variable `a` is not bound in all patterns
+    let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y;
+    //~^ ERROR variable `a` is not bound in all patterns
+    let A(A(a, b) | B(c), d) | B(e) = Y;
+    //~^ ERROR variable `a` is not bound in all patterns
+    //~| ERROR variable `a` is not bound in all patterns
+    //~| ERROR variable `b` is not bound in all patterns
+    //~| ERROR variable `b` is not bound in all patterns
+    //~| ERROR variable `c` is not bound in all patterns
+    //~| ERROR variable `c` is not bound in all patterns
+    //~| ERROR variable `d` is not bound in all patterns
+    //~| ERROR variable `e` is not bound in all patterns
+
+    // Three levels:
+    let (
+            V1(
+            //~^ ERROR variable `b` is not bound in all patterns
+            //~| ERROR variable `c` is not bound in all patterns
+                A(
+                    Ok(a) | Err(_), //~ ERROR variable `a` is not bound in all patterns
+                    _
+                ) |
+                B(Ok(a) | Err(a))
+            ) |
+            V2(
+                A(
+                    A(_, a) | //~ ERROR variable `b` is not bound in all patterns
+                    B(b), //~ ERROR variable `a` is not bound in all patterns
+                    _
+                ) |
+                B(_)
+                //~^ ERROR variable `a` is not bound in all patterns
+                //~| ERROR variable `b` is not bound in all patterns
+            ) |
+            V3(c),
+            //~^ ERROR variable `a` is not bound in all patterns
+        )
+        : (Vars3<E<Result<u8, u8>>, E<E<u8>>, u8>,)
+        = (V3(0),);
+}
diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr
new file mode 100644
index 00000000000..c73af7a42ee
--- /dev/null
+++ b/src/test/ui/or-patterns/missing-bindings.stderr
@@ -0,0 +1,250 @@
+error[E0408]: variable `beta` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:22:9
+   |
+LL |     let alpha | beta | charlie = alpha;
+   |         ^^^^^   ----   ^^^^^^^ pattern doesn't bind `beta`
+   |         |       |
+   |         |       variable not in all patterns
+   |         pattern doesn't bind `beta`
+
+error[E0408]: variable `beta` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:24:14
+   |
+LL |         Some(alpha | beta) => {}
+   |              ^^^^^   ---- variable not in all patterns
+   |              |
+   |              pattern doesn't bind `beta`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:36:19
+   |
+LL |     let A(a, _) | _ = X;
+   |           -       ^ pattern doesn't bind `a`
+   |           |
+   |           variable not in all patterns
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:37:9
+   |
+LL |     let _ | B(a) = X;
+   |         ^     - variable not in all patterns
+   |         |
+   |         pattern doesn't bind `a`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:38:9
+   |
+LL |     let A(..) | B(a) = X;
+   |         ^^^^^     - variable not in all patterns
+   |         |
+   |         pattern doesn't bind `a`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:39:19
+   |
+LL |     let A(a, _) | B(_) = X;
+   |           -       ^^^^ pattern doesn't bind `a`
+   |           |
+   |           variable not in all patterns
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:40:19
+   |
+LL |     let A(_, a) | B(_) = X;
+   |              -    ^^^^ pattern doesn't bind `a`
+   |              |
+   |              variable not in all patterns
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:41:19
+   |
+LL |     let A(a, b) | B(a) = X;
+   |              -    ^^^^ pattern doesn't bind `b`
+   |              |
+   |              variable not in all patterns
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:45:9
+   |
+LL |     let A(A(..) | B(_), _) | B(a) = Y;
+   |         ^^^^^^^^^^^^^^^^^^     - variable not in all patterns
+   |         |
+   |         pattern doesn't bind `a`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:46:11
+   |
+LL |     let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y;
+   |           ^^^^^     - variable not in all patterns
+   |           |
+   |           pattern doesn't bind `a`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:21
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |             -       ^^^^ pattern doesn't bind `a`
+   |             |
+   |             variable not in all patterns
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:21
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |                -    ^^^^ pattern doesn't bind `b`
+   |                |
+   |                variable not in all patterns
+
+error[E0408]: variable `c` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:11
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |           ^^^^^^^     - variable not in all patterns
+   |           |
+   |           pattern doesn't bind `c`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:32
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |             -                  ^^^^ pattern doesn't bind `a`
+   |             |
+   |             variable not in all patterns
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:32
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |                -               ^^^^ pattern doesn't bind `b`
+   |                |
+   |                variable not in all patterns
+
+error[E0408]: variable `c` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:32
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |                       -        ^^^^ pattern doesn't bind `c`
+   |                       |
+   |                       variable not in all patterns
+
+error[E0408]: variable `d` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:32
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |                           -    ^^^^ pattern doesn't bind `d`
+   |                           |
+   |                           variable not in all patterns
+
+error[E0408]: variable `e` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:48:9
+   |
+LL |     let A(A(a, b) | B(c), d) | B(e) = Y;
+   |         ^^^^^^^^^^^^^^^^^^^^     - variable not in all patterns
+   |         |
+   |         pattern doesn't bind `e`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:64:29
+   |
+LL |                     Ok(a) | Err(_),
+   |                        -    ^^^^^^ pattern doesn't bind `a`
+   |                        |
+   |                        variable not in all patterns
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:72:21
+   |
+LL |                     A(_, a) |
+   |                          - variable not in all patterns
+LL |                     B(b),
+   |                     ^^^^ pattern doesn't bind `a`
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:71:21
+   |
+LL |                     A(_, a) |
+   |                     ^^^^^^^ pattern doesn't bind `b`
+LL |                     B(b),
+   |                       - variable not in all patterns
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:75:17
+   |
+LL |                     A(_, a) |
+   |                          - variable not in all patterns
+...
+LL |                 B(_)
+   |                 ^^^^ pattern doesn't bind `a`
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:75:17
+   |
+LL |                     B(b),
+   |                       - variable not in all patterns
+...
+LL |                 B(_)
+   |                 ^^^^ pattern doesn't bind `b`
+
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:79:13
+   |
+LL |                 B(Ok(a) | Err(a))
+   |                               - variable not in all patterns
+...
+LL |                     A(_, a) |
+   |                          - variable not in all patterns
+...
+LL |             V3(c),
+   |             ^^^^^ pattern doesn't bind `a`
+
+error[E0408]: variable `b` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:60:13
+   |
+LL | /             V1(
+LL | |
+LL | |
+LL | |                 A(
+...  |
+LL | |                 B(Ok(a) | Err(a))
+LL | |             ) |
+   | |_____________^ pattern doesn't bind `b`
+...
+LL |                       B(b),
+   |                         - variable not in all patterns
+...
+LL |               V3(c),
+   |               ^^^^^ pattern doesn't bind `b`
+
+error[E0408]: variable `c` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:60:13
+   |
+LL | /             V1(
+LL | |
+LL | |
+LL | |                 A(
+...  |
+LL | |                 B(Ok(a) | Err(a))
+LL | |             ) |
+   | |_____________^ pattern doesn't bind `c`
+LL | /             V2(
+LL | |                 A(
+LL | |                     A(_, a) |
+LL | |                     B(b),
+...  |
+LL | |
+LL | |             ) |
+   | |_____________^ pattern doesn't bind `c`
+LL |               V3(c),
+   |                  - variable not in all patterns
+
+warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
+  --> $DIR/missing-bindings.rs:5:12
+   |
+LL | #![feature(or_patterns)]
+   |            ^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 26 previous errors
+
+For more information about this error, try `rustc --explain E0408`.
diff --git a/src/test/ui/pattern/pat-tuple-overfield.stderr b/src/test/ui/pattern/pat-tuple-overfield.stderr
index 0430897510b..e64b6efb08d 100644
--- a/src/test/ui/pattern/pat-tuple-overfield.stderr
+++ b/src/test/ui/pattern/pat-tuple-overfield.stderr
@@ -19,12 +19,18 @@ LL |         (1, 2, .., 3, 4) => {}
 error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
   --> $DIR/pat-tuple-overfield.rs:10:9
    |
+LL | struct S(u8, u8, u8);
+   | --------------------- tuple struct defined here
+...
 LL |         S(1, 2, 3, 4) => {}
    |         ^^^^^^^^^^^^^ expected 3 fields, found 4
 
 error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
   --> $DIR/pat-tuple-overfield.rs:12:9
    |
+LL | struct S(u8, u8, u8);
+   | --------------------- tuple struct defined here
+...
 LL |         S(1, 2, .., 3, 4) => {}
    |         ^^^^^^^^^^^^^^^^^ expected 3 fields, found 4
 
diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr
index a581f07496e..4fbc630644b 100644
--- a/src/test/ui/pattern/pattern-error-continue.stderr
+++ b/src/test/ui/pattern/pattern-error-continue.stderr
@@ -15,6 +15,9 @@ LL |         A::D(_) => (),
 error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
   --> $DIR/pattern-error-continue.rs:17:9
    |
+LL |     B(isize, isize),
+   |     --------------- tuple variant defined here
+...
 LL |         A::B(_, _, _) => (),
    |         ^^^^^^^^^^^^^ expected 2 fields, found 3
 
diff --git a/src/test/ui/shadowed/shadowing-in-the-same-pattern.stderr b/src/test/ui/shadowed/shadowing-in-the-same-pattern.stderr
index 71197efcaba..1c51653db6a 100644
--- a/src/test/ui/shadowed/shadowing-in-the-same-pattern.stderr
+++ b/src/test/ui/shadowed/shadowing-in-the-same-pattern.stderr
@@ -1,8 +1,8 @@
-error[E0416]: identifier `a` is bound more than once in the same pattern
+error[E0415]: identifier `a` is bound more than once in this parameter list
   --> $DIR/shadowing-in-the-same-pattern.rs:3:10
    |
 LL | fn f((a, a): (isize, isize)) {}
-   |          ^ used in a pattern more than once
+   |          ^ used as parameter more than once
 
 error[E0416]: identifier `a` is bound more than once in the same pattern
   --> $DIR/shadowing-in-the-same-pattern.rs:6:13
@@ -12,4 +12,5 @@ LL |     let (a, a) = (1, 1);
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0416`.
+Some errors have detailed explanations: E0415, E0416.
+For more information about an error, try `rustc --explain E0415`.
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index a564b991c40..1ed39f45d3e 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -15,6 +15,7 @@ const LICENSES: &[&str] = &[
     "Apache-2.0 / MIT",
     "MIT OR Apache-2.0",
     "Apache-2.0 OR MIT",
+    "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
     "MIT",
     "Unlicense/MIT",
     "Unlicense OR MIT",
@@ -172,6 +173,7 @@ const WHITELIST: &[Crate<'_>] = &[
     Crate("vcpkg"),
     Crate("version_check"),
     Crate("void"),
+    Crate("wasi"),
     Crate("winapi"),
     Crate("winapi-build"),
     Crate("winapi-i686-pc-windows-gnu"),