Skip to content
15 changes: 6 additions & 9 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,16 @@ hir_analysis_cannot_capture_late_bound_ty =
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
.label = `for<...>` is here

hir_analysis_cmse_call_generic =
function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
hir_analysis_cmse_generic =
generics are not allowed in `extern {$abi}` signatures

hir_analysis_cmse_entry_generic =
functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
hir_analysis_cmse_impl_trait =
`impl Trait` is not allowed in `extern {$abi}` signatures

hir_analysis_cmse_inputs_stack_spill =
arguments for `{$abi}` function too large to pass via registers
.label = {$plural ->
[false] this argument doesn't
*[true] these arguments don't
} fit in the available registers
.note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit available argument registers
.label = does not fit in the available registers
.note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit argument registers

hir_analysis_cmse_output_stack_spill =
return value of `{$abi}` function too large to pass via registers
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,8 +1616,7 @@ pub(crate) struct InvalidGenericReceiverTy<'tcx> {
pub(crate) struct CmseInputsStackSpill {
#[primary_span]
#[label]
pub span: Span,
pub plural: bool,
pub spans: Vec<Span>,
pub abi: ExternAbi,
}

Expand All @@ -1633,22 +1632,24 @@ pub(crate) struct CmseOutputStackSpill {
}

#[derive(Diagnostic)]
#[diag(hir_analysis_cmse_call_generic, code = E0798)]
pub(crate) struct CmseCallGeneric {
#[diag(hir_analysis_cmse_generic, code = E0798)]
pub(crate) struct CmseGeneric {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_bad_return_type_notation_position)]
pub(crate) struct BadReturnTypeNotation {
#[diag(hir_analysis_cmse_impl_trait, code = E0798)]
pub(crate) struct CmseImplTrait {
#[primary_span]
pub span: Span,
pub abi: ExternAbi,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_cmse_entry_generic, code = E0798)]
pub(crate) struct CmseEntryGeneric {
#[diag(hir_analysis_bad_return_type_notation_position)]
pub(crate) struct BadReturnTypeNotation {
#[primary_span]
pub span: Span,
}
Expand Down
138 changes: 50 additions & 88 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_hir::{self as hir, HirId};
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::Span;

use crate::errors;

Expand All @@ -17,15 +18,10 @@ pub(crate) fn validate_cmse_abi<'tcx>(
abi: ExternAbi,
fn_sig: ty::PolyFnSig<'tcx>,
) {
match abi {
ExternAbi::CmseNonSecureCall => {
let hir_node = tcx.hir_node(hir_id);
let hir::Node::Ty(hir::Ty {
span: fn_ptr_span,
kind: hir::TyKind::FnPtr(fn_ptr_ty),
..
}) = hir_node
else {
let fn_decl = match abi {
ExternAbi::CmseNonSecureCall => match tcx.hir_node(hir_id) {
hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), .. }) => fn_ptr_ty.decl,
_ => {
let span = match tcx.parent_hir_node(hir_id) {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::ForeignMod { .. },
Expand All @@ -42,45 +38,10 @@ pub(crate) fn validate_cmse_abi<'tcx>(
)
.emit();
return;
};

match is_valid_cmse_inputs(tcx, fn_sig) {
Ok(Ok(())) => {}
Ok(Err(index)) => {
// fn(x: u32, u32, u32, u16, y: u16) -> u32,
// ^^^^^^
let span = if let Some(ident) = fn_ptr_ty.param_idents[index] {
ident.span.to(fn_ptr_ty.decl.inputs[index].span)
} else {
fn_ptr_ty.decl.inputs[index].span
}
.to(fn_ptr_ty.decl.inputs.last().unwrap().span);
let plural = fn_ptr_ty.param_idents.len() - index != 1;
dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
}
}
}

match is_valid_cmse_output(tcx, fn_sig) {
Ok(true) => {}
Ok(false) => {
let span = fn_ptr_ty.decl.output.span();
dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
}
}
};
}
},
ExternAbi::CmseNonSecureEntry => {
let hir_node = tcx.hir_node(hir_id);
let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
let Some(hir::FnSig { decl, .. }) = tcx.hir_node(hir_id).fn_sig() else {
// might happen when this ABI is used incorrectly. That will be handled elsewhere
return;
};
Expand All @@ -91,53 +52,43 @@ pub(crate) fn validate_cmse_abi<'tcx>(
return;
}

match is_valid_cmse_inputs(tcx, fn_sig) {
Ok(Ok(())) => {}
Ok(Err(index)) => {
// fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
// ^^^^^^
let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
let plural = decl.inputs.len() - index != 1;
dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
}
}
}
decl
}
_ => return,
};

match is_valid_cmse_output(tcx, fn_sig) {
Ok(true) => {}
Ok(false) => {
let span = decl.output.span();
dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
}
Err(layout_err) => {
if should_emit_generic_error(abi, layout_err) {
dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
}
}
};
if let Err((span, layout_err)) = is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_decl, abi) {
if should_emit_layout_error(abi, layout_err) {
dcx.emit_err(errors::CmseGeneric { span, abi });
}
}

if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_decl, abi) {
if should_emit_layout_error(abi, layout_err) {
dcx.emit_err(errors::CmseGeneric { span: fn_decl.output.span(), abi });
}
_ => (),
}
}

/// Returns whether the inputs will fit into the available registers
fn is_valid_cmse_inputs<'tcx>(
tcx: TyCtxt<'tcx>,
dcx: DiagCtxtHandle<'_>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
let mut span = None;
fn_decl: &hir::FnDecl<'tcx>,
abi: ExternAbi,
) -> Result<(), (Span, &'tcx LayoutError<'tcx>)> {
let mut accum = 0u64;
let mut excess_argument_spans = Vec::new();

// this type is only used for layout computation, which does not rely on regions
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);

