about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-02 06:08:15 +0000
committerbors <bors@rust-lang.org>2018-03-02 06:08:15 +0000
commit9cb18a92ad87852c4c5d6726b8fbe8c38deda4ba (patch)
treef00265de1b24b45be03ed60902101a9d81b121b8
parentddfbf2b0f4726f11bc8e857274ae1a0f8343a73f (diff)
parent2b3c815ddfefcabd3231ea3bb82443853f2e2254 (diff)
downloadrust-9cb18a92ad87852c4c5d6726b8fbe8c38deda4ba.tar.gz
rust-9cb18a92ad87852c4c5d6726b8fbe8c38deda4ba.zip
Auto merge of #48653 - Manishearth:rollup2, r=Manishearth
Another rollup

None
-rw-r--r--.travis.yml2
-rw-r--r--README.md2
-rw-r--r--src/bootstrap/builder.rs6
-rw-r--r--src/bootstrap/compile.rs3
-rw-r--r--src/bootstrap/test.rs87
-rw-r--r--src/ci/docker/x86_64-gnu-tools/Dockerfile1
-rwxr-xr-xsrc/ci/docker/x86_64-gnu-tools/checkregression.py40
-rwxr-xr-xsrc/ci/docker/x86_64-gnu-tools/checktools.sh47
-rw-r--r--src/librustc/hir/lowering.rs154
-rw-r--r--src/librustc_lint/builtin.rs1
-rw-r--r--src/librustc_resolve/diagnostics.rs24
-rw-r--r--src/librustc_resolve/lib.rs16
-rw-r--r--src/librustc_trans/attributes.rs2
-rw-r--r--src/librustc_trans/llvm_util.rs24
-rw-r--r--src/librustc_trans/mir/mod.rs60
-rw-r--r--src/librustc_typeck/astconv.rs9
-rw-r--r--src/librustc_typeck/diagnostics.rs2
-rw-r--r--src/libstd/io/stdio.rs42
-rw-r--r--src/libstd/thread/local.rs128
-rw-r--r--src/libstd/thread/mod.rs2
-rw-r--r--src/libsyntax/ast.rs4
-rw-r--r--src/libsyntax/codemap.rs19
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/libsyntax/fold.rs1
-rw-r--r--src/libsyntax/parse/parser.rs66
-rw-r--r--src/libsyntax/print/pprust.rs5
-rw-r--r--src/libsyntax/visit.rs3
-rw-r--r--src/test/parse-fail/pat-tuple-2.rs2
-rw-r--r--src/test/run-make/longjmp-across-rust/Makefile5
-rw-r--r--src/test/run-make/longjmp-across-rust/foo.c28
-rw-r--r--src/test/run-make/longjmp-across-rust/main.rs40
-rw-r--r--src/test/run-pass/issue-48508-aux.rs16
-rw-r--r--src/test/run-pass/issue-48508.rs28
-rw-r--r--src/test/run-pass/issue-48551.rs43
-rw-r--r--src/test/run-pass/pat-tuple-7.rs (renamed from src/test/parse-fail/pat-tuple-6.rs)4
-rw-r--r--src/test/run-pass/tls-init-on-init.rs10
-rw-r--r--src/test/run-pass/tls-try-with.rs2
-rw-r--r--src/test/ui/error-codes/E0404.rs5
-rw-r--r--src/test/ui/error-codes/E0404.stderr6
-rw-r--r--src/test/ui/feature-gate-pattern_parentheses.rs15
-rw-r--r--src/test/ui/feature-gate-pattern_parentheses.stderr11
-rwxr-xr-xsrc/tools/publish_toolstate.py69
42 files changed, 705 insertions, 336 deletions
diff --git a/.travis.yml b/.travis.yml
index 280da056995..0d8641e45ed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -188,7 +188,7 @@ matrix:
       script:
         MESSAGE_FILE=$(mktemp -t msg.XXXXXX);
         . src/ci/docker/x86_64-gnu-tools/repo.sh;
-        commit_toolstate_change "$MESSAGE_FILE" "$TRAVIS_BUILD_DIR/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "$MESSAGE_FILE"
+        commit_toolstate_change "$MESSAGE_FILE" "$TRAVIS_BUILD_DIR/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "$MESSAGE_FILE" "$TOOLSTATE_REPO_ACCESS_TOKEN";
 
 env:
   global:
