From 06d401948ba7451d8ed10b4a58e1e2d08c0094f7 Mon Sep 17 00:00:00 2001
From: Sean Patrick Santos <SeanPatrickSantos@gmail.com>
Date: Thu, 15 Jan 2015 13:49:23 -0700
Subject: [PATCH 01/33] For issue 15149 test, don't execute from tmpfs, and
 wait to see if the child panics before passing.

---
 src/test/run-pass/issue-15149.rs | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs
index 57dc6fd75f0..59b1bb287fa 100644
--- a/src/test/run-pass/issue-15149.rs
+++ b/src/test/run-pass/issue-15149.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::io::{TempDir, Command, fs};
+use std::io::{Command, fs, USER_RWX};
 use std::os;
 
 fn main() {
@@ -22,19 +22,23 @@ fn main() {
 }
 
 fn test() {
-    // If we're the parent, copy our own binary to a tempr directory, and then
-    // make it executable.
-    let dir = TempDir::new("mytest").unwrap();
-    let me = os::self_exe_name().unwrap();
-    let dest = dir.path().join(format!("mytest{}", os::consts::EXE_SUFFIX));
-    fs::copy(&me, &dest).unwrap();
+    // If we're the parent, copy our own binary to a new directory.
+    let my_path = os::self_exe_name().unwrap();
+    let my_dir  = my_path.dir_path();
 
-    // Append the temp directory to our own PATH.
+    let child_dir = Path::new(my_dir.join("issue-15149-child"));
+    drop(fs::mkdir(&child_dir, USER_RWX));
+
+    let child_path = child_dir.join(format!("mytest{}",
+                                            os::consts::EXE_SUFFIX));
+    fs::copy(&my_path, &child_path).unwrap();
+
+    // Append the new directory to our own PATH.
     let mut path = os::split_paths(os::getenv("PATH").unwrap_or(String::new()));
-    path.push(dir.path().clone());
+    path.push(child_dir.clone());
     let path = os::join_paths(path.as_slice()).unwrap();
 
-    Command::new("mytest").env("PATH", path.as_slice())
-                          .arg("child")
-                          .spawn().unwrap();
+    assert!(Command::new("mytest").env("PATH", path.as_slice())
+                                  .arg("child")
+                                  .status().unwrap().success());
 }

From f39297f9918625ff95fdd0b771902037adf069f5 Mon Sep 17 00:00:00 2001
From: Brian Leibig <brian@brianleibig.com>
Date: Tue, 20 Jan 2015 18:46:44 -0800
Subject: [PATCH 02/33] Add a LALR grammar for Rust with testing support

---
 configure                      |    2 +
 mk/grammar.mk                  |   48 +
 src/grammar/lexer.l            |  342 ++++++
 src/grammar/parser-lalr-main.c |  203 ++++
 src/grammar/parser-lalr.y      | 1912 ++++++++++++++++++++++++++++++++
 src/grammar/testparser.py      |   65 ++
 src/grammar/tokens.h           |   91 ++
 7 files changed, 2663 insertions(+)
 create mode 100644 src/grammar/lexer.l
 create mode 100644 src/grammar/parser-lalr-main.c
 create mode 100644 src/grammar/parser-lalr.y
 create mode 100755 src/grammar/testparser.py
 create mode 100644 src/grammar/tokens.h

diff --git a/configure b/configure
index 712db849039..fa6a4579f19 100755
--- a/configure
+++ b/configure
@@ -647,6 +647,8 @@ probe CFG_ISCC             iscc
 probe CFG_JAVAC            javac
 probe CFG_ANTLR4           antlr4
 probe CFG_GRUN             grun
+probe CFG_FLEX             flex
+probe CFG_BISON            bison
 probe CFG_PANDOC           pandoc
 probe CFG_PDFLATEX         pdflatex
 probe CFG_XELATEX          xelatex
diff --git a/mk/grammar.mk b/mk/grammar.mk
index 93e40302f51..d9c66e282bc 100644
--- a/mk/grammar.mk
+++ b/mk/grammar.mk
@@ -14,6 +14,11 @@ B = $(CFG_BUILD_DIR)/$(CFG_BUILD)/stage2/
 L = $(B)lib/rustlib/$(CFG_BUILD)/lib
 LD = $(CFG_BUILD)/stage2/lib/rustlib/$(CFG_BUILD)/lib/
 RUSTC = $(STAGE2_T_$(CFG_BUILD)_H_$(CFG_BUILD))
+ifeq ($(CFG_OSTYPE),apple-darwin)
+	FLEX_LDFLAGS=-ll
+else
+	FLEX_LDFLAGS=-lfl
+endif
 
 # Run the reference lexer against libsyntax and compare the tokens and spans.
 # If "// ignore-lexer-test" is present in the file, it will be ignored.
@@ -67,3 +72,46 @@ $(info cfg: javac not available, skipping lexer test...)
 check-lexer:
 
 endif
+
+$(BG)lex.yy.c: $(SG)lexer.l $(BG)
+	@$(call E, flex: $@)
+	$(Q)$(CFG_FLEX) -o $@ $<
+
+$(BG)lexer-lalr.o: $(BG)lex.yy.c $(BG)parser-lalr.tab.h
+	@$(call E, cc: $@)
+	$(Q)$(CFG_CC) -include $(BG)parser-lalr.tab.h -c -o $@ $<
+
+$(BG)parser-lalr.tab.c $(BG)parser-lalr.tab.h: $(SG)parser-lalr.y
+	@$(call E, bison: $@)
+	$(Q)$(CFG_BISON) $< --output=$(BG)parser-lalr.tab.c --defines=$(BG)parser-lalr.tab.h \
+		--name-prefix=rs --warnings=error=all
+
+$(BG)parser-lalr.o: $(BG)parser-lalr.tab.c
+	@$(call E, cc: $@)
+	$(Q)$(CFG_CC) -c -o $@ $<
+
+$(BG)parser-lalr-main.o: $(SG)parser-lalr-main.c
+	@$(call E, cc: $@)
+	$(Q)$(CFG_CC) -std=c99 -c -o $@ $<
+
+$(BG)parser-lalr: $(BG)parser-lalr.o $(BG)parser-lalr-main.o $(BG)lexer-lalr.o
+	@$(call E, cc: $@)
+	$(Q)$(CFG_CC) -o $@ $^ $(FLEX_LDFLAGS)
+
+
+ifdef CFG_FLEX
+ifdef CFG_BISON
+check-grammar: $(BG) $(BG)parser-lalr
+	$(info Verifying grammar ...)
+	$(SG)testparser.py -p $(BG)parser-lalr -s $(S)src
+
+else
+$(info cfg: bison not available, skipping parser test...)
+check-grammar:
+
+endif
+else
+$(info cfg: flex not available, skipping parser test...)
+check-grammar:
+
+endif
diff --git a/src/grammar/lexer.l b/src/grammar/lexer.l
new file mode 100644
index 00000000000..58202a08c37
--- /dev/null
+++ b/src/grammar/lexer.l
@@ -0,0 +1,342 @@
+%{
+// Copyright 2015 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 <stdio.h>
+#include <ctype.h>
+
+static int num_hashes;
+static int end_hashes;
+static int saw_non_hash;
+
+%}
+
+%option stack
+%option yylineno
+
+%x str
+%x rawstr
+%x rawstr_esc_begin
+%x rawstr_esc_body
+%x rawstr_esc_end
+%x byte
+%x bytestr
+%x rawbytestr
+%x rawbytestr_nohash
+%x pound
+%x shebang_or_attr
+%x ltorchar
+%x linecomment
+%x doc_line
+%x blockcomment
+%x doc_block
+%x suffix
+
+ident [a-zA-Z\x80-\xff_][a-zA-Z0-9\x80-\xff_]*
+
+%%
+
+<suffix>{ident}            { BEGIN(INITIAL); }
+<suffix>(.|\n)  { yyless(0); BEGIN(INITIAL); }
+
+[ \n\t\r]             { }
+
+\xef\xbb\xbf {
+  // UTF-8 byte order mark (BOM), ignore if in line 1, error otherwise
+  if (yyget_lineno() != 1) {
+    return -1;
+  }
+}
+
+\/\/(\/|\!)           { BEGIN(doc_line); yymore(); }
+<doc_line>\n          { BEGIN(INITIAL);
+                        yyleng--;
+                        yytext[yyleng] = 0;
+                        return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT);
+                      }
+<doc_line>[^\n]*      { yymore(); }
+
+\/\/|\/\/\/\/         { BEGIN(linecomment); }
+<linecomment>\n       { BEGIN(INITIAL); }
+<linecomment>[^\n]*   { }
+
+\/\*(\*|\!)[^*]       { yy_push_state(INITIAL); yy_push_state(doc_block); yymore(); }
+<doc_block>\/\*       { yy_push_state(doc_block); yymore(); }
+<doc_block>\*\/       {
+    yy_pop_state();
+    if (yy_top_state() == doc_block) {
+        yymore();
+    } else {
+        return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT);
+    }
+}
+<doc_block>(.|\n)     { yymore(); }
+
+\/\*                  { yy_push_state(blockcomment); }
+<blockcomment>\/\*    { yy_push_state(blockcomment); }
+<blockcomment>\*\/    { yy_pop_state(); }
+<blockcomment>(.|\n)   { }
+
+_        { return UNDERSCORE; }
+as       { return AS; }
+box      { return BOX; }
+break    { return BREAK; }
+const    { return CONST; }
+continue { return CONTINUE; }
+crate    { return CRATE; }
+else     { return ELSE; }
+enum     { return ENUM; }
+extern   { return EXTERN; }
+false    { return FALSE; }
+fn       { return FN; }
+for      { return FOR; }
+if       { return IF; }
+impl     { return IMPL; }
+in       { return IN; }
+let      { return LET; }
+loop     { return LOOP; }
+match    { return MATCH; }
+mod      { return MOD; }
+move     { return MOVE; }
+mut      { return MUT; }
+priv     { return PRIV; }
+proc     { return PROC; }
+pub      { return PUB; }
+ref      { return REF; }
+return   { return RETURN; }
+self     { return SELF; }
+static   { return STATIC; }
+struct   { return STRUCT; }
+trait    { return TRAIT; }
+true     { return TRUE; }
+type     { return TYPE; }
+typeof   { return TYPEOF; }
+unsafe   { return UNSAFE; }
+use      { return USE; }
+where    { return WHERE; }
+while    { return WHILE; }
+
+{ident}  { return IDENT; }
+
+0x[0-9a-fA-F_]+                                    { BEGIN(suffix); return LIT_INTEGER; }
+0o[0-8_]+                                          { BEGIN(suffix); return LIT_INTEGER; }
+0b[01_]+                                           { BEGIN(suffix); return LIT_INTEGER; }
+[0-9][0-9_]*                                       { BEGIN(suffix); return LIT_INTEGER; }
+[0-9][0-9_]*\.(\.|[a-zA-Z])    { yyless(yyleng - 2); BEGIN(suffix); return LIT_INTEGER; }
+
+[0-9][0-9_]*\.[0-9_]*([eE][-\+]?[0-9_]+)?          { BEGIN(suffix); return LIT_FLOAT; }
+[0-9][0-9_]*(\.[0-9_]*)?[eE][-\+]?[0-9_]+          { BEGIN(suffix); return LIT_FLOAT; }
+
+;      { return ';'; }
+,      { return ','; }
+\.\.\. { return DOTDOTDOT; }
+\.\.   { return DOTDOT; }
+\.     { return '.'; }
+\(     { return '('; }
+\)     { return ')'; }
+\{     { return '{'; }
+\}     { return '}'; }
+\[     { return '['; }
+\]     { return ']'; }
+@      { return '@'; }
+#      { BEGIN(pound); yymore(); }
+<pound>\! { BEGIN(shebang_or_attr); yymore(); }
+<shebang_or_attr>\[ {
+  BEGIN(INITIAL);
+  yyless(2);
+  return SHEBANG;
+}
+<shebang_or_attr>[^\[\n]*\n {
+  // Since the \n was eaten as part of the token, yylineno will have
+  // been incremented to the value 2 if the shebang was on the first
+  // line. This yyless undoes that, setting yylineno back to 1.
+  yyless(yyleng - 1);
+  if (yyget_lineno() == 1) {
+    BEGIN(INITIAL);
+    return SHEBANG_LINE;
+  } else {
+    BEGIN(INITIAL);
+    yyless(2);
+    return SHEBANG;
+  }
+}
+<pound>. { BEGIN(INITIAL); yyless(1); return '#'; }
+
+\~     { return '~'; }
+::     { return MOD_SEP; }
+:      { return ':'; }
+\$     { return '$'; }
+\?     { return '?'; }
+
+==    { return EQEQ; }
+=>    { return FAT_ARROW; }
+=     { return '='; }
+\!=   { return NE; }
+\!    { return '!'; }
+\<=   { return LE; }
+\<\<  { return SHL; }
+\<\<= { return SHLEQ; }
+\<    { return '<'; }
+\>=   { return GE; }
+\>\>  { return SHR; }
+\>\>= { return SHREQ; }
+\>    { return '>'; }
+
+\x27                                  { BEGIN(ltorchar); yymore(); }
+<ltorchar>static                      { BEGIN(INITIAL); return STATIC_LIFETIME; }
+<ltorchar>{ident}                     { BEGIN(INITIAL); return LIFETIME; }
+<ltorchar>\\[nrt\\\x27\x220]\x27      { BEGIN(suffix); return LIT_CHAR; }
+<ltorchar>\\x[0-9a-fA-F]{2}\x27       { BEGIN(suffix); return LIT_CHAR; }
+<ltorchar>\\u\{[0-9a-fA-F]?{6}\}\x27  { BEGIN(suffix); return LIT_CHAR; }
+<ltorchar>.\x27                       { BEGIN(suffix); return LIT_CHAR; }
+<ltorchar>[\x80-\xff]{2,4}\x27        { BEGIN(suffix); return LIT_CHAR; }
+<ltorchar><<EOF>>                     { BEGIN(INITIAL); return -1; }
+
+b\x22              { BEGIN(bytestr); yymore(); }
+<bytestr>\x22      { BEGIN(suffix); return LIT_BINARY; }
+
+<bytestr><<EOF>>                { return -1; }
+<bytestr>\\[n\nrt\\\x27\x220]   { yymore(); }
+<bytestr>\\x[0-9a-fA-F]{2}      { yymore(); }
+<bytestr>\\u\{[0-9a-fA-F]?{6}\} { yymore(); }
+<bytestr>\\[^n\nrt\\\x27\x220]  { return -1; }
+<bytestr>(.|\n)                 { yymore(); }
+
+br\x22                      { BEGIN(rawbytestr_nohash); yymore(); }
+<rawbytestr_nohash>\x22     { BEGIN(suffix); return LIT_BINARY_RAW; }
+<rawbytestr_nohash>(.|\n)   { yymore(); }
+<rawbytestr_nohash><<EOF>>  { return -1; }
+
+br/# {
+    BEGIN(rawbytestr);
+    yymore();
+    num_hashes = 0;
+    saw_non_hash = 0;
+    end_hashes = 0;
+}
+<rawbytestr># {
+    if (!saw_non_hash) {
+        num_hashes++;
+    } else if (end_hashes != 0) {
+        end_hashes++;
+        if (end_hashes == num_hashes) {
+            BEGIN(INITIAL);
+            return LIT_BINARY_RAW;
+        }
+    }
+    yymore();
+}
+<rawbytestr>\x22# {
+    end_hashes = 1;
+    if (end_hashes == num_hashes) {
+        BEGIN(INITIAL);
+        return LIT_BINARY_RAW;
+    }
+    yymore();
+}
+<rawbytestr>(.|\n) {
+    if (!saw_non_hash) {
+        saw_non_hash = 1;
+    }
+    if (end_hashes != 0) {
+        end_hashes = 0;
+    }
+    yymore();
+}
+<rawbytestr><<EOF>> { return -1; }
+
+b\x27                        { BEGIN(byte); yymore(); }
+<byte>\\[nrt\\\x27\x220]\x27 { BEGIN(INITIAL); return LIT_BYTE; }
+<byte>\\x[0-9a-fA-F]{2}\x27  { BEGIN(INITIAL); return LIT_BYTE; }
+<byte>\\u[0-9a-fA-F]{4}\x27  { BEGIN(INITIAL); return LIT_BYTE; }
+<byte>\\U[0-9a-fA-F]{8}\x27  { BEGIN(INITIAL); return LIT_BYTE; }
+<byte>.\x27                  { BEGIN(INITIAL); return LIT_BYTE; }
+<byte><<EOF>>                { BEGIN(INITIAL); return -1; }
+
+r\x22           { BEGIN(rawstr); yymore(); }
+<rawstr>\x22    { BEGIN(suffix); return LIT_STR_RAW; }
+<rawstr>(.|\n)  { yymore(); }
+<rawstr><<EOF>> { return -1; }
+
+r/#             {
+    BEGIN(rawstr_esc_begin);
+    yymore();
+    num_hashes = 0;
+    saw_non_hash = 0;
+    end_hashes = 0;
+}
+
+<rawstr_esc_begin># {
+    num_hashes++;
+    yymore();
+}
+<rawstr_esc_begin>\x22 {
+    BEGIN(rawstr_esc_body);
+    yymore();
+}
+<rawstr_esc_begin>(.|\n) { return -1; }
+
+<rawstr_esc_body>\x22/# {
+  BEGIN(rawstr_esc_end);
+  yymore();
+ }
+<rawstr_esc_body>(.|\n) {
+  yymore();
+ }
+
+<rawstr_esc_end># {
+  end_hashes++;
+  if (end_hashes == num_hashes) {
+    BEGIN(INITIAL);
+    return LIT_STR_RAW;
+  }
+  yymore();
+ }
+<rawstr_esc_end>[^#] {
+  end_hashes = 0;
+  BEGIN(rawstr_esc_body);
+  yymore();
+ }
+
+<rawstr_esc_begin,rawstr_esc_body,rawstr_esc_end><<EOF>> { return -1; }
+
+\x22                     { BEGIN(str); yymore(); }
+<str>\x22                { BEGIN(suffix); return LIT_STR; }
+
+<str><<EOF>>                { return -1; }
+<str>\\[n\nrt\\\x27\x220]   { yymore(); }
+<str>\\x[0-9a-fA-F]{2}      { yymore(); }
+<str>\\u\{[0-9a-fA-F]?{6}\} { yymore(); }
+<str>\\[^n\nrt\\\x27\x220]  { return -1; }
+<str>(.|\n)                 { yymore(); }
+
+-\>  { return RARROW; }
+-    { return '-'; }
+-=   { return MINUSEQ; }
+&&   { return ANDAND; }
+&    { return '&'; }
+&=   { return ANDEQ; }
+\|\| { return OROR; }
+\|   { return '|'; }
+\|=  { return OREQ; }
+\+   { return '+'; }
+\+=  { return PLUSEQ; }
+\*   { return '*'; }
+\*=  { return STAREQ; }
+\/   { return '/'; }
+\/=  { return SLASHEQ; }
+\^   { return '^'; }
+\^=  { return CARETEQ; }
+%    { return '%'; }
+%=   { return PERCENTEQ; }
+
+<<EOF>> { return 0; }
+
+%%
diff --git a/src/grammar/parser-lalr-main.c b/src/grammar/parser-lalr-main.c
new file mode 100644
index 00000000000..db88a1f2999
--- /dev/null
+++ b/src/grammar/parser-lalr-main.c
@@ -0,0 +1,203 @@
+// Copyright 2015 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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern int yylex();
+extern int rsparse();
+
+#define PUSHBACK_LEN 4
+
+static char pushback[PUSHBACK_LEN];
+static int verbose;
+
+void print(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  if (verbose) {
+    vprintf(format, args);
+  }
+  va_end(args);
+}
+
+// If there is a non-null char at the head of the pushback queue,
+// dequeue it and shift the rest of the queue forwards. Otherwise,
+// return the token from calling yylex.
+int rslex() {
+  if (pushback[0] == '\0') {
+    return yylex();
+  } else {
+    char c = pushback[0];
+    memmove(pushback, pushback + 1, PUSHBACK_LEN - 1);
+    pushback[PUSHBACK_LEN - 1] = '\0';
+    return c;
+  }
+}
+
+// Note: this does nothing if the pushback queue is full. As long as
+// there aren't more than PUSHBACK_LEN consecutive calls to push_back
+// in an action, this shouldn't be a problem.
+void push_back(char c) {
+  for (int i = 0; i < PUSHBACK_LEN; ++i) {
+    if (pushback[i] == '\0') {
+      pushback[i] = c;
+      break;
+    }
+  }
+}
+
+extern int rsdebug;
+
+struct node {
+  struct node *next;
+  struct node *prev;
+  int own_string;
+  char const *name;
+  int n_elems;
+  struct node *elems[];
+};
+
+struct node *nodes = NULL;
+int n_nodes;
+
+struct node *mk_node(char const *name, int n, ...) {
+  va_list ap;
+  int i = 0;
+  unsigned sz = sizeof(struct node) + (n * sizeof(struct node *));
+  struct node *nn, *nd = (struct node *)malloc(sz);
+
+  print("# New %d-ary node: %s = %p\n", n, name, nd);
+
+  nd->own_string = 0;
+  nd->prev = NULL;
+  nd->next = nodes;
+  if (nodes) {
+    nodes->prev = nd;
+  }
+  nodes = nd;
+
+  nd->name = name;
+  nd->n_elems = n;
+
+  va_start(ap, n);
+  while (i < n) {
+    nn = va_arg(ap, struct node *);
+    print("#   arg[%d]: %p\n", i, nn);
+    print("#            (%s ...)\n", nn->name);
+    nd->elems[i++] = nn;
+  }
+  va_end(ap);
+  n_nodes++;
+  return nd;
+}
+
+struct node *mk_atom(char *name) {
+  struct node *nd = mk_node((char const *)strdup(name), 0);
+  nd->own_string = 1;
+  return nd;
+}
+
+struct node *mk_none() {
+  return mk_atom("<none>");
+}
+
+struct node *ext_node(struct node *nd, int n, ...) {
+  va_list ap;
+  int i = 0, c = nd->n_elems + n;
+  unsigned sz = sizeof(struct node) + (c * sizeof(struct node *));
+  struct node *nn;
+
+  print("# Extending %d-ary node by %d nodes: %s = %p",
+        nd->n_elems, c, nd->name, nd);
+
+  if (nd->next) {
+    nd->next->prev = nd->prev;
+  }
+  if (nd->prev) {
+    nd->prev->next = nd->next;
+  }
+  nd = realloc(nd, sz);
+  nd->prev = NULL;
+  nd->next = nodes;
+  nodes->prev = nd;
+  nodes = nd;
+
+  print(" ==> %p\n", nd);
+
+  va_start(ap, n);
+  while (i < n) {
+    nn = va_arg(ap, struct node *);
+    print("#   arg[%d]: %p\n", i, nn);
+    print("#            (%s ...)\n", nn->name);
+    nd->elems[nd->n_elems++] = nn;
+    ++i;
+  }
+  va_end(ap);
+  return nd;
+}
+
+int const indent_step = 4;
+
+void print_indent(int depth) {
+  while (depth) {
+    if (depth-- % indent_step == 0) {
+      print("|");
+    } else {
+      print(" ");
+    }
+  }
+}
+
+void print_node(struct node *n, int depth) {
+  int i = 0;
+  print_indent(depth);
+  if (n->n_elems == 0) {
+    print("%s\n", n->name);
+  } else {
+    print("(%s\n", n->name);
+    for (i = 0; i < n->n_elems; ++i) {
+      print_node(n->elems[i], depth + indent_step);
+    }
+    print_indent(depth);
+    print(")\n");
+  }
+}
+
+int main(int argc, char **argv) {
+  if (argc == 2 && strcmp(argv[1], "-v") == 0) {
+    verbose = 1;
+  } else {
+    verbose = 0;
+  }
+  int ret = 0;
+  struct node *tmp;
+  memset(pushback, '\0', PUSHBACK_LEN);
+  ret = rsparse();
+  print("--- PARSE COMPLETE: ret:%d, n_nodes:%d ---\n", ret, n_nodes);
+  if (nodes) {
+    print_node(nodes, 0);
+  }
+  while (nodes) {
+    tmp = nodes;
+    nodes = tmp->next;
+    if (tmp->own_string) {
+      free((void*)tmp->name);
+    }
+    free(tmp);
+  }
+  return ret;
+}
+
+void rserror(char const *s) {
+  fprintf(stderr, "%s\n", s);
+}
diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y
new file mode 100644
index 00000000000..24185ed65d5
--- /dev/null
+++ b/src/grammar/parser-lalr.y
@@ -0,0 +1,1912 @@
+// Copyright 2015 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.
+
+%{
+#define YYERROR_VERBOSE
+#define YYSTYPE struct node *
+struct node;
+extern int yylex();
+extern void yyerror(char const *s);
+extern struct node *mk_node(char const *name, int n, ...);
+extern struct node *mk_atom(char *text);
+extern struct node *mk_none();
+extern struct node *ext_node(struct node *nd, int n, ...);
+extern void push_back(char c);
+extern char *yytext;
+%}
+%debug
+
+%token SHL
+%token SHR
+%token LE
+%token EQEQ
+%token NE
+%token GE
+%token ANDAND
+%token OROR
+%token SHLEQ
+%token SHREQ
+%token MINUSEQ
+%token ANDEQ
+%token OREQ
+%token PLUSEQ
+%token STAREQ
+%token SLASHEQ
+%token CARETEQ
+%token PERCENTEQ
+%token DOTDOT
+%token DOTDOTDOT
+%token MOD_SEP
+%token RARROW
+%token FAT_ARROW
+%token LIT_BYTE
+%token LIT_CHAR
+%token LIT_INTEGER
+%token LIT_FLOAT
+%token LIT_STR
+%token LIT_STR_RAW
+%token LIT_BINARY
+%token LIT_BINARY_RAW
+%token IDENT
+%token UNDERSCORE
+%token LIFETIME
+
+// keywords
+%token SELF
+%token STATIC
+%token AS
+%token BREAK
+%token CRATE
+%token ELSE
+%token ENUM
+%token EXTERN
+%token FALSE
+%token FN
+%token FOR
+%token IF
+%token IMPL
+%token IN
+%token LET
+%token LOOP
+%token MATCH
+%token MOD
+%token MOVE
+%token MUT
+%token PRIV
+%token PUB
+%token REF
+%token RETURN
+%token STRUCT
+%token TRUE
+%token TRAIT
+%token TYPE
+%token UNSAFE
+%token USE
+%token WHILE
+%token CONTINUE
+%token PROC
+%token BOX
+%token CONST
+%token WHERE
+%token TYPEOF
+%token INNER_DOC_COMMENT
+%token OUTER_DOC_COMMENT
+
+%token SHEBANG
+%token SHEBANG_LINE
+%token STATIC_LIFETIME
+
+ /*
+   Quoting from the Bison manual:
+
+   "Finally, the resolution of conflicts works by comparing the precedence
+   of the rule being considered with that of the lookahead token. If the
+   token's precedence is higher, the choice is to shift. If the rule's
+   precedence is higher, the choice is to reduce. If they have equal
+   precedence, the choice is made based on the associativity of that
+   precedence level. The verbose output file made by ‘-v’ (see Invoking
+   Bison) says how each conflict was resolved"
+ */
+
+// We expect no shift/reduce or reduce/reduce conflicts in this grammar;
+// all potential ambiguities are scrutinized and eliminated manually.
+%expect 0
+
+// fake-precedence symbol to cause '|' bars in lambda context to parse
+// at low precedence, permit things like |x| foo = bar, where '=' is
+// otherwise lower-precedence than '|'. Also used for proc() to cause
+// things like proc() a + b to parse as proc() { a + b }.
+%precedence LAMBDA
+
+%precedence SELF
+
+// MUT should be lower precedence than IDENT so that in the pat rule,
+// "& MUT pat" has higher precedence than "binding_mode ident [@ pat]"
+%precedence MUT
+
+// IDENT needs to be lower than '{' so that 'foo {' is shifted when
+// trying to decide if we've got a struct-construction expr (esp. in
+// contexts like 'if foo { .')
+//
+// IDENT also needs to be lower precedence than '<' so that '<' in
+// 'foo:bar . <' is shifted (in a trait reference occurring in a
+// bounds list), parsing as foo:(bar<baz>) rather than (foo:bar)<baz>.
+%precedence IDENT
+
+// A couple fake-precedence symbols to use in rules associated with +
+// and < in trailing type contexts. These come up when you have a type
+// in the RHS of operator-AS, such as "foo as bar<baz>". The "<" there
+// has to be shifted so the parser keeps trying to parse a type, even
+// though it might well consider reducing the type "bar" and then
+// going on to "<" as a subsequent binop. The "+" case is with
+// trailing type-bounds ("foo as bar:A+B"), for the same reason.
+%precedence SHIFTPLUS
+
+%precedence MOD_SEP
+%precedence RARROW ':'
+
+// Binops & unops, and their precedences
+%precedence BOX
+%precedence BOXPLACE
+%nonassoc DOTDOT
+
+// RETURN needs to be lower-precedence than tokens that start
+// prefix_exprs
+%precedence RETURN
+
+%left '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ
+%left OROR
+%left ANDAND
+%left EQEQ NE
+%left '<' '>' LE GE
+%left '|'
+%left '^'
+%left '&'
+%left SHL SHR
+%left '+' '-'
+%precedence AS
+%left '*' '/' '%'
+%precedence '!'
+
+%precedence '{' '[' '(' '.'
+
+%start crate
+
+%%
+
+////////////////////////////////////////////////////////////////////////
+// Part 1: Items and attributes
+////////////////////////////////////////////////////////////////////////
+
+crate
+: maybe_shebang inner_attrs maybe_mod_items  { mk_node("crate", 2, $2, $3); }
+| maybe_shebang maybe_mod_items  { mk_node("crate", 1, $2); }
+;
+
+maybe_shebang
+: SHEBANG_LINE
+| %empty
+;
+
+maybe_inner_attrs
+: inner_attrs
+| %empty                   { $$ = mk_none(); }
+;
+
+inner_attrs
+: inner_attr               { $$ = mk_node("InnerAttrs", 1, $1); }
+| inner_attrs inner_attr   { $$ = ext_node($1, 1, $2); }
+;
+
+inner_attr
+: SHEBANG '[' meta_item ']'   { $$ = mk_node("InnerAttr", 1, $3); }
+| INNER_DOC_COMMENT           { $$ = mk_node("InnerAttr", 1, mk_node("doc-comment", 1, mk_atom(yytext))); }
+;
+
+maybe_outer_attrs
+: outer_attrs
+| %empty                   { $$ = mk_none(); }
+;
+
+outer_attrs
+: outer_attr               { $$ = mk_node("OuterAttrs", 1, $1); }
+| outer_attrs outer_attr   { $$ = ext_node($1, 1, $2); }
+;
+
+outer_attr
+: '#' '[' meta_item ']'    { $$ = $3; }
+| OUTER_DOC_COMMENT        { $$ = mk_node("doc-comment", 1, mk_atom(yytext)); }
+;
+
+meta_item
+: ident                      { $$ = mk_node("MetaWord", 1, $1); }
+| ident '=' lit              { $$ = mk_node("MetaNameValue", 2, $1, $3); }
+| ident '(' meta_seq ')'     { $$ = mk_node("MetaList", 2, $1, $3); }
+| ident '(' meta_seq ',' ')' { $$ = mk_node("MetaList", 2, $1, $3); }
+;
+
+meta_seq
+: %empty                   { $$ = mk_none(); }
+| meta_item                { $$ = mk_node("MetaItems", 1, $1); }
+| meta_seq ',' meta_item   { $$ = ext_node($1, 1, $3); }
+;
+
+maybe_mod_items
+: mod_items
+| %empty             { $$ = mk_none(); }
+;
+
+mod_items
+: mod_item                               { $$ = mk_node("Items", 1, $1); }
+| mod_items mod_item                     { $$ = ext_node($1, 1, $2); }
+;
+
+attrs_and_vis
+: maybe_outer_attrs visibility           { $$ = mk_node("AttrsAndVis", 2, $1, $2); }
+;
+
+mod_item
+: attrs_and_vis item    { $$ = mk_node("Item", 2, $1, $2); }
+;
+
+// items that can appear outside of a fn block
+item
+: item_static
+| item_const
+| item_type
+| block_item
+| view_item
+| item_macro
+;
+
+// items that can appear in "stmts"
+stmt_item
+: item_static
+| item_const
+| item_type
+| block_item
+| use_item
+| extern_fn_item
+;
+
+item_static
+: STATIC ident ':' ty '=' expr ';'  { $$ = mk_node("ItemStatic", 3, $2, $4, $6); }
+| STATIC MUT ident ':' ty '=' expr ';'  { $$ = mk_node("ItemStatic", 3, $3, $5, $7); }
+;
+
+item_const
+: CONST ident ':' ty '=' expr ';'  { $$ = mk_node("ItemConst", 3, $2, $4, $6); }
+;
+
+item_macro
+: path_expr '!' maybe_ident parens_delimited_token_trees ';'
+| path_expr '!' maybe_ident braces_delimited_token_trees
+| path_expr '!' maybe_ident brackets_delimited_token_trees ';'
+;
+
+view_item
+: use_item
+| extern_fn_item
+| EXTERN CRATE ident ';'                      { $$ = mk_node("ViewItemExternCrate", 1, $3); }
+| EXTERN CRATE ident '=' str ';'              { $$ = mk_node("ViewItemExternCrate", 2, $3, $5); }
+| EXTERN CRATE str AS ident ';'               { $$ = mk_node("ViewItemExternCrate", 2, $3, $5); }
+;
+
+extern_fn_item
+: EXTERN maybe_abi item_fn                    { $$ = mk_node("ViewItemExternFn", 2, $2, $3); }
+;
+
+use_item
+: USE view_path ';'                           { $$ = mk_node("ViewItemUse", 1, $2); }
+;
+
+view_path
+: path_no_types_allowed                                    { $$ = mk_node("ViewPathSimple", 1, $1); }
+| path_no_types_allowed MOD_SEP '{'                '}'     { $$ = mk_node("ViewPathList", 2, $1, mk_atom("ViewPathListEmpty")); }
+| path_no_types_allowed MOD_SEP '{' idents_or_self '}'     { $$ = mk_node("ViewPathList", 2, $1, $4); }
+| path_no_types_allowed MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 2, $1, $4); }
+| path_no_types_allowed MOD_SEP '*'                        { $$ = mk_node("ViewPathGlob", 1, $1); }
+|                               '{'                '}'     { $$ = mk_atom("ViewPathListEmpty"); }
+|                               '{' idents_or_self '}'     { $$ = mk_node("ViewPathList", 1, $2); }
+|                               '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $2); }
+| path_no_types_allowed AS ident                           { $$ = mk_node("ViewPathSimple", 2, $1, $3); }
+;
+
+block_item
+: item_fn
+| item_unsafe_fn
+| item_mod
+| item_foreign_mod          { $$ = mk_node("ItemForeignMod", 1, $1); }
+| item_struct
+| item_enum
+| item_trait
+| item_impl
+;
+
+maybe_ty_ascription
+: ':' ty { $$ = $2; }
+| %empty { $$ = mk_none(); }
+;
+
+maybe_init_expr
+: '=' expr { $$ = $2; }
+| %empty   { $$ = mk_none(); }
+;
+
+// structs
+item_struct
+: STRUCT ident generic_params maybe_where_clause struct_decl_args
+{
+  $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5);
+}
+| STRUCT ident generic_params struct_tuple_args maybe_where_clause ';'
+{
+  $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5);
+}
+| STRUCT ident generic_params maybe_where_clause ';'
+{
+  $$ = mk_node("ItemStruct", 3, $2, $3, $4);
+}
+;
+
+struct_decl_args
+: '{' struct_decl_fields '}'                  { $$ = $2; }
+| '{' struct_decl_fields ',' '}'              { $$ = $2; }
+;
+
+struct_tuple_args
+: '(' struct_tuple_fields ')'                 { $$ = $2; }
+| '(' struct_tuple_fields ',' ')'             { $$ = $2; }
+;
+
+struct_decl_fields
+: struct_decl_field                           { $$ = mk_node("StructFields", 1, $1); }
+| struct_decl_fields ',' struct_decl_field    { $$ = ext_node($1, 1, $3); }
+| %empty                                      { $$ = mk_none(); }
+;
+
+struct_decl_field
+: attrs_and_vis ident ':' ty_sum              { $$ = mk_node("StructField", 3, $1, $2, $4); }
+;
+
+struct_tuple_fields
+: struct_tuple_field                          { $$ = mk_node("StructFields", 1, $1); }
+| struct_tuple_fields ',' struct_tuple_field  { $$ = ext_node($1, 1, $3); }
+;
+
+struct_tuple_field
+: attrs_and_vis ty_sum                    { $$ = mk_node("StructField", 2, $1, $2); }
+;
+
+// enums
+item_enum
+: ENUM ident generic_params maybe_where_clause '{' enum_defs '}'     { $$ = mk_node("ItemEnum", 0); }
+| ENUM ident generic_params maybe_where_clause '{' enum_defs ',' '}' { $$ = mk_node("ItemEnum", 0); }
+;
+
+enum_defs
+: enum_def               { $$ = mk_node("EnumDefs", 1, $1); }
+| enum_defs ',' enum_def { $$ = ext_node($1, 1, $3); }
+| %empty                 { $$ = mk_none(); }
+;
+
+enum_def
+: attrs_and_vis ident enum_args { $$ = mk_node("EnumDef", 3, $1, $2, $3); }
+;
+
+enum_args
+: '{' struct_decl_fields '}'     { $$ = mk_node("EnumArgs", 1, $2); }
+| '{' struct_decl_fields ',' '}' { $$ = mk_node("EnumArgs", 1, $2); }
+| '(' maybe_ty_sums ')'          { $$ = mk_node("EnumArgs", 1, $2); }
+| '=' expr                       { $$ = mk_node("EnumArgs", 1, $2); }
+| %empty                         { $$ = mk_none(); }
+;
+
+item_mod
+: MOD ident ';'                                 { $$ = mk_node("ItemMod", 1, $2); }
+| MOD ident '{' maybe_mod_items '}'             { $$ = mk_node("ItemMod", 2, $2, $4); }
+| MOD ident '{' inner_attrs maybe_mod_items '}' { $$ = mk_node("ItemMod", 3, $2, $4, $5); }
+;
+
+item_foreign_mod
+: EXTERN maybe_abi '{' maybe_foreign_items '}'             { $$ = mk_node("ItemForeignMod", 1, $4); }
+| EXTERN maybe_abi '{' inner_attrs maybe_foreign_items '}' { $$ = mk_node("ItemForeignMod", 2, $4, $5); }
+;
+
+maybe_abi
+: str
+| %empty { $$ = mk_none(); }
+;
+
+maybe_foreign_items
+: foreign_items
+| %empty { $$ = mk_none(); }
+;
+
+foreign_items
+: foreign_item               { $$ = mk_node("ForeignItems", 1, $1); }
+| foreign_items foreign_item { $$ = ext_node($1, 1, $2); }
+;
+
+foreign_item
+: attrs_and_vis STATIC item_foreign_static { $$ = mk_node("ForeignItem", 2, $1, $3); }
+| attrs_and_vis item_foreign_fn            { $$ = mk_node("ForeignItem", 2, $1, $2); }
+| attrs_and_vis UNSAFE item_foreign_fn     { $$ = mk_node("ForeignItem", 2, $1, $3); }
+;
+
+item_foreign_static
+: maybe_mut ident ':' ty ';'               { $$ = mk_node("StaticItem", 3, $1, $2, $4); }
+;
+
+item_foreign_fn
+: FN ident generic_params fn_decl_allow_variadic maybe_where_clause ';' { $$ = mk_node("ForeignFn", 4, $2, $3, $4, $5); }
+;
+
+fn_decl_allow_variadic
+: fn_params_allow_variadic ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); }
+;
+
+fn_params_allow_variadic
+: '(' ')'                      { $$ = mk_none(); }
+| '(' params ')'               { $$ = $2; }
+| '(' params ',' ')'           { $$ = $2; }
+| '(' params ',' DOTDOTDOT ')' { $$ = $2; }
+;
+
+visibility
+: PUB      { $$ = mk_atom("Public"); }
+| %empty   { $$ = mk_atom("Inherited"); }
+;
+
+idents_or_self
+: ident_or_self                    { $$ = mk_node("IdentsOrSelf", 1, $1); }
+| idents_or_self ',' ident_or_self { $$ = ext_node($1, 1, $3); }
+;
+
+ident_or_self
+: ident
+| SELF  { $$ = mk_atom(yytext); }
+;
+
+item_type
+: TYPE ident generic_params maybe_where_clause '=' ty_sum ';'  { $$ = mk_node("ItemTy", 4, $2, $3, $4, $6); }
+;
+
+for_sized
+: FOR '?' ident { $$ = mk_node("ForSized", 1, $3); }
+| FOR ident '?' { $$ = mk_node("ForSized", 1, $2); }
+| %empty        { $$ = mk_none(); }
+;
+
+item_trait
+: maybe_unsafe TRAIT ident generic_params for_sized maybe_ty_param_bounds maybe_where_clause '{' maybe_trait_items '}'
+{
+  $$ = mk_node("ItemTrait", 7, $1, $3, $4, $5, $6, $7, $9);
+}
+;
+
+maybe_trait_items
+: trait_items
+| %empty { $$ = mk_none(); }
+;
+
+trait_items
+: trait_item               { $$ = mk_node("TraitItems", 1, $1); }
+| trait_items trait_item   { $$ = ext_node($1, 1, $2); }
+;
+
+trait_item
+: trait_type
+| trait_method
+;
+
+trait_type
+: maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); }
+;
+
+maybe_unsafe
+: UNSAFE { $$ = mk_atom("Unsafe"); }
+| %empty { $$ = mk_none(); }
+;
+
+trait_method
+: type_method { $$ = mk_node("Required", 1, $1); }
+| method      { $$ = mk_node("Provided", 1, $1); }
+;
+
+type_method
+: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';'
+{
+  $$ = mk_node("TypeMethod", 6, $1, $2, $4, $5, $6, $7);
+}
+| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';'
+{
+  $$ = mk_node("TypeMethod", 7, $1, $2, $4, $6, $7, $8, $9);
+}
+;
+
+method
+: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8);
+}
+| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10);
+}
+;
+
+impl_method
+: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8);
+}
+| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10);
+}
+;
+
+// There are two forms of impl:
+//
+// impl (<...>)? TY { ... }
+// impl (<...>)? TRAIT for TY { ... }
+//
+// Unfortunately since TY can begin with '<' itself -- as part of a
+// TyQualifiedPath type -- there's an s/r conflict when we see '<' after IMPL:
+// should we reduce one of the early rules of TY (such as maybe_once)
+// or shall we continue shifting into the generic_params list for the
+// impl?
+//
+// The production parser disambiguates a different case here by
+// permitting / requiring the user to provide parens around types when
+// they are ambiguous with traits. We do the same here, regrettably,
+// by splitting ty into ty and ty_prim.
+item_impl
+: maybe_unsafe IMPL generic_params ty_prim_sum maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}'
+{
+  $$ = mk_node("ItemImpl", 6, $1, $3, $4, $5, $7, $8);
+}
+| maybe_unsafe IMPL generic_params '(' ty ')' maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}'
+{
+  $$ = mk_node("ItemImpl", 6, $1, $3, 5, $6, $9, $10);
+}
+| maybe_unsafe IMPL generic_params trait_ref FOR ty maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}'
+{
+  $$ = mk_node("ItemImpl", 6, $3, $4, $6, $7, $9, $10);
+}
+| maybe_unsafe IMPL generic_params '!' trait_ref FOR ty maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}'
+{
+  $$ = mk_node("ItemImplNeg", 7, $1, $3, $5, $7, $8, $10, $11);
+}
+;
+
+maybe_impl_items
+: impl_items
+| %empty { $$ = mk_none(); }
+;
+
+impl_items
+: impl_item               { $$ = mk_node("ImplItems", 1, $1); }
+| impl_item impl_items    { $$ = ext_node($1, 1, $2); }
+;
+
+impl_item
+: impl_method
+| item_macro
+| trait_type
+;
+
+item_fn
+: FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("ItemFn", 5, $2, $3, $4, $5, $6);
+}
+;
+
+item_unsafe_fn
+: UNSAFE FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("ItemUnsafeFn", 5, $3, $4, $5, $6, $7);
+}
+| UNSAFE EXTERN maybe_abi FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block
+{
+  $$ = mk_node("ItemUnsafeFn", 6, $3, $5, $6, $7, $8, $9);
+}
+;
+
+fn_decl
+: fn_params ret_ty   { $$ = mk_node("FnDecl", 2, $1, $2); }
+;
+
+fn_decl_with_self
+: fn_params_with_self ret_ty   { $$ = mk_node("FnDecl", 2, $1, $2); }
+;
+
+fn_decl_with_self_allow_anon_params
+: fn_anon_params_with_self ret_ty   { $$ = mk_node("FnDecl", 2, $1, $2); }
+;
+
+fn_params
+: '(' maybe_params ')'  { $$ = $2; }
+;
+
+fn_anon_params
+: '(' anon_param anon_params_allow_variadic_tail ')' { $$ = ext_node($2, 1, $3); }
+| '(' ')'                                            { $$ = mk_none(); }
+;
+
+fn_params_with_self
+: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')'              { $$ = mk_node("SelfValue", 3, $2, $4, $5); }
+| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')'          { $$ = mk_node("SelfRegion", 3, $3, $5, $6); }
+| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); }
+| '(' maybe_params ')'                                                       { $$ = mk_node("SelfStatic", 1, $2); }
+;
+
+fn_anon_params_with_self
+: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')'              { $$ = mk_node("SelfValue", 3, $2, $4, $5); }
+| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')'          { $$ = mk_node("SelfRegion", 3, $3, $5, $6); }
+| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); }
+| '(' maybe_anon_params ')'                                                       { $$ = mk_node("SelfStatic", 1, $2); }
+;
+
+maybe_params
+: params
+| params ','
+| %empty  { $$ = mk_none(); }
+;
+
+params
+: param                { $$ = mk_node("Args", 1, $1); }
+| params ',' param     { $$ = ext_node($1, 1, $3); }
+;
+
+param
+: pat ':' ty   { $$ = mk_node("Arg", 2, $1, $3); }
+;
+
+inferrable_params
+: inferrable_param                       { $$ = mk_node("InferrableParams", 1, $1); }
+| inferrable_params ',' inferrable_param { $$ = ext_node($1, 1, $3); }
+;
+
+inferrable_param
+: pat maybe_ty_ascription { $$ = mk_node("InferrableParam", 2, $1, $2); }
+;
+
+maybe_unboxed_closure_kind
+: %empty
+| ':'
+| '&' maybe_mut ':'
+;
+
+maybe_comma_params
+: ','            { $$ = mk_none(); }
+| ',' params     { $$ = $2; }
+| ',' params ',' { $$ = $2; }
+| %empty         { $$ = mk_none(); }
+;
+
+maybe_comma_anon_params
+: ','                 { $$ = mk_none(); }
+| ',' anon_params     { $$ = $2; }
+| ',' anon_params ',' { $$ = $2; }
+| %empty              { $$ = mk_none(); }
+;
+
+maybe_anon_params
+: anon_params
+| anon_params ','
+| %empty      { $$ = mk_none(); }
+;
+
+anon_params
+: anon_param                 { $$ = mk_node("Args", 1, $1); }
+| anon_params ',' anon_param { $$ = ext_node($1, 1, $3); }
+;
+
+// anon means it's allowed to be anonymous (type-only), but it can
+// still have a name
+anon_param
+: named_arg ':' ty   { $$ = mk_node("Arg", 2, $1, $3); }
+| ty
+;
+
+anon_params_allow_variadic_tail
+: ',' DOTDOTDOT                                  { $$ = mk_none(); }
+| ',' anon_param anon_params_allow_variadic_tail { $$ = mk_node("Args", 2, $2, $3); }
+| %empty                                         { $$ = mk_none(); }
+;
+
+named_arg
+: ident
+| UNDERSCORE        { $$ = mk_atom("PatWild"); }
+| '&' ident         { $$ = $2; }
+| '&' UNDERSCORE    { $$ = mk_atom("PatWild"); }
+| ANDAND ident      { $$ = $2; }
+| ANDAND UNDERSCORE { $$ = mk_atom("PatWild"); }
+| MUT ident         { $$ = $2; }
+;
+
+ret_ty
+: RARROW '!'         { $$ = mk_none(); }
+| RARROW ty          { $$ = mk_node("ret-ty", 1, $2); }
+| %prec IDENT %empty { $$ = mk_none(); }
+;
+
+generic_params
+: '<' lifetimes '>'                   { $$ = mk_node("Generics", 2, $2, mk_none()); }
+| '<' lifetimes ',' '>'               { $$ = mk_node("Generics", 2, $2, mk_none()); }
+| '<' lifetimes SHR                   { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); }
+| '<' lifetimes ',' SHR               { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); }
+| '<' lifetimes ',' ty_params '>'     { $$ = mk_node("Generics", 2, $2, $4); }
+| '<' lifetimes ',' ty_params ',' '>' { $$ = mk_node("Generics", 2, $2, $4); }
+| '<' lifetimes ',' ty_params SHR     { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); }
+| '<' lifetimes ',' ty_params ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); }
+| '<' ty_params '>'                   { $$ = mk_node("Generics", 2, mk_none(), $2); }
+| '<' ty_params ',' '>'               { $$ = mk_node("Generics", 2, mk_none(), $2); }
+| '<' ty_params SHR                   { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); }
+| '<' ty_params ',' SHR               { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); }
+| %empty                              { $$ = mk_none(); }
+;
+
+maybe_where_clause
+: %empty                              { $$ = mk_none(); }
+| where_clause
+;
+
+where_clause
+: WHERE where_predicates              { $$ = mk_node("WhereClause", 1, $2); }
+| WHERE where_predicates ','          { $$ = mk_node("WhereClause", 1, $2); }
+;
+
+where_predicates
+: where_predicate                      { $$ = mk_node("WherePredicates", 1, $1); }
+| where_predicates ',' where_predicate { $$ = ext_node($1, 1, $3); }
+;
+
+where_predicate
+: lifetime ':' bounds    { $$ = mk_node("WherePredicate", 2, $1, $3); }
+| ty ':' ty_param_bounds { $$ = mk_node("WherePredicate", 2, $1, $3); }
+;
+
+ty_params
+: ty_param               { $$ = mk_node("TyParams", 1, $1); }
+| ty_params ',' ty_param { $$ = ext_node($1, 1, $3); }
+;
+
+// A path with no type parameters; e.g. `foo::bar::Baz`
+//
+// These show up in 'use' view-items, because these are processed
+// without respect to types.
+path_no_types_allowed
+: ident                               { $$ = mk_node("ViewPath", 1, $1); }
+| MOD_SEP ident                       { $$ = mk_node("ViewPath", 1, $2); }
+| SELF                                { $$ = mk_node("ViewPath", 1, mk_atom("Self")); }
+| MOD_SEP SELF                        { $$ = mk_node("ViewPath", 1, mk_atom("Self")); }
+| path_no_types_allowed MOD_SEP ident { $$ = ext_node($1, 1, $3); }
+;
+
+// A path with a lifetime and type parameters, with no double colons
+// before the type parameters; e.g. `foo::bar<'a>::Baz<T>`
+//
+// These show up in "trait references", the components of
+// type-parameter bounds lists, as well as in the prefix of the
+// path_generic_args_and_bounds rule, which is the full form of a
+// named typed expression.
+//
+// They do not have (nor need) an extra '::' before '<' because
+// unlike in expr context, there are no "less-than" type exprs to
+// be ambiguous with.
+path_generic_args_without_colons
+: %prec IDENT
+  ident                                                                       { $$ = mk_node("components", 1, $1); }
+| %prec IDENT
+  ident generic_args                                                          { $$ = mk_node("components", 2, $1, $2); }
+| %prec IDENT
+  ident '(' maybe_ty_sums ')' ret_ty                                          { $$ = mk_node("components", 2, $1, $3); }
+| %prec IDENT
+  path_generic_args_without_colons MOD_SEP ident                              { $$ = ext_node($1, 1, $3); }
+| %prec IDENT
+  path_generic_args_without_colons MOD_SEP ident generic_args                 { $$ = ext_node($1, 2, $3, $4); }
+| %prec IDENT
+  path_generic_args_without_colons MOD_SEP ident '(' maybe_ty_sums ')' ret_ty { $$ = ext_node($1, 2, $3, $5); }
+;
+
+generic_args
+: '<' generic_values '>'   { $$ = $2; }
+| '<' generic_values SHR   { push_back('>'); $$ = $2; }
+| '<' generic_values GE    { push_back('='); $$ = $2; }
+| '<' generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; }
+// If generic_args starts with "<<", the first arg must be a
+// TyQualifiedPath because that's the only type that can start with a
+// '<'. This rule parses that as the first ty_sum and then continues
+// with the rest of generic_values.
+| SHL ty_qualified_path_and_generic_values '>'   { $$ = $2; }
+| SHL ty_qualified_path_and_generic_values SHR   { push_back('>'); $$ = $2; }
+| SHL ty_qualified_path_and_generic_values GE    { push_back('='); $$ = $2; }
+| SHL ty_qualified_path_and_generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; }
+;
+
+generic_values
+: maybe_lifetimes maybe_ty_sums_and_or_bindings { $$ = mk_node("GenericValues", 2, $1, $2); }
+;
+
+maybe_ty_sums_and_or_bindings
+: ty_sums
+| ty_sums ','
+| ty_sums ',' bindings { $$ = mk_node("TySumsAndBindings", 2, $1, $3); }
+| bindings
+| bindings ','
+| %empty               { $$ = mk_none(); }
+;
+
+maybe_bindings
+: ',' bindings { $$ = $2; }
+| %empty       { $$ = mk_none(); }
+;
+
+////////////////////////////////////////////////////////////////////////
+// Part 2: Patterns
+////////////////////////////////////////////////////////////////////////
+
+pat
+: UNDERSCORE                                      { $$ = mk_atom("PatWild"); }
+| '&' pat                                         { $$ = mk_node("PatRegion", 1, $2); }
+| '&' MUT pat                                     { $$ = mk_node("PatRegion", 1, $3); }
+| ANDAND pat                                      { $$ = mk_node("PatRegion", 1, mk_node("PatRegion", 1, $2)); }
+| '(' ')'                                         { $$ = mk_atom("PatUnit"); }
+| '(' pat_tup ')'                                 { $$ = mk_node("PatTup", 1, $2); }
+| '(' pat_tup ',' ')'                             { $$ = mk_node("PatTup", 1, $2); }
+| '[' pat_vec ']'                                 { $$ = mk_node("PatVec", 1, $2); }
+| lit_or_path
+| lit_or_path DOTDOTDOT lit_or_path               { $$ = mk_node("PatRange", 2, $1, $3); }
+| path_expr '{' pat_struct '}'                    { $$ = mk_node("PatStruct", 2, $1, $3); }
+| path_expr '(' DOTDOT ')'                        { $$ = mk_node("PatEnum", 1, $1); }
+| path_expr '(' pat_tup ')'                       { $$ = mk_node("PatEnum", 2, $1, $3); }
+| path_expr '!' maybe_ident delimited_token_trees { $$ = mk_node("PatMac", 3, $1, $3, $4); }
+| binding_mode ident                              { $$ = mk_node("PatIdent", 2, $1, $2); }
+|              ident '@' pat                      { $$ = mk_node("PatIdent", 3, mk_node("BindByValue", 1, mk_atom("MutImmutable")), $1, $3); }
+| binding_mode ident '@' pat                      { $$ = mk_node("PatIdent", 3, $1, $2, $4); }
+| BOX pat                                         { $$ = mk_node("PatUniq", 1, $2); }
+;
+
+pats_or
+: pat              { $$ = mk_node("Pats", 1, $1); }
+| pats_or '|' pat  { $$ = ext_node($1, 1, $3); }
+;
+
+binding_mode
+: REF         { $$ = mk_node("BindByRef", 1, mk_atom("MutImmutable")); }
+| REF MUT     { $$ = mk_node("BindByRef", 1, mk_atom("MutMutable")); }
+| MUT         { $$ = mk_node("BindByValue", 1, mk_atom("MutMutable")); }
+;
+
+lit_or_path
+: path_expr    { $$ = mk_node("PatLit", 1, $1); }
+| lit          { $$ = mk_node("PatLit", 1, $1); }
+| '-' lit      { $$ = mk_node("PatLit", 1, $2); }
+;
+
+pat_field
+:                  ident        { $$ = mk_node("PatField", 1, $1); }
+|     binding_mode ident        { $$ = mk_node("PatField", 2, $1, $2); }
+| BOX              ident        { $$ = mk_node("PatField", 2, mk_atom("box"), $2); }
+| BOX binding_mode ident        { $$ = mk_node("PatField", 3, mk_atom("box"), $2, $3); }
+|              ident ':' pat    { $$ = mk_node("PatField", 2, $1, $3); }
+| binding_mode ident ':' pat    { $$ = mk_node("PatField", 3, $1, $2, $4); }
+;
+
+pat_fields
+: pat_field                  { $$ = mk_node("PatFields", 1, $1); }
+| pat_fields ',' pat_field   { $$ = ext_node($1, 1, $3); }
+;
+
+pat_struct
+: pat_fields                 { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); }
+| pat_fields ','             { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); }
+| pat_fields ',' DOTDOT      { $$ = mk_node("PatStruct", 2, $1, mk_atom("true")); }
+| DOTDOT                     { $$ = mk_node("PatStruct", 1, mk_atom("true")); }
+;
+
+pat_tup
+: pat               { $$ = mk_node("pat_tup", 1, $1); }
+| pat_tup ',' pat   { $$ = ext_node($1, 1, $3); }
+;
+
+pat_vec
+: pat_vec_elts                                  { $$ = mk_node("PatVec", 2, $1, mk_none()); }
+| pat_vec_elts                             ','  { $$ = mk_node("PatVec", 2, $1, mk_none()); }
+| pat_vec_elts     DOTDOT                       { $$ = mk_node("PatVec", 2, $1, mk_none()); }
+| pat_vec_elts ',' DOTDOT                       { $$ = mk_node("PatVec", 2, $1, mk_none()); }
+| pat_vec_elts     DOTDOT ',' pat_vec_elts      { $$ = mk_node("PatVec", 2, $1, $4); }
+| pat_vec_elts     DOTDOT ',' pat_vec_elts ','  { $$ = mk_node("PatVec", 2, $1, $4); }
+| pat_vec_elts ',' DOTDOT ',' pat_vec_elts      { $$ = mk_node("PatVec", 2, $1, $5); }
+| pat_vec_elts ',' DOTDOT ',' pat_vec_elts ','  { $$ = mk_node("PatVec", 2, $1, $5); }
+|                  DOTDOT ',' pat_vec_elts      { $$ = mk_node("PatVec", 2, mk_none(), $3); }
+|                  DOTDOT ',' pat_vec_elts ','  { $$ = mk_node("PatVec", 2, mk_none(), $3); }
+|                  DOTDOT                       { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); }
+| %empty                                        { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); }
+;
+
+pat_vec_elts
+: pat                    { $$ = mk_node("PatVecElts", 1, $1); }
+| pat_vec_elts ',' pat   { $$ = ext_node($1, 1, $3); }
+;
+
+////////////////////////////////////////////////////////////////////////
+// Part 3: Types
+////////////////////////////////////////////////////////////////////////
+
+ty
+: ty_prim
+| ty_closure
+| '<' ty_sum AS trait_ref '>' MOD_SEP ident                                { $$ = mk_node("TyQualifiedPath", 3, $2, $4, $7); }
+| SHL ty_sum AS trait_ref '>' MOD_SEP ident AS trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, mk_node("TyQualifiedPath", 3, $2, $4, $7), $9, $12); }
+| '(' ty_sums ')'                                                          { $$ = mk_node("TyTup", 1, $2); }
+| '(' ty_sums ',' ')'                                                      { $$ = mk_node("TyTup", 1, $2); }
+| '(' ')'                                                                  { $$ = mk_atom("TyNil"); }
+;
+
+ty_prim
+: %prec IDENT path_generic_args_without_colons              { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("false")), $1); }
+| %prec IDENT MOD_SEP path_generic_args_without_colons      { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("true")), $2); }
+| %prec IDENT SELF MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("self", 1, mk_atom("true")), $3); }
+| BOX ty                                                    { $$ = mk_node("TyBox", 1, $2); }
+| '*' maybe_mut_or_const ty                                 { $$ = mk_node("TyPtr", 2, $2, $3); }
+| '&' ty                                                    { $$ = mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2); }
+| '&' MUT ty                                                { $$ = mk_node("TyRptr", 2, mk_atom("MutMutable"), $3); }
+| ANDAND ty                                                 { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2)); }
+| ANDAND MUT ty                                             { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutMutable"), $3)); }
+| '&' lifetime maybe_mut ty                                 { $$ = mk_node("TyRptr", 3, $2, $3, $4); }
+| ANDAND lifetime maybe_mut ty                              { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 3, $2, $3, $4)); }
+| '[' ty ']'                                                { $$ = mk_node("TyVec", 1, $2); }
+| '[' ty ',' DOTDOT expr ']'                                { $$ = mk_node("TyFixedLengthVec", 2, $2, $5); }
+| '[' ty ';' expr ']'                                       { $$ = mk_node("TyFixedLengthVec", 2, $2, $4); }
+| TYPEOF '(' expr ')'                                       { $$ = mk_node("TyTypeof", 1, $3); }
+| UNDERSCORE                                                { $$ = mk_atom("TyInfer"); }
+| ty_bare_fn
+| ty_proc
+| for_in_type
+;
+
+ty_bare_fn
+:                         FN ty_fn_decl { $$ = $2; }
+| UNSAFE                  FN ty_fn_decl { $$ = $3; }
+|        EXTERN maybe_abi FN ty_fn_decl { $$ = $4; }
+| UNSAFE EXTERN maybe_abi FN ty_fn_decl { $$ = $5; }
+;
+
+ty_fn_decl
+: generic_params fn_anon_params ret_ty { $$ = mk_node("TyFnDecl", 3, $1, $2, $3); }
+;
+
+ty_closure
+: UNSAFE '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $3, $5, $6); }
+|        '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $2, $4, $5); }
+| UNSAFE OROR maybe_bounds ret_ty                { $$ = mk_node("TyClosure", 2, $3, $4); }
+|        OROR maybe_bounds ret_ty                { $$ = mk_node("TyClosure", 2, $2, $3); }
+;
+
+ty_proc
+: PROC generic_params fn_params maybe_bounds ret_ty { $$ = mk_node("TyProc", 4, $2, $3, $4, $5); }
+;
+
+for_in_type
+: FOR '<' maybe_lifetimes '>' for_in_type_suffix { $$ = mk_node("ForInType", 2, $3, $5); }
+;
+
+for_in_type_suffix
+: ty_proc
+| ty_bare_fn
+| trait_ref
+| ty_closure
+;
+
+maybe_mut
+: MUT              { $$ = mk_atom("MutMutable"); }
+| %prec MUT %empty { $$ = mk_atom("MutImmutable"); }
+;
+
+maybe_mut_or_const
+: MUT    { $$ = mk_atom("MutMutable"); }
+| CONST  { $$ = mk_atom("MutImmutable"); }
+| %empty { $$ = mk_atom("MutImmutable"); }
+;
+
+ty_qualified_path_and_generic_values
+: ty_qualified_path maybe_bindings
+{
+  $$ = mk_node("GenericValues", 3, mk_none(), mk_node("TySums", 1, mk_node("TySum", 1, $1)), $2);
+}
+| ty_qualified_path ',' ty_sums maybe_bindings
+{
+  $$ = mk_node("GenericValues", 3, mk_none(), ext_node(mk_node("TySums", 1, $1), 1, $3), $4); }
+;
+
+ty_qualified_path
+: ty_sum AS trait_ref '>' MOD_SEP ident                     { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); }
+| ty_sum AS trait_ref '>' MOD_SEP ident '+' ty_param_bounds { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); }
+;
+
+maybe_ty_sums
+: ty_sums
+| ty_sums ','
+| %empty { $$ = mk_none(); }
+;
+
+ty_sums
+: ty_sum             { $$ = mk_node("TySums", 1, $1); }
+| ty_sums ',' ty_sum { $$ = ext_node($1, 1, $3); }
+;
+
+ty_sum
+: ty                     { $$ = mk_node("TySum", 1, $1); }
+| ty '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); }
+;
+
+ty_prim_sum
+: ty_prim                     { $$ = mk_node("TySum", 1, $1); }
+| ty_prim '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); }
+;
+
+maybe_ty_param_bounds
+: ':' ty_param_bounds { $$ = $2; }
+| %empty              { $$ = mk_none(); }
+;
+
+ty_param_bounds
+: boundseq
+| %empty { $$ = mk_none(); }
+;
+
+boundseq
+: polybound
+| boundseq '+' polybound { $$ = ext_node($1, 1, $3); }
+;
+
+polybound
+: FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $3, $5); }
+| bound
+| '?' bound { $$ = $2; }
+;
+
+bindings
+: binding              { $$ = mk_node("Bindings", 1, $1); }
+| bindings ',' binding { $$ = ext_node($1, 1, $3); }
+;
+
+binding
+: ident '=' ty { mk_node("Binding", 2, $1, $3); }
+;
+
+ty_param
+: ident maybe_ty_param_bounds maybe_ty_default           { $$ = mk_node("TyParam", 3, $1, $2, $3); }
+| ident '?' ident maybe_ty_param_bounds maybe_ty_default { $$ = mk_node("TyParam", 4, $1, $3, $4, $5); }
+;
+
+maybe_bounds
+: %prec SHIFTPLUS
+  ':' bounds             { $$ = $2; }
+| %prec SHIFTPLUS %empty { $$ = mk_none(); }
+;
+
+bounds
+: bound            { $$ = mk_node("bounds", 1, $1); }
+| bounds '+' bound { $$ = ext_node($1, 1, $3); }
+;
+
+bound
+: lifetime
+| trait_ref
+;
+
+maybe_ltbounds
+: %prec SHIFTPLUS
+  ':' ltbounds       { $$ = $2; }
+| %empty             { $$ = mk_none(); }
+;
+
+ltbounds
+: lifetime              { $$ = mk_node("ltbounds", 1, $1); }
+| ltbounds '+' lifetime { $$ = ext_node($1, 1, $3); }
+;
+
+maybe_ty_default
+: '=' ty_sum { $$ = mk_node("TyDefault", 1, $2); }
+| %empty     { $$ = mk_none(); }
+;
+
+maybe_lifetimes
+: lifetimes
+| lifetimes ','
+| %empty { $$ = mk_none(); }
+;
+
+lifetimes
+: lifetime_and_bounds               { $$ = mk_node("Lifetimes", 1, $1); }
+| lifetimes ',' lifetime_and_bounds { $$ = ext_node($1, 1, $3); }
+;
+
+lifetime_and_bounds
+: LIFETIME maybe_ltbounds         { $$ = mk_node("lifetime", 2, mk_atom(yytext), $2); }
+| STATIC_LIFETIME                 { $$ = mk_atom("static_lifetime"); }
+;
+
+lifetime
+: LIFETIME         { $$ = mk_node("lifetime", 1, mk_atom(yytext)); }
+| STATIC_LIFETIME  { $$ = mk_atom("static_lifetime"); }
+;
+
+trait_ref
+: %prec IDENT path_generic_args_without_colons
+| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = $2; }
+;
+
+////////////////////////////////////////////////////////////////////////
+// Part 4: Blocks, statements, and expressions
+////////////////////////////////////////////////////////////////////////
+
+inner_attrs_and_block
+: '{' maybe_inner_attrs maybe_stmts '}'        { $$ = mk_node("ExprBlock", 2, $2, $3); }
+;
+
+block
+: '{' maybe_stmts '}'                          { $$ = mk_node("ExprBlock", 1, $2); }
+;
+
+maybe_stmts
+: stmts
+| stmts nonblock_expr { $$ = ext_node($1, 1, $2); }
+| nonblock_expr
+| %empty              { $$ = mk_none(); }
+;
+
+// There are two sub-grammars within a "stmts: exprs" derivation
+// depending on whether each stmt-expr is a block-expr form; this is to
+// handle the "semicolon rule" for stmt sequencing that permits
+// writing
+//
+//     if foo { bar } 10
+//
+// as a sequence of two stmts (one if-expr stmt, one lit-10-expr
+// stmt). Unfortunately by permitting juxtaposition of exprs in
+// sequence like that, the non-block expr grammar has to have a
+// second limited sub-grammar that excludes the prefix exprs that
+// are ambiguous with binops. That is to say:
+//
+//     {10} - 1
+//
+// should parse as (progn (progn 10) (- 1)) not (- (progn 10) 1), that
+// is to say, two statements rather than one, at least according to
+// the mainline rust parser.
+//
+// So we wind up with a 3-way split in exprs that occur in stmt lists:
+// block, nonblock-prefix, and nonblock-nonprefix.
+//
+// In non-stmts contexts, expr can relax this trichotomy.
+//
+// There are also two other expr subtypes: first, nonparen_expr
+// disallows exprs surrounded by parens (including tuple expressions),
+// this is neccesary for BOX (place) expressions, so a parens expr
+// following the BOX is always parsed as the place. There is also
+// expr_norange used in index_expr, which disallows '..' in
+// expressions as that has special meaning inside of brackets.
+
+stmts
+: stmt           { $$ = mk_node("stmts", 1, $1); }
+| stmts stmt     { $$ = ext_node($1, 1, $2); }
+;
+
+stmt
+: let
+|                 stmt_item
+|             PUB stmt_item { $$ = $2; }
+| outer_attrs     stmt_item { $$ = $2; }
+| outer_attrs PUB stmt_item { $$ = $3; }
+| full_block_expr
+| block
+| nonblock_expr ';'
+| ';'                   { $$ = mk_none(); }
+;
+
+maybe_exprs
+: exprs
+| exprs ','
+| %empty { $$ = mk_none(); }
+;
+
+maybe_expr
+: expr
+| %empty { $$ = mk_none(); }
+;
+
+exprs
+: expr                                                        { $$ = mk_node("exprs", 1, $1); }
+| exprs ',' expr                                              { $$ = ext_node($1, 1, $3); }
+;
+
+path_expr
+: path_generic_args_with_colons
+| MOD_SEP path_generic_args_with_colons      { $$ = $2; }
+| SELF MOD_SEP path_generic_args_with_colons { $$ = mk_node("SelfPath", 1, $3); }
+;
+
+// A path with a lifetime and type parameters with double colons before
+// the type parameters; e.g. `foo::bar::<'a>::Baz::<T>`
+//
+// These show up in expr context, in order to disambiguate from "less-than"
+// expressions.
+path_generic_args_with_colons
+: ident                                              { $$ = mk_node("components", 1, $1); }
+| path_generic_args_with_colons MOD_SEP ident        { $$ = ext_node($1, 1, $3); }
+| path_generic_args_with_colons MOD_SEP generic_args { $$ = ext_node($1, 1, $3); }
+;
+
+// the braces-delimited macro is a block_expr so it doesn't appear here
+macro_expr
+: path_expr '!' maybe_ident parens_delimited_token_trees   { $$ = mk_node("MacroExpr", 3, $1, $3, $4); }
+| path_expr '!' maybe_ident brackets_delimited_token_trees { $$ = mk_node("MacroExpr", 3, $1, $3, $4); }
+;
+
+nonblock_expr
+: lit                                                           { $$ = mk_node("ExprLit", 1, $1); }
+| %prec IDENT
+  path_expr                                                     { $$ = mk_node("ExprPath", 1, $1); }
+| SELF                                                          { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); }
+| macro_expr                                                    { $$ = mk_node("ExprMac", 1, $1); }
+| path_expr '{' struct_expr_fields '}'                          { $$ = mk_node("ExprStruct", 2, $1, $3); }
+| nonblock_expr '.' path_generic_args_with_colons               { $$ = mk_node("ExprField", 2, $1, $3); }
+| nonblock_expr '.' LIT_INTEGER                                 { $$ = mk_node("ExprTupleIndex", 1, $1); }
+| nonblock_expr '[' index_expr ']'                              { $$ = mk_node("ExprIndex", 2, $1, $3); }
+| nonblock_expr '(' maybe_exprs ')'                             { $$ = mk_node("ExprCall", 2, $1, $3); }
+| '[' vec_expr ']'                                              { $$ = mk_node("ExprVec", 1, $2); }
+| '(' maybe_exprs ')'                                           { $$ = mk_node("ExprParen", 1, $2); }
+| CONTINUE                                                      { $$ = mk_node("ExprAgain", 0); }
+| CONTINUE lifetime                                             { $$ = mk_node("ExprAgain", 1, $2); }
+| RETURN                                                        { $$ = mk_node("ExprRet", 0); }
+| RETURN expr                                                   { $$ = mk_node("ExprRet", 1, $2); }
+| BREAK                                                         { $$ = mk_node("ExprBreak", 0); }
+| BREAK lifetime                                                { $$ = mk_node("ExprBreak", 1, $2); }
+| nonblock_expr '=' expr                                        { $$ = mk_node("ExprAssign", 2, $1, $3); }
+| nonblock_expr SHLEQ expr                                      { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
+| nonblock_expr SHREQ expr                                      { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
+| nonblock_expr MINUSEQ expr                                    { $$ = mk_node("ExprAssignSub", 2, $1, $3); }
+| nonblock_expr ANDEQ expr                                      { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); }
+| nonblock_expr OREQ expr                                       { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); }
+| nonblock_expr PLUSEQ expr                                     { $$ = mk_node("ExprAssignAdd", 2, $1, $3); }
+| nonblock_expr STAREQ expr                                     { $$ = mk_node("ExprAssignMul", 2, $1, $3); }
+| nonblock_expr SLASHEQ expr                                    { $$ = mk_node("ExprAssignDiv", 2, $1, $3); }
+| nonblock_expr CARETEQ expr                                    { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); }
+| nonblock_expr PERCENTEQ expr                                  { $$ = mk_node("ExprAssignRem", 2, $1, $3); }
+| nonblock_expr OROR expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); }
+| nonblock_expr ANDAND expr                                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); }
+| nonblock_expr EQEQ expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); }
+| nonblock_expr NE expr                                         { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); }
+| nonblock_expr '<' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); }
+| nonblock_expr '>' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); }
+| nonblock_expr LE expr                                         { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); }
+| nonblock_expr GE expr                                         { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); }
+| nonblock_expr '|' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); }
+| nonblock_expr '^' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); }
+| nonblock_expr '&' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); }
+| nonblock_expr SHL expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); }
+| nonblock_expr SHR expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); }
+| nonblock_expr '+' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); }
+| nonblock_expr '-' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); }
+| nonblock_expr '*' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); }
+| nonblock_expr '/' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); }
+| nonblock_expr '%' expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); }
+| nonblock_expr DOTDOT                                          { $$ = mk_node("ExprRange", 2, $1, mk_none()); }
+| nonblock_expr DOTDOT expr                                     { $$ = mk_node("ExprRange", 2, $1, $3); }
+|               DOTDOT expr                                     { $$ = mk_node("ExprRange", 2, mk_none(), $2); }
+| nonblock_expr AS ty                                           { $$ = mk_node("ExprCast", 2, $1, $3); }
+| BOX nonparen_expr                                             { $$ = mk_node("ExprBox", 1, $2); }
+| %prec BOXPLACE BOX '(' maybe_expr ')' nonblock_expr           { $$ = mk_node("ExprBox", 2, $3, $5); }
+| nonblock_prefix_expr
+;
+
+expr
+: lit                                                 { $$ = mk_node("ExprLit", 1, $1); }
+| %prec IDENT
+  path_expr                                           { $$ = mk_node("ExprPath", 1, $1); }
+| SELF                                                { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); }
+| macro_expr                                          { $$ = mk_node("ExprMac", 1, $1); }
+| path_expr '{' struct_expr_fields '}'                { $$ = mk_node("ExprStruct", 2, $1, $3); }
+| expr '.' path_generic_args_with_colons              { $$ = mk_node("ExprField", 2, $1, $3); }
+| expr '.' LIT_INTEGER                                { $$ = mk_node("ExprTupleIndex", 1, $1); }
+| expr '[' index_expr ']'                             { $$ = mk_node("ExprIndex", 2, $1, $3); }
+| expr '(' maybe_exprs ')'                            { $$ = mk_node("ExprCall", 2, $1, $3); }
+| '(' maybe_exprs ')'                                 { $$ = mk_node("ExprParen", 1, $2); }
+| '[' vec_expr ']'                                    { $$ = mk_node("ExprVec", 1, $2); }
+| CONTINUE                                            { $$ = mk_node("ExprAgain", 0); }
+| CONTINUE ident                                      { $$ = mk_node("ExprAgain", 1, $2); }
+| RETURN                                              { $$ = mk_node("ExprRet", 0); }
+| RETURN expr                                         { $$ = mk_node("ExprRet", 1, $2); }
+| BREAK                                               { $$ = mk_node("ExprBreak", 0); }
+| BREAK ident                                         { $$ = mk_node("ExprBreak", 1, $2); }
+| expr '=' expr                                       { $$ = mk_node("ExprAssign", 2, $1, $3); }
+| expr SHLEQ expr                                     { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
+| expr SHREQ expr                                     { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
+| expr MINUSEQ expr                                   { $$ = mk_node("ExprAssignSub", 2, $1, $3); }
+| expr ANDEQ expr                                     { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); }
+| expr OREQ expr                                      { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); }
+| expr PLUSEQ expr                                    { $$ = mk_node("ExprAssignAdd", 2, $1, $3); }
+| expr STAREQ expr                                    { $$ = mk_node("ExprAssignMul", 2, $1, $3); }
+| expr SLASHEQ expr                                   { $$ = mk_node("ExprAssignDiv", 2, $1, $3); }
+| expr CARETEQ expr                                   { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); }
+| expr PERCENTEQ expr                                 { $$ = mk_node("ExprAssignRem", 2, $1, $3); }
+| expr OROR expr                                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); }
+| expr ANDAND expr                                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); }
+| expr EQEQ expr                                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); }
+| expr NE expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); }
+| expr '<' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); }
+| expr '>' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); }
+| expr LE expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); }
+| expr GE expr                                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); }
+| expr '|' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); }
+| expr '^' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); }
+| expr '&' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); }
+| expr SHL expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); }
+| expr SHR expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); }
+| expr '+' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); }
+| expr '-' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); }
+| expr '*' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); }
+| expr '/' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); }
+| expr '%' expr                                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); }
+| expr DOTDOT                                         { $$ = mk_node("ExprRange", 2, $1, mk_none()); }
+| expr DOTDOT expr                                    { $$ = mk_node("ExprRange", 2, $1, $3); }
+|      DOTDOT expr                                    { $$ = mk_node("ExprRange", 2, mk_none(), $2); }
+| expr AS ty                                          { $$ = mk_node("ExprCast", 2, $1, $3); }
+| BOX nonparen_expr                                   { $$ = mk_node("ExprBox", 1, $2); }
+| %prec BOXPLACE BOX '(' maybe_expr ')' expr          { $$ = mk_node("ExprBox", 2, $3, $5); }
+| block_expr
+| block
+| nonblock_prefix_expr
+;
+
+nonparen_expr
+: lit                                                 { $$ = mk_node("ExprLit", 1, $1); }
+| %prec IDENT
+  path_expr                                           { $$ = mk_node("ExprPath", 1, $1); }
+| SELF                                                { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); }
+| macro_expr                                          { $$ = mk_node("ExprMac", 1, $1); }
+| path_expr '{' struct_expr_fields '}'                { $$ = mk_node("ExprStruct", 2, $1, $3); }
+| nonparen_expr '.' path_generic_args_with_colons     { $$ = mk_node("ExprField", 2, $1, $3); }
+| nonparen_expr '.' LIT_INTEGER                       { $$ = mk_node("ExprTupleIndex", 1, $1); }
+| nonparen_expr '[' index_expr ']'                    { $$ = mk_node("ExprIndex", 2, $1, $3); }
+| nonparen_expr '(' maybe_exprs ')'                   { $$ = mk_node("ExprCall", 2, $1, $3); }
+| '[' vec_expr ']'                                    { $$ = mk_node("ExprVec", 1, $2); }
+| CONTINUE                                            { $$ = mk_node("ExprAgain", 0); }
+| CONTINUE ident                                      { $$ = mk_node("ExprAgain", 1, $2); }
+| RETURN                                              { $$ = mk_node("ExprRet", 0); }
+| RETURN expr                                         { $$ = mk_node("ExprRet", 1, $2); }
+| BREAK                                               { $$ = mk_node("ExprBreak", 0); }
+| BREAK ident                                         { $$ = mk_node("ExprBreak", 1, $2); }
+| nonparen_expr '=' nonparen_expr                     { $$ = mk_node("ExprAssign", 2, $1, $3); }
+| nonparen_expr SHLEQ nonparen_expr                   { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
+| nonparen_expr SHREQ nonparen_expr                   { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
+| nonparen_expr MINUSEQ nonparen_expr                 { $$ = mk_node("ExprAssignSub", 2, $1, $3); }
+| nonparen_expr ANDEQ nonparen_expr                   { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); }
+| nonparen_expr OREQ nonparen_expr                    { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); }
+| nonparen_expr PLUSEQ nonparen_expr                  { $$ = mk_node("ExprAssignAdd", 2, $1, $3); }
+| nonparen_expr STAREQ nonparen_expr                  { $$ = mk_node("ExprAssignMul", 2, $1, $3); }
+| nonparen_expr SLASHEQ nonparen_expr                 { $$ = mk_node("ExprAssignDiv", 2, $1, $3); }
+| nonparen_expr CARETEQ nonparen_expr                 { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); }
+| nonparen_expr PERCENTEQ nonparen_expr               { $$ = mk_node("ExprAssignRem", 2, $1, $3); }
+| nonparen_expr OROR nonparen_expr                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); }
+| nonparen_expr ANDAND nonparen_expr                  { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); }
+| nonparen_expr EQEQ nonparen_expr                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); }
+| nonparen_expr NE nonparen_expr                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); }
+| nonparen_expr '<' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); }
+| nonparen_expr '>' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); }
+| nonparen_expr LE nonparen_expr                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); }
+| nonparen_expr GE nonparen_expr                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); }
+| nonparen_expr '|' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); }
+| nonparen_expr '^' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); }
+| nonparen_expr '&' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); }
+| nonparen_expr SHL nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); }
+| nonparen_expr SHR nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); }
+| nonparen_expr '+' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); }
+| nonparen_expr '-' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); }
+| nonparen_expr '*' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); }
+| nonparen_expr '/' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); }
+| nonparen_expr '%' nonparen_expr                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); }
+| nonparen_expr DOTDOT                                { $$ = mk_node("ExprRange", 2, $1, mk_none()); }
+| nonparen_expr DOTDOT nonparen_expr                  { $$ = mk_node("ExprRange", 2, $1, $3); }
+|               DOTDOT nonparen_expr                  { $$ = mk_node("ExprRange", 2, mk_none(), $2); }
+| nonparen_expr AS ty                                 { $$ = mk_node("ExprCast", 2, $1, $3); }
+| BOX nonparen_expr                                   { $$ = mk_node("ExprBox", 1, $2); }
+| %prec BOXPLACE BOX '(' maybe_expr ')' expr          { $$ = mk_node("ExprBox", 1, $3, $5); }
+| block_expr
+| block
+| nonblock_prefix_expr
+;
+
+expr_norange
+: lit                                                 { $$ = mk_node("ExprLit", 1, $1); }
+| %prec IDENT
+  path_expr                                           { $$ = mk_node("ExprPath", 1, $1); }
+| SELF                                                { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); }
+| macro_expr                                          { $$ = mk_node("ExprMac", 1, $1); }
+| path_expr '{' struct_expr_fields '}'                { $$ = mk_node("ExprStruct", 2, $1, $3); }
+| expr_norange '.' path_generic_args_with_colons      { $$ = mk_node("ExprField", 2, $1, $3); }
+| expr_norange '.' LIT_INTEGER                        { $$ = mk_node("ExprTupleIndex", 1, $1); }
+| expr_norange '[' index_expr ']'                     { $$ = mk_node("ExprIndex", 2, $1, $3); }
+| expr_norange '(' maybe_exprs ')'                    { $$ = mk_node("ExprCall", 2, $1, $3); }
+| '(' maybe_exprs ')'                                 { $$ = mk_node("ExprParen", 1, $2); }
+| '[' vec_expr ']'                                    { $$ = mk_node("ExprVec", 1, $2); }
+| CONTINUE                                            { $$ = mk_node("ExprAgain", 0); }
+| CONTINUE ident                                      { $$ = mk_node("ExprAgain", 1, $2); }
+| RETURN                                              { $$ = mk_node("ExprRet", 0); }
+| RETURN expr                                         { $$ = mk_node("ExprRet", 1, $2); }
+| BREAK                                               { $$ = mk_node("ExprBreak", 0); }
+| BREAK ident                                         { $$ = mk_node("ExprBreak", 1, $2); }
+| expr_norange '=' expr_norange                       { $$ = mk_node("ExprAssign", 2, $1, $3); }
+| expr_norange SHLEQ expr_norange                     { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
+| expr_norange SHREQ expr_norange                     { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
+| expr_norange MINUSEQ expr_norange                   { $$ = mk_node("ExprAssignSub", 2, $1, $3); }
+| expr_norange ANDEQ expr_norange                     { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); }
+| expr_norange OREQ expr_norange                      { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); }
+| expr_norange PLUSEQ expr_norange                    { $$ = mk_node("ExprAssignAdd", 2, $1, $3); }
+| expr_norange STAREQ expr_norange                    { $$ = mk_node("ExprAssignMul", 2, $1, $3); }
+| expr_norange SLASHEQ expr_norange                   { $$ = mk_node("ExprAssignDiv", 2, $1, $3); }
+| expr_norange CARETEQ expr_norange                   { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); }
+| expr_norange PERCENTEQ expr_norange                 { $$ = mk_node("ExprAssignRem", 2, $1, $3); }
+| expr_norange OROR expr_norange                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); }
+| expr_norange ANDAND expr_norange                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); }
+| expr_norange EQEQ expr_norange                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); }
+| expr_norange NE expr_norange                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); }
+| expr_norange '<' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); }
+| expr_norange '>' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); }
+| expr_norange LE expr_norange                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); }
+| expr_norange GE expr_norange                        { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); }
+| expr_norange '|' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); }
+| expr_norange '^' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); }
+| expr_norange '&' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); }
+| expr_norange SHL expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); }
+| expr_norange SHR expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); }
+| expr_norange '+' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); }
+| expr_norange '-' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); }
+| expr_norange '*' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); }
+| expr_norange '/' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); }
+| expr_norange '%' expr_norange                       { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); }
+| expr_norange AS ty                                  { $$ = mk_node("Expr_NorangeCast", 2, $1, $3); }
+| BOX nonparen_expr                                   { $$ = mk_node("ExprBox", 1, $2); }
+| %prec BOXPLACE BOX '(' maybe_expr ')' expr_norange  { $$ = mk_node("ExprBox", 2, $3, $5); }
+| block_expr
+| block
+| nonblock_prefix_expr
+;
+
+expr_nostruct
+: lit                                                 { $$ = mk_node("ExprLit", 1, $1); }
+| %prec IDENT
+  path_expr                                           { $$ = mk_node("ExprPath", 1, $1); }
+| SELF                                                { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); }
+| macro_expr                                          { $$ = mk_node("ExprMac", 1, $1); }
+| expr_nostruct '.' path_generic_args_with_colons     { $$ = mk_node("ExprField", 2, $1, $3); }
+| expr_nostruct '.' LIT_INTEGER                       { $$ = mk_node("ExprTupleIndex", 1, $1); }
+| expr_nostruct '[' index_expr ']'                    { $$ = mk_node("ExprIndex", 2, $1, $3); }
+| expr_nostruct '(' maybe_exprs ')'                   { $$ = mk_node("ExprCall", 2, $1, $3); }
+| '[' vec_expr ']'                                    { $$ = mk_node("ExprVec", 1, $2); }
+| '(' maybe_exprs ')'                                 { $$ = mk_node("ExprParen", 1, $2); }
+| CONTINUE                                            { $$ = mk_node("ExprAgain", 0); }
+| CONTINUE ident                                      { $$ = mk_node("ExprAgain", 1, $2); }
+| RETURN                                              { $$ = mk_node("ExprRet", 0); }
+| RETURN expr                                         { $$ = mk_node("ExprRet", 1, $2); }
+| BREAK                                               { $$ = mk_node("ExprBreak", 0); }
+| BREAK ident                                         { $$ = mk_node("ExprBreak", 1, $2); }
+| expr_nostruct '=' expr_nostruct                     { $$ = mk_node("ExprAssign", 2, $1, $3); }
+| expr_nostruct SHLEQ expr_nostruct                   { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
+| expr_nostruct SHREQ expr_nostruct                   { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
+| expr_nostruct MINUSEQ expr_nostruct                 { $$ = mk_node("ExprAssignSub", 2, $1, $3); }
+| expr_nostruct ANDEQ expr_nostruct                   { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); }
+| expr_nostruct OREQ expr_nostruct                    { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); }
+| expr_nostruct PLUSEQ expr_nostruct                  { $$ = mk_node("ExprAssignAdd", 2, $1, $3); }
+| expr_nostruct STAREQ expr_nostruct                  { $$ = mk_node("ExprAssignMul", 2, $1, $3); }
+| expr_nostruct SLASHEQ expr_nostruct                 { $$ = mk_node("ExprAssignDiv", 2, $1, $3); }
+| expr_nostruct CARETEQ expr_nostruct                 { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); }
+| expr_nostruct PERCENTEQ expr_nostruct               { $$ = mk_node("ExprAssignRem", 2, $1, $3); }
+| expr_nostruct OROR expr_nostruct                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); }
+| expr_nostruct ANDAND expr_nostruct                  { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); }
+| expr_nostruct EQEQ expr_nostruct                    { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); }
+| expr_nostruct NE expr_nostruct                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); }
+| expr_nostruct '<' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); }
+| expr_nostruct '>' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); }
+| expr_nostruct LE expr_nostruct                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); }
+| expr_nostruct GE expr_nostruct                      { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); }
+| expr_nostruct '|' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); }
+| expr_nostruct '^' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); }
+| expr_nostruct '&' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); }
+| expr_nostruct SHL expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); }
+| expr_nostruct SHR expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); }
+| expr_nostruct '+' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); }
+| expr_nostruct '-' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); }
+| expr_nostruct '*' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); }
+| expr_nostruct '/' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); }
+| expr_nostruct '%' expr_nostruct                     { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); }
+| expr_nostruct DOTDOT                                { $$ = mk_node("ExprRange", 2, $1, mk_none()); }
+| expr_nostruct DOTDOT expr_nostruct                  { $$ = mk_node("ExprRange", 2, $1, $3); }
+|               DOTDOT expr_nostruct                  { $$ = mk_node("ExprRange", 2, mk_none(), $2); }
+| expr_nostruct AS ty                                 { $$ = mk_node("ExprCast", 2, $1, $3); }
+| BOX nonparen_expr                                   { $$ = mk_node("ExprBox", 1, $2); }
+| %prec BOXPLACE BOX '(' maybe_expr ')' expr_nostruct { $$ = mk_node("ExprBox", 1, $3, $5); }
+| block_expr
+| block
+| nonblock_prefix_expr_nostruct
+;
+
+nonblock_prefix_expr_nostruct
+: '-' expr_nostruct                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); }
+| '!' expr_nostruct                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); }
+| '*' expr_nostruct                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); }
+| '&' maybe_mut expr_nostruct               { $$ = mk_node("ExprAddrOf", 2, $2, $3); }
+| ANDAND maybe_mut expr_nostruct            { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); }
+| lambda_expr_nostruct
+| MOVE lambda_expr_nostruct                 { $$ = $2; }
+| proc_expr_nostruct
+;
+
+nonblock_prefix_expr
+: '-' expr                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); }
+| '!' expr                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); }
+| '*' expr                         { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); }
+| '&' maybe_mut expr               { $$ = mk_node("ExprAddrOf", 2, $2, $3); }
+| ANDAND maybe_mut expr            { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); }
+| lambda_expr
+| MOVE lambda_expr                 { $$ = $2; }
+| proc_expr
+;
+
+lambda_expr
+: %prec LAMBDA
+  OROR ret_ty expr                                        { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); }
+| %prec LAMBDA
+  '|' maybe_unboxed_closure_kind '|' ret_ty expr          { $$ = mk_node("ExprFnBlock", 3, mk_none(), $4, $5); }
+| %prec LAMBDA
+  '|' inferrable_params '|' ret_ty expr                   { $$ = mk_node("ExprFnBlock", 3, $2, $4, $5); }
+| %prec LAMBDA
+  '|' '&' maybe_mut ':' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $5, $7, $8); }
+| %prec LAMBDA
+  '|' ':' inferrable_params '|' ret_ty expr               { $$ = mk_node("ExprFnBlock", 3, $3, $5, $6); }
+;
+
+lambda_expr_nostruct
+: %prec LAMBDA
+  OROR expr_nostruct                                        { $$ = mk_node("ExprFnBlock", 2, mk_none(), $2); }
+| %prec LAMBDA
+  '|' maybe_unboxed_closure_kind '|'  expr_nostruct         { $$ = mk_node("ExprFnBlock", 2, mk_none(), $4); }
+| %prec LAMBDA
+  '|' inferrable_params '|' expr_nostruct                   { $$ = mk_node("ExprFnBlock", 2, $2, $4); }
+| %prec LAMBDA
+  '|' '&' maybe_mut ':' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $5, $7); }
+| %prec LAMBDA
+  '|' ':' inferrable_params '|' expr_nostruct               { $$ = mk_node("ExprFnBlock", 2, $3, $5); }
+
+;
+
+proc_expr
+: %prec LAMBDA
+  PROC '(' ')' expr                         { $$ = mk_node("ExprProc", 2, mk_none(), $4); }
+| %prec LAMBDA
+  PROC '(' inferrable_params ')' expr       { $$ = mk_node("ExprProc", 2, $3, $5); }
+;
+
+proc_expr_nostruct
+: %prec LAMBDA
+  PROC '(' ')' expr_nostruct                     { $$ = mk_node("ExprProc", 2, mk_none(), $4); }
+| %prec LAMBDA
+  PROC '(' inferrable_params ')' expr_nostruct   { $$ = mk_node("ExprProc", 2, $3, $5); }
+;
+
+vec_expr
+: maybe_exprs
+| exprs ';' expr { $$ = mk_node("VecRepeat", 2, $1, $3); }
+;
+
+index_expr
+: expr_norange                     { $$ = mk_node("Index", 1, $1); }
+| expr_norange DOTDOT              { $$ = mk_node("SliceToEnd", 1, $1); }
+|              DOTDOT expr_norange { $$ = mk_node("SliceFromBeginning", 1, $2); }
+| expr_norange DOTDOT expr_norange { $$ = mk_node("Slice", 2, $1, $3); }
+| %empty                           { $$ = mk_none(); }
+;
+
+struct_expr_fields
+: field_inits
+| field_inits ','
+| maybe_field_inits default_field_init { $$ = ext_node($1, 1, $2); }
+;
+
+maybe_field_inits
+: field_inits
+| field_inits ','
+| %empty { $$ = mk_none(); }
+;
+
+field_inits
+: field_init                 { $$ = mk_node("FieldInits", 1, $1); }
+| field_inits ',' field_init { $$ = ext_node($1, 1, $3); }
+;
+
+field_init
+: ident ':' expr   { $$ = mk_node("FieldInit", 2, $1, $3); }
+;
+
+default_field_init
+: DOTDOT expr   { $$ = mk_node("DefaultFieldInit", 1, $2); }
+;
+
+block_expr
+: expr_match
+| expr_if
+| expr_if_let
+| expr_while
+| expr_while_let
+| expr_loop
+| expr_for
+| UNSAFE block                                           { $$ = mk_node("UnsafeBlock", 1, $2); }
+| path_expr '!' maybe_ident braces_delimited_token_trees { $$ = mk_node("Macro", 3, $1, $3, $4); }
+;
+
+full_block_expr
+: block_expr
+| full_block_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); }
+| full_block_expr '.' LIT_INTEGER                   { $$ = mk_node("ExprTupleIndex", 1, $1); }
+;
+
+expr_match
+: MATCH expr_nostruct '{' '}'                                     { $$ = mk_node("ExprMatch", 1, $2); }
+| MATCH expr_nostruct '{' match_clauses                       '}' { $$ = mk_node("ExprMatch", 2, $2, $4); }
+| MATCH expr_nostruct '{' match_clauses nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, ext_node($4, 1, $5)); }
+| MATCH expr_nostruct '{'               nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, mk_node("Arms", 1, $4)); }
+;
+
+match_clauses
+: match_clause               { $$ = mk_node("Arms", 1, $1); }
+| match_clauses match_clause { $$ = ext_node($1, 1, $2); }
+;
+
+match_clause
+: nonblock_match_clause ','
+| block_match_clause
+| block_match_clause ','
+;
+
+nonblock_match_clause
+: maybe_outer_attrs pats_or maybe_guard FAT_ARROW nonblock_expr   { $$ = mk_node("Arm", 4, $1, $2, $3, $5); }
+| maybe_outer_attrs pats_or maybe_guard FAT_ARROW full_block_expr { $$ = mk_node("Arm", 4, $1, $2, $3, $5); }
+;
+
+block_match_clause
+: maybe_outer_attrs pats_or maybe_guard FAT_ARROW block { $$ = mk_node("Arm", 4, $1, $2, $3, $5); }
+;
+
+maybe_guard
+: IF expr_nostruct           { $$ = $2; }
+| %empty                     { $$ = mk_none(); }
+;
+
+expr_if
+: IF expr_nostruct block                              { $$ = mk_node("ExprIf", 2, $2, $3); }
+| IF expr_nostruct block ELSE block_or_if             { $$ = mk_node("ExprIf", 3, $2, $3, $5); }
+;
+
+expr_if_let
+: IF LET pat '=' expr_nostruct block                  { $$ = mk_node("ExprIfLet", 3, $3, $5, $6); }
+| IF LET pat '=' expr_nostruct block ELSE block_or_if { $$ = mk_node("ExprIfLet", 4, $3, $5, $6, $8); }
+;
+
+block_or_if
+: block
+| expr_if
+| expr_if_let
+;
+
+expr_while
+: maybe_label WHILE expr_nostruct block               { $$ = mk_node("ExprWhile", 3, $1, $3, $4); }
+;
+
+expr_while_let
+: maybe_label WHILE LET pat '=' expr_nostruct block   { $$ = mk_node("ExprWhileLet", 4, $1, $4, $6, $7); }
+;
+
+expr_loop
+: maybe_label LOOP block                              { $$ = mk_node("ExprLoop", 2, $1, $3); }
+;
+
+expr_for
+: maybe_label FOR pat IN expr_nostruct block          { $$ = mk_node("ExprForLoop", 4, $1, $3, $5, $6); }
+;
+
+maybe_label
+: lifetime ':'
+| %empty { $$ = mk_none(); }
+;
+
+let
+: LET pat maybe_ty_ascription maybe_init_expr ';' { $$ = mk_node("DeclLocal", 3, $2, $3, $4); }
+;
+
+////////////////////////////////////////////////////////////////////////
+// Part 5: Macros and misc. rules
+////////////////////////////////////////////////////////////////////////
+
+lit
+: LIT_BYTE                   { $$ = mk_node("LitByte", 1, mk_atom(yytext)); }
+| LIT_CHAR                   { $$ = mk_node("LitChar", 1, mk_atom(yytext)); }
+| LIT_INTEGER                { $$ = mk_node("LitInteger", 1, mk_atom(yytext)); }
+| LIT_FLOAT                  { $$ = mk_node("LitFloat", 1, mk_atom(yytext)); }
+| TRUE                       { $$ = mk_node("LitBool", 1, mk_atom(yytext)); }
+| FALSE                      { $$ = mk_node("LitBool", 1, mk_atom(yytext)); }
+| str
+;
+
+str
+: LIT_STR                    { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("CookedStr")); }
+| LIT_STR_RAW                { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("RawStr")); }
+| LIT_BINARY                 { $$ = mk_node("LitBinary", 1, mk_atom(yytext), mk_atom("BinaryStr")); }
+| LIT_BINARY_RAW             { $$ = mk_node("LitBinary", 1, mk_atom(yytext), mk_atom("RawBinaryStr")); }
+;
+
+maybe_ident
+: %empty { $$ = mk_none(); }
+| ident
+;
+
+ident
+: IDENT                      { $$ = mk_node("ident", 1, mk_atom(yytext)); }
+;
+
+unpaired_token
+: SHL                        { $$ = mk_atom(yytext); }
+| SHR                        { $$ = mk_atom(yytext); }
+| LE                         { $$ = mk_atom(yytext); }
+| EQEQ                       { $$ = mk_atom(yytext); }
+| NE                         { $$ = mk_atom(yytext); }
+| GE                         { $$ = mk_atom(yytext); }
+| ANDAND                     { $$ = mk_atom(yytext); }
+| OROR                       { $$ = mk_atom(yytext); }
+| SHLEQ                      { $$ = mk_atom(yytext); }
+| SHREQ                      { $$ = mk_atom(yytext); }
+| MINUSEQ                    { $$ = mk_atom(yytext); }
+| ANDEQ                      { $$ = mk_atom(yytext); }
+| OREQ                       { $$ = mk_atom(yytext); }
+| PLUSEQ                     { $$ = mk_atom(yytext); }
+| STAREQ                     { $$ = mk_atom(yytext); }
+| SLASHEQ                    { $$ = mk_atom(yytext); }
+| CARETEQ                    { $$ = mk_atom(yytext); }
+| PERCENTEQ                  { $$ = mk_atom(yytext); }
+| DOTDOT                     { $$ = mk_atom(yytext); }
+| DOTDOTDOT                  { $$ = mk_atom(yytext); }
+| MOD_SEP                    { $$ = mk_atom(yytext); }
+| RARROW                     { $$ = mk_atom(yytext); }
+| FAT_ARROW                  { $$ = mk_atom(yytext); }
+| LIT_BYTE                   { $$ = mk_atom(yytext); }
+| LIT_CHAR                   { $$ = mk_atom(yytext); }
+| LIT_INTEGER                { $$ = mk_atom(yytext); }
+| LIT_FLOAT                  { $$ = mk_atom(yytext); }
+| LIT_STR                    { $$ = mk_atom(yytext); }
+| LIT_STR_RAW                { $$ = mk_atom(yytext); }
+| LIT_BINARY                 { $$ = mk_atom(yytext); }
+| LIT_BINARY_RAW             { $$ = mk_atom(yytext); }
+| IDENT                      { $$ = mk_atom(yytext); }
+| UNDERSCORE                 { $$ = mk_atom(yytext); }
+| LIFETIME                   { $$ = mk_atom(yytext); }
+| SELF                       { $$ = mk_atom(yytext); }
+| STATIC                     { $$ = mk_atom(yytext); }
+| AS                         { $$ = mk_atom(yytext); }
+| BREAK                      { $$ = mk_atom(yytext); }
+| CRATE                      { $$ = mk_atom(yytext); }
+| ELSE                       { $$ = mk_atom(yytext); }
+| ENUM                       { $$ = mk_atom(yytext); }
+| EXTERN                     { $$ = mk_atom(yytext); }
+| FALSE                      { $$ = mk_atom(yytext); }
+| FN                         { $$ = mk_atom(yytext); }
+| FOR                        { $$ = mk_atom(yytext); }
+| IF                         { $$ = mk_atom(yytext); }
+| IMPL                       { $$ = mk_atom(yytext); }
+| IN                         { $$ = mk_atom(yytext); }
+| LET                        { $$ = mk_atom(yytext); }
+| LOOP                       { $$ = mk_atom(yytext); }
+| MATCH                      { $$ = mk_atom(yytext); }
+| MOD                        { $$ = mk_atom(yytext); }
+| MOVE                       { $$ = mk_atom(yytext); }
+| MUT                        { $$ = mk_atom(yytext); }
+| PRIV                       { $$ = mk_atom(yytext); }
+| PUB                        { $$ = mk_atom(yytext); }
+| REF                        { $$ = mk_atom(yytext); }
+| RETURN                     { $$ = mk_atom(yytext); }
+| STRUCT                     { $$ = mk_atom(yytext); }
+| TRUE                       { $$ = mk_atom(yytext); }
+| TRAIT                      { $$ = mk_atom(yytext); }
+| TYPE                       { $$ = mk_atom(yytext); }
+| UNSAFE                     { $$ = mk_atom(yytext); }
+| USE                        { $$ = mk_atom(yytext); }
+| WHILE                      { $$ = mk_atom(yytext); }
+| CONTINUE                   { $$ = mk_atom(yytext); }
+| PROC                       { $$ = mk_atom(yytext); }
+| BOX                        { $$ = mk_atom(yytext); }
+| CONST                      { $$ = mk_atom(yytext); }
+| WHERE                      { $$ = mk_atom(yytext); }
+| TYPEOF                     { $$ = mk_atom(yytext); }
+| INNER_DOC_COMMENT          { $$ = mk_atom(yytext); }
+| OUTER_DOC_COMMENT          { $$ = mk_atom(yytext); }
+| SHEBANG                    { $$ = mk_atom(yytext); }
+| STATIC_LIFETIME            { $$ = mk_atom(yytext); }
+| ';'                        { $$ = mk_atom(yytext); }
+| ','                        { $$ = mk_atom(yytext); }
+| '.'                        { $$ = mk_atom(yytext); }
+| '@'                        { $$ = mk_atom(yytext); }
+| '#'                        { $$ = mk_atom(yytext); }
+| '~'                        { $$ = mk_atom(yytext); }
+| ':'                        { $$ = mk_atom(yytext); }
+| '$'                        { $$ = mk_atom(yytext); }
+| '='                        { $$ = mk_atom(yytext); }
+| '?'                        { $$ = mk_atom(yytext); }
+| '!'                        { $$ = mk_atom(yytext); }
+| '<'                        { $$ = mk_atom(yytext); }
+| '>'                        { $$ = mk_atom(yytext); }
+| '-'                        { $$ = mk_atom(yytext); }
+| '&'                        { $$ = mk_atom(yytext); }
+| '|'                        { $$ = mk_atom(yytext); }
+| '+'                        { $$ = mk_atom(yytext); }
+| '*'                        { $$ = mk_atom(yytext); }
+| '/'                        { $$ = mk_atom(yytext); }
+| '^'                        { $$ = mk_atom(yytext); }
+| '%'                        { $$ = mk_atom(yytext); }
+;
+
+token_trees
+: %empty                     { $$ = mk_node("TokenTrees", 0); }
+| token_trees token_tree     { $$ = ext_node($1, 1, $2); }
+;
+
+token_tree
+: delimited_token_trees
+| unpaired_token         { $$ = mk_node("TTTok", 1, $1); }
+;
+
+delimited_token_trees
+: parens_delimited_token_trees
+| braces_delimited_token_trees
+| brackets_delimited_token_trees
+;
+
+parens_delimited_token_trees
+: '(' token_trees ')'
+{
+  $$ = mk_node("TTDelim", 3,
+               mk_node("TTTok", 1, mk_atom("(")),
+               $2,
+               mk_node("TTTok", 1, mk_atom(")")));
+}
+;
+
+braces_delimited_token_trees
+: '{' token_trees '}'
+{
+  $$ = mk_node("TTDelim", 3,
+               mk_node("TTTok", 1, mk_atom("{")),
+               $2,
+               mk_node("TTTok", 1, mk_atom("}")));
+}
+;
+
+brackets_delimited_token_trees
+: '[' token_trees ']'
+{
+  $$ = mk_node("TTDelim", 3,
+               mk_node("TTTok", 1, mk_atom("[")),
+               $2,
+               mk_node("TTTok", 1, mk_atom("]")));
+}
+;
diff --git a/src/grammar/testparser.py b/src/grammar/testparser.py
new file mode 100755
index 00000000000..38e57be288b
--- /dev/null
+++ b/src/grammar/testparser.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 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 os
+import subprocess
+import argparse
+
+# usage: testparser.py [-h] [-p PARSER [PARSER ...]] -s SOURCE_DIR
+
+# Parsers should read from stdin and return exit status 0 for a
+# successful parse, and nonzero for an unsuccessful parse
+
+parser = argparse.ArgumentParser()
+parser.add_argument('-p', '--parser', nargs='+')
+parser.add_argument('-s', '--source-dir', nargs=1, required=True)
+args = parser.parse_args(sys.argv[1:])
+
+total = 0
+ok = {}
+bad = {}
+for parser in args.parser:
+    ok[parser] = 0
+    bad[parser] = []
+devnull = open(os.devnull, 'w')
+print "\n"
+
+for base, dirs, files in os.walk(args.source_dir[0]):
+    for f in filter(lambda p: p.endswith('.rs'), files):
+        p = os.path.join(base, f)
+        compile_fail = 'compile-fail' in p
+        ignore = any('ignore-test' in line or 'ignore-lexer-test' in line
+                     for line in open(p).readlines())
+        if compile_fail or ignore:
+            continue
+        total += 1
+        for parser in args.parser:
+            if subprocess.call(parser, stdin=open(p), stderr=subprocess.STDOUT, stdout=devnull) == 0:
+                ok[parser] += 1
+            else:
+                bad[parser].append(p)
+        parser_stats = ', '.join(['{}: {}'.format(parser, ok[parser]) for parser in args.parser])
+        sys.stdout.write("\033[K\r total: {}, {}, scanned {}"
+                         .format(total, os.path.relpath(parser_stats), os.path.relpath(p)))
+
+devnull.close()
+
+print "\n"
+
+for parser in args.parser:
+    filename = os.path.basename(parser) + '.bad'
+    print("writing {} files that failed to parse with {} to {}".format(len(bad[parser]), parser, filename))
+    with open(filename, "w") as f:
+          for p in bad[parser]:
+              f.write(p)
+              f.write("\n")
diff --git a/src/grammar/tokens.h b/src/grammar/tokens.h
new file mode 100644
index 00000000000..4457e3f8b5a
--- /dev/null
+++ b/src/grammar/tokens.h
@@ -0,0 +1,91 @@
+// Copyright 2015 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.
+
+enum Token {
+  SHL = 257, // Parser generators reserve 0-256 for char literals
+  SHR,
+  LE,
+  EQEQ,
+  NE,
+  GE,
+  ANDAND,
+  OROR,
+  SHLEQ,
+  SHREQ,
+  MINUSEQ,
+  ANDEQ,
+  OREQ,
+  PLUSEQ,
+  STAREQ,
+  SLASHEQ,
+  CARETEQ,
+  PERCENTEQ,
+  DOTDOT,
+  DOTDOTDOT,
+  MOD_SEP,
+  RARROW,
+  FAT_ARROW,
+  LIT_BYTE,
+  LIT_CHAR,
+  LIT_INTEGER,
+  LIT_FLOAT,
+  LIT_STR,
+  LIT_STR_RAW,
+  LIT_BINARY,
+  LIT_BINARY_RAW,
+  IDENT,
+  UNDERSCORE,
+  LIFETIME,
+
+  // keywords
+  SELF,
+  STATIC,
+  AS,
+  BREAK,
+  CRATE,
+  ELSE,
+  ENUM,
+  EXTERN,
+  FALSE,
+  FN,
+  FOR,
+  IF,
+  IMPL,
+  IN,
+  LET,
+  LOOP,
+  MATCH,
+  MOD,
+  MOVE,
+  MUT,
+  PRIV,
+  PUB,
+  REF,
+  RETURN,
+  STRUCT,
+  TRUE,
+  TRAIT,
+  TYPE,
+  UNSAFE,
+  USE,
+  WHILE,
+  CONTINUE,
+  PROC,
+  BOX,
+  CONST,
+  WHERE,
+  TYPEOF,
+  INNER_DOC_COMMENT,
+  OUTER_DOC_COMMENT,
+
+  SHEBANG,
+  SHEBANG_LINE,
+  STATIC_LIFETIME
+};

