From af3795721cd9ffc591eeeb077df16d3658be150f Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Wed, 25 Feb 2015 22:05:07 +0200 Subject: [PATCH 1/5] syntax: parse `const fn` for free functions and inherent methods. --- src/librustc/metadata/encoder.rs | 4 +- src/librustc/middle/effect.rs | 4 +- src/librustc/middle/infer/error_reporting.rs | 19 +++++--- src/librustc/middle/reachable.rs | 6 +-- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/middle/stability.rs | 2 +- src/librustc/middle/ty.rs | 2 +- src/librustc_lint/builtin.rs | 4 +- src/librustc_resolve/build_reduced_graph.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/librustc_trans/trans/base.rs | 6 +-- src/librustc_trans/trans/inline.rs | 2 +- src/librustc_trans/trans/monomorphize.rs | 2 +- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/collect.rs | 4 +- src/librustc_typeck/lib.rs | 4 +- src/librustdoc/clean/inline.rs | 2 + src/librustdoc/clean/mod.rs | 9 +++- src/librustdoc/doctree.rs | 1 + src/librustdoc/html/format.rs | 19 ++++++++ src/librustdoc/html/render.rs | 33 ++++++++----- src/librustdoc/visit_ast.rs | 10 ++-- src/libsyntax/ast.rs | 9 +++- src/libsyntax/ast_map/blocks.rs | 20 ++++++-- src/libsyntax/ast_util.rs | 1 + src/libsyntax/ext/build.rs | 1 + src/libsyntax/ext/deriving/generic/mod.rs | 1 + src/libsyntax/ext/expand.rs | 5 +- src/libsyntax/feature_gate.rs | 4 +- src/libsyntax/fold.rs | 5 +- src/libsyntax/parse/parser.rs | 51 +++++++++++++++----- src/libsyntax/print/pprust.rs | 49 ++++++++++++++++--- src/libsyntax/test.rs | 10 ++-- src/libsyntax/visit.rs | 7 +-- 34 files changed, 218 insertions(+), 88 deletions(-) diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 86f33257e0903..744b743a337a9 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1049,7 +1049,7 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_stability(rbml_w, stab); rbml_w.end_tag(); } - ast::ItemFn(ref decl, _, _, ref generics, _) => { + ast::ItemFn(ref decl, _, _, _, ref generics, _) => { add_to_index(item, rbml_w, index); rbml_w.start_tag(tag_items_data_item); encode_def_id(rbml_w, def_id); @@ -1967,7 +1967,7 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) { for id in ecx.reachable { if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) { - if let ast::ItemFn(_, _, abi, ref generics, _) = i.node { + if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node { if abi != abi::Rust && !generics.is_type_parameterized() { rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id); } diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index b6a070c9332fd..963d8cf61bc74 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 8aca64484bf45..4ec5cf0336497 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 b532dc88df4cb..f374c9749c250 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 e7a03a9b7e174..467e404581187 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) }) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index d75dc861e8330..8a9e957f02afa 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -23,7 +23,7 @@ use syntax::{attr, visit}; use syntax::ast; use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant}; use syntax::ast::{Item, Generics, StructField}; -use syntax::ast_util::is_local; +use syntax::ast_util::{is_local, PostExpansionMethod}; use syntax::attr::{Stability, AttrMetaMethods}; use syntax::visit::{FnKind, Visitor}; use syntax::feature_gate::emit_feature_err; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 15e1ac2f2c65e..2765fd638600a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 6721c4a6636a9..abf0332b4a22f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -977,7 +977,7 @@ impl LintPass for NonSnakeCase { }, _ => (), }, - visit::FkItemFn(ident, _, _, _, _) => { + visit::FkItemFn(ident, _, _, _, _, _) => { self.check_snake_case(cx, "function", &token::get_ident(ident), Some(span)) }, _ => (), @@ -1853,7 +1853,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 4ea18968d43ac..30d5a4f111ba4 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 1f8d23fd4dc0d..1723313f015e5 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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/trans/base.rs b/src/librustc_trans/trans/base.rs index e44aae76c19ad..f21837ed10d09 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) 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 @@ -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/inline.rs b/src/librustc_trans/trans/inline.rs index 3f44bc40f356b..9a53c3f0bcdf5 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 03fdd0c45c16a..d086aa93a6f6b 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 0e64063d6a402..344d525d4cbf4 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); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 014991f7ea560..48876c2a1889a 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/lib.rs b/src/librustc_typeck/lib.rs index eaf7f1f92a75e..88ce75486a202 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 3ce8835b1a8de..9c64b7b4ab623 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 4c2357f8a8f0d..c2ae8020f284c 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 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 for doctree::Function { @@ -1091,6 +1094,7 @@ impl Clean for doctree::Function { decl: self.decl.clean(cx), generics: self.generics.clean(cx), unsafety: self.unsafety, + constness: self.constness, abi: self.abi, }), } @@ -2453,6 +2457,7 @@ impl Clean 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 8fa92304d249c..cadd43ec6bfd8 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, 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 bb53d532f52f7..a68e21aa01d9d 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -35,6 +35,10 @@ pub struct VisSpace(pub Option); /// 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)] +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 36cf650d54e9a..582f17168991f 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, "
{vis}{unsafety}{abi}fn \
+    try!(write!(w, "
{vis}{unsafety}{abi}{constness}fn \
                     {name}{generics}{decl}{where_clause}
", 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 {name}\ + write!(w, "{}{}{}fn {name}\ {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 4ad693578ccdc..92348aa648dc6 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, 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 e00cb82649b7b..bd7fb441bf53e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1475,6 +1475,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 { @@ -1543,6 +1549,7 @@ pub enum ExplicitSelf_ { pub type ExplicitSelf = Spanned; #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] + Constness, pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token @@ -1761,7 +1768,7 @@ pub enum Item_ { /// A `const` item ItemConst(P, P), /// A function declaration - ItemFn(P, Unsafety, Abi, Generics, P), + ItemFn(P, Unsafety, Constness, Abi, Generics, P), /// A module ItemMod(Mod), /// An external module diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 36a7f3a93817c..8b2b2fbeca140 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -120,6 +120,7 @@ struct ItemFnParts<'a> { ident: ast::Ident, decl: &'a ast::FnDecl, unsafety: ast::Unsafety, + constness: ast::Constness, abi: abi::Abi, vis: ast::Visibility, generics: &'a ast::Generics, @@ -180,7 +181,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.abi, p.constness, p.vis) }; let closure = |_: ClosureParts| { visit::FkFnBlock @@ -204,10 +205,19 @@ 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, ref abi, ref generics, ref block) => + item_fn(ItemFnParts { + id: i.id, + ident: i.ident, + decl: &**decl, + unsafety: unsafety, + constness: constness, + body: &**block, + generics: generics, + abi: abi, + 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 8471fef3487e6..45db8cc7b25a5 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -251,6 +251,7 @@ pub fn impl_pretty_name(trait_ref: &Option, ty: Option<&Ty>) -> Ident token::gensym_ident(&pretty[..]) } + _, pub fn struct_field_visibility(field: ast::StructField) -> Visibility { match field.node.kind { ast::NamedField(_, v) | ast::UnnamedField(v) => v diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 354a0bff74998..8a80e291a5384 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 b9968ca96088f..eab6c3ae7251a 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 484684a87f4cd..912cc841a642b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -513,11 +513,12 @@ pub fn expand_item(it: P, fld: &mut MacroExpander) /// Expand item_underscore fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ { match item { - ast::ItemFn(decl, fn_style, abi, generics, body) => { + ast::ItemFn(decl, unsafety, constness, abi, generics, body) => { let (rewritten_fn_decl, rewritten_body) = expand_and_rename_fn_decl_and_block(decl, body, fld); let expanded_generics = fold::noop_fold_generics(generics,fld); - ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body) + ast::ItemFn(rewritten_fn_decl, unsafety, constness, abi, + expanded_generics, rewritten_body) } _ => noop_fold_item_underscore(item, fld) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d79714248c89b..84546679b23b2 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -641,12 +641,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { span: Span, _node_id: NodeId) { 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, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 4bf15f509a048..c2382eaf82e37 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -917,10 +917,11 @@ pub fn noop_fold_item_underscore(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) @@ -1126,6 +1127,8 @@ pub fn noop_fold_method_sig(sig: MethodSig, folder: &mut T) -> Method unsafety: sig.unsafety, decl: folder.fold_fn_decl(sig.decl) } + constness, + constness, } pub fn noop_fold_pat(p: P, folder: &mut T) -> P { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2b6f91bbc993a..6fba8fd47fdce 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}; @@ -1175,7 +1175,7 @@ impl<'a> Parser<'a> { }; (ident, ConstTraitItem(ty, default)) } else { - let style = try!(p.parse_unsafety()); + let unsafety = try!(p.parse_unsafety()); let abi = if try!(p.eat_keyword(keywords::Extern)) { try!(p.parse_opt_abi()).unwrap_or(abi::C) } else { @@ -1195,7 +1195,8 @@ impl<'a> Parser<'a> { generics.where_clause = try!(p.parse_where_clause()); let sig = ast::MethodSig { - unsafety: style, + unsafety: unsafety, + constness: ast::Constness::NotConst; decl: d, generics: generics, abi: abi, @@ -4359,12 +4360,16 @@ impl<'a> Parser<'a> { } /// Parse an item-position function declaration. - fn parse_item_fn(&mut self, unsafety: Unsafety, abi: abi::Abi) -> PResult { + fn parse_item_fn(&mut self, + unsafety: Unsafety, + constness: Constness, + abi: abi::Abi) + -> PResult { 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))) } /// Parse an impl item. @@ -4445,11 +4450,17 @@ 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) + let is_const_fn = !is_trait_impl && self.eat_keyword(keywords::Const); + let (constness, unsafety, abi) = if is_const_fn { + (Constness::Const, Unsafety::Normal, abi::Rust) } else { - abi::Rust + let unsafety = 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)); let ident = try!(self.parse_ident()); @@ -4464,6 +4475,7 @@ impl<'a> Parser<'a> { abi: abi, explicit_self: explicit_self, unsafety: unsafety, + constness: constness; decl: decl }, body))) } @@ -5252,7 +5264,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 +5299,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 + self.bump(); + let (ident, item_, extra_attrs) = + self.parse_item_fn(Unsafety::Normal, Constness::Const, abi::Rust); + let last_span = self.last_span; + let item = self.mk_item(lo, + last_span.hi, + ident, + item_, + visibility, + maybe_append(attrs, extra_attrs)); + return Ok(item); + } + // CONST ITEM if try!(self.eat_keyword(keywords::Mut) ){ let last_span = self.last_span; @@ -5340,7 +5367,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 +5388,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 b71d65a8fb0c5..5889b968f4146 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -378,12 +378,28 @@ pub fn ident_to_string(id: &ast::Ident) -> String { to_string(|s| s.print_ident(*id)) } +<<<<<<< HEAD pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident, opt_explicit_self: Option<&ast::ExplicitSelf_>, generics: &ast::Generics) -> String { to_string(|s| { +||||||| parent of 61a958e... syntax: parse `const fn` for free functions and inherent methods. +pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident, + opt_explicit_self: Option<&ast::ExplicitSelf_>, + generics: &ast::Generics) -> String { + $to_string(|s| { +======= +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| { +>>>>>>> 61a958e... syntax: parse `const fn` for free functions and inherent methods. 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 +756,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 +883,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 +1259,7 @@ impl<'a> State<'a> { -> io::Result<()> { self.print_fn(&m.decl, m.unsafety, + m.constness, m.abi, Some(ident), &m.generics, @@ -2335,12 +2354,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, 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 +2746,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 +3001,17 @@ impl<'a> State<'a> { pub fn print_fn_header_info(&mut self, unsafety: ast::Unsafety, + constness: ast::Constness, abi: abi::Abi, vis: ast::Visibility) -> io::Result<()> { try!(word(&mut self.s, &visibility_qualified(vis, ""))); try!(self.print_unsafety(unsafety)); + match constness { + ast::Constness::NotConst => {} + ast::Constness::Const => try!(self.word_nbsp("unsafe")) + } + if abi != abi::Rust { try!(self.word_nbsp("extern")); try!(self.word_nbsp(&abi.to_string())); @@ -3018,8 +3049,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 458e3f7f0bd9b..c680d5bbbdf7a 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 { 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 6cf791b10be67..b9b81bd7c6f8e 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), @@ -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, From 1bd420555e7eb49c2c9a6ecc68e1797540bef403 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Wed, 25 Feb 2015 22:06:08 +0200 Subject: [PATCH 2/5] rustc: const-qualify `const fn` function and method calls. --- src/librustc/metadata/common.rs | 2 + src/librustc/metadata/csearch.rs | 5 + src/librustc/metadata/decoder.rs | 21 ++++ src/librustc/metadata/encoder.rs | 22 +++- src/librustc/middle/check_const.rs | 193 +++++++++++++++++++++++------ src/librustc/middle/const_eval.rs | 61 ++++++++- src/librustc/middle/ty.rs | 5 +- src/libsyntax/ast_map/blocks.rs | 36 +++--- 8 files changed, 288 insertions(+), 57 deletions(-) diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 478c0f2f564f8..901afc1d1904e 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 8a35c01200490..f834076e8eab5 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 { diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 2c11ee8baa9d6..ee5fd0202d69e 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 { 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 744b743a337a9..f721bb700ed02 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,14 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_attributes(rbml_w, &impl_item.attrs); let scheme = ty::lookup_item_type(ecx.tcx, m.def_id); let any_types = !scheme.generics.types.is_empty(); - if any_types || is_default_impl || attr::requests_inline(&impl_item.attrs) { + let needs_inline = any_types || is_default_impl || + attr::requests_inline(&impl_item.attrs); + let constness = ast_method.pe_constness(); + if needs_inline || constness == ast::Constness::Const { encode_inlined_item(ecx, rbml_w, IIImplItemRef(local_def(parent_id), impl_item)); } + encode_constness(rbml_w, constness); if !any_types { encode_symbol(ecx, rbml_w, m.def_id.node); } @@ -1049,7 +1063,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 +1073,14 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_name(rbml_w, item.ident.name); encode_path(rbml_w, path); encode_attributes(rbml_w, &item.attrs); - if tps_len > 0 || attr::requests_inline(&item.attrs) { + let needs_inline = tps_len > 0 || attr::requests_inline(&item.attrs); + if needs_inline || constness == ast::Constness::Const { encode_inlined_item(ecx, rbml_w, IIItemRef(item)); } if tps_len == 0 { encode_symbol(ecx, rbml_w, item.id); } + encode_constness(rbml_w, constness); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); encode_method_argument_names(rbml_w, &**decl); diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 794cc4ff38d34..aca2e2b473e4e 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -36,6 +36,7 @@ use util::nodemap::NodeMap; use util::ppaux::Repr; use syntax::ast; +use syntax::ast_util::PostExpansionMethod; use syntax::codemap::Span; use syntax::visit::{self, Visitor}; @@ -79,6 +80,7 @@ bitflags! { #[derive(Copy, Clone, Eq, PartialEq)] enum Mode { Const, + ConstFn, Static, StaticMut, @@ -136,10 +138,87 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { }) } + fn fn_like(&mut self, + fk: visit::FnKind, + fd: &ast::FnDecl, + b: &ast::Block, + s: Span, + fn_id: ast::NodeId) + -> ConstQualif { + match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) { + Entry::Occupied(entry) => return *entry.get(), + Entry::Vacant(entry) => { + // Prevent infinite recursion on re-entry. + entry.insert(PURE_CONST); + } + } + + let mode = match fk { + visit::FkItemFn(_, _, _, ast::Constness::Const, _) => { + Mode::ConstFn + } + visit::FkMethod(_, _, m) => { + if m.pe_constness() == ast::Constness::Const { + Mode::ConstFn + } else { + Mode::Var + } + } + _ => Mode::Var + }; + + // Ensure the arguments are simple, not mutable/by-ref or patterns. + if mode == Mode::ConstFn { + for arg in &fd.inputs { + match arg.pat.node { + ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {} + _ => { + span_err!(self.tcx.sess, arg.pat.span, E0022, + "arguments of constant functions can only \ + be immutable by-value bindings"); + } + } + } + } + + let qualif = self.with_mode(mode, |this| { + this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); + visit::walk_fn(this, fk, fd, b, s); + this.qualif + }); + + // Keep only bits that aren't affected by function body (NON_ZERO_SIZED), + // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE). + let qualif = qualif & (NON_ZERO_SIZED | PREFER_IN_PLACE); + + self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif); + qualif + } + fn add_qualif(&mut self, qualif: ConstQualif) { self.qualif = self.qualif | qualif; } + /// Returns true if the call is to a const fn or method. + fn handle_const_fn_call(&mut self, def_id: ast::DefId, ret_ty: Ty<'tcx>) -> bool { + if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) { + let qualif = self.fn_like(fn_like.kind(), + fn_like.decl(), + fn_like.body(), + fn_like.span(), + fn_like.id()); + self.add_qualif(qualif); + + if ty::type_contents(self.tcx, ret_ty).interior_unsafe() { + self.add_qualif(MUTABLE_MEM); + } + + true + } else { + false + } + } + fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) { match self.rvalue_borrows.entry(id) { Entry::Occupied(mut entry) => { @@ -158,6 +237,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { fn msg(&self) -> &'static str { match self.mode { Mode::Const => "constant", + Mode::ConstFn => "constant function", Mode::StaticMut | Mode::Static => "static", Mode::Var => unreachable!(), } @@ -251,9 +331,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 +347,35 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { } } + fn visit_block(&mut self, block: &ast::Block) { + // Check all statements in the block + for stmt in &block.stmts { + let span = match stmt.node { + ast::StmtDecl(ref decl, _) => { + match decl.node { + ast::DeclLocal(_) => decl.span, + + // Item statements are allowed + ast::DeclItem(_) => continue + } + } + ast::StmtExpr(ref expr, _) => expr.span, + ast::StmtSemi(ref semi, _) => semi.span, + ast::StmtMac(..) => { + self.tcx.sess.span_bug(stmt.span, "unexpanded statement \ + macro in const?!") + } + }; + self.add_qualif(NOT_CONST); + if self.mode != Mode::Var { + span_err!(self.tcx.sess, span, E0016, + "blocks in {}s are limited to items and \ + tail expressions", self.msg()); + } + } + visit::walk_block(self, block); + } + fn visit_expr(&mut self, ex: &ast::Expr) { let mut outer = self.qualif; self.qualif = ConstQualif::empty(); @@ -473,10 +580,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 +600,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(NOT_CONST | NON_ZERO_SIZED); + } def => { v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { @@ -517,19 +628,26 @@ 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()); } } } @@ -538,27 +656,28 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, let mut block_span_err = |span| { 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(NOT_CONST); + if v.mode != Mode::Var { + span_err!(v.tcx.sess, e.span, E0021, + "method calls in {}s are limited to \ + constant inherent methods", v.msg()); } } } @@ -579,7 +698,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 +707,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } } + ast::ExprBlock(_) | ast::ExprUnary(..) | ast::ExprBinary(..) | ast::ExprIndex(..) | @@ -616,8 +736,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 03de553e648d8..8daf17cb0033e 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::{self, PostExpansionMethod}; 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 { + match tcx.extern_const_fns.borrow().get(&def_id) { + Some(&ast::DUMMY_NODE_ID) => return None, + Some(&fn_id) => return Some(fn_id), + None => {} + } + + if !csearch::is_const_fn(&tcx.sess.cstore, def_id) { + tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); + return None; + } + + let fn_id = match csearch::maybe_get_item_ast(tcx, def_id, + box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) { + csearch::FoundAst::Found(&ast::IIItem(ref item)) => Some(item.id), + csearch::FoundAst::Found(&ast::IIImplItem(_, ast::MethodImplItem(ref m))) => Some(m.id), + _ => None + }; + tcx.extern_const_fns.borrow_mut().insert(def_id, + fn_id.unwrap_or(ast::DUMMY_NODE_ID)); + fn_id +} + +pub fn lookup_const_fn_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) + -> Option> { + + let fn_id = if !ast_util::is_local(def_id) { + if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { + fn_id + } else { + return None; + } + } else { + def_id.node + }; + + let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { + Some(fn_like) => fn_like, + None => return None + }; + + match fn_like.kind() { + visit::FkItemFn(_, _, _, ast::Constness::Const, _) => { + Some(fn_like) + } + visit::FkMethod(_, _, m) => { + if m.pe_constness() == ast::Constness::Const { + Some(fn_like) + } else { + None + } + } + _ => None + } +} + #[derive(Clone, PartialEq)] pub enum const_val { const_float(f64), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2765fd638600a..5ff6ee3c8b081 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, - /// 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>, pub extern_const_variants: RefCell>, + pub extern_const_fns: RefCell>, pub method_map: MethodMap<'tcx>, @@ -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/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 8b2b2fbeca140..58627b37a8766 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 { - 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)) } } } @@ -145,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 { + let fn_like = match node { + ast_map::NodeItem(item) => item.is_fn_like(), + ast_map::NodeTraitItem(tm) => tm.is_fn_like(), + ast_map::NodeImplItem(_) => true, + ast_map::NodeExpr(e) => e.is_fn_like(), + _ => false + }; + if fn_like { + Some(FnLikeNode { + node: node + }) + } else { + None + } + } + pub fn to_fn_parts(self) -> FnParts<'a> { FnParts { decl: self.decl(), From fb206bf34a2463317b9fa1ef3c0ff35d921f8920 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Wed, 25 Feb 2015 22:06:57 +0200 Subject: [PATCH 3/5] rustc_trans: evaluate `const fn` function and method calls. --- src/librustc_trans/trans/_match.rs | 6 +- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/consts.rs | 118 ++++++++++++++++++++++------- 3 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 84d464e8f0781..53a991170c70f 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 f21837ed10d09..9310d1e202ab3 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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, diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 5ca14b63bce09..38272ca8bf4e5 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; +type FnArgMap<'a> = Option<&'a NodeMap>; + 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]| { - 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]| -> Vec { + 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::>(); // 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) } } From df93deab10850d52252829770895b0249b0d7f1e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 May 2015 08:47:04 -0400 Subject: [PATCH 4/5] Make various fixes: - add feature gate - add basic tests - adjust parser to eliminate conflict between `const fn` and associated constants - allow `const fn` in traits/trait-impls, but forbid later in type check - correct some merge conflicts --- src/librustc/diagnostics.rs | 3 +- src/librustc/metadata/encoder.rs | 5 +- src/librustc/middle/check_const.rs | 35 +++------- src/librustc/middle/const_eval.rs | 16 ++--- src/librustc/middle/effect.rs | 2 +- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/middle/stability.rs | 2 +- src/librustc_lint/builtin.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/librustc_trans/save/dump_csv.rs | 2 +- src/librustc_trans/trans/consts.rs | 6 +- src/librustc_trans/trans/debuginfo/mod.rs | 2 +- src/librustc_typeck/check/mod.rs | 24 ++++++- src/librustc_typeck/diagnostics.rs | 3 +- src/librustdoc/clean/mod.rs | 7 +- src/librustdoc/html/format.rs | 2 +- src/librustdoc/visit_ast.rs | 4 +- src/libsyntax/ast.rs | 2 +- src/libsyntax/ast_map/blocks.rs | 5 +- src/libsyntax/ast_util.rs | 3 +- src/libsyntax/ext/expand.rs | 1 + src/libsyntax/feature_gate.rs | 26 +++++++ src/libsyntax/fold.rs | 3 +- src/libsyntax/parse/parser.rs | 68 +++++++++++-------- src/libsyntax/print/pprust.rs | 16 +---- src/libsyntax/visit.rs | 2 +- .../check-static-values-constraints.rs | 2 +- src/test/compile-fail/const-fn-mismatch.rs | 26 +++++++ .../compile-fail/const-fn-not-in-trait.rs | 21 ++++++ .../const-fn-not-safe-for-const.rs | 47 +++++++++++++ src/test/compile-fail/const-fn-stability.rs | 28 ++++++++ src/test/compile-fail/issue-16538.rs | 2 +- src/test/compile-fail/issue-7364.rs | 2 +- .../static-vec-repeat-not-constant.rs | 2 +- src/test/run-pass/const-fn-nested.rs | 23 +++++++ src/test/run-pass/const-fn.rs | 32 +++++++++ 36 files changed, 322 insertions(+), 108 deletions(-) create mode 100644 src/test/compile-fail/const-fn-mismatch.rs create mode 100644 src/test/compile-fail/const-fn-not-in-trait.rs create mode 100644 src/test/compile-fail/const-fn-not-safe-for-const.rs create mode 100644 src/test/compile-fail/const-fn-stability.rs create mode 100644 src/test/run-pass/const-fn-nested.rs create mode 100644 src/test/run-pass/const-fn.rs diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index f5cb1bd25d60d..b70d73c9141ae 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/encoder.rs b/src/librustc/metadata/encoder.rs index f721bb700ed02..88e0b739a0fbb 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -879,12 +879,11 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, let any_types = !scheme.generics.types.is_empty(); let needs_inline = any_types || is_default_impl || attr::requests_inline(&impl_item.attrs); - let constness = ast_method.pe_constness(); - if needs_inline || constness == ast::Constness::Const { + 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, constness); + encode_constness(rbml_w, sig.constness); if !any_types { encode_symbol(ecx, rbml_w, m.def_id.node); } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index aca2e2b473e4e..a70df34bd70a8 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -36,7 +36,6 @@ use util::nodemap::NodeMap; use util::ppaux::Repr; use syntax::ast; -use syntax::ast_util::PostExpansionMethod; use syntax::codemap::Span; use syntax::visit::{self, Visitor}; @@ -149,16 +148,16 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { Entry::Occupied(entry) => return *entry.get(), Entry::Vacant(entry) => { // Prevent infinite recursion on re-entry. - entry.insert(PURE_CONST); + entry.insert(ConstQualif::empty()); } } let mode = match fk { - visit::FkItemFn(_, _, _, ast::Constness::Const, _) => { + visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => { Mode::ConstFn } - visit::FkMethod(_, _, m) => { - if m.pe_constness() == ast::Constness::Const { + visit::FkMethod(_, m, _) => { + if m.constness == ast::Constness::Const { Mode::ConstFn } else { Mode::Var @@ -189,7 +188,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { // Keep only bits that aren't affected by function body (NON_ZERO_SIZED), // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE). - let qualif = qualif & (NON_ZERO_SIZED | PREFER_IN_PLACE); + let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif); qualif @@ -210,7 +209,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { self.add_qualif(qualif); if ty::type_contents(self.tcx, ret_ty).interior_unsafe() { - self.add_qualif(MUTABLE_MEM); + self.add_qualif(ConstQualif::MUTABLE_MEM); } true @@ -366,7 +365,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { macro in const?!") } }; - self.add_qualif(NOT_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 \ @@ -602,7 +601,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => { // Sadly, we can't determine whether the types are zero-sized. - v.add_qualif(NOT_CONST | NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED); } def => { v.add_qualif(ConstQualif::NOT_CONST); @@ -651,20 +650,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } } } - ast::ExprBlock(ref block) => { - // Check all statements in the block - let mut block_span_err = |span| { - 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::ExprMethodCall(..) => { - let method_did = match v.tcx.method_map.borrow()[method_call].origin { + let method_did = match v.tcx.method_map.borrow()[&method_call].origin { ty::MethodStatic(did) => Some(did), _ => None }; @@ -673,9 +660,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, None => false }; if !is_const { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { - span_err!(v.tcx.sess, e.span, E0021, + span_err!(v.tcx.sess, e.span, E0378, "method calls in {}s are limited to \ constant inherent methods", v.msg()); } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 8daf17cb0033e..e5100c32cc2da 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -25,7 +25,7 @@ use util::ppaux::Repr; use syntax::ast::{self, Expr}; use syntax::ast_map::blocks::FnLikeNode; -use syntax::ast_util::{self, PostExpansionMethod}; +use syntax::ast_util; use syntax::codemap::Span; use syntax::feature_gate; use syntax::parse::token::InternedString; @@ -216,7 +216,7 @@ fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: ast::DefId) let fn_id = match csearch::maybe_get_item_ast(tcx, def_id, box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) { csearch::FoundAst::Found(&ast::IIItem(ref item)) => Some(item.id), - csearch::FoundAst::Found(&ast::IIImplItem(_, ast::MethodImplItem(ref m))) => Some(m.id), + csearch::FoundAst::Found(&ast::IIImplItem(_, ref item)) => Some(item.id), _ => None }; tcx.extern_const_fns.borrow_mut().insert(def_id, @@ -224,9 +224,9 @@ fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: ast::DefId) fn_id } -pub fn lookup_const_fn_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) - -> Option> { - +pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: ast::DefId) + -> Option> +{ 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 @@ -243,11 +243,11 @@ pub fn lookup_const_fn_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) }; match fn_like.kind() { - visit::FkItemFn(_, _, _, ast::Constness::Const, _) => { + visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => { Some(fn_like) } - visit::FkMethod(_, _, m) => { - if m.pe_constness() == ast::Constness::Const { + visit::FkMethod(_, m, _) => { + if m.constness == ast::Constness::Const { Some(fn_like) } else { None diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index 963d8cf61bc74..cfff439f02d19 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -87,7 +87,7 @@ 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(_, _, unsafety, _, _) => + visit::FkItemFn(_, _, unsafety, _, _, _) => (true, unsafety == ast::Unsafety::Unsafe), visit::FkMethod(_, sig, _) => (true, sig.unsafety == ast::Unsafety::Unsafe), diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 467e404581187..dfdcba1678f19 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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/stability.rs b/src/librustc/middle/stability.rs index 8a9e957f02afa..d75dc861e8330 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -23,7 +23,7 @@ use syntax::{attr, visit}; use syntax::ast; use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant}; use syntax::ast::{Item, Generics, StructField}; -use syntax::ast_util::{is_local, PostExpansionMethod}; +use syntax::ast_util::is_local; use syntax::attr::{Stability, AttrMetaMethods}; use syntax::visit::{FnKind, Visitor}; use syntax::feature_gate::emit_feature_err; diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index abf0332b4a22f..e9a459b91191b 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1324,7 +1324,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, _) => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 1723313f015e5..7afc1afc224ee 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 } diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 7f66d3a833fde..00a1f728b9b00 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/consts.rs b/src/librustc_trans/trans/consts.rs index 38272ca8bf4e5..c11bb922f1cb2 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -41,7 +41,7 @@ use syntax::{ast, ast_util}; use syntax::parse::token; use syntax::ptr::P; -type FnArgMap<'a> = Option<&'a NodeMap>; +pub type FnArgMap<'a> = Option<&'a NodeMap>; pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit) -> ValueRef { @@ -863,7 +863,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, _ => break }; } - let def = cx.tcx().def_map.borrow()[callee.id].full_def(); + 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, _) => { @@ -893,7 +893,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, 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 { + 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") }; diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index 4e5407016ba9b..f9ad3d1a857f1 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_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 344d525d4cbf4..24f429a9ca015 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index b3db7405cfdf4..5796a5599ce99 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/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c2ae8020f284c..045a38228c685 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1352,7 +1352,10 @@ impl<'tcx> Clean 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 { @@ -1360,7 +1363,7 @@ impl<'tcx> Clean for ty::Method<'tcx> { generics: generics, self_: self_, decl: decl, - abi: self.fty.abi + abi: self.fty.abi, }) }; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a68e21aa01d9d..6e70b3711e4e8 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -37,7 +37,7 @@ pub struct VisSpace(pub Option); 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)] +#[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); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 92348aa648dc6..e86d77a3be03e 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -125,7 +125,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { name: ast::Ident, fd: &ast::FnDecl, unsafety: &ast::Unsafety, constness: ast::Constness, - _abi: &abi::Abi, + abi: &abi::Abi, gen: &ast::Generics) -> Function { debug!("Visiting fn"); Function { @@ -294,7 +294,7 @@ 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, unsafety, constness, ref abi, ref 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) => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index bd7fb441bf53e..5b03b3bf0385c 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, pub generics: Generics, @@ -1549,7 +1550,6 @@ pub enum ExplicitSelf_ { pub type ExplicitSelf = Spanned; #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] - Constness, pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 58627b37a8766..99686d54ce589 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -189,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.constness, p.vis) + visit::FkItemFn(p.ident, p.generics, p.unsafety, p.constness, p.abi, p.vis) }; let closure = |_: ClosureParts| { visit::FkFnBlock @@ -213,13 +213,12 @@ impl<'a> FnLikeNode<'a> { { match self.node { ast_map::NodeItem(i) => match i.node { - ast::ItemFn(ref decl, unsafety, constness, ref abi, ref generics, ref block) => + ast::ItemFn(ref decl, unsafety, constness, abi, ref generics, ref block) => item_fn(ItemFnParts { id: i.id, ident: i.ident, decl: &**decl, unsafety: unsafety, - constness: constness, body: &**block, generics: generics, abi: abi, diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 45db8cc7b25a5..bb8096f277069 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -251,7 +251,6 @@ pub fn impl_pretty_name(trait_ref: &Option, ty: Option<&Ty>) -> Ident token::gensym_ident(&pretty[..]) } - _, pub fn struct_field_visibility(field: ast::StructField) -> Visibility { match field.node.kind { ast::NamedField(_, v) | ast::UnnamedField(v) => v @@ -441,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/expand.rs b/src/libsyntax/ext/expand.rs index 912cc841a642b..5f0efd14eecc3 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1396,6 +1396,7 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P, 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 84546679b23b2..2b749dd890ece 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) @@ -640,6 +643,19 @@ 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 => { self.gate_feature("intrinsics", @@ -664,6 +680,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); @@ -676,6 +697,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 c2382eaf82e37..7806a27c53e1c 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1125,10 +1125,9 @@ pub fn noop_fold_method_sig(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) } - constness, - constness, } pub fn noop_fold_pat(p: P, folder: &mut T) -> P { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6fba8fd47fdce..eb6420165dab7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 unsafety = 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()); @@ -1196,7 +1191,7 @@ impl<'a> Parser<'a> { generics.where_clause = try!(p.parse_where_clause()); let sig = ast::MethodSig { unsafety: unsafety, - constness: ast::Constness::NotConst; + constness: constness, decl: d, generics: generics, abi: abi, @@ -4372,6 +4367,36 @@ impl<'a> Parser<'a> { 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. pub fn parse_impl_item(&mut self) -> PResult> { maybe_whole!(no_clone self, NtImplItem); @@ -4385,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()); @@ -4450,19 +4476,7 @@ impl<'a> Parser<'a> { } Ok((token::special_idents::invalid, vec![], ast::MacImplItem(m))) } else { - let is_const_fn = !is_trait_impl && self.eat_keyword(keywords::Const); - let (constness, unsafety, abi) = if is_const_fn { - (Constness::Const, Unsafety::Normal, abi::Rust) - } else { - 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)); + 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| { @@ -4475,7 +4489,7 @@ impl<'a> Parser<'a> { abi: abi, explicit_self: explicit_self, unsafety: unsafety, - constness: constness; + constness: constness, decl: decl }, body))) } @@ -5301,9 +5315,9 @@ impl<'a> Parser<'a> { if try!(self.eat_keyword(keywords::Const) ){ if self.check_keyword(keywords::Fn) { // CONST FUNCTION ITEM - self.bump(); + try!(self.bump()); let (ident, item_, extra_attrs) = - self.parse_item_fn(Unsafety::Normal, Constness::Const, abi::Rust); + 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, @@ -5311,7 +5325,7 @@ impl<'a> Parser<'a> { item_, visibility, maybe_append(attrs, extra_attrs)); - return Ok(item); + return Ok(Some(item)); } // CONST ITEM diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5889b968f4146..0b211cd073320 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -378,17 +378,6 @@ pub fn ident_to_string(id: &ast::Ident) -> String { to_string(|s| s.print_ident(*id)) } -<<<<<<< HEAD -pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident, - opt_explicit_self: Option<&ast::ExplicitSelf_>, - generics: &ast::Generics) -> String { - to_string(|s| { -||||||| parent of 61a958e... syntax: parse `const fn` for free functions and inherent methods. -pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident, - opt_explicit_self: Option<&ast::ExplicitSelf_>, - generics: &ast::Generics) -> String { - $to_string(|s| { -======= pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, constness: ast::Constness, @@ -396,8 +385,7 @@ pub fn fun_to_string(decl: &ast::FnDecl, opt_explicit_self: Option<&ast::ExplicitSelf_>, generics: &ast::Generics) -> String { - $to_string(|s| { ->>>>>>> 61a958e... syntax: parse `const fn` for free functions and inherent methods. + to_string(|s| { try!(s.head("")); try!(s.print_fn(decl, unsafety, constness, abi::Rust, Some(name), generics, opt_explicit_self, ast::Inherited)); @@ -2751,7 +2739,7 @@ impl<'a> State<'a> { ast::Constness::NotConst, abi, name, - generics, + &generics, opt_explicit_self, ast::Inherited)); self.end() diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index b9b81bd7c6f8e..61fddd6bed801 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -605,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 0180bccbca40b..c3a1de11752f8 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] = &[ 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 0000000000000..d813cf32954e3 --- /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 or the MIT license +// , 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 0000000000000..191f3e025270f --- /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 or the MIT license +// , 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 0000000000000..baa3eba06805f --- /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 or the MIT license +// , 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 0000000000000..8aa5189bcd6bb --- /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 or the MIT license +// , 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 a4e0f69b63b39..2b53e92d9bce6 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 6a36b2f84bfc9..5d85fe93a4888 100644 --- a/src/test/compile-fail/issue-7364.rs +++ b/src/test/compile-fail/issue-7364.rs @@ -17,6 +17,6 @@ static boxed: Box> = 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 7a957564587f9..a533a5bd54dcc 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 0000000000000..86f5dedc4d1b1 --- /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 or the MIT license +// , 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 0000000000000..9bd8eb55cc388 --- /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 or the MIT license +// , 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); + +} From 82ded3cd03e001e82b9dce9aedac621a5292231b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 22 May 2015 08:45:05 -0400 Subject: [PATCH 5/5] Two more small fixes. --- src/libsyntax/parse/mod.rs | 1 + src/libsyntax/print/pprust.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 68574560533fe..d6c28d4144792 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/print/pprust.rs b/src/libsyntax/print/pprust.rs index 0b211cd073320..8958370fda544 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2997,7 +2997,7 @@ impl<'a> State<'a> { match constness { ast::Constness::NotConst => {} - ast::Constness::Const => try!(self.word_nbsp("unsafe")) + ast::Constness::Const => try!(self.word_nbsp("const")) } if abi != abi::Rust {