diff --git a/README.md b/README.md
index fd35606ec0d..e78bbb82711 100644
--- a/README.md
+++ b/README.md
@@ -233,7 +233,7 @@ find out how various parts of the compiler work.
 [IRC]: https://en.wikipedia.org/wiki/Internet_Relay_Chat
 [#rust]: irc://irc.mozilla.org/rust
 [#rust-beginners]: irc://irc.mozilla.org/rust-beginners
-[rustc-guide]: https://rust-lang-nursery.github.io/rustc-guide/about-this-guide.html
+[rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/about-this-guide.html
 
 ## License
 [license]: #license
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index b5946b44e05..1df85323c41 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -231,7 +231,7 @@ pub struct ShouldRun<'a> {
     paths: BTreeSet<PathSet>,
 
     // If this is a default rule, this is an additional constraint placed on
-    // it's run. Generally something like compiler docs being enabled.
+    // its run. Generally something like compiler docs being enabled.
     is_really_default: bool,
 }
 
@@ -326,7 +326,9 @@ impl<'a> Builder<'a> {
                 test::RunPassPretty, test::RunFailPretty, test::RunPassValgrindPretty,
                 test::RunPassFullDepsPretty, test::RunFailFullDepsPretty, test::RunMake,
                 test::Crate, test::CrateLibrustc, test::Rustdoc, test::Linkcheck, test::Cargotest,
-                test::Cargo, test::Rls, test::Docs, test::ErrorIndex, test::Distcheck,
+                test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
+                test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
+                test::TheBook, test::UnstableBook,
                 test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme),
             Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
             Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 2c9f0ddb6c3..408d63be6c6 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -629,6 +629,8 @@ impl Step for CodegenBackend {
             .arg(build.src.join("src/librustc_trans/Cargo.toml"));
         rustc_cargo_env(build, &mut cargo);
 
+        let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
+
         match &*self.backend {
             "llvm" | "emscripten" => {
                 // Build LLVM for our target. This will implicitly build the
@@ -642,7 +644,6 @@ impl Step for CodegenBackend {
                     features.push_str(" emscripten");
                 }
 
-                let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
                 println!("Building stage{} codegen artifacts ({} -> {}, {})",
                          compiler.stage, &compiler.host, target, self.backend);
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index bd8c36a296c..b27ddfdbc5e 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -78,15 +78,17 @@ fn try_run(build: &Build, cmd: &mut Command) -> bool {
     true
 }
 
-fn try_run_quiet(build: &Build, cmd: &mut Command) {
+fn try_run_quiet(build: &Build, cmd: &mut Command) -> bool {
     if !build.fail_fast {
         if !build.try_run_quiet(cmd) {
             let mut failures = build.delayed_failures.borrow_mut();
             failures.push(format!("{:?}", cmd));
+            return false;
         }
     } else {
         build.run_quiet(cmd);
     }
+    true
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -994,23 +996,19 @@ impl Step for Compiletest {
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Docs {
+struct DocTest {
     compiler: Compiler,
+    path: &'static str,
+    name: &'static str,
+    is_ext_doc: bool,
 }
 
-impl Step for Docs {
+impl Step for DocTest {
     type Output = ();
-    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun) -> ShouldRun {
-        run.path("src/doc")
-    }
-
-    fn make_run(run: RunConfig) {
-        run.builder.ensure(Docs {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
-        });
+        run.never()
     }
 
     /// Run `rustdoc --test` for all documentation in `src/doc`.
@@ -1026,9 +1024,9 @@ impl Step for Docs {
 
         // Do a breadth-first traversal of the `src/doc` directory and just run
         // tests for all files that end in `*.md`
-        let mut stack = vec![build.src.join("src/doc")];
+        let mut stack = vec![build.src.join(self.path)];
         let _time = util::timeit();
-        let _folder = build.fold_output(|| "test_docs");
+        let _folder = build.fold_output(|| format!("test_{}", self.name));
 
         while let Some(p) = stack.pop() {
             if p.is_dir() {
@@ -1046,11 +1044,64 @@ impl Step for Docs {
                 continue;
             }
 
-            markdown_test(builder, compiler, &p);
+            let test_result = markdown_test(builder, compiler, &p);
+            if self.is_ext_doc {
+                let toolstate = if test_result {
+                    ToolState::TestPass
+                } else {
+                    ToolState::TestFail
+                };
+                build.save_toolstate(self.name, toolstate);
+            }
         }
     }
 }
 
+macro_rules! test_book {
+    ($($name:ident, $path:expr, $book_name:expr, default=$default:expr;)+) => {
+        $(
+            #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+            pub struct $name {
+                compiler: Compiler,
+            }
+
+            impl Step for $name {
+                type Output = ();
+                const DEFAULT: bool = $default;
+                const ONLY_HOSTS: bool = true;
+
+                fn should_run(run: ShouldRun) -> ShouldRun {
+                    run.path($path)
+                }
+
+                fn make_run(run: RunConfig) {
+                    run.builder.ensure($name {
+                        compiler: run.builder.compiler(run.builder.top_stage, run.host),
+                    });
+                }
+
+                fn run(self, builder: &Builder) {
+                    builder.ensure(DocTest {
+                        compiler: self.compiler,
+                        path: $path,
+                        name: $book_name,
+                        is_ext_doc: !$default,
+                    });
+                }
+            }
+        )+
+    }
+}
+
+test_book!(
+    Nomicon, "src/doc/nomicon", "nomicon", default=false;
+    Reference, "src/doc/reference", "reference", default=false;
+    RustdocBook, "src/doc/rustdoc", "rustdoc", default=true;
+    RustByExample, "src/doc/rust-by-example", "rust-by-example", default=false;
+    TheBook, "src/doc/book", "book", default=false;
+    UnstableBook, "src/doc/unstable-book", "unstable-book", default=true;
+);
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct ErrorIndex {
     compiler: Compiler,
@@ -1101,13 +1152,13 @@ impl Step for ErrorIndex {
     }
 }
 
-fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) {
+fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) -> bool {
     let build = builder.build;
     let mut file = t!(File::open(markdown));
     let mut contents = String::new();
     t!(file.read_to_string(&mut contents));
     if !contents.contains("```") {
-        return;
+        return true;
     }
 
     println!("doc tests for: {}", markdown.display());
@@ -1121,9 +1172,9 @@ fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) {
     cmd.arg("--test-args").arg(test_args);
 
     if build.config.quiet_tests {
-        try_run_quiet(build, &mut cmd);
+        try_run_quiet(build, &mut cmd)
     } else {
-        try_run(build, &mut cmd);
+        try_run(build, &mut cmd)
     }
 }
 
diff --git a/src/ci/docker/x86_64-gnu-tools/Dockerfile b/src/ci/docker/x86_64-gnu-tools/Dockerfile
index 8975d419d20..bab9145cbcb 100644
--- a/src/ci/docker/x86_64-gnu-tools/Dockerfile
+++ b/src/ci/docker/x86_64-gnu-tools/Dockerfile
@@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
+COPY x86_64-gnu-tools/checkregression.py /tmp/
 COPY x86_64-gnu-tools/checktools.sh /tmp/
 COPY x86_64-gnu-tools/repo.sh /tmp/
 
diff --git a/src/ci/docker/x86_64-gnu-tools/checkregression.py b/src/ci/docker/x86_64-gnu-tools/checkregression.py
new file mode 100755
index 00000000000..df791d12645
--- /dev/null
+++ b/src/ci/docker/x86_64-gnu-tools/checkregression.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+import sys
+import json
+
+if __name__ == '__main__':
+    os_name = sys.argv[1]
+    toolstate_file = sys.argv[2]
+    current_state = sys.argv[3]
+
+    with open(toolstate_file, 'r') as f:
+        toolstate = json.load(f)
+    with open(current_state, 'r') as f:
+        current = json.load(f)
+
+    regressed = False
+    for cur in current:
+        tool = cur['tool']
+        state = cur[os_name]
+        new_state = toolstate.get(tool, '')
+        if new_state < state:
+            print(
+                'Error: The state of "{}" has regressed from "{}" to "{}"'
+                .format(tool, state, new_state)
+            )
+            regressed = True
+
+    if regressed:
+        sys.exit(1)
diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh
index 61bb5a84d21..da89aa9423b 100755
--- a/src/ci/docker/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh
@@ -17,11 +17,18 @@ TOOLSTATE_FILE="$(realpath $2)"
 OS="$3"
 COMMIT="$(git rev-parse HEAD)"
 CHANGED_FILES="$(git diff --name-status HEAD HEAD^)"
+SIX_WEEK_CYCLE="$(( ($(date +%s) / 604800 - 3) % 6 ))"
+# ^ 1970 Jan 1st is a Thursday, and our release dates are also on Thursdays,
+#   thus we could divide by 604800 (7 days in seconds) directly.
 
 touch "$TOOLSTATE_FILE"
 
 set +e
 python2.7 "$X_PY" test --no-fail-fast \
+    src/doc/book \
+    src/doc/nomicon \
+    src/doc/reference \
+    src/doc/rust-by-example \
     src/tools/rls \
     src/tools/rustfmt \
     src/tools/miri \
@@ -29,27 +36,38 @@ python2.7 "$X_PY" test --no-fail-fast \
 set -e
 
 cat "$TOOLSTATE_FILE"
+echo
 
-# If this PR is intended to update one of these tools, do not let the build pass
-# when they do not test-pass.
-for TOOL in rls rustfmt clippy; do
-    echo "Verifying status of $TOOL..."
-    if echo "$CHANGED_FILES" | grep -q "^M[[:blank:]]src/tools/$TOOL$"; then
-        echo "This PR updated 'src/tools/$TOOL', verifying if status is 'test-pass'..."
-        if grep -vq '"'"$TOOL"'[^"]*":"test-pass"' "$TOOLSTATE_FILE"; then
+verify_status() {
+    echo "Verifying status of $1..."
+    if echo "$CHANGED_FILES" | grep -q "^M[[:blank:]]$2$"; then
+        echo "This PR updated '$2', verifying if status is 'test-pass'..."
+        if grep -vq '"'"$1"'":"test-pass"' "$TOOLSTATE_FILE"; then
             echo
-            echo "⚠️ We detected that this PR updated '$TOOL', but its tests failed."
+            echo "⚠️ We detected that this PR updated '$1', but its tests failed."
             echo
-            echo "If you do intend to update '$TOOL', please check the error messages above and"
+            echo "If you do intend to update '$1', please check the error messages above and"
             echo "commit another update."
             echo
-            echo "If you do NOT intend to update '$TOOL', please ensure you did not accidentally"
-            echo "change the submodule at 'src/tools/$TOOL'. You may ask your reviewer for the"
+            echo "If you do NOT intend to update '$1', please ensure you did not accidentally"
+            echo "change the submodule at '$2'. You may ask your reviewer for the"
             echo "proper steps."
             exit 3
         fi
     fi
-done
+}
+
+# If this PR is intended to update one of these tools, do not let the build pass
+# when they do not test-pass.
+
+verify_status book src/doc/book
+verify_status nomicon src/doc/nomicon
+verify_status reference src/doc/reference
+verify_status rust-by-example src/doc/rust-by-example
+verify_status rls src/tool/rls
+verify_status rustfmt src/tool/rustfmt
+verify_status clippy-driver src/tool/clippy
+#verify_status miri src/tool/miri
 
 if [ "$RUST_RELEASE_CHANNEL" = nightly -a -n "${TOOLSTATE_REPO_ACCESS_TOKEN+is_set}" ]; then
     . "$(dirname $0)/repo.sh"
@@ -59,6 +77,11 @@ if [ "$RUST_RELEASE_CHANNEL" = nightly -a -n "${TOOLSTATE_REPO_ACCESS_TOKEN+is_s
         sed -i "1 a\\
 $COMMIT\t$(cat "$TOOLSTATE_FILE")
 " "history/$OS.tsv"
+    # if we are at the last week in the 6-week release cycle, reject any kind of regression.
+    if [ $SIX_WEEK_CYCLE -eq 5 ]; then
+        python2.7 "$(dirname $0)/checkregression.py" \
+            "$OS" "$TOOLSTATE_FILE" "rust-toolstate/_data/latest.json"
+    fi
     rm -f "$MESSAGE_FILE"
     exit 0
 fi
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index b15ee53e225..a41ac5bceed 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2472,86 +2472,88 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
-        let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
+        let node = match p.node {
+            PatKind::Wild => hir::PatKind::Wild,
+            PatKind::Ident(ref binding_mode, pth1, ref sub) => {
+                match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
+                    // `None` can occur in body-less function signatures
+                    def @ None | def @ Some(Def::Local(_)) => {
+                        let canonical_id = match def {
+                            Some(Def::Local(id)) => id,
+                            _ => p.id
+                        };
+                        hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
+                                                canonical_id,
+                                                respan(pth1.span, pth1.node.name),
+                                                sub.as_ref().map(|x| self.lower_pat(x)))
+                    }
+                    Some(def) => {
+                        hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
+                            span: pth1.span,
+                            def,
+                            segments: hir_vec![
+                                hir::PathSegment::from_name(pth1.node.name)
+                            ],
+                        })))
+                    }
+                }
+            }
+            PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
+            PatKind::TupleStruct(ref path, ref pats, ddpos) => {
+                let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
+                                                ImplTraitContext::Disallowed);
+                hir::PatKind::TupleStruct(qpath,
+                                            pats.iter().map(|x| self.lower_pat(x)).collect(),
+                                            ddpos)
+            }
+            PatKind::Path(ref qself, ref path) => {
+                hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
+                                                    ImplTraitContext::Disallowed))
+            }
+            PatKind::Struct(ref path, ref fields, etc) => {
+                let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
+                                                ImplTraitContext::Disallowed);
+
+                let fs = fields.iter()
+                                .map(|f| {
+                                    Spanned {
+                                        span: f.span,
+                                        node: hir::FieldPat {
+                                            name: self.lower_ident(f.node.ident),
+                                            pat: self.lower_pat(&f.node.pat),
+                                            is_shorthand: f.node.is_shorthand,
+                                        },
+                                    }
+                                })
+                                .collect();
+                hir::PatKind::Struct(qpath, fs, etc)
+            }
+            PatKind::Tuple(ref elts, ddpos) => {
+                hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
+            }
+            PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
+            PatKind::Ref(ref inner, mutbl) => {
+                hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
+            }
+            PatKind::Range(ref e1, ref e2, ref end) => {
+                hir::PatKind::Range(P(self.lower_expr(e1)),
+                                    P(self.lower_expr(e2)),
+                                    self.lower_range_end(end))
+            }
+            PatKind::Slice(ref before, ref slice, ref after) => {
+                hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
+                            slice.as_ref().map(|x| self.lower_pat(x)),
+                            after.iter().map(|x| self.lower_pat(x)).collect())
+            }
+            PatKind::Paren(ref inner) => return self.lower_pat(inner),
+            PatKind::Mac(_) => panic!("Shouldn't exist here"),
+        };
 