From be138ed2bd6a4c4bf1c80287569709a9905e4891 Mon Sep 17 00:00:00 2001
From: P1start <rewi-github@whanau.org>
Date: Wed, 21 Jan 2015 11:04:17 +1300
Subject: [PATCH 03/33] Add `#[rustc_on_unimplemented]` annotations to more
 traits

---
 src/libcore/fmt/mod.rs | 4 ++++
 src/libcore/iter.rs    | 2 ++
 src/libcore/marker.rs  | 3 +++
 3 files changed, 9 insertions(+)

diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 0e8d31a62ee..60403d1c011 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -243,6 +243,8 @@ pub trait Show {
 /// Format trait for the `:?` format. Useful for debugging, most all types
 /// should implement this.
 #[unstable = "I/O and core have yet to be reconciled"]
+#[rustc_on_unimplemented = "`{Self}` cannot be formatted using `:?`; if it is defined in your \
+                            crate, add `#[derive(Debug)]` or manually implement it"]
 pub trait Debug {
     /// Formats the value using the given formatter.
     fn fmt(&self, &mut Formatter) -> Result;
@@ -266,6 +268,8 @@ pub trait String {
 /// When a value can be semantically expressed as a String, this trait may be
 /// used. It corresponds to the default format, `{}`.
 #[unstable = "I/O and core have yet to be reconciled"]
+#[rustc_on_unimplemented = "`{Self}` cannot be formatted with the default formatter; try using \
+                            `:?` instead if you are using a format string"]
 pub trait Display {
     /// Formats the value using the given formatter.
     fn fmt(&self, &mut Formatter) -> Result;
diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs
index 773ac99b0de..c31ad56fe5b 100644
--- a/src/libcore/iter.rs
+++ b/src/libcore/iter.rs
@@ -82,6 +82,8 @@ use uint;
 /// else.
 #[lang="iterator"]
 #[stable]
+#[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling `.iter()` or a similar \
+                            method"]
 pub trait Iterator {
     #[stable]
     type Item;
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index a987a0a5068..f32552f300c 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -30,6 +30,7 @@ use clone::Clone;
 /// Types able to be transferred across task boundaries.
 #[unstable = "will be overhauled with new lifetime rules; see RFC 458"]
 #[lang="send"]
+#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
 pub unsafe trait Send: 'static {
     // empty.
 }
@@ -37,6 +38,7 @@ pub unsafe trait Send: 'static {
 /// Types with a constant size known at compile-time.
 #[stable]
 #[lang="sized"]
+#[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"]
 pub trait Sized {
     // Empty.
 }
@@ -193,6 +195,7 @@ pub trait Copy {
 /// `transmute`-ing from `&T` to `&mut T` is illegal).
 #[unstable = "will be overhauled with new lifetime rules; see RFC 458"]
 #[lang="sync"]
+#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"]
 pub unsafe trait Sync {
     // Empty
 }

From 87db16cecd165ab8ed57dfa308a429868716fdc9 Mon Sep 17 00:00:00 2001
From: P1start <rewi-github@whanau.org>
Date: Thu, 22 Jan 2015 22:22:16 +1300
Subject: [PATCH 04/33] core::marker: s/task/thread/

---
 src/libcore/marker.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index f32552f300c..688f0484401 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -27,7 +27,7 @@
 
 use clone::Clone;
 
-/// Types able to be transferred across task boundaries.
+/// Types able to be transferred across thread boundaries.
 #[unstable = "will be overhauled with new lifetime rules; see RFC 458"]
 #[lang="send"]
 #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
@@ -148,11 +148,11 @@ pub trait Copy {
     // Empty.
 }
 
-/// Types that can be safely shared between tasks when aliased.
+/// Types that can be safely shared between threads when aliased.
 ///
 /// The precise definition is: a type `T` is `Sync` if `&T` is
 /// thread-safe. In other words, there is no possibility of data races
-/// when passing `&T` references between tasks.
+/// when passing `&T` references between threads.
 ///
 /// As one would expect, primitive types like `u8` and `f64` are all
 /// `Sync`, and so are simple aggregate types containing them (like

From 3dbac7882e27d680112cf172b789f9741c7ab25d Mon Sep 17 00:00:00 2001
From: blackbeam <aikorsky@gmail.com>
Date: Thu, 22 Jan 2015 12:29:49 +0300
Subject: [PATCH 05/33] libsyntax: fix for `has_test_signature`

---
 src/libsyntax/test.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index e5d8e4e5143..8933d3a9669 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -295,6 +295,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
           &ast::ItemFn(ref decl, _, _, ref generics, _) => {
             let no_output = match decl.output {
                 ast::DefaultReturn(..) => true,
+                ast::Return(ref t) if t.node == ast::TyTup(vec![]) => true,
                 _ => false
             };
             if decl.inputs.is_empty()
@@ -331,6 +332,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
                 let input_cnt = decl.inputs.len();
                 let no_output = match decl.output {
                     ast::DefaultReturn(..) => true,
+                    ast::Return(ref t) if t.node == ast::TyTup(vec![]) => true,
                     _ => false
                 };
                 let tparm_cnt = generics.ty_params.len();

From fd029209ec4901580b1cc0180098e0b4024bee5b Mon Sep 17 00:00:00 2001
From: blackbeam <aikorsky@gmail.com>
Date: Thu, 22 Jan 2015 21:38:00 +0300
Subject: [PATCH 06/33] Add test for `has_test_signature` for explicit return

---
 ...e-verification-for-explicit-return-type.rs | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs

diff --git a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs
new file mode 100644
index 00000000000..65a6ed2b332
--- /dev/null
+++ b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 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.
+
+// compile-flags: --test
+extern crate test;
+
+#[bench]
+fn bench_explicit_return_type(_: &mut ::test::Bencher) -> () {}
+
+#[test]
+fn test_explicit_return_type() -> () {}
+

From 4cfb70026c91a338518a17579bcd62a32a0a413d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= <john.kare.alsaker@gmail.com>
Date: Thu, 22 Jan 2015 19:43:39 +0100
Subject: [PATCH 07/33] Better inline assembly errors

---
 src/librustc_llvm/diagnostic.rs  | 33 +++++++++++++++++++++-
 src/librustc_llvm/lib.rs         |  4 +++
 src/librustc_trans/back/write.rs | 47 +++++++++++++++++++-------------
 src/rustllvm/RustWrapper.cpp     | 16 +++++++++++
 4 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/src/librustc_llvm/diagnostic.rs b/src/librustc_llvm/diagnostic.rs
index db2a569cdef..5d0ae9d1f92 100644
--- a/src/librustc_llvm/diagnostic.rs
+++ b/src/librustc_llvm/diagnostic.rs
@@ -13,7 +13,7 @@
 pub use self::OptimizationDiagnosticKind::*;
 pub use self::Diagnostic::*;
 
-use libc::c_char;
+use libc::{c_char, c_uint};
 use std::ptr;
 
 use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};
@@ -69,9 +69,37 @@ impl OptimizationDiagnostic {
     }
 }
 
+pub struct InlineAsmDiagnostic {
+    pub cookie: c_uint,
+    pub message: TwineRef,
+    pub instruction: ValueRef,
+}
+
+impl Copy for InlineAsmDiagnostic {}
+
+impl InlineAsmDiagnostic {
+    unsafe fn unpack(di: DiagnosticInfoRef)
+            -> InlineAsmDiagnostic {
+
+        let mut opt = InlineAsmDiagnostic {
+            cookie: 0,
+            message: ptr::null_mut(),
+            instruction: ptr::null_mut(),
+        };
+
+        super::LLVMUnpackInlineAsmDiagnostic(di,
+            &mut opt.cookie,
+            &mut opt.message,
+            &mut opt.instruction);
+
+        opt
+    }
+}
+
 #[derive(Copy)]
 pub enum Diagnostic {
     Optimization(OptimizationDiagnostic),
+    InlineAsm(InlineAsmDiagnostic),
 
     /// LLVM has other types that we do not wrap here.
     UnknownDiagnostic(DiagnosticInfoRef),
@@ -82,6 +110,9 @@ impl Diagnostic {
         let kind = super::LLVMGetDiagInfoKind(di);
 
         match kind {
+            super::DK_InlineAsm
+                => InlineAsm(InlineAsmDiagnostic::unpack(di)),
+
             super::DK_OptimizationRemark
                 => Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),
 
diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs
index eded88bb62e..8c651cf839b 100644
--- a/src/librustc_llvm/lib.rs
+++ b/src/librustc_llvm/lib.rs
@@ -2049,6 +2049,10 @@ extern {
                                             function_out: *mut ValueRef,
                                             debugloc_out: *mut DebugLocRef,
                                             message_out: *mut TwineRef);
+    pub fn LLVMUnpackInlineAsmDiagnostic(DI: DiagnosticInfoRef,
+                                            cookie_out: *mut c_uint,
+                                            message_out: *mut TwineRef,
+                                            instruction_out: *mut ValueRef);
 
     pub fn LLVMWriteDiagnosticInfoToString(DI: DiagnosticInfoRef, s: RustStringRef);
     pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity;
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index b07c2060e69..5e48ce384be 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -336,30 +336,36 @@ struct HandlerFreeVars<'a> {
     cgcx: &'a CodegenContext<'a>,
 }
 
+unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>,
+                                           msg: &'b str,
+                                           cookie: c_uint) {
+    use syntax::codemap::ExpnId;
+
+    match cgcx.lto_ctxt {
+        Some((sess, _)) => {
+            sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info {
+                Some(ei) => sess.span_err(ei.call_site, msg),
+                None     => sess.err(msg),
+            });
+        }
+
+        None => {
+            cgcx.handler.err(msg);
+            cgcx.handler.note("build without -C codegen-units for more exact errors");
+        }
+    }
+}
+
 unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef,
                                         user: *const c_void,
                                         cookie: c_uint) {
-    use syntax::codemap::ExpnId;
-
     let HandlerFreeVars { cgcx, .. }
         = *mem::transmute::<_, *const HandlerFreeVars>(user);
 
     let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s))
         .expect("non-UTF8 SMDiagnostic");
 
-    match cgcx.lto_ctxt {
-        Some((sess, _)) => {
-            sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info {
-                Some(ei) => sess.span_err(ei.call_site, &msg[]),
-                None     => sess.err(&msg[]),
-            });
-        }
-
-        None => {
-            cgcx.handler.err(&msg[]);
-            cgcx.handler.note("build without -C codegen-units for more exact errors");
-        }
-    }
+    report_inline_asm(cgcx, &msg[], cookie);
 }
 
 unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
