diff --git a/riscv-macros/CHANGELOG.md b/riscv-macros/CHANGELOG.md index 4dd05aaf..bf0523c4 100644 --- a/riscv-macros/CHANGELOG.md +++ b/riscv-macros/CHANGELOG.md @@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## v0.4.1 - Unreleased + +### Added + +- `entry` macro for the `riscv-rt` crate (migrated from `riscv-rt-macros`). +- `post_init` macro for the `riscv-rt` crate (migrated from `riscv-rt-macros`). ## v0.4.0 - 2025-12-19 diff --git a/riscv-macros/Cargo.toml b/riscv-macros/Cargo.toml index 483cc242..69f91aba 100644 --- a/riscv-macros/Cargo.toml +++ b/riscv-macros/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["riscv", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "riscv-macros" repository = "https://github.com/rust-embedded/riscv" -version = "0.4.0" +version = "0.4.1" edition = "2021" [lib] @@ -18,6 +18,8 @@ proc-macro = true [features] rt = [] rt-v-trap = ["rt"] +riscv-rt = ["rt", "syn/extra-traits", "syn/full"] +u-boot = [] [dependencies] proc-macro2 = "1.0" diff --git a/riscv-macros/src/lib.rs b/riscv-macros/src/lib.rs index b1534601..61b8b15e 100644 --- a/riscv-macros/src/lib.rs +++ b/riscv-macros/src/lib.rs @@ -4,6 +4,9 @@ use syn::{parse_macro_input, DeriveInput}; mod riscv; +#[cfg(feature = "riscv-rt")] +mod riscv_rt; + /// Attribute-like macro that implements the traits of the `riscv-types` crate for a given enum. /// /// As these traits are unsafe, the macro must be called with the `unsafe` keyword followed by the trait name. @@ -59,3 +62,76 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream { } .into() } + +/// Attribute to mark which function will be called before jumping to the entry point. +/// You must enable the `post-init` feature in the `riscv-rt` crate to use this macro. +/// +/// In contrast with `__pre_init`, this function is called after the static variables +/// are initialized, so it is safe to access them. It is also safe to run Rust code. +/// +/// The function must have the signature of `[unsafe] fn([usize])`, where the argument +/// corresponds to the hart ID of the current hart. This is useful for multi-hart systems +/// to perform hart-specific initialization. +/// +/// # IMPORTANT +/// +/// This attribute can appear at most *once* in the dependency graph. +/// +/// # Examples +/// +/// ``` +/// #[riscv_macros::post_init] +/// unsafe fn before_main(hart_id: usize) { +/// // do something here +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::post_init(args, input) +} + +/// Attribute to declare the entry point of the program +/// +/// The specified function will be called by the reset handler *after* RAM has been initialized. +/// If present, the FPU will also be enabled before the function is called. +/// +/// # Signature +/// +/// ## Regular Usage +/// +/// The type of the specified function must be `[unsafe] fn([usize[, usize[, usize]]]) -> !` (never ending function). +/// The optional arguments correspond to the values passed in registers `a0`, `a1`, and `a2`. +/// The first argument holds the hart ID of the current hart, which is useful for multi-hart systems. +/// The other two arguments are currently unused and reserved for future use. +/// +/// ## With U-Boot +/// +/// This runtime supports being booted by U-Boot. In this case, the entry point function +/// must have the signature `[unsafe] fn([c_int[, *const *const c_char]]) -> !`, where the first argument +/// corresponds to the `argc` parameter and the second argument corresponds to the `argv` parameter passed by U-Boot. +/// +/// Remember to enable the `u-boot` feature in the `riscv-rt` crate to use this functionality. +/// +/// # IMPORTANT +/// +/// This attribute can appear at most *once* in the dependency graph. +/// +/// The entry point will be called by the reset handler. The program can't reference to the entry +/// point, much less invoke it. +/// +/// # Examples +/// +/// ``` no_run +/// #[riscv_macros::entry] +/// fn main() -> ! { +/// loop { +/// /* .. */ +/// } +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::entry(args, input) +} diff --git a/riscv-macros/src/riscv_rt.rs b/riscv-macros/src/riscv_rt.rs new file mode 100644 index 00000000..8c970954 --- /dev/null +++ b/riscv-macros/src/riscv_rt.rs @@ -0,0 +1,275 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{ + parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Error, FnArg, Ident, + ItemFn, Result, ReturnType, Type, Visibility, +}; + +/// Enum representing the supported runtime function attributes +pub enum Fn { + PostInit, + Entry, +} + +impl Fn { + /// Convenience method to generate the token stream for the `post_init` attribute + pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream { + let errors = Self::PostInit.check_args_empty(args).err(); + Self::PostInit.quote_fn(input, errors) + } + + /// Convenience method to generate the token stream for the `entry` attribute + pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + let errors = Self::Entry.check_args_empty(args).err(); + Self::Entry.quote_fn(input, errors) + } + + /// Generate the token stream for the function with the given attribute + fn quote_fn(&self, item: TokenStream, errors: Option) -> TokenStream { + let mut func = parse_macro_input!(item as ItemFn); + + if let Err(e) = self.check_fn(&func, errors) { + return e.to_compile_error().into(); + } + + let export_name = self.export_name(&func); + let link_section = self.link_section(&func); + + // Append to function name the prefix __riscv_rt_ (to prevent users from calling it directly) + // Note that we do not change the export name, only the internal function name in the Rust code. + func.sig.ident = Ident::new( + &format!("__riscv_rt_{}", func.sig.ident), + func.sig.ident.span(), + ); + + quote! { + #export_name + #link_section + #func + } + .into() + } + + /// Check if the function signature is valid for the given attribute + fn check_fn(&self, f: &ItemFn, mut errors: Option) -> Result<()> { + // First, check that the function is private + if f.vis != Visibility::Inherited { + combine_err( + &mut errors, + Error::new(f.vis.span(), "function must be private"), + ); + } + + let sig = &f.sig; + // Next, check common aspects of the signature individually to accumulate errors + if let Some(constness) = sig.constness { + combine_err( + &mut errors, + Error::new(constness.span(), "function must not be const"), + ); + } + if let Some(asyncness) = sig.asyncness { + combine_err( + &mut errors, + Error::new(asyncness.span(), "function must not be async"), + ); + } + if let Some(abi) = &sig.abi { + combine_err( + &mut errors, + Error::new(abi.span(), "ABI must not be specified"), + ); + } + if !sig.generics.params.is_empty() { + // Use to_token_stream to get a span covering the entire <...> block + let span = sig.generics.params.span(); + combine_err(&mut errors, Error::new(span, "generics are not allowed")); + } + + // Check input parameters... + self.check_inputs(&sig.inputs, &mut errors); + // ... and variadic arguments (they are at the end of input parameters) + if let Some(variadic) = &sig.variadic { + combine_err( + &mut errors, + Error::new(variadic.span(), "variadic arguments are not allowed"), + ); + } + + // Check output type... + self.check_output(&sig.output, &mut errors); + // ... and where clause (they are after output type) + if let Some(where_clause) = &sig.generics.where_clause { + combine_err( + &mut errors, + Error::new(where_clause.span(), "where clause is not allowed"), + ); + } + + match errors { + Some(e) => Err(e), + None => Ok(()), + } + } + + /// Check if the function has valid input arguments for the given attribute + fn check_inputs(&self, inputs: &Punctuated, errors: &mut Option) { + // Use this match to specify expected input arguments for different functions in the future + match self { + Self::PostInit => self.check_fn_args(inputs, &["usize"], errors), + #[cfg(not(feature = "u-boot"))] + Self::Entry => self.check_fn_args(inputs, &["usize", "usize", "usize"], errors), + #[cfg(feature = "u-boot")] + Self::Entry => self.check_fn_args(inputs, &["c_int", "*const *const c_char"], errors), + } + } + + /// Check if the function has a valid output type for the given attribute + fn check_output(&self, output: &ReturnType, errors: &mut Option) { + // Use this match to specify expected output types for different functions in the future + match self { + Self::PostInit => check_output_empty(output, errors), + Self::Entry => check_output_never(output, errors), + } + } + + /// The export name for the given attribute + fn export_name(&self, _f: &ItemFn) -> Option { + // Use this match to specify export names for different functions in the future + let export_name = match self { + Self::PostInit => Some("__post_init".to_string()), + Self::Entry => Some("main".to_string()), + }; + + export_name.map(|name| match self { + Self::Entry => quote! { + // to avoid two main symbols when testing on host + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), export_name = #name)] + }, + _ => quote! { + #[export_name = #name] + }, + }) + } + + /// The link section attribute for the given attribute (if any) + fn link_section(&self, _f: &ItemFn) -> Option { + // Use this match to specify section names for different functions in the future + let section_name: Option = match self { + Self::PostInit | Self::Entry => None, + }; + + section_name.map(|section| quote! { + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), link_section = #section)] + }) + } + + /// Check that no arguments were provided to the macro attribute + fn check_args_empty(&self, args: TokenStream) -> Result<()> { + if args.is_empty() { + Ok(()) + } else { + let args: TokenStream2 = args.into(); + Err(Error::new(args.span(), "macro arguments are not allowed")) + } + } + + /// Iterates through the input arguments and checks that their types match the expected types + fn check_fn_args( + &self, + inputs: &Punctuated, + expected_types: &[&str], + errors: &mut Option, + ) { + let mut expected_iter = expected_types.iter(); + for arg in inputs.iter() { + match expected_iter.next() { + Some(expected) => { + if let Err(e) = check_arg_type(arg, expected) { + combine_err(errors, e); + } + } + None => { + combine_err(errors, Error::new(arg.span(), "too many input arguments")); + } + } + } + } +} + +/// Combine a new error into an optional accumulator +fn combine_err(acc: &mut Option, err: Error) { + match acc { + Some(e) => e.combine(err), + None => *acc = Some(err), + } +} + +/// Check if a function argument matches the expected type +fn check_arg_type(arg: &FnArg, expected: &str) -> Result<()> { + match arg { + FnArg::Typed(argument) => { + if !is_correct_type(&argument.ty, expected) { + Err(Error::new( + argument.ty.span(), + format!("argument type must be `{expected}`"), + )) + } else { + Ok(()) + } + } + FnArg::Receiver(_) => Err(Error::new(arg.span(), "invalid argument")), + } +} + +/// Check if a type matches the expected type name +fn is_correct_type(ty: &Type, expected: &str) -> bool { + let correct: Type = syn::parse_str(expected).unwrap(); + if let Some(ty) = strip_type_path(ty) { + ty == correct + } else { + false + } +} + +/// Strip the path of a type, returning only the last segment (e.g., `core::usize` -> `usize`) +fn strip_type_path(ty: &Type) -> Option { + match ty { + Type::Ptr(ty) => { + let mut ty = ty.clone(); + *ty.elem = strip_type_path(&ty.elem)?; + Some(Type::Ptr(ty)) + } + Type::Path(ty) => { + let mut ty = ty.clone(); + let last_segment = ty.path.segments.last().unwrap().clone(); + ty.path.segments = Punctuated::new(); + ty.path.segments.push_value(last_segment); + Some(Type::Path(ty)) + } + _ => None, + } +} + +/// Make sure the output type is either `()` or absent +fn check_output_empty(output: &ReturnType, errors: &mut Option) { + match output { + ReturnType::Default => {} + ReturnType::Type(_, ty) => match **ty { + Type::Tuple(ref tuple) => { + if !tuple.elems.is_empty() { + combine_err(errors, Error::new(tuple.span(), "return type must be ()")); + } + } + _ => combine_err(errors, Error::new(ty.span(), "return type must be ()")), + }, + } +} + +/// Make sure the output type is `!` (never) +fn check_output_never(output: &ReturnType, errors: &mut Option) { + if !matches!(output, ReturnType::Type(_, ty) if matches!(**ty, Type::Never(_))) { + combine_err(errors, Error::new(output.span(), "return type must be !")); + } +} diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 06535d63..1e731e52 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Using `entry` from `riscv-macros` instead of `riscv-rt-macros`. +- Using `post_init` from `riscv-macros` instead of `riscv-rt-macros`. + ## v0.17.1 - 2026-01-13 ### Fixed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 03f8db74..70111590 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -25,6 +25,7 @@ riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.2" } [dependencies] riscv = { path = "../riscv", version = "0.16.0", features = ["rt"] } +riscv-macros = { path = "../riscv-macros", version = "0.4.1", features = ["riscv-rt"] } riscv-types = { path = "../riscv-types", version = "0.1.0" } riscv-rt-macros = { path = "macros", version = "0.6.1" } @@ -40,8 +41,8 @@ pre-init = [] post-init = [] s-mode = ["riscv-rt-macros/s-mode"] single-hart = [] -v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap"] -u-boot = ["riscv-rt-macros/u-boot", "single-hart"] +v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap", "riscv-macros/rt-v-trap"] +u-boot = ["riscv-rt-macros/u-boot", "single-hart", "riscv-macros/u-boot"] no-interrupts = [] no-exceptions = [] no-mhartid = ["single-hart"] diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 5f960e1f..b1f26adb 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -699,11 +699,12 @@ use riscv::register::{ mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode}, }; -pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt}; +pub use riscv_macros::entry; +pub use riscv_rt_macros::{core_interrupt, exception, external_interrupt}; pub use riscv_types::*; #[cfg(feature = "post-init")] -pub use riscv_rt_macros::post_init; +pub use riscv_macros::post_init; #[cfg(feature = "pre-init")] pub use riscv_rt_macros::pre_init; diff --git a/tests-qemu/Cargo.toml b/tests-qemu/Cargo.toml index 33a06f2c..cf5cd7c6 100644 --- a/tests-qemu/Cargo.toml +++ b/tests-qemu/Cargo.toml @@ -15,4 +15,4 @@ riscv-semihosting = { path = "../riscv-semihosting" } default = ["single-hart"] s-mode = ["riscv-rt/s-mode", "single-hart"] single-hart = ["riscv-rt/single-hart", "riscv/critical-section-single-hart"] -multi-hart = [] +multi-hart = ["riscv-rt/post-init"] diff --git a/tests-qemu/examples/multi_hart.rs b/tests-qemu/examples/multi_hart.rs index 84f59048..df2fbe23 100644 --- a/tests-qemu/examples/multi_hart.rs +++ b/tests-qemu/examples/multi_hart.rs @@ -10,7 +10,7 @@ extern crate panic_halt; use core::arch::global_asm; use core::sync::atomic::{AtomicBool, Ordering}; -use riscv_rt::entry; +use riscv_rt::{entry, post_init}; use riscv_semihosting::debug::{self, EXIT_SUCCESS}; const UART_BASE: usize = 0x1000_0000; @@ -104,21 +104,24 @@ _mp_hook: "# ); -#[entry] -fn main(hartid: usize) -> ! { +#[post_init] +fn post_init(hartid: usize) { if hartid == 0 { uart_init(); uart_print("Hart 0: Initializing\n"); // Send IPI to Hart 1 (write to CLINT msip register for hart 1) - unsafe { - (0x02000004usize as *mut u32).write_volatile(1); - } + unsafe { (0x02000004usize as *mut u32).write_volatile(1) }; + } +} +#[entry] +fn main(hartid: usize) -> ! { + if hartid == 0 { + // Hart 0 reaches here after initializing UART and sending IPI while !HART1_DONE.load(Ordering::Acquire) { core::hint::spin_loop(); } - uart_print("Hart 0: Both harts done\n"); debug::exit(EXIT_SUCCESS); } else { diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr index 96e6fba5..3090b7cd 100644 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr +++ b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr @@ -7,9 +7,16 @@ error[E0277]: the trait bound `riscv::interrupt::Exception: CoreInterruptNumber` | | the trait `CoreInterruptNumber` is not implemented for `riscv::interrupt::Exception` | required by a bound introduced by this call | - = help: the following other types implement trait `CoreInterruptNumber`: - riscv::interrupt::Interrupt - riscv::interrupt::supervisor::Interrupt +help: the following other types implement trait `CoreInterruptNumber` + --> $WORKSPACE/riscv/src/interrupt/machine.rs + | + | unsafe impl CoreInterruptNumber for Interrupt {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::Interrupt` + | + ::: $WORKSPACE/riscv/src/interrupt/supervisor.rs + | + | unsafe impl CoreInterruptNumber for Interrupt {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::supervisor::Interrupt` note: required by a bound in `assert_impl` --> tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs:1:1 | diff --git a/tests-trybuild/tests/riscv-rt/entry/fail_full.rs b/tests-trybuild/tests/riscv-rt/entry/fail_full.rs new file mode 100644 index 00000000..cddf63cc --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/fail_full.rs @@ -0,0 +1,8 @@ +#[riscv_rt::entry(arg)] +pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) +where + T: Copy, +{ +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr b/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr new file mode 100644 index 00000000..ad047a82 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr @@ -0,0 +1,79 @@ +error: macro arguments are not allowed + --> tests/riscv-rt/entry/fail_full.rs:1:19 + | +1 | #[riscv_rt::entry(arg)] + | ^^^ + +error: function must be private + --> tests/riscv-rt/entry/fail_full.rs:2:1 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^ + +error: function must not be const + --> tests/riscv-rt/entry/fail_full.rs:2:5 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/entry/fail_full.rs:2:11 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/entry/fail_full.rs:2:17 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/entry/fail_full.rs:2:40 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:51 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:60 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:71 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^^ + +error: too many input arguments + --> tests/riscv-rt/entry/fail_full.rs:2:79 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/entry/fail_full.rs:2:90 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: return type must be ! + --> tests/riscv-rt/entry/fail_full.rs:1:1 + | +1 | #[riscv_rt::entry(arg)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `riscv_rt::entry` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: where clause is not allowed + --> tests/riscv-rt/entry/fail_full.rs:3:1 + | +3 | where + | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs new file mode 100644 index 00000000..d0f9f3af --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +fn entry() -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs new file mode 100644 index 00000000..48d3a89d --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +unsafe fn entry() -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs new file mode 100644 index 00000000..0a4174bf --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +fn entry(_a0: usize, _a1: usize, _a2: usize) -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs new file mode 100644 index 00000000..7c7a64bf --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs @@ -0,0 +1,5 @@ +#[riscv_rt::entry] +unsafe fn entry(_a0: usize, _a1: usize, _a2: usize) -> ! { + loop {} +} +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr b/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr index ce4c506b..72925cc7 100644 --- a/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr +++ b/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr @@ -7,9 +7,16 @@ error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExceptionNumber` is | | the trait `ExceptionNumber` is not implemented for `riscv::interrupt::Interrupt` | required by a bound introduced by this call | - = help: the following other types implement trait `ExceptionNumber`: - riscv::interrupt::Exception - riscv::interrupt::supervisor::Exception +help: the following other types implement trait `ExceptionNumber` + --> $WORKSPACE/riscv/src/interrupt/machine.rs + | + | unsafe impl ExceptionNumber for Exception { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::Exception` + | + ::: $WORKSPACE/riscv/src/interrupt/supervisor.rs + | + | unsafe impl ExceptionNumber for Exception { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::supervisor::Exception` note: required by a bound in `assert_impl` --> tests/riscv-rt/exception/fail_impl_exception_number.rs:1:1 | diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs deleted file mode 100644 index 6c94330d..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::post_init] -fn before_main(_hart_id: usize, _dtb: usize) {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr deleted file mode 100644 index 32ac7e78..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[post_init]` function has too many arguments - --> tests/riscv-rt/post_init/fail_arg_count.rs:2:33 - | -2 | fn before_main(_hart_id: usize, _dtb: usize) {} - | ^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs deleted file mode 100644 index fc5894c5..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::post_init] -fn before_main(_hart_id: String) {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr deleted file mode 100644 index 73f4f80c..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument type must be usize - --> tests/riscv-rt/post_init/fail_arg_type.rs:2:26 - | -2 | fn before_main(_hart_id: String) {} - | ^^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr deleted file mode 100644 index 0dff16ff..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[post_init]` function must have signature `[unsafe] fn([usize])` - --> tests/riscv-rt/post_init/fail_async.rs:2:1 - | -2 | async fn before_main() {} - | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs new file mode 100644 index 00000000..dc6fcc00 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs @@ -0,0 +1,8 @@ +#[riscv_rt::post_init(arg)] +pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! +where + T: Copy, +{ +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr new file mode 100644 index 00000000..542f4892 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr @@ -0,0 +1,65 @@ +error: macro arguments are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:1:23 + | +1 | #[riscv_rt::post_init(arg)] + | ^^^ + +error: function must be private + --> tests/riscv-rt/post_init/fail_full.rs:2:1 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^ + +error: function must not be const + --> tests/riscv-rt/post_init/fail_full.rs:2:5 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/post_init/fail_full.rs:2:11 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/post_init/fail_full.rs:2:17 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:2:46 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: argument type must be `usize` + --> tests/riscv-rt/post_init/fail_full.rs:2:57 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^ + +error: too many input arguments + --> tests/riscv-rt/post_init/fail_full.rs:2:62 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:2:73 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: return type must be () + --> tests/riscv-rt/post_init/fail_full.rs:2:85 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^ + +error: where clause is not allowed + --> tests/riscv-rt/post_init/fail_full.rs:3:1 + | +3 | where + | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/pass_empty.rs b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_safe.rs similarity index 100% rename from tests-trybuild/tests/riscv-rt/post_init/pass_empty.rs rename to tests-trybuild/tests/riscv-rt/post_init/pass_empty_safe.rs diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_async.rs b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs similarity index 57% rename from tests-trybuild/tests/riscv-rt/post_init/fail_async.rs rename to tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs index 27c95b93..7539e0b7 100644 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_async.rs +++ b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs @@ -1,4 +1,4 @@ #[riscv_rt::post_init] -async fn before_main() {} +unsafe fn before_main() {} fn main() {}