Skip to content

Commit 5aed495

Browse files
authored
Rollup merge of rust-lang#75857 - dtolnay:unsafe, r=nagisa
Syntactically permit unsafety on mods Similar to rust-lang#66183; we will accept these constructs syntactically but reject with a semantic check after macro expansion if a proc macro hasn't replaced it with something else meaningful to Rust. ```rust #[mymacro] unsafe mod m { ... } #[mymacro] unsafe extern "C++" { ... } ``` The intention is that this might be used as a kind of "item-level unsafe" in attribute macro DSLs -- holding things which are unsafe to declare but potentially safe to use. For example I look forward to using this in https://github.com/dtolnay/cxx. In the absence of a procedural macro rewriting them to something else, they'll continue to be rejected at compile time though with a better error message than before. ### Before: ```console error: expected item, found keyword `unsafe` --> src/main.rs:1:1 | 1 | unsafe mod m { | ^^^^^^ expected item ``` ### After: ```console error: module cannot be declared unsafe --> src/main.rs:1:1 | 1 | unsafe mod m { | ^^^^^^ error: extern block cannot be declared unsafe --> src/main.rs:4:1 | 4 | unsafe extern "C++" { | ^^^^^^ ``` Closes rust-lang#68048.
2 parents 8c35a92 + fd4dd00 commit 5aed495

File tree

17 files changed

+284
-37
lines changed

17 files changed

+284
-37
lines changed

compiler/rustc_ast/src/ast.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2289,22 +2289,28 @@ impl FnRetTy {
22892289
/// Module declaration.
22902290
///
22912291
/// E.g., `mod foo;` or `mod foo { .. }`.
2292-
#[derive(Clone, Encodable, Decodable, Debug, Default)]
2292+
#[derive(Clone, Encodable, Decodable, Debug)]
22932293
pub struct Mod {
22942294
/// A span from the first token past `{` to the last token until `}`.
22952295
/// For `mod foo;`, the inner span ranges from the first token
22962296
/// to the last token in the external file.
22972297
pub inner: Span,
2298+
/// `unsafe` keyword accepted syntactically for macro DSLs, but not
2299+
/// semantically by Rust.
2300+
pub unsafety: Unsafe,
22982301
pub items: Vec<P<Item>>,
22992302
/// `true` for `mod foo { .. }`; `false` for `mod foo;`.
23002303
pub inline: bool,
23012304
}
23022305

23032306
/// Foreign module declaration.
23042307
///
2305-
/// E.g., `extern { .. }` or `extern C { .. }`.
2308+
/// E.g., `extern { .. }` or `extern "C" { .. }`.
23062309
#[derive(Clone, Encodable, Decodable, Debug)]
23072310
pub struct ForeignMod {
2311+
/// `unsafe` keyword accepted syntactically for macro DSLs, but not
2312+
/// semantically by Rust.
2313+
pub unsafety: Unsafe,
23082314
pub abi: Option<StrLit>,
23092315
pub items: Vec<P<ForeignItem>>,
23102316
}

compiler/rustc_ast/src/mut_visit.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
490490
}
491491

492492
pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
493-
let ForeignMod { abi: _, items } = foreign_mod;
493+
let ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
494494
items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
495495
}
496496

@@ -970,7 +970,8 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
970970
vis.visit_asyncness(asyncness);
971971
}
972972

973-
pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
973+
pub fn noop_visit_mod<T: MutVisitor>(module: &mut Mod, vis: &mut T) {
974+
let Mod { inner, unsafety: _, items, inline: _ } = module;
974975
vis.visit_span(inner);
975976
items.flat_map_in_place(|item| vis.flat_map_item(item));
976977
}
@@ -990,7 +991,7 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
990991