@@ -367,6 +373,12 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
         = *mem::transmute::<_, *const HandlerFreeVars>(user);
 
     match llvm::diagnostic::Diagnostic::unpack(info) {
+        llvm::diagnostic::InlineAsm(inline) => {
+            report_inline_asm(cgcx,
+                              llvm::twine_to_string(inline.message).as_slice(),
+                              inline.cookie);
+        }
+
         llvm::diagnostic::Optimization(opt) => {
             let pass_name = str::from_utf8(ffi::c_str_to_bytes(&opt.pass_name))
                                 .ok()
@@ -407,10 +419,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
     let fv = &fv as *const HandlerFreeVars as *mut c_void;
 
     llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);
-
-    if !cgcx.remark.is_empty() {
-        llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
-    }
+    llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
 
     if config.emit_no_opt_bc {
         let ext = format!("{}.no-opt.bc", name_extra);
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index bce73a27699..728ff1461fc 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -894,6 +894,22 @@ LLVMUnpackOptimizationDiagnostic(
     *message_out = wrap(&opt->getMsg());
 }
 
+extern "C" void
+LLVMUnpackInlineAsmDiagnostic(
+    LLVMDiagnosticInfoRef di,
+    unsigned *cookie_out,
+    LLVMTwineRef *message_out,
+    LLVMValueRef *instruction_out)
+{
+    // Undefined to call this not on an inline assembly diagnostic!
+    llvm::DiagnosticInfoInlineAsm *ia
+        = static_cast<llvm::DiagnosticInfoInlineAsm*>(unwrap(di));
+
+    *cookie_out = ia->getLocCookie();
+    *message_out = wrap(&ia->getMsgStr());
+    *instruction_out = wrap(ia->getInstruction());
+}
+
 extern "C" void LLVMWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef di, RustStringRef str) {
     raw_rust_string_ostream os(str);
     DiagnosticPrinterRawOStream dp(os);

From 918dd3488f450383c5ce27f0a27f45fa3fc9d0d6 Mon Sep 17 00:00:00 2001
From: Sean Patrick Santos <SeanPatrickSantos@gmail.com>
Date: Thu, 22 Jan 2015 11:54:45 -0700
Subject: [PATCH 08/33] Attempt fix for assertion on Windows, and add extra
 output for debugging.

---
 src/test/run-pass/issue-15149.rs | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs
index 59b1bb287fa..4b345f639e4 100644
--- a/src/test/run-pass/issue-15149.rs
+++ b/src/test/run-pass/issue-15149.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::path::BytesContainer;
 use std::io::{Command, fs, USER_RWX};
 use std::os;
 
@@ -15,7 +16,8 @@ fn main() {
     // If we're the child, make sure we were invoked correctly
     let args = os::args();
     if args.len() > 1 && args[1].as_slice() == "child" {
-        return assert_eq!(args[0].as_slice(), "mytest");
+        return assert_eq!(args[0],
+                          format!("mytest{}", os::consts::EXE_SUFFIX));
     }
 
     test();
@@ -38,7 +40,12 @@ fn test() {
     path.push(child_dir.clone());
     let path = os::join_paths(path.as_slice()).unwrap();
 
-    assert!(Command::new("mytest").env("PATH", path.as_slice())
-                                  .arg("child")
-                                  .status().unwrap().success());
+    let child_output = Command::new("mytest").env("PATH", path.as_slice())
+                                             .arg("child")
+                                             .output().unwrap();
+
+    assert!(child_output.status.success(),
+            format!("child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
+                    child_output.output.container_as_str().unwrap(),
+                    child_output.error.container_as_str().unwrap()));
 }

From ea6dc180fe145176e20ae7ecd6b9b0b7afffe67c Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Thu, 22 Jan 2015 18:59:33 -0500
Subject: [PATCH 09/33] remove weird sentence

Fixes #21531
---
 src/doc/reference.md | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/doc/reference.md b/src/doc/reference.md
index 9ec4708eb2f..e5fefc9e4c2 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2640,9 +2640,7 @@ The currently implemented features of the reference compiler are:
                    declare a `static` as being unique per-thread leveraging
                    LLVM's implementation which works in concert with the kernel
                    loader and dynamic linker. This is not necessarily available
-                   on all platforms, and usage of it is discouraged (rust
-                   focuses more on thread-local data instead of thread-local
-                   data).
+                   on all platforms, and usage of it is discouraged.
 
 * `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty
                    hack that will certainly be removed.

From 7f45dc9e68ffe80d399560644af48383a25427fd Mon Sep 17 00:00:00 2001
From: Sean Patrick Santos <SeanPatrickSantos@gmail.com>
Date: Thu, 22 Jan 2015 18:34:58 -0700
Subject: [PATCH 10/33] Add a random number string to the end of the
 issue-15149 test's child directory's name, and remove the directory after a
 successful test.

---
 src/test/run-pass/issue-15149.rs | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs
index 4b345f639e4..5d3571e4d74 100644
--- a/src/test/run-pass/issue-15149.rs
+++ b/src/test/run-pass/issue-15149.rs
@@ -8,9 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::path::BytesContainer;
 use std::io::{Command, fs, USER_RWX};
 use std::os;
+use std::path::BytesContainer;
+use std::rand::random;
 
 fn main() {
     // If we're the child, make sure we were invoked correctly
@@ -28,8 +29,10 @@ fn test() {
     let my_path = os::self_exe_name().unwrap();
     let my_dir  = my_path.dir_path();
 
-    let child_dir = Path::new(my_dir.join("issue-15149-child"));
-    drop(fs::mkdir(&child_dir, USER_RWX));
+    let random_u32: u32 = random();
+    let child_dir = Path::new(my_dir.join(format!("issue-15149-child-{}",
+                                                  random_u32)));
+    fs::mkdir(&child_dir, USER_RWX).unwrap();
 
     let child_path = child_dir.join(format!("mytest{}",
                                             os::consts::EXE_SUFFIX));
@@ -48,4 +51,7 @@ fn test() {
             format!("child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
                     child_output.output.container_as_str().unwrap(),
                     child_output.error.container_as_str().unwrap()));
+
+    fs::rmdir_recursive(&child_dir).unwrap();
+
 }

From a80807028a027c0a677777c8120237c5b3047993 Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Thu, 22 Jan 2015 22:35:28 -0500
Subject: [PATCH 11/33] Language tweak in configure

Fixes #13082
---
 configure | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/configure b/configure
index b2f8c33380c..5d26d4e7ed5 100755
--- a/configure
+++ b/configure
@@ -28,8 +28,8 @@ need_ok() {
 
 need_cmd() {
     if command -v $1 >/dev/null 2>&1
-    then msg "found $1"
-    else err "need $1"
+    then msg "found program $1"
+    else err "need program $1"
     fi
 }
 

From 7aa3ed1ee8b04df11ed42aeea64495cc1190aaf5 Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Thu, 22 Jan 2015 22:44:43 -0500
Subject: [PATCH 12/33] Check for make in configure

Fixes #13179
---
 configure | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configure b/configure
index b2f8c33380c..d3867b85074 100755
--- a/configure
+++ b/configure
@@ -340,6 +340,7 @@ need_cmd date
 need_cmd tr
 need_cmd sed
 need_cmd file
+need_cmd make
 
 msg "inspecting environment"
 

From fc5bbdf70f8a9b0bfd7e377cc7df732b119bdac7 Mon Sep 17 00:00:00 2001
From: Kevin Yap <me@kevinyap.ca>
Date: Thu, 22 Jan 2015 17:54:49 -0800
Subject: [PATCH 13/33] Make `make tidy` Python scripts more idiomatic

Also makes errorck.py and tidy.py compatible with Python 3.
---
 src/etc/errorck.py   | 39 ++++++++++++++-------------------------
 src/etc/licenseck.py | 12 ++++++------
 src/etc/tidy.py      |  2 +-
 3 files changed, 21 insertions(+), 32 deletions(-)

diff --git a/src/etc/errorck.py b/src/etc/errorck.py
index 17659309d3b..952e299265d 100644
--- a/src/etc/errorck.py
+++ b/src/etc/errorck.py
@@ -14,11 +14,10 @@
 import sys, os, re
 
 src_dir = sys.argv[1]
-
-errcode_map = { }
+errcode_map = {}
+error_re = re.compile("(E\d\d\d\d)")
 
 for (dirpath, dirnames, filenames) in os.walk(src_dir):
-
     if "src/test" in dirpath or "src/llvm" in dirpath:
         # Short circuit for fast
         continue
@@ -28,15 +27,12 @@ for (dirpath, dirnames, filenames) in os.walk(src_dir):
             continue
 
         path = os.path.join(dirpath, filename)
-        line_num = 1
+
         with open(path, 'r') as f:
-            for line in f:
-
-                p = re.compile("(E\d\d\d\d)")
-                m = p.search(line)
-                if not m is None:
-                    errcode = m.group(1)
-
+            for line_num, line in enumerate(f, start=1):
+                match = error_re.search(line)
+                if match:
+                    errcode = match.group(1)
                     new_record = [(errcode, path, line_num, line)]
                     existing = errcode_map.get(errcode)
                     if existing is not None:
@@ -45,26 +41,19 @@ for (dirpath, dirnames, filenames) in os.walk(src_dir):
                     else:
                         errcode_map[errcode] = new_record
 
-                line_num += 1
-
 errors = False
 all_errors = []
-for errcode in errcode_map:
-    entries = errcode_map[errcode]
-    all_errors += [entries[0][0]]
+
+for errcode, entries in errcode_map.items():
+    all_errors.append(entries[0][0])
     if len(entries) > 1:
-        print "error: duplicate error code " + errcode
+        print("error: duplicate error code " + errcode)
         for entry in entries:
-            print entry[1] + ": " + str(entry[2])
-            print entry[3]
+            print("{1}: {2}\n{3}".format(*entry))
         errors = True
 
-print str(len(errcode_map)) + " error codes"
-
-all_errors.sort()
-all_errors.reverse()
-
-print "highest error code: " + all_errors[0]
+print("{0} error codes".format(len(errcode_map)))
+print("highest error code: " + max(all_errors))
 
 if errors:
     sys.exit(1)
diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py
index 9ac0acc38a7..44a50efcd09 100644
--- a/src/etc/licenseck.py
+++ b/src/etc/licenseck.py
@@ -57,18 +57,18 @@ exceptions = [
 
 def check_license(name, contents):
     # Whitelist check
-    for exception in exceptions:
-        if name.endswith(exception):
-            return True
+    if any(name.endswith(e) for e in exceptions):
+        return True
 
     # Xfail check
     firstlineish = contents[:100]
-    if firstlineish.find("ignore-license") != -1:
+    if "ignore-license" in firstlineish:
         return True
 
     # License check
     boilerplate = contents[:500]
-    if (boilerplate.find(license1) == -1 or boilerplate.find(license2) == -1) and \
-       (boilerplate.find(license3) == -1 or boilerplate.find(license4) == -1):
+    if (license1 not in boilerplate or license2 not in boilerplate) and \
+       (license3 not in boilerplate or license4 not in boilerplate):
         return False
+
     return True
diff --git a/src/etc/tidy.py b/src/etc/tidy.py
index 536ab7f30b9..c65b762e517 100644
--- a/src/etc/tidy.py
+++ b/src/etc/tidy.py
@@ -113,7 +113,7 @@ try:
     if current_name != "":
         do_license_check(current_name, current_contents)
 
-except UnicodeDecodeError, e:
+except UnicodeDecodeError as e:
     report_err("UTF-8 decoding error " + str(e))
 
 

From 76c279a4cfc9e54327bcc668a895ec19185139e9 Mon Sep 17 00:00:00 2001
From: Kevin Yap <me@kevinyap.ca>
Date: Thu, 22 Jan 2015 20:40:15 -0800
Subject: [PATCH 14/33] Use a regex to perform license check

---
 src/etc/licenseck.py | 39 ++++++++++++---------------------------
 1 file changed, 12 insertions(+), 27 deletions(-)

diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py
index 44a50efcd09..f38583ee1fb 100644
--- a/src/etc/licenseck.py
+++ b/src/etc/licenseck.py
@@ -8,29 +8,18 @@
 # option. This file may not be copied, modified, or distributed
 # except according to those terms.
 
-license1 = """// Copyright """
-license2 = """ 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 re
 
-license3 = """# Copyright """
-license4 = """ 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.
-"""
+license_re = re.compile(
+u"""(#|//) Copyright .* The Rust Project Developers. See the COPYRIGHT
+\\1 file at the top-level directory of this distribution and at
+\\1 http://rust-lang.org/COPYRIGHT.
+\\1
+\\1 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+\\1 http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+\\1 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+\\1 option. This file may not be copied, modified, or distributed
+\\1 except according to those terms.""")
 
 exceptions = [
     "rt/rust_android_dummy.cpp", # BSD, chromium
@@ -67,8 +56,4 @@ def check_license(name, contents):
 
     # License check
     boilerplate = contents[:500]
-    if (license1 not in boilerplate or license2 not in boilerplate) and \
-       (license3 not in boilerplate or license4 not in boilerplate):
-        return False
-
-    return True
+    return bool(license_re.search(boilerplate))

From 06714c2bceb1ec7803e1a3c52e613fa003722fbc Mon Sep 17 00:00:00 2001
From: Flavio Percoco <flaper87@gmail.com>
Date: Fri, 23 Jan 2015 15:21:11 +0100
Subject: [PATCH 15/33] Fix compile test for stage0

---
 src/compiletest/common.rs | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs
index c21785c45a3..eb8c013de0e 100644
--- a/src/compiletest/common.rs
+++ b/src/compiletest/common.rs
@@ -13,6 +13,20 @@ use std::fmt;
 use std::str::FromStr;
 use regex::Regex;
 
+#[cfg(stage0)] // NOTE: remove impl after snapshot
+#[derive(Clone, PartialEq, Show)]
+pub enum Mode {
+    CompileFail,
+    RunFail,
+    RunPass,
+    RunPassValgrind,
+    Pretty,
+    DebugInfoGdb,
+    DebugInfoLldb,
+    Codegen
+}
+
+#[cfg(not(stage0))] // NOTE: remove cfg after snapshot
 #[derive(Clone, PartialEq, Debug)]
 pub enum Mode {
     CompileFail,
@@ -25,6 +39,7 @@ pub enum Mode {
     Codegen
 }
 
+
 impl Copy for Mode {}
 
 impl FromStr for Mode {

From aca793966a84dfec785c3e300e62588dad35dfeb Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Tue, 13 Jan 2015 15:00:49 -0500
Subject: [PATCH 16/33] Soup up 'method syntax' chapter of the Book

Fixes #16969
---
 src/doc/trpl/method-syntax.md | 114 +++++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 2 deletions(-)

diff --git a/src/doc/trpl/method-syntax.md b/src/doc/trpl/method-syntax.md
index 3d8de00991c..e6570c2ee74 100644
--- a/src/doc/trpl/method-syntax.md
+++ b/src/doc/trpl/method-syntax.md
@@ -18,6 +18,8 @@ x.foo().bar().baz();
 Luckily, as you may have guessed with the leading question, you can! Rust provides
 the ability to use this *method call syntax* via the `impl` keyword.
 
+## Method calls
+
 Here's how it works:
 
 ```{rust}
@@ -56,11 +58,56 @@ other parameter. Because we know it's a `Circle`, we can access the `radius`
 just like we would with any other struct. An import of π and some
 multiplications later, and we have our area.
 
+## Chaining method calls
+
+So, now we know how to call a method, such as `foo.bar()`. But what about our
+original example, `foo.bar().baz()`? This is called 'method chaining', and we
+can do it by returning `self`.
+
+```
+struct Circle {
+    x: f64,
+    y: f64,
+    radius: f64,
+}
+
+impl Circle {
+    fn area(&self) -> f64 {
+        std::f64::consts::PI * (self.radius * self.radius)
+    }
+
+    fn grow(&self) -> Circle {
+        Circle { x: self.x, y: self.y, radius: (self.radius * 10.0) }
+    }
+}
+
+fn main() {
+    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
+    println!("{}", c.area());
+
+    let d = c.grow().area();
+    println!("{}", d);
+}
+```
+
+Check the return type:
+
+```
+# struct Circle;
+# impl Circle {
+fn grow(&self) -> Circle {
+# Circle } }
+```
+
+We just say we're returning a `Circle`. With this, we can grow a new circle
+that's twice as big as the old one.
+
+## Static methods
+
 You can also define methods that do not take a `self` parameter. Here's a
 pattern that's very common in Rust code:
 
-```{rust}
-# #![allow(non_shorthand_field_patterns)]
+```
 struct Circle {
     x: f64,
     y: f64,
@@ -86,3 +133,66 @@ This *static method* builds a new `Circle` for us. Note that static methods
 are called with the `Struct::method()` syntax, rather than the `ref.method()`
 syntax.
 
+## Builder Pattern
+
+Let's say that we want our users to be able to create Circles, but we will
+allow them to only set the properties they care about. Otherwise, the `x`
+and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't
+have method overloading, named arguments, or variable arguments. We employ
+the builder pattern instead. It looks like this:
+
+```
+struct Circle {
+    x: f64,
+    y: f64,
+    radius: f64,
+}
+
+impl Circle {
+    fn area(&self) -> f64 {
+        std::f64::consts::PI * (self.radius * self.radius)
+    }
+}
+
+struct CircleBuilder {
+    coordinate: f64,
+    radius: f64,
+}
+
+impl CircleBuilder {
+    fn new() -> CircleBuilder {
+        CircleBuilder { coordinate: 0.0, radius: 0.0, }
+    }
+
+    fn coordinate(&mut self, coordinate: f64) -> &mut CircleBuilder {
+	self.coordinate = coordinate;
+	self
+    }
+
+    fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
+	self.radius = radius;
+	self
+    }
+
+    fn finalize(&self) -> Circle {
+        Circle { x: self.coordinate, y: self.coordinate, radius: self.radius }
+    }
+}
+
+fn main() {
+    let c = CircleBuilder::new()
+                .coordinate(10.0)
+                .radius(5.0)
+                .finalize();
+
+
+    println!("area: {}", c.area());
+}
+```
+
+What we've done here is make another struct, `CircleBuilder`. We've defined our
+builder methods on it. We've also defined our `area()` method on `Circle`. We
+also made one more method on `CircleBuilder`: `finalize()`. This method creates
+our final `Circle` from the builder. Now, we've used the type system to enforce
+our concerns: we can use the methods on `CircleBuilder` to constrain making
+`Circle`s in any way we choose.

From dc2b3ac889d9841bff3cd9ddc7c63f5955212313 Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Thu, 22 Jan 2015 19:09:59 -0500
Subject: [PATCH 17/33] Remove lang items from the reference.

Fixes #19759
---
 src/doc/reference.md | 131 +------------------------------------------
 1 file changed, 1 insertion(+), 130 deletions(-)

diff --git a/src/doc/reference.md b/src/doc/reference.md
index 9ec4708eb2f..a0d46750097 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2279,136 +2279,7 @@ The name `str_eq` has a special meaning to the Rust compiler, and the presence
 of this definition means that it will use this definition when generating calls
 to the string equality function.
 
-A complete list of the built-in language items follows:
-
-#### Built-in Traits
-
-* `copy`
-  : Types that do not move ownership when used by-value.
-* `drop`
-  : Have destructors.
-* `send`
-  : Able to be sent across thread boundaries.
-* `sized`
-  : Has a size known at compile time.
-* `sync`
-  : Able to be safely shared between threads when aliased.
-
-#### Operators
-
-These language items are traits:
-
-* `add`
-  : Elements can be added (for example, integers and floats).
-* `sub`
-  : Elements can be subtracted.
-* `mul`
-  : Elements can be multiplied.
-* `div`
-  : Elements have a division operation.
-* `rem`
-  : Elements have a remainder operation.
-* `neg`
-  : Elements can be negated arithmetically.
-* `not`
-  : Elements can be negated logically.
-* `bitxor`
-  : Elements have an exclusive-or operation.
-* `bitand`
-  : Elements have a bitwise `and` operation.
-* `bitor`
-  : Elements have a bitwise `or` operation.
-* `shl`
-  : Elements have a left shift operation.
-* `shr`
-  : Elements have a right shift operation.
-* `index`
-  : Elements can be indexed.
-* `index_mut`
-  : ___Needs filling in___
-* `eq`
-  : Elements can be compared for equality.
-* `ord`
-  : Elements have a partial ordering.
-* `deref`
-  : `*` can be applied, yielding a reference to another type.
-* `deref_mut`
-  : `*` can be applied, yielding a mutable reference to another type.
-
-These are functions:
-
-* `fn`
-  : ___Needs filling in___
-* `fn_mut`
-  : ___Needs filling in___
-* `fn_once`
-  : ___Needs filling in___
-* `str_eq`
-  : Compare two strings (`&str`) for equality.
-* `strdup_uniq`
-  : Return a new unique string
-    containing a copy of the contents of a unique string.
-
-#### Types
-
-* `type_id`
-  : The type returned by the `type_id` intrinsic.
-* `unsafe`
-  : A type whose contents can be mutated through an immutable reference.
-
-#### Marker types
-
-These types help drive the compiler's analysis
-
-* `begin_unwind`
-  : ___Needs filling in___
-* `no_copy_bound`
-  : This type does not implement "copy", even if eligible.
-* `eh_personality`
-  : ___Needs filling in___
-* `exchange_free`
-  : Free memory that was allocated on the exchange heap.
-* `exchange_malloc`
-  : Allocate memory on the exchange heap.
-* `closure_exchange_malloc`
-  : ___Needs filling in___
-* `panic`
-  : Abort the program with an error.
-* `fail_bounds_check`
-  : Abort the program with a bounds check error.
-* `free`
-  : Free memory that was allocated on the managed heap.
-* `gc`
-  : ___Needs filling in___
-* `exchange_heap`
-  : ___Needs filling in___
-* `iterator`
-  : ___Needs filling in___
-* `contravariant_lifetime`
-  : The lifetime parameter should be considered contravariant.
-* `covariant_lifetime`
-  : The lifetime parameter should be considered covariant.
-* `invariant_lifetime`
-  : The lifetime parameter should be considered invariant.
-* `malloc`
-  : Allocate memory on the managed heap.
-* `owned_box`
-  : ___Needs filling in___
-* `stack_exhausted`
-  : ___Needs filling in___
-* `start`
-  : ___Needs filling in___
-* `contravariant_type`
-  : The type parameter should be considered contravariant.
-* `covariant_type`
-  : The type parameter should be considered covariant.
-* `invariant_type`
-  : The type parameter should be considered invariant.
-* `ty_desc`
-  : ___Needs filling in___
-
-> **Note:** This list is likely to become out of date. We should auto-generate
-> it from `librustc/middle/lang_items.rs`.
+A complete list of the built-in language items will be added in the future.
 
 ### Inline attributes
 

From 91037a417ed9bd19608f3f06fd6214fcc0daf9cf Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Fri, 23 Jan 2015 13:53:19 -0500
Subject: [PATCH 18/33] remove discuss link from the book

---
 src/doc/trpl/installing-rust.md | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/doc/trpl/installing-rust.md b/src/doc/trpl/installing-rust.md
index 5893b51a420..80288c4c3d9 100644
--- a/src/doc/trpl/installing-rust.md
+++ b/src/doc/trpl/installing-rust.md
@@ -83,7 +83,6 @@ If not, there are a number of places where you can get help. The easiest is
 you can access through
 [Mibbit](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust). Click
 that link, and you'll be chatting with other Rustaceans (a silly nickname we
-call ourselves), and we can help you out. Other great resources include [our
-forum](http://discuss.rust-lang.org/), [the /r/rust
-subreddit](http://www.reddit.com/r/rust), and [Stack
+call ourselves), and we can help you out. Other great resources include [the
+/r/rust subreddit](http://www.reddit.com/r/rust), and [Stack
 Overflow](http://stackoverflow.com/questions/tagged/rust).

From 08246520c0fef902b169233e26e15cf58ef1cd8b Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Fri, 23 Jan 2015 10:38:50 -0800
Subject: [PATCH 19/33] std: Relax Result::unwrap() to Debug

This commit relaxes the bound on `Result::unwrap` and `Result::unwrap_err` from
the `Display` trait to the `Debug` trait for generating an error message about
the unwrapping operation.

This commit is a breaking change and any breakage should be mitigated by
ensuring that `Debug` is implemented on the relevant type.

[breaking-change]
---
 src/libcore/result.rs       | 10 +++++-----
 src/libstd/sync/mpsc/mod.rs | 21 +++++++++++++++++++--
 src/libstd/sync/poison.rs   | 19 +++++++++++++++++--
 3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index c3d49e24978..118f29ccfa4 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -229,7 +229,7 @@
 use self::Result::{Ok, Err};
 
 use clone::Clone;
-use fmt::Display;
+use fmt::Debug;
 use iter::{Iterator, IteratorExt, DoubleEndedIterator, FromIterator, ExactSizeIterator};
 use ops::{FnMut, FnOnce};
 use option::Option::{self, None, Some};
@@ -714,7 +714,7 @@ impl<T, E> Result<T, E> {
 }
 
 #[stable]
-impl<T, E: Display> Result<T, E> {
+impl<T, E: Debug> Result<T, E> {
     /// Unwraps a result, yielding the content of an `Ok`.
     ///
     /// # Panics
@@ -739,13 +739,13 @@ impl<T, E: Display> Result<T, E> {
         match self {
             Ok(t) => t,
             Err(e) =>
-                panic!("called `Result::unwrap()` on an `Err` value: {}", e)
+                panic!("called `Result::unwrap()` on an `Err` value: {:?}", e)
         }
     }
 }
 
 #[stable]
-impl<T: Display, E> Result<T, E> {
+impl<T: Debug, E> Result<T, E> {
     /// Unwraps a result, yielding the content of an `Err`.
     ///
     /// # Panics
@@ -769,7 +769,7 @@ impl<T: Display, E> Result<T, E> {
     pub fn unwrap_err(self) -> E {
         match self {
             Ok(t) =>
-                panic!("called `Result::unwrap_err()` on an `Ok` value: {}", t),
+                panic!("called `Result::unwrap_err()` on an `Ok` value: {:?}", t),
             Err(e) => e
         }
     }
diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs
index 062bdf346cd..893f353b1a7 100644
--- a/src/libstd/sync/mpsc/mod.rs
+++ b/src/libstd/sync/mpsc/mod.rs
@@ -382,7 +382,7 @@ impl<T> !Sync for SyncSender<T> {}
 /// A `send` operation can only fail if the receiving end of a channel is
 /// disconnected, implying that the data could never be received. The error
 /// contains the data being sent as a payload so it can be recovered.
-#[derive(PartialEq, Eq, Show)]
+#[derive(PartialEq, Eq)]
 #[stable]
 pub struct SendError<T>(pub T);
 
@@ -412,7 +412,7 @@ pub enum TryRecvError {
 
 /// This enumeration is the list of the possible error outcomes for the
 /// `SyncSender::try_send` method.
-#[derive(PartialEq, Clone, Show)]
+#[derive(PartialEq, Clone)]
 #[stable]
 pub enum TrySendError<T> {
     /// The data could not be sent on the channel because it would require that
@@ -961,6 +961,13 @@ impl<T: Send> Drop for Receiver<T> {
     }
 }
 
+#[stable]
+impl<T> fmt::Debug for SendError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        "SendError(..)".fmt(f)
+    }
+}
+
 #[stable]
 impl<T> fmt::Display for SendError<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -968,6 +975,16 @@ impl<T> fmt::Display for SendError<T> {
     }
 }
 
+#[stable]
+impl<T> fmt::Debug for TrySendError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            TrySendError::Full(..) => "Full(..)".fmt(f),
+            TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f),
+        }
+    }
+}
+
 #[stable]
 impl<T> fmt::Display for TrySendError<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs
index c97fcf7cefb..d7fcee3ae2e 100644
--- a/src/libstd/sync/poison.rs
+++ b/src/libstd/sync/poison.rs
@@ -53,7 +53,6 @@ pub struct Guard {
 /// is held. The precise semantics for when a lock is poisoned is documented on
 /// each lock, but once a lock is poisoned then all future acquisitions will
 /// return this error.
-#[derive(Show)]
 #[stable]
 pub struct PoisonError<T> {
     guard: T,
@@ -61,7 +60,6 @@ pub struct PoisonError<T> {
 
 /// An enumeration of possible errors which can occur while calling the
 /// `try_lock` method.
-#[derive(Show)]
 #[stable]
 pub enum TryLockError<T> {
     /// The lock could not be acquired because another task failed while holding
@@ -92,6 +90,13 @@ pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
 #[stable]
 pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
 
+#[stable]
+impl<T> fmt::Debug for PoisonError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        "PoisonError { inner: .. }".fmt(f)
+    }
+}
+
 #[stable]
 impl<T> fmt::Display for PoisonError<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -133,6 +138,16 @@ impl<T> FromError<PoisonError<T>> for TryLockError<T> {
     }
 }
 
+#[stable]
+impl<T> fmt::Debug for TryLockError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
+            TryLockError::WouldBlock => "WouldBlock".fmt(f)
+        }
+    }
+}
+
 #[stable]
 impl<T> fmt::Display for TryLockError<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