+        let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
         P(hir::Pat {
             id: node_id,
             hir_id,
-            node: match p.node {
-                PatKind::Wild => hir::PatKind::Wild,
-                PatKind::Ident(ref binding_mode, pth1, ref sub) => {
-                    match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
-                        // `None` can occur in body-less function signatures
-                        def @ None | def @ Some(Def::Local(_)) => {
-                            let canonical_id = match def {
-                                Some(Def::Local(id)) => id,
-                                _ => p.id
-                            };
-                            hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
-                                                  canonical_id,
-                                                  respan(pth1.span, pth1.node.name),
-                                                  sub.as_ref().map(|x| self.lower_pat(x)))
-                        }
-                        Some(def) => {
-                            hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
-                                span: pth1.span,
-                                def,
-                                segments: hir_vec![
-                                    hir::PathSegment::from_name(pth1.node.name)
-                                ],
-                            })))
-                        }
-                    }
-                }
-                PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
-                PatKind::TupleStruct(ref path, ref pats, ddpos) => {
-                    let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
-                                                 ImplTraitContext::Disallowed);
-                    hir::PatKind::TupleStruct(qpath,
-                                              pats.iter().map(|x| self.lower_pat(x)).collect(),
-                                              ddpos)
-                }
-                PatKind::Path(ref qself, ref path) => {
-                    hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
-                                                        ImplTraitContext::Disallowed))
-                }
-                PatKind::Struct(ref path, ref fields, etc) => {
-                    let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
-                                                 ImplTraitContext::Disallowed);
-
-                    let fs = fields.iter()
-                                   .map(|f| {
-                                       Spanned {
-                                           span: f.span,
-                                           node: hir::FieldPat {
-                                               name: self.lower_ident(f.node.ident),
-                                               pat: self.lower_pat(&f.node.pat),
-                                               is_shorthand: f.node.is_shorthand,
-                                           },
-                                       }
-                                   })
-                                   .collect();
-                    hir::PatKind::Struct(qpath, fs, etc)
-                }
-                PatKind::Tuple(ref elts, ddpos) => {
-                    hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
-                }
-                PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
-                PatKind::Ref(ref inner, mutbl) => {
-                    hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
-                }
-                PatKind::Range(ref e1, ref e2, ref end) => {
-                    hir::PatKind::Range(P(self.lower_expr(e1)),
-                                        P(self.lower_expr(e2)),
-                                        self.lower_range_end(end))
-                }
-                PatKind::Slice(ref before, ref slice, ref after) => {
-                    hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
-                                slice.as_ref().map(|x| self.lower_pat(x)),
-                                after.iter().map(|x| self.lower_pat(x)).collect())
-                }
-                PatKind::Mac(_) => panic!("Shouldn't exist here"),
-            },
+            node,
             span: p.span,
         })
     }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index de55710bdf3..66f34a72edf 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -737,6 +737,7 @@ impl EarlyLintPass for IllegalFloatLiteralPattern {
                 PatKind::TupleStruct(..) |
                 PatKind::Ref(..) |
                 PatKind::Box(..) |
+                PatKind::Paren(..) |
                 PatKind::Slice(..) => (),
 
                 // Extract the expressions and check them
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index c1e6b4f5a17..a0fc5533f8e 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -542,7 +542,7 @@ fn foo<T, T>(s: T, u: T) {} // error: the name `T` is already used for a type
                             //        parameter in this type parameter list
 ```
 
-Please verify that none of the type parameterss are misspelled, and rename any
+Please verify that none of the type parameters are misspelled, and rename any
 clashing parameters. Example:
 
 ```
@@ -551,7 +551,8 @@ fn foo<T, Y>(s: T, u: Y) {} // ok!
 "##,
 
 E0404: r##"
-You tried to implement something which was not a trait on an object.
+You tried to use something which is not a trait in a trait position, such as
+a bound or `impl`.
 
 Erroneous code example:
 
@@ -562,6 +563,14 @@ struct Bar;
 impl Foo for Bar {} // error: `Foo` is not a trait
 ```
 
+Another erroneous code example:
+
+```compile_fail,E0404
+struct Foo;
+
+fn bar<T: Foo>(t: T) {} // error: `Foo` is not a trait
+```
+
 Please verify that you didn't misspell the trait's name or otherwise use the
 wrong identifier. Example:
 
@@ -575,6 +584,17 @@ impl Foo for Bar { // ok!
     // functions implementation
 }
 ```
+
+or
+
+```
+trait Foo {
+    // some functions
+}
+
+fn bar<T: Foo>(t: T) {} // ok!
+```
+
 "##,
 
 E0405: r##"
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index e4e9ee58330..42bcc62f291 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2163,6 +2163,7 @@ impl<'a> Resolver<'a> {
         result
     }
 
+    /// 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 Resolver, Option<DefId>) -> T
     {
@@ -2172,13 +2173,14 @@ impl<'a> Resolver<'a> {
             let path: Vec<_> = trait_ref.path.segments.iter()
                 .map(|seg| respan(seg.span, seg.identifier))
                 .collect();
-            let def = self.smart_resolve_path_fragment(trait_ref.ref_id,
-                                                       None,
-                                                       &path,
-                                                       trait_ref.path.span,
-                                                       trait_ref.path.segments.last().unwrap().span,
-                                                       PathSource::Trait(AliasPossibility::No))
-                .base_def();
+            let def = self.smart_resolve_path_fragment(
+                trait_ref.ref_id,
+                None,
+                &path,
+                trait_ref.path.span,
+                trait_ref.path.segments.last().unwrap().span,
+                PathSource::Trait(AliasPossibility::No)
+            ).base_def();
             if def != Def::Err {
                 new_id = Some(def.def_id());
                 let span = trait_ref.path.span;
diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs
index 8309c91ab25..57cd47063dc 100644
--- a/src/librustc_trans/attributes.rs
+++ b/src/librustc_trans/attributes.rs
@@ -212,7 +212,7 @@ fn from_target_feature(
         let value = value.as_str();
         for feature in value.split(',') {
             if whitelist.contains(feature) {
-                let llvm_feature = llvm_util::to_llvm_feature(feature);
+                let llvm_feature = llvm_util::to_llvm_feature(&tcx.sess, feature);
                 target_features.push(format!("+{}", llvm_feature));
                 continue
             }
diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs
index 00ac9d80245..45445a48e23 100644
--- a/src/librustc_trans/llvm_util.rs
+++ b/src/librustc_trans/llvm_util.rs
@@ -81,7 +81,9 @@ unsafe fn configure_llvm(sess: &Session) {
 
 const ARM_WHITELIST: &'static [&'static str] = &["neon", "v7", "vfp2", "vfp3", "vfp4"];
 
-const AARCH64_WHITELIST: &'static [&'static str] = &["neon", "v7"];
+const AARCH64_WHITELIST: &'static [&'static str] = &["fp", "neon", "sve", "crc", "crypto",
+                                                     "ras", "lse", "rdm", "fp16", "rcpc",
+                                                     "dotprod", "v8.1a", "v8.2a", "v8.3a"];
 
 const X86_WHITELIST: &'static [&'static str] = &["aes", "avx", "avx2", "avx512bw",
                                                  "avx512cd", "avx512dq", "avx512er",
@@ -104,12 +106,18 @@ const POWERPC_WHITELIST: &'static [&'static str] = &["altivec",
 
 const MIPS_WHITELIST: &'static [&'static str] = &["msa"];
 
-pub fn to_llvm_feature(s: &str) -> &str {
-    match s {
-        "pclmulqdq" => "pclmul",
-        "rdrand" => "rdrnd",
-        "bmi1" => "bmi",
-        s => s,
+pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
+    let arch = if sess.target.target.arch == "x86_64" {
+        "x86"
+    } else {
+        &*sess.target.target.arch
+    };
+    match (arch, s) {
+        ("x86", "pclmulqdq") => "pclmul",
+        ("x86", "rdrand") => "rdrnd",
+        ("x86", "bmi1") => "bmi",
+        ("aarch64", "fp16") => "fullfp16",
+        (_, s) => s,
     }
 }
 
@@ -118,7 +126,7 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
     target_feature_whitelist(sess)
         .iter()
         .filter(|feature| {
-            let llvm_feature = to_llvm_feature(feature);
+            let llvm_feature = to_llvm_feature(sess, feature);
             let cstr = CString::new(llvm_feature).unwrap();
             unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
         })
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index da01592d911..99e3a59e2c4 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use common::{C_i32, C_null};
 use libc::c_uint;
 use llvm::{self, ValueRef, BasicBlockRef};
 use llvm::debuginfo::DIScope;
@@ -23,6 +24,7 @@ use common::{CodegenCx, Funclet};
 use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
 use monomorphize::Instance;
 use abi::{ArgAttribute, FnType, PassMode};
+use type_::Type;
 
 use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span};
 use syntax::symbol::keywords;
@@ -222,7 +224,7 @@ pub fn trans_mir<'a, 'tcx: 'a>(
 
     // Compute debuginfo scopes from MIR scopes.
     let scopes = debuginfo::create_mir_scopes(cx, mir, &debug_context);
-    let (landing_pads, funclets) = create_funclets(&bx, &cleanup_kinds, &block_bxs);
+    let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs);
 
     let mut fx = FunctionCx {
         mir,
@@ -333,6 +335,7 @@ pub fn trans_mir<'a, 'tcx: 'a>(
 }
 
 fn create_funclets<'a, 'tcx>(
+    mir: &'a Mir<'tcx>,
     bx: &Builder<'a, 'tcx>,
     cleanup_kinds: &IndexVec<mir::BasicBlock, CleanupKind>,
     block_bxs: &IndexVec<mir::BasicBlock, BasicBlockRef>)
@@ -341,14 +344,59 @@ fn create_funclets<'a, 'tcx>(
 {
     block_bxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| {
         match *cleanup_kind {
-            CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {
+            CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {}
+            _ => return (None, None)
+        }
+
+        let cleanup;
+        let ret_llbb;
+        match mir[bb].terminator.as_ref().map(|t| &t.kind) {
+            // This is a basic block that we're aborting the program for,
+            // notably in an `extern` function. These basic blocks are inserted
+            // so that we assert that `extern` functions do indeed not panic,
+            // and if they do we abort the process.
+            //
+            // On MSVC these are tricky though (where we're doing funclets). If
+            // we were to do a cleanuppad (like below) the normal functions like
+            // `longjmp` would trigger the abort logic, terminating the
+            // program. Instead we insert the equivalent of `catch(...)` for C++
+            // which magically doesn't trigger when `longjmp` files over this
+            // frame.
+            //
+            // Lots more discussion can be found on #48251 but this codegen is
+            // modeled after clang's for:
+            //
+            //      try {
+            //          foo();
+            //      } catch (...) {
+            //          bar();
+            //      }
+            Some(&mir::TerminatorKind::Abort) => {
+                let cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb));
+                let cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb));
+                ret_llbb = cs_bx.llbb();
+
+                let cs = cs_bx.catch_switch(None, None, 1);
+                cs_bx.add_handler(cs, cp_bx.llbb());
+
+                // The "null" here is actually a RTTI type descriptor for the
+                // C++ personality function, but `catch (...)` has no type so
+                // it's null. The 64 here is actually a bitfield which
+                // represents that this is a catch-all block.
+                let null = C_null(Type::i8p(bx.cx));
+                let sixty_four = C_i32(bx.cx, 64);
+                cleanup = cp_bx.catch_pad(cs, &[null, sixty_four, null]);
+                cp_bx.br(llbb);
+            }
+            _ => {
                 let cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb));
-                let cleanup = cleanup_bx.cleanup_pad(None, &[]);
+                ret_llbb = cleanup_bx.llbb();
+                cleanup = cleanup_bx.cleanup_pad(None, &[]);
                 cleanup_bx.br(llbb);
-                (Some(cleanup_bx.llbb()), Some(Funclet::new(cleanup)))
             }
-            _ => (None, None)
-        }
+        };
+
+        (Some(ret_llbb), Some(Funclet::new(cleanup)))
     }).unzip()
 }
 
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index bc1b70ffc8e..6a6c49c9048 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -312,7 +312,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
 
     /// Instantiates the path for the given trait reference, assuming that it's
     /// bound to a valid trait type. Returns the def_id for the defining trait.