991992
let len = items.len();
992993
if len == 0 {
993-
let module = Mod { inner: span, items: vec![], inline: true };
994+
let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true };
994995
Crate { module, attrs: vec![], span, proc_macros }
995996
} else if len == 1 {
996997
let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner();

compiler/rustc_ast_passes/src/ast_validation.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -990,12 +990,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
990990
self.error_item_without_body(item.span, "function", msg, " { <body> }");
991991
}
992992
}
993-
ItemKind::ForeignMod(_) => {
993+
ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
994994
let old_item = mem::replace(&mut self.extern_mod, Some(item));
995995
self.invalid_visibility(
996996
&item.vis,
997997
Some("place qualifiers on individual foreign items instead"),
998998
);
999+
if let Unsafe::Yes(span) = unsafety {
1000+
self.err_handler().span_err(span, "extern block cannot be declared unsafe");
1001+
}
9991002
visit::walk_item(self, item);
10001003
self.extern_mod = old_item;
10011004
return; // Avoid visiting again.
@@ -1029,7 +1032,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10291032
walk_list!(self, visit_attribute, &item.attrs);
10301033
return;
10311034
}
1032-
ItemKind::Mod(Mod { inline, .. }) => {
1035+
ItemKind::Mod(Mod { inline, unsafety, .. }) => {
1036+
if let Unsafe::Yes(span) = unsafety {
1037+
self.err_handler().span_err(span, "module cannot be declared unsafe");
1038+
}
10331039
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
10341040
if !inline && !self.session.contains_name(&item.attrs, sym::path) {
10351041
self.check_mod_file_item_asciionly(item.ident);

compiler/rustc_ast_pretty/src/pprust.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,11 @@ impl<'a> State<'a> {
11391139
self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
11401140
}
11411141
ast::ItemKind::Mod(ref _mod) => {
1142-
self.head(visibility_qualified(&item.vis, "mod"));
1142+
self.head(to_string(|s| {
1143+
s.print_visibility(&item.vis);
1144+
s.print_unsafety(_mod.unsafety);
1145+
s.word("mod");
1146+
}));
11431147
self.print_ident(item.ident);
11441148

11451149
if _mod.inline || self.is_expanded {
@@ -1154,7 +1158,10 @@ impl<'a> State<'a> {
11541158
}
11551159
}
11561160
ast::ItemKind::ForeignMod(ref nmod) => {
1157-
self.head("extern");
1161+
self.head(to_string(|s| {
1162+
s.print_unsafety(nmod.unsafety);
1163+
s.word("extern");
1164+
}));
11581165
if let Some(abi) = nmod.abi {
11591166
self.print_literal(&abi.as_lit());
11601167
self.nbsp();

compiler/rustc_expand/src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ impl<'a> StripUnconfigured<'a> {
399399
}
400400

401401
pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
402-
let ast::ForeignMod { abi: _, items } = foreign_mod;
402+
let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
403403
items.flat_map_in_place(|item| self.configure(item));
404404
}
405405

compiler/rustc_expand/src/expand.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_ast::token;
1313
use rustc_ast::tokenstream::TokenStream;
1414
use rustc_ast::visit::{self, AssocCtxt, Visitor};
1515
use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path};
16-
use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind};
16+
use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe};
1717
use rustc_ast_pretty::pprust;
1818
use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
1919
use rustc_data_structures::map_in_place::MapInPlace;
@@ -370,11 +370,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
370370
None => {
371371
// Resolution failed so we return an empty expansion
372372
krate.attrs = vec![];
373-
krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
373+
krate.module = ast::Mod {
374+
inner: orig_mod_span,
375+
unsafety: Unsafe::No,
376+
items: vec![],
377+
inline: true,
378+
};
374379
}
375380
Some(ast::Item { span, kind, .. }) => {
376381
krate.attrs = vec![];
377-
krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
382+
krate.module = ast::Mod {
383+
inner: orig_mod_span,
384+
unsafety: Unsafe::No,
385+
items: vec![],
386+
inline: true,
387+
};
378388
self.cx.span_err(
379389
span,
380390
&format!(
@@ -1441,8 +1451,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
14411451
push_directory(&self.cx.sess, ident, &item.attrs, dir)
14421452
} else {
14431453
// We have an outline `mod foo;` so we need to parse the file.
1444-
let (new_mod, dir) =
1445-
parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
1454+
let (new_mod, dir) = parse_external_mod(
1455+
&self.cx.sess,
1456+
ident,
1457+
span,
1458+
old_mod.unsafety,
1459+
dir,
1460+
&mut attrs,
1461+
pushed,
1462+
);
14461463

14471464
let krate = ast::Crate {
14481465
span: new_mod.inner,

compiler/rustc_expand/src/module.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_ast::{token, Attribute, Mod};
1+
use rustc_ast::{token, Attribute, Mod, Unsafe};
22
use rustc_errors::{struct_span_err, PResult};
33
use rustc_parse::new_parser_from_file;
44
use rustc_session::parse::ParseSess;
@@ -42,6 +42,7 @@ crate fn parse_external_mod(
4242
sess: &Session,
4343
id: Ident,
4444
span: Span, // The span to blame on errors.
45+
unsafety: Unsafe,
4546
Directory { mut ownership, path }: Directory,
4647
attrs: &mut Vec<Attribute>,
4748
pop_mod_stack: &mut bool,
@@ -60,13 +61,16 @@ crate fn parse_external_mod(
6061
drop(included_mod_stack);
6162

6263
// Actually parse the external file as a module.
63-
let mut module =
64-
new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
64+
let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span));
65+
let mut module = parser.parse_mod(&token::Eof, unsafety)?;
6566
module.0.inline = false;
6667
module
6768
};
6869
// (1) ...instead, we return a dummy module.
69-
let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default();
70+
let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| {
71+
let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false };
72+
(module, Vec::new())
73+
});
7074
attrs.append(&mut new_attrs);
7175

7276
// Extract the directory path for submodules of `module`.

compiler/rustc_parse/src/parser/item.rs

+51-16
Original file line numberDiff line numberDiff line change
@@ -28,35 +28,46 @@ impl<'a> Parser<'a> {
2828
/// Parses a source module as a crate. This is the main entry point for the parser.
2929
pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
3030
let lo = self.token.span;
31-
let (module, attrs) = self.parse_mod(&token::Eof)?;
31+
let (module, attrs) = self.parse_mod(&token::Eof, Unsafe::No)?;
3232
let span = lo.to(self.token.span);
3333
let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
3434
Ok(ast::Crate { attrs, module, span, proc_macros })
3535
}
3636

3737
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
3838
fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
39+
let unsafety = self.parse_unsafety();
40+
self.expect_keyword(kw::Mod)?;
3941
let id = self.parse_ident()?;
4042
let (module, mut inner_attrs) = if self.eat(&token::Semi) {
41-
Default::default()
43+
(Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }, Vec::new())
4244
} else {
4345
self.expect(&token::OpenDelim(token::Brace))?;
44-
self.parse_mod(&token::CloseDelim(token::Brace))?
46+
self.parse_mod(&token::CloseDelim(token::Brace), unsafety)?
4547
};
4648
attrs.append(&mut inner_attrs);
4749
Ok((id, ItemKind::Mod(module)))
4850
}
4951

5052
/// Parses the contents of a module (inner attributes followed by module items).
51-
pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
53+
pub fn parse_mod(
54+
&mut self,
55+
term: &TokenKind,
56+
unsafety: Unsafe,
57+
) -> PResult<'a, (Mod, Vec<Attribute>)> {
5258
let lo = self.token.span;
5359
let attrs = self.parse_inner_attributes()?;
54-
let module = self.parse_mod_items(term, lo)?;
60+
let module = self.parse_mod_items(term, lo, unsafety)?;
5561
Ok((module, attrs))
5662
}
5763