for (index, ty) in fn_sig.inputs().iter().enumerate() {
let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?;
for (ty, hir_ty) in fn_sig.inputs().iter().zip(fn_decl.inputs) {
let layout = tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))
.map_err(|e| (hir_ty.span, e))?;

let align = layout.layout.align().bytes();
let size = layout.layout.size().bytes();
Expand All @@ -147,21 +98,27 @@ fn is_valid_cmse_inputs<'tcx>(

// i.e. exceeds 4 32-bit registers
if accum > 16 {
span = span.or(Some(index));
excess_argument_spans.push(hir_ty.span);
}
}

match span {
None => Ok(Ok(())),
Some(span) => Ok(Err(span)),
if !excess_argument_spans.is_empty() {
// fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
// ^^^^^^
dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, abi });
}

Ok(())
}

/// Returns whether the output will fit into the available registers
fn is_valid_cmse_output<'tcx>(
tcx: TyCtxt<'tcx>,
dcx: DiagCtxtHandle<'_>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<bool, &'tcx LayoutError<'tcx>> {
fn_decl: &hir::FnDecl<'tcx>,
abi: ExternAbi,
) -> Result<(), &'tcx LayoutError<'tcx>> {
// this type is only used for layout computation, which does not rely on regions
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
Expand All @@ -176,14 +133,19 @@ fn is_valid_cmse_output<'tcx>(
// `#[no_mangle]` or similar, so generics in the type really don't make sense.
//
// see also https://github.com/rust-lang/rust/issues/147242.
if return_type.has_opaque_types() {
return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type)));
if abi == ExternAbi::CmseNonSecureEntry && return_type.has_opaque_types() {
dcx.emit_err(errors::CmseImplTrait { span: fn_decl.output.span(), abi });
return Ok(());
}

let typing_env = ty::TypingEnv::fully_monomorphized();
let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;

Ok(is_valid_cmse_output_layout(layout))
if !is_valid_cmse_output_layout(layout) {
dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi });
}

Ok(())
}

/// Returns whether the output will fit into the available registers
Expand All @@ -208,15 +170,15 @@ fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64))
}

fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
use LayoutError::*;

match layout_err {
TooGeneric(ty) => {
match abi {
ExternAbi::CmseNonSecureCall => {
// prevent double reporting of this error
!ty.is_impl_trait()
!ty.has_opaque_types()
}
ExternAbi::CmseNonSecureEntry => true,
_ => bug!("invalid ABI: {abi}"),
Expand Down
2 changes: 1 addition & 1 deletion tests/assembly-llvm/x86_64-bigint-helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub unsafe extern "sysv64" fn bigint_chain_borrowing_sub(
n: usize,
mut carry: bool,
) -> bool {
// CHECK: mov [[TEMP:r..]], qword ptr [rsi + 8*[[IND:r..]] + 8]
// CHECK: mov [[TEMP:r.+]], qword ptr [rsi + 8*[[IND:r.+]] + 8]
// CHECK: sbb [[TEMP]], qword ptr [rdx + 8*[[IND]] + 8]
// CHECK: mov qword ptr [rdi + 8*[[IND]] + 8], [[TEMP]]
// CHECK: mov [[TEMP]], qword ptr [rsi + 8*[[IND]] + 16]
Expand Down
7 changes: 5 additions & 2 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ struct Test<T: Copy> {
f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy,
//~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters
//~| ERROR `impl Trait` is not allowed in `fn` pointer return types
f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
f4: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32),
//~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters
//~| ERROR `impl Trait` is not allowed in `fn` pointer return types
f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
f5: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
}

type WithReference = extern "cmse-nonsecure-call" fn(&usize);
Expand Down
42 changes: 29 additions & 13 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,36 @@ LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl C
|
= note: `impl Trait` is only allowed in arguments and return types of functions and methods

error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
--> $DIR/generics.rs:20:9
error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters
--> $DIR/generics.rs:20:42
|
LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32),
| ^^^^^^^^^
|
= note: `impl Trait` is only allowed in arguments and return types of functions and methods

error[E0562]: `impl Trait` is not allowed in `fn` pointer return types
--> $DIR/generics.rs:20:78
|
LL | f3: extern "cmse-nonsecure-call" fn((impl Copy, u32), u32, u32, u32) -> (impl Copy, u32),
| ^^^^^^^^^
|
= note: `impl Trait` is only allowed in arguments and return types of functions and methods

error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures
--> $DIR/generics.rs:23:41
|
LL | f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | f4: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
| ^

error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
--> $DIR/generics.rs:21:9
error[E0798]: generics are not allowed in `extern "cmse-nonsecure-call"` signatures
--> $DIR/generics.rs:24:41
|
LL | f4: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | f5: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
| ^^^^^^^^^^

error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/generics.rs:27:71
--> $DIR/generics.rs:30:71
|
LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait;
| ^^^^^^^^^^ this type doesn't fit in the available registers
Expand All @@ -60,7 +76,7 @@ LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size

error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/generics.rs:31:60
--> $DIR/generics.rs:34:60
|
LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait;
| ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
Expand All @@ -69,7 +85,7 @@ LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Tra
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size

error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/generics.rs:38:60
--> $DIR/generics.rs:41:60
|
LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent;
| ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
Expand All @@ -78,12 +94,12 @@ LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspare
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size

error[E0045]: C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported
--> $DIR/generics.rs:41:20
--> $DIR/generics.rs:44:20
|
LL | type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention

error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

Some errors have detailed explanations: E0045, E0412, E0562, E0798.
For more information about an error, try `rustc --explain E0045`.
Loading
Loading