From 9889aae13e14c306fce8cefd669841fa40f26ee9 Mon Sep 17 00:00:00 2001
From: Eduard Burtescu <edy.burt@gmail.com>
Date: Fri, 13 Mar 2015 11:34:51 +0200
Subject: [PATCH 1/4] syntax: use lookahead to distinguish inner and outer
 attributes, instead of passing the latter around.

---
 src/libsyntax/ext/quote.rs                   |  12 +-
 src/libsyntax/ext/source_util.rs             |   2 +-
 src/libsyntax/ext/tt/macro_parser.rs         |   7 +-
 src/libsyntax/ext/tt/macro_rules.rs          |  19 +-
 src/libsyntax/parse/attr.rs                  |  58 +-
 src/libsyntax/parse/mod.rs                   |  14 +-
 src/libsyntax/parse/parser.rs                | 711 +++++++------------
 src/libsyntax/util/parser_testing.rs         |   4 +-
 src/test/parse-fail/column-offset-1-based.rs |   2 +-
 src/test/parse-fail/issue-1655.rs            |   2 +-
 src/test/run-pass-fulldeps/quote-tokens.rs   |   2 +-
 11 files changed, 323 insertions(+), 510 deletions(-)

diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index 48c045ee4f98f..c38556b078219 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -361,8 +361,7 @@ pub mod rt {
             parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
                                               s,
                                               self.cfg(),
-                                              Vec::new(),
-                                              self.parse_sess())
+                                              self.parse_sess()).expect("parse error")
         }
 
         fn parse_expr(&self, s: String) -> P<ast::Expr> {
@@ -407,7 +406,7 @@ pub fn expand_quote_expr<'cx>(cx: &'cx mut ExtCtxt,
                               sp: Span,
                               tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
-    let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts);
+    let expanded = expand_parse_call(cx, sp, "parse_expr", vec!(), tts);
     base::MacEager::expr(expanded)
 }
 
@@ -415,8 +414,7 @@ pub fn expand_quote_item<'cx>(cx: &mut ExtCtxt,
                               sp: Span,
                               tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
-    let expanded = expand_parse_call(cx, sp, "parse_item_with_outer_attributes",
-                                    vec!(), tts);
+    let expanded = expand_parse_call(cx, sp, "parse_item", vec!(), tts);
     base::MacEager::expr(expanded)
 }
 
@@ -448,9 +446,7 @@ pub fn expand_quote_stmt(cx: &mut ExtCtxt,
                          sp: Span,
                          tts: &[ast::TokenTree])
                          -> Box<base::MacResult+'static> {