5864
/// Given a termination token, parses all of the items in a module.
59-
fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
65+
fn parse_mod_items(
66+
&mut self,
67+
term: &TokenKind,
68+
inner_lo: Span,
69+
unsafety: Unsafe,
70+
) -> PResult<'a, Mod> {
6071
let mut items = vec![];
6172
while let Some(item) = self.parse_item()? {
6273
items.push(item);
@@ -75,7 +86,7 @@ impl<'a> Parser<'a> {
7586

7687
let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };
7788

78-
Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
89+
Ok(Mod { inner: inner_lo.to(hi), unsafety, items, inline: true })
7990
}
8091
}
8192

@@ -235,8 +246,13 @@ impl<'a> Parser<'a> {
235246
self.parse_item_extern_crate()?
236247
} else {
237248
// EXTERN BLOCK
238-
self.parse_item_foreign_mod(attrs)?
249+
self.parse_item_foreign_mod(attrs, Unsafe::No)?
239250
}
251+
} else if self.is_unsafe_foreign_mod() {
252+
// EXTERN BLOCK
253+
let unsafety = self.parse_unsafety();
254+
self.expect_keyword(kw::Extern)?;
255+
self.parse_item_foreign_mod(attrs, unsafety)?
240256
} else if self.is_static_global() {
241257
// STATIC ITEM
242258
self.bump(); // `static`
@@ -256,7 +272,9 @@ impl<'a> Parser<'a> {
256272
{
257273
// IMPL ITEM
258274
self.parse_item_impl(attrs, def())?
259-
} else if self.eat_keyword(kw::Mod) {
275+
} else if self.check_keyword(kw::Mod)
276+
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod])
277+
{
260278
// MODULE ITEM
261279
self.parse_item_mod(attrs)?
262280
} else if self.eat_keyword(kw::Type) {
@@ -893,10 +911,14 @@ impl<'a> Parser<'a> {
893911
/// extern "C" {}
894912
/// extern {}
895913
/// ```
896-
fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
914+
fn parse_item_foreign_mod(
915+
&mut self,
916+
attrs: &mut Vec<Attribute>,
917+
unsafety: Unsafe,
918+
) -> PResult<'a, ItemInfo> {
897919
let abi = self.parse_abi(); // ABI?
898920
let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?;
899-
let module = ast::ForeignMod { abi, items };
921+
let module = ast::ForeignMod { unsafety, abi, items };
900922
Ok((Ident::invalid(), ItemKind::ForeignMod(module)))
901923
}
902924

@@ -938,6 +960,15 @@ impl<'a> Parser<'a> {
938960
.emit();
939961
}
940962

963+
fn is_unsafe_foreign_mod(&self) -> bool {
964+
self.token.is_keyword(kw::Unsafe)
965+
&& self.is_keyword_ahead(1, &[kw::Extern])
966+
&& self.look_ahead(
967+
2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize),
968+
|t| t.kind == token::OpenDelim(token::Brace),
969+
)
970+
}
971+
941972
fn is_static_global(&mut self) -> bool {
942973
if self.check_keyword(kw::Static) {
943974
// Check if this could be a closure.
@@ -1552,10 +1583,14 @@ impl<'a> Parser<'a> {
15521583
// `$qual fn` or `$qual $qual`:
15531584
|| QUALS.iter().any(|&kw| self.check_keyword(kw))
15541585
&& self.look_ahead(1, |t| {
1555-
// ...qualified and then `fn`, e.g. `const fn`.
1586+
// `$qual fn`, e.g. `const fn` or `async fn`.
15561587
t.is_keyword(kw::Fn)
1557-
// Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
1558-
|| t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
1588+
// Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
1589+
|| t.is_non_raw_ident_where(|i| QUALS.contains(&i.name)
1590+
// Rule out 2015 `const async: T = val`.
1591+
&& i.is_reserved()
1592+
// Rule out unsafe extern block.
1593+
&& !self.is_unsafe_foreign_mod())
15591594
})
15601595
// `extern ABI fn`
15611596
|| self.check_keyword(kw::Extern)
@@ -1567,9 +1602,9 @@ impl<'a> Parser<'a> {
15671602
/// up to and including the `fn` keyword. The formal grammar is:
15681603
///
15691604
/// ```
1570-
/// Extern = "extern" StringLit ;
1605+
/// Extern = "extern" StringLit? ;
15711606
/// FnQual = "const"? "async"? "unsafe"? Extern? ;
1572-
/// FnFrontMatter = FnQual? "fn" ;
1607+
/// FnFrontMatter = FnQual "fn" ;
15731608
/// ```
15741609
pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
15751610
let constness = self.parse_constness();
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
1+
{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}

0 commit comments

Comments
 (0)