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

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

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

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

View File

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

View File

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

View File

@ -384,6 +384,11 @@ pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool {
decoder::is_typedef(&*cdata, did.node)
}
pub fn is_const_fn(cstore: &cstore::CStore, did: ast::DefId) -> bool {
let cdata = cstore.get_crate_data(did.krate);
decoder::is_const_fn(&*cdata, did.node)
}
pub fn get_stability(cstore: &cstore::CStore,
def: ast::DefId)
-> Option<attr::Stability> {

View File

@ -178,6 +178,19 @@ fn item_visibility(item: rbml::Doc) -> ast::Visibility {
}
}
fn fn_constness(item: rbml::Doc) -> ast::Constness {
match reader::maybe_get_doc(item, tag_items_data_item_constness) {
None => ast::Constness::NotConst,
Some(constness_doc) => {
match reader::doc_as_u8(constness_doc) as char {
'c' => ast::Constness::Const,
'n' => ast::Constness::NotConst,
_ => panic!("unknown constness character")
}
}
}
}
fn item_sort(item: rbml::Doc) -> Option<char> {
let mut ret = None;
reader::tagged_docs(item, tag_item_trait_item_sort, |doc| {
@ -1525,6 +1538,14 @@ pub fn is_typedef(cdata: Cmd, id: ast::NodeId) -> bool {
}
}
pub fn is_const_fn(cdata: Cmd, id: ast::NodeId) -> bool {
let item_doc = lookup_item(id, cdata.data());
match fn_constness(item_doc) {
ast::Constness::Const => true,
ast::Constness::NotConst => false,
}
}
fn doc_generics<'tcx>(base_doc: rbml::Doc,
tcx: &ty::ctxt<'tcx>,
cdata: Cmd,

View File

@ -581,6 +581,16 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: ast::Visibility) {
rbml_w.wr_tagged_u8(tag_items_data_item_visibility, ch as u8);
}
fn encode_constness(rbml_w: &mut Encoder, constness: ast::Constness) {
rbml_w.start_tag(tag_items_data_item_constness);
let ch = match constness {
ast::Constness::Const => 'c',
ast::Constness::NotConst => 'n',
};
rbml_w.wr_str(&ch.to_string());
rbml_w.end_tag();
}
fn encode_explicit_self(rbml_w: &mut Encoder,
explicit_self: &ty::ExplicitSelfCategory) {
let tag = tag_item_trait_method_explicit_self;
@ -867,10 +877,13 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
encode_attributes(rbml_w, &impl_item.attrs);
let scheme = ty::lookup_item_type(ecx.tcx, m.def_id);
let any_types = !scheme.generics.types.is_empty();
if any_types || is_default_impl || attr::requests_inline(&impl_item.attrs) {
let needs_inline = any_types || is_default_impl ||
attr::requests_inline(&impl_item.attrs);
if needs_inline || sig.constness == ast::Constness::Const {
encode_inlined_item(ecx, rbml_w, IIImplItemRef(local_def(parent_id),
impl_item));
}
encode_constness(rbml_w, sig.constness);
if !any_types {
encode_symbol(ecx, rbml_w, m.def_id.node);
}
@ -1049,7 +1062,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_stability(rbml_w, stab);
rbml_w.end_tag();
}
ast::ItemFn(ref decl, _, _, ref generics, _) => {
ast::ItemFn(ref decl, _, constness, _, ref generics, _) => {
add_to_index(item, rbml_w, index);
rbml_w.start_tag(tag_items_data_item);
encode_def_id(rbml_w, def_id);
@ -1059,12 +1072,14 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_name(rbml_w, item.ident.name);
encode_path(rbml_w, path);
encode_attributes(rbml_w, &item.attrs);
if tps_len > 0 || attr::requests_inline(&item.attrs) {
let needs_inline = tps_len > 0 || attr::requests_inline(&item.attrs);
if needs_inline || constness == ast::Constness::Const {
encode_inlined_item(ecx, rbml_w, IIItemRef(item));
}
if tps_len == 0 {
encode_symbol(ecx, rbml_w, item.id);
}
encode_constness(rbml_w, constness);
encode_visibility(rbml_w, vis);
encode_stability(rbml_w, stab);
encode_method_argument_names(rbml_w, &**decl);
@ -1967,7 +1982,7 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) {
for id in ecx.reachable {
if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) {
if let ast::ItemFn(_, _, abi, ref generics, _) = i.node {
if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node {
if abi != abi::Rust && !generics.is_type_parameterized() {
rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
}

View File

@ -79,6 +79,7 @@ bitflags! {
#[derive(Copy, Clone, Eq, PartialEq)]
enum Mode {
Const,
ConstFn,
Static,
StaticMut,
@ -136,10 +137,87 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
})
}
fn fn_like(&mut self,
fk: visit::FnKind,
fd: &ast::FnDecl,
b: &ast::Block,
s: Span,
fn_id: ast::NodeId)
-> ConstQualif {
match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
Entry::Occupied(entry) => return *entry.get(),
Entry::Vacant(entry) => {
// Prevent infinite recursion on re-entry.
entry.insert(ConstQualif::empty());
}
}
let mode = match fk {
visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
Mode::ConstFn
}
visit::FkMethod(_, m, _) => {
if m.constness == ast::Constness::Const {
Mode::ConstFn
} else {
Mode::Var
}
}
_ => Mode::Var
};
// Ensure the arguments are simple, not mutable/by-ref or patterns.
if mode == Mode::ConstFn {
for arg in &fd.inputs {
match arg.pat.node {
ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {}
_ => {
span_err!(self.tcx.sess, arg.pat.span, E0022,
"arguments of constant functions can only \
be immutable by-value bindings");
}
}
}
}
let qualif = self.with_mode(mode, |this| {
this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
visit::walk_fn(this, fk, fd, b, s);
this.qualif
});
// Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
// and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
qualif
}
fn add_qualif(&mut self, qualif: ConstQualif) {
self.qualif = self.qualif | qualif;
}
/// Returns true if the call is to a const fn or method.
fn handle_const_fn_call(&mut self, def_id: ast::DefId, ret_ty: Ty<'tcx>) -> bool {
if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
let qualif = self.fn_like(fn_like.kind(),
fn_like.decl(),
fn_like.body(),
fn_like.span(),
fn_like.id());
self.add_qualif(qualif);
if ty::type_contents(self.tcx, ret_ty).interior_unsafe() {
self.add_qualif(ConstQualif::MUTABLE_MEM);
}
true
} else {
false
}
}
fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) {
match self.rvalue_borrows.entry(id) {
Entry::Occupied(mut entry) => {
@ -158,6 +236,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
fn msg(&self) -> &'static str {
match self.mode {
Mode::Const => "constant",
Mode::ConstFn => "constant function",
Mode::StaticMut | Mode::Static => "static",
Mode::Var => unreachable!(),
}
@ -251,9 +330,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
b: &'v ast::Block,
s: Span,
fn_id: ast::NodeId) {
assert!(self.mode == Mode::Var);
self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
visit::walk_fn(self, fk, fd, b, s);
self.fn_like(fk, fd, b, s, fn_id);
}
fn visit_pat(&mut self, p: &ast::Pat) {
@ -269,6 +346,35 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
}
}
fn visit_block(&mut self, block: &ast::Block) {
// Check all statements in the block
for stmt in &block.stmts {
let span = match stmt.node {
ast::StmtDecl(ref decl, _) => {
match decl.node {
ast::DeclLocal(_) => decl.span,
// Item statements are allowed
ast::DeclItem(_) => continue
}
}
ast::StmtExpr(ref expr, _) => expr.span,
ast::StmtSemi(ref semi, _) => semi.span,
ast::StmtMac(..) => {
self.tcx.sess.span_bug(stmt.span, "unexpanded statement \
macro in const?!")
}
};
self.add_qualif(ConstQualif::NOT_CONST);
if self.mode != Mode::Var {
span_err!(self.tcx.sess, span, E0016,
"blocks in {}s are limited to items and \
tail expressions", self.msg());
}
}
visit::walk_block(self, block);
}
fn visit_expr(&mut self, ex: &ast::Expr) {
let mut outer = self.qualif;
self.qualif = ConstQualif::empty();
@ -473,10 +579,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
Some(def::DefStatic(..)) => {
match v.mode {
Mode::Static | Mode::StaticMut => {}
Mode::Const => {
Mode::Const | Mode::ConstFn => {
span_err!(v.tcx.sess, e.span, E0013,
"constants cannot refer to other statics, \
insert an intermediate constant instead");
"{}s cannot refer to other statics, insert \
an intermediate constant instead", v.msg());
}
Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
}
@ -493,6 +599,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
doesn't point to a constant");
}
}
Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => {
// Sadly, we can't determine whether the types are zero-sized.
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
}
def => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
@ -517,48 +627,44 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
};
}
let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
match def {
Some(def::DefStruct(..)) => {}
let is_const = match def {
Some(def::DefStruct(..)) => true,
Some(def::DefVariant(..)) => {
// Count the discriminator.
v.add_qualif(ConstQualif::NON_ZERO_SIZED);
true
}
_ => {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0015,
"function calls in {}s are limited to \
struct and enum constructors", v.msg());
}
Some(def::DefMethod(did, def::FromImpl(_))) |
Some(def::DefFn(did, _)) => {
v.handle_const_fn_call(did, node_ty)
}
_ => false
};
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, e.span, E0015,
"function calls in {}s are limited to \
constant functions, \
struct and enum constructors", v.msg());
}
}
}
ast::ExprBlock(ref block) => {
// Check all statements in the block
let mut block_span_err = |span| {
ast::ExprMethodCall(..) => {
let method_did = match v.tcx.method_map.borrow()[&method_call].origin {
ty::MethodStatic(did) => Some(did),
_ => None
};
let is_const = match method_did {
Some(did) => v.handle_const_fn_call(did, node_ty),
None => false
};
if !is_const {
v.add_qualif(ConstQualif::NOT_CONST);
if v.mode != Mode::Var {
span_err!(v.tcx.sess, span, E0016,
"blocks in {}s are limited to items and \
tail expressions", v.msg());
}
};
for stmt in &block.stmts {
match stmt.node {
ast::StmtDecl(ref decl, _) => {
match decl.node {
ast::DeclLocal(_) => block_span_err(decl.span),
// Item statements are allowed
ast::DeclItem(_) => {}
}
}
ast::StmtExpr(ref expr, _) => block_span_err(expr.span),
ast::StmtSemi(ref semi, _) => block_span_err(semi.span),
ast::StmtMac(..) => {
v.tcx.sess.span_bug(e.span, "unexpanded statement \
macro in const?!")
}
span_err!(v.tcx.sess, e.span, E0378,
"method calls in {}s are limited to \
constant inherent methods", v.msg());
}
}
}
@ -579,7 +685,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
}
ast::ExprClosure(..) => {
// Paths in constant constexts cannot refer to local variables,
// Paths in constant contexts cannot refer to local variables,
// as there are none, and thus closures can't have upvars there.
if ty::with_freevars(v.tcx, e.id, |fv| !fv.is_empty()) {
assert!(v.mode == Mode::Var,
@ -588,6 +694,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
}
}
ast::ExprBlock(_) |
ast::ExprUnary(..) |
ast::ExprBinary(..) |
ast::ExprIndex(..) |
@ -616,8 +723,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
// Miscellaneous expressions that could be implemented.
ast::ExprRange(..) |
// Various other expressions.
ast::ExprMethodCall(..) |
// Expressions with side-effects.
ast::ExprAssign(..) |
ast::ExprAssignOp(..) |
ast::ExprInlineAsm(_) |

View File

@ -24,11 +24,13 @@ use util::num::ToPrimitive;
use util::ppaux::Repr;
use syntax::ast::{self, Expr};
use syntax::ast_map::blocks::FnLikeNode;
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::feature_gate;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use syntax::{ast_map, ast_util, codemap};
use syntax::{ast_map, codemap, visit};
use std::borrow::{Cow, IntoCow};
use std::num::wrapping::OverflowingOps;
@ -198,6 +200,63 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
}
}
fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: ast::DefId)
-> Option<ast::NodeId> {
match tcx.extern_const_fns.borrow().get(&def_id) {
Some(&ast::DUMMY_NODE_ID) => return None,
Some(&fn_id) => return Some(fn_id),
None => {}
}
if !csearch::is_const_fn(&tcx.sess.cstore, def_id) {
tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID);
return None;
}
let fn_id = match csearch::maybe_get_item_ast(tcx, def_id,
box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
csearch::FoundAst::Found(&ast::IIItem(ref item)) => Some(item.id),
csearch::FoundAst::Found(&ast::IIImplItem(_, ref item)) => Some(item.id),
_ => None
};
tcx.extern_const_fns.borrow_mut().insert(def_id,
fn_id.unwrap_or(ast::DUMMY_NODE_ID));
fn_id
}
pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: ast::DefId)
-> Option<FnLikeNode<'tcx>>
{
let fn_id = if !ast_util::is_local(def_id) {
if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) {
fn_id
} else {
return None;
}
} else {
def_id.node
};
let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) {
Some(fn_like) => fn_like,
None => return None
};
match fn_like.kind() {
visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
Some(fn_like)
}
visit::FkMethod(_, m, _) => {
if m.constness == ast::Constness::Const {
Some(fn_like)
} else {
None
}
}
_ => None
}
}
#[derive(Clone, PartialEq)]
pub enum const_val {
const_float(f64),

View File

@ -87,8 +87,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
block: &'v ast::Block, span: Span, _: ast::NodeId) {
let (is_item_fn, is_unsafe_fn) = match fn_kind {
visit::FkItemFn(_, _, fn_style, _, _) =>
(true, fn_style == ast::Unsafety::Unsafe),
visit::FkItemFn(_, _, unsafety, _, _, _) =>
(true, unsafety == ast::Unsafety::Unsafe),
visit::FkMethod(_, sig, _) =>
(true, sig.unsafety == ast::Unsafety::Unsafe),
_ => (false, false),

View File

@ -158,6 +158,7 @@ trait ErrorReportingHelpers<'tcx> {
fn give_expl_lifetime_param(&self,
decl: &ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
ident: ast::Ident,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
generics: &ast::Generics,
@ -826,8 +827,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
Some(ref node) => match *node {
ast_map::NodeItem(ref item) => {
match item.node {
ast::ItemFn(ref fn_decl, pur, _, ref gen, _) => {
Some((fn_decl, gen, pur, item.ident, None, item.span))
ast::ItemFn(ref fn_decl, unsafety, constness, _, ref gen, _) => {
Some((fn_decl, gen, unsafety, constness,
item.ident, None, item.span))
},
_ => None
}
@ -838,6 +840,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
Some((&sig.decl,
&sig.generics,
sig.unsafety,
sig.constness,
item.ident,
Some(&sig.explicit_self.node),
item.span))
@ -852,6 +855,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
Some((&sig.decl,
&sig.generics,
sig.unsafety,
sig.constness,
item.ident,
Some(&sig.explicit_self.node),
item.span))
@ -863,12 +867,12 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
},
None => None
};
let (fn_decl, generics, unsafety, ident, expl_self, span)
let (fn_decl, generics, unsafety, constness, ident, expl_self, span)
= node_inner.expect("expect item fn");
let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
generics, same_regions, &life_giver);
let (fn_decl, expl_self, generics) = rebuilder.rebuild();
self.give_expl_lifetime_param(&fn_decl, unsafety, ident,
self.give_expl_lifetime_param(&fn_decl, unsafety, constness, ident,
expl_self.as_ref(), &generics, span);
}
}
@ -1423,12 +1427,13 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
fn give_expl_lifetime_param(&self,
decl: &ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
ident: ast::Ident,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
generics: &ast::Generics,
span: codemap::Span) {
let suggested_fn = pprust::fun_to_string(decl, unsafety, ident,
opt_explicit_self, generics);
let suggested_fn = pprust::fun_to_string(decl, unsafety, constness, ident,
opt_explicit_self, generics);
let msg = format!("consider using an explicit lifetime \
parameter as shown: {}", suggested_fn);
self.tcx.sess.span_help(span, &msg[..]);
@ -1710,7 +1715,7 @@ fn lifetimes_in_scope(tcx: &ty::ctxt,
let method_id_opt = match tcx.map.find(parent) {
Some(node) => match node {
ast_map::NodeItem(item) => match item.node {
ast::ItemFn(_, _, _, ref gen, _) => {
ast::ItemFn(_, _, _, _, ref gen, _) => {
taken.push_all(&gen.lifetimes);
None
},

View File

@ -46,7 +46,7 @@ fn item_might_be_inlined(item: &ast::Item) -> bool {
match item.node {
ast::ItemImpl(_, _, ref generics, _, _, _) |
ast::ItemFn(_, _, _, ref generics, _) => {
ast::ItemFn(_, _, _, _, ref generics, _) => {
generics_require_inlining(generics)
}
_ => false,
@ -256,7 +256,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
// but all other rust-only interfaces can be private (they will not
// participate in linkage after this product is produced)
if let ast_map::NodeItem(item) = *node {
if let ast::ItemFn(_, _, abi, _, _) = item.node {
if let ast::ItemFn(_, _, _, abi, _, _) = item.node {
if abi != abi::Rust {
self.reachable_symbols.insert(search_item);
}
@ -273,7 +273,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
match *node {
ast_map::NodeItem(item) => {
match item.node {
ast::ItemFn(_, _, _, _, ref search_block) => {
ast::ItemFn(_, _, _, _, _, ref search_block) => {
if item_might_be_inlined(&*item) {
visit::walk_block(self, &**search_block)
}

View File

@ -154,7 +154,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
b: &'v ast::Block, s: Span, _: ast::NodeId) {
match fk {
visit::FkItemFn(_, generics, _, _, _) => {
visit::FkItemFn(_, generics, _, _, _, _) => {
self.visit_early_late(subst::FnSpace, generics, |this| {
this.walk_fn(fk, fd, b, s)
})
@ -447,7 +447,7 @@ impl<'a> LifetimeContext<'a> {
fb: &'b ast::Block,
_span: Span) {
match fk {
visit::FkItemFn(_, generics, _, _, _) => {
visit::FkItemFn(_, generics, _, _, _, _) => {
visit::walk_fn_decl(self, fd);
self.visit_generics(generics);
}

View File

@ -775,10 +775,10 @@ pub struct ctxt<'tcx> {
/// Borrows
pub upvar_capture_map: RefCell<UpvarCaptureMap>,
/// These two caches are used by const_eval when decoding external statics
/// and variants that are found.
/// These caches are used by const_eval when decoding external constants.
pub extern_const_statics: RefCell<DefIdMap<ast::NodeId>>,
pub extern_const_variants: RefCell<DefIdMap<ast::NodeId>>,
pub extern_const_fns: RefCell<DefIdMap<ast::NodeId>>,
pub method_map: MethodMap<'tcx>,
@ -2428,7 +2428,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
}
Some(ast_map::NodeItem(item)) => {
match item.node {
ast::ItemFn(_, _, _, _, ref body) => {
ast::ItemFn(_, _, _, _, _, ref body) => {
// We assume this is a function.
let fn_def_id = ast_util::local_def(id);
let fn_scheme = lookup_item_type(cx, fn_def_id);
@ -2808,6 +2808,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
upvar_capture_map: RefCell::new(FnvHashMap()),
extern_const_statics: RefCell::new(DefIdMap()),
extern_const_variants: RefCell::new(DefIdMap()),
extern_const_fns: RefCell::new(DefIdMap()),
method_map: RefCell::new(FnvHashMap()),
dependency_formats: RefCell::new(FnvHashMap()),
closure_kinds: RefCell::new(DefIdMap()),

View File

@ -994,7 +994,7 @@ impl LintPass for NonSnakeCase {
},
_ => (),
},
visit::FkItemFn(ident, _, _, _, _) => {
visit::FkItemFn(ident, _, _, _, _, _) => {
self.check_snake_case(cx, "function", &token::get_ident(ident), Some(span))
},
_ => (),
@ -1341,7 +1341,7 @@ impl LintPass for UnsafeCode {
fn check_fn(&mut self, cx: &Context, fk: visit::FnKind, _: &ast::FnDecl,
_: &ast::Block, span: Span, _: ast::NodeId) {
match fk {
visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _, _) =>
visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _, _, _) =>
cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
visit::FkMethod(_, sig, _) => {
@ -1870,7 +1870,7 @@ impl LintPass for UnconditionalRecursion {
ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool;
let (name, checker) = match fn_kind {
visit::FkItemFn(name, _, _, _, _) => (name, id_refers_to_this_fn as F),
visit::FkItemFn(name, _, _, _, _, _) => (name, id_refers_to_this_fn as F),
visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F),
// closures can't recur, so they don't matter.
visit::FkFnBlock => return

View File

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

View File

@ -245,7 +245,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Resolver<'a, 'tcx> {
_: Span,
node_id: NodeId) {
let rib_kind = match function_kind {
visit::FkItemFn(_, generics, _, _, _) => {
visit::FkItemFn(_, generics, _, _, _, _) => {
self.visit_generics(generics);
ItemRibKind
}
@ -1809,7 +1809,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ItemRibKind),
|this| visit::walk_item(this, item));
}
ItemFn(_, _, _, ref generics, _) => {
ItemFn(_, _, _, _, ref generics, _) => {
self.with_type_parameter_rib(HasTypeParameters(generics,
FnSpace,
ItemRibKind),

View File

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

View File

@ -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, _, _) => {

View File

@ -1073,7 +1073,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
let blk = match tcx.map.find(id) {
Some(ast_map::NodeItem(i)) => {
match i.node {
ast::ItemFn(_, _, _, _, ref blk) => {
ast::ItemFn(_, _, _, _, _, ref blk) => {
blk
}
_ => tcx.sess.bug("unexpected item variant in has_nested_returns")
@ -1966,7 +1966,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
match item.node {
ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
ast::ItemFn(ref decl, _, _, abi, ref generics, ref body) => {
if !generics.is_type_parameterized() {
let trans_everywhere = attr::requests_inline(&item.attrs);
// Ignore `trans_everywhere` for cross-crate inlined items
@ -2307,7 +2307,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
// We need the translated value here, because for enums the
// LLVM type is not fully determined by the Rust type.
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
let (v, ty) = consts::const_expr(ccx, &**expr, empty_substs);
let (v, ty) = consts::const_expr(ccx, &**expr, empty_substs, None);
ccx.static_values().borrow_mut().insert(id, v);
unsafe {
// boolean SSA values are i1, but they have to be stored in i8 slots,
@ -2336,7 +2336,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
}
ast::ItemFn(_, _, abi, _, _) => {
ast::ItemFn(_, _, _, abi, _, _) => {
let sym = sym();
let llfn = if abi == Rust {
register_fn(ccx, i.span, sym, i.id, ty)

View File

@ -33,6 +33,7 @@ use middle::cast::{CastTy,IntTy};
use middle::subst::Substs;
use middle::ty::{self, Ty};
use util::ppaux::{Repr, ty_to_string};
use util::nodemap::NodeMap;
use std::iter::repeat;
use libc::c_uint;
@ -40,6 +41,8 @@ use syntax::{ast, ast_util};
use syntax::parse::token;
use syntax::ptr::P;
pub type FnArgMap<'a> = Option<&'a NodeMap<ValueRef>>;
pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit)
-> ValueRef {
let _icx = push_ctxt("trans_lit");
@ -163,6 +166,29 @@ fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
node: ExprOrMethodCall,
def_id: ast::DefId,
arg_vals: &[ValueRef],
param_substs: &'tcx Substs<'tcx>) -> ValueRef {
let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id);
let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call");
let args = &fn_like.decl().inputs;
assert_eq!(args.len(), arg_vals.len());
let arg_ids = args.iter().map(|arg| arg.pat.id);
let fn_args = arg_ids.zip(arg_vals.iter().cloned()).collect();
let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs));
match fn_like.body().expr {
Some(ref expr) => {
const_expr(ccx, &**expr, substs, Some(&fn_args)).0
}
None => C_nil(ccx)
}
}
pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
def_id: ast::DefId,
ref_expr: &ast::Expr)
@ -221,9 +247,9 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// references, even when only the latter are correct.
let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
&ty::expr_ty(ccx.tcx(), expr));
const_expr_unadjusted(ccx, expr, ty, param_substs)
const_expr_unadjusted(ccx, expr, ty, param_substs, None)
} else {
const_expr(ccx, expr, param_substs).0
const_expr(ccx, expr, param_substs, None).0
};
// boolean SSA values are i1, but they have to be stored in i8 slots,
@ -243,11 +269,12 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
e: &ast::Expr,
param_substs: &'tcx Substs<'tcx>)
param_substs: &'tcx Substs<'tcx>,
fn_args: FnArgMap)
-> (ValueRef, Ty<'tcx>) {
let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs,
&ty::expr_ty(cx.tcx(), e));
let llconst = const_expr_unadjusted(cx, e, ety, param_substs);
let llconst = const_expr_unadjusted(cx, e, ety, param_substs, fn_args);
let mut llconst = llconst;
let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs,
&ty::expr_ty_adjusted(cx.tcx(), e));
@ -440,7 +467,8 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
e: &ast::Expr,
ety: Ty<'tcx>,
param_substs: &'tcx Substs<'tcx>)
param_substs: &'tcx Substs<'tcx>,
fn_args: FnArgMap)
-> ValueRef
{
debug!("const_expr_unadjusted(e={}, ety={}, param_substs={})",
@ -448,9 +476,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ety.repr(cx.tcx()),
param_substs.repr(cx.tcx()));
let map_list = |exprs: &[P<ast::Expr>]| {
exprs.iter().map(|e| const_expr(cx, &**e, param_substs).0)
.fold(Vec::new(), |mut l, val| { l.push(val); l })
let map_list = |exprs: &[P<ast::Expr>]| -> Vec<ValueRef> {
exprs.iter()
.map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
.collect()
};
unsafe {
let _icx = push_ctxt("const_expr");
@ -461,7 +490,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ast::ExprBinary(b, ref e1, ref e2) => {
/* Neither type is bottom, and we expect them to be unified
* already, so the following is safe. */
let (te1, ty) = const_expr(cx, &**e1, param_substs);
let (te1, ty) = const_expr(cx, &**e1, param_substs, fn_args);
debug!("const_expr_unadjusted: te1={}, ty={}",
cx.tn().val_to_string(te1),
ty.repr(cx.tcx()));
@ -474,7 +503,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let is_float = ty::type_is_fp(intype);
let signed = ty::type_is_signed(intype);
let (te2, _) = const_expr(cx, &**e2, param_substs);
let (te2, _) = const_expr(cx, &**e2, param_substs, fn_args);
check_binary_expr_validity(cx, e, ty, te1, te2);
@ -534,7 +563,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
},
ast::ExprUnary(u, ref inner_e) => {
let (te, ty) = const_expr(cx, &**inner_e, param_substs);
let (te, ty) = const_expr(cx, &**inner_e, param_substs, fn_args);
check_unary_expr_validity(cx, e, ty, te);
@ -551,7 +580,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
ast::ExprField(ref base, field) => {
let (bv, bt) = const_expr(cx, &**base, param_substs);
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
let brepr = adt::represent_type(cx, bt);
expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| {
let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys);
@ -559,7 +588,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
})
}
ast::ExprTupField(ref base, idx) => {
let (bv, bt) = const_expr(cx, &**base, param_substs);
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
let brepr = adt::represent_type(cx, bt);
expr::with_field_tys(cx.tcx(), bt, None, |discr, _| {
adt::const_get_field(cx, &*brepr, bv, discr, idx.node)
@ -567,7 +596,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
ast::ExprIndex(ref base, ref index) => {
let (bv, bt) = const_expr(cx, &**base, param_substs);
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) {
Ok(const_eval::const_int(i)) => i as u64,
Ok(const_eval::const_uint(u)) => u,
@ -619,7 +648,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ast::ExprCast(ref base, _) => {
let t_cast = ety;
let llty = type_of::type_of(cx, t_cast);
let (v, t_expr) = const_expr(cx, &**base, param_substs);
let (v, t_expr) = const_expr(cx, &**base, param_substs, fn_args);
debug!("trans_const_cast({} as {})", t_expr.repr(cx.tcx()), t_cast.repr(cx.tcx()));
if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) {
return v;
@ -707,12 +736,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
} else {
// If this isn't the address of a static, then keep going through
// normal constant evaluation.
let (v, _) = const_expr(cx, &**sub, param_substs);
let (v, _) = const_expr(cx, &**sub, param_substs, fn_args);
addr_of(cx, v, "ref")
}
}
ast::ExprAddrOf(ast::MutMutable, ref sub) => {
let (v, _) = const_expr(cx, &**sub, param_substs);
let (v, _) = const_expr(cx, &**sub, param_substs, fn_args);
addr_of_mut(cx, v, "ref_mut_slice")
}
ast::ExprTup(ref es) => {
@ -724,7 +753,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let repr = adt::represent_type(cx, ety);
let base_val = match *base_opt {
Some(ref base) => Some(const_expr(cx, &**base, param_substs)),
Some(ref base) => Some(const_expr(cx, &**base, param_substs, fn_args)),
None => None
};
@ -732,7 +761,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let cs = field_tys.iter().enumerate()
.map(|(ix, &field_ty)| {
match fs.iter().find(|f| field_ty.name == f.ident.node.name) {
Some(ref f) => const_expr(cx, &*f.expr, param_substs).0,
Some(ref f) => const_expr(cx, &*f.expr, param_substs, fn_args).0,
None => {
match base_val {
Some((bv, _)) => {
@ -757,7 +786,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ast::ExprVec(ref es) => {
let unit_ty = ty::sequence_element_type(cx.tcx(), ety);
let llunitty = type_of::type_of(cx, unit_ty);
let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs).0)
let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
.collect::<Vec<_>>();
// If the vector contains enums, an LLVM array won't work.
if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
@ -770,7 +799,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let unit_ty = ty::sequence_element_type(cx.tcx(), ety);
let llunitty = type_of::type_of(cx, unit_ty);
let n = ty::eval_repeat_count(cx.tcx(), count);
let unit_val = const_expr(cx, &**elem, param_substs).0;
let unit_val = const_expr(cx, &**elem, param_substs, fn_args).0;
let vs: Vec<_> = repeat(unit_val).take(n).collect();
if val_ty(unit_val) != llunitty {
C_struct(cx, &vs[..], false)
@ -781,6 +810,13 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ast::ExprPath(..) => {
let def = cx.tcx().def_map.borrow().get(&e.id).unwrap().full_def();
match def {
def::DefLocal(id) => {
if let Some(val) = fn_args.and_then(|args| args.get(&id).cloned()) {
val
} else {
cx.sess().span_bug(e.span, "const fn argument not found")
}
}
def::DefFn(..) | def::DefMethod(..) => {
expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
}
@ -816,10 +852,24 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
ast::ExprCall(ref callee, ref args) => {
let opt_def = cx.tcx().def_map.borrow().get(&callee.id).map(|d| d.full_def());
let arg_vals = map_list(&args[..]);
match opt_def {
Some(def::DefStruct(_)) => {
let mut callee = &**callee;
loop {
callee = match callee.node {
ast::ExprParen(ref inner) => &**inner,
ast::ExprBlock(ref block) => match block.expr {
Some(ref tail) => &**tail,
None => break
},
_ => break
};
}
let def = cx.tcx().def_map.borrow()[&callee.id].full_def();
let arg_vals = map_list(args);
match def {
def::DefFn(did, _) | def::DefMethod(did, _) => {
const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs)
}
def::DefStruct(_) => {
if ty::type_is_simd(cx.tcx(), ety) {
C_vector(&arg_vals[..])
} else {
@ -827,7 +877,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
adt::trans_const(cx, &*repr, 0, &arg_vals[..])
}
}
Some(def::DefVariant(enum_did, variant_did, _)) => {
def::DefVariant(enum_did, variant_did, _) => {
let repr = adt::represent_type(cx, ety);
let vinfo = ty::enum_variant_with_id(cx.tcx(),
enum_did,
@ -837,13 +887,23 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
vinfo.disr_val,
&arg_vals[..])
}
_ => cx.sess().span_bug(e.span, "expected a struct or variant def")
_ => cx.sess().span_bug(e.span, "expected a struct, variant, or const fn def")
}
}
ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs).0,
ast::ExprMethodCall(_, _, ref args) => {
let arg_vals = map_list(args);
let method_call = ty::MethodCall::expr(e.id);
let method_did = match cx.tcx().method_map.borrow()[&method_call].origin {
ty::MethodStatic(did) => did,
_ => cx.sess().span_bug(e.span, "expected a const method def")
};
const_fn_call(cx, MethodCallKey(method_call),
method_did, &arg_vals, param_substs)
}
ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs, fn_args).0,
ast::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => const_expr(cx, &**expr, param_substs).0,
Some(ref expr) => const_expr(cx, &**expr, param_substs, fn_args).0,
None => C_nil(cx)
}
}

View File

@ -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)
}
_ => {

View File

@ -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.

View File

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

View File

@ -741,7 +741,7 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
&enum_definition.variants,
it.id);
}
ast::ItemFn(_, _, _, _, _) => {} // entirely within check_item_body
ast::ItemFn(..) => {} // entirely within check_item_body
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
match ty::impl_trait_ref(ccx.tcx, local_def(it.id)) {
@ -796,7 +796,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
ty::item_path_str(ccx.tcx, local_def(it.id)));
let _indenter = indenter();
match it.node {
ast::ItemFn(ref decl, _, _, _, ref body) => {
ast::ItemFn(ref decl, _, _, _, _, ref body) => {
let fn_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
@ -830,11 +830,15 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
check_const(ccx, trait_item.span, &*expr, trait_item.id)
}
ast::MethodTraitItem(ref sig, Some(ref body)) => {
check_trait_fn_not_const(ccx, trait_item.span, sig.constness);
check_method_body(ccx, &trait_def.generics, sig, body,
trait_item.id, trait_item.span);
}
ast::MethodTraitItem(ref sig, None) => {
check_trait_fn_not_const(ccx, trait_item.span, sig.constness);
}
ast::ConstTraitItem(_, None) |
ast::MethodTraitItem(_, None) |
ast::TypeTraitItem(..) => {
// Nothing to do.
}
@ -845,6 +849,20 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
}
}
fn check_trait_fn_not_const<'a,'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
span: Span,
constness: ast::Constness)
{
match constness {
ast::Constness::NotConst => {
// good
}
ast::Constness::Const => {
span_err!(ccx.tcx.sess, span, E0379, "trait fns cannot be declared const");
}
}
}
fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
generics: &ast::Generics,
item: &ast::Item) {
@ -966,7 +984,9 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
}
}
}
ast::MethodImplItem(_, ref body) => {
ast::MethodImplItem(ref sig, ref body) => {
check_trait_fn_not_const(ccx, impl_item.span, sig.constness);
let impl_method_def_id = local_def(impl_item.id);
let impl_item_ty = ty::impl_or_trait_item(ccx.tcx,
impl_method_def_id);

View File

@ -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) => {

View File

@ -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
}

View File

@ -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");

View File

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

View File

@ -989,6 +989,7 @@ pub struct Method {
pub generics: Generics,
pub self_: SelfTy,
pub unsafety: ast::Unsafety,
pub constness: ast::Constness,
pub decl: FnDecl,
pub abi: abi::Abi
}
@ -1010,7 +1011,8 @@ impl Clean<Method> for ast::MethodSig {
Method {
generics: self.generics.clean(cx),
self_: self.explicit_self.node.clean(cx),
unsafety: self.unsafety.clone(),
unsafety: self.unsafety,
constness: self.constness,
decl: decl,
abi: self.abi
}
@ -1075,7 +1077,8 @@ pub struct Function {
pub decl: FnDecl,
pub generics: Generics,
pub unsafety: ast::Unsafety,
pub abi: abi::Abi
pub constness: ast::Constness,
pub abi: abi::Abi,
}
impl Clean<Item> for doctree::Function {
@ -1091,6 +1094,7 @@ impl Clean<Item> for doctree::Function {
decl: self.decl.clean(cx),
generics: self.generics.clean(cx),
unsafety: self.unsafety,
constness: self.constness,
abi: self.abi,
}),
}
@ -1348,7 +1352,10 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
generics: generics,
self_: self_,
decl: decl,
abi: self.fty.abi
abi: self.fty.abi,
// trait methods canot (currently, at least) be const
constness: ast::Constness::NotConst,
})
} else {
TyMethodItem(TyMethod {
@ -1356,7 +1363,7 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
generics: generics,
self_: self_,
decl: decl,
abi: self.fty.abi
abi: self.fty.abi,
})
};
@ -2453,6 +2460,7 @@ impl Clean<Item> for ast::ForeignItem {
generics: generics.clean(cx),
unsafety: ast::Unsafety::Unsafe,
abi: abi::Rust,
constness: ast::Constness::NotConst,
})
}
ast::ForeignItemStatic(ref ty, mutbl) => {

View File

@ -133,6 +133,7 @@ pub struct Function {
pub vis: ast::Visibility,
pub stab: Option<attr::Stability>,
pub unsafety: ast::Unsafety,
pub constness: ast::Constness,
pub whence: Span,
pub generics: ast::Generics,
pub abi: abi::Abi,

View File

@ -35,6 +35,10 @@ pub struct VisSpace(pub Option<ast::Visibility>);
/// space after it.
#[derive(Copy, Clone)]
pub struct UnsafetySpace(pub ast::Unsafety);
/// Similarly to VisSpace, this structure is used to render a function constness
/// with a space after it.
#[derive(Copy, Clone)]
pub struct ConstnessSpace(pub ast::Constness);
/// Wrapper struct for properly emitting a method declaration.
pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
/// Similar to VisSpace, but used for mutability
@ -63,6 +67,12 @@ impl UnsafetySpace {
}
}
impl ConstnessSpace {
pub fn get(&self) -> ast::Constness {
let ConstnessSpace(v) = *self; v
}
}
impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, item) in self.0.iter().enumerate() {
@ -607,6 +617,15 @@ impl fmt::Display for UnsafetySpace {
}
}
impl fmt::Display for ConstnessSpace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.get() {
ast::Constness::Const => write!(f, "const "),
ast::Constness::NotConst => Ok(())
}
}
}
impl fmt::Display for clean::Import {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {

View File

@ -63,6 +63,7 @@ use clean;
use doctree;
use fold::DocFolder;
use html::escape::Escape;
use html::format::{ConstnessSpace};
use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
use html::highlight;
@ -1753,11 +1754,12 @@ fn item_static(w: &mut fmt::Formatter, it: &clean::Item,
fn item_function(w: &mut fmt::Formatter, it: &clean::Item,
f: &clean::Function) -> fmt::Result {
try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}{abi}fn \
try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}{abi}{constness}fn \
{name}{generics}{decl}{where_clause}</pre>",
vis = VisSpace(it.visibility),
unsafety = UnsafetySpace(f.unsafety),
abi = AbiSpace(f.abi),
constness = ConstnessSpace(f.constness),
name = it.name.as_ref().unwrap(),
generics = f.generics,
where_clause = WhereClause(&f.generics),
@ -1957,10 +1959,16 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item,
link: AssocItemLink) -> fmt::Result {
fn method(w: &mut fmt::Formatter, it: &clean::Item,
unsafety: ast::Unsafety, abi: abi::Abi,
g: &clean::Generics, selfty: &clean::SelfTy,
d: &clean::FnDecl, link: AssocItemLink) -> fmt::Result {
fn method(w: &mut fmt::Formatter,
it: &clean::Item,
unsafety: ast::Unsafety,
constness: ast::Constness,
abi: abi::Abi,
g: &clean::Generics,
selfty: &clean::SelfTy,
d: &clean::FnDecl,
link: AssocItemLink)
-> fmt::Result {
use syntax::abi::Abi;
let name = it.name.as_ref().unwrap();
@ -1971,12 +1979,10 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item,
href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
}
};
write!(w, "{}{}fn <a href='{href}' class='fnname'>{name}</a>\
write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
{generics}{decl}{where_clause}",
match unsafety {
ast::Unsafety::Unsafe => "unsafe ",
_ => "",
},
UnsafetySpace(unsafety),
ConstnessSpace(constness),
match abi {
Abi::Rust => String::new(),
a => format!("extern {} ", a.to_string())
@ -1989,11 +1995,12 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item,
}
match meth.inner {
clean::TyMethodItem(ref m) => {
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl,
link)
method(w, meth, m.unsafety, ast::Constness::NotConst,
m.abi, &m.generics, &m.self_, &m.decl, link)
}
clean::MethodItem(ref m) => {
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl,
method(w, meth, m.unsafety, m.constness,
m.abi, &m.generics, &m.self_, &m.decl,
link)
}
clean::AssociatedConstItem(ref ty, ref default) => {

View File

@ -123,7 +123,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
pub fn visit_fn(&mut self, item: &ast::Item,
name: ast::Ident, fd: &ast::FnDecl,
unsafety: &ast::Unsafety, abi: &abi::Abi,
unsafety: &ast::Unsafety,
constness: ast::Constness,
abi: &abi::Abi,
gen: &ast::Generics) -> Function {
debug!("Visiting fn");
Function {
@ -136,6 +138,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
whence: item.span,
generics: gen.clone(),
unsafety: *unsafety,
constness: constness,
abi: *abi,
}
}
@ -291,8 +294,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
om.enums.push(self.visit_enum_def(item, name, ed, gen)),
ast::ItemStruct(ref sd, ref gen) =>
om.structs.push(self.visit_struct_def(item, name, &**sd, gen)),
ast::ItemFn(ref fd, ref pur, ref abi, ref gen, _) =>
om.fns.push(self.visit_fn(item, name, &**fd, pur, abi, gen)),
ast::ItemFn(ref fd, ref unsafety, constness, ref abi, ref gen, _) =>
om.fns.push(self.visit_fn(item, name, &**fd, unsafety,
constness, abi, gen)),
ast::ItemTy(ref ty, ref gen) => {
let t = Typedef {
ty: ty.clone(),

View File

@ -1215,6 +1215,7 @@ pub struct TypeField {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct MethodSig {
pub unsafety: Unsafety,
pub constness: Constness,
pub abi: Abi,
pub decl: P<FnDecl>,
pub generics: Generics,
@ -1475,6 +1476,12 @@ pub enum Unsafety {
Normal,
}
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum Constness {
Const,
NotConst,
}
impl fmt::Display for Unsafety {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(match *self {
@ -1761,7 +1768,7 @@ pub enum Item_ {
/// A `const` item
ItemConst(P<Ty>, P<Expr>),
/// A function declaration
ItemFn(P<FnDecl>, Unsafety, Abi, Generics, P<Block>),
ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Block>),
/// A module
ItemMod(Mod),
/// An external module

View File

@ -96,20 +96,10 @@ impl<'a> Code<'a> {
/// Attempts to construct a Code from presumed FnLike or Block node input.
pub fn from_node(node: Node) -> Option<Code> {
fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } }
match node {
ast_map::NodeItem(item) if item.is_fn_like() =>
Some(FnLikeCode(new(node))),
ast_map::NodeTraitItem(tm) if tm.is_fn_like() =>
Some(FnLikeCode(new(node))),
ast_map::NodeImplItem(_) =>
Some(FnLikeCode(new(node))),
ast_map::NodeExpr(e) if e.is_fn_like() =>
Some(FnLikeCode(new(node))),
ast_map::NodeBlock(block) =>
Some(BlockCode(block)),
_ =>
None,
if let ast_map::NodeBlock(block) = node {
Some(BlockCode(block))
} else {
FnLikeNode::from_node(node).map(|fn_like| FnLikeCode(fn_like))
}
}
}
@ -120,6 +110,7 @@ struct ItemFnParts<'a> {
ident: ast::Ident,
decl: &'a ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
abi: abi::Abi,
vis: ast::Visibility,
generics: &'a ast::Generics,
@ -144,6 +135,24 @@ impl<'a> ClosureParts<'a> {
}
impl<'a> FnLikeNode<'a> {
/// Attempts to construct a FnLikeNode from presumed FnLike node input.
pub fn from_node(node: Node) -> Option<FnLikeNode> {
let fn_like = match node {
ast_map::NodeItem(item) => item.is_fn_like(),
ast_map::NodeTraitItem(tm) => tm.is_fn_like(),
ast_map::NodeImplItem(_) => true,
ast_map::NodeExpr(e) => e.is_fn_like(),
_ => false
};
if fn_like {
Some(FnLikeNode {
node: node
})
} else {
None
}
}
pub fn to_fn_parts(self) -> FnParts<'a> {
FnParts {
decl: self.decl(),
@ -180,7 +189,7 @@ impl<'a> FnLikeNode<'a> {
pub fn kind(self) -> visit::FnKind<'a> {
let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> {
visit::FkItemFn(p.ident, p.generics, p.unsafety, p.abi, p.vis)
visit::FkItemFn(p.ident, p.generics, p.unsafety, p.constness, p.abi, p.vis)
};
let closure = |_: ClosureParts| {
visit::FkFnBlock
@ -204,10 +213,18 @@ impl<'a> FnLikeNode<'a> {
{
match self.node {
ast_map::NodeItem(i) => match i.node {
ast::ItemFn(ref decl, unsafety, abi, ref generics, ref block) =>
item_fn(ItemFnParts{
ident: i.ident, decl: &**decl, unsafety: unsafety, body: &**block,
generics: generics, abi: abi, vis: i.vis, id: i.id, span: i.span
ast::ItemFn(ref decl, unsafety, constness, abi, ref generics, ref block) =>
item_fn(ItemFnParts {
id: i.id,
ident: i.ident,
decl: &**decl,
unsafety: unsafety,
body: &**block,
generics: generics,
abi: abi,
vis: i.vis,
constness: constness,
span: i.span
}),
_ => panic!("item FnLikeNode that is not fn-like"),
},

View File

@ -440,7 +440,7 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
self.operation.visit_id(node_id);
match function_kind {
visit::FkItemFn(_, generics, _, _, _) => {
visit::FkItemFn(_, generics, _, _, _, _) => {
self.visit_generics_helper(generics)
}
visit::FkMethod(_, sig, _) => {

View File

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

View File

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

View File

@ -513,11 +513,12 @@ pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
/// Expand item_underscore
fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
match item {
ast::ItemFn(decl, fn_style, abi, generics, body) => {
ast::ItemFn(decl, unsafety, constness, abi, generics, body) => {
let (rewritten_fn_decl, rewritten_body)
= expand_and_rename_fn_decl_and_block(decl, body, fld);
let expanded_generics = fold::noop_fold_generics(generics,fld);
ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
ast::ItemFn(rewritten_fn_decl, unsafety, constness, abi,
expanded_generics, rewritten_body)
}
_ => noop_fold_item_underscore(item, fld)
}
@ -1395,6 +1396,7 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
abi: sig.abi,
explicit_self: fld.fold_explicit_self(sig.explicit_self),
unsafety: sig.unsafety,
constness: sig.constness,
decl: rewritten_fn_decl
}, rewritten_body)
}

View File

@ -155,6 +155,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
// Allows the definition of associated constants in `trait` or `impl`
// blocks.
("associated_consts", "1.0.0", Active),
// Allows the definition of `const fn` functions.
("const_fn", "1.2.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@ -656,13 +659,26 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
block: &'v ast::Block,
span: Span,
_node_id: NodeId) {
// check for const fn declarations
match fn_kind {
visit::FkItemFn(_, _, _, abi, _) if abi == Abi::RustIntrinsic => {
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",
span,
"intrinsics are subject to change")
}
visit::FkItemFn(_, _, _, abi, _) |
visit::FkItemFn(_, _, _, _, abi, _) |
visit::FkMethod(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
self.gate_feature("unboxed_closures",
span,
@ -680,6 +696,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
ti.span,
"associated constants are experimental")
}
ast::MethodTraitItem(ref sig, _) => {
if sig.constness == ast::Constness::Const {
self.gate_feature("const_fn", ti.span, "const fn is unstable");
}
}
_ => {}
}
visit::walk_trait_item(self, ti);
@ -692,6 +713,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
ii.span,
"associated constants are experimental")
}
ast::MethodImplItem(ref sig, _) => {
if sig.constness == ast::Constness::Const {
self.gate_feature("const_fn", ii.span, "const fn is unstable");
}
}
_ => {}
}
visit::walk_impl_item(self, ii);

View File

@ -917,10 +917,11 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
ItemConst(t, e) => {
ItemConst(folder.fold_ty(t), folder.fold_expr(e))
}
ItemFn(decl, unsafety, abi, generics, body) => {
ItemFn(decl, unsafety, constness, abi, generics, body) => {
ItemFn(
folder.fold_fn_decl(decl),
unsafety,
constness,
abi,
folder.fold_generics(generics),
folder.fold_block(body)
@ -1124,6 +1125,7 @@ pub fn noop_fold_method_sig<T: Folder>(sig: MethodSig, folder: &mut T) -> Method
abi: sig.abi,
explicit_self: folder.fold_explicit_self(sig.explicit_self),
unsafety: sig.unsafety,
constness: sig.constness,
decl: folder.fold_fn_decl(sig.decl)
}
}

View File

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

View File

@ -17,7 +17,7 @@ use ast::{Public, Unsafety};
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
use ast::{ConstImplItem, ConstTraitItem, Crate, CrateConfig};
use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig};
use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
@ -1160,7 +1160,8 @@ impl<'a> Parser<'a> {
let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param());
try!(p.expect(&token::Semi));
(ident, TypeTraitItem(bounds, default))
} else if try!(p.eat_keyword(keywords::Const)) {
} else if p.is_const_item() {
try!(p.expect_keyword(keywords::Const));
let ident = try!(p.parse_ident());
try!(p.expect(&token::Colon));
let ty = try!(p.parse_ty_sum());
@ -1175,13 +1176,7 @@ impl<'a> Parser<'a> {
};
(ident, ConstTraitItem(ty, default))
} else {
let style = try!(p.parse_unsafety());
let abi = if try!(p.eat_keyword(keywords::Extern)) {
try!(p.parse_opt_abi()).unwrap_or(abi::C)
} else {
abi::Rust
};
try!(p.expect_keyword(keywords::Fn));
let (constness, unsafety, abi) = try!(p.parse_fn_front_matter());
let ident = try!(p.parse_ident());
let mut generics = try!(p.parse_generics());
@ -1195,7 +1190,8 @@ impl<'a> Parser<'a> {
generics.where_clause = try!(p.parse_where_clause());
let sig = ast::MethodSig {
unsafety: style,
unsafety: unsafety,
constness: constness,
decl: d,
generics: generics,
abi: abi,
@ -4359,12 +4355,46 @@ impl<'a> Parser<'a> {
}
/// Parse an item-position function declaration.
fn parse_item_fn(&mut self, unsafety: Unsafety, abi: abi::Abi) -> PResult<ItemInfo> {
fn parse_item_fn(&mut self,
unsafety: Unsafety,
constness: Constness,
abi: abi::Abi)
-> PResult<ItemInfo> {
let (ident, mut generics) = try!(self.parse_fn_header());
let decl = try!(self.parse_fn_decl(false));
generics.where_clause = try!(self.parse_where_clause());
let (inner_attrs, body) = try!(self.parse_inner_attrs_and_block());
Ok((ident, ItemFn(decl, unsafety, abi, generics, body), Some(inner_attrs)))
Ok((ident, ItemFn(decl, unsafety, constness, abi, generics, body), Some(inner_attrs)))
}
/// true if we are looking at `const ID`, false for things like `const fn` etc
pub fn is_const_item(&mut self) -> bool {
self.token.is_keyword(keywords::Const) &&
!self.look_ahead(1, |t| t.is_keyword(keywords::Fn))
}
/// parses all the "front matter" for a `fn` declaration, up to
/// and including the `fn` keyword:
///
/// - `const fn`
/// - `unsafe fn`
/// - `extern fn`
/// - etc
pub fn parse_fn_front_matter(&mut self) -> PResult<(ast::Constness, ast::Unsafety, abi::Abi)> {
let is_const_fn = try!(self.eat_keyword(keywords::Const));
let (constness, unsafety, abi) = if is_const_fn {
(Constness::Const, Unsafety::Normal, abi::Rust)
} else {
let unsafety = try!(self.parse_unsafety());
let abi = if try!(self.eat_keyword(keywords::Extern)) {
try!(self.parse_opt_abi()).unwrap_or(abi::C)
} else {
abi::Rust
};
(Constness::NotConst, unsafety, abi)
};
try!(self.expect_keyword(keywords::Fn));
Ok((constness, unsafety, abi))
}
/// Parse an impl item.
@ -4380,7 +4410,8 @@ impl<'a> Parser<'a> {
let typ = try!(self.parse_ty_sum());
try!(self.expect(&token::Semi));
(name, TypeImplItem(typ))
} else if try!(self.eat_keyword(keywords::Const)) {
} else if self.is_const_item() {
try!(self.expect_keyword(keywords::Const));
let name = try!(self.parse_ident());
try!(self.expect(&token::Colon));
let typ = try!(self.parse_ty_sum());
@ -4445,13 +4476,7 @@ impl<'a> Parser<'a> {
}
Ok((token::special_idents::invalid, vec![], ast::MacImplItem(m)))
} else {
let unsafety = try!(self.parse_unsafety());
let abi = if try!(self.eat_keyword(keywords::Extern)) {
try!(self.parse_opt_abi()).unwrap_or(abi::C)
} else {
abi::Rust
};
try!(self.expect_keyword(keywords::Fn));
let (constness, unsafety, abi) = try!(self.parse_fn_front_matter());
let ident = try!(self.parse_ident());
let mut generics = try!(self.parse_generics());
let (explicit_self, decl) = try!(self.parse_fn_decl_with_self(|p| {
@ -4464,6 +4489,7 @@ impl<'a> Parser<'a> {
abi: abi,
explicit_self: explicit_self,
unsafety: unsafety,
constness: constness,
decl: decl
}, body)))
}
@ -5252,7 +5278,7 @@ impl<'a> Parser<'a> {
// EXTERN FUNCTION ITEM
let abi = opt_abi.unwrap_or(abi::C);
let (ident, item_, extra_attrs) =
try!(self.parse_item_fn(Unsafety::Normal, abi));
try!(self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi));
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,
@ -5287,6 +5313,21 @@ impl<'a> Parser<'a> {
return Ok(Some(item));
}
if try!(self.eat_keyword(keywords::Const) ){
if self.check_keyword(keywords::Fn) {
// CONST FUNCTION ITEM
try!(self.bump());
let (ident, item_, extra_attrs) =
try!(self.parse_item_fn(Unsafety::Normal, Constness::Const, abi::Rust));
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,
ident,
item_,
visibility,
maybe_append(attrs, extra_attrs));
return Ok(Some(item));
}
// CONST ITEM
if try!(self.eat_keyword(keywords::Mut) ){
let last_span = self.last_span;
@ -5340,7 +5381,7 @@ impl<'a> Parser<'a> {
// FUNCTION ITEM
try!(self.bump());
let (ident, item_, extra_attrs) =
try!(self.parse_item_fn(Unsafety::Normal, abi::Rust));
try!(self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi::Rust));
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,
@ -5361,7 +5402,7 @@ impl<'a> Parser<'a> {
};
try!(self.expect_keyword(keywords::Fn));
let (ident, item_, extra_attrs) =
try!(self.parse_item_fn(Unsafety::Unsafe, abi));
try!(self.parse_item_fn(Unsafety::Unsafe, Constness::NotConst, abi));
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,

View File

@ -378,12 +378,16 @@ pub fn ident_to_string(id: &ast::Ident) -> String {
to_string(|s| s.print_ident(*id))
}
pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
generics: &ast::Generics) -> String {
pub fn fun_to_string(decl: &ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
name: ast::Ident,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
generics: &ast::Generics)
-> String {
to_string(|s| {
try!(s.head(""));
try!(s.print_fn(decl, unsafety, abi::Rust, Some(name),
try!(s.print_fn(decl, unsafety, constness, abi::Rust, Some(name),
generics, opt_explicit_self, ast::Inherited));
try!(s.end()); // Close the head box
s.end() // Close the outer box
@ -740,7 +744,8 @@ impl<'a> State<'a> {
match item.node {
ast::ForeignItemFn(ref decl, ref generics) => {
try!(self.head(""));
try!(self.print_fn(&**decl, ast::Unsafety::Normal,
try!(self.print_fn(decl, ast::Unsafety::Normal,
ast::Constness::NotConst,
abi::Rust, Some(item.ident),
generics, None, item.vis));
try!(self.end()); // end head-ibox
@ -866,11 +871,12 @@ impl<'a> State<'a> {
try!(word(&mut self.s, ";"));
try!(self.end()); // end the outer cbox
}
ast::ItemFn(ref decl, unsafety, abi, ref typarams, ref body) => {
ast::ItemFn(ref decl, unsafety, constness, abi, ref typarams, ref body) => {
try!(self.head(""));
try!(self.print_fn(
decl,
unsafety,
constness,
abi,
Some(item.ident),
typarams,
@ -1241,6 +1247,7 @@ impl<'a> State<'a> {
-> io::Result<()> {
self.print_fn(&m.decl,
m.unsafety,
m.constness,
m.abi,
Some(ident),
&m.generics,
@ -2335,12 +2342,13 @@ impl<'a> State<'a> {
pub fn print_fn(&mut self,
decl: &ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
abi: abi::Abi,
name: Option<ast::Ident>,
generics: &ast::Generics,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
vis: ast::Visibility) -> io::Result<()> {
try!(self.print_fn_header_info(unsafety, abi, vis));
try!(self.print_fn_header_info(unsafety, constness, abi, vis));
if let Some(name) = name {
try!(self.nbsp());
@ -2726,8 +2734,13 @@ impl<'a> State<'a> {
predicates: Vec::new(),
},
};
try!(self.print_fn(decl, unsafety, abi, name,
&generics, opt_explicit_self,
try!(self.print_fn(decl,
unsafety,
ast::Constness::NotConst,
abi,
name,
&generics,
opt_explicit_self,
ast::Inherited));
self.end()
}
@ -2976,11 +2989,17 @@ impl<'a> State<'a> {
pub fn print_fn_header_info(&mut self,
unsafety: ast::Unsafety,
constness: ast::Constness,
abi: abi::Abi,
vis: ast::Visibility) -> io::Result<()> {
try!(word(&mut self.s, &visibility_qualified(vis, "")));
try!(self.print_unsafety(unsafety));
match constness {
ast::Constness::NotConst => {}
ast::Constness::Const => try!(self.word_nbsp("const"))
}
if abi != abi::Rust {
try!(self.word_nbsp("extern"));
try!(self.word_nbsp(&abi.to_string()));
@ -3018,8 +3037,10 @@ mod tests {
variadic: false
};
let generics = ast_util::empty_generics();
assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal, abba_ident,
None, &generics),
assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal,
ast::Constness::NotConst,
abba_ident,
None, &generics),
"fn abba()");
}

View File

@ -123,7 +123,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> {
let i = if is_test_fn(&self.cx, &*i) || is_bench_fn(&self.cx, &*i) {
match i.node {
ast::ItemFn(_, ast::Unsafety::Unsafe, _, _, _) => {
ast::ItemFn(_, ast::Unsafety::Unsafe, _, _, _, _) => {
let diag = self.cx.span_diagnostic;
panic!(diag.span_fatal(i.span, "unsafe functions cannot be used for tests"));
}
@ -320,7 +320,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
fn has_test_signature(i: &ast::Item) -> HasTestSignature {
match &i.node {
&ast::ItemFn(ref decl, _, _, ref generics, _) => {
&ast::ItemFn(ref decl, _, _, _, ref generics, _) => {
let no_output = match decl.output {
ast::DefaultReturn(..) => true,
ast::Return(ref t) if t.node == ast::TyTup(vec![]) => true,
@ -356,7 +356,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
fn has_test_signature(i: &ast::Item) -> bool {
match i.node {
ast::ItemFn(ref decl, _, _, ref generics, _) => {
ast::ItemFn(ref decl, _, _, _, ref generics, _) => {
let input_cnt = decl.inputs.len();
let no_output = match decl.output {
ast::DefaultReturn(..) => true,
@ -469,7 +469,9 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> {
let main_ret_ty = ecx.ty(sp, ast::TyTup(vec![]));
let main_body = ecx.block_all(sp, vec![call_test_main], None);
let main = ast::ItemFn(ecx.fn_decl(vec![], main_ret_ty),
ast::Unsafety::Normal, ::abi::Rust, empty_generics(), main_body);
ast::Unsafety::Normal,
ast::Constness::NotConst,
::abi::Rust, empty_generics(), main_body);
let main = P(ast::Item {
ident: token::str_to_ident("main"),
attrs: vec![main_attr],

View File

@ -35,7 +35,7 @@ use owned_slice::OwnedSlice;
#[derive(Copy, Clone)]
pub enum FnKind<'a> {
/// fn foo() or extern "Abi" fn foo()
FkItemFn(Ident, &'a Generics, Unsafety, Abi, Visibility),
FkItemFn(Ident, &'a Generics, Unsafety, Constness, Abi, Visibility),
/// fn foo(&self)
FkMethod(Ident, &'a MethodSig, Option<Visibility>),
@ -246,8 +246,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ty(&**typ);
visitor.visit_expr(&**expr);
}
ItemFn(ref declaration, fn_style, abi, ref generics, ref body) => {
visitor.visit_fn(FkItemFn(item.ident, generics, fn_style, abi, item.vis),
ItemFn(ref declaration, unsafety, constness, abi, ref generics, ref body) => {
visitor.visit_fn(FkItemFn(item.ident, generics, unsafety,
constness, abi, item.vis),
&**declaration,
&**body,
item.span,
@ -604,7 +605,7 @@ pub fn walk_fn<'v, V: Visitor<'v>>(visitor: &mut V,
walk_fn_decl(visitor, function_declaration);
match function_kind {
FkItemFn(_, generics, _, _, _) => {
FkItemFn(_, generics, _, _, _, _) => {
visitor.visit_generics(generics);
}
FkMethod(_, sig, _) => {

View File

@ -117,7 +117,7 @@ static mut STATIC14: SafeStruct = SafeStruct {
//~^ ERROR mutable statics are not allowed to have destructors
field1: SafeEnum::Variant1,
field2: SafeEnum::Variant4("str".to_string())
//~^ ERROR static contains unimplemented expression type
//~^ ERROR method calls in statics are limited to constant inherent methods
};
static STATIC15: &'static [Box<MyOwned>] = &[

View File

@ -0,0 +1,26 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we can't declare a const fn in an impl -- right now it's
// just not allowed at all, though eventually it'd make sense to allow
// it if the trait fn is const (but right now no trait fns can be
// const).
#![feature(const_fn)]
trait Foo {
fn f() -> u32;
}
impl Foo for u32 {
const fn f() -> u32 { 22 } //~ ERROR E0379
}
fn main() { }

View File

@ -0,0 +1,21 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that const fn is illegal in a trait declaration, whether or
// not a default is provided.
#![feature(const_fn)]
trait Foo {
const fn f() -> u32; //~ ERROR trait fns cannot be declared const
const fn g() -> u32 { 0 } //~ ERROR trait fns cannot be declared const
}
fn main() { }

View File

@ -0,0 +1,47 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we can't call random fns in a const fn or do other bad things.
#![feature(const_fn)]
use std::mem::transmute;
fn random() -> u32 { 0 }
const fn sub(x: &u32) -> usize {
unsafe { transmute(x) } //~ ERROR E0015
}
const fn sub1() -> u32 {
random() //~ ERROR E0015
}
static Y: u32 = 0;
const fn get_Y() -> u32 {
Y
//~^ ERROR E0013
//~| ERROR cannot refer to other statics by value
}
const fn get_Y_addr() -> &'static u32 {
&Y
//~^ ERROR E0013
}
const fn get() -> u32 {
let x = 22; //~ ERROR E0016
let y = 44; //~ ERROR E0016
x + y
}
fn main() {
}

View File

@ -0,0 +1,28 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test use of const fn without feature gate.
const fn foo() -> usize { 0 } //~ ERROR const fn is unstable
trait Foo {
const fn foo() -> u32; //~ ERROR const fn is unstable
const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
}
impl Foo {
const fn baz() -> u32 { 0 } //~ ERROR const fn is unstable
}
impl Foo for u32 {
const fn foo() -> u32 { 0 } //~ ERROR const fn is unstable
}
fn main() { }

View File

@ -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() {}

View File

@ -17,6 +17,6 @@ static boxed: Box<RefCell<isize>> = box RefCell::new(0);
//~^ ERROR allocations are not allowed in statics
//~| ERROR the trait `core::marker::Sync` is not implemented for the type
//~| ERROR the trait `core::marker::Sync` is not implemented for the type
//~| ERROR function calls in statics are limited to struct and enum constructors
//~| ERROR E0015
fn main() { }

View File

@ -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() {}

View File

@ -0,0 +1,23 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test a call whose argument is the result of another call.
#![feature(const_fn)]
const fn sub(x: u32, y: u32) -> u32 {
x - y
}
const X: u32 = sub(sub(88, 44), 22);
fn main() {
assert_eq!(X, 22);
}

View File

@ -0,0 +1,32 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A very basic test of const fn functionality.
#![feature(const_fn)]
const fn add(x: u32, y: u32) -> u32 {
x + y
}
const fn sub(x: u32, y: u32) -> u32 {
x - y
}
const SUM: u32 = add(44, 22);
const DIFF: u32 = sub(44, 22);
fn main() {
assert_eq!(SUM, 66);
assert!(SUM != 88);
assert_eq!(DIFF, 22);
}