diff options
| author | Folkert de Vries <folkert@folkertdev.nl> | 2025-05-07 20:02:29 +0200 |
|---|---|---|
| committer | Folkert de Vries <folkert@folkertdev.nl> | 2025-06-12 20:27:10 +0200 |
| commit | 5f73ce2b7ebfc0234a4bc736217110a2af1e2380 (patch) | |
| tree | ebc007a2643f2ab9d875026e22478682735f908f /compiler/rustc_ast_passes | |
| parent | 0a39445252ac076ef573bcacee63bbdc59497b52 (diff) | |
| download | rust-5f73ce2b7ebfc0234a4bc736217110a2af1e2380.tar.gz rust-5f73ce2b7ebfc0234a4bc736217110a2af1e2380.zip | |
add `extern "custom"` functions
Diffstat (limited to 'compiler/rustc_ast_passes')
| -rw-r--r-- | compiler/rustc_ast_passes/messages.ftl | 17 | ||||
| -rw-r--r-- | compiler/rustc_ast_passes/src/ast_validation.rs | 89 | ||||
| -rw-r--r-- | compiler/rustc_ast_passes/src/errors.rs | 64 |
3 files changed, 166 insertions, 4 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 80754a8f65a..9a267501230 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -1,3 +1,20 @@ +ast_passes_abi_custom_coroutine = + functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}` + .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton + +ast_passes_abi_custom_invalid_signature = + invalid signature for `extern "custom"` function + .note = functions with the `"custom"` ABI cannot have any parameters or return type + .suggestion = remove the parameters and return type + +ast_passes_abi_custom_safe_foreign_function = + foreign functions with the `"custom"` ABI cannot be safe + .suggestion = remove the `safe` keyword from this definition + +ast_passes_abi_custom_safe_function = + functions with the `"custom"` ABI must be unsafe + .suggestion = add the `unsafe` keyword to this definition + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d6fe04d2994..018887d0e8e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -18,6 +18,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; +use std::str::FromStr; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; @@ -81,6 +82,7 @@ struct AstValidator<'a> { /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. extern_mod_safety: Option<Safety>, + extern_mod_abi: Option<ExternAbi>, lint_node_id: NodeId, @@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> { self.outer_trait_or_trait_impl = old; } - fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + fn with_in_extern_mod( + &mut self, + extern_mod_safety: Safety, + abi: Option<ExternAbi>, + f: impl FnOnce(&mut Self), + ) { + let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + let old_abi = mem::replace(&mut self.extern_mod_abi, abi); f(self); - self.extern_mod_safety = old; + self.extern_mod_safety = old_safety; + self.extern_mod_abi = old_abi; } fn with_tilde_const( @@ -370,6 +379,65 @@ impl<'a> AstValidator<'a> { } } + /// An `extern "custom"` function must be unsafe, and must not have any parameters or return + /// type. + fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) { + let dcx = self.dcx(); + + // An `extern "custom"` function must be unsafe. + match sig.header.safety { + Safety::Unsafe(_) => { /* all good */ } + Safety::Safe(safe_span) => { + let safe_span = + self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span)); + dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span }); + } + Safety::Default => match ctxt { + FnCtxt::Foreign => { /* all good */ } + FnCtxt::Free | FnCtxt::Assoc(_) => { + self.dcx().emit_err(errors::AbiCustomSafeFunction { + span: sig.span, + unsafe_span: sig.span.shrink_to_lo(), + }); + } + }, + } + + // An `extern "custom"` function cannot be `async` and/or `gen`. + if let Some(coroutine_kind) = sig.header.coroutine_kind { + let coroutine_kind_span = self + .sess + .psess + .source_map() + .span_until_non_whitespace(coroutine_kind.span().to(sig.span)); + + self.dcx().emit_err(errors::AbiCustomCoroutine { + span: sig.span, + coroutine_kind_span, + coroutine_kind_str: coroutine_kind.as_str(), + }); + } + + // An `extern "custom"` function must not have any parameters or return type. + let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + spans.push(ret_ty.span); + } + + if !spans.is_empty() { + let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); + let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); + let padding = if header_span.is_empty() { "" } else { " " }; + + self.dcx().emit_err(errors::AbiCustomInvalidSignature { + spans, + symbol: ident.name, + suggestion_span, + padding, + }); + } + } + /// This ensures that items can only be `unsafe` (or unmarked) outside of extern /// blocks. /// @@ -1005,7 +1073,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if abi.is_none() { self.handle_missing_abi(*extern_span, item.id); } - self.with_in_extern_mod(*safety, |this| { + + let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok()); + self.with_in_extern_mod(*safety, extern_abi, |this| { visit::walk_item(this, item); }); self.extern_mod_span = old_item; @@ -1145,6 +1215,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(*ident); + if self.extern_mod_abi == Some(ExternAbi::Custom) { + self.check_custom_abi(FnCtxt::Foreign, ident, sig); + } } ForeignItemKind::TyAlias(box TyAlias { defaultness, @@ -1352,6 +1425,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_item_safety(span, safety); } + if let FnKind::Fn(ctxt, _, fun) = fk + && let Extern::Explicit(str_lit, _) = fun.sig.header.ext + && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str()) + { + self.check_custom_abi(ctxt, &fun.ident, &fun.sig); + } + self.check_c_variadic_type(fk); // Functions cannot both be `const async` or `const gen` @@ -1703,6 +1783,7 @@ pub fn check_crate( outer_impl_trait_span: None, disallow_tilde_const: Some(TildeConstReason::Item), extern_mod_safety: None, + extern_mod_abi: None, lint_node_id: CRATE_NODE_ID, is_sdylib_interface, lint_buffer: lints, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 6f9737e0831..c437e62f4d3 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -824,3 +824,67 @@ pub(crate) struct MissingAbi { #[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_safe_foreign_function)] +pub(crate) struct AbiCustomSafeForeignFunction { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "", + style = "verbose" + )] + pub safe_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_safe_function)] +pub(crate) struct AbiCustomSafeFunction { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "unsafe ", + style = "verbose" + )] + pub unsafe_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_coroutine)] +pub(crate) struct AbiCustomCoroutine { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "", + style = "verbose" + )] + pub coroutine_kind_span: Span, + pub coroutine_kind_str: &'static str, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_custom_invalid_signature)] +#[note] +pub(crate) struct AbiCustomInvalidSignature { + #[primary_span] + pub spans: Vec<Span>, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "{padding}fn {symbol}()", + style = "verbose" + )] + pub suggestion_span: Span, + pub symbol: Symbol, + pub padding: &'static str, +} |
