Auto merge of #25609 - nikomatsakis:const-fn, r=pnkfelix

This is a port of @eddyb's `const-fn` branch. I rebased it, tweaked a few things, and added tests as well as a feature gate. The set of tests is still pretty rudimentary, I'd appreciate suggestions on new tests to write. Also, a double-check that the feature-gate covers all necessary cases.

One question: currently, the feature-gate allows the *use* of const functions from stable code, just not the definition. This seems to fit our usual strategy, and implies that we might (perhaps) allow some constant functions in libstd someday, even before stabilizing const-fn, if we were willing to commit to the existence of const fns but found some details of their impl unsatisfactory.

r? @pnkfelix
This commit is contained in:
bors 2015-05-24 11:12:34 +00:00
commit ba0e1cd814
55 changed files with 848 additions and 212 deletions

View file

@ -845,5 +845,6 @@ register_diagnostics! {
E0314, // closure outlives stack frame
E0315, // cannot invoke closure outside of its lifetime
E0316, // nested quantification of lifetimes
E0370 // discriminant overflow
E0370, // discriminant overflow
E0378 // method calls limited to constant inherent methods
}

View file

@ -262,3 +262,5 @@ pub const tag_item_super_predicates: usize = 0xa3;
pub const tag_defaulted_trait: usize = 0xa4;
pub const tag_impl_coerce_unsized_kind: usize = 0xa5;
pub const tag_items_data_item_constness: usize = 0xa6;

View file

@ -384,6 +384,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> {

View file

@ -178,6 +178,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| {
@ -1525,6 +1538,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,

View file

@ -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;
@ -867,10 +877,13 @@ 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);
if needs_inline || sig.constness == ast::Constness::Const {
encode_inlined_item(ecx, rbml_w, IIImplItemRef(local_def(parent_id),
impl_item));
}
encode_constness(rbml_w, sig.constness);
if !any_types {
encode_symbol(ecx, rbml_w, m.def_id.node);
}
@ -1049,7 +1062,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);
@ -1059,12 +1072,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);
@ -1967,7 +1982,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);
}

View file

@ -79,6 +79,7 @@ bitflags! {
#[derive(Copy, Clone, Eq, PartialEq)]
enum Mode {
Const,
ConstFn,
Static,
StaticMut,
@ -136,10 +137,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(ConstQualif::empty());
}
}
let mode = match fk {
visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
Mode::ConstFn
}
visit::FkMethod(_, m, _) => {
if m.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 & (ConstQualif::NON_ZERO_SIZED | ConstQualif::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(ConstQualif::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 +236,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!(),
}
@ -251,9 +330,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) {
@ -269,6 +346,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(ConstQualif::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 = ConstQualif::empty();
@ -473,10 +579,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(ConstQualif::NOT_CONST)
}
@ -493,6 +599,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
doesn't point to a constant");
}
}
Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => {
// Sadly, we can't determine whether the types are zero-sized.
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
}
def => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
@ -517,48 +627,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(ConstQualif::NON_ZERO_SIZED);
true
}
_ => {
v.add_qualif(ConstQualif::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)
}
_ => false
};
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0015,
"function calls in {}s are limited to \
constant functions, \
struct and enum constructors", v.msg());
}
}
}
ast::ExprBlock(ref block) => {
// Check all statements in the block
let mut block_span_err = |span| {
ast::ExprMethodCall(..) => {
let method_did = match v.tcx.method_map.borrow()[&method_call].origin {
ty::MethodStatic(did) => Some(did),
_ => None
};
let is_const = match method_did {
Some(did) => v.handle_const_fn_call(did, node_ty),
None => false
};
if !is_const {
v.add_qualif(ConstQualif::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());
}
};
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?!")
}
span_err!(v.tcx.sess, e.span, E0378,
"method calls in {}s are limited to \
constant inherent methods", v.msg());
}
}
}
@ -579,7 +685,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,
@ -588,6 +694,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
}
}
ast::ExprBlock(_) |
ast::ExprUnary(..) |
ast::ExprBinary(..) |
ast::ExprIndex(..) |
@ -616,8 +723,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(_) |

View file

@ -24,11 +24,13 @@ use util::num::ToPrimitive;
use util::ppaux::Repr;
use syntax::ast::{self, Expr};
use syntax::ast_map::blocks::FnLikeNode;
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::feature_gate;
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;
@ -198,6 +200,63 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
}
}
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(_, ref item)) => Some(item.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<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: ast::DefId)
-> Option<FnLikeNode<'tcx>>
{
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.constness == ast::Constness::Const {
Some(fn_like)
} else {
None
}
}
_ => None
}
}
#[derive(Clone, PartialEq)]
pub enum const_val {
const_float(f64),

View file

@ -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),

View file

@ -158,6 +158,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,
@ -826,8 +827,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
}
@ -838,6 +840,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))
@ -852,6 +855,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))
@ -863,12 +867,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);
}
}
@ -1423,12 +1427,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[..]);
@ -1710,7 +1715,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
},

View file

@ -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,
@ -256,7 +256,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);
}
@ -273,7 +273,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)
}

View file

@ -154,7 +154,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| {
this.walk_fn(fk, fd, b, s)
})
@ -447,7 +447,7 @@ impl<'a> LifetimeContext<'a> {
fb: &'b ast::Block,
_span: Span) {
match fk {
visit::FkItemFn(_, generics, _, _, _) => {
visit::FkItemFn(_, generics, _, _, _, _) => {
visit::walk_fn_decl(self, fd);
self.visit_generics(generics);
}

View file

@ -775,10 +775,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>,
@ -2428,7 +2428,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);
@ -2808,6 +2808,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()),