-    /// Fails if the type is a type other than a trait type.
+    /// The type _cannot_ be a type other than a trait type.
     ///
     /// If the `projections` argument is `None`, then assoc type bindings like `Foo<T=X>`
     /// are disallowed. Otherwise, they are pushed onto the vector given.
@@ -330,6 +330,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                         trait_ref.path.segments.last().unwrap())
     }
 
+    /// Get the DefId of the given trait ref. It _must_ actually be a trait.
     fn trait_def_id(&self, trait_ref: &hir::TraitRef) -> DefId {
         let path = &trait_ref.path;
         match path.def {
@@ -338,13 +339,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             Def::Err => {
                 self.tcx().sess.fatal("cannot continue compilation due to previous error");
             }
-            _ => {
-                span_fatal!(self.tcx().sess, path.span, E0245, "`{}` is not a trait",
-                            self.tcx().hir.node_to_pretty_string(trait_ref.ref_id));
-            }
+            _ => unreachable!(),
         }
     }
 
+    /// The given `trait_ref` must actually be trait.
     pub(super) fn instantiate_poly_trait_ref_inner(&self,
         trait_ref: &hir::TraitRef,
         self_ty: Ty<'tcx>,
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 1c0e084832e..6c195a991c2 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -4802,7 +4802,7 @@ register_diagnostics! {
 //  E0240,
 //  E0241,
 //  E0242,
-    E0245, // not a trait
+//  E0245, // not a trait
 //  E0246, // invalid recursive type
 //  E0247,
 //  E0248, // value used as a type, now reported earlier during resolution as E0412
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 831688bb73d..1f73054e3be 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -17,7 +17,7 @@ use io::{self, Initializer, BufReader, LineWriter};
 use sync::{Arc, Mutex, MutexGuard};
 use sys::stdio;
 use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
-use thread::{LocalKey, LocalKeyState};
+use thread::LocalKey;
 
 /// Stdout used by print! and println! macros
 thread_local! {
@@ -663,29 +663,31 @@ pub fn set_print(sink: Option<Box<Write + Send>>) -> Option<Box<Write + Send>> {
 ///
 /// This function is used to print error messages, so it takes extra
 /// care to avoid causing a panic when `local_stream` is unusable.
-/// For instance, if the TLS key for the local stream is uninitialized
-/// or already destroyed, or if the local stream is locked by another
+/// For instance, if the TLS key for the local stream is
+/// already destroyed, or if the local stream is locked by another
 /// thread, it will just fall back to the global stream.
 ///
 /// However, if the actual I/O causes an error, this function does panic.
-fn print_to<T>(args: fmt::Arguments,
-               local_s: &'static LocalKey<RefCell<Option<Box<Write+Send>>>>,
-               global_s: fn() -> T,
-               label: &str) where T: Write {
-    let result = match local_s.state() {
-        LocalKeyState::Uninitialized |
-        LocalKeyState::Destroyed => global_s().write_fmt(args),
-        LocalKeyState::Valid => {
-            local_s.with(|s| {
-                if let Ok(mut borrowed) = s.try_borrow_mut() {
-                    if let Some(w) = borrowed.as_mut() {
-                        return w.write_fmt(args);
-                    }
-                }
-                global_s().write_fmt(args)
-            })
+fn print_to<T>(
+    args: fmt::Arguments,
+    local_s: &'static LocalKey<RefCell<Option<Box<Write+Send>>>>,
+    global_s: fn() -> T,
+    label: &str,
+)
+where
+    T: Write,
+{
+    let result = local_s.try_with(|s| {
+        if let Ok(mut borrowed) = s.try_borrow_mut() {
+            if let Some(w) = borrowed.as_mut() {
+                return w.write_fmt(args);
+            }
         }
-    };
+        global_s().write_fmt(args)
+    }).unwrap_or_else(|_| {
+        global_s().write_fmt(args)
+    });
+
     if let Err(e) = result {
         panic!("failed printing to {}: {}", label, e);
     }
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs
index fcbca38a98f..99479bc56ef 100644
--- a/src/libstd/thread/local.rs
+++ b/src/libstd/thread/local.rs
@@ -195,64 +195,20 @@ macro_rules! __thread_local_inner {
     }
 }
 
-/// Indicator of the state of a thread local storage key.
-#[unstable(feature = "thread_local_state",
-           reason = "state querying was recently added",
-           issue = "27716")]
-#[derive(Debug, Eq, PartialEq, Copy, Clone)]
-pub enum LocalKeyState {
-    /// All keys are in this state whenever a thread starts. Keys will
-    /// transition to the `Valid` state once the first call to [`with`] happens
-    /// and the initialization expression succeeds.
-    ///
-    /// Keys in the `Uninitialized` state will yield a reference to the closure
-    /// passed to [`with`] so long as the initialization routine does not panic.
-    ///
-    /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with
-    Uninitialized,
-
-    /// Once a key has been accessed successfully, it will enter the `Valid`
-    /// state. Keys in the `Valid` state will remain so until the thread exits,
-    /// at which point the destructor will be run and the key will enter the
-    /// `Destroyed` state.
-    ///
-    /// Keys in the `Valid` state will be guaranteed to yield a reference to the
-    /// closure passed to [`with`].
-    ///
-    /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with
-    Valid,
-
-    /// When a thread exits, the destructors for keys will be run (if
-    /// necessary). While a destructor is running, and possibly after a
-    /// destructor has run, a key is in the `Destroyed` state.
-    ///
-    /// Keys in the `Destroyed` states will trigger a panic when accessed via
-    /// [`with`].
-    ///
-    /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with
-    Destroyed,
-}
-
 /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
-#[unstable(feature = "thread_local_state",
-           reason = "state querying was recently added",
-           issue = "27716")]
+#[stable(feature = "thread_local_try_with", since = "1.26.0")]
 pub struct AccessError {
     _private: (),
 }
 
-#[unstable(feature = "thread_local_state",
-           reason = "state querying was recently added",
-           issue = "27716")]
+#[stable(feature = "thread_local_try_with", since = "1.26.0")]
 impl fmt::Debug for AccessError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_struct("AccessError").finish()
     }
 }
 