-    let e_attrs = cx.expr_vec_ng(sp);
-    let expanded = expand_parse_call(cx, sp, "parse_stmt",
-                                    vec!(e_attrs), tts);
+    let expanded = expand_parse_call(cx, sp, "parse_stmt", vec!(), tts);
     base::MacEager::expr(expanded)
 }
 
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index eb0daac3ab84c..c61aec0069db2 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -115,7 +115,7 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree
                       -> Option<SmallVector<P<ast::Item>>> {
             let mut ret = SmallVector::zero();
             while self.p.token != token::Eof {
-                match self.p.parse_item_with_outer_attributes() {
+                match self.p.parse_item() {
                     Some(item) => ret.push(item),
                     None => self.p.span_fatal(
                         self.p.span,
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index eb15d708232a0..b7d40a46f3ea2 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -521,12 +521,15 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
     // check at the beginning and the parser checks after each bump
     p.check_unknown_macro_variable();
     match name {
-      "item" => match p.parse_item(Vec::new()) {
+      "item" => match p.parse_item() {
         Some(i) => token::NtItem(i),
         None => p.fatal("expected an item keyword")
       },
       "block" => token::NtBlock(p.parse_block()),
-      "stmt" => token::NtStmt(p.parse_stmt(Vec::new())),
+      "stmt" => match p.parse_stmt() {
+        Some(s) => token::NtStmt(s),
+        None => p.fatal("expected a statement")
+      },
       "pat" => token::NtPat(p.parse_pat()),
       "expr" => token::NtExpr(p.parse_expr()),
       "ty" => token::NtTy(p.parse_ty()),
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 7575d4b5ecdbe..7a2ae55e91494 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -17,7 +17,6 @@ use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
 use ext::tt::macro_parser::{parse, parse_or_else};
 use parse::lexer::new_tt_reader;
 use parse::parser::Parser;
-use parse::attr::ParserAttr;
 use parse::token::{self, special_idents, gensym_ident, NtTT, Token};
 use parse::token::Token::*;
 use print;
@@ -68,15 +67,8 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
     }
     fn make_items(self: Box<ParserAnyMacro<'a>>) -> Option<SmallVector<P<ast::Item>>> {
         let mut ret = SmallVector::zero();
-        loop {
-            let mut parser = self.parser.borrow_mut();
-            // so... do outer attributes attached to the macro invocation
-            // just disappear? This question applies to make_impl_items, as
-            // well.
-            match parser.parse_item_with_outer_attributes() {
-                Some(item) => ret.push(item),
-                None => break
-            }
+        while let Some(item) = self.parser.borrow_mut().parse_item() {
+            ret.push(item);
         }
         self.ensure_complete_parse(false);
         Some(ret)
@@ -89,7 +81,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
             let mut parser = self.parser.borrow_mut();
             match parser.token {
                 token::Eof => break,
-                _ => ret.push(parser.parse_impl_item_with_outer_attributes())
+                _ => ret.push(parser.parse_impl_item())
             }
         }
         self.ensure_complete_parse(false);
@@ -97,10 +89,9 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
     }
 
     fn make_stmt(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Stmt>> {
-        let attrs = self.parser.borrow_mut().parse_outer_attributes();
-        let ret = self.parser.borrow_mut().parse_stmt(attrs);
+        let ret = self.parser.borrow_mut().parse_stmt();
         self.ensure_complete_parse(true);
-        Some(ret)
+        ret
     }
 }
 
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index db5583cf13ac1..a5dd4f222241d 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -19,9 +19,8 @@ use ptr::P;
 /// A parser that can parse attributes.
 pub trait ParserAttr {
     fn parse_outer_attributes(&mut self) -> Vec<ast::Attribute>;
+    fn parse_inner_attributes(&mut self) -> Vec<ast::Attribute>;
     fn parse_attribute(&mut self, permit_inner: bool) -> ast::Attribute;
-    fn parse_inner_attrs_and_next(&mut self)
-                                  -> (Vec<ast::Attribute>, Vec<ast::Attribute>);
     fn parse_meta_item(&mut self) -> P<ast::MetaItem>;
     fn parse_meta_seq(&mut self) -> Vec<P<ast::MetaItem>>;
     fn parse_optional_meta(&mut self) -> Vec<P<ast::MetaItem>>;
@@ -118,45 +117,40 @@ impl<'a> ParserAttr for Parser<'a> {
 
     /// Parse attributes that appear after the opening of an item. These should
     /// be preceded by an exclamation mark, but we accept and warn about one
-    /// terminated by a semicolon. In addition to a vector of inner attributes,
-    /// this function also returns a vector that may contain the first outer
-    /// attribute of the next item (since we can't know whether the attribute
-    /// is an inner attribute of the containing item or an outer attribute of
-    /// the first contained item until we see the semi).
-
-    /// matches inner_attrs* outer_attr?
-    /// you can make the 'next' field an Option, but the result is going to be
-    /// more useful as a vector.
-    fn parse_inner_attrs_and_next(&mut self)
-                                  -> (Vec<ast::Attribute> , Vec<ast::Attribute> ) {
-        let mut inner_attrs: Vec<ast::Attribute> = Vec::new();
-        let mut next_outer_attrs: Vec<ast::Attribute> = Vec::new();
+    /// terminated by a semicolon.
+
+    /// matches inner_attrs*
+    fn parse_inner_attributes(&mut self) -> Vec<ast::Attribute> {
+        let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
-            let attr = match self.token {
+            match self.token {
                 token::Pound => {
-                    self.parse_attribute(true)
+                    // Don't even try to parse if it's not an inner attribute.
+                    if !self.look_ahead(1, |t| t == &token::Not) {
+                        break;
+                    }
+
+                    let attr = self.parse_attribute(true);
+                    assert!(attr.node.style == ast::AttrInner);
+                    attrs.push(attr);
                 }
                 token::DocComment(s) => {
                     // we need to get the position of this token before we bump.
                     let Span { lo, hi, .. } = self.span;
-                    self.bump();
-                    attr::mk_sugared_doc_attr(attr::mk_attr_id(),
-                                              self.id_to_interned_str(s.ident()),
-                                              lo,
-                                              hi)
-                }
-                _ => {
-                    break;
+                    let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(),
+                                                         self.id_to_interned_str(s.ident()),
+                                                         lo, hi);
+                    if attr.node.style == ast::AttrInner {
+                        attrs.push(attr);
+                        self.bump();
+                    } else {
+                        break;
+                    }
                 }
-            };
-            if attr.node.style == ast::AttrInner {
-                inner_attrs.push(attr);
-            } else {
-                next_outer_attrs.push(attr);
-                break;
+                _ => break
             }
         }
-        (inner_attrs, next_outer_attrs)
+        attrs
     }
 
     /// matches meta_item = IDENT
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index fae305f955174..3e02b4e0752c0 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -96,9 +96,7 @@ pub fn parse_crate_attrs_from_file(
     cfg: ast::CrateConfig,
     sess: &ParseSess
 ) -> Vec<ast::Attribute> {
-    let mut parser = new_parser_from_file(sess, cfg, input);
-    let (inner, _) = parser.parse_inner_attrs_and_next();
-    inner
+    new_parser_from_file(sess, cfg, input).parse_inner_attributes()
 }
 
 pub fn parse_crate_from_source_str(name: String,
@@ -122,8 +120,7 @@ pub fn parse_crate_attrs_from_source_str(name: String,
                                            cfg,
                                            name,
                                            source);
-    let (inner, _) = maybe_aborted(p.parse_inner_attrs_and_next(),p);
-    inner
+    maybe_aborted(p.parse_inner_attributes(), p)
 }
 
 pub fn parse_expr_from_source_str(name: String,
@@ -141,7 +138,7 @@ pub fn parse_item_from_source_str(name: String,
                                   sess: &ParseSess)
                                   -> Option<P<ast::Item>> {
     let mut p = new_parser_from_source_str(sess, cfg, name, source);
-    maybe_aborted(p.parse_item_with_outer_attributes(),p)
+    maybe_aborted(p.parse_item(),p)
 }
 
 pub fn parse_meta_from_source_str(name: String,
@@ -156,16 +153,15 @@ pub fn parse_meta_from_source_str(name: String,
 pub fn parse_stmt_from_source_str(name: String,
                                   source: String,
                                   cfg: ast::CrateConfig,
-                                  attrs: Vec<ast::Attribute> ,
                                   sess: &ParseSess)
-                                  -> P<ast::Stmt> {
+                                  -> Option<P<ast::Stmt>> {
     let mut p = new_parser_from_source_str(
         sess,
         cfg,
         name,
         source
     );
-    maybe_aborted(p.parse_stmt(attrs),p)
+    maybe_aborted(p.parse_stmt(), p)
 }
 
 // Note: keep in sync with `with_hygiene::parse_tts_from_source_str`
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ea1c17055141a..450c38e83b4d0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -39,7 +39,7 @@ use ast::{LitStr, LitInt, Local, LocalLet};
 use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
 use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
 use ast::{MutTy, BiMul, Mutability};
-use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, NodeId, UnNot};
+use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
 use ast::{Pat, PatEnum, PatIdent, PatLit, PatRange, PatRegion, PatStruct};
 use ast::{PatTup, PatBox, PatWild, PatWildMulti, PatWildSingle};
 use ast::{PolyTraitRef, QSelf};
@@ -117,11 +117,6 @@ pub enum BoundParsingMode {
     Modified,
 }
 
-/// The `Err` case indicates a failure to parse any kind of item.
-/// The attributes are returned.
-type MaybeItem = Result<P<Item>, Vec<Attribute>>;
-
-
 /// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
 /// dropped into the token stream, which happens while parsing the result of
 /// macro expansion). Placement of these is not as complex as I feared it would
@@ -208,7 +203,7 @@ macro_rules! maybe_whole {
             }
         }
     );
-    (Some $p:expr, $constructor:ident) => (
+    (Some deref $p:expr, $constructor:ident) => (
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
@@ -217,7 +212,7 @@ macro_rules! maybe_whole {
                 _ => None
             };
             if let Some(token::Interpolated(token::$constructor(x))) = found {
-                return Some(x.clone());
+                return Some((*x).clone());
             }
         }
     );
@@ -1240,41 +1235,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parses `type Foo;` in a trait declaration only. The `type` keyword has
-    /// already been parsed.
-    fn parse_assoc_ty_in_trait(&mut self, attrs: Vec<Attribute>)
-                               -> P<TraitItem> {
-        let TyParam {id, ident, bounds, default, span} = self.parse_ty_param();
-        self.expect(&token::Semi);
-        P(TraitItem {
-            id: id,
-            ident: ident,
-            attrs: attrs,
-            node: TypeTraitItem(bounds, default),
-            span: span,
-        })
-    }
-
-    /// Parses `type Foo = TYPE;` in an implementation declaration only. The
-    /// `type` keyword has already been parsed.
-    fn parse_assoc_ty_in_impl(&mut self, attrs: Vec<Attribute>, vis: Visibility)
-                              -> P<ImplItem> {
-        let lo = self.span.lo;
-        let ident = self.parse_ident();
-        self.expect(&token::Eq);
-        let typ = self.parse_ty_sum();
-        let hi = self.span.hi;
-        self.expect(&token::Semi);
-        P(ImplItem {
-            id: ast::DUMMY_NODE_ID,
-            span: mk_sp(lo, hi),
-            ident: ident,
-            vis: vis,
-            attrs: attrs,
-            node: TypeImplItem(typ),
-        })
-    }
-
     /// Parse the items in a trait declaration
     pub fn parse_trait_items(&mut self) -> Vec<P<TraitItem>> {
         self.parse_unspanned_seq(
@@ -1282,13 +1242,14 @@ impl<'a> Parser<'a> {
             &token::CloseDelim(token::Brace),
             seq_sep_none(),
             |p| {
+            let lo = p.span.lo;
             let mut attrs = p.parse_outer_attributes();
 
-            if p.eat_keyword(keywords::Type) {
-                p.parse_assoc_ty_in_trait(attrs)
+            let (name, node) = if p.eat_keyword(keywords::Type) {
+                let TyParam {ident, bounds, default, ..} = p.parse_ty_param();
+                p.expect(&token::Semi);
+                (ident, TypeTraitItem(bounds, default))
             } else {
-                let lo = p.span.lo;
-
                 let style = p.parse_unsafety();
                 let abi = if p.eat_keyword(keywords::Extern) {
                     p.parse_opt_abi().unwrap_or(abi::C)
@@ -1316,7 +1277,6 @@ impl<'a> Parser<'a> {
                     explicit_self: explicit_self,
                 };
 
-                let hi = p.last_span.hi;
                 let body = match p.token {
                   token::Semi => {
                     p.bump();
@@ -1337,15 +1297,16 @@ impl<'a> Parser<'a> {
                                        token_str)[..])
                   }
                 };
+                (ident, ast::MethodTraitItem(sig, body))
+            };
 
-                P(TraitItem {
-                    id: ast::DUMMY_NODE_ID,
-                    ident: ident,
-                    attrs: attrs,
-                    node: ast::MethodTraitItem(sig, body),
-                    span: mk_sp(lo, hi),
-                })
-            }
+            P(TraitItem {
+                id: ast::DUMMY_NODE_ID,
+                ident: name,
+                attrs: attrs,
+                node: node,
+                span: mk_sp(lo, p.last_span.hi),
+            })
         })
     }
 
@@ -3647,41 +3608,47 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Get an expected item after attributes error message.
-    fn expected_item_err(attrs: &[Attribute]) -> &'static str {
-        match attrs.last() {
+    /// Emit an expected item after attributes error.
+    fn expected_item_err(&self, attrs: &[Attribute]) {
+        let message = match attrs.last() {
             Some(&Attribute { node: ast::Attribute_ { is_sugared_doc: true, .. }, .. }) => {
                 "expected item after doc comment"
             }
             _ => "expected item after attributes",
-        }
+        };
+
+        self.span_err(self.last_span, message);
     }
 
     /// Parse a statement. may include decl.
-    /// Precondition: any attributes are parsed already
-    pub fn parse_stmt(&mut self, item_attrs: Vec<Attribute>) -> P<Stmt> {
-        maybe_whole!(self, NtStmt);
+    pub fn parse_stmt(&mut self) -> Option<P<Stmt>> {
+        self.parse_stmt_().map(P)
+    }
+
+    fn parse_stmt_(&mut self) -> Option<Stmt> {
+        maybe_whole!(Some deref self, NtStmt);
 
         fn check_expected_item(p: &mut Parser, attrs: &[Attribute]) {
             // If we have attributes then we should have an item
             if !attrs.is_empty() {
-                let last_span = p.last_span;
-                p.span_err(last_span, Parser::expected_item_err(attrs));
+                p.expected_item_err(attrs);
             }
         }
 
         let lo = self.span.lo;
-        if self.check_keyword(keywords::Let) {
-            check_expected_item(self, &item_attrs[..]);
+        let attrs = self.parse_outer_attributes();
+
+        Some(if self.check_keyword(keywords::Let) {
+            check_expected_item(self, &attrs);
             self.expect_keyword(keywords::Let);
             let decl = self.parse_let();
-            P(spanned(lo, decl.span.hi, StmtDecl(decl, ast::DUMMY_NODE_ID)))
+            spanned(lo, decl.span.hi, StmtDecl(decl, ast::DUMMY_NODE_ID))
         } else if self.token.is_ident()
             && !self.token.is_any_keyword()
             && self.look_ahead(1, |t| *t == token::Not) {
             // it's a macro invocation:
 
-            check_expected_item(self, &item_attrs[..]);
+            check_expected_item(self, &attrs);
 
             // Potential trouble: if we allow macros with paths instead of
             // idents, we'd need to look ahead past the whole path here...
@@ -3728,12 +3695,11 @@ impl<'a> Parser<'a> {
             };
 
             if id.name == token::special_idents::invalid.name {
-                P(spanned(lo,
-                          hi,
-                          StmtMac(P(spanned(lo,
+                spanned(lo, hi,
+                        StmtMac(P(spanned(lo,
                                           hi,
                                           MacInvocTT(pth, tts, EMPTY_CTXT))),
-                                  style)))
+                                  style))
             } else {
                 // if it has a special ident, it's definitely an item
                 //
@@ -3747,35 +3713,38 @@ impl<'a> Parser<'a> {
                                        followed by a semicolon");
                     }
                 }
-                P(spanned(lo, hi, StmtDecl(
+                spanned(lo, hi, StmtDecl(
                     P(spanned(lo, hi, DeclItem(
                         self.mk_item(
                             lo, hi, id /*id is good here*/,
                             ItemMac(spanned(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT))),
                             Inherited, Vec::new(/*no attrs*/))))),
-                    ast::DUMMY_NODE_ID)))
+                    ast::DUMMY_NODE_ID))
             }
         } else {
-            let found_attrs = !item_attrs.is_empty();
-            let item_err = Parser::expected_item_err(&item_attrs[..]);
-            match self.parse_item_(item_attrs, false) {
-                Ok(i) => {
+            match self.parse_item_(attrs, false) {
+                Some(i) => {
                     let hi = i.span.hi;
                     let decl = P(spanned(lo, hi, DeclItem(i)));
-                    P(spanned(lo, hi, StmtDecl(decl, ast::DUMMY_NODE_ID)))
+                    spanned(lo, hi, StmtDecl(decl, ast::DUMMY_NODE_ID))
                 }
-                Err(_) => {
-                    if found_attrs {
-                        let last_span = self.last_span;
-                        self.span_err(last_span, item_err);
+                None => {
+                    // Do not attempt to parse an expression if we're done here.
+                    if self.token == token::Semi {
+                        self.bump();
+                        return None;
+                    }
+
+                    if self.token == token::CloseDelim(token::Brace) {
+                        return None;
                     }
 
                     // Remainder are line-expr stmts.
                     let e = self.parse_expr_res(RESTRICTION_STMT_EXPR);
-                    P(spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID)))
+                    spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID))
                 }
             }
-        }
+        })
     }
 
     /// Is this expression a successfully-parsed statement?
@@ -3798,153 +3767,110 @@ impl<'a> Parser<'a> {
                                  "place this code inside a block");
         }
 
-        return self.parse_block_tail_(lo, DefaultBlock, Vec::new());
+        self.parse_block_tail(lo, DefaultBlock)
     }
 
     /// Parse a block. Inner attrs are allowed.
-    fn parse_inner_attrs_and_block(&mut self)
-        -> (Vec<Attribute> , P<Block>) {
-
+    fn parse_inner_attrs_and_block(&mut self) -> (Vec<Attribute>, P<Block>) {
         maybe_whole!(pair_empty self, NtBlock);
 
         let lo = self.span.lo;
         self.expect(&token::OpenDelim(token::Brace));
-        let (inner, next) = self.parse_inner_attrs_and_next();
-
-        (inner, self.parse_block_tail_(lo, DefaultBlock, next))
+        (self.parse_inner_attributes(),
+         self.parse_block_tail(lo, DefaultBlock))
     }
 
+    /// Parse the rest of a block expression or function body
     /// Precondition: already parsed the '{'.
     fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> P<Block> {
-        self.parse_block_tail_(lo, s, Vec::new())
-    }
-
-    /// Parse the rest of a block expression or function body
-    fn parse_block_tail_(&mut self, lo: BytePos, s: BlockCheckMode,
-                         first_item_attrs: Vec<Attribute>) -> P<Block> {
         let mut stmts = vec![];
         let mut expr = None;
-        let mut attributes_box = first_item_attrs;
 
-        while self.token != token::CloseDelim(token::Brace) {
-            // parsing items even when they're not allowed lets us give
-            // better error messages and recover more gracefully.
-            attributes_box.push_all(&self.parse_outer_attributes());
-            match self.token {
-                token::Semi => {
-                    if !attributes_box.is_empty() {
-                        let last_span = self.last_span;
-                        self.span_err(last_span,
-                                      Parser::expected_item_err(&attributes_box[..]));
-                        attributes_box = Vec::new();
-                    }
-                    self.bump(); // empty
-                }
-                token::CloseDelim(token::Brace) => {
-                    // fall through and out.
+        while !self.eat(&token::CloseDelim(token::Brace)) {
+            let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
+                s
+            } else {
+                // Found only `;` or `}`.
+                continue;
+            };
+            match node {
+                StmtExpr(e, _) => {
+                    self.handle_expression_like_statement(e, span, &mut stmts, &mut expr);
                 }
-                _ => {
-                    let stmt = self.parse_stmt(attributes_box);
-                    attributes_box = Vec::new();
-                    stmt.and_then(|Spanned {node, span}| match node {
-                        StmtExpr(e, stmt_id) => {
-                            self.handle_expression_like_statement(e,
-                                                                  stmt_id,
-                                                                  span,
-                                                                  &mut stmts,
-                                                                  &mut expr);
+                StmtMac(mac, MacStmtWithoutBraces) => {
+                    // statement macro without braces; might be an
+                    // expr depending on whether a semicolon follows
+                    match self.token {
+                        token::Semi => {
+                            stmts.push(P(Spanned {
+                                node: StmtMac(mac, MacStmtWithSemicolon),
+                                span: span,
+                            }));
+                            self.bump();
                         }
-                        StmtMac(mac, MacStmtWithoutBraces) => {
-                            // statement macro without braces; might be an
-                            // expr depending on whether a semicolon follows
-                            match self.token {
-                                token::Semi => {
-                                    stmts.push(P(Spanned {
-                                        node: StmtMac(mac,
-                                                      MacStmtWithSemicolon),
-                                        span: span,
-                                    }));
-                                    self.bump();
-                                }
-                                _ => {
-                                    let e = self.mk_mac_expr(span.lo,
-                                                             span.hi,
-                                                             mac.and_then(|m| m.node));
-                                    let e = self.parse_dot_or_call_expr_with(e);
-                                    let e = self.parse_more_binops(e, 0);
-                                    let e = self.parse_assign_expr_with(e);
-                                    self.handle_expression_like_statement(
-                                        e,
-                                        ast::DUMMY_NODE_ID,
-                                        span,
-                                        &mut stmts,
-                                        &mut expr);
-                                }
-                            }
+                        _ => {
+                            let e = self.mk_mac_expr(span.lo, span.hi,
+                                                     mac.and_then(|m| m.node));
+                            let e = self.parse_dot_or_call_expr_with(e);
+                            let e = self.parse_more_binops(e, 0);
+                            let e = self.parse_assign_expr_with(e);
+                            self.handle_expression_like_statement(
+                                e,
+                                span,
+                                &mut stmts,
+                                &mut expr);
                         }
-                        StmtMac(m, style) => {
-                            // statement macro; might be an expr
-                            match self.token {
-                                token::Semi => {
-                                    stmts.push(P(Spanned {
-                                        node: StmtMac(m,
-                                                      MacStmtWithSemicolon),
-                                        span: span,
-                                    }));
-                                    self.bump();
-                                }
-                                token::CloseDelim(token::Brace) => {
-                                    // if a block ends in `m!(arg)` without
-                                    // a `;`, it must be an expr
-                                    expr = Some(
-                                        self.mk_mac_expr(span.lo,
-                                                         span.hi,
+                    }
+                }
+                StmtMac(m, style) => {
+                    // statement macro; might be an expr
+                    match self.token {
+                        token::Semi => {
+                            stmts.push(P(Spanned {
+                                node: StmtMac(m, MacStmtWithSemicolon),
+                                span: span,
+                            }));
+                            self.bump();
+                        }
+                        token::CloseDelim(token::Brace) => {
+                            // if a block ends in `m!(arg)` without
+                            // a `;`, it must be an expr
+                            expr = Some(self.mk_mac_expr(span.lo, span.hi,
                                                          m.and_then(|x| x.node)));
-                                }
-                                _ => {
-                                    stmts.push(P(Spanned {
-                                        node: StmtMac(m, style),
-                                        span: span
-                                    }));
-                                }
-                            }
                         }
-                        _ => { // all other kinds of statements:
-                            if classify::stmt_ends_with_semi(&node) {
-                                self.commit_stmt_expecting(token::Semi);
-                            }
-
+                        _ => {
                             stmts.push(P(Spanned {
-                                node: node,
+                                node: StmtMac(m, style),
                                 span: span
                             }));
                         }
-                    })
+                    }
                 }
-            }
-        }
+                _ => { // all other kinds of statements:
+                    if classify::stmt_ends_with_semi(&node) {
+                        self.commit_stmt_expecting(token::Semi);
+                    }
 
-        if !attributes_box.is_empty() {
-            let last_span = self.last_span;
-            self.span_err(last_span,
-                          Parser::expected_item_err(&attributes_box[..]));
+                    stmts.push(P(Spanned {
+                        node: node,
+                        span: span
+                    }));
+                }
+            }
         }
 
-        let hi = self.span.hi;
-        self.bump();
         P(ast::Block {
             stmts: stmts,
             expr: expr,
             id: ast::DUMMY_NODE_ID,
             rules: s,
-            span: mk_sp(lo, hi),
+            span: mk_sp(lo, self.last_span.hi),
         })
     }
 
     fn handle_expression_like_statement(
             &mut self,
             e: P<Expr>,
-            stmt_id: NodeId,
             span: Span,
             stmts: &mut Vec<P<Stmt>>,
             last_block_expr: &mut Option<P<Expr>>) {
@@ -3964,14 +3890,14 @@ impl<'a> Parser<'a> {
                     expn_id: span.expn_id,
                 };
                 stmts.push(P(Spanned {
-                    node: StmtSemi(e, stmt_id),
+                    node: StmtSemi(e, ast::DUMMY_NODE_ID),
                     span: span_with_semi,
                 }));
             }
             token::CloseDelim(token::Brace) => *last_block_expr = Some(e),
             _ => {
                 stmts.push(P(Spanned {
-                    node: StmtExpr(e, stmt_id),
+                    node: StmtExpr(e, ast::DUMMY_NODE_ID),
                     span: span
                 }));
             }
@@ -4626,14 +4552,30 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an impl item.
-    pub fn parse_impl_item_with_outer_attributes(&mut self) -> P<ImplItem> {
-        let attrs = self.parse_outer_attributes();
+    pub fn parse_impl_item(&mut self) -> P<ImplItem> {
+        let lo = self.span.lo;
+        let mut attrs = self.parse_outer_attributes();
         let vis = self.parse_visibility();
-        if self.eat_keyword(keywords::Type) {
-            self.parse_assoc_ty_in_impl(attrs, vis)
+        let (name, node) = if self.eat_keyword(keywords::Type) {
+            let name = self.parse_ident();
+            self.expect(&token::Eq);
+            let typ = self.parse_ty_sum();
+            self.expect(&token::Semi);
+            (name, TypeImplItem(typ))
         } else {
-            self.parse_method(attrs, vis)
-        }
+            let (name, inner_attrs, node) = self.parse_impl_method(vis);
+            attrs.extend(inner_attrs.into_iter());
+            (name, node)
+        };
+
+        P(ImplItem {
+            id: ast::DUMMY_NODE_ID,
+            span: mk_sp(lo, self.last_span.hi),
+            ident: name,
+            vis: vis,
+            attrs: attrs,
+            node: node
+        })
     }
 
     fn complain_if_pub_macro(&mut self, visa: Visibility, span: Span) {
@@ -4647,76 +4589,58 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a method in a trait impl, starting with `attrs` attributes.
-    pub fn parse_method(&mut self,
-                        attrs: Vec<Attribute>,
-                        vis: Visibility)
-                        -> P<ImplItem> {
-        let lo = self.span.lo;
-
+    /// Parse a method or a macro invocation in a trait impl.
+    fn parse_impl_method(&mut self, vis: Visibility)
+                         -> (Ident, Vec<ast::Attribute>, ast::ImplItem_) {
         // code copied from parse_macro_use_or_failure... abstraction!
-        let (method_, hi, new_attrs, ident) = {
-            if !self.token.is_any_keyword()
-                && self.look_ahead(1, |t| *t == token::Not)
-                && (self.look_ahead(2, |t| *t == token::OpenDelim(token::Paren))
-                    || self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))) {
-                // method macro.
+        if !self.token.is_any_keyword()
+            && self.look_ahead(1, |t| *t == token::Not)
+            && (self.look_ahead(2, |t| *t == token::OpenDelim(token::Paren))
+                || self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))) {
+            // method macro.
 
-                let last_span = self.last_span;
-                self.complain_if_pub_macro(vis, last_span);
-
-                let pth = self.parse_path(NoTypesAllowed);
-                self.expect(&token::Not);
-
-                // eat a matched-delimiter token tree:
-                let delim = self.expect_open_delim();
-                let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
-                                                seq_sep_none(),
-                                                |p| p.parse_token_tree());
-                let m_ = ast::MacInvocTT(pth, tts, EMPTY_CTXT);
-                let m: ast::Mac = codemap::Spanned { node: m_,
-                                                 span: mk_sp(self.span.lo,
-                                                             self.span.hi) };
-                if delim != token::Brace {
-                    self.expect(&token::Semi)
-                }
-                (ast::MacImplItem(m), self.span.hi, attrs,
-                 token::special_idents::invalid)
-            } else {
-                let unsafety = self.parse_unsafety();
-                let abi = if self.eat_keyword(keywords::Extern) {
-                    self.parse_opt_abi().unwrap_or(abi::C)
-                } else {
-                    abi::Rust
-                };
-                self.expect_keyword(keywords::Fn);
-                let ident = self.parse_ident();
-                let mut generics = self.parse_generics();
-                let (explicit_self, decl) = self.parse_fn_decl_with_self(|p| {
-                        p.parse_arg()
-                    });
-                self.parse_where_clause(&mut generics);
-                let (inner_attrs, body) = self.parse_inner_attrs_and_block();
-                let body_span = body.span;
-                let mut new_attrs = attrs;
-                new_attrs.push_all(&inner_attrs[..]);
-                (MethodImplItem(ast::MethodSig {
-                    generics: generics,
-                    abi: abi,
-                    explicit_self: explicit_self,
-                    unsafety: unsafety,
-                    decl: decl
-                 }, body), body_span.hi, new_attrs, ident)
+            let last_span = self.last_span;
+            self.complain_if_pub_macro(vis, last_span);
+
+            let pth = self.parse_path(NoTypesAllowed);
+            self.expect(&token::Not);
+
+            // eat a matched-delimiter token tree:
+            let delim = self.expect_open_delim();
+            let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
+                                            seq_sep_none(),
+                                            |p| p.parse_token_tree());
+            let m_ = ast::MacInvocTT(pth, tts, EMPTY_CTXT);
+            let m: ast::Mac = codemap::Spanned { node: m_,
+                                                span: mk_sp(self.span.lo,
+                                                            self.span.hi) };
+            if delim != token::Brace {
+                self.expect(&token::Semi)
             }
-        };
-        P(ImplItem {
-            id: ast::DUMMY_NODE_ID,
-            attrs: new_attrs,
-            vis: vis,
-            ident: ident,
-            node: method_,
-            span: mk_sp(lo, hi),
-        })
+            (token::special_idents::invalid, vec![], ast::MacImplItem(m))
+        } else {
+            let unsafety = self.parse_unsafety();
+            let abi = if self.eat_keyword(keywords::Extern) {
+                self.parse_opt_abi().unwrap_or(abi::C)
+            } else {
+                abi::Rust
+            };
+            self.expect_keyword(keywords::Fn);
+            let ident = self.parse_ident();
+            let mut generics = self.parse_generics();
+            let (explicit_self, decl) = self.parse_fn_decl_with_self(|p| {
+                    p.parse_arg()
+                });
+            self.parse_where_clause(&mut generics);
+            let (inner_attrs, body) = self.parse_inner_attrs_and_block();
+            (ident, inner_attrs, MethodImplItem(ast::MethodSig {
+                generics: generics,
+                abi: abi,
+                explicit_self: explicit_self,
+                unsafety: unsafety,
+                decl: decl
+             }, body))
+        }
     }
 
     /// Parse trait Foo { ... }
@@ -4747,28 +4671,6 @@ impl<'a> Parser<'a> {
         (ident, ItemTrait(unsafety, tps, bounds, meths), None)
     }
 
-    fn parse_impl_items(&mut self) -> (Vec<P<ImplItem>>, Vec<Attribute>) {
-        let mut impl_items = Vec::new();
-        self.expect(&token::OpenDelim(token::Brace));
-        let (inner_attrs, mut method_attrs) =
-            self.parse_inner_attrs_and_next();
-        loop {
-            method_attrs.extend(self.parse_outer_attributes().into_iter());
-            if method_attrs.is_empty() && self.eat(&token::CloseDelim(token::Brace)) {
-                break;
-            }
-
-            let vis = self.parse_visibility();
-            impl_items.push(if self.eat_keyword(keywords::Type) {
-                self.parse_assoc_ty_in_impl(method_attrs, vis)
-            } else {
-                self.parse_method(method_attrs, vis)
-            });
-            method_attrs = vec![];
-        }
-        (impl_items, inner_attrs)
-    }
-
     /// Parses items implementations variants
     ///    impl<T> Foo { ... }
     ///    impl<T> ToString for &'static T { ... }
@@ -4835,7 +4737,14 @@ impl<'a> Parser<'a> {
                 ty = self.parse_ty_sum();
             }
             self.parse_where_clause(&mut generics);
-            let (impl_items, attrs) = self.parse_impl_items();
+
+            self.expect(&token::OpenDelim(token::Brace));
+            let attrs = self.parse_inner_attributes();
+
+            let mut impl_items = vec![];
+            while !self.eat(&token::CloseDelim(token::Brace)) {
+                impl_items.push(self.parse_impl_item());
+            }
 
             (ast_util::impl_pretty_name(&opt_trait, Some(&*ty)),
              ItemImpl(unsafety, polarity, generics, opt_trait, ty, impl_items),
@@ -5074,53 +4983,16 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Given a termination token and a vector of already-parsed
-    /// attributes (of length 0 or 1), parse all of the items in a module
-    fn parse_mod_items(&mut self,
-                       term: token::Token,
-                       first_item_attrs: Vec<Attribute>,
-                       inner_lo: BytePos)
-                       -> Mod {
-        // Parse all of the items up to closing or an attribute.
-
-        let mut attrs = first_item_attrs;
-        attrs.push_all(&self.parse_outer_attributes());
+    /// Given a termination token, parse all of the items in a module
+    fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> Mod {
         let mut items = vec![];
-
-        loop {
-            match self.parse_item_(attrs, true) {
-                Err(returned_attrs) => {
-                    attrs = returned_attrs;
-                    break
-                }
-                Ok(item) => {
-                    attrs = self.parse_outer_attributes();
-                    items.push(item)
-                }
-            }
-        }
-
-        // don't think this other loop is even necessary....
-
-        while self.token != term {
-            let mut attrs = mem::replace(&mut attrs, vec![]);
-            attrs.push_all(&self.parse_outer_attributes());
-            debug!("parse_mod_items: parse_item_(attrs={:?})", attrs);
-            match self.parse_item_(attrs, true /* macros allowed */) {
-              Ok(item) => items.push(item),
-              Err(_) => {
-                  let token_str = self.this_token_to_string();
-                  self.fatal(&format!("expected item, found `{}`",
-                                     token_str))
-              }
-            }
+        while let Some(item) = self.parse_item() {
+            items.push(item);
         }
 
-        if !attrs.is_empty() {
-            // We parsed attributes for the first item but didn't find it
-            let last_span = self.last_span;
-            self.span_err(last_span,
-                          Parser::expected_item_err(&attrs[..]));
+        if !self.eat(term) {
+            let token_str = self.this_token_to_string();
+            self.fatal(&format!("expected item, found `{}`", token_str))
         }
 
         ast::Mod {
@@ -5158,12 +5030,11 @@ impl<'a> Parser<'a> {
             let mod_inner_lo = self.span.lo;
             let old_owns_directory = self.owns_directory;
             self.owns_directory = true;
-            let (inner, next) = self.parse_inner_attrs_and_next();
-            let m = self.parse_mod_items(token::CloseDelim(token::Brace), next, mod_inner_lo);
-            self.expect(&token::CloseDelim(token::Brace));
+            let attrs = self.parse_inner_attributes();
+            let m = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo);
             self.owns_directory = old_owns_directory;
             self.pop_mod_path();
-            (id, ItemMod(m), Some(inner))
+            (id, ItemMod(m), Some(attrs))
         }
     }
 
@@ -5290,11 +5161,10 @@ impl<'a> Parser<'a> {
                                      Some(name),
                                      id_sp);
         let mod_inner_lo = p0.span.lo;
-        let (mod_attrs, next) = p0.parse_inner_attrs_and_next();
-        let first_item_outer_attrs = next;
-        let m0 = p0.parse_mod_items(token::Eof, first_item_outer_attrs, mod_inner_lo);
+        let mod_attrs = p0.parse_inner_attributes();
+        let m0 = p0.parse_mod_items(&token::Eof, mod_inner_lo);
         self.sess.included_mod_stack.borrow_mut().pop();
-        return (ast::ItemMod(m0), mod_attrs);
+        (ast::ItemMod(m0), mod_attrs)
     }
 
     /// Parse a function declaration from a foreign module
@@ -5341,20 +5211,6 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// At this point, this is essentially a wrapper for
-    /// parse_foreign_items.
-    fn parse_foreign_mod_items(&mut self,
-                               abi: abi::Abi,
-                               first_item_attrs: Vec<Attribute>)
-                               -> ForeignMod {
-        let foreign_items = self.parse_foreign_items(first_item_attrs);
-        assert!(self.token == token::CloseDelim(token::Brace));
-        ast::ForeignMod {
-            abi: abi,
-            items: foreign_items
-        }
-    }
-
     /// Parse extern crate links
     ///
     /// # Examples
@@ -5433,24 +5289,31 @@ impl<'a> Parser<'a> {
                               lo: BytePos,
                               opt_abi: Option<abi::Abi>,
                               visibility: Visibility,
-                              attrs: Vec<Attribute>)
+                              mut attrs: Vec<Attribute>)
                               -> P<Item> {
-
         self.expect(&token::OpenDelim(token::Brace));
 
         let abi = opt_abi.unwrap_or(abi::C);
 
-        let (inner, next) = self.parse_inner_attrs_and_next();
-        let m = self.parse_foreign_mod_items(abi, next);
+        attrs.extend(self.parse_inner_attributes().into_iter());
+
+        let mut foreign_items = vec![];
+        while let Some(item) = self.parse_foreign_item() {
+            foreign_items.push(item);
+        }
         self.expect(&token::CloseDelim(token::Brace));
 
         let last_span = self.last_span;
+        let m = ast::ForeignMod {
+            abi: abi,
+            items: foreign_items
+        };
         self.mk_item(lo,
                      last_span.hi,
                      special_idents::invalid,
                      ItemForeignMod(m),
                      visibility,
-                     maybe_append(attrs, Some(inner)))
+                     attrs)
     }
 
     /// Parse type Foo = Bar;
@@ -5592,12 +5455,11 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse one of the items allowed by the flags; on failure,
-    /// return `Err(remaining_attrs)`.
+    /// Parse one of the items allowed by the flags.
     /// NB: this function no longer parses the items inside an
     /// extern crate.
     fn parse_item_(&mut self, attrs: Vec<Attribute>,
-                   macros_allowed: bool) -> MaybeItem {
+                   macros_allowed: bool) -> Option<P<Item>> {
         let nt_item = match self.token {
             token::Interpolated(token::NtItem(ref item)) => {
                 Some((**item).clone())
@@ -5610,7 +5472,7 @@ impl<'a> Parser<'a> {
                 let mut attrs = attrs;
                 mem::swap(&mut item.attrs, &mut attrs);
                 item.attrs.extend(attrs.into_iter());
-                return Ok(P(item));
+                return Some(P(item));
             }
             None => {}
         }
@@ -5631,12 +5493,12 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     attrs);
-            return Ok(item);
+            return Some(item);
         }
 
         if self.eat_keyword(keywords::Extern) {
             if self.eat_keyword(keywords::Crate) {
-                return Ok(self.parse_item_extern_crate(lo, visibility, attrs));
+                return Some(self.parse_item_extern_crate(lo, visibility, attrs));
             }
 
             let opt_abi = self.parse_opt_abi();
@@ -5653,9 +5515,9 @@ impl<'a> Parser<'a> {
                                         item_,
                                         visibility,
                                         maybe_append(attrs, extra_attrs));
-                return Ok(item);
+                return Some(item);
             } else if self.check(&token::OpenDelim(token::Brace)) {
-                return Ok(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs));
+                return Some(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs));
             }
 
             let span = self.span;
@@ -5681,7 +5543,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Const) {
             // CONST ITEM
@@ -5698,7 +5560,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.check_keyword(keywords::Unsafe) &&
             self.look_ahead(1, |t| t.is_keyword(keywords::Trait))
@@ -5715,7 +5577,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.check_keyword(keywords::Unsafe) &&
             self.look_ahead(1, |t| t.is_keyword(keywords::Impl))
@@ -5731,7 +5593,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.check_keyword(keywords::Fn) {
             // FUNCTION ITEM
@@ -5745,7 +5607,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.check_keyword(keywords::Unsafe)
             && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace)) {
@@ -5766,7 +5628,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Mod) {
             // MODULE ITEM
@@ -5779,7 +5641,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Type) {
             // TYPE ITEM
@@ -5791,7 +5653,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Enum) {
             // ENUM ITEM
@@ -5803,7 +5665,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Trait) {
             // TRAIT ITEM
@@ -5816,7 +5678,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Impl) {
             // IMPL ITEM
@@ -5828,7 +5690,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         if self.eat_keyword(keywords::Struct) {
             // STRUCT ITEM
@@ -5840,30 +5702,34 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     maybe_append(attrs, extra_attrs));
-            return Ok(item);
+            return Some(item);
         }
         self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility)
     }
 
-    /// Parse a foreign item; on failure, return `Err(remaining_attrs)`.
-    fn parse_foreign_item(&mut self, attrs: Vec<Attribute>)
-                          -> Result<P<ForeignItem>, Vec<Attribute>> {
+    /// Parse a foreign item.
+    fn parse_foreign_item(&mut self) -> Option<P<ForeignItem>> {
         let lo = self.span.lo;
 
+        let attrs = self.parse_outer_attributes();
         let visibility = self.parse_visibility();
 
         if self.check_keyword(keywords::Static) {
             // FOREIGN STATIC ITEM
-            return Ok(self.parse_item_foreign_static(visibility, attrs));
+            return Some(self.parse_item_foreign_static(visibility, attrs));
         }
         if self.check_keyword(keywords::Fn) || self.check_keyword(keywords::Unsafe) {
             // FOREIGN FUNCTION ITEM
-            return Ok(self.parse_item_foreign_fn(visibility, attrs));
+            return Some(self.parse_item_foreign_fn(visibility, attrs));
         }
 
         // FIXME #5668: this will occur for a macro invocation:
-        let item = try!(self.parse_macro_use_or_failure(attrs, true, lo, visibility));
-        self.span_fatal(item.span, "macros cannot expand to foreign items");
+        match self.parse_macro_use_or_failure(attrs, true, lo, visibility) {
+            Some(item) => {
+                self.span_fatal(item.span, "macros cannot expand to foreign items");
+            }
+            None => None
+        }
     }
 
     /// This is the fall-through for parsing items.
@@ -5873,7 +5739,7 @@ impl<'a> Parser<'a> {
         macros_allowed: bool,
         lo: BytePos,
         visibility: Visibility
-    ) -> MaybeItem {
+    ) -> Option<P<Item>> {
         if macros_allowed && !self.token.is_any_keyword()
                 && self.look_ahead(1, |t| *t == token::Not)
                 && (self.look_ahead(2, |t| t.is_plain_ident())
@@ -5925,7 +5791,7 @@ impl<'a> Parser<'a> {
                                     item_,
                                     visibility,
                                     attrs);
-            return Ok(item);
+            return Some(item);
         }
 
         // FAILURE TO PARSE ITEM
@@ -5936,16 +5802,22 @@ impl<'a> Parser<'a> {
                 self.span_fatal(last_span, "unmatched visibility `pub`");
             }
         }
-        Err(attrs)
+
+        if !attrs.is_empty() {
+            self.expected_item_err(&attrs);
+        }
+        None
     }
 
+    // HACK(eddyb) staging required for `quote_item!`.
+    #[cfg(stage0)] // SNAP 270a677
     pub fn parse_item_with_outer_attributes(&mut self) -> Option<P<Item>> {
-        let attrs = self.parse_outer_attributes();
-        self.parse_item(attrs)
+        self.parse_item()
     }
 
-    pub fn parse_item(&mut self, attrs: Vec<Attribute>) -> Option<P<Item>> {
-        self.parse_item_(attrs, true).ok()
+    pub fn parse_item(&mut self) -> Option<P<Item>> {
+        let attrs = self.parse_outer_attributes();
+        self.parse_item_(attrs, true)
     }
 
     /// Matches view_path : MOD? non_global_path as IDENT
@@ -6051,52 +5923,13 @@ impl<'a> Parser<'a> {
         P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path)))
     }
 
-    /// Parses a sequence of foreign items. Stops when it finds program
-    /// text that can't be parsed as an item
-    fn parse_foreign_items(&mut self, first_item_attrs: Vec<Attribute>)
-                           -> Vec<P<ForeignItem>> {
-        let mut attrs = first_item_attrs;
-        attrs.push_all(&self.parse_outer_attributes());
-        let mut foreign_items = Vec::new();
-        loop {
-            match self.parse_foreign_item(attrs) {
-                Ok(foreign_item) => {
-                    foreign_items.push(foreign_item);
-                }
-                Err(returned_attrs) => {
-                    if self.check(&token::CloseDelim(token::Brace)) {
-                        attrs = returned_attrs;
-                        break
-                    }
-                    self.unexpected();
-                }
-            }
-            attrs = self.parse_outer_attributes();
-        }
-
-        if !attrs.is_empty() {
-            let last_span = self.last_span;
-            self.span_err(last_span,
-                          Parser::expected_item_err(&attrs[..]));
-        }
-
-        foreign_items
-    }
-
     /// Parses a source module as a crate. This is the main
     /// entry point for the parser.
     pub fn parse_crate_mod(&mut self) -> Crate {
         let lo = self.span.lo;
-        // parse the crate's inner attrs, maybe (oops) one
-        // of the attrs of an item:
-        let (inner, next) = self.parse_inner_attrs_and_next();
-        let first_item_outer_attrs = next;
-        // parse the items inside the crate:
-        let m = self.parse_mod_items(token::Eof, first_item_outer_attrs, lo);
-
         ast::Crate {
-            module: m,
-            attrs: inner,
+            attrs: self.parse_inner_attributes(),
+            module: self.parse_mod_items(&token::Eof, lo),
             config: self.cfg.clone(),
             span: mk_sp(lo, self.span.lo),
             exported_macros: Vec::new(),
diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs
index 89854f5d979a1..9b570c2b1fe29 100644
--- a/src/libsyntax/util/parser_testing.rs
+++ b/src/libsyntax/util/parser_testing.rs
@@ -58,14 +58,14 @@ pub fn string_to_expr (source_str : String) -> P<ast::Expr> {
 /// Parse a string, return an item
 pub fn string_to_item (source_str : String) -> Option<P<ast::Item>> {
     with_error_checking_parse(source_str, |p| {
-        p.parse_item(Vec::new())
+        p.parse_item()
     })
 }
 
 /// Parse a string, return a stmt
 pub fn string_to_stmt(source_str : String) -> P<ast::Stmt> {
     with_error_checking_parse(source_str, |p| {
-        p.parse_stmt(Vec::new())
+        p.parse_stmt().unwrap()
     })
 }
 
diff --git a/src/test/parse-fail/column-offset-1-based.rs b/src/test/parse-fail/column-offset-1-based.rs
index 621b480fe77d3..a00ded61758c2 100644
--- a/src/test/parse-fail/column-offset-1-based.rs
+++ b/src/test/parse-fail/column-offset-1-based.rs
@@ -8,4 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-# //~ ERROR 11:1: 11:2 error: expected one of `!` or `[`, found `<eof>`
+# //~ ERROR 11:1: 11:2 error: expected `[`, found `<eof>`
diff --git a/src/test/parse-fail/issue-1655.rs b/src/test/parse-fail/issue-1655.rs
index a8704f7545f06..6bdcf5c5edced 100644
--- a/src/test/parse-fail/issue-1655.rs
+++ b/src/test/parse-fail/issue-1655.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:expected one of `!` or `[`, found `vec`
+// error-pattern:expected `[`, found `vec`
 mod blade_runner {
     #vec[doc(
         brief = "Blade Runner is probably the best movie ever",
diff --git a/src/test/run-pass-fulldeps/quote-tokens.rs b/src/test/run-pass-fulldeps/quote-tokens.rs
index 7f7ed586878e5..020f5f562d2c3 100644
--- a/src/test/run-pass-fulldeps/quote-tokens.rs
+++ b/src/test/run-pass-fulldeps/quote-tokens.rs
@@ -25,7 +25,7 @@ fn syntax_extension(cx: &ExtCtxt) {
     let a: P<syntax::ast::Expr> = quote_expr!(cx, 1 + 2);
     let _b: Option<P<syntax::ast::Item>> = quote_item!(cx, static foo : int = $e_toks; );
     let _c: P<syntax::ast::Pat> = quote_pat!(cx, (x, 1 .. 4, *) );
-    let _d: P<syntax::ast::Stmt> = quote_stmt!(cx, let x = $a; );
+    let _d: Option<P<syntax::ast::Stmt>> = quote_stmt!(cx, let x = $a; );
     let _d: syntax::ast::Arm = quote_arm!(cx, (ref x, ref y) = (x, y) );
     let _e: P<syntax::ast::Expr> = quote_expr!(cx, match foo { $p_toks => 10 } );
 

From e04462fde2437a87ea06a5e7f4a7a979363bd73c Mon Sep 17 00:00:00 2001
From: Eduard Burtescu <edy.burt@gmail.com>
Date: Wed, 25 Feb 2015 22:05:07 +0200
Subject: [PATCH 2/4] syntax: parse `const fn` for free functions and inherent
 methods.

---
 src/librustc/metadata/encoder.rs             |  4 +-
 src/librustc/middle/effect.rs                |  4 +-
 src/librustc/middle/infer/error_reporting.rs | 19 ++++---
 src/librustc/middle/reachable.rs             |  6 +-
 src/librustc/middle/resolve_lifetime.rs      |  2 +-
 src/librustc/middle/stability.rs             |  2 +-
 src/librustc/middle/ty.rs                    |  2 +-
 src/librustc_lint/builtin.rs                 |  6 +-
 src/librustc_resolve/build_reduced_graph.rs  |  2 +-
 src/librustc_resolve/lib.rs                  |  4 +-
 src/librustc_trans/save/mod.rs               |  2 +-
 src/librustc_trans/trans/base.rs             |  6 +-
 src/librustc_trans/trans/debuginfo.rs        |  2 +-
 src/librustc_trans/trans/inline.rs           |  2 +-
 src/librustc_trans/trans/monomorphize.rs     |  2 +-
 src/librustc_typeck/check/mod.rs             |  2 +-
 src/librustc_typeck/collect.rs               |  4 +-
 src/librustc_typeck/lib.rs                   |  4 +-
 src/librustdoc/clean/inline.rs               |  2 +
 src/librustdoc/clean/mod.rs                  |  7 ++-
 src/librustdoc/doctree.rs                    |  1 +
 src/librustdoc/html/format.rs                | 19 +++++++
 src/librustdoc/html/render.rs                | 29 ++++++----
 src/librustdoc/visit_ast.rs                  | 12 ++--
 src/libsyntax/ast.rs                         |  9 ++-
 src/libsyntax/ast_map/blocks.rs              | 18 ++++--
 src/libsyntax/ast_util.rs                    |  3 +-
 src/libsyntax/ext/build.rs                   |  1 +
 src/libsyntax/ext/deriving/generic/mod.rs    |  1 +
 src/libsyntax/ext/expand.rs                  |  5 +-
 src/libsyntax/feature_gate.rs                |  2 +-
 src/libsyntax/fold.rs                        |  5 +-
 src/libsyntax/parse/parser.rs                | 59 +++++++++++++++-----
 src/libsyntax/print/pprust.rs                | 38 +++++++++----
 src/libsyntax/test.rs                        | 10 ++--
 src/libsyntax/visit.rs                       |  8 +--
 36 files changed, 207 insertions(+), 97 deletions(-)

diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 611d8bc27d19d..644f36262c7b9 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1015,7 +1015,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_stability(rbml_w, stab);
         rbml_w.end_tag();
       }
-      ast::ItemFn(ref decl, _, _, ref generics, _) => {
+      ast::ItemFn(ref decl, _, _, _, ref generics, _) => {
         add_to_index(item, rbml_w, index);
         rbml_w.start_tag(tag_items_data_item);
         encode_def_id(rbml_w, def_id);
@@ -1875,7 +1875,7 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
 
     for id in ecx.reachable {
         if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) {
-            if let ast::ItemFn(_, _, abi, ref generics, _) = i.node {
+            if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node {
                 if abi != abi::Rust && !generics.is_type_parameterized() {
                     rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
                 }
diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs
index 378f3db082339..38e3ee50c82a4 100644
--- a/src/librustc/middle/effect.rs
+++ b/src/librustc/middle/effect.rs
@@ -87,8 +87,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
                 block: &'v ast::Block, span: Span, _: ast::NodeId) {
 
         let (is_item_fn, is_unsafe_fn) = match fn_kind {
-            visit::FkItemFn(_, _, fn_style, _) =>
-                (true, fn_style == ast::Unsafety::Unsafe),
+            visit::FkItemFn(_, _, unsafety, _, _) =>
+                (true, unsafety == ast::Unsafety::Unsafe),
             visit::FkMethod(_, sig) =>
                 (true, sig.unsafety == ast::Unsafety::Unsafe),
             _ => (false, false),
diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs
index 1ca56596a0147..88d69f5e081d7 100644
--- a/src/librustc/middle/infer/error_reporting.rs
+++ b/src/librustc/middle/infer/error_reporting.rs
@@ -159,6 +159,7 @@ trait ErrorReportingHelpers<'tcx> {
     fn give_expl_lifetime_param(&self,
                                 decl: &ast::FnDecl,
                                 unsafety: ast::Unsafety,
+                                constness: ast::Constness,
                                 ident: ast::Ident,
                                 opt_explicit_self: Option<&ast::ExplicitSelf_>,
                                 generics: &ast::Generics,
@@ -840,8 +841,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
             Some(ref node) => match *node {
                 ast_map::NodeItem(ref item) => {
                     match item.node {
-                        ast::ItemFn(ref fn_decl, pur, _, ref gen, _) => {
-                            Some((fn_decl, gen, pur, item.ident, None, item.span))
+                        ast::ItemFn(ref fn_decl, unsafety, constness, _, ref gen, _) => {
+                            Some((fn_decl, gen, unsafety, constness,
+                                  item.ident, None, item.span))
                         },
                         _ => None
                     }
@@ -852,6 +854,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                             Some((&sig.decl,
                                   &sig.generics,
                                   sig.unsafety,
+                                  sig.constness,
                                   item.ident,
                                   Some(&sig.explicit_self.node),
                                   item.span))
@@ -866,6 +869,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                             Some((&sig.decl,
                                   &sig.generics,
                                   sig.unsafety,
+                                  sig.constness,
                                   item.ident,
                                   Some(&sig.explicit_self.node),
                                   item.span))
@@ -877,12 +881,12 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
             },
             None => None
         };
-        let (fn_decl, generics, unsafety, ident, expl_self, span)
+        let (fn_decl, generics, unsafety, constness, ident, expl_self, span)
                                     = node_inner.expect("expect item fn");
         let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
                                        generics, same_regions, &life_giver);
         let (fn_decl, expl_self, generics) = rebuilder.rebuild();
-        self.give_expl_lifetime_param(&fn_decl, unsafety, ident,
+        self.give_expl_lifetime_param(&fn_decl, unsafety, constness, ident,
                                       expl_self.as_ref(), &generics, span);
     }
 }
@@ -1437,12 +1441,13 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
     fn give_expl_lifetime_param(&self,
                                 decl: &ast::FnDecl,
                                 unsafety: ast::Unsafety,
+                                constness: ast::Constness,
                                 ident: ast::Ident,
                                 opt_explicit_self: Option<&ast::ExplicitSelf_>,
                                 generics: &ast::Generics,
                                 span: codemap::Span) {
-        let suggested_fn = pprust::fun_to_string(decl, unsafety, ident,
-                                              opt_explicit_self, generics);
+        let suggested_fn = pprust::fun_to_string(decl, unsafety, constness, ident,
+                                                 opt_explicit_self, generics);
         let msg = format!("consider using an explicit lifetime \
                            parameter as shown: {}", suggested_fn);
         self.tcx.sess.span_help(span, &msg[..]);
@@ -1724,7 +1729,7 @@ fn lifetimes_in_scope(tcx: &ty::ctxt,
     let method_id_opt = match tcx.map.find(parent) {
         Some(node) => match node {
             ast_map::NodeItem(item) => match item.node {
-                ast::ItemFn(_, _, _, ref gen, _) => {
+                ast::ItemFn(_, _, _, _, ref gen, _) => {
                     taken.push_all(&gen.lifetimes);
                     None
                 },
diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs
index 7ded344414ce6..9a803da643989 100644
--- a/src/librustc/middle/reachable.rs
+++ b/src/librustc/middle/reachable.rs
@@ -46,7 +46,7 @@ fn item_might_be_inlined(item: &ast::Item) -> bool {
 
     match item.node {
         ast::ItemImpl(_, _, ref generics, _, _, _) |
-        ast::ItemFn(_, _, _, ref generics, _) => {
+        ast::ItemFn(_, _, _, _, ref generics, _) => {
             generics_require_inlining(generics)
         }
         _ => false,
@@ -254,7 +254,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
             // but all other rust-only interfaces can be private (they will not
             // participate in linkage after this product is produced)
             if let ast_map::NodeItem(item) = *node {
-                if let ast::ItemFn(_, _, abi, _, _) = item.node {
+                if let ast::ItemFn(_, _, _, abi, _, _) = item.node {
                     if abi != abi::Rust {
                         self.reachable_symbols.insert(search_item);
                     }
@@ -271,7 +271,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
         match *node {
             ast_map::NodeItem(item) => {
                 match item.node {
-                    ast::ItemFn(_, _, _, _, ref search_block) => {
+                    ast::ItemFn(_, _, _, _, _, ref search_block) => {
                         if item_might_be_inlined(&*item) {
                             visit::walk_block(self, &**search_block)
                         }
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index e33a255343161..d8a32cce63bbf 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -142,7 +142,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
     fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
                 b: &'v ast::Block, s: Span, _: ast::NodeId) {
         match fk {
-            visit::FkItemFn(_, generics, _, _) => {
+            visit::FkItemFn(_, generics, _, _, _) =>
                 self.visit_early_late(subst::FnSpace, generics, |this| {
                     visit::walk_fn(this, fk, fd, b, s)
                 })
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 01766b0de085f..9b2f20e137bd5 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -23,7 +23,7 @@ use syntax::{attr, visit};
 use syntax::ast;
 use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant};
 use syntax::ast::{Item, Generics, StructField};
-use syntax::ast_util::is_local;
+use syntax::ast_util::{is_local, PostExpansionMethod};
 use syntax::attr::{Stability, AttrMetaMethods};
 use syntax::visit::{FnKind, Visitor};
 use syntax::feature_gate::emit_feature_warn;
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 8705e56b09425..196dd36493c4e 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -2355,7 +2355,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
             }
             Some(ast_map::NodeItem(item)) => {
                 match item.node {
-                    ast::ItemFn(_, _, _, _, ref body) => {
+                    ast::ItemFn(_, _, _, _, _, ref body) => {
                         // We assume this is a function.
                         let fn_def_id = ast_util::local_def(id);
                         let fn_scheme = lookup_item_type(cx, fn_def_id);
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 074591fb927d2..4b8e42adb3ffc 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -991,7 +991,7 @@ impl LintPass for NonSnakeCase {
                 },
                 _ => (),
             },
-            visit::FkItemFn(ident, _, _, _) => {
+            visit::FkItemFn(ident, _, _, _, _) => {
                 self.check_snake_case(cx, "function", ident, span)
             },
             _ => (),
@@ -1315,7 +1315,7 @@ impl LintPass for UnsafeCode {
     fn check_fn(&mut self, cx: &Context, fk: visit::FnKind, _: &ast::FnDecl,
                 _: &ast::Block, span: Span, _: ast::NodeId) {
         match fk {
-            visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _) =>
+            visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _, _) =>
                 cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
 
             visit::FkMethod(_, sig) => {
@@ -1808,7 +1808,7 @@ impl LintPass for UnconditionalRecursion {
                               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::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
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 1cbbcad955090..4aa5ded95a565 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -421,7 +421,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                     .define_value(DefConst(local_def(item.id)), sp, modifiers);
                 parent.clone()
             }
-            ItemFn(_, _, _, _, _) => {
+            ItemFn(_, _, _, _, _, _) => {
                 let name_bindings = self.add_child(name, parent, ForbidDuplicateValues, sp);
 
                 let def = DefFn(local_def(item.id), false);
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index e49fdc9c5d356..cacb7159227be 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -238,7 +238,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Resolver<'a, 'tcx> {
                 _: Span,
                 node_id: NodeId) {
         let rib_kind = match function_kind {
-            visit::FkItemFn(_, generics, _, _) => {
+            visit::FkItemFn(_, generics, _, _, _) => {
                 self.visit_generics(generics);
                 ItemRibKind
             }
@@ -2767,7 +2767,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                                                ItemRibKind),
                                              |this| visit::walk_item(this, item));
             }
-            ItemFn(_, _, _, ref generics, _) => {
+            ItemFn(_, _, _, _, ref generics, _) => {
                 self.with_type_parameter_rib(HasTypeParameters(generics,
                                                                FnSpace,
                                                                ItemRibKind),
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 83bb5efb425d2..5e870358cb7fc 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -1176,7 +1176,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
                                           &location[..],
                                           self.cur_scope);
             }
-            ast::ItemFn(ref decl, _, _, ref ty_params, ref body) =>
+            ast::ItemFn(ref decl, _, _, _, ref ty_params, ref body) =>
                 self.process_fn(item, &**decl, ty_params, &**body),
             ast::ItemStatic(ref typ, mt, ref expr) =>
                 self.process_static(item, &**typ, mt, &**expr),
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 74326d4ea919c..49c27dbac8cdc 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -1262,7 +1262,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
     let blk = match tcx.map.find(id) {
         Some(ast_map::NodeItem(i)) => {
             match i.node {
-                ast::ItemFn(_, _, _, _, ref blk) => {
+                ast::ItemFn(_, _, _, _, _, ref blk) => {
                     blk
                 }
                 _ => tcx.sess.bug("unexpected item variant in has_nested_returns")
@@ -2230,7 +2230,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
     let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
 
     match item.node {
-      ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
+      ast::ItemFn(ref decl, _, _, abi, ref generics, ref body) => {
         if !generics.is_type_parameterized() {
             let trans_everywhere = attr::requests_inline(&item.attrs);
             // Ignore `trans_everywhere` for cross-crate inlined items
@@ -2771,7 +2771,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                     }
                 }
 
-                ast::ItemFn(_, _, abi, _, _) => {
+                ast::ItemFn(_, _, _, abi, _, _) => {
                     let sym = sym();
                     let llfn = if abi == Rust {
                         register_fn(ccx, i.span, sym, i.id, ty)
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index b5ab2c2825127..7cd4b51ebe787 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -1290,7 +1290,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
 
             match item.node {
-                ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => {
+                ast::ItemFn(ref fn_decl, _, _, _, ref generics, ref top_level_block) => {
                     (item.ident, fn_decl, generics, top_level_block, item.span, true)
                 }
                 _ => {
diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs
index 2034c6223c134..58ac32fce4acd 100644
--- a/src/librustc_trans/trans/inline.rs
+++ b/src/librustc_trans/trans/inline.rs
@@ -55,7 +55,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
             trans_item(ccx, &**item);
 
             let linkage = match item.node {
-                ast::ItemFn(_, _, _, ref generics, _) => {
+                ast::ItemFn(_, _, _, _, ref generics, _) => {
                     if generics.is_type_parameterized() {
                         // Generics have no symbol, so they can't be given any
                         // linkage.
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 2083e737f89b1..c4637d6f21e8d 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -173,7 +173,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         ast_map::NodeItem(i) => {
             match *i {
               ast::Item {
-                  node: ast::ItemFn(ref decl, _, abi, _, ref body),
+                  node: ast::ItemFn(ref decl, _, _, abi, _, ref body),
                   ..
               } => {
                   let d = mk_lldecl(abi);
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 41951ab2b6200..9288338d20882 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -718,7 +718,7 @@ pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
                             &enum_definition.variants,
                             it.id);
       }
-      ast::ItemFn(ref decl, _, _, _, ref body) => {
+      ast::ItemFn(ref decl, _, _, _, _, ref body) => {
         let fn_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
         let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
         check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 2372680576797..069556e46adcd 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1391,7 +1391,7 @@ fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
             let ty = ccx.icx(&()).to_ty(&ExplicitRscope, &**t);
             ty::TypeScheme { ty: ty, generics: ty::Generics::empty() }
         }
-        ast::ItemFn(ref decl, unsafety, abi, ref generics, _) => {
+        ast::ItemFn(ref decl, unsafety, _, abi, ref generics, _) => {
             let ty_generics = ty_generics_for_fn(ccx, generics, &ty::Generics::empty());
             let tofd = astconv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &**decl);
             let ty = ty::mk_bare_fn(tcx, Some(local_def(it.id)), tcx.mk_bare_fn(tofd));
@@ -1443,7 +1443,7 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         ast::ItemStatic(..) | ast::ItemConst(..) => {
             ty::GenericPredicates::empty()
         }
-        ast::ItemFn(_, _, _, ref ast_generics, _) => {
+        ast::ItemFn(_, _, _, _, ref ast_generics, _) => {
             ty_generic_predicates_for_fn(ccx, ast_generics, &ty::GenericPredicates::empty())
         }
         ast::ItemTy(_, ref generics) => {
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index bbc64a54013b7..bd6fa7a8941d9 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -218,7 +218,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
             match tcx.map.find(main_id) {
                 Some(ast_map::NodeItem(it)) => {
                     match it.node {
-                        ast::ItemFn(_, _, _, ref ps, _)
+                        ast::ItemFn(_, _, _, _, ref ps, _)
                         if ps.is_parameterized() => {
                             span_err!(ccx.tcx.sess, main_span, E0131,
                                       "main function is not allowed to have type parameters");
@@ -265,7 +265,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
             match tcx.map.find(start_id) {
                 Some(ast_map::NodeItem(it)) => {
                     match it.node {
-                        ast::ItemFn(_,_,_,ref ps,_)
+                        ast::ItemFn(_,_,_,_,ref ps,_)
                         if ps.is_parameterized() => {
                             span_err!(tcx.sess, start_span, E0132,
                                       "start function is not allowed to have type parameters");
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 9f7b68f38fa6e..ec279c5c05fc0 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -169,6 +169,7 @@ fn build_external_function(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) ->
         decl: decl,
         generics: (&t.generics, &predicates, subst::FnSpace).clean(cx),
         unsafety: style,
+        constness: ast::Constness::NotConst
     }
 }
 
@@ -300,6 +301,7 @@ fn build_impl(cx: &DocContext, tcx: &ty::ctxt,
                     }) => {
                         clean::MethodItem(clean::Method {
                             unsafety: unsafety,
+                            constness: ast::Constness::NotConst,
                             decl: decl,
                             self_: self_,
                             generics: generics,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index e91e95961c521..51df1145712bf 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -944,6 +944,7 @@ pub struct Method {
     pub generics: Generics,
     pub self_: SelfTy,
     pub unsafety: ast::Unsafety,
+    pub constness: ast::Constness,
     pub decl: FnDecl,
     pub abi: abi::Abi
 }
@@ -965,7 +966,8 @@ impl Clean<Method> for ast::MethodSig {
         Method {
             generics: self.generics.clean(cx),
             self_: self.explicit_self.node.clean(cx),
-            unsafety: self.unsafety.clone(),
+            unsafety: self.unsafety,
+            constness: self.constness,
             decl: decl,
             abi: self.abi
         }
@@ -1030,6 +1032,7 @@ pub struct Function {
     pub decl: FnDecl,
     pub generics: Generics,
     pub unsafety: ast::Unsafety,
+    pub constness: ast::Constness,
 }
 
 impl Clean<Item> for doctree::Function {
@@ -1045,6 +1048,7 @@ impl Clean<Item> for doctree::Function {
                 decl: self.decl.clean(cx),
                 generics: self.generics.clean(cx),
                 unsafety: self.unsafety,
+                constness: self.constness,
             }),
         }
     }
@@ -2243,6 +2247,7 @@ impl Clean<Item> for ast::ForeignItem {
                     decl: decl.clean(cx),
                     generics: generics.clean(cx),
                     unsafety: ast::Unsafety::Unsafe,
+                    constness: ast::Constness::NotConst
                 })
             }
             ast::ForeignItemStatic(ref ty, mutbl) => {
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 5a4deaa2e7266..6fd546d39aa32 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -132,6 +132,7 @@ pub struct Function {
     pub vis: ast::Visibility,
     pub stab: Option<attr::Stability>,
     pub unsafety: ast::Unsafety,
+    pub constness: ast::Constness,
     pub whence: Span,
     pub generics: ast::Generics,
 }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 03a2d708ee43a..258a15d87eb3c 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -36,6 +36,10 @@ pub struct VisSpace(pub Option<ast::Visibility>);
 /// space after it.
 #[derive(Copy)]
 pub struct UnsafetySpace(pub ast::Unsafety);
+/// Similarly to VisSpace, this structure is used to render a function constness
+/// with a space after it.
+#[derive(Copy)]
+pub struct ConstnessSpace(pub ast::Constness);
 /// Wrapper struct for properly emitting a method declaration.
 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
 /// Similar to VisSpace, but used for mutability
@@ -67,6 +71,12 @@ impl UnsafetySpace {
     }
 }
 
+impl ConstnessSpace {
+    pub fn get(&self) -> ast::Constness {
+        let ConstnessSpace(v) = *self; v
+    }
+}
+
 impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         for (i, item) in self.0.iter().enumerate() {
@@ -613,6 +623,15 @@ impl fmt::Display for UnsafetySpace {
     }
 }
 
+impl fmt::Display for ConstnessSpace {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.get() {
+            ast::Constness::Const => write!(f, "const "),
+            ast::Constness::NotConst => Ok(())
+        }
+    }
+}
+
 impl fmt::Display for clean::Import {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index dba7b16eceecb..28be87952904a 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -60,7 +60,7 @@ use clean;
 use doctree;
 use fold::DocFolder;
 use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace, Stability};
-use html::format::{ConciseStability, TyParamBounds, WhereClause};
+use html::format::{ConciseStability, ConstnessSpace, TyParamBounds, WhereClause};
 use html::highlight;
 use html::item_type::ItemType;
 use html::layout;
@@ -1651,10 +1651,11 @@ fn item_static(w: &mut fmt::Formatter, it: &clean::Item,
 
 fn item_function(w: &mut fmt::Formatter, it: &clean::Item,
                  f: &clean::Function) -> fmt::Result {
-    try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}fn \
+    try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}{constness}fn \
                     {name}{generics}{decl}{where_clause}</pre>",
            vis = VisSpace(it.visibility),
            unsafety = UnsafetySpace(f.unsafety),
+           constness = ConstnessSpace(f.constness),
            name = it.name.as_ref().unwrap(),
            generics = f.generics,
            where_clause = WhereClause(&f.generics),
@@ -1818,18 +1819,20 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
 }
 
 fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
-    fn method(w: &mut fmt::Formatter, it: &clean::Item,
-              unsafety: ast::Unsafety, abi: abi::Abi,
-              g: &clean::Generics, selfty: &clean::SelfTy,
+    fn method(w: &mut fmt::Formatter,
+              it: &clean::Item,
+              unsafety: ast::Unsafety,
+              constness: ast::Constness,
+              abi: abi::Abi,
+              g: &clean::Generics,
+              selfty: &clean::SelfTy,
               d: &clean::FnDecl) -> fmt::Result {
         use syntax::abi::Abi;
 
-        write!(w, "{}{}fn <a href='#{ty}.{name}' class='fnname'>{name}</a>\
+        write!(w, "{}{}{}fn <a href='#{ty}.{name}' class='fnname'>{name}</a>\
                    {generics}{decl}{where_clause}",
-               match unsafety {
-                   ast::Unsafety::Unsafe => "unsafe ",
-                   _ => "",
-               },
+               UnsafetySpace(unsafety),
+               ConstnessSpace(constness),
                match abi {
                    Abi::Rust => String::new(),
                    a => format!("extern {} ", a.to_string())
@@ -1842,10 +1845,12 @@ fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
     }
     match meth.inner {
         clean::TyMethodItem(ref m) => {
-            method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl)
+            method(w, meth, m.unsafety, ast::Constness::NotConst,
+                   m.abi, &m.generics, &m.self_, &m.decl)
         }
         clean::MethodItem(ref m) => {
-            method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl)
+            method(w, meth, m.unsafety, m.constness,
+                   m.abi, &m.generics, &m.self_, &m.decl)
         }
         clean::AssociatedTypeItem(ref bounds, ref default) => {
             assoc_type(w, meth, bounds, default)
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 3e998166397bc..6ad38cb0ed850 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -120,7 +120,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
     pub fn visit_fn(&mut self, item: &ast::Item,
                     name: ast::Ident, fd: &ast::FnDecl,
-                    unsafety: &ast::Unsafety, _abi: &abi::Abi,
+                    unsafety: ast::Unsafety,
+                    constness: ast::Constness,
+                    _abi: &abi::Abi,
                     gen: &ast::Generics) -> Function {
         debug!("Visiting fn");
         Function {
@@ -132,7 +134,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             name: name,
             whence: item.span,
             generics: gen.clone(),
-            unsafety: *unsafety,
+            unsafety: unsafety,
+            constness: constness
         }
     }
 
@@ -285,8 +288,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 om.enums.push(self.visit_enum_def(item, name, ed, gen)),
             ast::ItemStruct(ref sd, ref gen) =>
                 om.structs.push(self.visit_struct_def(item, name, &**sd, gen)),
-            ast::ItemFn(ref fd, ref pur, ref abi, ref gen, _) =>
-                om.fns.push(self.visit_fn(item, name, &**fd, pur, abi, gen)),
+            ast::ItemFn(ref fd, unsafety, constness, ref abi, ref gen, _) =>
+                om.fns.push(self.visit_fn(item, name, &**fd, unsafety,
+                                          constness, abi, gen)),
             ast::ItemTy(ref ty, ref gen) => {
                 let t = Typedef {
                     ty: ty.clone(),
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 657ffcaece9f8..51f742ab1e660 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1350,6 +1350,12 @@ pub enum Unsafety {
     Normal,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum Constness {
+    Const,
+    NotConst,
+}
+
 impl fmt::Display for Unsafety {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(match *self {
@@ -1416,6 +1422,7 @@ pub enum ExplicitSelf_ {
 pub type ExplicitSelf = Spanned<ExplicitSelf_>;
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+             Constness,
 pub struct Mod {
     /// A span from the first token past `{` to the last token until `}`.
     /// For `mod foo;`, the inner span ranges from the first token
@@ -1623,7 +1630,7 @@ pub enum Item_ {
 
     ItemStatic(P<Ty>, Mutability, P<Expr>),
     ItemConst(P<Ty>, P<Expr>),
-    ItemFn(P<FnDecl>, Unsafety, Abi, Generics, P<Block>),
+    ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Block>),
     ItemMod(Mod),
     ItemForeignMod(ForeignMod),
     ItemTy(P<Ty>, Generics),
diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs
index 16a339cdcb530..d659519013c48 100644
--- a/src/libsyntax/ast_map/blocks.rs
+++ b/src/libsyntax/ast_map/blocks.rs
@@ -120,6 +120,7 @@ struct ItemFnParts<'a> {
     ident:    ast::Ident,
     decl:     &'a ast::FnDecl,
     unsafety: ast::Unsafety,
+    constness: ast::Constness,
     abi:      abi::Abi,
     generics: &'a ast::Generics,
     body:     &'a Block,
@@ -179,7 +180,7 @@ impl<'a> FnLikeNode<'a> {
 
     pub fn kind(self) -> visit::FnKind<'a> {
         let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> {
-            visit::FkItemFn(p.ident, p.generics, p.unsafety, p.abi)
+            visit::FkItemFn(p.ident, p.generics, p.unsafety, p.constness, p.abi)
         };
         let closure = |_: ClosureParts| {
             visit::FkFnBlock
@@ -197,10 +198,17 @@ impl<'a> FnLikeNode<'a> {
     {
         match self.node {
             ast_map::NodeItem(i) => match i.node {
-                ast::ItemFn(ref decl, unsafety, abi, ref generics, ref block) =>
-                    item_fn(ItemFnParts{
-                        ident: i.ident, decl: &**decl, unsafety: unsafety, body: &**block,
-                        generics: generics, abi: abi, id: i.id, span: i.span
+                ast::ItemFn(ref decl, unsafety, constness, abi, ref generics, ref block) =>
+                    item_fn(ItemFnParts {
+                        id: i.id,
+                        ident: i.ident,
+                        decl: &**decl,
+                        unsafety: unsafety,
+                        constness: constness,
+                        body: &**block,
+                        generics: generics,
+                        abi: abi,
+                        span: i.span
                     }),
                 _ => panic!("item FnLikeNode that is not fn-like"),
             },
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index cec824e79ff5a..e776bfba13b47 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -267,6 +267,7 @@ pub fn impl_pretty_name(trait_ref: &Option<TraitRef>, ty: Option<&Ty>) -> Ident
     token::gensym_ident(&pretty[..])
 }
 
+                 _,
 pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
     match field.node.kind {
         ast::NamedField(_, v) | ast::UnnamedField(v) => v
@@ -456,7 +457,7 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
         self.operation.visit_id(node_id);
 
         match function_kind {
-            visit::FkItemFn(_, generics, _, _) => {
+            visit::FkItemFn(_, generics, _, _, _) => {
                 self.visit_generics_helper(generics)
             }
             visit::FkMethod(_, sig) => {
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index d916651b05617..54deae2426b41 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -1034,6 +1034,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                   Vec::new(),
                   ast::ItemFn(self.fn_decl(inputs, output),
                               ast::Unsafety::Normal,
+                              ast::Constness::NotConst,
                               abi::Rust,
                               generics,
                               body))
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 58b6d96607df7..68f73d096359d 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -729,6 +729,7 @@ impl<'a> MethodDef<'a> {
                 abi: abi,
                 explicit_self: explicit_self,
                 unsafety: ast::Unsafety::Normal,
+                constness: ast::Constness::NotConst,
                 decl: fn_decl
             }, body_block)
         })
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index ee2cf9017bbf3..ea01054741126 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -557,11 +557,12 @@ fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
 /// Expand item_underscore
 fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
     match item {
-        ast::ItemFn(decl, fn_style, abi, generics, body) => {
+        ast::ItemFn(decl, unsafety, constness, abi, generics, body) => {
             let (rewritten_fn_decl, rewritten_body)
                 = expand_and_rename_fn_decl_and_block(decl, body, fld);
             let expanded_generics = fold::noop_fold_generics(generics,fld);
-            ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
+            ast::ItemFn(rewritten_fn_decl, unsafety, constness, abi,
+                        expanded_generics, rewritten_body)
         }
         _ => noop_fold_item_underscore(item, fld)
     }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c3bac0cf57c75..9ae800ba3d093 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -703,7 +703,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                 span: Span,
                 _node_id: NodeId) {
         match fn_kind {
-            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
+            visit::FkItemFn(_, _, _, _, abi) if abi == RustIntrinsic => {
                 self.gate_feature("intrinsics",
                                   span,
                                   "intrinsics are subject to change")
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 105a61d085725..e8dd5402a90d1 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -907,10 +907,11 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
         ItemConst(t, e) => {
             ItemConst(folder.fold_ty(t), folder.fold_expr(e))
         }
-        ItemFn(decl, unsafety, abi, generics, body) => {
+        ItemFn(decl, unsafety, constness, abi, generics, body) => {
             ItemFn(
                 folder.fold_fn_decl(decl),
                 unsafety,
+                constness,
                 abi,
                 folder.fold_generics(generics),
                 folder.fold_block(body)
@@ -1109,6 +1110,8 @@ pub fn noop_fold_method_sig<T: Folder>(sig: MethodSig, folder: &mut T) -> Method
         unsafety: sig.unsafety,
         decl: folder.fold_fn_decl(sig.decl)
     }
+                     constness,
+                         constness,
 }
 
 pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 450c38e83b4d0..ba2fdb9ce7c13 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -17,7 +17,7 @@ use ast::{Public, Unsafety};
 use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
 use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
 use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
-use ast::{Crate, CrateConfig, Decl, DeclItem};
+use ast::{Crate, CrateConfig, Constness, Decl, DeclItem};
 use ast::{DeclLocal, DefaultBlock, DefaultReturn};
 use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
 use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
@@ -1250,7 +1250,7 @@ impl<'a> Parser<'a> {
                 p.expect(&token::Semi);
                 (ident, TypeTraitItem(bounds, default))
             } else {
-                let style = p.parse_unsafety();
+                let unsafety = p.parse_unsafety();
                 let abi = if p.eat_keyword(keywords::Extern) {
                     p.parse_opt_abi().unwrap_or(abi::C)
                 } else {
@@ -1271,6 +1271,7 @@ impl<'a> Parser<'a> {
                 p.parse_where_clause(&mut generics);
                 let sig = ast::MethodSig {
                     unsafety: style,
+                    constness: ast::Constness::NotConst;
                     decl: d,
                     generics: generics,
                     abi: abi,
@@ -4543,16 +4544,20 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an item-position function declaration.
-    fn parse_item_fn(&mut self, unsafety: Unsafety, abi: abi::Abi) -> ItemInfo {
+    fn parse_item_fn(&mut self,
+                     unsafety: Unsafety,
+                     constness: Constness,
+                     abi: abi::Abi) -> ItemInfo {
         let (ident, mut generics) = self.parse_fn_header();
         let decl = self.parse_fn_decl(false);
         self.parse_where_clause(&mut generics);
         let (inner_attrs, body) = self.parse_inner_attrs_and_block();
-        (ident, ItemFn(decl, unsafety, abi, generics, body), Some(inner_attrs))
+        (ident, ItemFn(decl, unsafety, constness, abi, generics, body),
+         Some(inner_attrs))
     }
 
     /// Parse an impl item.
-    pub fn parse_impl_item(&mut self) -> P<ImplItem> {
+    pub fn parse_impl_item(&mut self, is_trait_impl: bool) -> P<ImplItem> {
         let lo = self.span.lo;
         let mut attrs = self.parse_outer_attributes();
         let vis = self.parse_visibility();
@@ -4563,7 +4568,8 @@ impl<'a> Parser<'a> {
             self.expect(&token::Semi);
             (name, TypeImplItem(typ))
         } else {
-            let (name, inner_attrs, node) = self.parse_impl_method(vis);
+            let (name, inner_attrs, node) =
+                self.parse_impl_method(vis, is_trait_impl);
             attrs.extend(inner_attrs.into_iter());
             (name, node)
         };
@@ -4590,7 +4596,8 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a method or a macro invocation in a trait impl.
-    fn parse_impl_method(&mut self, vis: Visibility)
+    fn parse_impl_method(&mut self, vis: Visibility,
+                         is_trait_impl: bool)
                          -> (Ident, Vec<ast::Attribute>, ast::ImplItem_) {
         // code copied from parse_macro_use_or_failure... abstraction!
         if !self.token.is_any_keyword()
@@ -4619,11 +4626,17 @@ impl<'a> Parser<'a> {
             }
             (token::special_idents::invalid, vec![], ast::MacImplItem(m))
         } else {
-            let unsafety = self.parse_unsafety();
-            let abi = if self.eat_keyword(keywords::Extern) {
-                self.parse_opt_abi().unwrap_or(abi::C)
+            let is_const_fn = !is_trait_impl && self.eat_keyword(keywords::Const);
+            let (constness, unsafety, abi) = if is_const_fn {
+                (Constness::Const, Unsafety::Normal, abi::Rust)
             } else {
-                abi::Rust
+                let unsafety = self.parse_unsafety();
+                let abi = if self.eat_keyword(keywords::Extern) {
+                    self.parse_opt_abi().unwrap_or(abi::C)
+                } else {
+                    abi::Rust
+                };
+                (Constness::NotConst, unsafety, abi)
             };
             self.expect_keyword(keywords::Fn);
             let ident = self.parse_ident();
@@ -4638,6 +4651,7 @@ impl<'a> Parser<'a> {
                 abi: abi,
                 explicit_self: explicit_self,
                 unsafety: unsafety,
+                constness: constness;
                 decl: decl
              }, body))
         }
@@ -4743,7 +4757,7 @@ impl<'a> Parser<'a> {
 
             let mut impl_items = vec![];
             while !self.eat(&token::CloseDelim(token::Brace)) {
-                impl_items.push(self.parse_impl_item());
+                impl_items.push(self.parse_impl_item(opt_trait.is_some()));
             }
 
             (ast_util::impl_pretty_name(&opt_trait, Some(&*ty)),
@@ -5507,7 +5521,7 @@ impl<'a> Parser<'a> {
                 // EXTERN FUNCTION ITEM
                 let abi = opt_abi.unwrap_or(abi::C);
                 let (ident, item_, extra_attrs) =
-                    self.parse_item_fn(Unsafety::Normal, abi);
+                    self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi);
                 let last_span = self.last_span;
                 let item = self.mk_item(lo,
                                         last_span.hi,
@@ -5546,6 +5560,21 @@ impl<'a> Parser<'a> {
             return Some(item);
         }
         if self.eat_keyword(keywords::Const) {
+            if self.check_keyword(keywords::Fn) {
+                // CONST FUNCTION ITEM
+                self.bump();
+                let (ident, item_, extra_attrs) =
+                    self.parse_item_fn(Unsafety::Normal, Constness::Const, abi::Rust);
+                let last_span = self.last_span;
+                let item = self.mk_item(lo,
+                                        last_span.hi,
+                                        ident,
+                                        item_,
+                                        visibility,
+                                        maybe_append(attrs, extra_attrs));
+                return Ok(item);
+            }
+
             // CONST ITEM
             if self.eat_keyword(keywords::Mut) {
                 let last_span = self.last_span;
@@ -5599,7 +5628,7 @@ impl<'a> Parser<'a> {
             // FUNCTION ITEM
             self.bump();
             let (ident, item_, extra_attrs) =
-                self.parse_item_fn(Unsafety::Normal, abi::Rust);
+                self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi::Rust);
             let last_span = self.last_span;
             let item = self.mk_item(lo,
                                     last_span.hi,
@@ -5620,7 +5649,7 @@ impl<'a> Parser<'a> {
             };
             self.expect_keyword(keywords::Fn);
             let (ident, item_, extra_attrs) =
-                self.parse_item_fn(Unsafety::Unsafe, abi);
+                self.parse_item_fn(Unsafety::Unsafe, Constness::NotConst, abi);
             let last_span = self.last_span;
             let item = self.mk_item(lo,
                                     last_span.hi,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 07303ba51ff70..d7f548f57dbbe 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -371,12 +371,16 @@ pub fn ident_to_string(id: &ast::Ident) -> String {
     $to_string(|s| s.print_ident(*id))
 }
 
-pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident,
-                  opt_explicit_self: Option<&ast::ExplicitSelf_>,
-                  generics: &ast::Generics) -> String {
+pub fn fun_to_string(decl: &ast::FnDecl,
+                     unsafety: ast::Unsafety,
+                     constness: ast::Constness,
+                     name: ast::Ident,
+                     opt_explicit_self: Option<&ast::ExplicitSelf_>,
+                     generics: &ast::Generics)
+                     -> String {
     $to_string(|s| {
         try!(s.head(""));
-        try!(s.print_fn(decl, unsafety, abi::Rust, Some(name),
+        try!(s.print_fn(decl, unsafety, constness, abi::Rust, Some(name),
                         generics, opt_explicit_self, ast::Inherited));
         try!(s.end()); // Close the head box
         s.end() // Close the outer box
@@ -761,7 +765,8 @@ impl<'a> State<'a> {
         match item.node {
             ast::ForeignItemFn(ref decl, ref generics) => {
                 try!(self.head(""));
-                try!(self.print_fn(&**decl, ast::Unsafety::Normal,
+                try!(self.print_fn(decl, ast::Unsafety::Normal,
+                                   ast::Constness::NotConst,
                                    abi::Rust, Some(item.ident),
                                    generics, None, item.vis));
                 try!(self.end()); // end head-ibox
@@ -863,11 +868,12 @@ impl<'a> State<'a> {
                 try!(word(&mut self.s, ";"));
                 try!(self.end()); // end the outer cbox
             }
-            ast::ItemFn(ref decl, unsafety, abi, ref typarams, ref body) => {
+            ast::ItemFn(ref decl, unsafety, constness, abi, ref typarams, ref body) => {
                 try!(self.head(""));
                 try!(self.print_fn(
                     decl,
                     unsafety,
+                    constness,
                     abi,
                     Some(item.ident),
                     typarams,
@@ -1238,6 +1244,7 @@ impl<'a> State<'a> {
                             -> io::Result<()> {
         self.print_fn(&m.decl,
                       m.unsafety,
+                      m.constness,
                       m.abi,
                       Some(ident),
                       &m.generics,
@@ -2317,12 +2324,13 @@ impl<'a> State<'a> {
     pub fn print_fn(&mut self,
                     decl: &ast::FnDecl,
                     unsafety: ast::Unsafety,
+                    constness: ast::Constness,
                     abi: abi::Abi,
                     name: Option<ast::Ident>,
                     generics: &ast::Generics,
                     opt_explicit_self: Option<&ast::ExplicitSelf_>,
                     vis: ast::Visibility) -> io::Result<()> {
-        try!(self.print_fn_header_info(unsafety, abi, vis));
+        try!(self.print_fn_header_info(unsafety, constness, abi, vis));
 
         if let Some(name) = name {
             try!(self.nbsp());
@@ -2699,8 +2707,8 @@ impl<'a> State<'a> {
                        opt_explicit_self: Option<&ast::ExplicitSelf_>)
                        -> io::Result<()> {
         try!(self.ibox(indent_unit));
-        try!(self.print_fn(decl, unsafety, abi, name,
-                           generics, opt_explicit_self,
+        try!(self.print_fn(decl, unsafety, ast::Constness::NotConst,
+                           abi, name, generics, opt_explicit_self,
                            ast::Inherited));
         self.end()
     }
@@ -2949,11 +2957,17 @@ impl<'a> State<'a> {
 
     pub fn print_fn_header_info(&mut self,
                                 unsafety: ast::Unsafety,
+                                constness: ast::Constness,
                                 abi: abi::Abi,
                                 vis: ast::Visibility) -> io::Result<()> {
         try!(word(&mut self.s, &visibility_qualified(vis, "")));
         try!(self.print_unsafety(unsafety));
 
+        match constness {
+            ast::Constness::NotConst => {}
+            ast::Constness::Const => try!(self.word_nbsp("unsafe"))
+        }
+
         if abi != abi::Rust {
             try!(self.word_nbsp("extern"));
             try!(self.word_nbsp(&abi.to_string()));
@@ -2991,8 +3005,10 @@ mod test {
             variadic: false
         };
         let generics = ast_util::empty_generics();
-        assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal, abba_ident,
-                               None, &generics),
+        assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal,
+                                 ast::Constness::NotConst,
+                                 abba_ident,
+                                 None, &generics),
                    "fn abba()");
     }
 
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 5e858d8a79f76..73fd56948c142 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -123,7 +123,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> {
 
         if is_test_fn(&self.cx, &*i) || is_bench_fn(&self.cx, &*i) {
             match i.node {
-                ast::ItemFn(_, ast::Unsafety::Unsafe, _, _, _) => {
+                ast::ItemFn(_, ast::Unsafety::Unsafe, _, _, _, _) => {
                     let diag = self.cx.span_diagnostic;
                     diag.span_fatal(i.span,
                                     "unsafe functions cannot be used for \
@@ -312,7 +312,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
 
     fn has_test_signature(i: &ast::Item) -> HasTestSignature {
         match &i.node {
-          &ast::ItemFn(ref decl, _, _, ref generics, _) => {
+          &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,
@@ -348,7 +348,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
 
     fn has_test_signature(i: &ast::Item) -> bool {
         match i.node {
-            ast::ItemFn(ref decl, _, _, ref generics, _) => {
+            ast::ItemFn(ref decl, _, _, _, ref generics, _) => {
                 let input_cnt = decl.inputs.len();
                 let no_output = match decl.output {
                     ast::DefaultReturn(..) => true,
@@ -469,7 +469,9 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> {
     let main_ret_ty = ecx.ty(sp, ast::TyTup(vec![]));
     let main_body = ecx.block_all(sp, vec![call_test_main], None);
     let main = ast::ItemFn(ecx.fn_decl(vec![], main_ret_ty),
-                           ast::Unsafety::Normal, ::abi::Rust, empty_generics(), main_body);
+                           ast::Unsafety::Normal,
+                           ast::Constness::NotConst,
+                           ::abi::Rust, empty_generics(), main_body);
     let main = P(ast::Item {
         ident: token::str_to_ident("main"),
         attrs: vec![main_attr],
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 638ddd3ea2e5b..0663d49d88eee 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -35,7 +35,7 @@ use owned_slice::OwnedSlice;
 #[derive(Copy)]
 pub enum FnKind<'a> {
     /// fn foo() or extern "Abi" fn foo()
-    FkItemFn(Ident, &'a Generics, Unsafety, Abi),
+    FkItemFn(Ident, &'a Generics, Unsafety, Constness, Abi),
 
     /// fn foo(&self)
     FkMethod(Ident, &'a MethodSig),
@@ -246,8 +246,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
             visitor.visit_ty(&**typ);
             visitor.visit_expr(&**expr);
         }
-        ItemFn(ref declaration, fn_style, abi, ref generics, ref body) => {
-            visitor.visit_fn(FkItemFn(item.ident, generics, fn_style, abi),
+        ItemFn(ref declaration, unsafety, constness, abi, ref generics, ref body) => {
+            visitor.visit_fn(FkItemFn(item.ident, generics, unsafety, constness, abi),
                              &**declaration,
                              &**body,
                              item.span,
@@ -600,7 +600,7 @@ pub fn walk_fn<'v, V: Visitor<'v>>(visitor: &mut V,
     walk_fn_decl(visitor, function_declaration);
 
     match function_kind {
-        FkItemFn(_, generics, _, _) => {
+        FkItemFn(_, generics, _, _, _) => {
             visitor.visit_generics(generics);
         }
         FkMethod(_, sig) => {

From 1b207f12c866f46c2bda3106f73d7822bc03e965 Mon Sep 17 00:00:00 2001
From: Eduard Burtescu <edy.burt@gmail.com>
Date: Wed, 25 Feb 2015 22:06:08 +0200
Subject: [PATCH 3/4] rustc: const-qualify `const fn` function and method
 calls.

---
 src/librustc/diagnostics.rs        |   1 +
 src/librustc/metadata/common.rs    |   2 +
 src/librustc/metadata/csearch.rs   |   5 +
 src/librustc/metadata/decoder.rs   |  21 ++++
 src/librustc/metadata/encoder.rs   |  22 +++-
 src/librustc/middle/check_const.rs | 191 ++++++++++++++++++++++-------
 src/librustc/middle/const_eval.rs  |  61 ++++++++-
 src/librustc/middle/ty.rs          |   5 +-
 src/libsyntax/ast_map/blocks.rs    |  36 +++---
 9 files changed, 282 insertions(+), 62 deletions(-)

diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 133bef30e4083..532f5d544423d 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -67,6 +67,7 @@ register_diagnostics! {
     E0018,
     E0019,
     E0020,
+    E0021,
     E0022,
     E0109,
     E0110,
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 081c64ecae881..d746173b2d760 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -256,3 +256,5 @@ pub const tag_codemap_filemap: uint = 0xa2;
 pub const tag_item_super_predicates: uint = 0xa3;
 
 pub const tag_defaulted_trait: uint = 0xa4;
+
+pub const tag_items_data_item_constness: uint = 0xa5;
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index ed5783c8dba66..c6ba7dac473da 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -377,6 +377,11 @@ pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool {
     decoder::is_typedef(&*cdata, did.node)
 }
 
+pub fn is_const_fn(cstore: &cstore::CStore, did: ast::DefId) -> bool {
+    let cdata = cstore.get_crate_data(did.krate);
+    decoder::is_const_fn(&*cdata, did.node)
+}
+
 pub fn get_stability(cstore: &cstore::CStore,
                      def: ast::DefId)
                      -> Option<attr::Stability> {
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index dbbc17c018a2f..f2835d03da519 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -176,6 +176,19 @@ fn item_visibility(item: rbml::Doc) -> ast::Visibility {
     }
 }
 
+fn fn_constness(item: rbml::Doc) -> ast::Constness {
+    match reader::maybe_get_doc(item, tag_items_data_item_constness) {
+        None => ast::Constness::NotConst,
+        Some(constness_doc) => {
+            match reader::doc_as_u8(constness_doc) as char {
+                'c' => ast::Constness::Const,
+                'n' => ast::Constness::NotConst,
+                _ => panic!("unknown constness character")
+            }
+        }
+    }
+}
+
 fn item_sort(item: rbml::Doc) -> Option<char> {
     let mut ret = None;
     reader::tagged_docs(item, tag_item_trait_item_sort, |doc| {
@@ -1451,6 +1464,14 @@ pub fn is_typedef(cdata: Cmd, id: ast::NodeId) -> bool {
     }
 }
 
+pub fn is_const_fn(cdata: Cmd, id: ast::NodeId) -> bool {
+    let item_doc = lookup_item(id, cdata.data());
+    match fn_constness(item_doc) {
+        ast::Constness::Const => true,
+        ast::Constness::NotConst => false,
+    }
+}
+
 fn doc_generics<'tcx>(base_doc: rbml::Doc,
                       tcx: &ty::ctxt<'tcx>,
                       cdata: Cmd,
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 644f36262c7b9..9bbfc92cfdf85 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -581,6 +581,16 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: ast::Visibility) {
     rbml_w.wr_tagged_u8(tag_items_data_item_visibility, ch as u8);
 }
 
+fn encode_constness(rbml_w: &mut Encoder, constness: ast::Constness) {
+    rbml_w.start_tag(tag_items_data_item_constness);
+    let ch = match constness {
+        ast::Constness::Const => 'c',
+        ast::Constness::NotConst => 'n',
+    };
+    rbml_w.wr_str(&ch.to_string());
+    rbml_w.end_tag();
+}
+
 fn encode_explicit_self(rbml_w: &mut Encoder,
                         explicit_self: &ty::ExplicitSelfCategory) {
     let tag = tag_item_trait_method_explicit_self;
@@ -831,10 +841,14 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_attributes(rbml_w, &impl_item.attrs);
             let scheme = ty::lookup_item_type(ecx.tcx, m.def_id);
             let any_types = !scheme.generics.types.is_empty();
-            if any_types || is_default_impl || attr::requests_inline(&impl_item.attrs) {
+            let needs_inline = any_types || is_default_impl ||
+                               attr::requests_inline(&impl_item.attrs);
+            let constness = ast_method.pe_constness();
+            if needs_inline || constness == ast::Constness::Const {
                 encode_inlined_item(ecx, rbml_w, IIImplItemRef(local_def(parent_id),
                                                                impl_item));
             }
+            encode_constness(rbml_w, constness);
             if !any_types {
                 encode_symbol(ecx, rbml_w, m.def_id.node);
             }
@@ -1015,7 +1029,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_stability(rbml_w, stab);
         rbml_w.end_tag();
       }
-      ast::ItemFn(ref decl, _, _, _, ref generics, _) => {
+      ast::ItemFn(ref decl, _, constness, _, ref generics, _) => {
         add_to_index(item, rbml_w, index);
         rbml_w.start_tag(tag_items_data_item);
         encode_def_id(rbml_w, def_id);
@@ -1025,12 +1039,14 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_name(rbml_w, item.ident.name);
         encode_path(rbml_w, path);
         encode_attributes(rbml_w, &item.attrs);
-        if tps_len > 0 || attr::requests_inline(&item.attrs) {
+        let needs_inline = tps_len > 0 || attr::requests_inline(&item.attrs);
+        if needs_inline || constness == ast::Constness::Const {
             encode_inlined_item(ecx, rbml_w, IIItemRef(item));
         }
         if tps_len == 0 {
             encode_symbol(ecx, rbml_w, item.id);
         }
+        encode_constness(rbml_w, constness);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
         encode_method_argument_names(rbml_w, &**decl);
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 497022ac6ac49..d7fdb5ff6ce83 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -35,6 +35,7 @@ use util::nodemap::NodeMap;
 use util::ppaux;
 
 use syntax::ast;
+use syntax::ast_util::PostExpansionMethod;
 use syntax::codemap::Span;
 use syntax::print::pprust;
 use syntax::visit::{self, Visitor};
@@ -79,6 +80,7 @@ bitflags! {
 #[derive(Copy, Eq, PartialEq)]
 enum Mode {
     Const,
+    ConstFn,
     Static,
     StaticMut,
 
@@ -136,10 +138,87 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
         })
     }
 
+    fn fn_like(&mut self,
+               fk: visit::FnKind,
+               fd: &ast::FnDecl,
+               b: &ast::Block,
+               s: Span,
+               fn_id: ast::NodeId)
+               -> ConstQualif {
+        match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
+            Entry::Occupied(entry) => return *entry.get(),
+            Entry::Vacant(entry) => {
+                // Prevent infinite recursion on re-entry.
+                entry.insert(PURE_CONST);
+            }
+        }
+
+        let mode = match fk {
+            visit::FkItemFn(_, _, _, ast::Constness::Const, _) => {
+                Mode::ConstFn
+            }
+            visit::FkMethod(_, _, m) => {
+                if m.pe_constness() == ast::Constness::Const {
+                    Mode::ConstFn
+                } else {
+                    Mode::Var
+                }
+            }
+            _ => Mode::Var
+        };
+
+        // Ensure the arguments are simple, not mutable/by-ref or patterns.
+        if mode == Mode::ConstFn {
+            for arg in &fd.inputs {
+                match arg.pat.node {
+                    ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {}
+                    _ => {
+                        span_err!(self.tcx.sess, arg.pat.span, E0022,
+                                  "arguments of constant functions can only \
+                                   be immutable by-value bindings");
+                    }
+                }
+            }
+        }
+
+        let qualif = self.with_mode(mode, |this| {
+            this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
+            visit::walk_fn(this, fk, fd, b, s);
+            this.qualif
+        });
+
+        // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
+        // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
+        let qualif = qualif & (NON_ZERO_SIZED | PREFER_IN_PLACE);
+
+        self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
+        qualif
+    }
+
     fn add_qualif(&mut self, qualif: ConstQualif) {
         self.qualif = self.qualif | qualif;
     }
 
+    /// Returns true if the call is to a const fn or method.
+    fn handle_const_fn_call(&mut self, def_id: ast::DefId, ret_ty: Ty<'tcx>) -> bool {
+        if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
+            let qualif = self.fn_like(fn_like.kind(),
+                                      fn_like.decl(),
+                                      fn_like.body(),
+                                      fn_like.span(),
+                                      fn_like.id());
+            self.add_qualif(qualif);
+
+            if ty::type_contents(self.tcx, ret_ty).interior_unsafe() {
+                self.add_qualif(MUTABLE_MEM);
+            }
+
+            true
+        } else {
+            false
+        }
+    }
+
     fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) {
         match self.rvalue_borrows.entry(id) {
             Entry::Occupied(mut entry) => {
@@ -158,6 +237,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
     fn msg(&self) -> &'static str {
         match self.mode {
             Mode::Const => "constant",
+            Mode::ConstFn => "constant function",
             Mode::StaticMut | Mode::Static => "static",
             Mode::Var => unreachable!(),
         }
@@ -229,9 +309,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                 b: &'v ast::Block,
                 s: Span,
                 fn_id: ast::NodeId) {
-        assert!(self.mode == Mode::Var);
-        self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
-        visit::walk_fn(self, fk, fd, b, s);
+        self.fn_like(fk, fd, b, s, fn_id);
     }
 
     fn visit_pat(&mut self, p: &ast::Pat) {
@@ -247,6 +325,35 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
         }
     }
 
+    fn visit_block(&mut self, block: &ast::Block) {
+        // Check all statements in the block
+        for stmt in &block.stmts {
+            let span = match stmt.node {
+                ast::StmtDecl(ref decl, _) => {
+                    match decl.node {
+                        ast::DeclLocal(_) => decl.span,
+
+                        // Item statements are allowed
+                        ast::DeclItem(_) => continue
+                    }
+                }
+                ast::StmtExpr(ref expr, _) => expr.span,
+                ast::StmtSemi(ref semi, _) => semi.span,
+                ast::StmtMac(..) => {
+                    self.tcx.sess.span_bug(stmt.span, "unexpanded statement \
+                                                       macro in const?!")
+                }
+            };
+            self.add_qualif(NOT_CONST);
+            if self.mode != Mode::Var {
+                span_err!(self.tcx.sess, span, E0016,
+                          "blocks in {}s are limited to items and \
+                           tail expressions", self.msg());
+            }
+        }
+        visit::walk_block(self, block);
+    }
+
     fn visit_expr(&mut self, ex: &ast::Expr) {
         let mut outer = self.qualif;
         self.qualif = PURE_CONST;
@@ -460,10 +567,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 Some(def::DefStatic(..)) => {
                     match v.mode {
                         Mode::Static | Mode::StaticMut => {}
-                        Mode::Const => {
+                        Mode::Const | Mode::ConstFn => {
                             span_err!(v.tcx.sess, e.span, E0013,
-                                "constants cannot refer to other statics, \
-                                 insert an intermediate constant instead");
+                                "{}s cannot refer to other statics, insert \
+                                 an intermediate constant instead", v.msg());
                         }
                         Mode::Var => v.add_qualif(NOT_CONST)
                     }
@@ -477,6 +584,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                                                      to an ItemConst");
                     }
                 }
+                Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => {
+                    // Sadly, we can't determine whether the types are zero-sized.
+                    v.add_qualif(NOT_CONST | NON_ZERO_SIZED);
+                }
                 def => {
                     v.add_qualif(NOT_CONST);
                     if v.mode != Mode::Var {
@@ -501,48 +612,44 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 };
             }
             let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
-            match def {
-                Some(def::DefStruct(..)) => {}
+            let is_const = match def {
+                Some(def::DefStruct(..)) => true,
                 Some(def::DefVariant(..)) => {
                     // Count the discriminator.
                     v.add_qualif(NON_ZERO_SIZED);
+                    true
                 }
-                _ => {
-                    v.add_qualif(NOT_CONST);
-                    if v.mode != Mode::Var {
-                        span_err!(v.tcx.sess, e.span, E0015,
-                                  "function calls in {}s are limited to \
-                                   struct and enum constructors", v.msg());
-                    }
+                Some(def::DefMethod(did, def::FromImpl(_))) |
+                Some(def::DefFn(did, _)) => {
+                    v.handle_const_fn_call(did, node_ty)
                 }
-            }
-        }
-        ast::ExprBlock(ref block) => {
-            // Check all statements in the block
-            let mut block_span_err = |span| {
+                _ => false
+            };
+            if !is_const {
                 v.add_qualif(NOT_CONST);
                 if v.mode != Mode::Var {
-                    span_err!(v.tcx.sess, span, E0016,
-                              "blocks in {}s are limited to items and \
-                               tail expressions", v.msg());
+                    span_err!(v.tcx.sess, e.span, E0015,
+                              "function calls in {}s are limited to \
+                               constant functions, \
+                               struct and enum constructors", v.msg());
                 }
+            }
+        }
+        ast::ExprMethodCall(..) => {
+            let method_did = match v.tcx.method_map.borrow()[method_call].origin {
+                ty::MethodStatic(did) => Some(did),
+                _ => None
             };
-            for stmt in &block.stmts {
-                match stmt.node {
-                    ast::StmtDecl(ref decl, _) => {
-                        match decl.node {
-                            ast::DeclLocal(_) => block_span_err(decl.span),
-
-                            // Item statements are allowed
-                            ast::DeclItem(_) => {}
-                        }
-                    }
-                    ast::StmtExpr(ref expr, _) => block_span_err(expr.span),
-                    ast::StmtSemi(ref semi, _) => block_span_err(semi.span),
-                    ast::StmtMac(..) => {
-                        v.tcx.sess.span_bug(e.span, "unexpanded statement \
-                                                     macro in const?!")
-                    }
+            let is_const = match method_did {
+                Some(did) => v.handle_const_fn_call(did, node_ty),
+                None => false
+            };
+            if !is_const {
+                v.add_qualif(NOT_CONST);
+                if v.mode != Mode::Var {
+                    span_err!(v.tcx.sess, e.span, E0021,
+                              "method calls in {}s are limited to \
+                               constant inherent methods", v.msg());
                 }
             }
         }
@@ -563,7 +670,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         }
 
         ast::ExprClosure(..) => {
-            // Paths in constant constexts cannot refer to local variables,
+            // Paths in constant contexts cannot refer to local variables,
             // as there are none, and thus closures can't have upvars there.
             if ty::with_freevars(v.tcx, e.id, |fv| !fv.is_empty()) {
                 assert!(v.mode == Mode::Var,
@@ -572,6 +679,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             }
         }
 
+        ast::ExprBlock(_) |
         ast::ExprUnary(..) |
         ast::ExprBinary(..) |
         ast::ExprIndex(..) |
@@ -600,8 +708,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         // Miscellaneous expressions that could be implemented.
         ast::ExprRange(..) |
 
-        // Various other expressions.
-        ast::ExprMethodCall(..) |
+        // Expressions with side-effects.
         ast::ExprAssign(..) |
         ast::ExprAssignOp(..) |
         ast::ExprInlineAsm(_) |
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 96433729a9b96..06b0fc2669020 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -20,10 +20,12 @@ use middle::ty::{self, Ty};
 use middle::astconv_util::ast_ty_to_prim_ty;
 
 use syntax::ast::{self, Expr};
+use syntax::ast_map::blocks::FnLikeNode;
+use syntax::ast_util::{self, PostExpansionMethod};
 use syntax::codemap::Span;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
-use syntax::{ast_map, ast_util, codemap};
+use syntax::{ast_map, codemap, visit};
 
 use std::borrow::{Cow, IntoCow};
 use std::num::wrapping::OverflowingOps;
@@ -132,6 +134,63 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
     }
 }
 
+fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: ast::DefId)
+                                       -> Option<ast::NodeId> {
+    match tcx.extern_const_fns.borrow().get(&def_id) {
+        Some(&ast::DUMMY_NODE_ID) => return None,
+        Some(&fn_id) => return Some(fn_id),
+        None => {}
+    }
+
+    if !csearch::is_const_fn(&tcx.sess.cstore, def_id) {
+        tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID);
+        return None;
+    }
+
+    let fn_id = match csearch::maybe_get_item_ast(tcx, def_id,
+        box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
+        csearch::FoundAst::Found(&ast::IIItem(ref item)) => Some(item.id),
+        csearch::FoundAst::Found(&ast::IIImplItem(_, ast::MethodImplItem(ref m))) => Some(m.id),
+        _ => None
+    };
+    tcx.extern_const_fns.borrow_mut().insert(def_id,
+                                             fn_id.unwrap_or(ast::DUMMY_NODE_ID));
+    fn_id
+}
+
+pub fn lookup_const_fn_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
+                                 -> Option<FnLikeNode<'a>> {
+
+    let fn_id = if !ast_util::is_local(def_id) {
+        if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) {
+            fn_id
+        } else {
+            return None;
+        }
+    } else {
+        def_id.node
+    };
+
+    let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) {
+        Some(fn_like) => fn_like,
+        None => return None
+    };
+
+    match fn_like.kind() {
+        visit::FkItemFn(_, _, _, ast::Constness::Const, _) => {
+            Some(fn_like)
+        }
+        visit::FkMethod(_, _, m) => {
+            if m.pe_constness() == ast::Constness::Const {
+                Some(fn_like)
+            } else {
+                None
+            }
+        }
+        _ => None
+    }
+}
+
 #[derive(Clone, PartialEq)]
 pub enum const_val {
     const_float(f64),
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 196dd36493c4e..f403cf82f2826 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -791,10 +791,10 @@ pub struct ctxt<'tcx> {
     /// Borrows
     pub upvar_capture_map: RefCell<UpvarCaptureMap>,
 
-    /// These two caches are used by const_eval when decoding external statics
-    /// and variants that are found.
+    /// These caches are used by const_eval when decoding external constants.
     pub extern_const_statics: RefCell<DefIdMap<ast::NodeId>>,
     pub extern_const_variants: RefCell<DefIdMap<ast::NodeId>>,
+    pub extern_const_fns: RefCell<DefIdMap<ast::NodeId>>,
 
     pub method_map: MethodMap<'tcx>,
 
@@ -2602,6 +2602,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
         upvar_capture_map: RefCell::new(FnvHashMap()),
         extern_const_statics: RefCell::new(DefIdMap()),
         extern_const_variants: RefCell::new(DefIdMap()),
+        extern_const_fns: RefCell::new(DefIdMap()),
         method_map: RefCell::new(FnvHashMap()),
         dependency_formats: RefCell::new(FnvHashMap()),
         closure_kinds: RefCell::new(DefIdMap()),
diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs
index d659519013c48..a6f574a5a4be2 100644
--- a/src/libsyntax/ast_map/blocks.rs
+++ b/src/libsyntax/ast_map/blocks.rs
@@ -96,20 +96,10 @@ impl<'a> Code<'a> {
 
     /// Attempts to construct a Code from presumed FnLike or Block node input.
     pub fn from_node(node: Node) -> Option<Code> {
-        fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } }
-        match node {
-            ast_map::NodeItem(item) if item.is_fn_like() =>
-                Some(FnLikeCode(new(node))),
-            ast_map::NodeTraitItem(tm) if tm.is_fn_like() =>
-                Some(FnLikeCode(new(node))),
-            ast_map::NodeImplItem(_) =>
-                Some(FnLikeCode(new(node))),
-            ast_map::NodeExpr(e) if e.is_fn_like() =>
-                Some(FnLikeCode(new(node))),
-            ast_map::NodeBlock(block) =>
-                Some(BlockCode(block)),
-            _ =>
-                None,
+        if let ast_map::NodeBlock(block) = node {
+            Some(BlockCode(block))
+        } else {
+            FnLikeNode::from_node(node).map(|fn_like| FnLikeCode(fn_like))
         }
     }
 }
@@ -144,6 +134,24 @@ impl<'a> ClosureParts<'a> {
 }
 
 impl<'a> FnLikeNode<'a> {
+    /// Attempts to construct a FnLikeNode from presumed FnLike node input.
+    pub fn from_node(node: Node) -> Option<FnLikeNode> {
+        let fn_like = match node {
+            ast_map::NodeItem(item) => item.is_fn_like(),
+            ast_map::NodeTraitItem(tm) => tm.is_fn_like(),
+            ast_map::NodeImplItem(_) => true,
+            ast_map::NodeExpr(e) => e.is_fn_like(),
+            _ => false
+        };
+        if fn_like {
+            Some(FnLikeNode {
+                node: node
+            })
+        } else {
+            None
+        }
+    }
+
     pub fn to_fn_parts(self) -> FnParts<'a> {
         FnParts {
             decl: self.decl(),

From c39d45803d5c9e5b43591db2ccaef5616d9ebfeb Mon Sep 17 00:00:00 2001
From: Eduard Burtescu <edy.burt@gmail.com>
Date: Wed, 25 Feb 2015 22:06:57 +0200
Subject: [PATCH 4/4] rustc_trans: evaluate `const fn` function and method
 calls.

---
 src/librustc_trans/trans/_match.rs |   6 +-
 src/librustc_trans/trans/base.rs   |   2 +-
 src/librustc_trans/trans/consts.rs | 118 ++++++++++++++++++++++-------
 3 files changed, 93 insertions(+), 33 deletions(-)

diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index 2ab6f5b0f9521..dd2ac9ee4f918 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -278,14 +278,14 @@ impl<'a, 'tcx> Opt<'a, 'tcx> {
         match *self {
             ConstantValue(ConstantExpr(lit_expr), _) => {
                 let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id);
-                let (llval, _) = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs);
+                let (llval, _) = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs, None);
                 let lit_datum = immediate_rvalue(llval, lit_ty);
                 let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
                 SingleResult(Result::new(bcx, lit_datum.val))
             }
             ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2), _) => {
-                let (l1, _) = consts::const_expr(ccx, &**l1, bcx.fcx.param_substs);
-                let (l2, _) = consts::const_expr(ccx, &**l2, bcx.fcx.param_substs);
+                let (l1, _) = consts::const_expr(ccx, &**l1, bcx.fcx.param_substs, None);
+                let (l2, _) = consts::const_expr(ccx, &**l2, bcx.fcx.param_substs, None);
                 RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
             }
             Variant(disr_val, ref repr, _, _) => {
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 49c27dbac8cdc..ec578c80fa1f6 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -2743,7 +2743,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                     // We need the translated value here, because for enums the
                     // LLVM type is not fully determined by the Rust type.
                     let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
-                    let (v, ty) = consts::const_expr(ccx, &**expr, empty_substs);
+                    let (v, ty) = consts::const_expr(ccx, &**expr, empty_substs, None);
                     ccx.static_values().borrow_mut().insert(id, v);
                     unsafe {
                         // boolean SSA values are i1, but they have to be stored in i8 slots,
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 8dcabe0a94bb8..ddf1fa4ae5c15 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -23,12 +23,15 @@ use trans::type_of;
 use middle::subst::Substs;
 use middle::ty::{self, Ty};
 use util::ppaux::{Repr, ty_to_string};
+use util::nodemap::NodeMap;
 
 use std::iter::repeat;
 use libc::c_uint;
 use syntax::{ast, ast_util};
 use syntax::ptr::P;
 
+type FnArgMap<'a> = Option<&'a NodeMap<ValueRef>>;
+
 pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit)
     -> ValueRef {
     let _icx = push_ctxt("trans_lit");
@@ -157,6 +160,29 @@ fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     }
 }
 
+fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                           node: ExprOrMethodCall,
+                           def_id: ast::DefId,
+                           arg_vals: &[ValueRef],
+                           param_substs: &'tcx Substs<'tcx>) -> ValueRef {
+    let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id);
+    let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call");
+
+    let args = &fn_like.decl().inputs;
+    assert_eq!(args.len(), arg_vals.len());
+
+    let arg_ids = args.iter().map(|arg| arg.pat.id);
+    let fn_args = arg_ids.zip(arg_vals.iter().cloned()).collect();
+
+    let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs));
+    match fn_like.body().expr {
+        Some(ref expr) => {
+            const_expr(ccx, &**expr, substs, Some(&fn_args)).0
+        }
+        None => C_nil(ccx)
+    }
+}
+
 pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 def_id: ast::DefId,
                                 ref_expr: &ast::Expr)
@@ -217,9 +243,9 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // references, even when only the latter are correct.
         let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
                                                   &ty::expr_ty(ccx.tcx(), expr));
-        const_expr_unadjusted(ccx, expr, ty, param_substs)
+        const_expr_unadjusted(ccx, expr, ty, param_substs, None)
     } else {
-        const_expr(ccx, expr, param_substs).0
+        const_expr(ccx, expr, param_substs, None).0
     };
 
     // boolean SSA values are i1, but they have to be stored in i8 slots,
@@ -239,11 +265,12 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                             e: &ast::Expr,
-                            param_substs: &'tcx Substs<'tcx>)
+                            param_substs: &'tcx Substs<'tcx>,
+                            fn_args: FnArgMap)
                             -> (ValueRef, Ty<'tcx>) {
     let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs,
                                                &ty::expr_ty(cx.tcx(), e));
-    let llconst = const_expr_unadjusted(cx, e, ety, param_substs);
+    let llconst = const_expr_unadjusted(cx, e, ety, param_substs, fn_args);
     let mut llconst = llconst;
     let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs,
                                                             &ty::expr_ty_adjusted(cx.tcx(), e));
@@ -352,10 +379,12 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                    e: &ast::Expr,
                                    ety: Ty<'tcx>,
-                                   param_substs: &'tcx Substs<'tcx>) -> ValueRef {
-    let map_list = |exprs: &[P<ast::Expr>]| {
-        exprs.iter().map(|e| const_expr(cx, &**e, param_substs).0)
-             .fold(Vec::new(), |mut l, val| { l.push(val); l })
+                                   param_substs: &'tcx Substs<'tcx>,
+                                   fn_args: FnArgMap)
+                                   -> ValueRef {
+    let map_list = |exprs: &[P<ast::Expr>]| -> Vec<ValueRef> {
+        exprs.iter().map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
+             .collect()
     };
     unsafe {
         let _icx = push_ctxt("const_expr");
@@ -366,7 +395,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
           ast::ExprBinary(b, ref e1, ref e2) => {
             /* Neither type is bottom, and we expect them to be unified
              * already, so the following is safe. */
-            let (te1, ty) = const_expr(cx, &**e1, param_substs);
+            let (te1, ty) = const_expr(cx, &**e1, param_substs, fn_args);
             let is_simd = ty::type_is_simd(cx.tcx(), ty);
             let intype = if is_simd {
                 ty::simd_type(cx.tcx(), ty)
@@ -376,7 +405,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             let is_float = ty::type_is_fp(intype);
             let signed = ty::type_is_signed(intype);
 
-            let (te2, _) = const_expr(cx, &**e2, param_substs);
+            let (te2, _) = const_expr(cx, &**e2, param_substs, fn_args);
             let te2 = base::cast_shift_const_rhs(b, te1, te2);
 
             match b.node {
@@ -431,7 +460,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
           },
           ast::ExprUnary(u, ref e) => {
-            let (te, ty) = const_expr(cx, &**e, param_substs);
+            let (te, ty) = const_expr(cx, &**e, param_substs, fn_args);
             let is_float = ty::type_is_fp(ty);
             match u {
               ast::UnUniq | ast::UnDeref => {
@@ -445,7 +474,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
           }
           ast::ExprField(ref base, field) => {
-              let (bv, bt) = const_expr(cx, &**base, param_substs);
+              let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
               let brepr = adt::represent_type(cx, bt);
               expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| {
                   let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys);
@@ -453,7 +482,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
               })
           }
           ast::ExprTupField(ref base, idx) => {
-              let (bv, bt) = const_expr(cx, &**base, param_substs);
+              let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
               let brepr = adt::represent_type(cx, bt);
               expr::with_field_tys(cx.tcx(), bt, None, |discr, _| {
                   adt::const_get_field(cx, &*brepr, bv, discr, idx.node)
@@ -461,7 +490,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
           }
 
           ast::ExprIndex(ref base, ref index) => {
-              let (bv, bt) = const_expr(cx, &**base, param_substs);
+              let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
               let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) {
                   Ok(const_eval::const_int(i)) => i as u64,
                   Ok(const_eval::const_uint(u)) => u,
@@ -512,7 +541,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
           }
           ast::ExprCast(ref base, _) => {
             let llty = type_of::type_of(cx, ety);
-            let (v, basety) = const_expr(cx, &**base, param_substs);
+            let (v, basety) = const_expr(cx, &**base, param_substs, fn_args);
             if expr::cast_is_noop(basety, ety) {
                 return v;
             }
@@ -590,12 +619,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
               } else {
                   // If this isn't the address of a static, then keep going through
                   // normal constant evaluation.
-                  let (v, _) = const_expr(cx, &**sub, param_substs);
+                  let (v, _) = const_expr(cx, &**sub, param_substs, fn_args);
                   addr_of(cx, v, "ref", e.id)
               }
           }
           ast::ExprAddrOf(ast::MutMutable, ref sub) => {
-              let (v, _) = const_expr(cx, &**sub, param_substs);
+              let (v, _) = const_expr(cx, &**sub, param_substs, fn_args);
               addr_of_mut(cx, v, "ref_mut_slice", e.id)
           }
           ast::ExprTup(ref es) => {
@@ -607,7 +636,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
               let repr = adt::represent_type(cx, ety);
 
               let base_val = match *base_opt {
-                Some(ref base) => Some(const_expr(cx, &**base, param_substs)),
+                Some(ref base) => Some(const_expr(cx, &**base, param_substs, fn_args)),
                 None => None
               };
 
@@ -615,7 +644,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                   let cs = field_tys.iter().enumerate()
                                     .map(|(ix, &field_ty)| {
                       match fs.iter().find(|f| field_ty.name == f.ident.node.name) {
-                          Some(ref f) => const_expr(cx, &*f.expr, param_substs).0,
+                          Some(ref f) => const_expr(cx, &*f.expr, param_substs, fn_args).0,
                           None => {
                               match base_val {
                                   Some((bv, _)) => {
@@ -640,7 +669,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
           ast::ExprVec(ref es) => {
             let unit_ty = ty::sequence_element_type(cx.tcx(), ety);
             let llunitty = type_of::type_of(cx, unit_ty);
-            let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs).0)
+            let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
                               .collect::<Vec<_>>();
             // If the vector contains enums, an LLVM array won't work.
             if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
@@ -657,7 +686,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 Ok(const_eval::const_uint(i)) => i as uint,
                 _ => cx.sess().span_bug(count.span, "count must be integral const expression.")
             };
-            let unit_val = const_expr(cx, &**elem, param_substs).0;
+            let unit_val = const_expr(cx, &**elem, param_substs, fn_args).0;
             let vs: Vec<_> = repeat(unit_val).take(n).collect();
             if val_ty(unit_val) != llunitty {
                 C_struct(cx, &vs[..], false)
@@ -668,6 +697,13 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
           ast::ExprPath(..) => {
             let def = cx.tcx().def_map.borrow()[e.id].full_def();
             match def {
+                def::DefLocal(id) => {
+                    if let Some(val) = fn_args.and_then(|args| args.get(&id).cloned()) {
+                        val
+                    } else {
+                        cx.sess().span_bug(e.span, "const fn argument not found")
+                    }
+                }
                 def::DefFn(..) | def::DefMethod(..) => {
                     expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
                 }
@@ -703,10 +739,24 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
           }
           ast::ExprCall(ref callee, ref args) => {
-              let opt_def = cx.tcx().def_map.borrow().get(&callee.id).map(|d| d.full_def());
-              let arg_vals = map_list(&args[..]);
-              match opt_def {
-                  Some(def::DefStruct(_)) => {
+              let mut callee = &**callee;
+              loop {
+                  callee = match callee.node {
+                      ast::ExprParen(ref inner) => &**inner,
+                      ast::ExprBlock(ref block) => match block.expr {
+                          Some(ref tail) => &**tail,
+                          None => break
+                      },
+                      _ => break
+                  };
+              }
+              let def = cx.tcx().def_map.borrow()[callee.id].full_def();
+              let arg_vals = map_list(args);
+              match def {
+                  def::DefFn(did, _) | def::DefMethod(did, _) => {
+                      const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs)
+                  }
+                  def::DefStruct(_) => {
                       if ty::type_is_simd(cx.tcx(), ety) {
                           C_vector(&arg_vals[..])
                       } else {
@@ -714,7 +764,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                           adt::trans_const(cx, &*repr, 0, &arg_vals[..])
                       }
                   }
-                  Some(def::DefVariant(enum_did, variant_did, _)) => {
+                  def::DefVariant(enum_did, variant_did, _) => {
                       let repr = adt::represent_type(cx, ety);
                       let vinfo = ty::enum_variant_with_id(cx.tcx(),
                                                            enum_did,
@@ -724,13 +774,23 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                        vinfo.disr_val,
                                        &arg_vals[..])
                   }
-                  _ => cx.sess().span_bug(e.span, "expected a struct or variant def")
+                  _ => cx.sess().span_bug(e.span, "expected a struct, variant, or const fn def")
               }
           }
-          ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs).0,
+          ast::ExprMethodCall(_, _, ref args) => {
+              let arg_vals = map_list(args);
+              let method_call = ty::MethodCall::expr(e.id);
+              let method_did = match cx.tcx().method_map.borrow()[method_call].origin {
+                  ty::MethodStatic(did) => did,
+                  _ => cx.sess().span_bug(e.span, "expected a const method def")
+              };
+              const_fn_call(cx, MethodCallKey(method_call),
+                            method_did, &arg_vals, param_substs)
+          }
+          ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs, fn_args).0,
           ast::ExprBlock(ref block) => {
             match block.expr {
-                Some(ref expr) => const_expr(cx, &**expr, param_substs).0,
+                Some(ref expr) => const_expr(cx, &**expr, param_substs, fn_args).0,
                 None => C_nil(cx)
             }
           }