From 6f7c0b16d3bb0be438a0394595c74904ab868770 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Fri, 23 Jan 2015 19:35:10 -0500
Subject: [PATCH 20/33] add test for issue 19660

closes #19660
---
 src/test/compile-fail/issue-19660.rs | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 src/test/compile-fail/issue-19660.rs

diff --git a/src/test/compile-fail/issue-19660.rs b/src/test/compile-fail/issue-19660.rs
new file mode 100644
index 00000000000..f83037d47bb
--- /dev/null
+++ b/src/test/compile-fail/issue-19660.rs
@@ -0,0 +1,22 @@
+// Copyright 2015 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.
+
+// error-pattern: requires `copy` lang_item
+
+#![feature(lang_items, start)]
+#![no_std]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[start]
+fn main(_: int, _: *const *const u8) -> int {
+    0
+}

From 000dc07f71baafc31da74e392ad5530fb6de9757 Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Thu, 1 Jan 2015 03:03:14 +1100
Subject: [PATCH 21/33] Store a method-from-trait's impl in some cases when it
 is known.

This allows one to look at an `ExprMethodCall` `foo.bar()` where `bar`
is a method in some trait and (sometimes) extract the `impl` that `bar`
is defined in, e.g.

    trait Foo {
        fn bar(&self);
    }

    impl Foo for uint { // <A>
        fn bar(&self) {}
    }

    fn main() {
        1u.bar(); // impl_def_id == Some(<A>)
    }

