diff options
| author | bors <bors@rust-lang.org> | 2015-05-24 11:12:34 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-05-24 11:12:34 +0000 |
| commit | ba0e1cd8147d452c356aacb29fb87568ca26f111 (patch) | |
| tree | 0cf03636be9210de94e30bb07467097aeb5273c7 /src | |
| parent | cc56c20ba4ccc12f58e1c844b77168bc4805cbb6 (diff) | |
| parent | 82ded3cd03e001e82b9dce9aedac621a5292231b (diff) | |
| download | rust-ba0e1cd8147d452c356aacb29fb87568ca26f111.tar.gz rust-ba0e1cd8147d452c356aacb29fb87568ca26f111.zip | |
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
Diffstat (limited to 'src')
55 files changed, 848 insertions, 212 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index f5cb1bd25d6..b70d73c9141 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 } diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 478c0f2f564..901afc1d190 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -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; diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 8a35c012004..f834076e8ea 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -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> { diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 2c11ee8baa9..ee5fd0202d6 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -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, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 86f33257e09..88e0b739a0f 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; @@ -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); } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 794cc4ff38d..a70df34bd70 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -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) } - } - } - ast::ExprBlock(ref block) => { - // Check all statements in the block - let mut block_span_err = |span| { + _ => 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()); + 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(ConstQualif::NOT_CONST); + if v.mode != Mode::Var { + 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(_) | diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 03de553e648..e5100c32cc2 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -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), diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index b6a070c9332..cfff439f02d 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 8aca64484bf..4ec5cf03364 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -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 }, diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index b532dc88df4..f374c9749c2 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, @@ -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) } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index e7a03a9b7e1..dfdcba1678f 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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); } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 15e1ac2f2c6..5ff6ee3c8b0 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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()), diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f50abc7b3ad..e9db80b71ea 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -994,7 +994,7 @@ impl LintPass for NonSnakeCase { }, _ => (), }, - visit::FkItemFn(ident, _, _, _, _) => { + visit::FkItemFn(ident, _, _, _, _, _) => { self.check_snake_case(cx, "function", &token::get_ident(ident), Some(span)) }, _ => (), @@ -1341,7 +1341,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, _) => { @@ -1870,7 +1870,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 4ea18968d43..30d5a4f111b 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -425,7 +425,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 1f8d23fd4dc..7afc1afc224 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -245,7 +245,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 } @@ -1809,7 +1809,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/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 7f66d3a833f..00a1f728b9b 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -1167,7 +1167,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'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, _, ref expr) => self.process_static_or_const_item(item, typ, expr), diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 84d464e8f07..53a991170c7 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 e44aae76c19..9310d1e202a 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1073,7 +1073,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") @@ -1966,7 +1966,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 @@ -2307,7 +2307,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, @@ -2336,7 +2336,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/consts.rs b/src/librustc_trans/trans/consts.rs index 5ca14b63bce..c11bb922f1c 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -33,6 +33,7 @@ use middle::cast::{CastTy,IntTy}; 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; @@ -40,6 +41,8 @@ use syntax::{ast, ast_util}; use syntax::parse::token; use syntax::ptr::P; +pub 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"); @@ -163,6 +166,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) @@ -221,9 +247,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, @@ -243,11 +269,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)); @@ -440,7 +467,8 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty, fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e: &ast::Expr, ety: Ty<'tcx>, - param_substs: &'tcx Substs<'tcx>) + param_substs: &'tcx Substs<'tcx>, + fn_args: FnArgMap) -> ValueRef { debug!("const_expr_unadjusted(e={}, ety={}, param_substs={})", @@ -448,9 +476,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ety.repr(cx.tcx()), param_substs.repr(cx.tcx())); - 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 }) + 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"); @@ -461,7 +490,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); debug!("const_expr_unadjusted: te1={}, ty={}", cx.tn().val_to_string(te1), ty.repr(cx.tcx())); @@ -474,7 +503,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); check_binary_expr_validity(cx, e, ty, te1, te2); @@ -534,7 +563,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } }, ast::ExprUnary(u, ref inner_e) => { - let (te, ty) = const_expr(cx, &**inner_e, param_substs); + let (te, ty) = const_expr(cx, &**inner_e, param_substs, fn_args); check_unary_expr_validity(cx, e, ty, te); @@ -551,7 +580,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); @@ -559,7 +588,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) @@ -567,7 +596,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, @@ -619,7 +648,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ast::ExprCast(ref base, _) => { let t_cast = ety; let llty = type_of::type_of(cx, t_cast); - let (v, t_expr) = const_expr(cx, &**base, param_substs); + let (v, t_expr) = const_expr(cx, &**base, param_substs, fn_args); debug!("trans_const_cast({} as {})", t_expr.repr(cx.tcx()), t_cast.repr(cx.tcx())); if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) { return v; @@ -707,12 +736,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") } } 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") } ast::ExprTup(ref es) => { @@ -724,7 +753,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 }; @@ -732,7 +761,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, _)) => { @@ -757,7 +786,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) { @@ -770,7 +799,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let unit_ty = ty::sequence_element_type(cx.tcx(), ety); let llunitty = type_of::type_of(cx, unit_ty); let n = ty::eval_repeat_count(cx.tcx(), count); - 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) @@ -781,6 +810,13 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ast::ExprPath(..) => { let def = cx.tcx().def_map.borrow().get(&e.id).unwrap().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 } @@ -816,10 +852,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 { @@ -827,7 +877,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, @@ -837,13 +887,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) } } diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index 4e5407016ba..f9ad3d1a857 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -232,7 +232,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.name, 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 3f44bc40f35..9a53c3f0bcd 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 03fdd0c45c1..d086aa93a6f 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -177,7 +177,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 0e64063d6a4..24f429a9ca0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -741,7 +741,7 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { &enum_definition.variants, it.id); } - ast::ItemFn(_, _, _, _, _) => {} // entirely within check_item_body + ast::ItemFn(..) => {} // entirely within check_item_body ast::ItemImpl(_, _, _, _, _, ref impl_items) => { debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id); match ty::impl_trait_ref(ccx.tcx, local_def(it.id)) { @@ -796,7 +796,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { ty::item_path_str(ccx.tcx, local_def(it.id))); let _indenter = indenter(); match it.node { - 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); @@ -830,11 +830,15 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { check_const(ccx, trait_item.span, &*expr, trait_item.id) } ast::MethodTraitItem(ref sig, Some(ref body)) => { + check_trait_fn_not_const(ccx, trait_item.span, sig.constness); + check_method_body(ccx, &trait_def.generics, sig, body, trait_item.id, trait_item.span); } + ast::MethodTraitItem(ref sig, None) => { + check_trait_fn_not_const(ccx, trait_item.span, sig.constness); + } ast::ConstTraitItem(_, None) | - ast::MethodTraitItem(_, None) | ast::TypeTraitItem(..) => { // Nothing to do. } @@ -845,6 +849,20 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { } } +fn check_trait_fn_not_const<'a,'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + span: Span, + constness: ast::Constness) +{ + match constness { + ast::Constness::NotConst => { + // good + } + ast::Constness::Const => { + span_err!(ccx.tcx.sess, span, E0379, "trait fns cannot be declared const"); + } + } +} + fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, generics: &ast::Generics, item: &ast::Item) { @@ -966,7 +984,9 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } } - ast::MethodImplItem(_, ref body) => { + ast::MethodImplItem(ref sig, ref body) => { + check_trait_fn_not_const(ccx, impl_item.span, sig.constness); + let impl_method_def_id = local_def(impl_item.id); let impl_item_ty = ty::impl_or_trait_item(ccx.tcx, impl_method_def_id); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 014991f7ea5..48876c2a188 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1440,7 +1440,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)); @@ -1492,7 +1492,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/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index b3db7405cfd..5796a5599ce 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1113,6 +1113,7 @@ register_diagnostics! { // fields need coercions E0376, // the trait `CoerceUnsized` may only be implemented for a coercion // between structures - E0377 // the trait `CoerceUnsized` may only be implemented for a coercion + E0377, // the trait `CoerceUnsized` may only be implemented for a coercion // between structures with the same definition + E0379 // trait fns cannot be const } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index eaf7f1f92a7..88ce75486a2 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -215,7 +215,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"); @@ -262,7 +262,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 3ce8835b1a8..9c64b7b4ab6 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -175,6 +175,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, abi: abi, } } @@ -348,6 +349,7 @@ pub fn build_impl(cx: &DocContext, }) => { 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 4c2357f8a8f..045a38228c6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -989,6 +989,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 } @@ -1010,7 +1011,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 } @@ -1075,7 +1077,8 @@ pub struct Function { pub decl: FnDecl, pub generics: Generics, pub unsafety: ast::Unsafety, - pub abi: abi::Abi + pub constness: ast::Constness, + pub abi: abi::Abi, } impl Clean<Item> for doctree::Function { @@ -1091,6 +1094,7 @@ impl Clean<Item> for doctree::Function { decl: self.decl.clean(cx), generics: self.generics.clean(cx), unsafety: self.unsafety, + constness: self.constness, abi: self.abi, }), } @@ -1348,7 +1352,10 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> { generics: generics, self_: self_, decl: decl, - abi: self.fty.abi + abi: self.fty.abi, + + // trait methods canot (currently, at least) be const + constness: ast::Constness::NotConst, }) } else { TyMethodItem(TyMethod { @@ -1356,7 +1363,7 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> { generics: generics, self_: self_, decl: decl, - abi: self.fty.abi + abi: self.fty.abi, }) }; @@ -2453,6 +2460,7 @@ impl Clean<Item> for ast::ForeignItem { generics: generics.clean(cx), unsafety: ast::Unsafety::Unsafe, abi: abi::Rust, + constness: ast::Constness::NotConst, }) } ast::ForeignItemStatic(ref ty, mutbl) => { diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 8fa92304d24..cadd43ec6bf 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -133,6 +133,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, pub abi: abi::Abi, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index bb53d532f52..6e70b3711e4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -35,6 +35,10 @@ pub struct VisSpace(pub Option<ast::Visibility>); /// space after it. #[derive(Copy, Clone)] 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, Clone)] +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 @@ -63,6 +67,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() { @@ -607,6 +617,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 36cf650d54e..582f1716899 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -63,6 +63,7 @@ use clean; use doctree; use fold::DocFolder; use html::escape::Escape; +use html::format::{ConstnessSpace}; use html::format::{TyParamBounds, WhereClause, href, AbiSpace}; use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; use html::highlight; @@ -1753,11 +1754,12 @@ 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}{abi}fn \ + try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}{abi}{constness}fn \ {name}{generics}{decl}{where_clause}</pre>", vis = VisSpace(it.visibility), unsafety = UnsafetySpace(f.unsafety), abi = AbiSpace(f.abi), + constness = ConstnessSpace(f.constness), name = it.name.as_ref().unwrap(), generics = f.generics, where_clause = WhereClause(&f.generics), @@ -1957,10 +1959,16 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item, fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, link: AssocItemLink) -> fmt::Result { - fn method(w: &mut fmt::Formatter, it: &clean::Item, - unsafety: ast::Unsafety, abi: abi::Abi, - g: &clean::Generics, selfty: &clean::SelfTy, - d: &clean::FnDecl, link: AssocItemLink) -> fmt::Result { + 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, + link: AssocItemLink) + -> fmt::Result { use syntax::abi::Abi; let name = it.name.as_ref().unwrap(); @@ -1971,12 +1979,10 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) } }; - write!(w, "{}{}fn <a href='{href}' class='fnname'>{name}</a>\ + write!(w, "{}{}{}fn <a href='{href}' 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()) @@ -1989,11 +1995,12 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, } match meth.inner { clean::TyMethodItem(ref m) => { - method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl, - link) + method(w, meth, m.unsafety, ast::Constness::NotConst, + m.abi, &m.generics, &m.self_, &m.decl, link) } 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, link) } clean::AssociatedConstItem(ref ty, ref default) => { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 4ad693578cc..e86d77a3be0 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -123,7 +123,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 { @@ -136,6 +138,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, generics: gen.clone(), unsafety: *unsafety, + constness: constness, abi: *abi, } } @@ -291,8 +294,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, ref 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 e00cb82649b..5b03b3bf038 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1215,6 +1215,7 @@ pub struct TypeField { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct MethodSig { pub unsafety: Unsafety, + pub constness: Constness, pub abi: Abi, pub decl: P<FnDecl>, pub generics: Generics, @@ -1475,6 +1476,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 { @@ -1761,7 +1768,7 @@ pub enum Item_ { /// A `const` item ItemConst(P<Ty>, P<Expr>), /// A function declaration - ItemFn(P<FnDecl>, Unsafety, Abi, Generics, P<Block>), + ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Block>), /// A module ItemMod(Mod), /// An external module diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 36a7f3a9381..99686d54ce5 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)) } } } @@ -120,6 +110,7 @@ struct ItemFnParts<'a> { ident: ast::Ident, decl: &'a ast::FnDecl, unsafety: ast::Unsafety, + constness: ast::Constness, abi: abi::Abi, vis: ast::Visibility, generics: &'a ast::Generics, @@ -144,6 +135,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(), @@ -180,7 +189,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, p.vis) + visit::FkItemFn(p.ident, p.generics, p.unsafety, p.constness, p.abi, p.vis) }; let closure = |_: ClosureParts| { visit::FkFnBlock @@ -204,10 +213,18 @@ 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, vis: i.vis, 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, + body: &**block, + generics: generics, + abi: abi, + vis: i.vis, + constness: constness, + 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 8471fef3487..bb8096f2770 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -440,7 +440,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 354a0bff749..8a80e291a53 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 b9968ca9608..eab6c3ae725 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -880,6 +880,7 @@ impl<'a> MethodDef<'a> { abi: abi, explicit_self: explicit_self, unsafety: unsafety, + constness: ast::Constness::NotConst, decl: fn_decl }, body_block) }) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 744849de445..aa74c27dc61 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -513,11 +513,12 @@ pub fn expand_item(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) } @@ -1395,6 +1396,7 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>, abi: sig.abi, explicit_self: fld.fold_explicit_self(sig.explicit_self), unsafety: sig.unsafety, + constness: sig.constness, decl: rewritten_fn_decl }, rewritten_body) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index bd73e6b7de7..1177ebdc531 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -155,6 +155,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // Allows the definition of associated constants in `trait` or `impl` // blocks. ("associated_consts", "1.0.0", Active), + + // Allows the definition of `const fn` functions. + ("const_fn", "1.2.0", Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -656,13 +659,26 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { block: &'v ast::Block, span: Span, _node_id: NodeId) { + // check for const fn declarations + match fn_kind { + visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => { + self.gate_feature("const_fn", span, "const fn is unstable"); + } + _ => { + // stability of const fn methods are covered in + // visit_trait_item and visit_impl_item below; this is + // because default methods don't pass through this + // point. + } + } + match fn_kind { - visit::FkItemFn(_, _, _, abi, _) if abi == Abi::RustIntrinsic => { + visit::FkItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => { self.gate_feature("intrinsics", span, "intrinsics are subject to change") } - visit::FkItemFn(_, _, _, abi, _) | + visit::FkItemFn(_, _, _, _, abi, _) | visit::FkMethod(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => { self.gate_feature("unboxed_closures", span, @@ -680,6 +696,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { ti.span, "associated constants are experimental") } + ast::MethodTraitItem(ref sig, _) => { + if sig.constness == ast::Constness::Const { + self.gate_feature("const_fn", ti.span, "const fn is unstable"); + } + } _ => {} } visit::walk_trait_item(self, ti); @@ -692,6 +713,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { ii.span, "associated constants are experimental") } + ast::MethodImplItem(ref sig, _) => { + if sig.constness == ast::Constness::Const { + self.gate_feature("const_fn", ii.span, "const fn is unstable"); + } + } _ => {} } visit::walk_impl_item(self, ii); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 4bf15f509a0..7806a27c53e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -917,10 +917,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) @@ -1124,6 +1125,7 @@ pub fn noop_fold_method_sig<T: Folder>(sig: MethodSig, folder: &mut T) -> Method abi: sig.abi, explicit_self: folder.fold_explicit_self(sig.explicit_self), unsafety: sig.unsafety, + constness: sig.constness, decl: folder.fold_fn_decl(sig.decl) } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 68574560533..d6c28d41447 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -923,6 +923,7 @@ mod tests { variadic: false }), ast::Unsafety::Normal, + ast::Constness::NotConst, abi::Rust, ast::Generics{ // no idea on either of these: lifetimes: Vec::new(), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2b6f91bbc99..eb6420165da 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::{ConstImplItem, ConstTraitItem, Crate, CrateConfig}; +use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig}; use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn}; use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; @@ -1160,7 +1160,8 @@ impl<'a> Parser<'a> { let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param()); try!(p.expect(&token::Semi)); (ident, TypeTraitItem(bounds, default)) - } else if try!(p.eat_keyword(keywords::Const)) { + } else if p.is_const_item() { + try!(p.expect_keyword(keywords::Const)); let ident = try!(p.parse_ident()); try!(p.expect(&token::Colon)); let ty = try!(p.parse_ty_sum()); @@ -1175,13 +1176,7 @@ impl<'a> Parser<'a> { }; (ident, ConstTraitItem(ty, default)) } else { - let style = try!(p.parse_unsafety()); - let abi = if try!(p.eat_keyword(keywords::Extern)) { - try!(p.parse_opt_abi()).unwrap_or(abi::C) - } else { - abi::Rust - }; - try!(p.expect_keyword(keywords::Fn)); + let (constness, unsafety, abi) = try!(p.parse_fn_front_matter()); let ident = try!(p.parse_ident()); let mut generics = try!(p.parse_generics()); @@ -1195,7 +1190,8 @@ impl<'a> Parser<'a> { generics.where_clause = try!(p.parse_where_clause()); let sig = ast::MethodSig { - unsafety: style, + unsafety: unsafety, + constness: constness, decl: d, generics: generics, abi: abi, @@ -4359,12 +4355,46 @@ impl<'a> Parser<'a> { } /// Parse an item-position function declaration. - fn parse_item_fn(&mut self, unsafety: Unsafety, abi: abi::Abi) -> PResult<ItemInfo> { + fn parse_item_fn(&mut self, + unsafety: Unsafety, + constness: Constness, + abi: abi::Abi) + -> PResult<ItemInfo> { let (ident, mut generics) = try!(self.parse_fn_header()); let decl = try!(self.parse_fn_decl(false)); generics.where_clause = try!(self.parse_where_clause()); let (inner_attrs, body) = try!(self.parse_inner_attrs_and_block()); - Ok((ident, ItemFn(decl, unsafety, abi, generics, body), Some(inner_attrs))) + Ok((ident, ItemFn(decl, unsafety, constness, abi, generics, body), Some(inner_attrs))) + } + + /// true if we are looking at `const ID`, false for things like `const fn` etc + pub fn is_const_item(&mut self) -> bool { + self.token.is_keyword(keywords::Const) && + !self.look_ahead(1, |t| t.is_keyword(keywords::Fn)) + } + + /// parses all the "front matter" for a `fn` declaration, up to + /// and including the `fn` keyword: + /// + /// - `const fn` + /// - `unsafe fn` + /// - `extern fn` + /// - etc + pub fn parse_fn_front_matter(&mut self) -> PResult<(ast::Constness, ast::Unsafety, abi::Abi)> { + let is_const_fn = try!(self.eat_keyword(keywords::Const)); + let (constness, unsafety, abi) = if is_const_fn { + (Constness::Const, Unsafety::Normal, abi::Rust) + } else { + let unsafety = try!(self.parse_unsafety()); + let abi = if try!(self.eat_keyword(keywords::Extern)) { + try!(self.parse_opt_abi()).unwrap_or(abi::C) + } else { + abi::Rust + }; + (Constness::NotConst, unsafety, abi) + }; + try!(self.expect_keyword(keywords::Fn)); + Ok((constness, unsafety, abi)) } /// Parse an impl item. @@ -4380,7 +4410,8 @@ impl<'a> Parser<'a> { let typ = try!(self.parse_ty_sum()); try!(self.expect(&token::Semi)); (name, TypeImplItem(typ)) - } else if try!(self.eat_keyword(keywords::Const)) { + } else if self.is_const_item() { + try!(self.expect_keyword(keywords::Const)); let name = try!(self.parse_ident()); try!(self.expect(&token::Colon)); let typ = try!(self.parse_ty_sum()); @@ -4445,13 +4476,7 @@ impl<'a> Parser<'a> { } Ok((token::special_idents::invalid, vec![], ast::MacImplItem(m))) } else { - let unsafety = try!(self.parse_unsafety()); - let abi = if try!(self.eat_keyword(keywords::Extern)) { - try!(self.parse_opt_abi()).unwrap_or(abi::C) - } else { - abi::Rust - }; - try!(self.expect_keyword(keywords::Fn)); + let (constness, unsafety, abi) = try!(self.parse_fn_front_matter()); let ident = try!(self.parse_ident()); let mut generics = try!(self.parse_generics()); let (explicit_self, decl) = try!(self.parse_fn_decl_with_self(|p| { @@ -4464,6 +4489,7 @@ impl<'a> Parser<'a> { abi: abi, explicit_self: explicit_self, unsafety: unsafety, + constness: constness, decl: decl }, body))) } @@ -5252,7 +5278,7 @@ impl<'a> Parser<'a> { // EXTERN FUNCTION ITEM let abi = opt_abi.unwrap_or(abi::C); let (ident, item_, extra_attrs) = - try!(self.parse_item_fn(Unsafety::Normal, abi)); + try!(self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi)); let last_span = self.last_span; let item = self.mk_item(lo, last_span.hi, @@ -5287,6 +5313,21 @@ impl<'a> Parser<'a> { return Ok(Some(item)); } if try!(self.eat_keyword(keywords::Const) ){ + if self.check_keyword(keywords::Fn) { + // CONST FUNCTION ITEM + try!(self.bump()); + let (ident, item_, extra_attrs) = + try!(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(Some(item)); + } + // CONST ITEM if try!(self.eat_keyword(keywords::Mut) ){ let last_span = self.last_span; @@ -5340,7 +5381,7 @@ impl<'a> Parser<'a> { // FUNCTION ITEM try!(self.bump()); let (ident, item_, extra_attrs) = - try!(self.parse_item_fn(Unsafety::Normal, abi::Rust)); + try!(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, @@ -5361,7 +5402,7 @@ impl<'a> Parser<'a> { }; try!(self.expect_keyword(keywords::Fn)); let (ident, item_, extra_attrs) = - try!(self.parse_item_fn(Unsafety::Unsafe, abi)); + try!(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 b71d65a8fb0..8958370fda5 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -378,12 +378,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 @@ -740,7 +744,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 @@ -866,11 +871,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, @@ -1241,6 +1247,7 @@ impl<'a> State<'a> { -> io::Result<()> { self.print_fn(&m.decl, m.unsafety, + m.constness, m.abi, Some(ident), &m.generics, @@ -2335,12 +2342,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()); @@ -2726,8 +2734,13 @@ impl<'a> State<'a> { predicates: Vec::new(), }, }; - 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() } @@ -2976,11 +2989,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("const")) + } + if abi != abi::Rust { try!(self.word_nbsp("extern")); try!(self.word_nbsp(&abi.to_string())); @@ -3018,8 +3037,10 @@ mod tests { 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 458e3f7f0bd..c680d5bbbdf 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -123,7 +123,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { let i = 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; panic!(diag.span_fatal(i.span, "unsafe functions cannot be used for tests")); } @@ -320,7 +320,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, @@ -356,7 +356,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 6cf791b10be..61fddd6bed8 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -35,7 +35,7 @@ use owned_slice::OwnedSlice; #[derive(Copy, Clone)] pub enum FnKind<'a> { /// fn foo() or extern "Abi" fn foo() - FkItemFn(Ident, &'a Generics, Unsafety, Abi, Visibility), + FkItemFn(Ident, &'a Generics, Unsafety, Constness, Abi, Visibility), /// fn foo(&self) FkMethod(Ident, &'a MethodSig, Option<Visibility>), @@ -246,8 +246,9 @@ 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, item.vis), + ItemFn(ref declaration, unsafety, constness, abi, ref generics, ref body) => { + visitor.visit_fn(FkItemFn(item.ident, generics, unsafety, + constness, abi, item.vis), &**declaration, &**body, item.span, @@ -604,7 +605,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, _) => { diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs index 0180bccbca4..c3a1de11752 100644 --- a/src/test/compile-fail/check-static-values-constraints.rs +++ b/src/test/compile-fail/check-static-values-constraints.rs @@ -117,7 +117,7 @@ static mut STATIC14: SafeStruct = SafeStruct { //~^ ERROR mutable statics are not allowed to have destructors field1: SafeEnum::Variant1, field2: SafeEnum::Variant4("str".to_string()) -//~^ ERROR static contains unimplemented expression type +//~^ ERROR method calls in statics are limited to constant inherent methods }; static STATIC15: &'static [Box<MyOwned>] = &[ diff --git a/src/test/compile-fail/const-fn-mismatch.rs b/src/test/compile-fail/const-fn-mismatch.rs new file mode 100644 index 00000000000..d813cf32954 --- /dev/null +++ b/src/test/compile-fail/const-fn-mismatch.rs @@ -0,0 +1,26 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can't declare a const fn in an impl -- right now it's +// just not allowed at all, though eventually it'd make sense to allow +// it if the trait fn is const (but right now no trait fns can be +// const). + +#![feature(const_fn)] + +trait Foo { + fn f() -> u32; +} + +impl Foo for u32 { + const fn f() -> u32 { 22 } //~ ERROR E0379 +} + +fn main() { } diff --git a/src/test/compile-fail/const-fn-not-in-trait.rs b/src/test/compile-fail/const-fn-not-in-trait.rs new file mode 100644 index 00000000000..191f3e02527 --- /dev/null +++ b/src/test/compile-fail/const-fn-not-in-trait.rs @@ -0,0 +1,21 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that const fn is illegal in a trait declaration, whether or +// not a default is provided. + +#![feature(const_fn)] + +trait Foo { + const fn f() -> u32; //~ ERROR trait fns cannot be declared const + const fn g() -> u32 { 0 } //~ ERROR trait fns cannot be declared const +} + +fn main() { } diff --git a/src/test/compile-fail/const-fn-not-safe-for-const.rs b/src/test/compile-fail/const-fn-not-safe-for-const.rs new file mode 100644 index 00000000000..baa3eba0680 --- /dev/null +++ b/src/test/compile-fail/const-fn-not-safe-for-const.rs @@ -0,0 +1,47 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can't call random fns in a const fn or do other bad things. + +#![feature(const_fn)] + +use std::mem::transmute; + +fn random() -> u32 { 0 } + +const fn sub(x: &u32) -> usize { + unsafe { transmute(x) } //~ ERROR E0015 +} + +const fn sub1() -> u32 { + random() //~ ERROR E0015 +} + +static Y: u32 = 0; + +const fn get_Y() -> u32 { + Y + //~^ ERROR E0013 + //~| ERROR cannot refer to other statics by value +} + +const fn get_Y_addr() -> &'static u32 { + &Y + //~^ ERROR E0013 +} + +const fn get() -> u32 { + let x = 22; //~ ERROR E0016 + let y = 44; //~ ERROR E0016 + x + y +} + +fn main() { +} diff --git a/src/test/compile-fail/const-fn-stability.rs b/src/test/compile-fail/const-fn-stability.rs new file mode 100644 index 00000000000..8aa5189bcd6 --- /dev/null +++ b/src/test/compile-fail/const-fn-stability.rs @@ -0,0 +1,28 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test use of const fn without feature gate. + +const fn foo() -> usize { 0 } //~ ERROR const fn is unstable + +trait Foo { + const fn foo() -> u32; //~ ERROR const fn is unstable + const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable +} + +impl Foo { + const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable +} + +impl Foo for u32 { + const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable +} + +fn main() { } diff --git a/src/test/compile-fail/issue-16538.rs b/src/test/compile-fail/issue-16538.rs index a4e0f69b63b..2b53e92d9bc 100644 --- a/src/test/compile-fail/issue-16538.rs +++ b/src/test/compile-fail/issue-16538.rs @@ -20,6 +20,6 @@ mod Y { static foo: *const Y::X = Y::foo(Y::x as *const Y::X); //~^ ERROR the trait `core::marker::Sync` is not implemented for the type -//~| ERROR function calls in statics are limited to struct and enum constructors +//~| ERROR E0015 fn main() {} diff --git a/src/test/compile-fail/issue-7364.rs b/src/test/compile-fail/issue-7364.rs index 6a36b2f84bf..5d85fe93a48 100644 --- a/src/test/compile-fail/issue-7364.rs +++ b/src/test/compile-fail/issue-7364.rs @@ -17,6 +17,6 @@ static boxed: Box<RefCell<isize>> = box RefCell::new(0); //~^ ERROR allocations are not allowed in statics //~| ERROR the trait `core::marker::Sync` is not implemented for the type //~| ERROR the trait `core::marker::Sync` is not implemented for the type -//~| ERROR function calls in statics are limited to struct and enum constructors +//~| ERROR E0015 fn main() { } diff --git a/src/test/compile-fail/static-vec-repeat-not-constant.rs b/src/test/compile-fail/static-vec-repeat-not-constant.rs index 7a957564587..a533a5bd54d 100644 --- a/src/test/compile-fail/static-vec-repeat-not-constant.rs +++ b/src/test/compile-fail/static-vec-repeat-not-constant.rs @@ -11,6 +11,6 @@ fn foo() -> isize { 23 } static a: [isize; 2] = [foo(); 2]; -//~^ ERROR: function calls in statics are limited to struct and enum constructors +//~^ ERROR: E0015 fn main() {} diff --git a/src/test/run-pass/const-fn-nested.rs b/src/test/run-pass/const-fn-nested.rs new file mode 100644 index 00000000000..86f5dedc4d1 --- /dev/null +++ b/src/test/run-pass/const-fn-nested.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test a call whose argument is the result of another call. + +#![feature(const_fn)] + +const fn sub(x: u32, y: u32) -> u32 { + x - y +} + +const X: u32 = sub(sub(88, 44), 22); + +fn main() { + assert_eq!(X, 22); +} diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs new file mode 100644 index 00000000000..9bd8eb55cc3 --- /dev/null +++ b/src/test/run-pass/const-fn.rs @@ -0,0 +1,32 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A very basic test of const fn functionality. + +#![feature(const_fn)] + +const fn add(x: u32, y: u32) -> u32 { + x + y +} + +const fn sub(x: u32, y: u32) -> u32 { + x - y +} + +const SUM: u32 = add(44, 22); +const DIFF: u32 = sub(44, 22); + +fn main() { + assert_eq!(SUM, 66); + assert!(SUM != 88); + + assert_eq!(DIFF, 22); + +} |
