diff --git a/rust/flatbuffers/Cargo.toml b/rust/flatbuffers/Cargo.toml index 9e4d8d11216..821c5aec749 100644 --- a/rust/flatbuffers/Cargo.toml +++ b/rust/flatbuffers/Cargo.toml @@ -12,14 +12,13 @@ categories = ["encoding", "data-structures", "memory-management"] rust = "1.51" [features] -default = ["thiserror"] -no_std = ["core2", "thiserror_core2"] +default = ["std"] +std = [] serialize = ["serde"] [dependencies] bitflags = "1.2.1" serde = { version = "1.0", optional = true } -thiserror = { version = "1.0.30", optional = true } -core2 = { version = "0.4.0", optional = true } -# This version is compliant with mainline 1.0.30 -thiserror_core2 = { version = "2.0.0", default-features = false, optional = true } + +[build-dependencies] +rustc_version = "0.4.0" diff --git a/rust/flatbuffers/build.rs b/rust/flatbuffers/build.rs new file mode 100644 index 00000000000..c13ed1da485 --- /dev/null +++ b/rust/flatbuffers/build.rs @@ -0,0 +1,12 @@ +use rustc_version::{version_meta, Channel}; + +fn main() { + let version_meta = version_meta().unwrap(); + + // To use nightly features we declare this and then we can use + // #[cfg(nightly)] + // for nightly only features + if version_meta.channel == Channel::Nightly { + println!("cargo:rustc-cfg=nightly") + } +} diff --git a/rust/flatbuffers/src/builder.rs b/rust/flatbuffers/src/builder.rs index d0e42235cda..7d0f408ba1f 100644 --- a/rust/flatbuffers/src/builder.rs +++ b/rust/flatbuffers/src/builder.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -#[cfg(feature = "no_std")] +#[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; use core::cmp::max; use core::iter::{DoubleEndedIterator, ExactSizeIterator}; diff --git a/rust/flatbuffers/src/lib.rs b/rust/flatbuffers/src/lib.rs index e28d3be9007..deb8ff740de 100644 --- a/rust/flatbuffers/src/lib.rs +++ b/rust/flatbuffers/src/lib.rs @@ -28,9 +28,10 @@ //! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: //! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.) -#![cfg_attr(feature = "no_std", no_std)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(nightly, not(feature = "std")), feature(error_in_core))] -#[cfg(feature = "no_std")] +#[cfg(not(feature = "std"))] extern crate alloc; mod array; diff --git a/rust/flatbuffers/src/verifier.rs b/rust/flatbuffers/src/verifier.rs index ff84baa7f15..047d4f61360 100644 --- a/rust/flatbuffers/src/verifier.rs +++ b/rust/flatbuffers/src/verifier.rs @@ -1,14 +1,14 @@ use crate::follow::Follow; use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET}; -#[cfg(feature = "no_std")] +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ops::Range; use core::option::Option; -#[cfg(not(feature = "no_std"))] -use thiserror::Error; -#[cfg(feature = "no_std")] -use thiserror_core2::Error; +#[cfg(all(nightly, not(feature = "std")))] +use core::error::Error; +#[cfg(feature = "std")] +use std::error::Error; /// Traces the location of data errors. Not populated for Dos detecting errors. /// Useful for MissingRequiredField and Utf8Error in particular, though @@ -41,64 +41,138 @@ impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace { /// Describes how a flatuffer is invalid and, for data errors, roughly where. No extra tracing /// information is given for DoS detecting errors since it will probably be a lot. -#[derive(Clone, Error, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum InvalidFlatbuffer { - #[error("Missing required field `{required}`.\n{error_trace}")] MissingRequiredField { required: &'static str, error_trace: ErrorTrace, }, - #[error( - "Union exactly one of union discriminant (`{field_type}`) and value \ - (`{field}`) are present.\n{error_trace}" - )] InconsistentUnion { field: &'static str, field_type: &'static str, error_trace: ErrorTrace, }, - #[error("Utf8 error for string in {range:?}: {error}\n{error_trace}")] Utf8Error { - #[source] error: core::str::Utf8Error, range: Range, error_trace: ErrorTrace, }, - #[error("String in range [{}, {}) is missing its null terminator.\n{error_trace}", - range.start, range.end)] MissingNullTerminator { range: Range, error_trace: ErrorTrace, }, - #[error("Type `{unaligned_type}` at position {position} is unaligned.\n{error_trace}")] Unaligned { position: usize, unaligned_type: &'static str, error_trace: ErrorTrace, }, - #[error("Range [{}, {}) is out of bounds.\n{error_trace}", range.start, range.end)] RangeOutOfBounds { range: Range, error_trace: ErrorTrace, }, - #[error( - "Signed offset at position {position} has value {soffset} which points out of bounds.\ - \n{error_trace}" - )] SignedOffsetOutOfBounds { soffset: SOffsetT, position: usize, error_trace: ErrorTrace, }, // Dos detecting errors. These do not get error traces since it will probably be very large. - #[error("Too many tables.")] TooManyTables, - #[error("Apparent size too large.")] ApparentSizeTooLarge, - #[error("Nested table depth limit reached.")] DepthLimitReached, } +#[cfg(any(nightly, feature = "std"))] +impl Error for InvalidFlatbuffer { + fn source(&self) -> Option<&(dyn Error + 'static)> { + if let InvalidFlatbuffer::Utf8Error { error: source, .. } = self { + Some(source) + } else { + None + } + } +} + +impl core::fmt::Display for InvalidFlatbuffer { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + InvalidFlatbuffer::MissingRequiredField { + required, + error_trace, + } => { + writeln!(f, "Missing required field `{}`.\n{}", required, error_trace)?; + } + InvalidFlatbuffer::InconsistentUnion { + field, + field_type, + error_trace, + } => { + writeln!( + f, + "Exactly one of union discriminant (`{}`) and value (`{}`) are present.\n{}", + field_type, field, error_trace + )?; + } + InvalidFlatbuffer::Utf8Error { + error, + range, + error_trace, + } => { + writeln!( + f, + "Utf8 error for string in {:?}: {}\n{}", + range, error, error_trace + )?; + } + InvalidFlatbuffer::MissingNullTerminator { range, error_trace } => { + writeln!( + f, + "String in range [{}, {}) is missing its null terminator.\n{}", + range.start, range.end, error_trace + )?; + } + InvalidFlatbuffer::Unaligned { + position, + unaligned_type, + error_trace, + } => { + writeln!( + f, + "Type `{}` at position {} is unaligned.\n{}", + unaligned_type, position, error_trace + )?; + } + InvalidFlatbuffer::RangeOutOfBounds { range, error_trace } => { + writeln!( + f, + "Range [{}, {}) is out of bounds.\n{}", + range.start, range.end, error_trace + )?; + } + InvalidFlatbuffer::SignedOffsetOutOfBounds { + soffset, + position, + error_trace, + } => { + writeln!( + f, + "Signed offset at position {} has value {} which points out of bounds.\n{}", + position, soffset, error_trace + )?; + } + InvalidFlatbuffer::TooManyTables {} => { + writeln!(f, "Too many tables.")?; + } + InvalidFlatbuffer::ApparentSizeTooLarge {} => { + writeln!(f, "Apparent size too large.")?; + } + InvalidFlatbuffer::DepthLimitReached {} => { + writeln!(f, "Nested table depth limit reached.")?; + } + } + Ok(()) + } +} + impl core::fmt::Display for ErrorTrace { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { use ErrorTraceDetail::*; diff --git a/tests/RustTest.bat b/tests/RustTest.bat index e897534cf20..236cd9d7f5b 100644 --- a/tests/RustTest.bat +++ b/tests/RustTest.bat @@ -21,3 +21,10 @@ cargo run --bin=flatbuffers_alloc_check || exit /b 1 cargo run --bin=flexbuffers_alloc_check || exit /b 1 cargo run --bin=monster_example || exit /b 1 cd .. + +cd rust_no_std_compilation_test +rustup install nightly +rustup component add rust-src --toolchain nightly +rustup target add thumbv7m-none-eabi +cargo build || exit /b 1 +cd .. diff --git a/tests/RustTest.sh b/tests/RustTest.sh index c127221c627..1e7d0eed9d4 100755 --- a/tests/RustTest.sh +++ b/tests/RustTest.sh @@ -35,11 +35,18 @@ cd ./rust_serialize_test cargo run $TARGET_FLAG -- --quiet check_test_result "Rust serde tests" +cd ../rust_no_std_compilation_test +rustup install nightly +rustup component add rust-src --toolchain nightly +rustup target add thumbv7m-none-eabi +cargo +nightly build +check_test_result "Rust flatbuffers test no_std compilation" + cd ../rust_usage_test cargo test $TARGET_FLAG -- --quiet check_test_result "Rust tests" -cargo test $TARGET_FLAG --no-default-features --features no_std -- --quiet +cargo test $TARGET_FLAG --no-default-features -- --quiet check_test_result "Rust tests (no_std)" cargo run $TARGET_FLAG --bin=flatbuffers_alloc_check diff --git a/tests/rust_no_std_compilation_test/.cargo/config.toml b/tests/rust_no_std_compilation_test/.cargo/config.toml new file mode 100644 index 00000000000..4fc6b9a91e2 --- /dev/null +++ b/tests/rust_no_std_compilation_test/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "thumbv7m-none-eabi" + +[unstable] +build-std = ["core", "alloc"] diff --git a/tests/rust_no_std_compilation_test/Cargo.toml b/tests/rust_no_std_compilation_test/Cargo.toml new file mode 100644 index 00000000000..86e650a26d4 --- /dev/null +++ b/tests/rust_no_std_compilation_test/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust_test_no_std_compilation" +version = "0.1.0" +edition = "2021" + +[dependencies] +flatbuffers = { path = "../../rust/flatbuffers", default-features = false } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/tests/rust_no_std_compilation_test/src/main.rs b/tests/rust_no_std_compilation_test/src/main.rs new file mode 100644 index 00000000000..0430599dbe8 --- /dev/null +++ b/tests/rust_no_std_compilation_test/src/main.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(default_alloc_error_handler)] + +// Include flatbuffers purely to check that it compiles in a no_std binary +#[allow(unused_imports)] +use flatbuffers; + +// The rest is just no_std boilerplate + +use core::alloc::{GlobalAlloc, Layout}; + +struct NullAllocator; +unsafe impl GlobalAlloc for NullAllocator { + unsafe fn alloc(&self, _lt: Layout) -> *mut u8 { + core::ptr::null_mut() + } + unsafe fn dealloc(&self, _ptr: *mut u8, _lt: Layout) { + panic!("won't deallocate: we never allocated!"); + } +} + +#[global_allocator] +static A: NullAllocator = NullAllocator; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + 0 +} diff --git a/tests/rust_usage_test/Cargo.toml b/tests/rust_usage_test/Cargo.toml index d5731e7d208..5b7152b9c59 100644 --- a/tests/rust_usage_test/Cargo.toml +++ b/tests/rust_usage_test/Cargo.toml @@ -16,7 +16,7 @@ libc_alloc = { version = "1.0.3", optional = true } [features] default = ["flatbuffers/default"] -no_std = ["flatbuffers/no_std", "libc_alloc"] +no_std = ["libc_alloc"] [[bin]] name = "monster_example"