This definitely doesn't handle all cases, but is correct when it is
known, meaning it should only be used for certain linting/heuristic
purposes; no safety analysis.
---
 src/librustc/middle/astencode.rs            | 22 +++++++++++++++++++++
 src/librustc/middle/ty.rs                   |  7 ++++++-
 src/librustc/middle/ty_fold.rs              |  3 ++-
 src/librustc_trans/trans/meth.rs            |  3 ++-
 src/librustc_typeck/check/method/confirm.rs |  9 ++++++---
 src/librustc_typeck/check/method/mod.rs     |  3 ++-
 6 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 537a2b3f545..430b63f81c8 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -627,6 +627,7 @@ impl<'tcx> tr for MethodOrigin<'tcx> {
                         // def-id is already translated when we read it out
                         trait_ref: mp.trait_ref.clone(),
                         method_num: mp.method_num,
+                        impl_def_id: mp.impl_def_id.tr(dcx),
                     }
                 )
             }
@@ -879,6 +880,16 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
                             try!(this.emit_struct_field("method_num", 0, |this| {
                                 this.emit_uint(p.method_num)
                             }));
+                            try!(this.emit_struct_field("impl_def_id", 0, |this| {
+                                this.emit_option(|this| {
+                                    match p.impl_def_id {
+                                        None => this.emit_option_none(),
+                                        Some(did) => this.emit_option_some(|this| {
+                                            Ok(this.emit_def_id(did))
+                                        })
+                                    }
+                                })
+                            }));
                             Ok(())
                         })
                     })
@@ -1452,6 +1463,17 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
                                         this.read_struct_field("method_num", 1, |this| {
                                             this.read_uint()
                                         }).unwrap()
+                                    },
+                                    impl_def_id: {
+                                        this.read_struct_field("impl_def_id", 2, |this| {
+                                            this.read_option(|this, b| {
+                                                if b {
+                                                    Ok(Some(this.read_def_id(dcx)))
+                                                } else {
+                                                    Ok(None)
+                                                }
+                                            })
+                                        }).unwrap()
                                     }
                                 }))
                         }).unwrap()
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 83bbdf14e4a..5b5b53d1246 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -453,9 +453,14 @@ pub struct MethodParam<'tcx> {
     // never contains bound regions; those regions should have been
     // instantiated with fresh variables at this point.
     pub trait_ref: Rc<ty::TraitRef<'tcx>>,
-
     // index of uint in the list of methods for the trait
     pub method_num: uint,
+
+    /// The impl for the trait from which the method comes. This
+    /// should only be used for certain linting/heuristic purposes
+    /// since there is no guarantee that this is Some in every
+    /// situation that it could/should be.
+    pub impl_def_id: Option<ast::DefId>,
 }
 
 // details for a method invoked with a receiver whose type is an object
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index b4e6cff954b..0b8c7786015 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -310,7 +310,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::MethodOrigin<'tcx> {
             ty::MethodTypeParam(ref param) => {
                 ty::MethodTypeParam(ty::MethodParam {
                     trait_ref: param.trait_ref.fold_with(folder),
-                    method_num: param.method_num
+                    method_num: param.method_num,
+                    impl_def_id: param.impl_def_id,
                 })
             }
             ty::MethodTraitObject(ref object) => {
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index c2f19670e4f..9356be1b9b4 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -132,7 +132,8 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
         ty::MethodTypeParam(ty::MethodParam {
             ref trait_ref,
-            method_num
+            method_num,
+            impl_def_id: _
         }) => {
             let trait_ref = ty::Binder(bcx.monomorphize(trait_ref));
             let span = bcx.tcx().map.span(method_call.expr_id);
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 79460774859..4aa0a211221 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -256,7 +256,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                         &impl_polytype.substs,
                         &ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap());
                 let origin = MethodTypeParam(MethodParam { trait_ref: impl_trait_ref.clone(),
-                                                           method_num: method_num });
+                                                           method_num: method_num,
+                                                           impl_def_id: Some(impl_def_id) });
                 (impl_trait_ref.substs.clone(), origin)
             }
 
@@ -275,7 +276,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                 let trait_ref =
                     Rc::new(ty::TraitRef::new(trait_def_id, self.tcx().mk_substs(substs.clone())));
                 let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref,
-                                                           method_num: method_num });
+                                                           method_num: method_num,
+                                                           impl_def_id: None });
                 (substs, origin)
             }
 
@@ -285,7 +287,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                 let trait_ref = self.replace_late_bound_regions_with_fresh_var(&*poly_trait_ref);
                 let substs = trait_ref.substs.clone();
                 let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref,
-                                                           method_num: method_num });
+                                                           method_num: method_num,
+                                                           impl_def_id: None });
                 (substs, origin)
             }
         }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 345bc5fd2aa..d92cc1dfc1e 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -287,7 +287,8 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
     let callee = MethodCallee {
         origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(),
-                                            method_num: method_num}),
+                                            method_num: method_num,
+                                            impl_def_id: None}),
         ty: fty,
         substs: trait_ref.substs.clone()
     };

From 2595780e2690364f081c4c33a78286e7ddaa1953 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Fri, 23 Jan 2015 21:13:35 -0800
Subject: [PATCH 22/33] Fix beta naming

---
 mk/main.mk | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mk/main.mk b/mk/main.mk
index 8c910f47596..0e52f168f59 100644
--- a/mk/main.mk
+++ b/mk/main.mk
@@ -30,8 +30,8 @@ CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)
 CFG_DISABLE_UNSTABLE_FEATURES=1
 endif
 ifeq ($(CFG_RELEASE_CHANNEL),beta)
-CFG_RELEASE=$(CFG_RELEASE_NUM)-beta(CFG_PRERELEASE_VERSION)
-CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-beta(CFG_PRERELEASE_VERSION)
+CFG_RELEASE=$(CFG_RELEASE_NUM)-beta$(CFG_PRERELEASE_VERSION)
+CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-beta$(CFG_PRERELEASE_VERSION)
 CFG_DISABLE_UNSTABLE_FEATURES=1
 endif
 ifeq ($(CFG_RELEASE_CHANNEL),nightly)

From ec88426ea8dbee3a4647039d65deea4de31fb69a Mon Sep 17 00:00:00 2001
From: Daniel Griffen <daniel@dgriffen.com>
Date: Mon, 5 Jan 2015 23:30:58 -0600
Subject: [PATCH 23/33] Implement winsize() for unix.

---
 src/libstd/sys/unix/tty.rs | 41 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/src/libstd/sys/unix/tty.rs b/src/libstd/sys/unix/tty.rs
index bee3d440a16..d20469f5106 100644
--- a/src/libstd/sys/unix/tty.rs
+++ b/src/libstd/sys/unix/tty.rs
@@ -11,14 +11,22 @@
 use prelude::v1::*;
 
 use sys::fs::FileDesc;
-use libc::{self, c_int};
+use libc::{self, c_int, c_ulong, funcs};
 use io::{self, IoResult, IoError};
+use sys::c;
 use sys_common;
 
 pub struct TTY {
     pub fd: FileDesc,
 }
 
+#[cfg(any(target_os = "macos",
+          target_os = "freebsd"))]
+const TIOCGWINSZ: c_ulong = 0x40087468;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+const TIOCGWINSZ: c_ulong = 0x00005413;
+
 impl TTY {
     pub fn new(fd: c_int) -> IoResult<TTY> {
         if unsafe { libc::isatty(fd) } != 0 {
@@ -41,8 +49,39 @@ impl TTY {
     pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
         Err(sys_common::unimpl())
     }
+
+    #[cfg(any(target_os = "linux",
+              target_os = "android",
+              target_os = "macos",
+              target_os = "freebsd"))]
+    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
+        unsafe {
+            #[repr(C)]
+            struct winsize {
+                ws_row: u16,
+                ws_col: u16,
+                ws_xpixel: u16,
+                ws_ypixel: u16
+            }
+
+            let mut size = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
+            if c::ioctl(self.fd.fd(), TIOCGWINSZ, &mut size) == -1 {
+                Err(IoError {
+                    kind: io::OtherIoError,
+                    desc: "Size of terminal could not be determined",
+                    detail: None,
+                })
+            } else {
+                Ok((size.ws_col as int, size.ws_row as int))
+            }
+        }
+    }
+
+    #[cfg(any(target_os = "ios",
+              target_os = "dragonfly"))]
     pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
         Err(sys_common::unimpl())
     }
+
     pub fn isatty(&self) -> bool { false }
 }

From fbef241709093b3fb18cb12500fd33e4ab964b62 Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Thu, 1 Jan 2015 03:06:38 +1100
Subject: [PATCH 24/33] Add a lint to detect unconditional recursion.

E.g. `fn foo() { foo() }`, or, more subtlely

    impl Foo for Box<Foo+'static> {
        fn bar(&self) {
            self.bar();
        }
    }

The compiler will warn and point out the points where recursion occurs,
if it determines that the function cannot return without calling itself.

Closes #17899.
---
 src/librustc/lint/builtin.rs                  | 192 +++++++++++++++++-
 src/librustc/lint/context.rs                  |   1 +
 .../lint-unconditional-recursion.rs           |  66 ++++++
 3 files changed, 258 insertions(+), 1 deletion(-)
 create mode 100644 src/test/compile-fail/lint-unconditional-recursion.rs

diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index fef1017b782..531bdc2941e 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -32,10 +32,12 @@ use middle::subst::Substs;
 use middle::ty::{self, Ty};
 use middle::{def, pat_util, stability};
 use middle::const_eval::{eval_const_expr_partial, const_int, const_uint};
+use middle::cfg;
 use util::ppaux::{ty_to_string};
 use util::nodemap::{FnvHashMap, NodeSet};
-use lint::{Context, LintPass, LintArray, Lint};
+use lint::{Level, Context, LintPass, LintArray, Lint};
 
+use std::collections::BitvSet;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::num::SignedInt;
 use std::{cmp, slice};
@@ -1788,6 +1790,194 @@ impl LintPass for Stability {
     }
 }
 
