Skip to content
/ rust Public
forked from rust-lang/rust

Commit da9f1e4

Browse files
authored
Rollup merge of rust-lang#159780 - folkertdev:extern-custom-check-fn-ptrs, r=WaffleLapkin
check `extern "custom"` function pointers tracking issue: rust-lang#140829 related RFC: rust-lang/rfcs#3980 Best reviewed commit-by-commit. This PR makes 3 changes - extend the ABI checks we already performed on function definitions and trait and foreign declarations to function pointer types. This touches the various `interupt` ABIs and `extern "custom"`. - remove the ability for `extern "custom"` to return `!` - improve the suggestion when `safe` is used in a function pointer type
2 parents ac3f45c + d0dde60 commit da9f1e4

31 files changed

Lines changed: 705 additions & 207 deletions

compiler/rustc_ast/src/ast.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,27 +2339,7 @@ pub struct FnSig {
23392339
impl FnSig {
23402340
/// Return a span encompassing the header, or where to insert it if empty.
23412341
pub fn header_span(&self) -> Span {
2342-
match self.header.ext {
2343-
Extern::Implicit(span) | Extern::Explicit(_, span) => {
2344-
return self.span.with_hi(span.hi());
2345-
}
2346-
Extern::None => {}
2347-
}
2348-
2349-
match self.header.safety {
2350-
Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()),
2351-
Safety::Default => {}
2352-
};
2353-
2354-
if let Some(coroutine_kind) = self.header.coroutine_kind {
2355-
return self.span.with_hi(coroutine_kind.span().hi());
2356-
}
2357-
2358-
if let Const::Yes(span) = self.header.constness {
2359-
return self.span.with_hi(span.hi());
2360-
}
2361-
2362-
self.span.shrink_to_lo()
2342+
self.header.span().unwrap_or(self.span.shrink_to_lo())
23632343
}
23642344

23652345
/// The span of the header's safety, or where to insert it if empty.
@@ -2382,6 +2362,19 @@ impl FnSig {
23822362
pub fn extern_span(&self) -> Span {
23832363
self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi())
23842364
}
2365+
2366+
pub fn as_borrowed(&self) -> BorrowedFnSig<'_> {
2367+
BorrowedFnSig { header: self.header, decl: &self.decl, span: self.span }
2368+
}
2369+
}
2370+
2371+
/// A borrowed version of `FnSig`, used to share logic between function declarations and function
2372+
/// pointer types.
2373+
#[derive(Clone, Debug)]
2374+
pub struct BorrowedFnSig<'a> {
2375+
pub header: FnHeader,
2376+
pub decl: &'a FnDecl,
2377+
pub span: Span,
23852378
}
23862379

23872380
/// A constraint on an associated item.
@@ -2487,6 +2480,16 @@ pub struct FnPtrTy {
24872480
pub decl_span: Span,
24882481
}
24892482

2483+
impl FnPtrTy {
2484+
pub fn header(&self) -> FnHeader {
2485+
FnHeader { constness: Const::No, coroutine_kind: None, safety: self.safety, ext: self.ext }
2486+
}
2487+
2488+
pub fn as_borrowed_fn_sig<'a>(&'a self) -> BorrowedFnSig<'a> {
2489+
BorrowedFnSig { header: self.header(), decl: &self.decl, span: self.decl_span }
2490+
}
2491+
}
2492+
24902493
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
24912494
pub struct UnsafeBinderTy {
24922495
pub generic_params: ThinVec<GenericParam>,
@@ -3845,6 +3848,30 @@ impl FnHeader {
38453848
|| matches!(constness, Const::Yes(_))
38463849
|| !matches!(ext, Extern::None)
38473850
}
3851+
3852+
pub fn span(&self) -> Option<Span> {
3853+
let mut spans = smallvec::SmallVec::<[Span; 4]>::new();
3854+
3855+
match self.ext {
3856+
Extern::Implicit(span) | Extern::Explicit(_, span) => spans.push(span),
3857+
Extern::None => {}
3858+
}
3859+
3860+
match self.safety {
3861+
Safety::Unsafe(span) | Safety::Safe(span) => spans.push(span),
3862+
Safety::Default => {}
3863+
};
3864+
3865+
if let Some(coroutine_kind) = self.coroutine_kind {
3866+
spans.push(coroutine_kind.span());
3867+
}
3868+
3869+
if let Const::Yes(span) = self.constness {
3870+
spans.push(span)
3871+
}
3872+
3873+
spans.into_iter().reduce(Span::to)
3874+
}
38483875
}
38493876