-#[unstable(feature = "thread_local_state",
-           reason = "state querying was recently added",
-           issue = "27716")]
+#[stable(feature = "thread_local_try_with", since = "1.26.0")]
 impl fmt::Display for AccessError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt("already destroyed", f)
@@ -312,64 +268,21 @@ impl<T: 'static> LocalKey<T> {
         (*ptr).as_ref().unwrap()
     }
 
-    /// Query the current state of this key.
-    ///
-    /// A key is initially in the `Uninitialized` state whenever a thread
-    /// starts. It will remain in this state up until the first call to [`with`]
-    /// within a thread has run the initialization expression successfully.
-    ///
-    /// Once the initialization expression succeeds, the key transitions to the
-    /// `Valid` state which will guarantee that future calls to [`with`] will
-    /// succeed within the thread. Some keys might skip the `Uninitialized`
-    /// state altogether and start in the `Valid` state as an optimization
-    /// (e.g. keys initialized with a constant expression), but no guarantees
-    /// are made.
-    ///
-    /// When a thread exits, each key will be destroyed in turn, and as keys are
-    /// destroyed they will enter the `Destroyed` state just before the
-    /// destructor starts to run. Keys may remain in the `Destroyed` state after
-    /// destruction has completed. Keys without destructors (e.g. with types
-    /// that are [`Copy`]), may never enter the `Destroyed` state.
-    ///
-    /// Keys in the `Uninitialized` state can be accessed so long as the
-    /// initialization does not panic. Keys in the `Valid` state are guaranteed
-    /// to be able to be accessed. Keys in the `Destroyed` state will panic on
-    /// any call to [`with`].
-    ///
-    /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with
-    /// [`Copy`]: ../../std/marker/trait.Copy.html
-    #[unstable(feature = "thread_local_state",
-               reason = "state querying was recently added",
-               issue = "27716")]
-    pub fn state(&'static self) -> LocalKeyState {
-        unsafe {
-            match (self.inner)() {
-                Some(cell) => {
-                    match *cell.get() {
-                        Some(..) => LocalKeyState::Valid,
-                        None => LocalKeyState::Uninitialized,
-                    }
-                }
-                None => LocalKeyState::Destroyed,
-            }
-        }
-    }
-
     /// Acquires a reference to the value in this TLS key.
     ///
     /// This will lazily initialize the value if this thread has not referenced
     /// this key yet. If the key has been destroyed (which may happen if this is called
-    /// in a destructor), this function will return a ThreadLocalError.
+    /// in a destructor), this function will return a `ThreadLocalError`.
     ///
     /// # Panics
     ///
     /// This function will still `panic!()` if the key is uninitialized and the
     /// key's initializer panics.
-    #[unstable(feature = "thread_local_state",
-               reason = "state querying was recently added",
-               issue = "27716")]
+    #[stable(feature = "thread_local_try_with", since = "1.26.0")]
     pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
