rustc: Enable writing "unsafe extern fn() {}"

Previously, the parser would not allow you to simultaneously implement a
function with a different abi as well as being unsafe at the same time. This
extends the parser to allow functions of the form:

    unsafe extern fn foo() {
        // ...
    }

The closure type grammar was also changed to reflect this reversal, types
previously written as "extern unsafe fn()" must now be written as
"unsafe extern fn()". The parser currently has a hack which allows the old
style, but this will go away once a snapshot has landed.

Closes #10025

[breaking-change]
This commit is contained in:
Alex Crichton 2014-05-06 18:43:56 -07:00
parent cf6857b9e9
commit 08237cad8d
15 changed files with 59 additions and 49 deletions

View File

@ -110,7 +110,6 @@ enum Family {
UnsafeFn, // u
StaticMethod, // F
UnsafeStaticMethod, // U
ForeignFn, // e
Type, // y
ForeignType, // T
Mod, // m
@ -134,7 +133,6 @@ fn item_family(item: ebml::Doc) -> Family {
'u' => UnsafeFn,
'F' => StaticMethod,
'U' => UnsafeStaticMethod,
'e' => ForeignFn,
'y' => Type,
'T' => ForeignType,
'm' => Mod,
@ -339,7 +337,6 @@ fn item_to_def_like(item: ebml::Doc, did: ast::DefId, cnum: ast::CrateNum)
Struct => DlDef(ast::DefStruct(did)),
UnsafeFn => DlDef(ast::DefFn(did, ast::UnsafeFn)),
Fn => DlDef(ast::DefFn(did, ast::NormalFn)),
ForeignFn => DlDef(ast::DefFn(did, ast::ExternFn)),
StaticMethod | UnsafeStaticMethod => {
let fn_style = if fam == UnsafeStaticMethod { ast::UnsafeFn } else
{ ast::NormalFn };

View File

@ -787,7 +787,6 @@ fn style_fn_family(s: FnStyle) -> char {
match s {
UnsafeFn => 'u',
NormalFn => 'f',
ExternFn => 'e'
}
}
@ -795,7 +794,6 @@ fn fn_style_static_method_family(s: FnStyle) -> char {
match s {
UnsafeFn => 'U',
NormalFn => 'F',
_ => fail!("extern fn can't be static")
}
}

View File

@ -441,7 +441,6 @@ fn parse_fn_style(c: char) -> FnStyle {
match c {
'u' => UnsafeFn,
'n' => NormalFn,
'c' => ExternFn,
_ => fail!("parse_fn_style: bad fn_style {}", c)
}
}

View File

@ -293,7 +293,6 @@ fn enc_fn_style(w: &mut MemWriter, p: FnStyle) {
match p {
NormalFn => mywrite!(w, "n"),
UnsafeFn => mywrite!(w, "u"),
ExternFn => mywrite!(w, "c")
}
}

View File

@ -22,6 +22,7 @@ use middle::privacy;
use util::nodemap::NodeSet;
use collections::HashSet;
use syntax::abi;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{def_id_of_def, is_local};
@ -250,8 +251,10 @@ impl<'a> ReachableContext<'a> {
match *node {
ast_map::NodeItem(item) => {
match item.node {
ast::ItemFn(_, ast::ExternFn, _, _, _) => {
self.reachable_symbols.insert(search_item);
ast::ItemFn(_, _, abi, _, _) => {
if abi != abi::Rust {
self.reachable_symbols.insert(search_item);
}
}
_ => {}
}

View File

@ -1594,8 +1594,8 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> {
pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
let _icx = push_ctxt("trans_item");
match item.node {
ast::ItemFn(decl, fn_style, _abi, ref generics, body) => {
if fn_style == ast::ExternFn {
ast::ItemFn(decl, _fn_style, abi, ref generics, body) => {
if abi != Rust {
let llfndecl = get_item_val(ccx, item.id);
foreign::trans_rust_fn_with_foreign_abi(
ccx, decl, body, item.attrs.as_slice(), llfndecl, item.id);
@ -1939,8 +1939,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
}
ast::ItemFn(_, fn_style, _, _, _) => {
let llfn = if fn_style != ast::ExternFn {
ast::ItemFn(_, _, abi, _, _) => {
let llfn = if abi == Rust {
register_fn(ccx, i.span, sym, i.id, ty)
} else {
foreign::register_rust_fn_with_foreign_abi(ccx,

View File

@ -403,6 +403,5 @@ pub fn ast_fn_style_constant(fn_style: ast::FnStyle) -> uint {
match fn_style {
ast::UnsafeFn => 1u,
ast::NormalFn => 2u,
ast::ExternFn => 3u
}
}

View File

@ -22,7 +22,7 @@ use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::infer::fold_regions_in_sig;
use syntax::ast::{Many, Once, MutImmutable, MutMutable};
use syntax::ast::{ExternFn, NormalFn, UnsafeFn, NodeId};
use syntax::ast::{NormalFn, UnsafeFn, NodeId};
use syntax::ast::{Onceness, FnStyle};
use collections::HashMap;
use util::common::{indenter};
@ -83,7 +83,6 @@ impl<'f> Combine for Glb<'f> {
fn fn_styles(&self, a: FnStyle, b: FnStyle) -> cres<FnStyle> {
match (a, b) {
(ExternFn, _) | (_, ExternFn) => Ok(ExternFn),
(NormalFn, _) | (_, NormalFn) => Ok(NormalFn),
(UnsafeFn, UnsafeFn) => Ok(UnsafeFn)
}

View File

@ -23,7 +23,7 @@ use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::{TypeTrace, Subtype};
use collections::HashMap;
use syntax::ast::{Many, Once, NodeId};
use syntax::ast::{ExternFn, NormalFn, UnsafeFn};
use syntax::ast::{NormalFn, UnsafeFn};
use syntax::ast::{Onceness, FnStyle};
use util::ppaux::mt_to_str;
@ -78,8 +78,7 @@ impl<'f> Combine for Lub<'f> {
fn fn_styles(&self, a: FnStyle, b: FnStyle) -> cres<FnStyle> {
match (a, b) {
(UnsafeFn, _) | (_, UnsafeFn) => Ok(UnsafeFn),
(NormalFn, _) | (_, NormalFn) => Ok(NormalFn),
(ExternFn, ExternFn) => Ok(ExternFn),
(NormalFn, NormalFn) => Ok(NormalFn),
}
}

View File

@ -1200,7 +1200,7 @@ impl Clean<Item> for ast::ForeignItem {
ForeignFunctionItem(Function {
decl: decl.clean(),
generics: generics.clean(),
fn_style: ast::ExternFn,
fn_style: ast::NormalFn,
})
}
ast::ForeignItemStatic(ref ty, mutbl) => {

View File

@ -397,10 +397,10 @@ impl fmt::Show for clean::Type {
clean::BareFunction(ref decl) => {
write!(f.buf, "{}{}fn{}{}",
FnStyleSpace(decl.fn_style),
match decl.abi {
ref x if "" == *x => "".to_owned(),
ref x if "\"Rust\"" == *x => "".to_owned(),
ref s => " " + *s + " ",
match decl.abi.as_slice() {
"" => " extern ".to_owned(),
"\"Rust\"" => "".to_owned(),
s => format!(" extern {} ", s)
},
decl.generics,
decl.decl)
@ -517,7 +517,6 @@ impl fmt::Show for FnStyleSpace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.get() {
ast::UnsafeFn => write!(f.buf, "unsafe "),
ast::ExternFn => write!(f.buf, "extern "),
ast::NormalFn => Ok(())
}
}

View File

@ -876,7 +876,6 @@ pub struct FnDecl {
pub enum FnStyle {
UnsafeFn, // declared with "unsafe fn"
NormalFn, // declared with "fn"
ExternFn, // declared with "extern fn"
}
impl fmt::Show for FnStyle {
@ -884,7 +883,6 @@ impl fmt::Show for FnStyle {
match *self {
NormalFn => "normal".fmt(f),
UnsafeFn => "unsafe".fmt(f),
ExternFn => "extern".fmt(f),
}
}
}

View File

@ -27,7 +27,7 @@ use ast::{ExprLit, ExprLoop, ExprMac};
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc};
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
use ast::{ExprVec, ExprVstore, ExprVstoreSlice};
use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, ExternFn, Field, FnDecl};
use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, Field, FnDecl};
use ast::{ExprVstoreUniq, Once, Many};
use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod};
use ast::{Ident, NormalFn, Inherited, Item, Item_, ItemStatic};
@ -881,25 +881,29 @@ impl<'a> Parser<'a> {
pub fn parse_ty_bare_fn(&mut self) -> Ty_ {
/*
[extern "ABI"] [unsafe] fn <'lt> (S) -> T
^~~~^ ^~~~~~~^ ^~~~^ ^~^ ^
| | | | |
| | | | Return type
| | | Argument types
| | Lifetimes
| |
| Function Style
ABI
[unsafe] [extern "ABI"] fn <'lt> (S) -> T
^~~~^ ^~~~^ ^~~~^ ^~^ ^
| | | | |
| | | | Return type
| | | Argument types
| | Lifetimes
| ABI
Function Style
*/
let fn_style = self.parse_unsafety();
let abi = if self.eat_keyword(keywords::Extern) {
self.parse_opt_abi().unwrap_or(abi::C)
} else {
abi::Rust
};
let fn_style = self.parse_unsafety();
// NOTE: remove after a stage0 snapshot
let fn_style = match self.parse_unsafety() {
UnsafeFn => UnsafeFn,
NormalFn => fn_style,
};
self.expect_keyword(keywords::Fn);
let (decl, lifetimes) = self.parse_ty_fn_decl(true);
return TyBareFn(@BareFnTy {
@ -1245,6 +1249,7 @@ impl<'a> Parser<'a> {
self.expect_and();
self.parse_borrowed_pointee()
} else if self.is_keyword(keywords::Extern) ||
self.is_keyword(keywords::Unsafe) ||
self.token_is_bare_fn_keyword() {
// BARE FUNCTION
self.parse_ty_bare_fn()
@ -4551,7 +4556,7 @@ impl<'a> Parser<'a> {
// EXTERN FUNCTION ITEM
let abi = opt_abi.unwrap_or(abi::C);
let (ident, item_, extra_attrs) =
self.parse_item_fn(ExternFn, abi);
self.parse_item_fn(NormalFn, abi);
let item = self.mk_item(lo,
self.last_span.hi,
ident,
@ -4605,9 +4610,14 @@ impl<'a> Parser<'a> {
&& self.look_ahead(1u, |t| *t != token::LBRACE) {
// UNSAFE FUNCTION ITEM
self.bump();
let abi = if self.eat_keyword(keywords::Extern) {
self.parse_opt_abi().unwrap_or(abi::C)
} else {
abi::Rust
};
self.expect_keyword(keywords::Fn);
let (ident, item_, extra_attrs) =
self.parse_item_fn(UnsafeFn, abi::Rust);
self.parse_item_fn(UnsafeFn, abi);
let item = self.mk_item(lo,
self.last_span.hi,
ident,

View File

@ -2372,16 +2372,10 @@ impl<'a> State<'a> {
abi: abi::Abi,
vis: ast::Visibility) -> IoResult<()> {
try!(word(&mut self.s, visibility_qualified(vis, "")));
try!(self.print_opt_fn_style(opt_fn_style));
if abi != abi::Rust {
try!(self.word_nbsp("extern"));
try!(self.word_nbsp(abi.to_str()));
if opt_fn_style != Some(ast::ExternFn) {
try!(self.print_opt_fn_style(opt_fn_style));
}
} else {
try!(self.print_opt_fn_style(opt_fn_style));
}
word(&mut self.s, "fn")
@ -2391,7 +2385,6 @@ impl<'a> State<'a> {
match s {
ast::NormalFn => Ok(()),
ast::UnsafeFn => self.word_nbsp("unsafe"),
ast::ExternFn => self.word_nbsp("extern")
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2014 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.
unsafe extern fn foo() {}
unsafe extern "C" fn bar() {}
fn main() {
let _a: unsafe extern fn() = foo;
let _a: unsafe extern "C" fn() = foo;
}