38503877
impl Default for FnHeader {

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,13 @@ impl<'a> AstValidator<'a> {
546546
}
547547

548548
/// Check that the signature of this function does not violate the constraints of its ABI.
549-
fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
549+
fn check_extern_fn_signature(
550+
&self,
551+
abi: ExternAbi,
552+
ctxt: FnCtxt,
553+
opt_function_name: Option<&Ident>, // None for function pointers
554+
sig: &BorrowedFnSig<'_>,
555+
) {
550556
match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) {
551557
AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => {
552558
match canon_abi {
@@ -569,13 +575,13 @@ impl<'a> AstValidator<'a> {
569575

570576
CanonAbi::Custom => {
571577
// An `extern "custom"` function must be unsafe.
572-
self.reject_safe_fn(abi, ctxt, sig);
578+
self.reject_safe_fn(abi, ctxt, sig, opt_function_name.is_none());
573579

574580
// An `extern "custom"` function cannot be `async` and/or `gen`.
575581
self.reject_coroutine(abi, sig);
576582

577583
// An `extern "custom"` function must have type `fn()`.
578-
self.reject_params_or_return(abi, ident, sig);
584+
self.reject_params_or_return(abi, opt_function_name, sig);
579585
}
580586

581587
CanonAbi::Interrupt(interrupt_kind) => {
@@ -600,7 +606,7 @@ impl<'a> AstValidator<'a> {
600606
self.reject_return(abi, sig);
601607
} else {
602608
// An `extern "interrupt"` function must have type `fn()`.
603-
self.reject_params_or_return(abi, ident, sig);
609+
self.reject_params_or_return(abi, opt_function_name, sig);
604610
}
605611
}
606612
}
@@ -609,18 +615,27 @@ impl<'a> AstValidator<'a> {
609615
}
610616
}
611617

612-
fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) {
618+
fn reject_safe_fn(
619+
&self,
620+
abi: ExternAbi,
621+
ctxt: FnCtxt,
622+
sig: &BorrowedFnSig<'_>,
623+
is_fn_ptr: bool,
624+
) {
613625
let dcx = self.dcx();
614626

615627
match sig.header.safety {
616628
Safety::Unsafe(_) => { /* all good */ }
617629
Safety::Safe(safe_span) => {
618-
let source_map = self.sess.psess.source_map();
619-
let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
620-
dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction {
621-
span: sig.span,
622-
safe_span,
623-
});
630+
// Function pointers already error when `safe` is used.
631+
if !is_fn_ptr {
632+
let source_map = self.sess.psess.source_map();
633+
let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
634+
dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction {
635+
span: sig.span,
636+
safe_span,
637+
});
638+
}
624639
}
625640
Safety::Default => match ctxt {
626641
FnCtxt::Foreign => { /* all good */ }
@@ -635,7 +650,7 @@ impl<'a> AstValidator<'a> {
635650
}
636651
}
637652

638-
fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) {
653+
fn reject_coroutine(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) {
639654
if let Some(coroutine_kind) = sig.header.coroutine_kind {
640655
let coroutine_kind_span = self
641656
.sess
@@ -652,7 +667,7 @@ impl<'a> AstValidator<'a> {
652667
}
653668
}
654669

655-
fn reject_return(&self, abi: ExternAbi, sig: &FnSig) {
670+
fn reject_return(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) {
656671
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
657672
&& match &ret_ty.kind {
658673
TyKind::Never => false,
@@ -664,29 +679,41 @@ impl<'a> AstValidator<'a> {
664679
}
665680
}
666681

667-
fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
682+
fn reject_params_or_return(
683+
&self,
684+
abi: ExternAbi,
685+
opt_function_name: Option<&Ident>, // None for function pointers
686+
sig: &BorrowedFnSig<'_>,
687+
) {
668688
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
689+
690+
let allowed_return = |ret_ty: &Ty| match &ret_ty.kind {
691+
TyKind::Never if abi != ExternAbi::Custom => true,
692+
TyKind::Tup(tup) if tup.is_empty() => true,
693+
_ => false,
694+
};
695+
669696
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
670-
&& match &ret_ty.kind {
671-
TyKind::Never => false,
672-
TyKind::Tup(tup) if tup.is_empty() => false,
673-
_ => true,
674-
}
697+
&& !allowed_return(ret_ty)
675698
{
676699
spans.push(ret_ty.span);
677700
}
678701

679702
if !spans.is_empty() {
680-
let header_span = sig.header_span();
703+
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
681704
let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
682705
let padding = if header_span.is_empty() { "" } else { " " };
683706

684707
self.dcx().emit_err(diagnostics::AbiMustNotHaveParametersOrReturnType {
685708
spans,
686-
symbol: ident.name,
709+
abi,
710+
687711
suggestion_span,
688712
padding,
689-
abi,
713+
symbol: match opt_function_name {
714+
Some(ident) => format!(" {}", ident.name),
715+
None => String::new(),
716+
},
690717
});
691718
}
692719
}
@@ -717,8 +744,12 @@ impl<'a> AstValidator<'a> {
717744
}
718745

719746
fn check_fn_ptr_safety(&self, span: Span, safety: Safety) {
720-
if matches!(safety, Safety::Safe(_)) {
721-
self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { span });
747+
if let Safety::Safe(safe_span) = safety {
748+
let remove_span = self.sess.source_map().span_until_non_whitespace(span);
749+
self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr {
750+
span: safe_span,
751+
safe_span: remove_span,
752+
});
722753
}
723754
}
724755

@@ -1138,6 +1169,24 @@ impl<'a> AstValidator<'a> {
11381169
if let Extern::Implicit(extern_span) = bfty.ext {
11391170
self.handle_missing_abi(extern_span, ty.id);
11401171
}
1172+
1173+
let ext = match bfty.ext {
1174+
Extern::None => None,
1175+
Extern::Implicit(_) => Some(ExternAbi::FALLBACK),
1176+
Extern::Explicit(str_lit, _) => {
1177+
ExternAbi::from_str(str_lit.symbol.as_str()).ok()
1178+
}
1179+
};
1180+
1181+
// Some ABIs impose special restrictions on the signature.
1182+
if let Some(extern_abi) = ext {
1183+
self.check_extern_fn_signature(
1184+
extern_abi,
1185+
FnCtxt::Free,
1186+
None,
1187+
&bfty.as_borrowed_fn_sig(),
1188+
);
1189+
}
11411190
}
11421191
TyKind::TraitObject(bounds, ..) => {
11431192
let mut any_lifetime_bounds = false;
@@ -1664,8 +1713,8 @@ impl Visitor<'_> for AstValidator<'_> {
16641713
self.check_extern_fn_signature(
16651714
self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK),
16661715
FnCtxt::Foreign,
1667-
ident,
1668-
sig,
1716+
Some(ident),
1717+
&sig.as_borrowed(),
16691718
);
16701719

16711720
if let Some(attr) = attr::find_by_name(fi.attrs(), sym::track_caller)
@@ -1870,7 +1919,12 @@ impl Visitor<'_> for AstValidator<'_> {
18701919

18711920
if let Some((extern_abi, extern_abi_span)) = ext {
18721921
// Some ABIs impose special restrictions on the signature.
1873-
self.check_extern_fn_signature(extern_abi, ctxt, &fun.ident, &fun.sig);
1922+
self.check_extern_fn_signature(
1923+
extern_abi,
1924+
ctxt,
1925+
Some(&fun.ident),
1926+
&fun.sig.as_borrowed(),
1927+
);
18741928

18751929
// #[track_caller] can only be used with the rust ABI.
18761930
if let Some(attr) = attr::find_by_name(attrs, sym::track_caller)

compiler/rustc_ast_passes/src/diagnostics.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,13 @@ pub(crate) struct InvalidSafetyOnItem {
382382
pub(crate) struct InvalidSafetyOnFnPtr {
383383
#[primary_span]
384384
pub span: Span,
385+
#[suggestion(
386+
"remove the `safe` qualifier",
387+
code = "",
388+
applicability = "machine-applicable",
389+
style = "verbose"
390+
)]
391+
pub safe_span: Span,
385392
}
386393

387394
#[derive(Diagnostic)]
@@ -1115,11 +1122,11 @@ pub(crate) struct AbiMustNotHaveParametersOrReturnType {
11151122
#[suggestion(
11161123
"remove the parameters and return type",
11171124
applicability = "maybe-incorrect",
1118-
code = "{padding}fn {symbol}()",
1125+
code = "{padding}fn{symbol}()",
11191126
style = "verbose"
11201127
)]
11211128
pub suggestion_span: Span,
1122-
pub symbol: Symbol,
1129+
pub symbol: String,
11231130
pub padding: &'static str,
11241131
}
11251132

0 commit comments

Comments
 (0)