-                      where F: FnOnce(&T) -> R {
+    where
+        F: FnOnce(&T) -> R,
+    {
         unsafe {
             let slot = (self.inner)().ok_or(AccessError {
                 _private: (),
@@ -530,7 +443,6 @@ pub mod os {
 mod tests {
     use sync::mpsc::{channel, Sender};
     use cell::{Cell, UnsafeCell};
-    use super::LocalKeyState;
     use thread;
 
     struct Foo(Sender<()>);
@@ -569,21 +481,13 @@ mod tests {
         struct Foo;
         impl Drop for Foo {
             fn drop(&mut self) {
-                assert!(FOO.state() == LocalKeyState::Destroyed);
+                assert!(FOO.try_with(|_| ()).is_err());
             }
         }
-        fn foo() -> Foo {
-            assert!(FOO.state() == LocalKeyState::Uninitialized);
-            Foo
-        }
-        thread_local!(static FOO: Foo = foo());
+        thread_local!(static FOO: Foo = Foo);
 
         thread::spawn(|| {
-            assert!(FOO.state() == LocalKeyState::Uninitialized);
-            FOO.with(|_| {
-                assert!(FOO.state() == LocalKeyState::Valid);
-            });
-            assert!(FOO.state() == LocalKeyState::Valid);
+            assert!(FOO.try_with(|_| ()).is_ok());
         }).join().ok().unwrap();
     }
 
@@ -613,7 +517,7 @@ mod tests {
             fn drop(&mut self) {
                 unsafe {
                     HITS += 1;
-                    if K2.state() == LocalKeyState::Destroyed {
+                    if K2.try_with(|_| ()).is_err() {
                         assert_eq!(HITS, 3);
                     } else {
                         if HITS == 1 {
@@ -629,7 +533,7 @@ mod tests {
             fn drop(&mut self) {
                 unsafe {
                     HITS += 1;
-                    assert!(K1.state() != LocalKeyState::Destroyed);
+                    assert!(K1.try_with(|_| ()).is_ok());
                     assert_eq!(HITS, 2);
                     K1.with(|s| *s.get() = Some(S1));
                 }
@@ -648,7 +552,7 @@ mod tests {
 
         impl Drop for S1 {
             fn drop(&mut self) {
-                assert!(K1.state() == LocalKeyState::Destroyed);
+                assert!(K1.try_with(|_| ()).is_err());
             }
         }
 
@@ -672,9 +576,7 @@ mod tests {
             fn drop(&mut self) {
                 let S1(ref tx) = *self;
                 unsafe {
-                    if K2.state() != LocalKeyState::Destroyed {
-                        K2.with(|s| *s.get() = Some(Foo(tx.clone())));
-                    }
+                    let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
                 }
             }
         }
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index ff121e2d7ee..71aee673cfe 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -191,7 +191,7 @@ use time::Duration;
 #[macro_use] mod local;
 
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::local::{LocalKey, LocalKeyState, AccessError};
+pub use self::local::{LocalKey, AccessError};
 
 // The types used by the thread_local! macro to access TLS keys. Note that there
 // are two types, the "OS" type and the "fast" type. The OS thread local key
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 6609b77b132..40000bc378e 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -562,7 +562,7 @@ impl Pat {
             PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
                 s.iter().all(|p| p.walk(it))
             }
-            PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
+            PatKind::Box(ref s) | PatKind::Ref(ref s, _) | PatKind::Paren(ref s) => {
                 s.walk(it)
             }
             PatKind::Slice(ref before, ref slice, ref after) => {
@@ -656,6 +656,8 @@ pub enum PatKind {
     /// `[a, b, ..i, y, z]` is represented as:
     ///     `PatKind::Slice(box [a, b], Some(i), box [y, z])`
     Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
+    /// Parentheses in patters used for grouping, i.e. `(PAT)`.
+    Paren(P<Pat>),
     /// A macro pattern; pre-expansion
     Mac(Mac),
 }
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 926548b6031..fcd2b236213 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -705,17 +705,20 @@ impl CodeMap {
         };
         debug!("find_width_of_character_at_span: snippet=`{:?}`", snippet);
 
-        let file_start_pos = local_begin.fm.start_pos.to_usize();
-        let file_end_pos = local_begin.fm.end_pos.to_usize();
-        debug!("find_width_of_character_at_span: file_start_pos=`{:?}` file_end_pos=`{:?}`",
-               file_start_pos, file_end_pos);
-
         let mut target = if forwards { end_index + 1 } else { end_index - 1 };
         debug!("find_width_of_character_at_span: initial target=`{:?}`", target);
 
-        while !snippet.is_char_boundary(target - start_index)
-                && target >= file_start_pos && target <= file_end_pos {
-            target = if forwards { target + 1 } else { target - 1 };
+        while !snippet.is_char_boundary(target - start_index) && target < source_len {
+            target = if forwards {
+                target + 1
+            } else {
+                match target.checked_sub(1) {
+                    Some(target) => target,
+                    None => {
+                        break;
+                    }
+                }
+            };
             debug!("find_width_of_character_at_span: target=`{:?}`", target);
         }
         debug!("find_width_of_character_at_span: final target=`{:?}`", target);
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 6444c846a8b..70ea015de4e 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -449,6 +449,9 @@ declare_features! (
 
     // Multiple patterns with `|` in `if let` and `while let`
     (active, if_while_or_patterns, "1.26.0", Some(48215)),
+
+    // Parentheses in patterns
+    (active, pattern_parentheses, "1.26.0", None),
 );
 
 declare_features! (
@@ -1663,6 +1666,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate_feature_post!(&self, dotdoteq_in_patterns, pattern.span,
                                    "`..=` syntax in patterns is experimental");
             }
+            PatKind::Paren(..) => {
+                gate_feature_post!(&self, pattern_parentheses, pattern.span,
+                                   "parentheses in patterns are unstable");
+            }
             _ => {}
         }
         visit::walk_pat(self, pattern)
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index e8eb75f5e60..1963ab45f1a 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1148,6 +1148,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                        slice.map(|x| folder.fold_pat(x)),
                        after.move_map(|x| folder.fold_pat(x)))
             }
+            PatKind::Paren(inner) => PatKind::Paren(folder.fold_pat(inner)),
             PatKind::Mac(mac) => PatKind::Mac(folder.fold_mac(mac))
         },
         span: folder.new_span(span)
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 09dd00fa5fa..881e3e412d4 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3484,33 +3484,47 @@ impl<'a> Parser<'a> {
         };
     }
 
-    fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
-                                -> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
-        let mut fields = vec![];
-        let mut ddpos = None;
+    // Parses a parenthesized list of patterns like
+    // `()`, `(p)`, `(p,)`, `(p, q)`, or `(p, .., q)`. Returns:
+    // - a vector of the patterns that were parsed
+    // - an option indicating the index of the `..` element
+    // - a boolean indicating whether a trailing comma was present.
+    // Trailing commas are significant because (p) and (p,) are different patterns.
+    fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
+        self.expect(&token::OpenDelim(token::Paren))?;
 
-        while !self.check(&token::CloseDelim(token::Paren)) {
-            if ddpos.is_none() && self.eat(&token::DotDot) {
-                ddpos = Some(fields.len());
-                if self.eat(&token::Comma) {
-                    // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
-                    fields.push(self.parse_pat()?);
+        let mut fields = Vec::new();
+        let mut ddpos = None;
+        let mut trailing_comma = false;
+        loop {
+            if self.eat(&token::DotDot) {
+                if ddpos.is_none() {
+                    ddpos = Some(fields.len());
+                } else {
+                    // Emit a friendly error, ignore `..` and continue parsing
+                    self.span_err(self.prev_span,
+                                  "`..` can only be used once per tuple or tuple struct pattern");
                 }
-            } else if ddpos.is_some() && self.eat(&token::DotDot) {
-                // Emit a friendly error, ignore `..` and continue parsing
-                self.span_err(self.prev_span, "`..` can only be used once per \
-                                               tuple or tuple struct pattern");
-            } else {
+            } else if !self.check(&token::CloseDelim(token::Paren)) {
                 fields.push(self.parse_pat()?);
+            } else {
+                break
             }
 
-            if !self.check(&token::CloseDelim(token::Paren)) ||
-                    (unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
-                self.expect(&token::Comma)?;
+            trailing_comma = self.eat(&token::Comma);
+            if !trailing_comma {
+                break
             }
         }
 
-        Ok((fields, ddpos))
+        if ddpos == Some(fields.len()) && trailing_comma {
+            // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
+            self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
+        }
+
+        self.expect(&token::CloseDelim(token::Paren))?;
+
+        Ok((fields, ddpos, trailing_comma))
     }
 
     fn parse_pat_vec_elements(
@@ -3714,10 +3728,12 @@ impl<'a> Parser<'a> {
             }
             token::OpenDelim(token::Paren) => {
                 // Parse (pat,pat,pat,...) as tuple pattern
-                self.bump();
-                let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
-                self.expect(&token::CloseDelim(token::Paren))?;
-                pat = PatKind::Tuple(fields, ddpos);
+                let (fields, ddpos, trailing_comma) = self.parse_parenthesized_pat_list()?;
+                pat = if fields.len() == 1 && ddpos.is_none() && !trailing_comma {
+                    PatKind::Paren(fields.into_iter().nth(0).unwrap())
+                } else {
+                    PatKind::Tuple(fields, ddpos)
+                };
             }
             token::OpenDelim(token::Bracket) => {
                 // Parse [pat,pat,...] as slice pattern
@@ -3807,9 +3823,7 @@ impl<'a> Parser<'a> {
                             return Err(self.fatal("unexpected `(` after qualified path"));
                         }
                         // Parse tuple struct or enum pattern
-                        self.bump();
-                        let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
-                        self.expect(&token::CloseDelim(token::Paren))?;
+                        let (fields, ddpos, _) = self.parse_parenthesized_pat_list()?;
                         pat = PatKind::TupleStruct(path, fields, ddpos)
                     }
                     _ => pat = PatKind::Path(qself, path),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 9cad9f46e98..77afafbb4e0 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2659,6 +2659,11 @@ impl<'a> State<'a> {
                                    |s, p| s.print_pat(p))?;
                 self.s.word("]")?;
             }
+            PatKind::Paren(ref inner) => {
+                self.popen()?;
+                self.print_pat(inner)?;
+                self.pclose()?;
+            }
             PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
         }
         self.ann.post(self, NodePat(pat))
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 640f90ecb4a..5a24c61cb5a 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -425,7 +425,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
             walk_list!(visitor, visit_pat, tuple_elements);
         }
         PatKind::Box(ref subpattern) |
-        PatKind::Ref(ref subpattern, _) => {
+        PatKind::Ref(ref subpattern, _) |
+        PatKind::Paren(ref subpattern) => {
             visitor.visit_pat(subpattern)
         }
         PatKind::Ident(_, ref pth1, ref optional_subpattern) => {
diff --git a/src/test/parse-fail/pat-tuple-2.rs b/src/test/parse-fail/pat-tuple-2.rs
index ad52fa57870..07b391d3279 100644
--- a/src/test/parse-fail/pat-tuple-2.rs
+++ b/src/test/parse-fail/pat-tuple-2.rs
@@ -12,6 +12,6 @@
 
 fn main() {
     match 0 {
-        (pat, ..,) => {} //~ ERROR expected pattern, found `)`
+        (pat, ..,) => {} //~ ERROR trailing comma is not permitted after `..`
     }
 }
diff --git a/src/test/run-make/longjmp-across-rust/Makefile b/src/test/run-make/longjmp-across-rust/Makefile
new file mode 100644
index 00000000000..9d71ed8fcf3
--- /dev/null
+++ b/src/test/run-make/longjmp-across-rust/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all: $(call NATIVE_STATICLIB,foo)
+	$(RUSTC) main.rs
+	$(call RUN,main)
diff --git a/src/test/run-make/longjmp-across-rust/foo.c b/src/test/run-make/longjmp-across-rust/foo.c
new file mode 100644
index 00000000000..eb993957674
--- /dev/null
+++ b/src/test/run-make/longjmp-across-rust/foo.c
@@ -0,0 +1,28 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include <assert.h>
+#include <setjmp.h>
+
+static jmp_buf ENV;
+
+extern void test_middle();
+
+void test_start(void(*f)()) {
+  if (setjmp(ENV) != 0)
+    return;
+  f();
+  assert(0);
+}
+
+void test_end() {
+  longjmp(ENV, 1);
+  assert(0);
+}
diff --git a/src/test/run-make/longjmp-across-rust/main.rs b/src/test/run-make/longjmp-across-rust/main.rs
new file mode 100644
index 00000000000..c420473a560
--- /dev/null
+++ b/src/test/run-make/longjmp-across-rust/main.rs
@@ -0,0 +1,40 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[link(name = "foo", kind = "static")]
+extern {
+    fn test_start(f: extern fn());
+    fn test_end();
+}
+
+fn main() {
+    unsafe {
+        test_start(test_middle);
+    }
+}
+
+struct A;
+
+impl Drop for A {
+    fn drop(&mut self) {
+    }
+}
+
+extern fn test_middle() {
+    let _a = A;
+    foo();
+}
+
+fn foo() {
+    let _a = A;
+    unsafe {
+        test_end();
+    }
+}
diff --git a/src/test/run-pass/issue-48508-aux.rs b/src/test/run-pass/issue-48508-aux.rs
new file mode 100644
index 00000000000..a00361a2c9d
--- /dev/null
+++ b/src/test/run-pass/issue-48508-aux.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-test Not a test. Used by issue-48508.rs
+
+pub fn other() -> f64 {
+    let µ = 1.0;
+    µ
+}
diff --git a/src/test/run-pass/issue-48508.rs b/src/test/run-pass/issue-48508.rs
new file mode 100644
index 00000000000..1b10d873f11
--- /dev/null
+++ b/src/test/run-pass/issue-48508.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for issue #48508:
+//
+// Confusion between global and local file offsets caused incorrect handling of multibyte character
+// spans when compiling multiple files. One visible effect was an ICE generating debug information
+// when a multibyte character is at the end of a scope. The problematic code is actually in
+// issue-48508-aux.rs
+
+// compile-flags:-g
+// ignore-pretty issue #37195
+
+#![feature(non_ascii_idents)]
+
+#[path = "issue-48508-aux.rs"]
+mod other_file;
+
+fn main() {
+    other_file::other();
+}
diff --git a/src/test/run-pass/issue-48551.rs b/src/test/run-pass/issue-48551.rs
new file mode 100644
index 00000000000..93bddbc5335
--- /dev/null
+++ b/src/test/run-pass/issue-48551.rs
@@ -0,0 +1,43 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #48551. Covers a case where duplicate candidates
+// arose during associated type projection.
+
+use std::ops::{Mul, MulAssign};
+
+pub trait ClosedMul<Right>: Sized + Mul<Right, Output = Self> + MulAssign<Right> {}
+impl<T, Right> ClosedMul<Right> for T
+where
+    T: Mul<Right, Output = T> + MulAssign<Right>,
+{
+}
+
+pub trait InnerSpace: ClosedMul<<Self as InnerSpace>::Real> {
+    type Real;
+}
+
+pub trait FiniteDimVectorSpace: ClosedMul<<Self as FiniteDimVectorSpace>::Field> {
+    type Field;
+}
+
+pub trait FiniteDimInnerSpace
+    : InnerSpace + FiniteDimVectorSpace<Field = <Self as InnerSpace>::Real> {
+}
+
+pub trait EuclideanSpace: ClosedMul<<Self as EuclideanSpace>::Real> {
+    type Coordinates: FiniteDimInnerSpace<Real = Self::Real>
+        + Mul<Self::Real, Output = Self::Coordinates>
+        + MulAssign<Self::Real>;
+
+    type Real;
+}
+
+fn main() {}
diff --git a/src/test/parse-fail/pat-tuple-6.rs b/src/test/run-pass/pat-tuple-7.rs
index 3252d92fe1b..6d51df63e15 100644
--- a/src/test/parse-fail/pat-tuple-6.rs
+++ b/src/test/run-pass/pat-tuple-7.rs
@@ -8,10 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-flags: -Z parse-only
+#![feature(pattern_parentheses)]
 
 fn main() {
     match 0 {
-        (pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
+        (pat) => assert_eq!(pat, 0)
     }
 }
diff --git a/src/test/run-pass/tls-init-on-init.rs b/src/test/run-pass/tls-init-on-init.rs
index b44c535d3a4..48a0d4a99ec 100644
--- a/src/test/run-pass/tls-init-on-init.rs
+++ b/src/test/run-pass/tls-init-on-init.rs
@@ -10,9 +10,9 @@
 
 // ignore-emscripten no threads support
 
-#![feature(thread_local_state)]
+#![feature(thread_local_try_with)]
 
-use std::thread::{self, LocalKeyState};
+use std::thread;
 use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
 
 struct Foo { cnt: usize }
@@ -37,10 +37,8 @@ impl Drop for Foo {
             FOO.with(|foo| assert_eq!(foo.cnt, 0));
         } else {
             assert_eq!(self.cnt, 0);
-            match FOO.state() {
-                LocalKeyState::Valid => panic!("should not be in valid state"),
-                LocalKeyState::Uninitialized |
-                LocalKeyState::Destroyed => {}
+            if FOO.try_with(|_| ()).is_ok() {
+                panic!("should not be in valid state");
             }
         }
     }
diff --git a/src/test/run-pass/tls-try-with.rs b/src/test/run-pass/tls-try-with.rs
index c072ec0679d..552f4c5e829 100644
--- a/src/test/run-pass/tls-try-with.rs
+++ b/src/test/run-pass/tls-try-with.rs
@@ -10,7 +10,7 @@
 
 // ignore-emscripten no threads support
 
-#![feature(thread_local_state)]
+#![feature(thread_local_try_with)]
 
 use std::thread;
 
diff --git a/src/test/ui/error-codes/E0404.rs b/src/test/ui/error-codes/E0404.rs
index 1c088a71d7d..1c6ff5ae841 100644
--- a/src/test/ui/error-codes/E0404.rs
+++ b/src/test/ui/error-codes/E0404.rs
@@ -13,5 +13,6 @@ struct Bar;
 
 impl Foo for Bar {} //~ ERROR E0404
 
-fn main() {
-}
+fn main() {}
+
+fn baz<T: Foo>(_: T) {} //~ ERROR E0404
diff --git a/src/test/ui/error-codes/E0404.stderr b/src/test/ui/error-codes/E0404.stderr
index d49c0f3ea5e..ac1d2a00cf4 100644
--- a/src/test/ui/error-codes/E0404.stderr
+++ b/src/test/ui/error-codes/E0404.stderr
@@ -4,6 +4,12 @@ error[E0404]: expected trait, found struct `Foo`
 LL | impl Foo for Bar {} //~ ERROR E0404
    |      ^^^ not a trait
 
+error[E0404]: expected trait, found struct `Foo`
+  --> $DIR/E0404.rs:18:11
+   |
+LL | fn baz<T: Foo>(_: T) {} //~ ERROR E0404
+   |           ^^^ not a trait
+
 error: cannot continue compilation due to previous error
 
 If you want more information on this error, try using "rustc --explain E0404"
diff --git a/src/test/ui/feature-gate-pattern_parentheses.rs b/src/test/ui/feature-gate-pattern_parentheses.rs
new file mode 100644
index 00000000000..29768018f0e
--- /dev/null
+++ b/src/test/ui/feature-gate-pattern_parentheses.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    match 0 {
+        (pat) => {} //~ ERROR parentheses in patterns are unstable
+    }
+}
diff --git a/src/test/ui/feature-gate-pattern_parentheses.stderr b/src/test/ui/feature-gate-pattern_parentheses.stderr
new file mode 100644
index 00000000000..4fc1441a0fa
--- /dev/null
+++ b/src/test/ui/feature-gate-pattern_parentheses.stderr
@@ -0,0 +1,11 @@
+error[E0658]: parentheses in patterns are unstable
+  --> $DIR/feature-gate-pattern_parentheses.rs:13:9
+   |
+LL |         (pat) => {} //~ ERROR parentheses in patterns are unstable
+   |         ^^^^^
+   |
+   = help: add #![feature(pattern_parentheses)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+If you want more information on this error, try using "rustc --explain E0658"
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index b90947e5a43..8e23519f57e 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -17,6 +17,11 @@ import json
 import copy
 import datetime
 import collections
+import textwrap
+try:
+    import urllib2
+except ImportError:
+    import urllib.request as urllib2
 
 # List of people to ping when the status of a tool changed.
 MAINTAINERS = {
@@ -24,6 +29,10 @@ MAINTAINERS = {
     'clippy-driver': '@Manishearth @llogiq @mcarton @oli-obk',
     'rls': '@nrc',
     'rustfmt': '@nrc',
+    'book': '@carols10cents @steveklabnik',
+    'nomicon': '@frewsxcv @Gankro',
+    'reference': '@steveklabnik @Havvy @matthewjasper @alercah',
+    'rust-by-example': '@steveklabnik @marioidival @projektir',
 }
 
 
@@ -38,7 +47,12 @@ def read_current_status(current_commit, path):
     return {}
 
 
-def update_latest(current_commit, relevant_pr_number, current_datetime):
+def update_latest(
+    current_commit,
+    relevant_pr_number,
+    relevant_pr_url,
+    current_datetime
+):
     '''Updates `_data/latest.json` to match build result of the given commit.
     '''
     with open('_data/latest.json', 'rb+') as f:
@@ -50,8 +64,13 @@ def update_latest(current_commit, relevant_pr_number, current_datetime):
         }
 
         slug = 'rust-lang/rust'
-        message = '📣 Toolstate changed by {}!\n\nTested on commit {}@{}.\n\n' \
-            .format(relevant_pr_number, slug, current_commit)
+        message = textwrap.dedent('''\
+            📣 Toolstate changed by {}!
+
+            Tested on commit {}@{}.
+            Direct link to PR: <{}>
+
+        ''').format(relevant_pr_number, slug, current_commit, relevant_pr_url)
         anything_changed = False
         for status in latest:
             tool = status['tool']
@@ -68,7 +87,7 @@ def update_latest(current_commit, relevant_pr_number, current_datetime):
                 elif new < old:
                     changed = True
                     message += '💔 {} on {}: {} → {} (cc {}).\n' \
-                        .format(tool, os, old, new, MAINTAINERS[tool])
+                        .format(tool, os, old, new, MAINTAINERS.get(tool))
 
             if changed:
                 status['commit'] = current_commit
@@ -89,17 +108,41 @@ if __name__ == '__main__':
     cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
     cur_commit_msg = sys.argv[2]
     save_message_to_path = sys.argv[3]
+    github_token = sys.argv[4]
 
-    relevant_pr_match = re.search('#[0-9]+', cur_commit_msg)
+    relevant_pr_match = re.search('#([0-9]+)', cur_commit_msg)
     if relevant_pr_match:
-        relevant_pr_number = 'rust-lang/rust' + relevant_pr_match.group(0)
+        number = relevant_pr_match.group(1)
+        relevant_pr_number = 'rust-lang/rust#' + number
+        relevant_pr_url = 'https://github.com/rust-lang/rust/pull/' + number
     else:
+        number = '-1'
         relevant_pr_number = '<unknown PR>'
-
-    message = update_latest(cur_commit, relevant_pr_number, cur_datetime)
-    if message:
-        print(message)
-        with open(save_message_to_path, 'w') as f:
-            f.write(message)
-    else:
+        relevant_pr_url = '<unknown>'
+
+    message = update_latest(
+        cur_commit,
+        relevant_pr_number,
+        relevant_pr_url,
+        cur_datetime
+    )
+    if not message:
         print('<Nothing changed>')
+        sys.exit(0)
+
+    print(message)
+    with open(save_message_to_path, 'w') as f:
+        f.write(message)
+
+    # Write the toolstate comment on the PR as well.
+    gh_url = 'https://api.github.com/repos/rust-lang/rust/issues/{}/comments' \
+        .format(number)
+    response = urllib2.urlopen(urllib2.Request(
+        gh_url,
+        json.dumps({'body': message}),
+        {
+            'Authorization': 'token ' + github_token,
+            'Content-Type': 'application/json',
+        }
+    ))
+    response.read()