+declare_lint! {
+    pub UNCONDITIONAL_RECURSION,
+    Warn,
+    "functions that cannot return without calling themselves"
+}
+
+#[derive(Copy)]
+pub struct UnconditionalRecursion;
+
+
+impl LintPass for UnconditionalRecursion {
+    fn get_lints(&self) -> LintArray {
+        lint_array![UNCONDITIONAL_RECURSION]
+    }
+
+    fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl,
+                blk: &ast::Block, sp: Span, id: ast::NodeId) {
+        type F = for<'tcx> fn(&ty::ctxt<'tcx>,
+                              ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool;
+
+        let (name, checker) = match fn_kind {
+            visit::FkItemFn(name, _, _, _) => (name, id_refers_to_this_fn as F),
+            visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F),
+            // closures can't recur, so they don't matter.
+            visit::FkFnBlock => return
+        };
+
+        let impl_def_id = ty::impl_of_method(cx.tcx, ast_util::local_def(id))
+            .unwrap_or(ast_util::local_def(ast::DUMMY_NODE_ID));
+        assert!(ast_util::is_local(impl_def_id));
+        let impl_node_id = impl_def_id.node;
+
+        // Walk through this function (say `f`) looking to see if
+        // every possible path references itself, i.e. the function is
+        // called recursively unconditionally. This is done by trying
+        // to find a path from the entry node to the exit node that
+        // *doesn't* call `f` by traversing from the entry while
+        // pretending that calls of `f` are sinks (i.e. ignoring any
+        // exit edges from them).
+        //
+        // NB. this has an edge case with non-returning statements,
+        // like `loop {}` or `panic!()`: control flow never reaches
+        // the exit node through these, so one can have a function
+        // that never actually calls itselfs but is still picked up by
+        // this lint:
+        //
+        //     fn f(cond: bool) {
+        //         if !cond { panic!() } // could come from `assert!(cond)`
+        //         f(false)
+        //     }
+        //
+        // In general, functions of that form may be able to call
+        // itself a finite number of times and then diverge. The lint
+        // considers this to be an error for two reasons, (a) it is
+        // easier to implement, and (b) it seems rare to actually want
+        // to have behaviour like the above, rather than
+        // e.g. accidentally recurring after an assert.
+
+        let cfg = cfg::CFG::new(cx.tcx, blk);
+
+        let mut work_queue = vec![cfg.entry];
+        let mut reached_exit_without_self_call = false;
+        let mut self_call_spans = vec![];
+        let mut visited = BitvSet::new();
+
+        while let Some(idx) = work_queue.pop() {
+            let cfg_id = idx.node_id();
+            if idx == cfg.exit {
+                // found a path!
+                reached_exit_without_self_call = true;
+                break
+            } else if visited.contains(&cfg_id) {
+                // already done
+                continue
+            }
+            visited.insert(cfg_id);
+            let node_id = cfg.graph.node_data(idx).id;
+
+            // is this a recursive call?
+            if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) {
+
+                self_call_spans.push(cx.tcx.map.span(node_id));
+                // this is a self call, so we shouldn't explore past
+                // this node in the CFG.
+                continue
+            }
+            // add the successors of this node to explore the graph further.
+            cfg.graph.each_outgoing_edge(idx, |_, edge| {
+                let target_idx = edge.target();
+                let target_cfg_id = target_idx.node_id();
+                if !visited.contains(&target_cfg_id) {
+                    work_queue.push(target_idx)
+                }
+                true
+            });
+        }
+
+        // check the number of sell calls because a function that
+        // doesn't return (e.g. calls a `-> !` function or `loop { /*
+        // no break */ }`) shouldn't be linted unless it actually
+        // recurs.
+        if !reached_exit_without_self_call && self_call_spans.len() > 0 {
+            cx.span_lint(UNCONDITIONAL_RECURSION, sp,
+                         "function cannot return without recurring");
+
+            // FIXME #19668: these could be span_lint_note's instead of this manual guard.
+            if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
+                let sess = cx.sess();
+                // offer some help to the programmer.
+                for call in self_call_spans.iter() {
+                    sess.span_note(*call, "recursive call site")
+                }
+                sess.span_help(sp, "a `loop` may express intention better if this is on purpose")
+            }
+        }
+
+        // all done
+        return;
+
+        // Functions for identifying if the given NodeId `id`
+        // represents a call to the function `fn_id`/method
+        // `method_id`.
+
+        fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                      _: ast::NodeId,
+                                      fn_id: ast::NodeId,
+                                      _: ast::Ident,
+                                      id: ast::NodeId) -> bool {
+            tcx.def_map.borrow().get(&id)
+                .map_or(false, |def| {
+                    let did = def.def_id();
+                    ast_util::is_local(did) && did.node == fn_id
+                })
+        }
+
+        // check if the method call `id` refers to method `method_id`
+        // (with name `method_name` contained in impl `impl_id`).
+        fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                          impl_id: ast::NodeId,
+                                          method_id: ast::NodeId,
+                                          method_name: ast::Ident,
+                                          id: ast::NodeId) -> bool {
+            let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) {
+                None => return false,
+                Some(m) => match m.origin {
+                    // There's no way to know if a method call via a
+                    // vtable is recursion, so we assume it's not.
+                    ty::MethodTraitObject(_) => return false,
+
+                    // This `did` refers directly to the method definition.
+                    ty::MethodStatic(did) | ty::MethodStaticUnboxedClosure(did) => did,
+
+                    // MethodTypeParam are methods from traits:
+
+                    // The `impl ... for ...` of this method call
+                    // isn't known, e.g. it might be a default method
+                    // in a trait, so we get the def-id of the trait
+                    // method instead.
+                    ty::MethodTypeParam(
+                        ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => {
+                        ty::trait_item(tcx, trait_ref.def_id, method_num).def_id()
+                    }
+
+                    // The `impl` is known, so we check that with a
+                    // special case:
+                    ty::MethodTypeParam(
+                        ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => {
+
+                        let name = match tcx.map.expect_expr(id).node {
+                            ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node,
+                            _ => tcx.sess.span_bug(
+                                tcx.map.span(id),
+                                "non-method call expr behaving like a method call?")
+                        };
+                        // it matches if it comes from the same impl,
+                        // and has the same method name.
+                        return ast_util::is_local(impl_def_id)
+                            && impl_def_id.node == impl_id
+                            && method_name.name == name.name
+                    }
+                }
+            };
+
+            ast_util::is_local(did) && did.node == method_id
+        }
+    }
+}
+
 declare_lint! {
     pub UNUSED_IMPORTS,
     Warn,
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 4cbfcf7e91a..3728e6f4980 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -211,6 +211,7 @@ impl LintStore {
                      UnusedAllocation,
                      MissingCopyImplementations,
                      UnstableFeatures,
+                     UnconditionalRecursion,
         );
 
         add_builtin_with_new!(sess,
diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs
new file mode 100644
index 00000000000..0c3d1c6adea
--- /dev/null
+++ b/src/test/compile-fail/lint-unconditional-recursion.rs
@@ -0,0 +1,66 @@
+// Copyright 2014 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.
+
+#![deny(unconditional_recursion)]
+#![allow(dead_code)]
+fn foo() { //~ ERROR function cannot return without recurring
+    foo(); //~ NOTE recursive call site
+}
+
+fn bar() {
+    if true {
+        bar()
+    }
+}
+
+fn baz() { //~ ERROR function cannot return without recurring
+    if true {
+        baz() //~ NOTE recursive call site
+    } else {
+        baz() //~ NOTE recursive call site
+    }
+}
+
+fn qux() {
+    loop {}
+}
+
+fn quz() -> bool { //~ ERROR function cannot return without recurring
+    if true {
+        while quz() {} //~ NOTE recursive call site
+        true
+    } else {
+        loop { quz(); } //~ NOTE recursive call site
+    }
+}
+
+trait Foo {
+    fn bar(&self) { //~ ERROR function cannot return without recurring
+        self.bar() //~ NOTE recursive call site
+    }
+}
+
+impl Foo for Box<Foo+'static> {
+    fn bar(&self) { //~ ERROR function cannot return without recurring
+        loop {
+            self.bar() //~ NOTE recursive call site
+        }
+    }
+
+}
+
+struct Baz;
+impl Baz {
+    fn qux(&self) { //~ ERROR function cannot return without recurring
+        self.qux(); //~ NOTE recursive call site
+    }
+}
+
+fn main() {}

From 0684c8ebf92933e6382d1ac13a77999296411dac Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Sun, 25 Jan 2015 00:05:42 +1100
Subject: [PATCH 25/33] Fix infinite recursion in the compiler.

This was detected by the unconditional_recursion lint.
---
 src/librustc/util/ppaux.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index c5aced4eb86..266048fce51 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -544,7 +544,7 @@ impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for Option<T> {
 
 impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for P<T> {
     fn repr(&self, tcx: &ctxt<'tcx>) -> String {
-        (*self).repr(tcx)
+        (**self).repr(tcx)
     }
 }
 

From 2e888d0341e81de1744b257c25b012c2c148f0ba Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Tue, 13 Jan 2015 14:24:37 +1100
Subject: [PATCH 26/33] Add the span of the operator itself to ast::BinOp.

---
 src/librustc/lint/builtin.rs              | 14 +++++++-------
 src/librustc/middle/cfg/construct.rs      |  2 +-
 src/librustc/middle/const_eval.rs         | 12 ++++++------
 src/librustc/middle/expr_use_visitor.rs   |  2 +-
 src/librustc/middle/liveness.rs           |  4 ++--
 src/librustc/middle/region.rs             |  6 +++---
 src/librustc/middle/ty.rs                 |  2 +-
 src/librustc_back/svh.rs                  |  8 ++++----
 src/librustc_trans/trans/base.rs          | 10 +++++-----
 src/librustc_trans/trans/consts.rs        |  2 +-
 src/librustc_trans/trans/expr.rs          |  8 ++++----
 src/librustc_typeck/check/mod.rs          | 18 +++++++++---------
 src/librustc_typeck/check/regionck.rs     |  2 +-
 src/libsyntax/ast.rs                      |  6 ++++--
 src/libsyntax/ast_util.rs                 | 12 ++++++------
 src/libsyntax/ext/build.rs                |  6 +++---
 src/libsyntax/ext/deriving/generic/mod.rs |  2 +-
 src/libsyntax/parse/parser.rs             | 12 +++++++-----
 src/libsyntax/parse/token.rs              |  2 +-
 src/libsyntax/print/pprust.rs             |  4 ++--
 20 files changed, 69 insertions(+), 65 deletions(-)

diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index fef1017b782..9a7b7e0eb94 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -44,7 +44,7 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 use syntax::{abi, ast, ast_map};
 use syntax::ast_util::is_shift_binop;
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax::codemap::{self, Span, DUMMY_SP};
 use syntax::parse::token;
 use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
 use syntax::ast_util;
@@ -185,7 +185,7 @@ impl LintPass for TypeLimits {
                                  "comparison is useless due to type limits");
                 }
 
-                if is_shift_binop(binop) {
+                if is_shift_binop(binop.node) {
                     let opt_ty_bits = match ty::expr_ty(cx.tcx, &**l).sty {
                         ty::ty_int(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
                         ty::ty_uint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
@@ -272,7 +272,7 @@ impl LintPass for TypeLimits {
 
         fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
                                 min: T, max: T) -> bool {
-            match binop {
+            match binop.node {
                 ast::BiLt => v >  min && v <= max,
                 ast::BiLe => v >= min && v <  max,
                 ast::BiGt => v >= min && v <  max,
@@ -283,13 +283,13 @@ impl LintPass for TypeLimits {
         }
 
         fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
-            match binop {
+            codemap::respan(binop.span, match binop.node {
                 ast::BiLt => ast::BiGt,
                 ast::BiLe => ast::BiGe,
                 ast::BiGt => ast::BiLt,
                 ast::BiGe => ast::BiLe,
-                _ => binop
-            }
+                _ => return binop
+            })
         }
 
         // for int & uint, be conservative with the warnings, so that the
@@ -382,7 +382,7 @@ impl LintPass for TypeLimits {
         }
 
         fn is_comparison(binop: ast::BinOp) -> bool {
-            match binop {
+            match binop.node {
                 ast::BiEq | ast::BiLt | ast::BiLe |
                 ast::BiNe | ast::BiGe | ast::BiGt => true,
                 _ => false
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index 1a2162b3076..6162f61fde1 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -372,7 +372,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 expr_exit
             }
 
-            ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
+            ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => {
                 //
                 //     [pred]
                 //       |
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 05dd47f5a36..c2533c1a9c6 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -400,7 +400,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
         match (eval_const_expr_partial(tcx, &**a),
                eval_const_expr_partial(tcx, &**b)) {
           (Ok(const_float(a)), Ok(const_float(b))) => {
-            match op {
+            match op.node {
               ast::BiAdd => Ok(const_float(a + b)),
               ast::BiSub => Ok(const_float(a - b)),
               ast::BiMul => Ok(const_float(a * b)),
@@ -416,7 +416,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
             }
           }
           (Ok(const_int(a)), Ok(const_int(b))) => {
-            match op {
+            match op.node {
               ast::BiAdd => Ok(const_int(a + b)),
               ast::BiSub => Ok(const_int(a - b)),
               ast::BiMul => Ok(const_int(a * b)),
@@ -443,7 +443,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
             }
           }
           (Ok(const_uint(a)), Ok(const_uint(b))) => {
-            match op {
+            match op.node {
               ast::BiAdd => Ok(const_uint(a + b)),
               ast::BiSub => Ok(const_uint(a - b)),
               ast::BiMul => Ok(const_uint(a * b)),
@@ -471,21 +471,21 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
           }
           // shifts can have any integral type as their rhs
           (Ok(const_int(a)), Ok(const_uint(b))) => {
-            match op {
+            match op.node {
               ast::BiShl => Ok(const_int(a << b as uint)),
               ast::BiShr => Ok(const_int(a >> b as uint)),
               _ => Err("can't do this op on an int and uint".to_string())
             }
           }
           (Ok(const_uint(a)), Ok(const_int(b))) => {
-            match op {
+            match op.node {
               ast::BiShl => Ok(const_uint(a << b as uint)),
               ast::BiShr => Ok(const_uint(a >> b as uint)),
               _ => Err("can't do this op on a uint and int".to_string())
             }
           }
           (Ok(const_bool(a)), Ok(const_bool(b))) => {
-            Ok(const_bool(match op {
+            Ok(const_bool(match op.node {
               ast::BiAnd => a && b,
               ast::BiOr => a || b,
               ast::BiBitXor => a ^ b,
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index a5f2dc398e9..d9ab86deb3b 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -568,7 +568,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             }
 
             ast::ExprBinary(op, ref lhs, ref rhs) => {
-                let pass_args = if ast_util::is_by_value_binop(op) {
+                let pass_args = if ast_util::is_by_value_binop(op.node) {
                     PassArgs::ByValue
                 } else {
                     PassArgs::ByRef
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 43989d0aadc..c08af95f139 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -504,7 +504,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
         visit::walk_expr(ir, expr);
       }
-      ast::ExprBinary(op, _, _) if ast_util::lazy_binop(op) => {
+      ast::ExprBinary(op, _, _) if ast_util::lazy_binop(op.node) => {
         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
         visit::walk_expr(ir, expr);
       }
@@ -1177,7 +1177,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             self.propagate_through_exprs(&exprs[], succ)
           }
 
-          ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
+          ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => {
             let r_succ = self.propagate_through_expr(&**r, succ);
 
             let ln = self.live_node(expr.id, expr.span);
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index b4b2e1b63e8..67c0e52d664 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -22,7 +22,7 @@ use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 use util::common::can_reach;
 
 use std::cell::RefCell;
-use syntax::codemap::Span;
+use syntax::codemap::{self, Span};
 use syntax::{ast, visit};
 use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
 use syntax::ast_util::{stmt_id};
@@ -496,8 +496,8 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
             // scopes, meaning that temporaries cannot outlive them.
             // This ensures fixed size stacks.
 
-            ast::ExprBinary(ast::BiAnd, _, ref r) |
-            ast::ExprBinary(ast::BiOr, _, ref r) => {
+            ast::ExprBinary(codemap::Spanned { node: ast::BiAnd, .. }, _, ref r) |
+            ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => {
                 // For shortcircuiting operators, mark the RHS as a terminating
                 // scope since it only executes conditionally.
                 terminating(r.id);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 83bbdf14e4a..bae41b78c08 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -5716,7 +5716,7 @@ pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool
     static opcat_mod: int = 8;
 
     fn opcat(op: ast::BinOp) -> int {
-        match op {
+        match op.node {
           ast::BiAdd => opcat_add,
           ast::BiSub => opcat_sub,
           ast::BiMul => opcat_mult,
diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs
index ebeaf3e6e22..65a7fbf60a5 100644
--- a/src/librustc_back/svh.rs
+++ b/src/librustc_back/svh.rs
@@ -231,7 +231,7 @@ mod svh_visitor {
         SawExprCall,
         SawExprMethodCall,
         SawExprTup,
-        SawExprBinary(ast::BinOp),
+        SawExprBinary(ast::BinOp_),
         SawExprUnary(ast::UnOp),
         SawExprLit(ast::Lit_),
         SawExprCast,
@@ -241,7 +241,7 @@ mod svh_visitor {
         SawExprClosure,
         SawExprBlock,
         SawExprAssign,
-        SawExprAssignOp(ast::BinOp),
+        SawExprAssignOp(ast::BinOp_),
         SawExprIndex,
         SawExprRange,
         SawExprPath,
@@ -262,7 +262,7 @@ mod svh_visitor {
             ExprCall(..)             => SawExprCall,
             ExprMethodCall(..)       => SawExprMethodCall,
             ExprTup(..)              => SawExprTup,
-            ExprBinary(op, _, _)     => SawExprBinary(op),
+            ExprBinary(op, _, _)     => SawExprBinary(op.node),
             ExprUnary(op, _)         => SawExprUnary(op),
             ExprLit(ref lit)         => SawExprLit(lit.node.clone()),
             ExprCast(..)             => SawExprCast,
@@ -273,7 +273,7 @@ mod svh_visitor {
             ExprClosure(..)          => SawExprClosure,
             ExprBlock(..)            => SawExprBlock,
             ExprAssign(..)           => SawExprAssign,
-            ExprAssignOp(op, _, _)   => SawExprAssignOp(op),
+            ExprAssignOp(op, _, _)   => SawExprAssignOp(op.node),
             ExprField(_, id)         => SawExprField(content(id.node)),
             ExprTupField(_, id)      => SawExprTupField(id.node),
             ExprIndex(..)            => SawExprIndex,
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 572dfd165ee..4c1fdc6140e 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -540,7 +540,7 @@ pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                         lhs: ValueRef,
                                         rhs: ValueRef,
                                         t: Ty<'tcx>,
-                                        op: ast::BinOp)
+                                        op: ast::BinOp_)
                                         -> Result<'blk, 'tcx> {
     let f = |&: a| Result::new(cx, compare_scalar_values(cx, lhs, rhs, a, op));
 
@@ -561,7 +561,7 @@ pub fn compare_scalar_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                          lhs: ValueRef,
                                          rhs: ValueRef,
                                          nt: scalar_type,
-                                         op: ast::BinOp)
+                                         op: ast::BinOp_)
                                          -> ValueRef {
     let _icx = push_ctxt("compare_scalar_values");
     fn die(cx: Block) -> ! {
@@ -635,7 +635,7 @@ pub fn compare_simd_types<'blk, 'tcx>(
                            not supported for floating point SIMD types")
         },
         ty::ty_uint(_) | ty::ty_int(_) => {
-            let cmp = match op {
+            let cmp = match op.node {
                 ast::BiEq => llvm::IntEQ,
                 ast::BiNe => llvm::IntNE,
                 ast::BiLt => llvm::IntSLT,
@@ -823,7 +823,7 @@ pub fn cast_shift_rhs<F, G>(op: ast::BinOp,
     G: FnOnce(ValueRef, Type) -> ValueRef,
 {
     // Shifts may have any size int on the rhs
-    if ast_util::is_shift_binop(op) {
+    if ast_util::is_shift_binop(op.node) {
         let mut rhs_llty = val_ty(rhs);
         let mut lhs_llty = val_ty(lhs);
         if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() }
@@ -852,7 +852,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(
                                 rhs: ValueRef,
                                 rhs_t: Ty<'tcx>)
                                 -> Block<'blk, 'tcx> {
-    let (zero_text, overflow_text) = if divrem == ast::BiDiv {
+    let (zero_text, overflow_text) = if divrem.node == ast::BiDiv {
         ("attempted to divide by zero",
          "attempted to divide with overflow")
     } else {
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 29cf9f72ef8..ba3af53a916 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -310,7 +310,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef {
             let ty = ty::expr_ty(cx.tcx(), &**e1);
             let is_float = ty::type_is_fp(ty);
             let signed = ty::type_is_signed(ty);
-            return match b {
+            return match b.node {
               ast::BiAdd   => {
                 if is_float { llvm::LLVMConstFAdd(te1, te2) }
                 else        { llvm::LLVMConstAdd(te1, te2) }
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 4ebaf91d111..f0b491bdea8 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -1132,7 +1132,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let rhs_datum = unpack_datum!(bcx, trans(bcx, &**rhs));
             trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs,
                                 vec![(rhs_datum, rhs.id)], Some(dest),
-                                !ast_util::is_by_value_binop(op)).bcx
+                                !ast_util::is_by_value_binop(op.node)).bcx
         }
         ast::ExprUnary(op, ref subexpr) => {
             // if not overloaded, would be RvalueDatumExpr
@@ -1676,7 +1676,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let binop_debug_loc = binop_expr.debug_loc();
 
     let mut bcx = bcx;
-    let val = match op {
+    let val = match op.node {
       ast::BiAdd => {
         if is_float {
             FAdd(bcx, lhs, rhs, binop_debug_loc)
@@ -1739,7 +1739,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
       }
       ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => {
         if ty::type_is_scalar(rhs_t) {
-            unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op))
+            unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op.node))
         } else if is_simd {
             base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op)
         } else {
@@ -1811,7 +1811,7 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     // if overloaded, would be RvalueDpsExpr
     assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id)));
 
-    match op {
+    match op.node {
         ast::BiAnd => {
             trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs)
         }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 5f8ae09b5bd..a9f81d3a266 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2859,7 +2859,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         let lhs_t = structurally_resolved_type(fcx, lhs.span,
                                                fcx.expr_ty(&*lhs));
 
-        if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
+        if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) {
             // Shift is a special case: rhs must be uint, no matter what lhs is
             check_expr(fcx, &**rhs);
             let rhs_ty = fcx.expr_ty(&**rhs);
@@ -2887,7 +2887,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             demand::suptype(fcx, expr.span, tvar, lhs_t);
             check_expr_has_type(fcx, &**rhs, tvar);
 
-            let result_t = match op {
+            let result_t = match op.node {
                 ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe |
                 ast::BiGt => {
                     if ty::type_is_simd(tcx, lhs_t) {
@@ -2898,7 +2898,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                              operation `{}` not \
                                              supported for floating \
                                              point SIMD vector `{}`",
-                                            ast_util::binop_to_string(op),
+                                            ast_util::binop_to_string(op.node),
                                             actual)
                                 },
                                 lhs_t,
@@ -2919,7 +2919,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             return;
         }
 
-        if op == ast::BiOr || op == ast::BiAnd {
+        if op.node == ast::BiOr || op.node == ast::BiAnd {
             // This is an error; one of the operands must have the wrong
             // type
             fcx.write_error(expr.id);
@@ -2928,7 +2928,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                    |actual| {
                     format!("binary operation `{}` cannot be applied \
                              to type `{}`",
-                            ast_util::binop_to_string(op),
+                            ast_util::binop_to_string(op.node),
                             actual)
                 },
                 lhs_t,
@@ -2945,7 +2945,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                  operation `{}=` \
                                                  cannot be applied to \
                                                  type `{}`",
-                                                ast_util::binop_to_string(op),
+                                                ast_util::binop_to_string(op.node),
                                                 actual)
                                    },
                                    lhs_t,
@@ -2968,7 +2968,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                   rhs: &P<ast::Expr>) -> Ty<'tcx> {
         let tcx = fcx.ccx.tcx;
         let lang = &tcx.lang_items;
-        let (name, trait_did) = match op {
+        let (name, trait_did) = match op.node {
             ast::BiAdd => ("add", lang.add_trait()),
             ast::BiSub => ("sub", lang.sub_trait()),
             ast::BiMul => ("mul", lang.mul_trait()),
@@ -2994,10 +2994,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                          trait_did, lhs_expr, Some(rhs), || {
             fcx.type_error_message(ex.span, |actual| {
                 format!("binary operation `{}` cannot be applied to type `{}`",
-                        ast_util::binop_to_string(op),
+                        ast_util::binop_to_string(op.node),
                         actual)
             }, lhs_resolved_t, None)
-        }, if ast_util::is_by_value_binop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
+        }, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes })
     }
 
     fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 56b700663d4..af5250805b7 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -564,7 +564,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
         },
 
         ast::ExprBinary(op, ref lhs, ref rhs) if has_method_map => {
-            let implicitly_ref_args = !ast_util::is_by_value_binop(op);
+            let implicitly_ref_args = !ast_util::is_by_value_binop(op.node);
 
             // As `expr_method_call`, but the call is via an
             // overloaded op.  Note that we (sadly) currently use an
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 7111fe3af1f..25a30b5e8e2 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -13,7 +13,7 @@
 pub use self::AsmDialect::*;
 pub use self::AttrStyle::*;
 pub use self::BindingMode::*;
-pub use self::BinOp::*;
+pub use self::BinOp_::*;
 pub use self::BlockCheckMode::*;
 pub use self::CaptureClause::*;
 pub use self::Decl_::*;
@@ -582,7 +582,7 @@ pub enum Mutability {
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
-pub enum BinOp {
+pub enum BinOp_ {
     BiAdd,
     BiSub,
     BiMul,
@@ -603,6 +603,8 @@ pub enum BinOp {
     BiGt,
 }
 
+pub type BinOp = Spanned<BinOp_>;
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
 pub enum UnOp {
     UnUniq,
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index cf0aac5bf4a..5aeea47ac60 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -46,7 +46,7 @@ pub fn stmt_id(s: &Stmt) -> NodeId {
     }
 }
 
-pub fn binop_to_string(op: BinOp) -> &'static str {
+pub fn binop_to_string(op: BinOp_) -> &'static str {
     match op {
         BiAdd => "+",
         BiSub => "-",
@@ -69,7 +69,7 @@ pub fn binop_to_string(op: BinOp) -> &'static str {
     }
 }
 
-pub fn lazy_binop(b: BinOp) -> bool {
+pub fn lazy_binop(b: BinOp_) -> bool {
     match b {
       BiAnd => true,
       BiOr => true,
@@ -77,7 +77,7 @@ pub fn lazy_binop(b: BinOp) -> bool {
     }
 }
 
-pub fn is_shift_binop(b: BinOp) -> bool {
+pub fn is_shift_binop(b: BinOp_) -> bool {
     match b {
       BiShl => true,
       BiShr => true,
@@ -85,7 +85,7 @@ pub fn is_shift_binop(b: BinOp) -> bool {
     }
 }
 
-pub fn is_comparison_binop(b: BinOp) -> bool {
+pub fn is_comparison_binop(b: BinOp_) -> bool {
     match b {
         BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true,
         _ => false
@@ -93,7 +93,7 @@ pub fn is_comparison_binop(b: BinOp) -> bool {
 }
 
 /// Returns `true` if the binary operator takes its arguments by value
-pub fn is_by_value_binop(b: BinOp) -> bool {
+pub fn is_by_value_binop(b: BinOp_) -> bool {
     match b {
         BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => {
             true
@@ -319,7 +319,7 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
 }
 
 /// Maps a binary operator to its precedence
-pub fn operator_prec(op: ast::BinOp) -> usize {
+pub fn operator_prec(op: ast::BinOp_) -> usize {
   match op {
       // 'as' sits here with 12
       BiMul | BiDiv | BiRem     => 11us,
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 92619cf42e4..2b3a7212683 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -106,7 +106,7 @@ pub trait AstBuilder {
     fn expr_ident(&self, span: Span, id: ast::Ident) -> P<ast::Expr>;
 
     fn expr_self(&self, span: Span) -> P<ast::Expr>;
-    fn expr_binary(&self, sp: Span, op: ast::BinOp,
+    fn expr_binary(&self, sp: Span, op: ast::BinOp_,
                    lhs: P<ast::Expr>, rhs: P<ast::Expr>) -> P<ast::Expr>;
     fn expr_deref(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr>;
     fn expr_unary(&self, sp: Span, op: ast::UnOp, e: P<ast::Expr>) -> P<ast::Expr>;
@@ -561,9 +561,9 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.expr_ident(span, special_idents::self_)
     }
 
-    fn expr_binary(&self, sp: Span, op: ast::BinOp,
+    fn expr_binary(&self, sp: Span, op: ast::BinOp_,
                    lhs: P<ast::Expr>, rhs: P<ast::Expr>) -> P<ast::Expr> {
-        self.expr(sp, ast::ExprBinary(op, lhs, rhs))
+        self.expr(sp, ast::ExprBinary(Spanned { node: op, span: sp }, lhs, rhs))
     }
 
     fn expr_deref(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr> {
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 272b0464010..f8812f4d28d 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -1449,7 +1449,7 @@ pub fn cs_same_method_fold<F>(use_foldl: bool,
 /// Use a given binop to combine the result of calling the derived method
 /// on all the fields.
 #[inline]
-pub fn cs_binop(binop: ast::BinOp, base: P<Expr>,
+pub fn cs_binop(binop: ast::BinOp_, base: P<Expr>,
                 enum_nonmatch_f: EnumNonMatchCollapsedFunc,
                 cx: &mut ExtCtxt, trait_span: Span,
                 substructure: &Substructure) -> P<Expr> {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index e59dbe52b76..15254988ce0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2840,6 +2840,7 @@ impl<'a> Parser<'a> {
 
         self.expected_tokens.push(TokenType::Operator);
 
+        let cur_op_span = self.span;
         let cur_opt = self.token.to_binop();
         match cur_opt {
             Some(cur_op) => {
@@ -2853,7 +2854,7 @@ impl<'a> Parser<'a> {
                     let rhs = self.parse_more_binops(expr, cur_prec + 1);
                     let lhs_span = lhs.span;
                     let rhs_span = rhs.span;
-                    let binary = self.mk_binary(cur_op, lhs, rhs);
+                    let binary = self.mk_binary(codemap::respan(cur_op_span, cur_op), lhs, rhs);
                     let bin = self.mk_expr(lhs_span.lo, rhs_span.hi, binary);
                     self.parse_more_binops(bin, min_prec)
                 } else {
@@ -2877,14 +2878,14 @@ impl<'a> Parser<'a> {
     /// Produce an error if comparison operators are chained (RFC #558).
     /// We only need to check lhs, not rhs, because all comparison ops
     /// have same precedence and are left-associative
-    fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp) {
+    fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp_) {
         debug_assert!(ast_util::is_comparison_binop(outer_op));
         match lhs.node {
-            ExprBinary(op, _, _) if ast_util::is_comparison_binop(op) => {
+            ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
                 let op_span = self.span;
                 self.span_err(op_span,
                     "Chained comparison operators require parentheses");
-                if op == BiLt && outer_op == BiGt {
+                if op.node == BiLt && outer_op == BiGt {
                     self.span_help(op_span,
                         "use ::< instead of < if you meant to specify type arguments");
                 }
@@ -2919,6 +2920,7 @@ impl<'a> Parser<'a> {
 
     pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> P<Expr> {
         let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL;
+        let op_span = self.span;
         match self.token {
           token::Eq => {
               self.bump();
@@ -2942,7 +2944,7 @@ impl<'a> Parser<'a> {
               };
               let rhs_span = rhs.span;
               let span = lhs.span;
-              let assign_op = self.mk_assign_op(aop, lhs, rhs);
+              let assign_op = self.mk_assign_op(codemap::respan(op_span, aop), lhs, rhs);
               self.mk_expr(span.lo, rhs_span.hi, assign_op)
           }
           // A range expression, either `expr..expr` or `expr..`.
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index a129fd19d94..ac694afac6b 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -249,7 +249,7 @@ impl Token {
     }
 
     /// Maps a token to its corresponding binary operator.
-    pub fn to_binop(&self) -> Option<ast::BinOp> {
+    pub fn to_binop(&self) -> Option<ast::BinOp_> {
         match *self {
             BinOp(Star)     => Some(ast::BiMul),
             BinOp(Slash)    => Some(ast::BiDiv),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 42f156d6a11..1026f4cb323 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1618,7 +1618,7 @@ impl<'a> State<'a> {
                          rhs: &ast::Expr) -> IoResult<()> {
         try!(self.print_expr(lhs));
         try!(space(&mut self.s));
-        try!(self.word_space(ast_util::binop_to_string(op)));
+        try!(self.word_space(ast_util::binop_to_string(op.node)));
         self.print_expr(rhs)
     }
 
@@ -1786,7 +1786,7 @@ impl<'a> State<'a> {
             ast::ExprAssignOp(op, ref lhs, ref rhs) => {
                 try!(self.print_expr(&**lhs));
                 try!(space(&mut self.s));
-                try!(word(&mut self.s, ast_util::binop_to_string(op)));
+                try!(word(&mut self.s, ast_util::binop_to_string(op.node)));
                 try!(self.word_space("="));
                 try!(self.print_expr(&**rhs));
             }

From ec790d6fcc426e13431cbb7510c62864515a3ef8 Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Tue, 13 Jan 2015 15:18:55 +1100
Subject: [PATCH 27/33] Tweak chained comparison errors.

Lower case and give a more precise span: from operator to operator, not
just the last one.
---
 src/libsyntax/parse/parser.rs                             | 7 ++++---
 .../compile-fail/require-parens-for-chained-comparison.rs | 8 ++++----
 src/test/compile-fail/unsized2.rs                         | 4 ++--
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 15254988ce0..759e5e8229a 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2882,12 +2882,13 @@ impl<'a> Parser<'a> {
         debug_assert!(ast_util::is_comparison_binop(outer_op));
         match lhs.node {
             ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
-                let op_span = self.span;
+                // respan to include both operators
+                let op_span = mk_sp(op.span.lo, self.span.hi);
                 self.span_err(op_span,
-                    "Chained comparison operators require parentheses");
+                    "chained comparison operators require parentheses");
                 if op.node == BiLt && outer_op == BiGt {
                     self.span_help(op_span,
-                        "use ::< instead of < if you meant to specify type arguments");
+                        "use `::<...>` instead of `<...>` if you meant to specify type arguments");
                 }
             }
             _ => {}
diff --git a/src/test/compile-fail/require-parens-for-chained-comparison.rs b/src/test/compile-fail/require-parens-for-chained-comparison.rs
index f5d8c574814..f2705f58331 100644
--- a/src/test/compile-fail/require-parens-for-chained-comparison.rs
+++ b/src/test/compile-fail/require-parens-for-chained-comparison.rs
@@ -12,12 +12,12 @@ fn f<T>() {}
 
 fn main() {
     false == false == false;
-    //~^ ERROR: Chained comparison operators require parentheses
+    //~^ ERROR: chained comparison operators require parentheses
 
     false == 0 < 2;
-    //~^ ERROR: Chained comparison operators require parentheses
+    //~^ ERROR: chained comparison operators require parentheses
 
     f<X>();
-    //~^ ERROR: Chained comparison operators require parentheses
-    //~^^ HELP: use ::< instead of < if you meant to specify type arguments
+    //~^ ERROR: chained comparison operators require parentheses
+    //~^^ HELP: use `::<...>` instead of `<...>`
 }
diff --git a/src/test/compile-fail/unsized2.rs b/src/test/compile-fail/unsized2.rs
index a47d81e38cc..b2eb2064aeb 100644
--- a/src/test/compile-fail/unsized2.rs
+++ b/src/test/compile-fail/unsized2.rs
@@ -15,6 +15,6 @@ fn f<X>() {}
 pub fn main() {
     f<type>();
     //~^ ERROR expected identifier, found keyword `type`
-    //~^^ ERROR: Chained comparison operators require parentheses
-    //~^^^ HELP: use ::< instead of < if you meant to specify type arguments
+    //~^^ ERROR: chained comparison
+    //~^^^ HELP: use `::<
 }

From 612ded78e3275332e4b68391701a2866ca3689cc Mon Sep 17 00:00:00 2001
From: Michael Woerister <michaelwoerister@posteo>
Date: Thu, 8 Jan 2015 14:55:13 +0100
Subject: [PATCH 28/33] debuginfo: Extend option-like-enum test case to contain
 nested discriminants.

---
 src/test/debuginfo/option-like-enum.rs | 34 ++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/src/test/debuginfo/option-like-enum.rs b/src/test/debuginfo/option-like-enum.rs
index 71c235c878c..fdfbcda7421 100644
--- a/src/test/debuginfo/option-like-enum.rs
+++ b/src/test/debuginfo/option-like-enum.rs
@@ -36,6 +36,12 @@
 // gdb-command:print void_droid_gdb->internals
 // gdb-check:$6 = (isize *) 0x0
 
+// gdb-command:print nested_non_zero_yep
+// gdb-check:$7 = {RUST$ENCODED$ENUM$1$2$Nope = {10.5, {a = 10, b = 20, c = [...]}}}
+
+// gdb-command:print nested_non_zero_nope
+// gdb-check:$8 = {RUST$ENCODED$ENUM$1$2$Nope = {[...], {a = [...], b = [...], c = 0x0}}}
+
 // gdb-command:continue
 
 
@@ -67,6 +73,12 @@
 // lldb-command:print none_str
 // lldb-check:[...]$7 = None
 
+// lldb-command:print nested_non_zero_yep
+// lldb-check:[...]$8 = Yep(10.5, NestedNonZeroField { a: 10, b: 20, c: &[...] })
+
+// lldb-command:print nested_non_zero_nope
+// lldb-check:[...]$9 = Nope
+
 
 #![omit_gdb_pretty_printer_section]
 
@@ -102,6 +114,17 @@ struct NamedFieldsRepr<'a> {
     internals: &'a isize
 }
 
+struct NestedNonZeroField<'a> {
+    a: u16,
+    b: u32,
+    c: &'a char,
+}
+
+enum NestedNonZero<'a> {
+    Yep(f64, NestedNonZeroField<'a>),
+    Nope
+}
+
 fn main() {
 
     let some_str: Option<&'static str> = Some("abc");
@@ -124,6 +147,17 @@ fn main() {
     let void_droid = NamedFields::Void;
     let void_droid_gdb: &NamedFieldsRepr = unsafe { std::mem::transmute(&NamedFields::Void) };
 
+    let x = 'x';
+    let nested_non_zero_yep = NestedNonZero::Yep(
+        10.5,
+        NestedNonZeroField {
+            a: 10,
+            b: 20,
+            c: &x
+        });
+
+    let nested_non_zero_nope = NestedNonZero::Nope;
+
     zzz(); // #break
 }
 

From 75ad1161dd598ba0e61b9215a08216e744dd9f4c Mon Sep 17 00:00:00 2001
From: Michael Woerister <michaelwoerister@posteo>
Date: Thu, 8 Jan 2015 16:50:45 +0100
Subject: [PATCH 29/33] debuginfo: Improve DWARF representation of unsized vecs
 and strings.

---
 src/librustc_trans/trans/debuginfo.rs | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index 39413d63482..b366c7faefa 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -207,7 +207,7 @@ use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
 use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet};
 use util::ppaux;
 
-use libc::c_uint;
+use libc::{c_uint, c_longlong};
 use std::ffi::CString;
 use std::cell::{Cell, RefCell};
 use std::ptr;
@@ -2764,7 +2764,7 @@ fn create_struct_stub(cx: &CrateContext,
 fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                 unique_type_id: UniqueTypeId,
                                 element_type: Ty<'tcx>,
-                                len: uint,
+                                len: Option<u64>,
                                 span: Span)
                                 -> MetadataCreationResult {
     let element_type_metadata = type_metadata(cx, element_type, span);
@@ -2774,18 +2774,20 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let element_llvm_type = type_of::type_of(cx, element_type);
     let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type);
 
+    let (array_size_in_bytes, upper_bound) = match len {
+        Some(len) => (element_type_size * len, len as c_longlong),
+        None => (0, -1)
+    };
+
     let subrange = unsafe {
-        llvm::LLVMDIBuilderGetOrCreateSubrange(
-            DIB(cx),
-            0,
-            len as i64)
+        llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)
     };
 
     let subscripts = create_DIArray(DIB(cx), &[subrange]);
     let metadata = unsafe {
         llvm::LLVMDIBuilderCreateArrayType(
             DIB(cx),
-            bytes_to_bits(element_type_size * (len as u64)),
+            bytes_to_bits(array_size_in_bytes),
             bytes_to_bits(element_type_align),
             element_type_metadata,
             subscripts)
@@ -2991,12 +2993,12 @@ fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::ty_enum(def_id, _) => {
             prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx)
         }
-        ty::ty_vec(typ, Some(len)) => {
-            fixed_vec_metadata(cx, unique_type_id, typ, len, usage_site_span)
+        ty::ty_vec(typ, len) => {
+            fixed_vec_metadata(cx, unique_type_id, typ, len.map(|x| x as u64), usage_site_span)
+        }
+        ty::ty_str => {
+            fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, None, usage_site_span)
         }
-        // FIXME Can we do better than this for unsized vec/str fields?
-        ty::ty_vec(typ, None) => fixed_vec_metadata(cx, unique_type_id, typ, 0, usage_site_span),
-        ty::ty_str => fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, 0, usage_site_span),
         ty::ty_trait(..) => {
             MetadataCreationResult::new(
                         trait_pointer_metadata(cx, t, None, unique_type_id),

From 577c4643dd3f098697c17606e64d92477247d4b5 Mon Sep 17 00:00:00 2001
From: Flavio Percoco <flaper87@gmail.com>
Date: Sat, 24 Jan 2015 15:10:37 +0100
Subject: [PATCH 30/33] don't run pretty-rpass for tests using #![main]

---
 .../test-fn-signature-verification-for-explicit-return-type.rs   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs
index 65a6ed2b332..7c99c968e35 100644
--- a/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs
+++ b/src/test/run-pass/test-fn-signature-verification-for-explicit-return-type.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 // compile-flags: --test
+// no-pretty-expanded
 extern crate test;
 
 #[bench]

From c5369ebc7f4791c4e291951751b8964052c7a523 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Wed, 21 Jan 2015 15:55:31 -0800
Subject: [PATCH 31/33] Add ffi::OsString and OsStr

Per [RFC 517](https://github.com/rust-lang/rfcs/pull/575/), this commit
introduces platform-native strings. The API is essentially as described
in the RFC.

The WTF-8 implementation is adapted from @SimonSapin's
[implementation](https://github.com/SimonSapin/rust-wtf8). To make this
work, some encodign and decoding functionality in `libcore` is now
exported in a "raw" fashion reusable for WTF-8. These exports are *not*
reexported in `std`, nor are they stable.
---
 src/libcore/char.rs              |   96 ++-
 src/libcore/str/mod.rs           |  116 +--
 src/libstd/ffi/mod.rs            |    5 +
 src/libstd/ffi/os_str.rs         |  259 +++++++
 src/libstd/sys/common/mod.rs     |   15 +-
 src/libstd/sys/common/wtf8.rs    | 1212 ++++++++++++++++++++++++++++++
 src/libstd/sys/unix/ext.rs       |   35 +-
 src/libstd/sys/unix/mod.rs       |    1 +
 src/libstd/sys/unix/os_str.rs    |   86 +++
 src/libstd/sys/windows/ext.rs    |   34 +-
 src/libstd/sys/windows/mod.rs    |    1 +
 src/libstd/sys/windows/os_str.rs |   82 ++
 12 files changed, 1850 insertions(+), 92 deletions(-)
 create mode 100644 src/libstd/ffi/os_str.rs
 create mode 100644 src/libstd/sys/common/wtf8.rs
 create mode 100644 src/libstd/sys/unix/os_str.rs
 create mode 100644 src/libstd/sys/windows/os_str.rs

diff --git a/src/libcore/char.rs b/src/libcore/char.rs
index caac894c0da..0e6b634bd11 100644
--- a/src/libcore/char.rs
+++ b/src/libcore/char.rs
@@ -258,49 +258,69 @@ impl CharExt for char {
     #[inline]
     #[unstable = "pending decision about Iterator/Writer/Reader"]
     fn encode_utf8(self, dst: &mut [u8]) -> Option<uint> {
-        // Marked #[inline] to allow llvm optimizing it away
-        let code = self as u32;
-        if code < MAX_ONE_B && dst.len() >= 1 {
-            dst[0] = code as u8;
-            Some(1)
-        } else if code < MAX_TWO_B && dst.len() >= 2 {
-            dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B;
-            dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT;
-            Some(2)
-        } else if code < MAX_THREE_B && dst.len() >= 3  {
-            dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B;
-            dst[1] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
-            dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT;
-            Some(3)
-        } else if dst.len() >= 4 {
-            dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B;
-            dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT;
-            dst[2] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
-            dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT;
-            Some(4)
-        } else {
-            None
-        }
+        encode_utf8_raw(self as u32, dst)
     }
 
     #[inline]
     #[unstable = "pending decision about Iterator/Writer/Reader"]
     fn encode_utf16(self, dst: &mut [u16]) -> Option<uint> {
-        // Marked #[inline] to allow llvm optimizing it away
-        let mut ch = self as u32;
-        if (ch & 0xFFFF_u32) == ch  && dst.len() >= 1 {
-            // The BMP falls through (assuming non-surrogate, as it should)
-            dst[0] = ch as u16;
-            Some(1)
-        } else if dst.len() >= 2 {
-            // Supplementary planes break into surrogates.
-            ch -= 0x1_0000_u32;
-            dst[0] = 0xD800_u16 | ((ch >> 10) as u16);
-            dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16);
-            Some(2)
-        } else {
-            None
-        }
+        encode_utf16_raw(self as u32, dst)
+    }
+}
+
+/// Encodes a raw u32 value as UTF-8 into the provided byte buffer,
+/// and then returns the number of bytes written.
+///
+/// If the buffer is not large enough, nothing will be written into it
+/// and a `None` will be returned.
+#[inline]
+#[unstable]
+pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> Option<uint> {
+    // Marked #[inline] to allow llvm optimizing it away
+    if code < MAX_ONE_B && dst.len() >= 1 {
+        dst[0] = code as u8;
+        Some(1)
+    } else if code < MAX_TWO_B && dst.len() >= 2 {
+        dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B;
+        dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT;
+        Some(2)
+    } else if code < MAX_THREE_B && dst.len() >= 3  {
+        dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B;
+        dst[1] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
+        dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT;
+        Some(3)
+    } else if dst.len() >= 4 {
+        dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B;
+        dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT;
+        dst[2] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
+        dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT;
+        Some(4)
+    } else {
+        None
+    }
+}
+
+/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer,
+/// and then returns the number of `u16`s written.
+///
+/// If the buffer is not large enough, nothing will be written into it
+/// and a `None` will be returned.
+#[inline]
+#[unstable]
+pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option<uint> {
+    // Marked #[inline] to allow llvm optimizing it away
+    if (ch & 0xFFFF_u32) == ch  && dst.len() >= 1 {
+        // The BMP falls through (assuming non-surrogate, as it should)
+        dst[0] = ch as u16;
+        Some(1)
+    } else if dst.len() >= 2 {
+        // Supplementary planes break into surrogates.
+        ch -= 0x1_0000_u32;
+        dst[0] = 0xD800_u16 | ((ch >> 10) as u16);
+        dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16);
+        Some(2)
+    } else {
+        None
     }
 }
 
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index bdac686cb66..1e01da4e41d 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -305,43 +305,52 @@ fn unwrap_or_0(opt: Option<&u8>) -> u8 {
     }
 }
 
+/// Reads the next code point out of a byte iterator (assuming a
+/// UTF-8-like encoding).
+#[unstable]
+pub fn next_code_point(bytes: &mut slice::Iter<u8>) -> Option<u32> {
+    // Decode UTF-8
+    let x = match bytes.next() {
+        None => return None,
+        Some(&next_byte) if next_byte < 128 => return Some(next_byte as u32),
+        Some(&next_byte) => next_byte,
+    };
+
+    // Multibyte case follows
+    // Decode from a byte combination out of: [[[x y] z] w]
+    // NOTE: Performance is sensitive to the exact formulation here
+    let init = utf8_first_byte!(x, 2);
+    let y = unwrap_or_0(bytes.next());
+    let mut ch = utf8_acc_cont_byte!(init, y);
+    if x >= 0xE0 {
+        // [[x y z] w] case
+        // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid
+        let z = unwrap_or_0(bytes.next());
+        let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z);
+        ch = init << 12 | y_z;
+        if x >= 0xF0 {
+            // [x y z w] case
+            // use only the lower 3 bits of `init`
+            let w = unwrap_or_0(bytes.next());
+            ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w);
+        }
+    }
+
+    Some(ch)
+}
+
 #[stable]
 impl<'a> Iterator for Chars<'a> {
     type Item = char;
 
     #[inline]
     fn next(&mut self) -> Option<char> {
-        // Decode UTF-8, using the valid UTF-8 invariant
-        let x = match self.iter.next() {
-            None => return None,
-            Some(&next_byte) if next_byte < 128 => return Some(next_byte as char),
-            Some(&next_byte) => next_byte,
-        };
-
-        // Multibyte case follows
-        // Decode from a byte combination out of: [[[x y] z] w]
-        // NOTE: Performance is sensitive to the exact formulation here
-        let init = utf8_first_byte!(x, 2);
-        let y = unwrap_or_0(self.iter.next());
-        let mut ch = utf8_acc_cont_byte!(init, y);
-        if x >= 0xE0 {
-            // [[x y z] w] case
-            // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid
-            let z = unwrap_or_0(self.iter.next());
-            let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z);
-            ch = init << 12 | y_z;
-            if x >= 0xF0 {
-                // [x y z w] case
-                // use only the lower 3 bits of `init`
-                let w = unwrap_or_0(self.iter.next());
-                ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w);
+        next_code_point(&mut self.iter).map(|ch| {
+            // str invariant says `ch` is a valid Unicode Scalar Value
+            unsafe {
+                mem::transmute(ch)
             }
-        }
-
-        // str invariant says `ch` is a valid Unicode Scalar Value
-        unsafe {
-            Some(mem::transmute(ch))
-        }
+        })
     }
 
     #[inline]
@@ -1517,25 +1526,8 @@ impl StrExt for str {
 
     #[inline]
     fn char_range_at(&self, i: uint) -> CharRange {
-        if self.as_bytes()[i] < 128u8 {
-            return CharRange {ch: self.as_bytes()[i] as char, next: i + 1 };
-        }
-
-        // Multibyte case is a fn to allow char_range_at to inline cleanly
-        fn multibyte_char_range_at(s: &str, i: uint) -> CharRange {
-            let mut val = s.as_bytes()[i] as u32;
-            let w = UTF8_CHAR_WIDTH[val as uint] as uint;
-            assert!((w != 0));
-
-            val = utf8_first_byte!(val, w);
-            val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 1]);
-            if w > 2 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 2]); }
-            if w > 3 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 3]); }
-
-            return CharRange {ch: unsafe { mem::transmute(val) }, next: i + w};
-        }
-
-        return multibyte_char_range_at(self, i);
+        let (c, n) = char_range_at_raw(self.as_bytes(), i);
+        CharRange { ch: unsafe { mem::transmute(c) }, next: n }
     }
 
     #[inline]
@@ -1653,6 +1645,32 @@ impl StrExt for str {
     fn parse<T: FromStr>(&self) -> Option<T> { FromStr::from_str(self) }
 }
 
+/// Pluck a code point out of a UTF-8-like byte slice and return the
+/// index of the next code point.
+#[inline]
+#[unstable]
+pub fn char_range_at_raw(bytes: &[u8], i: uint) -> (u32, usize) {
+    if bytes[i] < 128u8 {
+        return (bytes[i] as u32, i + 1);
+    }
+
+    // Multibyte case is a fn to allow char_range_at to inline cleanly
+    fn multibyte_char_range_at(bytes: &[u8], i: uint) -> (u32, usize) {
+        let mut val = bytes[i] as u32;
+        let w = UTF8_CHAR_WIDTH[val as uint] as uint;
+        assert!((w != 0));
+
+        val = utf8_first_byte!(val, w);
+        val = utf8_acc_cont_byte!(val, bytes[i + 1]);
+        if w > 2 { val = utf8_acc_cont_byte!(val, bytes[i + 2]); }
+        if w > 3 { val = utf8_acc_cont_byte!(val, bytes[i + 3]); }
+
+        return (val, i + w);
+    }
+
+    multibyte_char_range_at(bytes, i)
+}
+
 #[stable]
 impl<'a> Default for &'a str {
     #[stable]
diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs
index cc86f804e3e..95ad6178bab 100644
--- a/src/libstd/ffi/mod.rs
+++ b/src/libstd/ffi/mod.rs
@@ -17,4 +17,9 @@ pub use self::c_str::CString;
 pub use self::c_str::c_str_to_bytes;
 pub use self::c_str::c_str_to_bytes_with_nul;
 
+pub use self::os_str::OsString;
+pub use self::os_str::OsStr;
+pub use self::os_str::AsOsStr;
+
 mod c_str;
+mod os_str;
diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs
new file mode 100644
index 00000000000..9c5cf62f853
--- /dev/null
+++ b/src/libstd/ffi/os_str.rs
@@ -0,0 +1,259 @@
+// Copyright 2015 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.
+
+//! A type that can represent all platform-native strings, but is cheaply
+//! interconvertable with Rust strings.
+//!
+//! The need for this type arises from the fact that:
+//!
+//! * On Unix systems, strings are often arbitrary sequences of non-zero
+//!   bytes, in many cases interpreted as UTF-8.
+//!
+//! * On Windows, strings are often arbitrary sequences of non-zero 16-bit
+//!   values, interpreted as UTF-16 when it is valid to do so.
+//!
+//! * In Rust, strings are always valid UTF-8, but may contain zeros.
+//!
+//! The types in this module bridge this gap by simultaneously representing Rust
+//! and platform-native string values, and in particular allowing a Rust string
+//! to be converted into an "OS" string with no cost.
+//!
+//! **Note**: At the moment, these types are extremely bare-bones, usable only
+//! for conversion to/from various other string types. Eventually these types
+//! will offer a full-fledged string API.
+
+#![unstable = "recently added as part of path/io reform"]
+
+use core::prelude::*;
+
+use core::borrow::{BorrowFrom, ToOwned};
+use fmt::{self, Debug};
+use mem;
+use string::{String, CowString};
+use ops;
+use cmp;
+use hash::{Hash, Hasher, Writer};
+
+use sys::os_str::{Buf, Slice};
+use sys_common::{AsInner, IntoInner, FromInner};
+
+/// Owned, mutable OS strings.
+#[derive(Clone)]
+pub struct OsString {
+    inner: Buf
+}
+
+/// Slices into OS strings.
+pub struct OsStr {
+    inner: Slice
+}
+
+impl OsString {
+    /// Constructs an `OsString` at no cost by consuming a `String`.
+    pub fn from_string(s: String) -> OsString {
+        OsString { inner: Buf::from_string(s) }
+    }
+
+    /// Constructs an `OsString` by copying from a `&str` slice.
+    ///
+    /// Equivalent to: `OsString::from_string(String::from_str(s))`.
+    pub fn from_str(s: &str) -> OsString {
+        OsString { inner: Buf::from_str(s) }
+    }
+
+    /// Convert the `OsString` into a `String` if it contains valid Unicode data.
+    ///
+    /// On failure, ownership of the original `OsString` is returned.
+    pub fn into_string(self) -> Result<String, OsString> {
+        self.inner.into_string().map_err(|buf| OsString { inner: buf} )
+    }
+
+    /// Extend the string with the given `&OsStr` slice.
+    pub fn push_os_str(&mut self, s: &OsStr) {
+        self.inner.push_slice(&s.inner)
+    }
+}
+
+impl ops::Index<ops::FullRange> for OsString {
+    type Output = OsStr;
+
+    #[inline]
+    fn index(&self, _index: &ops::FullRange) -> &OsStr {
+        unsafe { mem::transmute(self.inner.as_slice()) }
+    }
+}
+
+impl ops::Deref for OsString {
+    type Target = OsStr;
+
+    #[inline]
+    fn deref(&self) -> &OsStr {
+        &self[]
+    }
+}
+
+impl Debug for OsString {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Debug::fmt(&**self, formatter)
+    }
+}
+
+impl OsStr {
+    /// Coerce directly from a `&str` slice to a `&OsStr` slice.
+    pub fn from_str(s: &str) -> &OsStr {
+        unsafe { mem::transmute(Slice::from_str(s)) }
+    }
+
+    /// Yield a `&str` slice if the `OsStr` is valid unicode.
+    ///
+    /// This conversion may entail doing a check for UTF-8 validity.
+    pub fn to_str(&self) -> Option<&str> {
+        self.inner.to_str()
+    }
+
+    /// Convert an `OsStr` to a `CowString`.
+    ///
+    /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
+    pub fn to_string_lossy(&self) -> CowString {
+        self.inner.to_string_lossy()
+    }
+
+    /// Copy the slice into an onwed `OsString`.
+    pub fn to_os_string(&self) -> OsString {
+        OsString { inner: self.inner.to_owned() }
+    }
+
+    /// Get the underlying byte representation.
+    ///
+    /// Note: it is *crucial* that this API is private, to avoid
+    /// revealing the internal, platform-specific encodings.
+    fn bytes(&self) -> &[u8] {
+        unsafe { mem::transmute(&self.inner) }
+    }
+}
+
+impl PartialEq for OsStr {
+    fn eq(&self, other: &OsStr) -> bool {
+        self.bytes().eq(other.bytes())
+    }
+}
+
+impl PartialEq<str> for OsStr {
+    fn eq(&self, other: &str) -> bool {
+        *self == *OsStr::from_str(other)
+    }
+}
+
+impl PartialEq<OsStr> for str {
+    fn eq(&self, other: &OsStr) -> bool {
+        *other == *OsStr::from_str(self)
+    }
+}
+
+impl Eq for OsStr {}
+
+impl PartialOrd for OsStr {
+    #[inline]
+    fn partial_cmp(&self, other: &OsStr) -> Option<cmp::Ordering> {
+        self.bytes().partial_cmp(other.bytes())
+    }
+    #[inline]
+    fn lt(&self, other: &OsStr) -> bool { self.bytes().lt(other.bytes()) }
+    #[inline]
+    fn le(&self, other: &OsStr) -> bool { self.bytes().le(other.bytes()) }
+    #[inline]
+    fn gt(&self, other: &OsStr) -> bool { self.bytes().gt(other.bytes()) }
+    #[inline]
+    fn ge(&self, other: &OsStr) -> bool { self.bytes().ge(other.bytes()) }
+}
+
+impl PartialOrd<str> for OsStr {
+    #[inline]
+    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
+        self.partial_cmp(OsStr::from_str(other))
+    }
+}
+
+// FIXME (#19470): cannot provide PartialOrd<OsStr> for str until we
+// have more flexible coherence rules.
+
+impl Ord for OsStr {
+    #[inline]
+    fn cmp(&self, other: &OsStr) -> cmp::Ordering { self.bytes().cmp(other.bytes()) }
+}
+
+impl<'a, S: Hasher + Writer> Hash<S> for OsStr {
+    #[inline]
+    fn hash(&self, state: &mut S) {
+        self.bytes().hash(state)
+    }
+}
+
+impl Debug for OsStr {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.inner.fmt(formatter)
+    }
+}
+
+impl BorrowFrom<OsString> for OsStr {
+    fn borrow_from(owned: &OsString) -> &OsStr { &owned[] }
+}
+
+impl ToOwned<OsString> for OsStr {
+    fn to_owned(&self) -> OsString { self.to_os_string() }
+}
+
+/// Freely convertible to an `&OsStr` slice.
+pub trait AsOsStr {
+    /// Convert to an `&OsStr` slice.
+    fn as_os_str(&self) -> &OsStr;
+}
+
+impl AsOsStr for OsStr {
+    fn as_os_str(&self) -> &OsStr {
+        self
+    }
+}
+
+impl AsOsStr for OsString {
+    fn as_os_str(&self) -> &OsStr {
+        &self[]
+    }
+}
+
+impl AsOsStr for str {
+    fn as_os_str(&self) -> &OsStr {
+        OsStr::from_str(self)
+    }
+}
+
+impl AsOsStr for String {
+    fn as_os_str(&self) -> &OsStr {
+        OsStr::from_str(&self[])
+    }
+}
+
+impl FromInner<Buf> for OsString {
+    fn from_inner(buf: Buf) -> OsString {
+        OsString { inner: buf }
+    }
+}
+
+impl IntoInner<Buf> for OsString {
+    fn into_inner(self) -> Buf {
+        self.inner
+    }
+}
+
+impl AsInner<Slice> for OsStr {
+    fn as_inner(&self) -> &Slice {
+        &self.inner
+    }
+}
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index a31dcc9884f..272cf9bd0c0 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -29,6 +29,7 @@ pub mod stack;
 pub mod thread;
 pub mod thread_info;
 pub mod thread_local;
+pub mod wtf8;
 
 // common error constructors
 
@@ -93,11 +94,21 @@ pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where
     return (origamt - amt) as i64;
 }
 
-// A trait for extracting representations from std::io types
-pub trait AsInner<Inner> {
+/// A trait for viewing representations from std types
+pub trait AsInner<Inner: ?Sized> {
     fn as_inner(&self) -> &Inner;
 }
 
+/// A trait for extracting representations from std types
+pub trait IntoInner<Inner> {
+    fn into_inner(self) -> Inner;
+}
+
+/// A trait for creating std types from internal representations
+pub trait FromInner<Inner> {
+    fn from_inner(inner: Inner) -> Self;
+}
+
 pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
     fn program(&self) -> &CString;
     fn args(&self) -> &[CString];
diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs
new file mode 100644
index 00000000000..bc072155063
--- /dev/null
+++ b/src/libstd/sys/common/wtf8.rs
@@ -0,0 +1,1212 @@
+// Copyright 2015 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.
+
+//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/).
+//!
+//! This library uses Rust’s type system to maintain
+//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed),
+//! like the `String` and `&str` types do for UTF-8.
+//!
+//! Since [WTF-8 must not be used
+//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience),
+//! this library deliberately does not provide access to the underlying bytes
+//! of WTF-8 strings,
+//! nor can it decode WTF-8 from arbitrary bytes.
+//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points.
+
+use core::prelude::*;
+
+use core::char::{encode_utf8_raw, encode_utf16_raw};
+use core::str::{char_range_at_raw, next_code_point};
+use core::raw::Slice as RawSlice;
+
+use borrow::Cow;
+use cmp;
+use fmt;
+use hash::{Hash, Writer, Hasher};
+use iter::FromIterator;
+use mem;
+use num::Int;
+use ops;
+use slice;
+use str;
+use string::{String, CowString};
+use unicode::str::{Utf16Item, utf16_items};
+use vec::Vec;
+
+static UTF8_REPLACEMENT_CHARACTER: &'static [u8] = b"\xEF\xBF\xBD";
+
+/// A Unicode code point: from U+0000 to U+10FFFF.
+///
+/// Compare with the `char` type,
+/// which represents a Unicode scalar value:
+/// a code point that is not a surrogate (U+D800 to U+DFFF).
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
+pub struct CodePoint {
+    value: u32
+}
+
+/// Format the code point as `U+` followed by four to six hexadecimal digits.
+/// Example: `U+1F4A9`
+impl fmt::Debug for CodePoint {
+    #[inline]
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(formatter, "U+{:04X}", self.value)
+    }
+}
+
+impl CodePoint {
+    /// Unsafely create a new `CodePoint` without checking the value.
+    ///
+    /// Only use when `value` is known to be less than or equal to 0x10FFFF.
+    #[inline]
+    pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint {
+        CodePoint { value: value }
+    }
+
+    /// Create a new `CodePoint` if the value is a valid code point.
+    ///
+    /// Return `None` if `value` is above 0x10FFFF.
+    #[inline]
+    pub fn from_u32(value: u32) -> Option<CodePoint> {
+        match value {
+            0 ... 0x10FFFF => Some(CodePoint { value: value }),
+            _ => None
+        }
+    }
+
+    /// Create a new `CodePoint` from a `char`.
+    ///
+    /// Since all Unicode scalar values are code points, this always succeds.
+    #[inline]
+    pub fn from_char(value: char) -> CodePoint {
+        CodePoint { value: value as u32 }
+    }
+
+    /// Return the numeric value of the code point.
+    #[inline]
+    pub fn to_u32(&self) -> u32 {
+        self.value
+    }
+
+    /// Optionally return a Unicode scalar value for the code point.
+    ///
+    /// Return `None` if the code point is a surrogate (from U+D800 to U+DFFF).
+    #[inline]
+    pub fn to_char(&self) -> Option<char> {
+        match self.value {
+            0xD800 ... 0xDFFF => None,
+            _ => Some(unsafe { mem::transmute(self.value) })
+        }
+    }
+
+    /// Return a Unicode scalar value for the code point.
+    ///
+    /// Return `'\u{FFFD}'` (the replacement character “�”)
+    /// if the code point is a surrogate (from U+D800 to U+DFFF).
+    #[inline]
+    pub fn to_char_lossy(&self) -> char {
+        self.to_char().unwrap_or('\u{FFFD}')
+    }
+}
+
+/// An owned, growable string of well-formed WTF-8 data.
+///
+/// Similar to `String`, but can additionally contain surrogate code points
+/// if they’re not in a surrogate pair.
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)]
+pub struct Wtf8Buf {
+    bytes: Vec<u8>
+}
+
+impl ops::Deref for Wtf8Buf {
+    type Target = Wtf8;
+
+    fn deref(&self) -> &Wtf8 {
+        self.as_slice()
+    }
+}
+
+/// Format the string with double quotes,
+/// and surrogates as `\u` followed by four hexadecimal digits.
+/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800]
+impl fmt::Debug for Wtf8Buf {
+    #[inline]
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.as_slice().fmt(formatter)
+    }
+}
+
+impl Wtf8Buf {
+    /// Create an new, empty WTF-8 string.
+    #[inline]
+    pub fn new() -> Wtf8Buf {
+        Wtf8Buf { bytes: Vec::new() }
+    }
+
+    /// Create an new, empty WTF-8 string with pre-allocated capacity for `n` bytes.
+    #[inline]
+    pub fn with_capacity(n: uint) -> Wtf8Buf {
+        Wtf8Buf { bytes: Vec::with_capacity(n) }
+    }
+
+    /// Create a WTF-8 string from an UTF-8 `String`.
+    ///
+    /// This takes ownership of the `String` and does not copy.
+    ///
+    /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+    #[inline]
+    pub fn from_string(string: String) -> Wtf8Buf {
+        Wtf8Buf { bytes: string.into_bytes() }
+    }
+
+    /// Create a WTF-8 string from an UTF-8 `&str` slice.
+    ///
+    /// This copies the content of the slice.
+    ///
+    /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+    #[inline]
+    pub fn from_str(str: &str) -> Wtf8Buf {
+        Wtf8Buf { bytes: slice::SliceExt::to_vec(str.as_bytes()) }
+    }
+
+    /// Create a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units.
+    ///
+    /// This is lossless: calling `.encode_wide()` on the resulting string
+    /// will always return the original code units.
+    pub fn from_wide(v: &[u16]) -> Wtf8Buf {
+        let mut string = Wtf8Buf::with_capacity(v.len());
+        for item in utf16_items(v) {
+            match item {
+                Utf16Item::ScalarValue(c) => string.push_char(c),
+                Utf16Item::LoneSurrogate(s) => {
+                    // Surrogates are known to be in the code point range.
+                    let code_point = unsafe { CodePoint::from_u32_unchecked(s as u32) };
+                    // Skip the WTF-8 concatenation check,
+                    // surrogate pairs are already decoded by utf16_items
+                    string.push_code_point_unchecked(code_point)
+                }
+            }
+        }
+        string
+    }
+
+    /// Copied from String::push
+    /// This does **not** include the WTF-8 concatenation check.
+    fn push_code_point_unchecked(&mut self, code_point: CodePoint) {
+        let cur_len = self.len();
+        // This may use up to 4 bytes.
+        self.reserve(4);
+
+        unsafe {
+            // Attempt to not use an intermediate buffer by just pushing bytes
+            // directly onto this string.
+            let slice = RawSlice {
+                data: self.bytes.as_ptr().offset(cur_len as int),
+                len: 4,
+            };
+            let used = encode_utf8_raw(code_point.value, mem::transmute(slice))
+                .unwrap_or(0);
+            self.bytes.set_len(cur_len + used);
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &Wtf8 {
+        unsafe { mem::transmute(self.bytes.as_slice()) }
+    }
+
+    /// Reserves capacity for at least `additional` more bytes to be inserted
+    /// in the given `Wtf8Buf`.
+    /// The collection may reserve more space to avoid frequent reallocations.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the new capacity overflows `uint`.
+    #[inline]
+    pub fn reserve(&mut self, additional: uint) {
+        self.bytes.reserve(additional)
+    }
+
+    /// Returns the number of bytes that this string buffer can hold without reallocating.
+    #[inline]
+    pub fn capacity(&self) -> uint {
+        self.bytes.capacity()
+    }
+
+    /// Append an UTF-8 slice at the end of the string.
+    #[inline]
+    pub fn push_str(&mut self, other: &str) {
+        self.bytes.push_all(other.as_bytes())
+    }
+
+    /// Append a WTF-8 slice at the end of the string.
+    ///
+    /// This replaces newly paired surrogates at the boundary
+    /// with a supplementary code point,
+    /// like concatenating ill-formed UTF-16 strings effectively would.
+    #[inline]
+    pub fn push_wtf8(&mut self, other: &Wtf8) {
+        match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) {
+            // Replace newly paired surrogates by a supplementary code point.
+            (Some(lead), Some(trail)) => {
+                let len_without_lead_surrogate = self.len() - 3;
+                self.bytes.truncate(len_without_lead_surrogate);
+                let other_without_trail_surrogate = &other.bytes[3..];
+                // 4 bytes for the supplementary code point
+                self.bytes.reserve(4 + other_without_trail_surrogate.len());
+                self.push_char(decode_surrogate_pair(lead, trail));
+                self.bytes.push_all(other_without_trail_surrogate);
+            }
+            _ => self.bytes.push_all(&other.bytes)
+        }
+    }
+
+    /// Append a Unicode scalar value at the end of the string.
+    #[inline]
+    pub fn push_char(&mut self, c: char) {
+        self.push_code_point_unchecked(CodePoint::from_char(c))
+    }
+
+    /// Append a code point at the end of the string.
+    ///
+    /// This replaces newly paired surrogates at the boundary
+    /// with a supplementary code point,
+    /// like concatenating ill-formed UTF-16 strings effectively would.
+    #[inline]
+    pub fn push(&mut self, code_point: CodePoint) {
+        match code_point.to_u32() {
+            trail @ 0xDC00...0xDFFF => {
+                match (&*self).final_lead_surrogate() {
+                    Some(lead) => {
+                        let len_without_lead_surrogate = self.len() - 3;
+                        self.bytes.truncate(len_without_lead_surrogate);
+                        self.push_char(decode_surrogate_pair(lead, trail as u16));
+                        return
+                    }
+                    _ => {}
+                }
+            }
+            _ => {}
+        }
+
+        // No newly paired surrogates at the boundary.
+        self.push_code_point_unchecked(code_point)
+    }
+
+    /// Shortens a string to the specified length.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `new_len` > current length,
+    /// or if `new_len` is not a code point boundary.
+    #[inline]
+    pub fn truncate(&mut self, new_len: uint) {
+        assert!(is_code_point_boundary(self.as_slice(), new_len));
+        self.bytes.truncate(new_len)
+    }
+
+    /// Consume the WTF-8 string and try to convert it to UTF-8.
+    ///
+    /// This does not copy the data.
+    ///
+    /// If the contents are not well-formed UTF-8
+    /// (that is, if the string contains surrogates),
+    /// the original WTF-8 string is returned instead.
+    pub fn into_string(self) -> Result<String, Wtf8Buf> {
+        match self.next_surrogate(0) {
+            None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }),
+            Some(_) => Err(self),
+        }
+    }
+
+    /// Consume the WTF-8 string and convert it lossily to UTF-8.
+    ///
+    /// This does not copy the data (but may overwrite parts of it in place).
+    ///
+    /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”)
+    pub fn into_string_lossy(mut self) -> String {
+        let mut pos = 0;
+        loop {
+            match self.next_surrogate(pos) {
+                Some((surrogate_pos, _)) => {
+                    pos = surrogate_pos + 3;
+                    slice::bytes::copy_memory(
+                        &mut self.bytes[surrogate_pos .. pos],
+                        UTF8_REPLACEMENT_CHARACTER
+                    );
+                },
+                None => return unsafe { String::from_utf8_unchecked(self.bytes) }
+            }
+        }
+    }
+}
+
+/// Create a new WTF-8 string from an iterator of code points.
+///
+/// This replaces surrogate code point pairs with supplementary code points,
+/// like concatenating ill-formed UTF-16 strings effectively would.
+impl FromIterator<CodePoint> for Wtf8Buf {
+    fn from_iter<T: Iterator<Item=CodePoint>>(iterator: T) -> Wtf8Buf {
+        let mut string = Wtf8Buf::new();
+        string.extend(iterator);
+        string
+    }
+}
+
+/// Append code points from an iterator to the string.
+///
+/// This replaces surrogate code point pairs with supplementary code points,
+/// like concatenating ill-formed UTF-16 strings effectively would.
+impl Extend<CodePoint> for Wtf8Buf {
+    fn extend<T: Iterator<Item=CodePoint>>(&mut self, mut iterator: T) {
+        let (low, _high) = iterator.size_hint();
+        // Lower bound of one byte per code point (ASCII only)
+        self.bytes.reserve(low);
+        for code_point in iterator {
+            self.push(code_point);
+        }
+    }
+}
+
+/// A borrowed slice of well-formed WTF-8 data.
+///
+/// Similar to `&str`, but can additionally contain surrogate code points
+/// if they’re not in a surrogate pair.
+pub struct Wtf8 {
+    bytes: [u8]
+}
+
+// FIXME: https://github.com/rust-lang/rust/issues/18805
+impl PartialEq for Wtf8 {
+    fn eq(&self, other: &Wtf8) -> bool { self.bytes.eq(&other.bytes) }
+}
+
+// FIXME: https://github.com/rust-lang/rust/issues/18805
+impl Eq for Wtf8 {}
+
+// FIXME: https://github.com/rust-lang/rust/issues/18738
+impl PartialOrd for Wtf8 {
+    #[inline]
+    fn partial_cmp(&self, other: &Wtf8) -> Option<cmp::Ordering> {
+        self.bytes.partial_cmp(&other.bytes)
+    }
+    #[inline]
+    fn lt(&self, other: &Wtf8) -> bool { self.bytes.lt(&other.bytes) }
+    #[inline]
+    fn le(&self, other: &Wtf8) -> bool { self.bytes.le(&other.bytes) }
+    #[inline]
+    fn gt(&self, other: &Wtf8) -> bool { self.bytes.gt(&other.bytes) }
+    #[inline]
+    fn ge(&self, other: &Wtf8) -> bool { self.bytes.ge(&other.bytes) }
+}
+
+// FIXME: https://github.com/rust-lang/rust/issues/18738
+impl Ord for Wtf8 {
+    #[inline]
+    fn cmp(&self, other: &Wtf8) -> cmp::Ordering { self.bytes.cmp(&other.bytes) }
+}
+
+/// Format the slice with double quotes,
+/// and surrogates as `\u` followed by four hexadecimal digits.
+/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800]
+impl fmt::Debug for Wtf8 {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        try!(formatter.write_str("\""));
+        let mut pos = 0;
+        loop {
+            match self.next_surrogate(pos) {
+                None => break,
+                Some((surrogate_pos, surrogate)) => {
+                    try!(formatter.write_str(unsafe {
+                        // the data in this slice is valid UTF-8, transmute to &str
+                        mem::transmute(&self.bytes[pos .. surrogate_pos])
+                    }));
+                    try!(write!(formatter, "\\u{{{:X}}}", surrogate));
+                    pos = surrogate_pos + 3;
+                }
+            }
+        }
+        try!(formatter.write_str(unsafe {
+            // the data in this slice is valid UTF-8, transmute to &str
+            mem::transmute(&self.bytes[pos..])
+        }));
+        formatter.write_str("\"")
+    }
+}
+
+impl Wtf8 {
+    /// Create a WTF-8 slice from a UTF-8 `&str` slice.
+    ///
+    /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+    #[inline]
+    pub fn from_str(value: &str) -> &Wtf8 {
+        unsafe { mem::transmute(value.as_bytes()) }
+    }
+
+    /// Return the length, in WTF-8 bytes.
+    #[inline]
+    pub fn len(&self) -> uint {
+        self.bytes.len()
+    }
+
+    /// Return the code point at `position` if it is in the ASCII range,
+    /// or `b'\xFF' otherwise.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `position` is beyond the end of the string.
+    #[inline]
+    pub fn ascii_byte_at(&self, position: uint) -> u8 {
+        match self.bytes[position] {
+            ascii_byte @ 0x00 ... 0x7F => ascii_byte,
+            _ => 0xFF
+        }
+    }
+
+    /// Return the code point at `position`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `position` is not at a code point boundary,
+    /// or is beyond the end of the string.
+    #[inline]
+    pub fn code_point_at(&self, position: uint) -> CodePoint {
+        let (code_point, _) = self.code_point_range_at(position);
+        code_point
+    }
+
+    /// Return the code point at `position`
+    /// and the position of the next code point.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `position` is not at a code point boundary,
+    /// or is beyond the end of the string.
+    #[inline]
+    pub fn code_point_range_at(&self, position: uint) -> (CodePoint, uint) {
+        let (c, n) = char_range_at_raw(&self.bytes, position);
+        (CodePoint { value: c }, n)
+    }
+
+    /// Return an iterator for the string’s code points.
+    #[inline]
+    pub fn code_points(&self) -> Wtf8CodePoints {
+        Wtf8CodePoints { bytes: self.bytes.iter() }
+    }
+
+    /// Try to convert the string to UTF-8 and return a `&str` slice.
+    ///
+    /// Return `None` if the string contains surrogates.
+    ///
+    /// This does not copy the data.
+    #[inline]
+    pub fn as_str(&self) -> Option<&str> {
+        // Well-formed WTF-8 is also well-formed UTF-8
+        // if and only if it contains no surrogate.
+        match self.next_surrogate(0) {
+            None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }),
+            Some(_) => None,
+        }
+    }
+
+    /// Lossily convert the string to UTF-8.
+    /// Return an UTF-8 `&str` slice if the contents are well-formed in UTF-8.
+    ///
+    /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”).
+    ///
+    /// This only copies the data if necessary (if it contains any surrogate).
+    pub fn to_string_lossy(&self) -> CowString {
+        let surrogate_pos = match self.next_surrogate(0) {
+            None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }),
+            Some((pos, _)) => pos,
+        };
+        let wtf8_bytes = &self.bytes;
+        let mut utf8_bytes = Vec::with_capacity(self.len());
+        utf8_bytes.push_all(&wtf8_bytes[..surrogate_pos]);
+        utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER);
+        let mut pos = surrogate_pos + 3;
+        loop {
+            match self.next_surrogate(pos) {
+                Some((surrogate_pos, _)) => {
+                    utf8_bytes.push_all(&wtf8_bytes[pos .. surrogate_pos]);
+                    utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER);
+                    pos = surrogate_pos + 3;
+                },
+                None => {
+                    utf8_bytes.push_all(&wtf8_bytes[pos..]);
+                    return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) })
+                }
+            }
+        }
+    }
+
+    /// Convert the WTF-8 string to potentially ill-formed UTF-16
+    /// and return an iterator of 16-bit code units.
+    ///
+    /// This is lossless:
+    /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units
+    /// would always return the original WTF-8 string.
+    #[inline]
+    pub fn encode_wide(&self) -> EncodeWide {
+        EncodeWide { code_points: self.code_points(), extra: 0 }
+    }
+
+    #[inline]
+    fn next_surrogate(&self, mut pos: uint) -> Option<(uint, u16)> {
+        let mut iter = self.bytes[pos..].iter();
+        loop {
+            let b = match iter.next() {
+                None => return None,
+                Some(&b) => b,
+            };
+            if b < 0x80 {
+                pos += 1;
+            } else if b < 0xE0 {
+                iter.next();
+                pos += 2;
+            } else if b == 0xED {
+                match (iter.next(), iter.next()) {
+                    (Some(&b2), Some(&b3)) if b2 >= 0xA0 => {
+                        return Some((pos, decode_surrogate(b2, b3)))
+                    }
+                    _ => pos += 3
+                }
+            } else if b < 0xF0 {
+                iter.next();
+                iter.next();
+                pos += 3;
+            } else {
+                iter.next();
+                iter.next();
+                iter.next();
+                pos += 4;
+            }
+        }
+    }
+
+    #[inline]
+    fn final_lead_surrogate(&self) -> Option<u16> {
+        let len = self.len();
+        if len < 3 {
+            return None
+        }
+        match &self.bytes[(len - 3)..] {
+            [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)),
+            _ => None
+        }
+    }
+
+    #[inline]
+    fn initial_trail_surrogate(&self) -> Option<u16> {
+        let len = self.len();
+        if len < 3 {
+            return None
+        }
+        match &self.bytes[..3] {
+            [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)),
+            _ => None
+        }
+    }
+}
+
+
+/// Return a slice of the given string for the byte range [`begin`..`end`).
+///
+/// # Panics
+///
+/// Panics when `begin` and `end` do not point to code point boundaries,
+/// or point beyond the end of the string.
+impl ops::Index<ops::Range<usize>> for Wtf8 {
+    type Output = Wtf8;
+
+    #[inline]
+    fn index(&self, range: &ops::Range<usize>) -> &Wtf8 {
+        // is_code_point_boundary checks that the index is in [0, .len()]
+        if range.start <= range.end &&
+           is_code_point_boundary(self, range.start) &&
+           is_code_point_boundary(self, range.end) {
+            unsafe { slice_unchecked(self, range.start, range.end) }
+        } else {
+            slice_error_fail(self, range.start, range.end)
+        }
+    }
+}
+
+/// Return a slice of the given string from byte `begin` to its end.
+///
+/// # Panics
+///
+/// Panics when `begin` is not at a code point boundary,
+/// or is beyond the end of the string.
+impl ops::Index<ops::RangeFrom<usize>> for Wtf8 {
+    type Output = Wtf8;
+
+    #[inline]
+    fn index(&self, range: &ops::RangeFrom<usize>) -> &Wtf8 {
+        // is_code_point_boundary checks that the index is in [0, .len()]
+        if is_code_point_boundary(self, range.start) {
+            unsafe { slice_unchecked(self, range.start, self.len()) }
+        } else {
+            slice_error_fail(self, range.start, self.len())
+        }
+    }
+}
+
+/// Return a slice of the given string from its beginning to byte `end`.
+///
+/// # Panics
+///
+/// Panics when `end` is not at a code point boundary,
+/// or is beyond the end of the string.
+impl ops::Index<ops::RangeTo<usize>> for Wtf8 {
+    type Output = Wtf8;
+
+    #[inline]
+    fn index(&self, range: &ops::RangeTo<usize>) -> &Wtf8 {
+        // is_code_point_boundary checks that the index is in [0, .len()]
+        if is_code_point_boundary(self, range.end) {
+            unsafe { slice_unchecked(self, 0, range.end) }
+        } else {
+            slice_error_fail(self, 0, range.end)
+        }
+    }
+}
+
+impl ops::Index<ops::FullRange> for Wtf8 {
+    type Output = Wtf8;
+
+    #[inline]
+    fn index(&self, _range: &ops::FullRange) -> &Wtf8 {
+        self
+    }
+}
+
+#[inline]
+fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 {
+    // The first byte is assumed to be 0xED
+    0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F
+}
+
+#[inline]
+fn decode_surrogate_pair(lead: u16, trail: u16) -> char {
+    let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32);
+    unsafe { mem::transmute(code_point) }
+}
+
+/// Copied from core::str::StrPrelude::is_char_boundary
+#[inline]
+pub fn is_code_point_boundary(slice: &Wtf8, index: uint) -> bool {
+    if index == slice.len() { return true; }
+    match slice.bytes.get(index) {
+        None => false,
+        Some(&b) => b < 128u8 || b >= 192u8,
+    }
+}
+
+/// Copied from core::str::raw::slice_unchecked
+#[inline]
+pub unsafe fn slice_unchecked(s: &Wtf8, begin: uint, end: uint) -> &Wtf8 {
+    mem::transmute(RawSlice {
+        data: s.bytes.as_ptr().offset(begin as int),
+        len: end - begin,
+    })
+}
+
+/// Copied from core::str::raw::slice_error_fail
+#[inline(never)]
+pub fn slice_error_fail(s: &Wtf8, begin: uint, end: uint) -> ! {
+    assert!(begin <= end);
+    panic!("index {} and/or {} in `{:?}` do not lie on character boundary",
+          begin, end, s);
+}
+
+/// Iterator for the code points of a WTF-8 string.
+///
+/// Created with the method `.code_points()`.
+#[derive(Clone)]
+pub struct Wtf8CodePoints<'a> {
+    bytes: slice::Iter<'a, u8>
+}
+
+impl<'a> Iterator for Wtf8CodePoints<'a> {
+    type Item = CodePoint;
+
+    #[inline]
+    fn next(&mut self) -> Option<CodePoint> {
+        next_code_point(&mut self.bytes).map(|c| CodePoint { value: c })
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (uint, Option<uint>) {
+        let (len, _) = self.bytes.size_hint();
+        (len.saturating_add(3) / 4, Some(len))
+    }
+}
+
+#[derive(Clone)]
+pub struct EncodeWide<'a> {
+    code_points: Wtf8CodePoints<'a>,
+    extra: u16
+}
+
+// Copied from libunicode/u_str.rs
+impl<'a> Iterator for EncodeWide<'a> {
+    type Item = u16;
+
+    #[inline]
+    fn next(&mut self) -> Option<u16> {
+        if self.extra != 0 {
+            let tmp = self.extra;
+            self.extra = 0;
+            return Some(tmp);
+        }
+
+        let mut buf = [0u16; 2];
+        self.code_points.next().map(|code_point| {
+            let n = encode_utf16_raw(code_point.value, buf.as_mut_slice())
+                .unwrap_or(0);
+            if n == 2 { self.extra = buf[1]; }
+            buf[0]
+        })
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (uint, Option<uint>) {
+        let (low, high) = self.code_points.size_hint();
+        // every code point gets either one u16 or two u16,
+        // so this iterator is between 1 or 2 times as
+        // long as the underlying iterator.
+        (low, high.and_then(|n| n.checked_mul(2)))
+    }
+}
+
+impl<S: Writer + Hasher> Hash<S> for CodePoint {
+    #[inline]
+    fn hash(&self, state: &mut S) {
+        self.value.hash(state)
+    }
+}
+
+impl<S: Writer + Hasher> Hash<S> for Wtf8Buf {
+    #[inline]
+    fn hash(&self, state: &mut S) {
+        state.write(self.bytes.as_slice());
+        0xfeu8.hash(state)
+    }
+}
+
+impl<'a, S: Writer + Hasher> Hash<S> for Wtf8 {
+    #[inline]
+    fn hash(&self, state: &mut S) {
+        state.write(&self.bytes);
+        0xfeu8.hash(state)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::v1::*;
+    use borrow::Cow;
+    use super::*;
+    use mem::transmute;
+    use string::CowString;
+
+    #[test]
+    fn code_point_from_u32() {
+        assert!(CodePoint::from_u32(0).is_some());
+        assert!(CodePoint::from_u32(0xD800).is_some());
+        assert!(CodePoint::from_u32(0x10FFFF).is_some());
+        assert!(CodePoint::from_u32(0x110000).is_none());
+    }
+
+    #[test]
+    fn code_point_to_u32() {
+        fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() }
+        assert_eq!(c(0).to_u32(), 0);
+        assert_eq!(c(0xD800).to_u32(), 0xD800);
+        assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF);
+    }
+
+    #[test]
+    fn code_point_from_char() {
+        assert_eq!(CodePoint::from_char('a').to_u32(), 0x61);
+        assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9);
+    }
+
+    #[test]
+    fn code_point_to_string() {
+        assert_eq!(format!("{:?}", CodePoint::from_char('a')).as_slice(), "U+0061");
+        assert_eq!(format!("{:?}", CodePoint::from_char('💩')).as_slice(), "U+1F4A9");
+    }
+
+    #[test]
+    fn code_point_to_char() {
+        fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() }
+        assert_eq!(c(0x61).to_char(), Some('a'));
+        assert_eq!(c(0x1F4A9).to_char(), Some('💩'));
+        assert_eq!(c(0xD800).to_char(), None);
+    }
+
+    #[test]
+    fn code_point_to_char_lossy() {
+        fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() }
+        assert_eq!(c(0x61).to_char_lossy(), 'a');
+        assert_eq!(c(0x1F4A9).to_char_lossy(), '💩');
+        assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}');
+    }
+
+    #[test]
+    fn wtf8buf_new() {
+        assert_eq!(Wtf8Buf::new().bytes.as_slice(), b"");
+    }
+
+    #[test]
+    fn wtf8buf_from_str() {
+        assert_eq!(Wtf8Buf::from_str("").bytes.as_slice(), b"");
+        assert_eq!(Wtf8Buf::from_str("aé 💩").bytes.as_slice(),
+                   b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8buf_from_string() {
+        assert_eq!(Wtf8Buf::from_string(String::from_str("")).bytes.as_slice(), b"");
+        assert_eq!(Wtf8Buf::from_string(String::from_str("aé 💩")).bytes.as_slice(),
+                   b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8buf_from_wide() {
+        assert_eq!(Wtf8Buf::from_wide(&[]).bytes.as_slice(), b"");
+        assert_eq!(Wtf8Buf::from_wide(
+                      &[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes.as_slice(),
+                   b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8buf_push_str() {
+        let mut string = Wtf8Buf::new();
+        assert_eq!(string.bytes.as_slice(), b"");
+        string.push_str("aé 💩");
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8buf_push_char() {
+        let mut string = Wtf8Buf::from_str("aé ");
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 ");
+        string.push_char('💩');
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8buf_push() {
+        let mut string = Wtf8Buf::from_str("aé ");
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 ");
+        string.push(CodePoint::from_char('💩'));
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+        fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() }
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xD83D));  // lead
+        string.push(c(0xDCA9));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9");  // Magic!
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xD83D));  // lead
+        string.push(c(0x20));  // not surrogate
+        string.push(c(0xDCA9));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9");
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xD800));  // lead
+        string.push(c(0xDBFF));  // lead
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF");
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xD800));  // lead
+        string.push(c(0xE000));  // not surrogate
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xD7FF));  // not surrogate
+        string.push(c(0xDC00));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0x61));  // not surrogate, < 3 bytes
+        string.push(c(0xDC00));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push(c(0xDC00));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80");
+    }
+
+    #[test]
+    fn wtf8buf_push_wtf8() {
+        let mut string = Wtf8Buf::from_str("aé");
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9");
+        string.push_wtf8(Wtf8::from_str(" 💩"));
+        assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+        fn w(value: &[u8]) -> &Wtf8 { unsafe { transmute(value) } }
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\xA0\xBD"));  // lead
+        string.push_wtf8(w(b"\xED\xB2\xA9"));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9");  // Magic!
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\xA0\xBD"));  // lead
+        string.push_wtf8(w(b" "));  // not surrogate
+        string.push_wtf8(w(b"\xED\xB2\xA9"));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9");
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\xA0\x80"));  // lead
+        string.push_wtf8(w(b"\xED\xAF\xBF"));  // lead
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF");
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\xA0\x80"));  // lead
+        string.push_wtf8(w(b"\xEE\x80\x80"));  // not surrogate
+        assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\x9F\xBF"));  // not surrogate
+        string.push_wtf8(w(b"\xED\xB0\x80"));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"a"));  // not surrogate, < 3 bytes
+        string.push_wtf8(w(b"\xED\xB0\x80"));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80");
+
+        let mut string = Wtf8Buf::new();
+        string.push_wtf8(w(b"\xED\xB0\x80"));  // trail
+        assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80");
+    }
+
+    #[test]
+    fn wtf8buf_truncate() {
+        let mut string = Wtf8Buf::from_str("aé");
+        string.truncate(1);
+        assert_eq!(string.bytes.as_slice(), b"a");
+    }
+
+    #[test]
+    #[should_fail]
+    fn wtf8buf_truncate_fail_code_point_boundary() {
+        let mut string = Wtf8Buf::from_str("aé");
+        string.truncate(2);
+    }
+
+    #[test]
+    #[should_fail]
+    fn wtf8buf_truncate_fail_longer() {
+        let mut string = Wtf8Buf::from_str("aé");
+        string.truncate(4);
+    }
+
+    #[test]
+    fn wtf8buf_into_string() {
+        let mut string = Wtf8Buf::from_str("aé 💩");
+        assert_eq!(string.clone().into_string(), Ok(String::from_str("aé 💩")));
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        assert_eq!(string.clone().into_string(), Err(string));
+    }
+
+    #[test]
+    fn wtf8buf_into_string_lossy() {
+        let mut string = Wtf8Buf::from_str("aé 💩");
+        assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩"));
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩�"));
+    }
+
+    #[test]
+    fn wtf8buf_from_iterator() {
+        fn f(values: &[u32]) -> Wtf8Buf {
+            values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>()
+        };
+        assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+        assert_eq!(f(&[0xD83D, 0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9");  // Magic!
+        assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9");
+        assert_eq!(f(&[0xD800, 0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF");
+        assert_eq!(f(&[0xD800, 0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80");
+        assert_eq!(f(&[0xD7FF, 0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80");
+        assert_eq!(f(&[0x61, 0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80");
+        assert_eq!(f(&[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80");
+    }
+
+    #[test]
+    fn wtf8buf_extend() {
+        fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf {
+            fn c(value: &u32) -> CodePoint { CodePoint::from_u32(*value).unwrap() }
+            let mut string = initial.iter().map(c).collect::<Wtf8Buf>();
+            string.extend(extended.iter().map(c));
+            string
+        };
+
+        assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes.as_slice(),
+                   b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+        assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9");  // Magic!
+        assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9");
+        assert_eq!(e(&[0xD800], &[0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF");
+        assert_eq!(e(&[0xD800], &[0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80");
+        assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80");
+        assert_eq!(e(&[0x61], &[0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80");
+        assert_eq!(e(&[], &[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80");
+    }
+
+    #[test]
+    fn wtf8buf_show() {
+        let mut string = Wtf8Buf::from_str("aé 💩");
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        assert_eq!(format!("{:?}", string).as_slice(), r#""aé 💩\u{D800}""#);
+    }
+
+    #[test]
+    fn wtf8buf_as_slice() {
+        assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé"));
+    }
+
+    #[test]
+    fn wtf8_show() {
+        let mut string = Wtf8Buf::from_str("aé 💩");
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        assert_eq!(format!("{:?}", string.as_slice()).as_slice(), r#""aé 💩\u{D800}""#);
+    }
+
+    #[test]
+    fn wtf8_from_str() {
+        assert_eq!(&Wtf8::from_str("").bytes, b"");
+        assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    fn wtf8_len() {
+        assert_eq!(Wtf8::from_str("").len(), 0);
+        assert_eq!(Wtf8::from_str("aé 💩").len(), 8);
+    }
+
+    #[test]
+    fn wtf8_slice() {
+        assert_eq!(&Wtf8::from_str("aé 💩")[1.. 4].bytes, b"\xC3\xA9 ");
+    }
+
+    #[test]
+    #[should_fail]
+    fn wtf8_slice_not_code_point_boundary() {
+        &Wtf8::from_str("aé 💩")[2.. 4];
+    }
+
+    #[test]
+    fn wtf8_slice_from() {
+        assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9");
+    }
+
+    #[test]
+    #[should_fail]
+    fn wtf8_slice_from_not_code_point_boundary() {
+        &Wtf8::from_str("aé 💩")[2..];
+    }
+
+    #[test]
+    fn wtf8_slice_to() {
+        assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 ");
+    }
+
+    #[test]
+    #[should_fail]
+    fn wtf8_slice_to_not_code_point_boundary() {
+        &Wtf8::from_str("aé 💩")[5..];
+    }
+
+    #[test]
+    fn wtf8_ascii_byte_at() {
+        let slice = Wtf8::from_str("aé 💩");
+        assert_eq!(slice.ascii_byte_at(0), b'a');
+        assert_eq!(slice.ascii_byte_at(1), b'\xFF');
+        assert_eq!(slice.ascii_byte_at(2), b'\xFF');
+        assert_eq!(slice.ascii_byte_at(3), b' ');
+        assert_eq!(slice.ascii_byte_at(4), b'\xFF');
+    }
+
+    #[test]
+    fn wtf8_code_point_at() {
+        let mut string = Wtf8Buf::from_str("aé ");
+        string.push(CodePoint::from_u32(0xD83D).unwrap());
+        string.push_char('💩');
+        assert_eq!(string.code_point_at(0), CodePoint::from_char('a'));
+        assert_eq!(string.code_point_at(1), CodePoint::from_char('é'));
+        assert_eq!(string.code_point_at(3), CodePoint::from_char(' '));
+        assert_eq!(string.code_point_at(4), CodePoint::from_u32(0xD83D).unwrap());
+        assert_eq!(string.code_point_at(7), CodePoint::from_char('💩'));
+    }
+
+    #[test]
+    fn wtf8_code_point_range_at() {
+        let mut string = Wtf8Buf::from_str("aé ");
+        string.push(CodePoint::from_u32(0xD83D).unwrap());
+        string.push_char('💩');
+        assert_eq!(string.code_point_range_at(0), (CodePoint::from_char('a'), 1));
+        assert_eq!(string.code_point_range_at(1), (CodePoint::from_char('é'), 3));
+        assert_eq!(string.code_point_range_at(3), (CodePoint::from_char(' '), 4));
+        assert_eq!(string.code_point_range_at(4), (CodePoint::from_u32(0xD83D).unwrap(), 7));
+        assert_eq!(string.code_point_range_at(7), (CodePoint::from_char('💩'), 11));
+    }
+
+    #[test]
+    fn wtf8_code_points() {
+        fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() }
+        fn cp(string: &Wtf8Buf) -> Vec<Option<char>> {
+            string.code_points().map(|c| c.to_char()).collect::<Vec<_>>()
+        }
+        let mut string = Wtf8Buf::from_str("é ");
+        assert_eq!(cp(&string), vec![Some('é'), Some(' ')]);
+        string.push(c(0xD83D));
+        assert_eq!(cp(&string), vec![Some('é'), Some(' '), None]);
+        string.push(c(0xDCA9));
+        assert_eq!(cp(&string), vec![Some('é'), Some(' '), Some('💩')]);
+    }
+
+    #[test]
+    fn wtf8_as_str() {
+        assert_eq!(Wtf8::from_str("").as_str(), Some(""));
+        assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩"));
+        let mut string = Wtf8Buf::new();
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        assert_eq!(string.as_str(), None);
+    }
+
+    #[test]
+    fn wtf8_to_string_lossy() {
+        assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed(""));
+        assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩"));
+        let mut string = Wtf8Buf::from_str("aé 💩");
+        string.push(CodePoint::from_u32(0xD800).unwrap());
+        let expected: CowString = Cow::Owned(String::from_str("aé 💩�"));
+        assert_eq!(string.to_string_lossy(), expected);
+    }
+
+    #[test]
+    fn wtf8_encode_wide() {
+        let mut string = Wtf8Buf::from_str("aé ");
+        string.push(CodePoint::from_u32(0xD83D).unwrap());
+        string.push_char('💩');
+        assert_eq!(string.encode_wide().collect::<Vec<_>>(),
+                   vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]);
+    }
+}
diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs
index 0e4a9d1b307..9c659b56d93 100644
--- a/src/libstd/sys/unix/ext.rs
+++ b/src/libstd/sys/unix/ext.rs
@@ -31,7 +31,10 @@
 
 #![unstable]
 
-use sys_common::AsInner;
+use vec::Vec;
+use sys::os_str::Buf;
+use sys_common::{AsInner, IntoInner, FromInner};
+use ffi::{OsStr, OsString};
 use libc;
 
 use io;
@@ -99,6 +102,36 @@ impl AsRawFd for io::net::udp::UdpSocket {
     }
 }
 
+// Unix-specific extensions to `OsString`.
+pub trait OsStringExt {
+    /// Create an `OsString` from a byte vector.
+    fn from_vec(vec: Vec<u8>) -> Self;
+
+    /// Yield the underlying byte vector of this `OsString`.
+    fn into_vec(self) -> Vec<u8>;
+}
+
+impl OsStringExt for OsString {
+    fn from_vec(vec: Vec<u8>) -> OsString {
+        FromInner::from_inner(Buf { inner: vec })
+    }
+
+    fn into_vec(self) -> Vec<u8> {
+        self.into_inner().inner
+    }
+}
+
+// Unix-specific extensions to `OsStr`.
+pub trait OsStrExt {
+    fn as_byte_slice(&self) -> &[u8];
+}
+
+impl OsStrExt for OsStr {
+    fn as_byte_slice(&self) -> &[u8] {
+        &self.as_inner().inner
+    }
+}
+
 /// A prelude for conveniently writing platform-specific code.
 ///
 /// Includes all extension traits, and some important type definitions.
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index bb98d1e052a..5493bc20a87 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -44,6 +44,7 @@ pub mod fs;
 pub mod helper_signal;
 pub mod mutex;
 pub mod os;
+pub mod os_str;
 pub mod pipe;
 pub mod process;
 pub mod rwlock;
diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs
new file mode 100644
index 00000000000..3dd89ad0759
--- /dev/null
+++ b/src/libstd/sys/unix/os_str.rs
@@ -0,0 +1,86 @@
+// Copyright 2015 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.
+
+/// The underlying OsString/OsStr implementation on Unix systems: just
+/// a `Vec<u8>`/`[u8]`.
+
+use core::prelude::*;
+
+use fmt::{self, Debug};
+use vec::Vec;
+use slice::SliceExt as StdSliceExt;
+use str;
+use string::{String, CowString};
+use mem;
+
+#[derive(Clone)]
+pub struct Buf {
+    pub inner: Vec<u8>
+}
+
+pub struct Slice {
+    pub inner: [u8]
+}
+
+impl Debug for Slice {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.to_string_lossy().fmt(formatter)
+    }
+}
+
+impl Debug for Buf {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.as_slice().fmt(formatter)
+    }
+}
+
+impl Buf {
+    pub fn from_string(s: String) -> Buf {
+        Buf { inner: s.into_bytes() }
+    }
+
+    pub fn from_str(s: &str) -> Buf {
+        Buf { inner: s.as_bytes().to_vec() }
+    }
+
+    pub fn as_slice(&self) -> &Slice {
+        unsafe { mem::transmute(self.inner.as_slice()) }
+    }
+
+    pub fn into_string(self) -> Result<String, Buf> {
+        String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
+    }
+
+    pub fn push_slice(&mut self, s: &Slice) {
+        self.inner.push_all(&s.inner)
+    }
+}
+
+impl Slice {
+    fn from_u8_slice(s: &[u8]) -> &Slice {
+        unsafe { mem::transmute(s) }
+    }
+
+    pub fn from_str(s: &str) -> &Slice {
+        unsafe { mem::transmute(s.as_bytes()) }
+    }
+
+    pub fn to_str(&self) -> Option<&str> {
+        str::from_utf8(&self.inner).ok()
+    }
+
+    pub fn to_string_lossy(&self) -> CowString {
+        String::from_utf8_lossy(&self.inner)
+    }
+
+    pub fn to_owned(&self) -> Buf {
+        Buf { inner: self.inner.to_vec() }
+    }
+}
diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs
index 87ff31ab73c..de37c428288 100644
--- a/src/libstd/sys/windows/ext.rs
+++ b/src/libstd/sys/windows/ext.rs
@@ -16,7 +16,11 @@
 
 #![unstable]
 
-use sys_common::AsInner;
+pub use sys_common::wtf8::{Wtf8Buf, EncodeWide};
+
+use sys::os_str::Buf;
+use sys_common::{AsInner, FromInner};
+use ffi::{OsStr, OsString};
 use libc;
 
 use io;
@@ -92,9 +96,35 @@ impl AsRawSocket for io::net::udp::UdpSocket {
     }
 }
 
+// Windows-specific extensions to `OsString`.
+pub trait OsStringExt {
+    /// Create an `OsString` from a potentially ill-formed UTF-16 slice of 16-bit code units.
+    ///
+    /// This is lossless: calling `.encode_wide()` on the resulting string
+    /// will always return the original code units.
+    fn from_wide(wide: &[u16]) -> Self;
+}
+
+impl OsStringExt for OsString {
+    fn from_wide(wide: &[u16]) -> OsString {
+        FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) })
+    }
+}
+
+// Windows-specific extensions to `OsStr`.
+pub trait OsStrExt {
+    fn encode_wide(&self) -> EncodeWide;
+}
+
+impl OsStrExt for OsStr {
+    fn encode_wide(&self) -> EncodeWide {
+        self.as_inner().inner.encode_wide()
+    }
+}
+
 /// A prelude for conveniently writing platform-specific code.
 ///
 /// Includes all extension traits, and some important type definitions.
 pub mod prelude {
-    pub use super::{Socket, Handle, AsRawSocket, AsRawHandle};
+    pub use super::{Socket, Handle, AsRawSocket, AsRawHandle, OsStrExt, OsStringExt};
 }
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 72fc2f8700d..876159623ac 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -44,6 +44,7 @@ pub mod fs;
 pub mod helper_signal;
 pub mod mutex;
 pub mod os;
+pub mod os_str;
 pub mod pipe;
 pub mod process;
 pub mod rwlock;
diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs
new file mode 100644
index 00000000000..aab2406cef9
--- /dev/null
+++ b/src/libstd/sys/windows/os_str.rs
@@ -0,0 +1,82 @@
+// Copyright 2015 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.
+
+/// The underlying OsString/OsStr implementation on Windows is a
+/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more.
+
+use fmt::{self, Debug};
+use sys_common::wtf8::{Wtf8, Wtf8Buf};
+use string::{String, CowString};
+use result::Result;
+use option::Option;
+use mem;
+
+#[derive(Clone)]
+pub struct Buf {
+    pub inner: Wtf8Buf
+}
+
+impl Debug for Buf {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.as_slice().fmt(formatter)
+    }
+}
+
+pub struct Slice {
+    pub inner: Wtf8
+}
+
+impl Debug for Slice {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        self.inner.fmt(formatter)
+    }
+}
+
+impl Buf {
+    pub fn from_string(s: String) -> Buf {
+        Buf { inner: Wtf8Buf::from_string(s) }
+    }
+
+    pub fn from_str(s: &str) -> Buf {
+        Buf { inner: Wtf8Buf::from_str(s) }
+    }
+
+    pub fn as_slice(&self) -> &Slice {
+        unsafe { mem::transmute(self.inner.as_slice()) }
+    }
+
+    pub fn into_string(self) -> Result<String, Buf> {
+        self.inner.into_string().map_err(|buf| Buf { inner: buf })
+    }
+
+    pub fn push_slice(&mut self, s: &Slice) {
+        self.inner.push_wtf8(&s.inner)
+    }
+}
+
+impl Slice {
+    pub fn from_str(s: &str) -> &Slice {
+        unsafe { mem::transmute(Wtf8::from_str(s)) }
+    }
+
+    pub fn to_str(&self) -> Option<&str> {
+        self.inner.as_str()
+    }
+
+    pub fn to_string_lossy(&self) -> CowString {
+        self.inner.to_string_lossy()
+    }
+
+    pub fn to_owned(&self) -> Buf {
+        let mut buf = Wtf8Buf::with_capacity(self.inner.len());
+        buf.push_wtf8(&self.inner);
+        Buf { inner: buf }
+    }
+}

From f67e7470b31a38e80c9f1007e4182ff5b56db2f7 Mon Sep 17 00:00:00 2001
From: Alfie John <alfiej@fastmail.fm>
Date: Thu, 22 Jan 2015 14:08:56 +0000
Subject: [PATCH 32/33] Moving away from deprecated i/u suffixes in libcore

---
 src/libcore/atomic.rs    | 20 ++++++-------
 src/libcore/cell.rs      |  8 +++---
 src/libcore/char.rs      | 16 +++++------
 src/libcore/cmp.rs       | 16 +++++------
 src/libcore/default.rs   | 20 ++++++-------
 src/libcore/fmt/float.rs |  6 ++--
 src/libcore/fmt/mod.rs   |  6 ++--
 src/libcore/fmt/num.rs   |  2 +-
 src/libcore/hash/sip.rs  |  2 +-
 src/libcore/iter.rs      | 10 +++----
 src/libcore/macros.rs    | 10 +++----
 src/libcore/mem.rs       | 14 ++++-----
 src/libcore/num/f32.rs   |  6 ++--
 src/libcore/num/f64.rs   |  6 ++--
 src/libcore/num/mod.rs   |  8 +++---
 src/libcore/option.rs    | 62 ++++++++++++++++++++--------------------
 src/libcore/result.rs    | 26 ++++++++---------
 src/libcore/slice.rs     |  8 +++---
 src/libcore/str/mod.rs   | 10 +++----
 19 files changed, 128 insertions(+), 128 deletions(-)

diff --git a/src/libcore/atomic.rs b/src/libcore/atomic.rs
index 18f7fff9053..e75481198d4 100644
--- a/src/libcore/atomic.rs
+++ b/src/libcore/atomic.rs
@@ -589,7 +589,7 @@ impl AtomicUsize {
     /// ```
     /// use std::sync::atomic::AtomicUsize;
     ///
-    /// let atomic_forty_two = AtomicUsize::new(42u);
+    /// let atomic_forty_two = AtomicUsize::new(42);
     /// ```
     #[inline]
     pub fn new(v: usize) -> AtomicUsize {
@@ -765,7 +765,7 @@ impl<T> AtomicPtr<T> {
     /// ```
     /// use std::sync::atomic::AtomicPtr;
     ///
-    /// let ptr = &mut 5i;
+    /// let ptr = &mut 5;
     /// let atomic_ptr  = AtomicPtr::new(ptr);
     /// ```
     #[inline]
@@ -787,7 +787,7 @@ impl<T> AtomicPtr<T> {
     /// ```
     /// use std::sync::atomic::{AtomicPtr, Ordering};
     ///
-    /// let ptr = &mut 5i;
+    /// let ptr = &mut 5;
     /// let some_ptr  = AtomicPtr::new(ptr);
     ///
     /// let value = some_ptr.load(Ordering::Relaxed);
@@ -809,10 +809,10 @@ impl<T> AtomicPtr<T> {
     /// ```
     /// use std::sync::atomic::{AtomicPtr, Ordering};
     ///
-    /// let ptr = &mut 5i;
+    /// let ptr = &mut 5;
     /// let some_ptr  = AtomicPtr::new(ptr);
     ///
-    /// let other_ptr = &mut 10i;
+    /// let other_ptr = &mut 10;
     ///
     /// some_ptr.store(other_ptr, Ordering::Relaxed);
     /// ```
@@ -835,10 +835,10 @@ impl<T> AtomicPtr<T> {
     /// ```
     /// use std::sync::atomic::{AtomicPtr, Ordering};
     ///
-    /// let ptr = &mut 5i;
+    /// let ptr = &mut 5;
     /// let some_ptr  = AtomicPtr::new(ptr);
     ///
-    /// let other_ptr = &mut 10i;
+    /// let other_ptr = &mut 10;
     ///
     /// let value = some_ptr.swap(other_ptr, Ordering::Relaxed);
     /// ```
@@ -860,11 +860,11 @@ impl<T> AtomicPtr<T> {
     /// ```
     /// use std::sync::atomic::{AtomicPtr, Ordering};
     ///
-    /// let ptr = &mut 5i;
+    /// let ptr = &mut 5;
     /// let some_ptr  = AtomicPtr::new(ptr);
     ///
-    /// let other_ptr   = &mut 10i;
-    /// let another_ptr = &mut 10i;
+    /// let other_ptr   = &mut 10;
+    /// let another_ptr = &mut 10;
     ///
     /// let value = some_ptr.compare_and_swap(other_ptr, another_ptr, Ordering::Relaxed);
     /// ```
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index 7f73be9eb5f..94307166ad5 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -67,10 +67,10 @@
 //!
 //! fn main() {
 //!     let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new()));
-//!     shared_map.borrow_mut().insert("africa", 92388i);
-//!     shared_map.borrow_mut().insert("kyoto", 11837i);
-//!     shared_map.borrow_mut().insert("piccadilly", 11826i);
-//!     shared_map.borrow_mut().insert("marbles", 38i);
+//!     shared_map.borrow_mut().insert("africa", 92388);
+//!     shared_map.borrow_mut().insert("kyoto", 11837);
+//!     shared_map.borrow_mut().insert("piccadilly", 11826);
+//!     shared_map.borrow_mut().insert("marbles", 38);
 //! }
 //! ```
 //!
diff --git a/src/libcore/char.rs b/src/libcore/char.rs
index 0e6b634bd11..c17f7cdccba 100644
--- a/src/libcore/char.rs
+++ b/src/libcore/char.rs
@@ -102,7 +102,7 @@ pub fn from_digit(num: uint, radix: uint) -> Option<char> {
             if num < 10 {
                 Some(transmute(('0' as uint + num) as u32))
             } else {
-                Some(transmute(('a' as uint + num - 10u) as u32))
+                Some(transmute(('a' as uint + num - 10) as u32))
             }
         }
     } else {
@@ -208,8 +208,8 @@ impl CharExt for char {
         }
         let val = match self {
           '0' ... '9' => self as uint - ('0' as uint),
-          'a' ... 'z' => self as uint + 10u - ('a' as uint),
-          'A' ... 'Z' => self as uint + 10u - ('A' as uint),
+          'a' ... 'z' => self as uint + 10 - ('a' as uint),
+          'A' ... 'Z' => self as uint + 10 - ('A' as uint),
           _ => return None,
         };
         if val < radix { Some(val) }
@@ -241,10 +241,10 @@ impl CharExt for char {
     fn len_utf8(self) -> uint {
         let code = self as u32;
         match () {
-            _ if code < MAX_ONE_B   => 1u,
-            _ if code < MAX_TWO_B   => 2u,
-            _ if code < MAX_THREE_B => 3u,
-            _  => 4u,
+            _ if code < MAX_ONE_B   => 1,
+            _ if code < MAX_TWO_B   => 2,
+            _ if code < MAX_THREE_B => 3,
+            _  => 4,
         }
     }
 
@@ -359,7 +359,7 @@ impl Iterator for EscapeUnicode {
                 Some('u')
             }
             EscapeUnicodeState::LeftBrace => {
-                let mut n = 0u;
+                let mut n = 0;
                 while (self.c as u32) >> (4 * (n + 1)) != 0 {
                     n += 1;
                 }
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index c1f1997df74..6cda9d0e653 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -110,13 +110,13 @@ pub trait Eq: PartialEq<Self> {
 pub enum Ordering {
     /// An ordering where a compared value is less [than another].
     #[stable]
-    Less = -1i,
+    Less = -1,
     /// An ordering where a compared value is equal [to another].
     #[stable]
-    Equal = 0i,
+    Equal = 0,
     /// An ordering where a compared value is greater [than another].
     #[stable]
-    Greater = 1i,
+    Greater = 1,
 }
 
 impl Ordering {
@@ -132,12 +132,12 @@ impl Ordering {
     /// assert_eq!(Equal.reverse(), Equal);
     /// assert_eq!(Greater.reverse(), Less);
     ///
-    /// let mut data: &mut [_] = &mut [2u, 10, 5, 8];
+    /// let mut data: &mut [_] = &mut [2, 10, 5, 8];
     ///
     /// // sort the array from largest to smallest.
     /// data.sort_by(|a, b| a.cmp(b).reverse());
     ///
-    /// let b: &mut [_] = &mut [10u, 8, 5, 2];
+    /// let b: &mut [_] = &mut [10, 8, 5, 2];
     /// assert!(data == b);
     /// ```
     #[inline]
@@ -174,9 +174,9 @@ pub trait Ord: Eq + PartialOrd<Self> {
     /// ```
     /// use std::cmp::Ordering::{Less, Equal, Greater};
     ///
-    /// assert_eq!( 5u.cmp(&10), Less);     // because 5 < 10
-    /// assert_eq!(10u.cmp(&5),  Greater);  // because 10 > 5
-    /// assert_eq!( 5u.cmp(&5),  Equal);    // because 5 == 5
+    /// assert_eq!( 5.cmp(&10), Less);     // because 5 < 10
+    /// assert_eq!(10.cmp(&5),  Greater);  // because 10 > 5
+    /// assert_eq!( 5.cmp(&5),  Equal);    // because 5 == 5
     /// ```
     #[stable]
     fn cmp(&self, other: &Self) -> Ordering;
diff --git a/src/libcore/default.rs b/src/libcore/default.rs
index 8d4ecf72243..3b1e5e23150 100644
--- a/src/libcore/default.rs
+++ b/src/libcore/default.rs
@@ -150,17 +150,17 @@ default_impl! { (), () }
 default_impl! { bool, false }
 default_impl! { char, '\x00' }
 
-default_impl! { uint, 0u }
-default_impl! { u8,  0u8 }
-default_impl! { u16, 0u16 }
-default_impl! { u32, 0u32 }
-default_impl! { u64, 0u64 }
+default_impl! { uint, 0 }
+default_impl! { u8, 0 }
+default_impl! { u16, 0 }
+default_impl! { u32, 0 }
+default_impl! { u64, 0 }
 
-default_impl! { int, 0i }
-default_impl! { i8,  0i8 }
-default_impl! { i16, 0i16 }
-default_impl! { i32, 0i32 }
-default_impl! { i64, 0i64 }
+default_impl! { int, 0 }
+default_impl! { i8, 0 }
+default_impl! { i16, 0 }
+default_impl! { i32, 0 }
+default_impl! { i64, 0 }
 
 default_impl! { f32, 0.0f32 }
 default_impl! { f64, 0.0f64 }
diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs
index 245dc00d838..50123499eba 100644
--- a/src/libcore/fmt/float.rs
+++ b/src/libcore/fmt/float.rs
@@ -53,7 +53,7 @@ pub enum SignFormat {
     SignNeg
 }
 
-static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
+static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11;
 
 /// Converts a number to its string representation as a byte vector.
 /// This is meant to be a common base implementation for all numeric string
@@ -191,7 +191,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
     if deccum != _0 || (limit_digits && exact && digit_count > 0) {
         buf[end] = b'.';
         end += 1;
-        let mut dig = 0u;
+        let mut dig = 0;
 
         // calculate new digits while
         // - there is no limit and there are digits left
@@ -218,7 +218,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
 
             // Decrease the deccumulator one fractional digit at a time
             deccum = deccum.fract();
-            dig += 1u;
+            dig += 1;
         }
 
         // If digits are limited, and that limit has been reached,
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 0e8d31a62ee..17eff98d429 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -560,8 +560,8 @@ impl<'a> Formatter<'a> {
         };
 
         let (pre_pad, post_pad) = match align {
-            rt::AlignLeft => (0u, padding),
-            rt::AlignRight | rt::AlignUnknown => (padding, 0u),
+            rt::AlignLeft => (0, padding),
+            rt::AlignRight | rt::AlignUnknown => (padding, 0),
             rt::AlignCenter => (padding / 2, (padding + 1) / 2),
         };
 
@@ -846,7 +846,7 @@ macro_rules! tuple {
             fn fmt(&self, f: &mut Formatter) -> Result {
                 try!(write!(f, "("));
                 let ($(ref $name,)*) = *self;
-                let mut n = 0i;
+                let mut n = 0;
                 $(
                     if n > 0 {
                         try!(write!(f, ", "));
diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs
index c456b3379e8..a659a9988bb 100644
--- a/src/libcore/fmt/num.rs
+++ b/src/libcore/fmt/num.rs
@@ -145,7 +145,7 @@ pub struct RadixFmt<T, R>(T, R);
 ///
 /// ```
 /// use std::fmt::radix;
-/// assert_eq!(format!("{}", radix(55i, 36)), "1j".to_string());
+/// assert_eq!(format!("{}", radix(55, 36)), "1j".to_string());
 /// ```
 #[unstable = "may be renamed or move to a different module"]
 pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs
index c20fb8457d2..8840eaf5fbc 100644
--- a/src/libcore/hash/sip.rs
+++ b/src/libcore/hash/sip.rs
@@ -122,7 +122,7 @@ impl Writer for SipHasher {
         let length = msg.len();
         self.length += length;
 
-        let mut needed = 0u;
+        let mut needed = 0;
 
         if self.ntail != 0 {
             needed = 8 - self.ntail;
diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs
index 2673cf1af78..591822d6e1b 100644
--- a/src/libcore/iter.rs
+++ b/src/libcore/iter.rs
@@ -33,7 +33,7 @@
 //! translated to the `loop` below.
 //!
 //! ```
-//! let values = vec![1i, 2, 3];
+//! let values = vec![1, 2, 3];
 //!
 //! // "Syntactical sugar" taking advantage of an iterator
 //! for &x in values.iter() {
@@ -615,7 +615,7 @@ pub trait IteratorExt: Iterator + Sized {
     /// # Examples
     ///
     /// ```
-    /// let a = [1i, 2, 3, 4, 5];
+    /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().all(|x| *x > 0));
     /// assert!(!a.iter().all(|x| *x > 2));
     /// ```
@@ -1141,7 +1141,7 @@ pub trait AdditiveIterator<A> {
     /// ```
     /// use std::iter::AdditiveIterator;
     ///
-    /// let a = [1i, 2, 3, 4, 5];
+    /// let a = [1i32, 2, 3, 4, 5];
     /// let mut it = a.iter().map(|&x| x);
     /// assert!(it.sum() == 15);
     /// ```
@@ -1183,7 +1183,7 @@ pub trait MultiplicativeIterator<A> {
     /// use std::iter::{count, MultiplicativeIterator};
     ///
     /// fn factorial(n: usize) -> usize {
-    ///     count(1u, 1).take_while(|&i| i <= n).product()
+    ///     count(1, 1).take_while(|&i| i <= n).product()
     /// }
     /// assert!(factorial(0) == 1);
     /// assert!(factorial(1) == 1);
@@ -2526,7 +2526,7 @@ pub struct Range<A> {
 /// ```
 /// let array = [0, 1, 2, 3, 4];
 ///
-/// for i in range(0, 5u) {
+/// for i in range(0, 5) {
 ///     println!("{}", i);
 ///     assert_eq!(i,  array[i]);
 /// }
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index 1c37126e8e9..d44eaae820f 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -48,7 +48,7 @@ macro_rules! panic {
 /// let x = true;
 /// assert!(x, "x wasn't true!");
 ///
-/// let a = 3i; let b = 27i;
+/// let a = 3; let b = 27;
 /// assert!(a + b == 30, "a = {}, b = {}", a, b);
 /// ```
 #[macro_export]
@@ -74,8 +74,8 @@ macro_rules! assert {
 /// # Example
 ///
 /// ```
-/// let a = 3i;
-/// let b = 1i + 2i;
+/// let a = 3;
+/// let b = 1 + 2;
 /// assert_eq!(a, b);
 /// ```
 #[macro_export]
@@ -141,8 +141,8 @@ macro_rules! debug_assert {
 /// # Example
 ///
 /// ```
-/// let a = 3i;
-/// let b = 1i + 2i;
+/// let a = 3;
+/// let b = 1 + 2;
 /// debug_assert_eq!(a, b);
 /// ```
 #[macro_export]
diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs
index 8438c9b206e..608a78325b3 100644
--- a/src/libcore/mem.rs
+++ b/src/libcore/mem.rs
@@ -187,13 +187,13 @@ pub unsafe fn uninitialized<T>() -> T {
 /// ```
 /// use std::mem;
 ///
-/// let x = &mut 5i;
-/// let y = &mut 42i;
+/// let x = &mut 5;
+/// let y = &mut 42;
 ///
 /// mem::swap(x, y);
 ///
-/// assert_eq!(42i, *x);
-/// assert_eq!(5i, *y);
+/// assert_eq!(42, *x);
+/// assert_eq!(5, *y);
 /// ```
 #[inline]
 #[stable]
@@ -277,7 +277,7 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T {
 /// ```
 /// use std::cell::RefCell;
 ///
-/// let x = RefCell::new(1i);
+/// let x = RefCell::new(1);
 ///
 /// let mut mutable_borrow = x.borrow_mut();
 /// *mutable_borrow = 1;
@@ -306,9 +306,9 @@ pub fn drop<T>(_x: T) { }
 /// ```
 /// use std::mem;
 ///
-/// let one = unsafe { mem::transmute_copy(&1i) };
+/// let one = unsafe { mem::transmute_copy(&1) };
 ///
-/// assert_eq!(1u, one);
+/// assert_eq!(1, one);
 /// ```
 #[inline]
 #[stable]
diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index f8a85e788e9..2186101bc2f 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -23,12 +23,12 @@ use num::FpCategory as Fp;
 use option::Option;
 
 #[unstable = "pending integer conventions"]
-pub const RADIX: uint = 2u;
+pub const RADIX: uint = 2;
 
 #[unstable = "pending integer conventions"]
-pub const MANTISSA_DIGITS: uint = 24u;
+pub const MANTISSA_DIGITS: uint = 24;
 #[unstable = "pending integer conventions"]
-pub const DIGITS: uint = 6u;
+pub const DIGITS: uint = 6;
 
 #[stable]
 pub const EPSILON: f32 = 1.19209290e-07_f32;
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 840428179cd..a7f0266b095 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -27,11 +27,11 @@ use option::Option;
 // members of `Bounded` and `Float`.
 
 #[unstable = "pending integer conventions"]
-pub const RADIX: uint = 2u;
+pub const RADIX: uint = 2;
 
-pub const MANTISSA_DIGITS: uint = 53u;
+pub const MANTISSA_DIGITS: uint = 53;
 #[unstable = "pending integer conventions"]
-pub const DIGITS: uint = 15u;
+pub const DIGITS: uint = 15;
 
 #[stable]
 pub const EPSILON: f64 = 2.2204460492503131e-16_f64;
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index 599a5515e3b..910c9e6c670 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -356,7 +356,7 @@ pub trait Int
     /// ```rust
     /// use std::num::Int;
     ///
-    /// assert_eq!(2i.pow(4), 16);
+    /// assert_eq!(2.pow(4), 16);
     /// ```
     #[unstable = "pending integer conventions"]
     #[inline]
@@ -1185,7 +1185,7 @@ impl_from_primitive! { f64, to_f64 }
 /// ```
 /// use std::num;
 ///
-/// let twenty: f32 = num::cast(0x14i).unwrap();
+/// let twenty: f32 = num::cast(0x14).unwrap();
 /// assert_eq!(twenty, 20f32);
 /// ```
 ///
@@ -1571,8 +1571,8 @@ macro_rules! from_str_radix_float_impl {
                 let exp = match exp_info {
                     Some((c, offset)) => {
                         let base = match c {
-                            'E' | 'e' if radix == 10 => 10u as $T,
-                            'P' | 'p' if radix == 16 => 2u as $T,
+                            'E' | 'e' if radix == 10 => 10.0,
+                            'P' | 'p' if radix == 16 => 2.0,
                             _ => return None,
                         };
 
diff --git a/src/libcore/option.rs b/src/libcore/option.rs
index af7fc875389..3ee2aa678e9 100644
--- a/src/libcore/option.rs
+++ b/src/libcore/option.rs
@@ -254,12 +254,12 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let mut x = Some(2u);
+    /// let mut x = Some(2);
     /// match x.as_mut() {
     ///     Some(v) => *v = 42,
     ///     None => {},
     /// }
-    /// assert_eq!(x, Some(42u));
+    /// assert_eq!(x, Some(42));
     /// ```
     #[inline]
     #[stable]
@@ -384,9 +384,9 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let k = 10u;
-    /// assert_eq!(Some(4u).unwrap_or_else(|| 2 * k), 4u);
-    /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20u);
+    /// let k = 10i32;
+    /// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
+    /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20);
     /// ```
     #[inline]
     #[stable]
@@ -427,10 +427,10 @@ impl<T> Option<T> {
     ///
     /// ```
     /// let x = Some("foo");
-    /// assert_eq!(x.map_or(42u, |v| v.len()), 3u);
+    /// assert_eq!(x.map_or(42, |v| v.len()), 3);
     ///
     /// let x: Option<&str> = None;
-    /// assert_eq!(x.map_or(42u, |v| v.len()), 42u);
+    /// assert_eq!(x.map_or(42, |v| v.len()), 42);
     /// ```
     #[inline]
     #[stable]
@@ -446,13 +446,13 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let k = 21u;
+    /// let k = 21;
     ///
     /// let x = Some("foo");
-    /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3u);
+    /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);
     ///
     /// let x: Option<&str> = None;
-    /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42u);
+    /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);
     /// ```
     #[inline]
     #[stable]
@@ -470,10 +470,10 @@ impl<T> Option<T> {
     ///
     /// ```
     /// let x = Some("foo");
-    /// assert_eq!(x.ok_or(0i), Ok("foo"));
+    /// assert_eq!(x.ok_or(0), Ok("foo"));
     ///
     /// let x: Option<&str> = None;
-    /// assert_eq!(x.ok_or(0i), Err(0i));
+    /// assert_eq!(x.ok_or(0), Err(0));
     /// ```
     #[inline]
     #[unstable]
@@ -491,10 +491,10 @@ impl<T> Option<T> {
     ///
     /// ```
     /// let x = Some("foo");
-    /// assert_eq!(x.ok_or_else(|| 0i), Ok("foo"));
+    /// assert_eq!(x.ok_or_else(|| 0), Ok("foo"));
     ///
     /// let x: Option<&str> = None;
-    /// assert_eq!(x.ok_or_else(|| 0i), Err(0i));
+    /// assert_eq!(x.ok_or_else(|| 0), Err(0));
     /// ```
     #[inline]
     #[unstable]
@@ -514,7 +514,7 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let x = Some(4u);
+    /// let x = Some(4);
     /// assert_eq!(x.iter().next(), Some(&4));
     ///
     /// let x: Option<uint> = None;
@@ -531,9 +531,9 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let mut x = Some(4u);
+    /// let mut x = Some(4);
     /// match x.iter_mut().next() {
-    ///     Some(&mut ref mut v) => *v = 42u,
+    ///     Some(&mut ref mut v) => *v = 42,
     ///     None => {},
     /// }
     /// assert_eq!(x, Some(42));
@@ -575,7 +575,7 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let x = Some(2u);
+    /// let x = Some(2);
     /// let y: Option<&str> = None;
     /// assert_eq!(x.and(y), None);
     ///
@@ -583,7 +583,7 @@ impl<T> Option<T> {
     /// let y = Some("foo");
     /// assert_eq!(x.and(y), None);
     ///
-    /// let x = Some(2u);
+    /// let x = Some(2);
     /// let y = Some("foo");
     /// assert_eq!(x.and(y), Some("foo"));
     ///
@@ -628,17 +628,17 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let x = Some(2u);
+    /// let x = Some(2);
     /// let y = None;
-    /// assert_eq!(x.or(y), Some(2u));
+    /// assert_eq!(x.or(y), Some(2));
     ///
     /// let x = None;
-    /// let y = Some(100u);
-    /// assert_eq!(x.or(y), Some(100u));
+    /// let y = Some(100);
+    /// assert_eq!(x.or(y), Some(100));
     ///
-    /// let x = Some(2u);
-    /// let y = Some(100u);
-    /// assert_eq!(x.or(y), Some(2u));
+    /// let x = Some(2);
+    /// let y = Some(100);
+    /// assert_eq!(x.or(y), Some(2));
     ///
     /// let x: Option<uint> = None;
     /// let y = None;
@@ -684,7 +684,7 @@ impl<T> Option<T> {
     /// # Example
     ///
     /// ```
-    /// let mut x = Some(2u);
+    /// let mut x = Some(2);
     /// x.take();
     /// assert_eq!(x, None);
     ///
@@ -728,8 +728,8 @@ impl<T: Default> Option<T> {
     /// let good_year = good_year_from_input.parse().unwrap_or_default();
     /// let bad_year = bad_year_from_input.parse().unwrap_or_default();
     ///
-    /// assert_eq!(1909i, good_year);
-    /// assert_eq!(0i, bad_year);
+    /// assert_eq!(1909, good_year);
+    /// assert_eq!(0, bad_year);
     /// ```
     #[inline]
     #[stable]
@@ -894,12 +894,12 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> {
     /// ```rust
     /// use std::uint;
     ///
-    /// let v = vec!(1u, 2u);
+    /// let v = vec!(1, 2);
     /// let res: Option<Vec<uint>> = v.iter().map(|&x: &uint|
     ///     if x == uint::MAX { None }
     ///     else { Some(x + 1) }
     /// ).collect();
-    /// assert!(res == Some(vec!(2u, 3u)));
+    /// assert!(res == Some(vec!(2, 3)));
     /// ```
     #[inline]
     #[stable]
diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index c3d49e24978..4ea3bf8e5bd 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -482,8 +482,8 @@ impl<T, E> Result<T, E> {
     /// ```
     /// fn stringify(x: uint) -> String { format!("error code: {}", x) }
     ///
-    /// let x: Result<uint, uint> = Ok(2u);
-    /// assert_eq!(x.map_err(stringify), Ok(2u));
+    /// let x: Result<uint, uint> = Ok(2);
+    /// assert_eq!(x.map_err(stringify), Ok(2));
     ///
     /// let x: Result<uint, uint> = Err(13);
     /// assert_eq!(x.map_err(stringify), Err("error code: 13".to_string()));
@@ -546,7 +546,7 @@ impl<T, E> Result<T, E> {
     /// ```
     /// let x: Result<uint, &str> = Ok(5);
     /// let v: Vec<uint> = x.into_iter().collect();
-    /// assert_eq!(v, vec![5u]);
+    /// assert_eq!(v, vec![5]);
     ///
     /// let x: Result<uint, &str> = Err("nothing!");
     /// let v: Vec<uint> = x.into_iter().collect();
@@ -676,9 +676,9 @@ impl<T, E> Result<T, E> {
     /// # Example
     ///
     /// ```
-    /// let optb = 2u;
-    /// let x: Result<uint, &str> = Ok(9u);
-    /// assert_eq!(x.unwrap_or(optb), 9u);
+    /// let optb = 2;
+    /// let x: Result<uint, &str> = Ok(9);
+    /// assert_eq!(x.unwrap_or(optb), 9);
     ///
     /// let x: Result<uint, &str> = Err("error");
     /// assert_eq!(x.unwrap_or(optb), optb);
@@ -700,8 +700,8 @@ impl<T, E> Result<T, E> {
     /// ```
     /// fn count(x: &str) -> uint { x.len() }
     ///
-    /// assert_eq!(Ok(2u).unwrap_or_else(count), 2u);
-    /// assert_eq!(Err("foo").unwrap_or_else(count), 3u);
+    /// assert_eq!(Ok(2).unwrap_or_else(count), 2);
+    /// assert_eq!(Err("foo").unwrap_or_else(count), 3);
     /// ```
     #[inline]
     #[stable]
@@ -725,8 +725,8 @@ impl<T, E: Display> Result<T, E> {
     /// # Example
     ///
     /// ```
-    /// let x: Result<uint, &str> = Ok(2u);
-    /// assert_eq!(x.unwrap(), 2u);
+    /// let x: Result<uint, &str> = Ok(2);
+    /// assert_eq!(x.unwrap(), 2);
     /// ```
     ///
     /// ```{.should_fail}
@@ -756,7 +756,7 @@ impl<T: Display, E> Result<T, E> {
     /// # Example
     ///
     /// ```{.should_fail}
-    /// let x: Result<uint, &str> = Ok(2u);
+    /// let x: Result<uint, &str> = Ok(2);
     /// x.unwrap_err(); // panics with `2`
     /// ```
     ///
@@ -897,12 +897,12 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
     /// ```rust
     /// use std::uint;
     ///
-    /// let v = vec!(1u, 2u);
+    /// let v = vec!(1, 2);
     /// let res: Result<Vec<uint>, &'static str> = v.iter().map(|&x: &uint|
     ///     if x == uint::MAX { Err("Overflow!") }
     ///     else { Ok(x + 1) }
     /// ).collect();
-    /// assert!(res == Ok(vec!(2u, 3u)));
+    /// assert!(res == Ok(vec!(2, 3)));
     /// ```
     #[inline]
     fn from_iter<I: Iterator<Item=Result<A, E>>>(iter: I) -> Result<V, E> {
diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs
index ec43a35248e..f08978db8fe 100644
--- a/src/libcore/slice.rs
+++ b/src/libcore/slice.rs
@@ -653,7 +653,7 @@ macro_rules! iterator {
                             self.ptr = transmute(self.ptr as uint + 1);
 
                             // Use a non-null pointer value
-                            Some(transmute(1u))
+                            Some(&mut *(1 as *mut _))
                         } else {
                             let old = self.ptr;
                             self.ptr = self.ptr.offset(1);
@@ -687,7 +687,7 @@ macro_rules! iterator {
                             self.end = transmute(self.end as uint - 1);
 
                             // Use a non-null pointer value
-                            Some(transmute(1u))
+                            Some(&mut *(1 as *mut _))
                         } else {
                             self.end = self.end.offset(-1);
 
@@ -795,7 +795,7 @@ impl<'a, T> RandomAccessIterator for Iter<'a, T> {
             if index < self.indexable() {
                 if mem::size_of::<T>() == 0 {
                     // Use a non-null pointer value
-                    Some(transmute(1u))
+                    Some(&mut *(1 as *mut _))
                 } else {
                     Some(transmute(self.ptr.offset(index as int)))
                 }
@@ -1175,7 +1175,7 @@ impl<'a, T> Iterator for Windows<'a, T> {
             (0, Some(0))
         } else {
             let x = self.v.len() - self.size;
-            (x.saturating_add(1), x.checked_add(1u))
+            (x.saturating_add(1), x.checked_add(1))
         }
     }
 }
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index 1e01da4e41d..b0227c749cc 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -198,9 +198,9 @@ pub unsafe fn from_utf8_unchecked<'a>(v: &'a [u8]) -> &'a str {
 #[deprecated = "use std::ffi::c_str_to_bytes + str::from_utf8"]
 pub unsafe fn from_c_str(s: *const i8) -> &'static str {
     let s = s as *const u8;
-    let mut len = 0u;
+    let mut len = 0;
     while *s.offset(len as int) != 0 {
-        len += 1u;
+        len += 1;
     }
     let v: &'static [u8] = ::mem::transmute(Slice { data: s, len: len });
     from_utf8(v).ok().expect("from_c_str passed invalid utf-8 data")
@@ -1510,7 +1510,7 @@ impl StrExt for str {
             None => "",
             Some(last) => {
                 let next = self.char_range_at(last).next;
-                unsafe { self.slice_unchecked(0u, next) }
+                unsafe { self.slice_unchecked(0, next) }
             }
         }
     }
@@ -1543,7 +1543,7 @@ impl StrExt for str {
         fn multibyte_char_range_at_reverse(s: &str, mut i: uint) -> CharRange {
             // while there is a previous byte == 10......
             while i > 0 && s.as_bytes()[i] & !CONT_MASK == TAG_CONT_U8 {
-                i -= 1u;
+                i -= 1;
             }
 
             let mut val = s.as_bytes()[i] as u32;
@@ -1613,7 +1613,7 @@ impl StrExt for str {
         if self.is_empty() {
             None
         } else {
-            let CharRange {ch, next} = self.char_range_at(0u);
+            let CharRange {ch, next} = self.char_range_at(0);
             let next_s = unsafe { self.slice_unchecked(next, self.len()) };
             Some((ch, next_s))
         }

From 7e83e46556fea9f448e47f7879f5970c400fb24c Mon Sep 17 00:00:00 2001
From: Flavio Percoco <flaper87@gmail.com>
Date: Sun, 25 Jan 2015 10:52:55 +0100
Subject: [PATCH 33/33] assert path ends with executable. On Windows the
 process executable contains the full path

---
 src/test/run-pass/issue-15149.rs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/test/run-pass/issue-15149.rs b/src/test/run-pass/issue-15149.rs
index 5d3571e4d74..bf395b14eb4 100644
--- a/src/test/run-pass/issue-15149.rs
+++ b/src/test/run-pass/issue-15149.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::slice::SliceExt;
 use std::io::{Command, fs, USER_RWX};
 use std::os;
 use std::path::BytesContainer;
@@ -17,8 +18,11 @@ fn main() {
     // If we're the child, make sure we were invoked correctly
     let args = os::args();
     if args.len() > 1 && args[1].as_slice() == "child" {
-        return assert_eq!(args[0],
-                          format!("mytest{}", os::consts::EXE_SUFFIX));
+        // FIXME: This should check the whole `args[0]` instead of just
+        // checking that it ends_with the executable name. This
+        // is needed because of Windows, which has a different behavior.
+        // See #15149 for more info.
+        return assert!(args[0].ends_with(&format!("mytest{}", os::consts::EXE_SUFFIX)[]));
     }
 
     test();