From 50ba821e12f51903aba6902b2b404edbc94011d2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 15:57:00 +0200 Subject: [PATCH 01/16] add rust error message for CMSE stack spill when the `C-cmse-nonsecure-call` ABI is used, arguments and return values must be passed via registers. Failing to do so (i.e. spilling to the stack) causes an LLVM error down the line, but now rustc will properly emit an error a bit earlier in the chain --- compiler/rustc_codegen_ssa/messages.ftl | 12 ++++ compiler/rustc_codegen_ssa/src/errors.rs | 22 ++++++++ compiler/rustc_codegen_ssa/src/mir/block.rs | 56 ++++++++++++++++++- .../src/error_codes/E0798.md | 36 ++++++++++++ compiler/rustc_error_codes/src/lib.rs | 1 + .../cmse-nonsecure-call/params-on-stack.rs | 15 ++--- .../params-on-stack.stderr | 12 +++- 7 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0798.md diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 000fe2e3ce0f5..0aee5e16a537d 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,18 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. +codegen_ssa_cmse_call_inputs_stack_spill = + arguments for `C-cmse-nonsecure-call` function too large to pass via registers + .label = this function uses the `C-cmse-nonsecure-call` ABI + .call = but its arguments don't fit in the available registers + .note = functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +codegen_ssa_cmse_call_output_stack_spill = + return value of `C-cmse-nonsecure-call` function too large to pass via registers + .label = this function uses the `C-cmse-nonsecure-call` ABI + .call = but its return value doesn't fit in the available registers + .note = functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + codegen_ssa_compiler_builtins_cannot_call = `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index e9d31db92541b..13a2dce3e69cb 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1033,3 +1033,25 @@ pub struct CompilerBuiltinsCannotCall { pub caller: String, pub callee: String, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_cmse_call_inputs_stack_spill, code = E0798)] +#[note] +pub struct CmseCallInputsStackSpill { + #[primary_span] + #[label(codegen_ssa_call)] + pub span: Span, + #[label] + pub func_span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_cmse_call_output_stack_spill, code = E0798)] +#[note] +pub struct CmseCallOutputStackSpill { + #[primary_span] + #[label(codegen_ssa_call)] + pub span: Span, + #[label] + pub func_span: Span, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6a5525dc2b34f..8011604d57666 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,7 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; -use crate::errors::CompilerBuiltinsCannotCall; +use crate::errors::{ + CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall, +}; use crate::meth; use crate::traits::*; use crate::MemFlags; @@ -834,6 +836,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(bx, func); + let fn_sig = callee.layout.ty.fn_sig(bx.tcx()).skip_binder(); + + if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi { + let mut accum = 0u64; + + for arg_def in fn_sig.inputs().iter() { + let layout = bx.layout_of(*arg_def); + + let align = layout.layout.align().abi.bytes(); + let size = layout.layout.size().bytes(); + + accum += size; + accum = accum.next_multiple_of(Ord::max(4, align)); + } + + // the available argument space is 16 bytes (4 32-bit registers) in total + let available_space = 16; + + if accum > available_space { + let err = CmseCallInputsStackSpill { span, func_span: func.span(self.mir) }; + bx.tcx().dcx().emit_err(err); + } + + let mut ret_layout = bx.layout_of(fn_sig.output()); + + // unwrap any `repr(transparent)` wrappers + loop { + if ret_layout.is_transparent::() { + match ret_layout.non_1zst_field(bx) { + None => break, + Some((_, layout)) => ret_layout = layout, + } + } else { + break; + } + } + + let valid_2register_return_types = + [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64]; + + // A Composite Type larger than 4 bytes is stored in memory at an address + // passed as an extra argument when the function was called. That is not allowed + // for cmse_nonsecure_entry functions. + let is_valid_output = ret_layout.layout.size().bytes() <= 4 + || valid_2register_return_types.contains(&ret_layout.ty); + + if !is_valid_output { + let err = CmseCallOutputStackSpill { span, func_span: func.span(self.mir) }; + bx.tcx().dcx().emit_err(err); + } + } + let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, args) => ( Some( diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md new file mode 100644 index 0000000000000..79ed041004e97 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -0,0 +1,36 @@ +Functions marked as `C-cmse-nonsecure-call` place restrictions on their +inputs and outputs. + +- inputs must fit in the 4 available 32-bit argument registers. Alignment +is relevant. +- outputs must either fit in 4 bytes, or be a foundational type of +size 8 (`i64`, `u64`, `f64`). + +For more information, +see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). + +Erroneous code example: + +```compile_fail,E0798 +#![feature(abi_c_cmse_nonsecure_call)] + +fn test( + f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, +) -> u32 { + f(1, 2, 3, 4, 5) +} +``` + +Arguments' alignment is respected. In the example below, padding is inserted +so that the `u64` argument is passed in registers r2 and r3. There is then no +room left for the final `f32` argument + +```compile_fail,E0798 +#![feature(abi_c_cmse_nonsecure_call)] + +fn test( + f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, +) -> u32 { + f(1, 2, 3.0) +} +``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index d13d5e1bca219..2a7bc2501c081 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -536,6 +536,7 @@ E0794: 0794, E0795: 0795, E0796: 0796, E0797: 0797, +E0798: 0798, ); ) } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs index c225a26c065d6..f13e81d006086 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs @@ -3,10 +3,10 @@ //@ needs-llvm-components: arm #![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] #![no_core] -#[lang="sized"] -pub trait Sized { } -#[lang="copy"] -pub trait Copy { } +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} impl Copy for u32 {} extern "rust-intrinsic" { @@ -16,12 +16,9 @@ extern "rust-intrinsic" { #[no_mangle] pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { let non_secure_function = unsafe { - transmute::< - usize, - extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32> - ( + transmute:: u32>( 0x10000004, ) }; - non_secure_function(a, b, c, d, e) + non_secure_function(a, b, c, d, e) //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr index a8aced2483e8f..ee4effa56d4a0 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr @@ -1,4 +1,14 @@ -error: :0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-on-stack.rs:23:5 + | +LL | let non_secure_function = unsafe { + | ------------------- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | non_secure_function(a, b, c, d, e) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0798`. From 1e8606408d14e2509f59c0a87c02788cb112b55a Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 16:35:23 +0200 Subject: [PATCH 02/16] add more tests for `cmse-nonsecure-call` stack spills --- .../params-on-registers.rs | 24 ----- .../cmse-nonsecure-call/params-on-stack.rs | 24 ----- .../params-on-stack.stderr | 14 --- .../cmse-nonsecure-call/params-via-stack.rs | 29 ++++++ .../params-via-stack.stderr | 58 +++++++++++ .../cmse-nonsecure-call/return-via-stack.rs | 41 ++++++++ .../return-via-stack.stderr | 97 +++++++++++++++++++ .../cmse-nonsecure-call/via-registers.rs | 55 +++++++++++ 8 files changed, 280 insertions(+), 62 deletions(-) delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs delete mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs deleted file mode 100644 index 364d0858afb96..0000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ build-pass -//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -//@ needs-llvm-components: arm -#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] -#![no_core] -#[lang="sized"] -pub trait Sized { } -#[lang="copy"] -pub trait Copy { } -impl Copy for u32 {} - -extern "rust-intrinsic" { - pub fn transmute(e: T) -> U; -} - -#[no_mangle] -pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 { - let non_secure_function = unsafe { - transmute:: u32>( - 0x10000004, - ) - }; - non_secure_function(a, b, c, d) -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs deleted file mode 100644 index f13e81d006086..0000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ build-fail -//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib -//@ needs-llvm-components: arm -#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] -#![no_core] -#[lang = "sized"] -pub trait Sized {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for u32 {} - -extern "rust-intrinsic" { - pub fn transmute(e: T) -> U; -} - -#[no_mangle] -pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { - let non_secure_function = unsafe { - transmute:: u32>( - 0x10000004, - ) - }; - non_secure_function(a, b, c, d, e) //~ ERROR [E0798] -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr deleted file mode 100644 index ee4effa56d4a0..0000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-on-stack.rs:23:5 - | -LL | let non_secure_function = unsafe { - | ------------------- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | non_secure_function(a, b, c, d, e) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers - | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs new file mode 100644 index 0000000000000..a4cc74f716f19 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -0,0 +1,29 @@ +//@ build-fail +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} + +#[repr(C, align(16))] +#[allow(unused)] +pub struct AlignRelevant(u32); + +#[no_mangle] +pub fn test( + f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32, + f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32, + f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32, + f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32, +) { + f1(1, 2, 3, 4, 5); //~ ERROR [E0798] + f2(1, 2, 3, 4, 5); //~ ERROR [E0798] + f3(1, 2, 3); //~ ERROR [E0798] + f4(AlignRelevant(1), 2); //~ ERROR [E0798] + f5([0xAA; 5]); //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr new file mode 100644 index 0000000000000..b161c90d01aca --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -0,0 +1,58 @@ +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-via-stack.rs:24:5 + | +LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f1(1, 2, 3, 4, 5); + | ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-via-stack.rs:25:5 + | +LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f2(1, 2, 3, 4, 5); + | ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-via-stack.rs:26:5 + | +LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f3(1, 2, 3); + | ^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-via-stack.rs:27:5 + | +LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f4(AlignRelevant(1), 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/params-via-stack.rs:28:5 + | +LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f5([0xAA; 5]); + | ^^^^^^^^^^^^^ but its arguments don't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs new file mode 100644 index 0000000000000..c417dddac4ffb --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -0,0 +1,41 @@ +//@ build-fail +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} + +#[repr(C)] +pub struct ReprCU64(u64); + +#[repr(C)] +pub struct ReprCBytes(u8, u8, u8, u8, u8); + +#[repr(C)] +pub struct U64Compound(u32, u32); + +#[repr(C, align(16))] +pub struct ReprCAlign16(u16); + +#[no_mangle] +pub fn test( + f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, + f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, + f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, + f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], + f6: extern "C-cmse-nonsecure-call" fn() -> u128, //~ WARNING [improper_ctypes_definitions] + f7: extern "C-cmse-nonsecure-call" fn() -> i128, //~ WARNING [improper_ctypes_definitions] +) { + f1(); //~ ERROR [E0798] + f2(); //~ ERROR [E0798] + f3(); //~ ERROR [E0798] + f4(); //~ ERROR [E0798] + f5(); //~ ERROR [E0798] + f6(); //~ ERROR [E0798] + f7(); //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr new file mode 100644 index 0000000000000..6dea0cf68a0df --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -0,0 +1,97 @@ +warning: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/return-via-stack.rs:31:9 + | +LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/return-via-stack.rs:32:9 + | +LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:34:5 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f1(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:35:5 + | +LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f2(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:36:5 + | +LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f3(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:37:5 + | +LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f4(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:38:5 + | +LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f5(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:39:5 + | +LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f6(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:40:5 + | +LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f7(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error: aborting due to 7 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs new file mode 100644 index 0000000000000..72b405ef2820e --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -0,0 +1,55 @@ +//@ build-pass +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} + +#[repr(transparent)] +pub struct ReprTransparentU64(u64); + +#[repr(C)] +pub struct U32Compound(u16, u16); + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub fn params( + f1: extern "C-cmse-nonsecure-call" fn(), + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32), + f3: extern "C-cmse-nonsecure-call" fn(u64, u64), + f4: extern "C-cmse-nonsecure-call" fn(u128), + f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentU64, U32Compound), + f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), +) { + f1(); + f2(1, 2, 3, 4); + f3(1, 2); + f4(1); + f5(1.0, 2.0, 3.0); + f6(ReprTransparentU64(1), U32Compound(2, 3)); + f7([0xDEADBEEF; 4]); +} + +#[no_mangle] +pub fn returns( + f1: extern "C-cmse-nonsecure-call" fn() -> u32, + f2: extern "C-cmse-nonsecure-call" fn() -> u64, + f3: extern "C-cmse-nonsecure-call" fn() -> i64, + f4: extern "C-cmse-nonsecure-call" fn() -> f64, + f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentU64, + f7: extern "C-cmse-nonsecure-call" fn() -> U32Compound, +) { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + f7(); +} From 36d23713fb524589be409392472d78b4b3e20da6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 18:38:42 +0200 Subject: [PATCH 03/16] make function pub in error_codes markdown file the error is only generated for functions that are actually codegen'd --- compiler/rustc_error_codes/src/error_codes/E0798.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 79ed041004e97..f63796a67f198 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -14,7 +14,7 @@ Erroneous code example: ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] -fn test( +pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, ) -> u32 { f(1, 2, 3, 4, 5) @@ -28,7 +28,7 @@ room left for the final `f32` argument ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] -fn test( +pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, ) -> u32 { f(1, 2, 3.0) From c7ff46c971ea7c17b67b2b619fd094c4df981863 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 18:39:30 +0200 Subject: [PATCH 04/16] move cmse ABI validation into its own module Co-authored-by: Tamme Dittrich --- compiler/rustc_codegen_ssa/src/errors.rs | 8 +-- compiler/rustc_codegen_ssa/src/mir/block.rs | 60 ++--------------- compiler/rustc_codegen_ssa/src/mir/cmse.rs | 72 +++++++++++++++++++++ compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 + 4 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/mir/cmse.rs diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 13a2dce3e69cb..af7835633cfd1 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1040,9 +1040,9 @@ pub struct CompilerBuiltinsCannotCall { pub struct CmseCallInputsStackSpill { #[primary_span] #[label(codegen_ssa_call)] - pub span: Span, + pub call_site_span: Span, #[label] - pub func_span: Span, + pub function_definition_span: Span, } #[derive(Diagnostic)] @@ -1051,7 +1051,7 @@ pub struct CmseCallInputsStackSpill { pub struct CmseCallOutputStackSpill { #[primary_span] #[label(codegen_ssa_call)] - pub span: Span, + pub call_site_span: Span, #[label] - pub func_span: Span, + pub function_definition_span: Span, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8011604d57666..24d3b8ad264b7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,10 +5,9 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; -use crate::errors::{ - CmseCallInputsStackSpill, CmseCallOutputStackSpill, CompilerBuiltinsCannotCall, -}; +use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; +use crate::mir::cmse; use crate::traits::*; use crate::MemFlags; @@ -836,58 +835,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(bx, func); - let fn_sig = callee.layout.ty.fn_sig(bx.tcx()).skip_binder(); - - if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi { - let mut accum = 0u64; - - for arg_def in fn_sig.inputs().iter() { - let layout = bx.layout_of(*arg_def); - - let align = layout.layout.align().abi.bytes(); - let size = layout.layout.size().bytes(); - - accum += size; - accum = accum.next_multiple_of(Ord::max(4, align)); - } - - // the available argument space is 16 bytes (4 32-bit registers) in total - let available_space = 16; - - if accum > available_space { - let err = CmseCallInputsStackSpill { span, func_span: func.span(self.mir) }; - bx.tcx().dcx().emit_err(err); - } - - let mut ret_layout = bx.layout_of(fn_sig.output()); - - // unwrap any `repr(transparent)` wrappers - loop { - if ret_layout.is_transparent::() { - match ret_layout.non_1zst_field(bx) { - None => break, - Some((_, layout)) => ret_layout = layout, - } - } else { - break; - } - } - - let valid_2register_return_types = - [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64]; - - // A Composite Type larger than 4 bytes is stored in memory at an address - // passed as an extra argument when the function was called. That is not allowed - // for cmse_nonsecure_entry functions. - let is_valid_output = ret_layout.layout.size().bytes() <= 4 - || valid_2register_return_types.contains(&ret_layout.ty); - - if !is_valid_output { - let err = CmseCallOutputStackSpill { span, func_span: func.span(self.mir) }; - bx.tcx().dcx().emit_err(err); - } - } - let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, args) => ( Some( @@ -923,6 +870,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let sig = callee.layout.ty.fn_sig(bx.tcx()); let abi = sig.abi(); + // emit errors if cmse ABI conditions are violated + cmse::validate_cmse_abi(bx, &sig.skip_binder(), span, func.span(self.mir)); + let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| { let op_ty = op_arg.node.ty(self.mir, bx.tcx()); diff --git a/compiler/rustc_codegen_ssa/src/mir/cmse.rs b/compiler/rustc_codegen_ssa/src/mir/cmse.rs new file mode 100644 index 0000000000000..d3583d79c00d2 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/cmse.rs @@ -0,0 +1,72 @@ +use rustc_middle::ty::FnSig; +use rustc_span::Span; + +use crate::errors::{CmseCallInputsStackSpill, CmseCallOutputStackSpill}; +use crate::traits::BuilderMethods; + +/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be +/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these +/// conditions, but by checking them here rustc can emit nicer error messages. +pub fn validate_cmse_abi<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &Bx, + fn_sig: &FnSig<'tcx>, + call_site_span: Span, + function_definition_span: Span, +) { + if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi { + if !has_valid_inputs(bx, fn_sig) { + let err = CmseCallInputsStackSpill { call_site_span, function_definition_span }; + bx.tcx().dcx().emit_err(err); + } + + if !has_valid_output(bx, fn_sig) { + let err = CmseCallOutputStackSpill { call_site_span, function_definition_span }; + bx.tcx().dcx().emit_err(err); + } + } +} + +/// Returns whether the inputs will fit into the available registers +fn has_valid_inputs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool { + let mut accum = 0u64; + + for arg_def in fn_sig.inputs().iter() { + let layout = bx.layout_of(*arg_def); + + let align = layout.layout.align().abi.bytes(); + let size = layout.layout.size().bytes(); + + accum += size; + accum = accum.next_multiple_of(Ord::max(4, align)); + } + + // the available argument space is 16 bytes (4 32-bit registers) in total + let available_space = 16; + + accum <= available_space +} + +/// Returns whether the output will fit into the available registers +fn has_valid_output<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool { + let mut ret_layout = bx.layout_of(fn_sig.output()); + + // unwrap any `repr(transparent)` wrappers + loop { + if ret_layout.is_transparent::() { + match ret_layout.non_1zst_field(bx) { + None => break, + Some((_, layout)) => ret_layout = layout, + } + } else { + break; + } + } + + // Fundamental types of size 8 can be passed via registers according to the ABI + let valid_2register_return_types = [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64]; + + // A Composite Type larger than 4 bytes is stored in memory at an address + // passed as an extra argument when the function was called. That is not allowed + // for cmse_nonsecure_entry functions. + ret_layout.layout.size().bytes() <= 4 || valid_2register_return_types.contains(&ret_layout.ty) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 61f57c9030a1a..ec28def8b83ec 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -16,6 +16,7 @@ use std::iter; mod analyze; mod block; +mod cmse; pub mod constant; pub mod coverageinfo; pub mod debuginfo; From 1a7960603f60239a5b91c27b3d63e2b87eb09512 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 20:36:55 +0200 Subject: [PATCH 05/16] another attempt at fixing the examples in the error codes .md --- compiler/rustc_error_codes/src/error_codes/E0798.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index f63796a67f198..012c13b96ed4b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -14,6 +14,7 @@ Erroneous code example: ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] +#[no_mangle] pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, ) -> u32 { @@ -28,6 +29,7 @@ room left for the final `f32` argument ```compile_fail,E0798 #![feature(abi_c_cmse_nonsecure_call)] +#[no_mangle] pub fn test( f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, ) -> u32 { From 09b620d179713cceb343dc172d7e5633bfd7a824 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Jul 2024 21:26:21 +0200 Subject: [PATCH 06/16] stop running code samples in the error code .md --- compiler/rustc_error_codes/src/error_codes/E0798.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 012c13b96ed4b..89e6f4d571800 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -11,7 +11,7 @@ see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). Erroneous code example: -```compile_fail,E0798 +```ignore (only fails on supported targets) #![feature(abi_c_cmse_nonsecure_call)] #[no_mangle] @@ -26,7 +26,7 @@ Arguments' alignment is respected. In the example below, padding is inserted so that the `u64` argument is passed in registers r2 and r3. There is then no room left for the final `f32` argument -```compile_fail,E0798 +```ignore (only fails on supported targets) #![feature(abi_c_cmse_nonsecure_call)] #[no_mangle] From 5f0f690bd6d0fbe98e82b5b98036ffe08dff4fbe Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 15:41:16 +0200 Subject: [PATCH 07/16] add test for repr(transparent) enum --- .../cmse-nonsecure-call/via-registers.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 72b405ef2820e..43768dd72f70a 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,7 +10,12 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentU64(u64); +pub struct ReprTransparentStructU64(u64); + +#[repr(transparent)] +pub enum ReprTransparentEnumU64 { + A(u64), +} #[repr(C)] pub struct U32Compound(u16, u16); @@ -23,7 +28,7 @@ pub fn params( f3: extern "C-cmse-nonsecure-call" fn(u64, u64), f4: extern "C-cmse-nonsecure-call" fn(u128), f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), - f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentU64, U32Compound), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStructU64, U32Compound), f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), ) { f1(); @@ -31,7 +36,7 @@ pub fn params( f3(1, 2); f4(1); f5(1.0, 2.0, 3.0); - f6(ReprTransparentU64(1), U32Compound(2, 3)); + f6(ReprTransparentStructU64(1), U32Compound(2, 3)); f7([0xDEADBEEF; 4]); } @@ -42,8 +47,9 @@ pub fn returns( f3: extern "C-cmse-nonsecure-call" fn() -> i64, f4: extern "C-cmse-nonsecure-call" fn() -> f64, f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], - f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentU64, - f7: extern "C-cmse-nonsecure-call" fn() -> U32Compound, + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStructU64, + f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, + f8: extern "C-cmse-nonsecure-call" fn() -> U32Compound, ) { f1(); f2(); @@ -52,4 +58,5 @@ pub fn returns( f5(); f6(); f7(); + f8(); } From 8a3dd7fb5f9e2c3b6c8411e59ed7fa8d674c5a91 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 17:01:59 +0200 Subject: [PATCH 08/16] add test for unions and repr(transparent) with ZST fields --- .../cmse-nonsecure-call/return-via-stack.rs | 19 ++++++++++ .../return-via-stack.stderr | 38 ++++++++++++++++++- .../cmse-nonsecure-call/via-registers.rs | 12 +++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index c417dddac4ffb..2ea6932739201 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -39,3 +39,22 @@ pub fn test( f6(); //~ ERROR [E0798] f7(); //~ ERROR [E0798] } + +#[repr(C)] +pub union ReprCUnionU64 { + _unused: u64, +} + +#[repr(Rust)] +pub union ReprRustUnionU64 { + _unused: u64, +} + +#[no_mangle] +pub fn test_union( + f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ WARNING [improper_ctypes_definitions] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, +) { + f1(); //~ ERROR [E0798] + f2(); //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index 6dea0cf68a0df..99dc7ad4a9fe2 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -15,6 +15,20 @@ LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, | = note: 128-bit integers don't currently have a known stable ABI +warning: `extern` fn uses type `ReprRustUnionU64`, which is not FFI-safe + --> $DIR/return-via-stack.rs:55:9 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union + = note: this union has unspecified layout +note: the type is defined here + --> $DIR/return-via-stack.rs:49:1 + | +LL | pub union ReprRustUnionU64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers --> $DIR/return-via-stack.rs:34:5 | @@ -92,6 +106,28 @@ LL | f7(); | = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers -error: aborting due to 7 previous errors; 2 warnings emitted +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:58:5 + | +LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f1(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers + --> $DIR/return-via-stack.rs:59:5 + | +LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, + | -- this function uses the `C-cmse-nonsecure-call` ABI +... +LL | f2(); + | ^^^^ but its return value doesn't fit in the available registers + | + = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + +error: aborting due to 9 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 43768dd72f70a..91aadd4fe1d7e 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,7 +10,12 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentStructU64(u64); +pub struct ReprTransparentStructU64 { + _marker1: (), + _marker2: (), + field: u64, + _marker3: (), +} #[repr(transparent)] pub enum ReprTransparentEnumU64 { @@ -36,7 +41,10 @@ pub fn params( f3(1, 2); f4(1); f5(1.0, 2.0, 3.0); - f6(ReprTransparentStructU64(1), U32Compound(2, 3)); + f6( + ReprTransparentStructU64 { _marker1: (), _marker2: (), field: 1, _marker3: () }, + U32Compound(2, 3), + ); f7([0xDEADBEEF; 4]); } From 2abdc4e98c5e37385b2791769fd60f4ba65e9699 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Wed, 3 Jul 2024 18:00:42 +0000 Subject: [PATCH 09/16] CFI: Support provided methods on traits Provided methods currently don't get type erasure performed on them because they are not in an `impl` block. If we are instantiating a method that is an associated item, but *not* in an impl block, treat it as a provided method instead. --- .../cfi/typeid/itanium_cxx_abi/transform.rs | 95 +++++++++++-------- tests/ui/sanitizer/cfi-supertraits.rs | 6 ++ 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index f0f2d1fefd2f3..9b05576d721c5 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -9,10 +9,10 @@ use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{ - self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, UintTy, + self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty, + TyCtxt, TypeFoldable, TypeVisitableExt, UintTy, }; -use rustc_span::sym; +use rustc_span::{def_id::DefId, sym}; use rustc_trait_selection::traits; use std::iter; use tracing::{debug, instrument}; @@ -360,41 +360,29 @@ pub fn transform_instance<'tcx>( if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) { // Perform type erasure for calls on trait objects by transforming self into a trait object // of the trait that defines the method. - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) - && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) - { - let impl_method = tcx.associated_item(instance.def_id()); - let method_id = impl_method - .trait_item_def_id - .expect("Part of a trait implementation, but not linked to the def_id?"); - let trait_method = tcx.associated_item(method_id); - let trait_id = trait_ref.skip_binder().def_id; - if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.is_object_safe(trait_id) - { - // Trait methods will have a Self polymorphic parameter, where the concreteized - // implementatation will not. We need to walk back to the more general trait method - let trait_ref = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::ParamEnv::reveal_all(), - trait_ref, - ); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - // At the call site, any call to this concrete function through a vtable will be - // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the - // original method id, and we've recovered the trait arguments, we can make the callee - // instance we're computing the alias set for match the caller instance. - // - // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. - // If we ever *do* start encoding the vtable index, we will need to generate an alias set - // based on which vtables we are putting this method into, as there will be more than one - // index value when supertraits are involved. - instance.def = ty::InstanceKind::Virtual(method_id, 0); - let abstract_trait_args = - tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); - } + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceKind::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, ancestor, abstract_trait_args); } else if tcx.is_closure_like(instance.def_id()) { // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, // instantiate it, and take the type of its only method as our own. @@ -452,3 +440,36 @@ pub fn transform_instance<'tcx>( instance } + +fn implemented_method<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> { + let trait_ref; + let method_id; + let trait_id; + let trait_method; + let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) { + // Implementation in an `impl` block + trait_ref = tcx.impl_trait_ref(impl_id)?; + let impl_method = tcx.associated_item(instance.def_id()); + method_id = impl_method.trait_item_def_id?; + trait_method = tcx.associated_item(method_id); + trait_id = trait_ref.skip_binder().def_id; + impl_id + } else if let InstanceKind::Item(def_id) = instance.def + && let Some(trait_method_bound) = tcx.opt_associated_item(def_id) + { + // Provided method in a `trait` block + trait_method = trait_method_bound; + method_id = instance.def_id(); + trait_id = tcx.trait_of_item(method_id)?; + trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args)); + trait_id + } else { + return None; + }; + let vtable_possible = + traits::is_vtable_safe_method(tcx, trait_id, trait_method) && tcx.is_object_safe(trait_id); + vtable_possible.then_some((trait_ref, method_id, ancestor)) +} diff --git a/tests/ui/sanitizer/cfi-supertraits.rs b/tests/ui/sanitizer/cfi-supertraits.rs index ed3d722ebb78e..4bb6177577f7c 100644 --- a/tests/ui/sanitizer/cfi-supertraits.rs +++ b/tests/ui/sanitizer/cfi-supertraits.rs @@ -16,6 +16,9 @@ trait Parent1 { type P1; fn p1(&self) -> Self::P1; + fn d(&self) -> i32 { + 42 + } } trait Parent2 { @@ -60,14 +63,17 @@ fn main() { x.c(); x.p1(); x.p2(); + x.d(); // Parents can be created and access their methods. let y = &Foo as &dyn Parent1; y.p1(); + y.d(); let z = &Foo as &dyn Parent2; z.p2(); // Trait upcasting works let x1 = x as &dyn Parent1; x1.p1(); + x1.d(); let x2 = x as &dyn Parent2; x2.p2(); } From 7b6373496107807327094ec990d317b98e34fd63 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Jul 2024 16:47:03 +0200 Subject: [PATCH 10/16] move CMSE validation to hir_analysis --- .../src/error_codes/E0798.md | 1 + compiler/rustc_hir_analysis/messages.ftl | 13 ++ .../rustc_hir_analysis/src/check/check.rs | 7 +- compiler/rustc_hir_analysis/src/errors.rs | 25 +++ .../src/hir_ty_lowering/cmse.rs | 152 ++++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 10 +- .../cmse-nonsecure-call/generics.rs | 21 +++ .../cmse-nonsecure-call/generics.stderr | 47 ++++++ .../cmse-nonsecure-call/params-via-stack.rs | 18 +-- .../params-via-stack.stderr | 65 +++----- .../cmse-nonsecure-call/return-via-stack.rs | 34 ++-- .../return-via-stack.stderr | 136 +++++----------- .../cmse-nonsecure-call/via-registers.rs | 31 +--- 13 files changed, 363 insertions(+), 197 deletions(-) create mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md index 89e6f4d571800..da08cde301000 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0798.md +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -5,6 +5,7 @@ inputs and outputs. is relevant. - outputs must either fit in 4 bytes, or be a foundational type of size 8 (`i64`, `u64`, `f64`). +- no generics can be used in the signature For more information, see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 24c5377a3b125..8361804113390 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -58,6 +58,19 @@ 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 `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type + +hir_analysis_cmse_call_inputs_stack_spill = + arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = these arguments don't fit in the available registers + .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + +hir_analysis_cmse_call_output_stack_spill = + return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = this type doesn't fit in the available registers + .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index bf8ef18c04fcc..aa302e4c25f4e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1287,9 +1287,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, trivial, check_non_exhaustive(tcx, ty).break_value()) }); - let non_trivial_fields = field_infos - .clone() - .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); + let non_trivial_fields = + field_infos.clone().filter_map( + |(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }, + ); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0ee87a13e9e37..f6783364f1637 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1682,3 +1682,28 @@ pub struct InvalidReceiverTy<'tcx> { #[note] #[help] pub struct EffectsWithoutNextSolver; + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)] +#[note] +pub struct CmseCallInputsStackSpill { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)] +#[note] +pub struct CmseCallOutputStackSpill { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_generic, code = E0798)] +pub struct CmseCallGeneric { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs new file mode 100644 index 0000000000000..8980173f73817 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -0,0 +1,152 @@ +use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; +use rustc_hir::HirId; +use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::{self, ParamEnv, TyCtxt}; +use rustc_span::Span; +use rustc_target::spec::abi; + +use crate::errors; + +/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be +/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these +/// conditions, but by checking them here rustc can emit nicer error messages. +pub fn validate_cmse_abi<'tcx>( + tcx: TyCtxt<'tcx>, + dcx: &DiagCtxtHandle<'_>, + hir_id: HirId, + abi: abi::Abi, + fn_sig: ty::PolyFnSig<'tcx>, +) { + if let abi::Abi::CCmseNonSecureCall = abi { + let hir_node = tcx.hir_node(hir_id); + let hir::Node::Ty(hir::Ty { + span: bare_fn_span, + kind: hir::TyKind::BareFn(bare_fn_ty), + .. + }) = hir_node + else { + // might happen when this ABI is used incorrectly. That will be handled elsewhere + return; + }; + + // fn(u32, u32, u32, u16, u16) -> u32, + // ^^^^^^^^^^^^^^^^^^^^^^^ ^^^ + let output_span = bare_fn_ty.decl.output.span(); + let inputs_span = match ( + bare_fn_ty.param_names.first(), + bare_fn_ty.decl.inputs.first(), + bare_fn_ty.decl.inputs.last(), + ) { + (Some(ident), Some(ty1), Some(ty2)) => ident.span.to(ty1.span).to(ty2.span), + _ => *bare_fn_span, + }; + + match is_valid_cmse_inputs(tcx, fn_sig) { + Ok(true) => {} + Ok(false) => { + dcx.emit_err(errors::CmseCallInputsStackSpill { span: inputs_span }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, inputs_span) { + dcx.emit_err(err); + } + } + } + + match is_valid_cmse_output(tcx, fn_sig) { + Ok(true) => {} + Ok(false) => { + dcx.emit_err(errors::CmseCallOutputStackSpill { span: output_span }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, output_span) { + dcx.emit_err(err); + } + } + }; + } +} + +/// Returns whether the inputs will fit into the available registers +fn is_valid_cmse_inputs<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, +) -> Result> { + let mut accum = 0u64; + + for arg_def in fn_sig.inputs().iter() { + let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?; + + let align = layout.layout.align().abi.bytes(); + let size = layout.layout.size().bytes(); + + accum += size; + accum = accum.next_multiple_of(Ord::max(4, align)); + } + + // i.e. 4 32-bit registers + Ok(accum <= 16) +} + +/// Returns whether the output will fit into the available registers +fn is_valid_cmse_output<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, +) -> Result> { + let mut ret_ty = fn_sig.output().skip_binder(); + let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?; + let size = layout.layout.size().bytes(); + + if size <= 4 { + return Ok(true); + } else if size > 8 { + return Ok(false); + } + + // next we need to peel any repr(transparent) layers off + 'outer: loop { + let ty::Adt(adt_def, args) = ret_ty.kind() else { + break; + }; + + if !adt_def.repr().transparent() { + break; + } + + // the first field with non-trivial size and alignment must be the data + for variant_def in adt_def.variants() { + for field_def in variant_def.fields.iter() { + let ty = field_def.ty(tcx, args); + let layout = tcx.layout_of(ParamEnv::reveal_all().and(ty))?; + + if !layout.layout.is_1zst() { + ret_ty = ty; + continue 'outer; + } + } + } + } + + Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64) +} + +fn cmse_layout_err<'tcx>( + layout_err: &'tcx LayoutError<'tcx>, + span: Span, +) -> Option { + use LayoutError::*; + + match layout_err { + Unknown(ty) => { + if ty.is_impl_trait() { + None // prevent double reporting of this error + } else { + Some(errors::CmseCallGeneric { span }) + } + } + SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { + None // not our job to report these + } + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a665306f2c6a8..5a98e4d79bb08 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -14,6 +14,7 @@ //! trait references and bounds. mod bounds; +mod cmse; pub mod errors; pub mod generics; mod lint; @@ -1748,7 +1749,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !indices.contains(&index) { Some(seg) } else { None } + if !indices.contains(&index) { + Some(seg) + } else { + None + } }), GenericsArgsErrExtend::DefVariant, ); @@ -2324,6 +2329,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + // reject function types that violate cmse ABI requirements + cmse::validate_cmse_abi(self.tcx(), &self.dcx(), hir_id, abi, bare_fn_ty); + // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. // diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs new file mode 100644 index 0000000000000..e6b0bf3e6860b --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -0,0 +1,21 @@ +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +impl Copy for u32 {} + +#[repr(C)] +struct Wrapper(T); + +struct Test { + f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, //~ ERROR cannot find type `U` in this scope + //~^ ERROR function pointer types may not have generic parameters + f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, + //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters + f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, //~ ERROR [E0798] +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr new file mode 100644 index 0000000000000..b255d26193856 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -0,0 +1,47 @@ +error: function pointer types may not have generic parameters + --> $DIR/generics.rs:15:42 + | +LL | f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, + | ^^^^^^^^^ + +error[E0412]: cannot find type `U` in this scope + --> $DIR/generics.rs:15:52 + | +LL | struct Test { + | - similarly named type parameter `T` defined here +LL | f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, + | ^ + | +help: a type parameter with a similar name exists + | +LL | f1: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, + | ~ +help: you might be missing a type parameter + | +LL | struct Test { + | +++ + +error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters + --> $DIR/generics.rs:17:43 + | +LL | f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + +error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type + --> $DIR/generics.rs:19:43 + | +LL | f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, + | ^^^^^^^^^^^^^^^^ + +error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type + --> $DIR/generics.rs:20:43 + | +LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0412, E0562, E0798. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs index a4cc74f716f19..f8ef441992100 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -1,7 +1,6 @@ -//@ build-fail //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm -#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} @@ -15,15 +14,10 @@ pub struct AlignRelevant(u32); #[no_mangle] pub fn test( - f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, - f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32, - f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32, - f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32, - f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32, + f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] ) { - f1(1, 2, 3, 4, 5); //~ ERROR [E0798] - f2(1, 2, 3, 4, 5); //~ ERROR [E0798] - f3(1, 2, 3); //~ ERROR [E0798] - f4(AlignRelevant(1), 2); //~ ERROR [E0798] - f5([0xAA; 5]); //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index b161c90d01aca..499122ab2c547 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -1,57 +1,42 @@ -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:24:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:17:43 | -LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f1(1, 2, 3, 4, 5); - | ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers +LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), + | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:25:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:18:43 | -LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16) -> u32, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f2(1, 2, 3, 4, 5); - | ^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers +LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), + | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:26:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:19:43 | -LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32) -> u32, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f3(1, 2, 3); - | ^^^^^^^^^^^ but its arguments don't fit in the available registers +LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), + | ^^^^^^^^^^^^^ these arguments don't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:27:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:20:43 | -LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32) -> u32, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f4(AlignRelevant(1), 2); - | ^^^^^^^^^^^^^^^^^^^^^^^ but its arguments don't fit in the available registers +LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), + | ^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers -error[E0798]: arguments for `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/params-via-stack.rs:28:5 +error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/params-via-stack.rs:21:43 | -LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]) -> u32, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f5([0xAA; 5]); - | ^^^^^^^^^^^^^ but its arguments don't fit in the available registers +LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), + | ^^^^^^^^ these arguments don't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers error: aborting due to 5 previous errors diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index 2ea6932739201..e6af1d60e773f 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -1,7 +1,6 @@ -//@ build-fail //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm -#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)] +#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} @@ -23,21 +22,18 @@ pub struct ReprCAlign16(u16); #[no_mangle] pub fn test( - f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, - f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, - f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, - f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, - f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], - f6: extern "C-cmse-nonsecure-call" fn() -> u128, //~ WARNING [improper_ctypes_definitions] - f7: extern "C-cmse-nonsecure-call" fn() -> i128, //~ WARNING [improper_ctypes_definitions] + f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], //~ ERROR [E0798] ) { - f1(); //~ ERROR [E0798] - f2(); //~ ERROR [E0798] - f3(); //~ ERROR [E0798] - f4(); //~ ERROR [E0798] - f5(); //~ ERROR [E0798] - f6(); //~ ERROR [E0798] - f7(); //~ ERROR [E0798] +} + +#[allow(improper_ctypes_definitions)] +struct Test { + u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798] + i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798] } #[repr(C)] @@ -52,9 +48,7 @@ pub union ReprRustUnionU64 { #[no_mangle] pub fn test_union( - f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ WARNING [improper_ctypes_definitions] - f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, + f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, //~ ERROR [E0798] ) { - f1(); //~ ERROR [E0798] - f2(); //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index 99dc7ad4a9fe2..c8984e3c0d902 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -1,133 +1,75 @@ -warning: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/return-via-stack.rs:31:9 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:35:50 | -LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | u128: extern "C-cmse-nonsecure-call" fn() -> u128, + | ^^^^ this type doesn't fit in the available registers | - = note: 128-bit integers don't currently have a known stable ABI - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -warning: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/return-via-stack.rs:32:9 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:36:50 | -LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | i128: extern "C-cmse-nonsecure-call" fn() -> i128, + | ^^^^ this type doesn't fit in the available registers | - = note: 128-bit integers don't currently have a known stable ABI + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -warning: `extern` fn uses type `ReprRustUnionU64`, which is not FFI-safe - --> $DIR/return-via-stack.rs:55:9 - | -LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union - = note: this union has unspecified layout -note: the type is defined here - --> $DIR/return-via-stack.rs:49:1 - | -LL | pub union ReprRustUnionU64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:34:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:25:48 | LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f1(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:35:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:26:48 | LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f2(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:36:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:27:48 | LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f3(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:37:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:28:48 | LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f4(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:38:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:29:48 | LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f5(); - | ^^^^ but its return value doesn't fit in the available registers - | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:39:5 - | -LL | f6: extern "C-cmse-nonsecure-call" fn() -> u128, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f6(); - | ^^^^ but its return value doesn't fit in the available registers - | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers - -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:40:5 - | -LL | f7: extern "C-cmse-nonsecure-call" fn() -> i128, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f7(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:58:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:51:48 | LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f1(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error[E0798]: return value of `C-cmse-nonsecure-call` function too large to pass via registers - --> $DIR/return-via-stack.rs:59:5 +error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + --> $DIR/return-via-stack.rs:52:48 | LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, - | -- this function uses the `C-cmse-nonsecure-call` ABI -... -LL | f2(); - | ^^^^ but its return value doesn't fit in the available registers + | ^^^^^^^^^^^^^ this type doesn't fit in the available registers | - = note: functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers + = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers -error: aborting due to 9 previous errors; 3 warnings emitted +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0798`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 91aadd4fe1d7e..9fda55c2a4807 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -10,10 +10,10 @@ pub trait Copy {} impl Copy for u32 {} #[repr(transparent)] -pub struct ReprTransparentStructU64 { +pub struct ReprTransparentStruct { _marker1: (), _marker2: (), - field: u64, + field: T, _marker3: (), } @@ -33,19 +33,9 @@ pub fn params( f3: extern "C-cmse-nonsecure-call" fn(u64, u64), f4: extern "C-cmse-nonsecure-call" fn(u128), f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32), - f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStructU64, U32Compound), + f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStruct, U32Compound), f7: extern "C-cmse-nonsecure-call" fn([u32; 4]), ) { - f1(); - f2(1, 2, 3, 4); - f3(1, 2); - f4(1); - f5(1.0, 2.0, 3.0); - f6( - ReprTransparentStructU64 { _marker1: (), _marker2: (), field: 1, _marker3: () }, - U32Compound(2, 3), - ); - f7([0xDEADBEEF; 4]); } #[no_mangle] @@ -55,16 +45,9 @@ pub fn returns( f3: extern "C-cmse-nonsecure-call" fn() -> i64, f4: extern "C-cmse-nonsecure-call" fn() -> f64, f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4], - f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStructU64, - f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, - f8: extern "C-cmse-nonsecure-call" fn() -> U32Compound, + f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct, + f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct>, + f8: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64, + f9: extern "C-cmse-nonsecure-call" fn() -> U32Compound, ) { - f1(); - f2(); - f3(); - f4(); - f5(); - f6(); - f7(); - f8(); } From 6b6b8422bafe310b911832119cbcf4d93f46871e Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 18 Jul 2024 12:08:23 +0200 Subject: [PATCH 11/16] remove cmse validation from rustc_codegen_ssa it was moved to hir_analysis in the previous commit --- compiler/rustc_codegen_ssa/messages.ftl | 12 ---- compiler/rustc_codegen_ssa/src/errors.rs | 22 ------ compiler/rustc_codegen_ssa/src/mir/block.rs | 4 -- compiler/rustc_codegen_ssa/src/mir/cmse.rs | 72 ------------------- compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 - .../rustc_hir_analysis/src/check/check.rs | 7 +- .../src/hir_ty_lowering/mod.rs | 6 +- 7 files changed, 4 insertions(+), 120 deletions(-) delete mode 100644 compiler/rustc_codegen_ssa/src/mir/cmse.rs diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 0aee5e16a537d..000fe2e3ce0f5 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,18 +16,6 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. -codegen_ssa_cmse_call_inputs_stack_spill = - arguments for `C-cmse-nonsecure-call` function too large to pass via registers - .label = this function uses the `C-cmse-nonsecure-call` ABI - .call = but its arguments don't fit in the available registers - .note = functions with the `C-cmse-nonsecure-call` ABI must pass all their arguments via the 4 32-bit available argument registers - -codegen_ssa_cmse_call_output_stack_spill = - return value of `C-cmse-nonsecure-call` function too large to pass via registers - .label = this function uses the `C-cmse-nonsecure-call` ABI - .call = but its return value doesn't fit in the available registers - .note = functions with the `C-cmse-nonsecure-call` ABI must pass their result via the available return registers - codegen_ssa_compiler_builtins_cannot_call = `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index af7835633cfd1..e9d31db92541b 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1033,25 +1033,3 @@ pub struct CompilerBuiltinsCannotCall { pub caller: String, pub callee: String, } - -#[derive(Diagnostic)] -#[diag(codegen_ssa_cmse_call_inputs_stack_spill, code = E0798)] -#[note] -pub struct CmseCallInputsStackSpill { - #[primary_span] - #[label(codegen_ssa_call)] - pub call_site_span: Span, - #[label] - pub function_definition_span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_cmse_call_output_stack_spill, code = E0798)] -#[note] -pub struct CmseCallOutputStackSpill { - #[primary_span] - #[label(codegen_ssa_call)] - pub call_site_span: Span, - #[label] - pub function_definition_span: Span, -} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 24d3b8ad264b7..6a5525dc2b34f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -7,7 +7,6 @@ use crate::base; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; -use crate::mir::cmse; use crate::traits::*; use crate::MemFlags; @@ -870,9 +869,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let sig = callee.layout.ty.fn_sig(bx.tcx()); let abi = sig.abi(); - // emit errors if cmse ABI conditions are violated - cmse::validate_cmse_abi(bx, &sig.skip_binder(), span, func.span(self.mir)); - let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| { let op_ty = op_arg.node.ty(self.mir, bx.tcx()); diff --git a/compiler/rustc_codegen_ssa/src/mir/cmse.rs b/compiler/rustc_codegen_ssa/src/mir/cmse.rs deleted file mode 100644 index d3583d79c00d2..0000000000000 --- a/compiler/rustc_codegen_ssa/src/mir/cmse.rs +++ /dev/null @@ -1,72 +0,0 @@ -use rustc_middle::ty::FnSig; -use rustc_span::Span; - -use crate::errors::{CmseCallInputsStackSpill, CmseCallOutputStackSpill}; -use crate::traits::BuilderMethods; - -/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be -/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these -/// conditions, but by checking them here rustc can emit nicer error messages. -pub fn validate_cmse_abi<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &Bx, - fn_sig: &FnSig<'tcx>, - call_site_span: Span, - function_definition_span: Span, -) { - if let rustc_target::spec::abi::Abi::CCmseNonSecureCall = fn_sig.abi { - if !has_valid_inputs(bx, fn_sig) { - let err = CmseCallInputsStackSpill { call_site_span, function_definition_span }; - bx.tcx().dcx().emit_err(err); - } - - if !has_valid_output(bx, fn_sig) { - let err = CmseCallOutputStackSpill { call_site_span, function_definition_span }; - bx.tcx().dcx().emit_err(err); - } - } -} - -/// Returns whether the inputs will fit into the available registers -fn has_valid_inputs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool { - let mut accum = 0u64; - - for arg_def in fn_sig.inputs().iter() { - let layout = bx.layout_of(*arg_def); - - let align = layout.layout.align().abi.bytes(); - let size = layout.layout.size().bytes(); - - accum += size; - accum = accum.next_multiple_of(Ord::max(4, align)); - } - - // the available argument space is 16 bytes (4 32-bit registers) in total - let available_space = 16; - - accum <= available_space -} - -/// Returns whether the output will fit into the available registers -fn has_valid_output<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &Bx, fn_sig: &FnSig<'tcx>) -> bool { - let mut ret_layout = bx.layout_of(fn_sig.output()); - - // unwrap any `repr(transparent)` wrappers - loop { - if ret_layout.is_transparent::() { - match ret_layout.non_1zst_field(bx) { - None => break, - Some((_, layout)) => ret_layout = layout, - } - } else { - break; - } - } - - // Fundamental types of size 8 can be passed via registers according to the ABI - let valid_2register_return_types = [bx.tcx().types.i64, bx.tcx().types.u64, bx.tcx().types.f64]; - - // A Composite Type larger than 4 bytes is stored in memory at an address - // passed as an extra argument when the function was called. That is not allowed - // for cmse_nonsecure_entry functions. - ret_layout.layout.size().bytes() <= 4 || valid_2register_return_types.contains(&ret_layout.ty) -} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index ec28def8b83ec..61f57c9030a1a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -16,7 +16,6 @@ use std::iter; mod analyze; mod block; -mod cmse; pub mod constant; pub mod coverageinfo; pub mod debuginfo; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index aa302e4c25f4e..bf8ef18c04fcc 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1287,10 +1287,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, trivial, check_non_exhaustive(tcx, ty).break_value()) }); - let non_trivial_fields = - field_infos.clone().filter_map( - |(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }, - ); + let non_trivial_fields = field_infos + .clone() + .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5a98e4d79bb08..c118181780a12 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1749,11 +1749,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !indices.contains(&index) { - Some(seg) - } else { - None - } + if !indices.contains(&index) { Some(seg) } else { None } }), GenericsArgsErrExtend::DefVariant, ); From c2894a4297fd1e824e071fd21a92bfe475c0169a Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 18 Jul 2024 14:32:10 +0200 Subject: [PATCH 12/16] improve error reporting --- compiler/rustc_hir_analysis/messages.ftl | 8 ++- compiler/rustc_hir_analysis/src/errors.rs | 4 +- .../src/hir_ty_lowering/cmse.rs | 50 ++++++++++--------- .../src/hir_ty_lowering/mod.rs | 2 +- .../cmse-nonsecure-call/generics.stderr | 8 +-- .../cmse-nonsecure-call/params-via-stack.rs | 10 ++-- .../params-via-stack.stderr | 20 ++++---- .../return-via-stack.stderr | 9 ++++ 8 files changed, 65 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 8361804113390..9e5fa37de322c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -63,13 +63,17 @@ hir_analysis_cmse_call_generic = hir_analysis_cmse_call_inputs_stack_spill = arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - .label = these arguments don't fit in the available registers + .label = {$plural -> + [false] this argument doesn't + *[true] these arguments don't + } fit in the available registers .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers hir_analysis_cmse_call_output_stack_spill = return value of `"C-cmse-nonsecure-call"` function too large to pass via registers .label = this type doesn't fit in the available registers - .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f6783364f1637..a77f967a5caa3 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,11 +1690,13 @@ pub struct CmseCallInputsStackSpill { #[primary_span] #[label] pub span: Span, + pub plural: bool, } #[derive(Diagnostic)] #[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)] -#[note] +#[note(hir_analysis_note1)] +#[note(hir_analysis_note2)] pub struct CmseCallOutputStackSpill { #[primary_span] #[label] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 8980173f73817..e99717ce00f8c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -13,7 +13,7 @@ use crate::errors; /// conditions, but by checking them here rustc can emit nicer error messages. pub fn validate_cmse_abi<'tcx>( tcx: TyCtxt<'tcx>, - dcx: &DiagCtxtHandle<'_>, + dcx: DiagCtxtHandle<'_>, hir_id: HirId, abi: abi::Abi, fn_sig: ty::PolyFnSig<'tcx>, @@ -30,25 +30,20 @@ pub fn validate_cmse_abi<'tcx>( return; }; - // fn(u32, u32, u32, u16, u16) -> u32, - // ^^^^^^^^^^^^^^^^^^^^^^^ ^^^ - let output_span = bare_fn_ty.decl.output.span(); - let inputs_span = match ( - bare_fn_ty.param_names.first(), - bare_fn_ty.decl.inputs.first(), - bare_fn_ty.decl.inputs.last(), - ) { - (Some(ident), Some(ty1), Some(ty2)) => ident.span.to(ty1.span).to(ty2.span), - _ => *bare_fn_span, - }; - match is_valid_cmse_inputs(tcx, fn_sig) { - Ok(true) => {} - Ok(false) => { - dcx.emit_err(errors::CmseCallInputsStackSpill { span: inputs_span }); + Ok(Ok(())) => {} + Ok(Err(index)) => { + // fn(x: u32, u32, u32, u16, y: u16) -> u32, + // ^^^^^^ + let span = bare_fn_ty.param_names[index] + .span + .to(bare_fn_ty.decl.inputs[index].span) + .to(bare_fn_ty.decl.inputs.last().unwrap().span); + let plural = bare_fn_ty.param_names.len() - index != 1; + dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural }); } Err(layout_err) => { - if let Some(err) = cmse_layout_err(layout_err, inputs_span) { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { dcx.emit_err(err); } } @@ -57,10 +52,11 @@ pub fn validate_cmse_abi<'tcx>( match is_valid_cmse_output(tcx, fn_sig) { Ok(true) => {} Ok(false) => { - dcx.emit_err(errors::CmseCallOutputStackSpill { span: output_span }); + let span = bare_fn_ty.decl.output.span(); + dcx.emit_err(errors::CmseCallOutputStackSpill { span }); } Err(layout_err) => { - if let Some(err) = cmse_layout_err(layout_err, output_span) { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { dcx.emit_err(err); } } @@ -72,10 +68,11 @@ pub fn validate_cmse_abi<'tcx>( fn is_valid_cmse_inputs<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, -) -> Result> { +) -> Result, &'tcx LayoutError<'tcx>> { + let mut span = None; let mut accum = 0u64; - for arg_def in fn_sig.inputs().iter() { + for (index, arg_def) in fn_sig.inputs().iter().enumerate() { let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?; let align = layout.layout.align().abi.bytes(); @@ -83,10 +80,17 @@ fn is_valid_cmse_inputs<'tcx>( accum += size; accum = accum.next_multiple_of(Ord::max(4, align)); + + // i.e. exceeds 4 32-bit registers + if accum > 16 { + span = span.or(Some(index)); + } } - // i.e. 4 32-bit registers - Ok(accum <= 16) + match span { + None => Ok(Ok(())), + Some(span) => Ok(Err(span)), + } } /// Returns whether the output will fit into the available registers diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c118181780a12..a632619aef205 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2326,7 +2326,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); // reject function types that violate cmse ABI requirements - cmse::validate_cmse_abi(self.tcx(), &self.dcx(), hir_id, abi, bare_fn_ty); + cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty); // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index b255d26193856..fa68d95218ccd 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -30,16 +30,16 @@ LL | f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:19:43 + --> $DIR/generics.rs:19:9 | LL | f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:20:43 + --> $DIR/generics.rs:20:9 | LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs index f8ef441992100..083a563bd7ba7 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs @@ -14,10 +14,10 @@ pub struct AlignRelevant(u32); #[no_mangle] pub fn test( - f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), //~ ERROR [E0798] - f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] - f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] - f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] - f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] + f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798] + f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798] + f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798] + f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798] + f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798] ) { } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr index 499122ab2c547..a5f5e1c3151d8 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr @@ -1,32 +1,32 @@ error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:17:43 + --> $DIR/params-via-stack.rs:17:63 | -LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32), - | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers +LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), + | ^^^^^^^^^^^^^^ these arguments don't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:18:43 + --> $DIR/params-via-stack.rs:18:63 | LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), - | ^^^^^^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:19:43 + --> $DIR/params-via-stack.rs:19:53 | LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), - | ^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/params-via-stack.rs:20:43 + --> $DIR/params-via-stack.rs:20:58 | LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), - | ^^^^^^^^^^^^^^^^^^ these arguments don't fit in the available registers + | ^^^ this argument doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers @@ -34,7 +34,7 @@ error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass --> $DIR/params-via-stack.rs:21:43 | LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), - | ^^^^^^^^ these arguments don't fit in the available registers + | ^^^^^^^^ this argument doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr index c8984e3c0d902..89f7f159d6e4d 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr @@ -5,6 +5,7 @@ LL | u128: extern "C-cmse-nonsecure-call" fn() -> u128, | ^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:36:50 @@ -13,6 +14,7 @@ LL | i128: extern "C-cmse-nonsecure-call" fn() -> i128, | ^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:25:48 @@ -21,6 +23,7 @@ LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, | ^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:26:48 @@ -29,6 +32,7 @@ LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, | ^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:27:48 @@ -37,6 +41,7 @@ LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, | ^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:28:48 @@ -45,6 +50,7 @@ LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, | ^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:29:48 @@ -53,6 +59,7 @@ LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], | ^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:51:48 @@ -61,6 +68,7 @@ LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, | ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = 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 `"C-cmse-nonsecure-call"` function too large to pass via registers --> $DIR/return-via-stack.rs:52:48 @@ -69,6 +77,7 @@ LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, | ^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error: aborting due to 9 previous errors From af7ecb6333950031bf07e5a2fadc7327b7f84963 Mon Sep 17 00:00:00 2001 From: Princess Entrapta Date: Fri, 19 Jul 2024 04:15:42 +0200 Subject: [PATCH 13/16] fix: explain E0120 better cover cases when its raised --- .../src/error_codes/E0120.md | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0120.md b/compiler/rustc_error_codes/src/error_codes/E0120.md index dc7258d87317f..aa701df577465 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0120.md +++ b/compiler/rustc_error_codes/src/error_codes/E0120.md @@ -1,7 +1,7 @@ -Drop was implemented on a trait, which is not allowed: only structs and -enums can implement Drop. +`Drop` was implemented on a trait object or reference, which is not allowed; +only structs, enums, and unions can implement Drop. -Erroneous code example: +Erroneous code examples: ```compile_fail,E0120 trait MyTrait {} @@ -11,8 +11,16 @@ impl Drop for MyTrait { } ``` -A workaround for this problem is to wrap the trait up in a struct, and implement -Drop on that: +```compile_fail,E0120 +struct Concrete {} + +impl Drop for &'_ mut Concrete { + fn drop(&mut self) {} +} +``` + +A workaround for traits is to create a wrapper struct with a generic type, +add a trait bound to the type, and implement `Drop` on the wrapper: ``` trait MyTrait {} @@ -24,13 +32,13 @@ impl Drop for MyWrapper { ``` -Alternatively, wrapping trait objects requires something: +Alternatively, the `Drop` wrapper can contain the trait object: ``` trait MyTrait {} -//or Box, if you wanted an owned trait object -struct MyWrapper<'a> { foo: &'a MyTrait } +// or Box, if you wanted an owned trait object +struct MyWrapper<'a> { foo: &'a dyn MyTrait } impl <'a> Drop for MyWrapper<'a> { fn drop(&mut self) {} From a0db06bdebfef83b4424415bd6e64cfb5bdcc978 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 19 Jul 2024 14:21:30 +0000 Subject: [PATCH 14/16] Use structured suggestions for unconstrained generic parameters on impl blocks --- compiler/rustc_hir_analysis/messages.ftl | 5 ++ compiler/rustc_hir_analysis/src/errors.rs | 14 ++++ .../rustc_hir_analysis/src/impl_wf_check.rs | 76 +++++-------------- 3 files changed, 39 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index b43559f422533..2fcf43c77d126 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -519,6 +519,11 @@ hir_analysis_typeof_reserved_keyword_used = .suggestion = consider replacing `typeof(...)` with an actual type .label = reserved keyword +hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates + .label = unconstrained {$param_def_kind} + .const_param_note = expressions using a const parameter must map each value to a distinct output value + .const_param_note2 = proving the result of expressions other than the parameter are unique is not supported + hir_analysis_unconstrained_opaque_type = unconstrained opaque type .note = `{$name}` must be used in combination with a concrete type within the same {$what} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 03311fed39615..8e88f33a07968 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1604,6 +1604,20 @@ pub(crate) enum UnusedGenericParameterHelp { TyAlias { param_name: Ident }, } +#[derive(Diagnostic)] +#[diag(hir_analysis_unconstrained_generic_parameter)] +pub(crate) struct UnconstrainedGenericParameter { + #[primary_span] + #[label] + pub span: Span, + pub param_name: Symbol, + pub param_def_kind: &'static str, + #[note(hir_analysis_const_param_note)] + pub const_param_note: Option<()>, + #[note(hir_analysis_const_param_note2)] + pub const_param_note2: Option<()>, +} + #[derive(Diagnostic)] pub enum UnnamedFieldsRepr<'a> { #[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)] diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 5cc1ec71757be..f0fcbd5528f4e 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -8,15 +8,15 @@ //! specialization errors. These things can (and probably should) be //! fixed, but for the moment it's easier to do these checks early. -use crate::constrained_generic_params as cgp; +use crate::{constrained_generic_params as cgp, errors::UnconstrainedGenericParameter}; use min_specialization::check_min_specialization; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_errors::codes::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::ErrorGuaranteed; mod min_specialization; @@ -117,43 +117,34 @@ fn enforce_impl_params_are_constrained( let mut res = Ok(()); for param in &impl_generics.own_params { - match param.kind { + let err = match param.kind { // Disallow ANY unconstrained type parameters. ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "type", - param_ty.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ty)) } ty::GenericParamDefKind::Lifetime => { let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - if lifetimes_in_associated_types.contains(¶m_lt) && // (*) + lifetimes_in_associated_types.contains(¶m_lt) && // (*) !input_parameters.contains(¶m_lt) - { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "lifetime", - param.name, - )); - } } ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "const", - param_ct.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ct)) } + }; + if err { + let const_param_note = + matches!(param.kind, ty::GenericParamDefKind::Const { .. }).then_some(()); + let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { + span: tcx.def_span(param.def_id), + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + const_param_note, + const_param_note2: const_param_note, + }); + diag.code(E0207); + res = Err(diag.emit()); } } res @@ -177,30 +168,3 @@ fn enforce_impl_params_are_constrained( // associated types. I believe this is sound, because lifetimes // used elsewhere are not projected back out. } - -fn report_unused_parameter( - tcx: TyCtxt<'_>, - span: Span, - kind: &str, - name: Symbol, -) -> ErrorGuaranteed { - let mut err = struct_span_code_err!( - tcx.dcx(), - span, - E0207, - "the {} parameter `{}` is not constrained by the \ - impl trait, self type, or predicates", - kind, - name - ); - err.span_label(span, format!("unconstrained {kind} parameter")); - if kind == "const" { - err.note( - "expressions using a const parameter must map each value to a distinct output value", - ); - err.note( - "proving the result of expressions other than the parameter are unique is not supported", - ); - } - err.emit() -} From 91275b2c2bf824286b844c38252fdbeb6d995105 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 19 Jul 2024 11:51:21 -0400 Subject: [PATCH 15/16] Avoid ref when using format! for perf Clean up a few minor refs in `format!` macro, as it has a tiny perf cost. A few more minor related cleanups. --- library/core/src/fmt/builders.rs | 2 +- library/core/tests/future.rs | 2 +- library/proc_macro/src/lib.rs | 4 ++-- library/std/src/fs/tests.rs | 2 +- library/std/src/io/error.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 4ccb585862cdf..36fae1c15960e 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1026,7 +1026,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{}", value), "a"); /// assert_eq!(format!("{:?}", value), "'a'"); /// -/// let wrapped = fmt::FormatterFn(|f| write!(f, "{:?}", &value)); +/// let wrapped = fmt::FormatterFn(|f| write!(f, "{value:?}")); /// assert_eq!(format!("{}", wrapped), "'a'"); /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs index db417256dd01e..93aca72d59082 100644 --- a/library/core/tests/future.rs +++ b/library/core/tests/future.rs @@ -56,7 +56,7 @@ fn test_join() { let y = String::new(); let x = join!(async { - println!("{}", &y); + println!("{y}"); 1 }) .await; diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 581d7e3efe373..57247359fbf29 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1544,10 +1544,10 @@ impl fmt::Debug for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Literal") // format the kind on one line even in {:#?} mode - .field("kind", &format_args!("{:?}", &self.0.kind)) + .field("kind", &format_args!("{:?}", self.0.kind)) .field("symbol", &self.0.symbol) // format `Some("...")` on one line even in {:#?} mode - .field("suffix", &format_args!("{:?}", &self.0.suffix)) + .field("suffix", &format_args!("{:?}", self.0.suffix)) .field("span", &self.0.span) .finish() } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 5ca631399aa4a..c1fc2e5488d0c 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -683,7 +683,7 @@ fn recursive_rmdir_toctou() { let drop_canary_arc = Arc::new(()); let drop_canary_weak = Arc::downgrade(&drop_canary_arc); - eprintln!("x: {:?}", &victim_del_path); + eprintln!("x: {victim_del_path:?}"); // victim just continuously removes `victim_del` thread::spawn(move || { diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index f366cb8f42baa..8de27367a3f21 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -775,7 +775,7 @@ impl Error { /// /// impl Display for MyError { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", &self.v) + /// write!(f, "MyError: {}", self.v) /// } /// } /// From 756459ed851ac4aab71ce5353683bd45c38eb61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 19 Jul 2024 18:53:40 +0200 Subject: [PATCH 16/16] LTA: Diag: Detect bivariant ty params that are only used recursively --- .../rustc_hir_analysis/src/check/wfcheck.rs | 73 ++++++++++--------- .../inherent-impls-overflow.next.stderr | 23 +++--- .../inherent-impls-overflow.rs | 4 +- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 809427f86eef1..71a7b0b16389b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1922,31 +1922,24 @@ fn report_bivariance<'tcx>( ); if !usage_spans.is_empty() { - // First, check if the ADT is (probably) cyclical. We say probably here, since - // we're not actually looking into substitutions, just walking through fields. - // And we only recurse into the fields of ADTs, and not the hidden types of - // opaques or anything else fancy. + // First, check if the ADT/LTA is (probably) cyclical. We say probably here, since we're + // not actually looking into substitutions, just walking through fields / the "RHS". + // We don't recurse into the hidden types of opaques or anything else fancy. let item_def_id = item.owner_id.to_def_id(); - let is_probably_cyclical = if matches!( - tcx.def_kind(item_def_id), - DefKind::Struct | DefKind::Union | DefKind::Enum - ) { - IsProbablyCyclical { tcx, adt_def_id: item_def_id, seen: Default::default() } - .visit_all_fields(tcx.adt_def(item_def_id)) - .is_break() - } else { - false - }; - // If the ADT is cyclical, then if at least one usage of the type parameter or - // the `Self` alias is present in the, then it's probably a cyclical struct, and - // we should call those parameter usages recursive rather than just saying they're - // unused... + let is_probably_cyclical = + IsProbablyCyclical { tcx, item_def_id, seen: Default::default() } + .visit_def(item_def_id) + .is_break(); + // If the ADT/LTA is cyclical, then if at least one usage of the type parameter or + // the `Self` alias is present in the, then it's probably a cyclical struct/ type + // alias, and we should call those parameter usages recursive rather than just saying + // they're unused... // // We currently report *all* of the parameter usages, since computing the exact // subset is very involved, and the fact we're mentioning recursion at all is // likely to guide the user in the right direction. if is_probably_cyclical { - let diag = tcx.dcx().create_err(errors::RecursiveGenericParameter { + return tcx.dcx().emit_err(errors::RecursiveGenericParameter { spans: usage_spans, param_span: param.span, param_name, @@ -1954,7 +1947,6 @@ fn report_bivariance<'tcx>( help, note: (), }); - return diag.emit(); } } @@ -1974,42 +1966,51 @@ fn report_bivariance<'tcx>( diag.emit() } -/// Detects cases where an ADT is trivially cyclical -- we want to detect this so -/// /we only mention that its parameters are used cyclically if the ADT is truly +/// Detects cases where an ADT/LTA is trivially cyclical -- we want to detect this so +/// we only mention that its parameters are used cyclically if the ADT/LTA is truly /// cyclical. /// /// Notably, we don't consider substitutions here, so this may have false positives. struct IsProbablyCyclical<'tcx> { tcx: TyCtxt<'tcx>, - adt_def_id: DefId, + item_def_id: DefId, seen: FxHashSet, } impl<'tcx> IsProbablyCyclical<'tcx> { - fn visit_all_fields(&mut self, adt_def: ty::AdtDef<'tcx>) -> ControlFlow<(), ()> { - for field in adt_def.all_fields() { - self.tcx.type_of(field.did).instantiate_identity().visit_with(self)?; + fn visit_def(&mut self, def_id: DefId) -> ControlFlow<(), ()> { + match self.tcx.def_kind(def_id) { + DefKind::Struct | DefKind::Enum | DefKind::Union => { + self.tcx.adt_def(def_id).all_fields().try_for_each(|field| { + self.tcx.type_of(field.did).instantiate_identity().visit_with(self) + }) + } + DefKind::TyAlias if self.tcx.type_alias_is_lazy(def_id) => { + self.tcx.type_of(def_id).instantiate_identity().visit_with(self) + } + _ => ControlFlow::Continue(()), } - - ControlFlow::Continue(()) } } impl<'tcx> TypeVisitor> for IsProbablyCyclical<'tcx> { type Result = ControlFlow<(), ()>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<(), ()> { - if let Some(adt_def) = t.ty_adt_def() { - if adt_def.did() == self.adt_def_id { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> { + let def_id = match ty.kind() { + ty::Adt(adt_def, _) => Some(adt_def.did()), + ty::Alias(ty::Weak, alias_ty) => Some(alias_ty.def_id), + _ => None, + }; + if let Some(def_id) = def_id { + if def_id == self.item_def_id { return ControlFlow::Break(()); } - - if self.seen.insert(adt_def.did()) { - self.visit_all_fields(adt_def)?; + if self.seen.insert(def_id) { + self.visit_def(def_id)?; } } - - t.super_visit_with(self) + ty.super_visit_with(self) } } diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr index 48e7da9661210..192b5eebdaac8 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr @@ -4,27 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _` LL | impl Loop {} | ^^^^ -error[E0392]: type parameter `T` is never used - --> $DIR/inherent-impls-overflow.rs:14:12 +error: type parameter `T` is only used recursively + --> $DIR/inherent-impls-overflow.rs:14:24 | LL | type Poly0 = Poly1<(T,)>; - | ^ - `T` is named here, but is likely unused in the containing type + | - ^ | | - | unused type parameter + | type parameter must be used non-recursively in the definition | = help: consider removing `T` or referring to it in the body of the type alias - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + = note: all type parameters must be used in a non-recursive way in order to constrain their variance -error[E0392]: type parameter `T` is never used - --> $DIR/inherent-impls-overflow.rs:17:12 +error: type parameter `T` is only used recursively + --> $DIR/inherent-impls-overflow.rs:17:24 | LL | type Poly1 = Poly0<(T,)>; - | ^ - `T` is named here, but is likely unused in the containing type + | - ^ | | - | unused type parameter + | type parameter must be used non-recursively in the definition | = help: consider removing `T` or referring to it in the body of the type alias - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + = note: all type parameters must be used in a non-recursive way in order to constrain their variance error[E0275]: overflow evaluating the requirement `Poly0<()> == _` --> $DIR/inherent-impls-overflow.rs:21:6 @@ -36,5 +36,4 @@ LL | impl Poly0<()> {} error: aborting due to 4 previous errors -Some errors have detailed explanations: E0275, E0392. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs index 98f0d811a47be..1397695a3fe41 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs @@ -13,10 +13,10 @@ impl Loop {} type Poly0 = Poly1<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` -//[next]~^^ ERROR type parameter `T` is never used +//[next]~^^ ERROR type parameter `T` is only used recursively type Poly1 = Poly0<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` -//[next]~^^ ERROR type parameter `T` is never used +//[next]~^^ ERROR type parameter `T` is only used recursively impl Poly0<()> {} //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`