|
18 | 18 |
|
19 | 19 | use std::mem;
|
20 | 20 | use std::ops::{Deref, DerefMut};
|
| 21 | +use std::str::FromStr; |
21 | 22 |
|
22 | 23 | use itertools::{Either, Itertools};
|
23 | 24 | use rustc_abi::ExternAbi;
|
@@ -81,6 +82,7 @@ struct AstValidator<'a> {
|
81 | 82 |
|
82 | 83 | /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
|
83 | 84 | extern_mod_safety: Option<Safety>,
|
| 85 | + extern_mod_abi: Option<ExternAbi>, |
84 | 86 |
|
85 | 87 | lint_node_id: NodeId,
|
86 | 88 |
|
@@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
|
121 | 123 | self.outer_trait_or_trait_impl = old;
|
122 | 124 | }
|
123 | 125 |
|
124 |
| - fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) { |
125 |
| - let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); |
| 126 | + fn with_in_extern_mod( |
| 127 | + &mut self, |
| 128 | + extern_mod_safety: Safety, |
| 129 | + abi: Option<ExternAbi>, |
| 130 | + f: impl FnOnce(&mut Self), |
| 131 | + ) { |
| 132 | + let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); |
| 133 | + let old_abi = mem::replace(&mut self.extern_mod_abi, abi); |
126 | 134 | f(self);
|
127 |
| - self.extern_mod_safety = old; |
| 135 | + self.extern_mod_safety = old_safety; |
| 136 | + self.extern_mod_abi = old_abi; |
128 | 137 | }
|
129 | 138 |
|
130 | 139 | fn with_tilde_const(
|
@@ -370,6 +379,65 @@ impl<'a> AstValidator<'a> {
|
370 | 379 | }
|
371 | 380 | }
|
372 | 381 |
|
| 382 | + /// An `extern "custom"` function must be unsafe, and must not have any parameters or return |
| 383 | + /// type. |
| 384 | + fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) { |
| 385 | + let dcx = self.dcx(); |
| 386 | + |
| 387 | + // An `extern "custom"` function must be unsafe. |
| 388 | + match sig.header.safety { |
| 389 | + Safety::Unsafe(_) => { /* all good */ } |
| 390 | + Safety::Safe(safe_span) => { |
| 391 | + let safe_span = |
| 392 | + self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span)); |
| 393 | + dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span }); |
| 394 | + } |
| 395 | + Safety::Default => match ctxt { |
| 396 | + FnCtxt::Foreign => { /* all good */ } |
| 397 | + FnCtxt::Free | FnCtxt::Assoc(_) => { |
| 398 | + self.dcx().emit_err(errors::AbiCustomSafeFunction { |
| 399 | + span: sig.span, |
| 400 | + unsafe_span: sig.span.shrink_to_lo(), |
| 401 | + }); |
| 402 | + } |
| 403 | + }, |
| 404 | + } |
| 405 | + |
| 406 | + // An `extern "custom"` function cannot be `async` and/or `gen`. |
| 407 | + if let Some(coroutine_kind) = sig.header.coroutine_kind { |
| 408 | + let coroutine_kind_span = self |
| 409 | + .sess |
| 410 | + .psess |
| 411 | + .source_map() |
| 412 | + .span_until_non_whitespace(coroutine_kind.span().to(sig.span)); |
| 413 | + |
| 414 | + self.dcx().emit_err(errors::AbiCustomCoroutine { |
| 415 | + span: sig.span, |
| 416 | + coroutine_kind_span, |
| 417 | + coroutine_kind_str: coroutine_kind.as_str(), |
| 418 | + }); |
| 419 | + } |
| 420 | + |
| 421 | + // An `extern "custom"` function must not have any parameters or return type. |
| 422 | + let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); |
| 423 | + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { |
| 424 | + spans.push(ret_ty.span); |
| 425 | + } |
| 426 | + |
| 427 | + if !spans.is_empty() { |
| 428 | + let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); |
| 429 | + let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); |
| 430 | + let padding = if header_span.is_empty() { "" } else { " " }; |
| 431 | + |
| 432 | + self.dcx().emit_err(errors::AbiCustomInvalidSignature { |
| 433 | + spans, |
| 434 | + symbol: ident.name, |
| 435 | + suggestion_span, |
| 436 | + padding, |
| 437 | + }); |
| 438 | + } |
| 439 | + } |
| 440 | + |
373 | 441 | /// This ensures that items can only be `unsafe` (or unmarked) outside of extern
|
374 | 442 | /// blocks.
|
375 | 443 | ///
|
@@ -1005,7 +1073,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
1005 | 1073 | if abi.is_none() {
|
1006 | 1074 | self.handle_missing_abi(*extern_span, item.id);
|
1007 | 1075 | }
|
1008 |
| - self.with_in_extern_mod(*safety, |this| { |
| 1076 | + |
| 1077 | + let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok()); |
| 1078 | + self.with_in_extern_mod(*safety, extern_abi, |this| { |
1009 | 1079 | visit::walk_item(this, item);
|
1010 | 1080 | });
|
1011 | 1081 | self.extern_mod_span = old_item;
|
@@ -1145,6 +1215,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
1145 | 1215 | self.check_foreign_fn_bodyless(*ident, body.as_deref());
|
1146 | 1216 | self.check_foreign_fn_headerless(sig.header);
|
1147 | 1217 | self.check_foreign_item_ascii_only(*ident);
|
| 1218 | + if self.extern_mod_abi == Some(ExternAbi::Custom) { |
| 1219 | + self.check_custom_abi(FnCtxt::Foreign, ident, sig); |
| 1220 | + } |
1148 | 1221 | }
|
1149 | 1222 | ForeignItemKind::TyAlias(box TyAlias {
|
1150 | 1223 | defaultness,
|
@@ -1352,6 +1425,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
1352 | 1425 | self.check_item_safety(span, safety);
|
1353 | 1426 | }
|
1354 | 1427 |
|
| 1428 | + if let FnKind::Fn(ctxt, _, fun) = fk |
| 1429 | + && let Extern::Explicit(str_lit, _) = fun.sig.header.ext |
| 1430 | + && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str()) |
| 1431 | + { |
| 1432 | + self.check_custom_abi(ctxt, &fun.ident, &fun.sig); |
| 1433 | + } |
| 1434 | + |
1355 | 1435 | self.check_c_variadic_type(fk);
|
1356 | 1436 |
|
1357 | 1437 | // Functions cannot both be `const async` or `const gen`
|
@@ -1703,6 +1783,7 @@ pub fn check_crate(
|
1703 | 1783 | outer_impl_trait_span: None,
|
1704 | 1784 | disallow_tilde_const: Some(TildeConstReason::Item),
|
1705 | 1785 | extern_mod_safety: None,
|
| 1786 | + extern_mod_abi: None, |
1706 | 1787 | lint_node_id: CRATE_NODE_ID,
|
1707 | 1788 | is_sdylib_interface,
|
1708 | 1789 | lint_buffer: lints,
|
|
0 commit comments