Skip to content

Commit

Permalink
Rust fix compilation for no_std targets #2 (#7553)
Browse files Browse the repository at this point in the history
* Fix nightly no_std

* Fix nightly no_std
  • Loading branch information
danlapid authored Oct 19, 2022
1 parent 0edb275 commit 5792623
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 35 deletions.
11 changes: 5 additions & 6 deletions rust/flatbuffers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
12 changes: 12 additions & 0 deletions rust/flatbuffers/build.rs
Original file line number Diff line number Diff line change
@@ -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")
}
}
2 changes: 1 addition & 1 deletion rust/flatbuffers/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
5 changes: 3 additions & 2 deletions rust/flatbuffers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: <https://github.com/google/flatbuffers>
//! (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;
Expand Down
122 changes: 98 additions & 24 deletions rust/flatbuffers/src/verifier.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<usize>,
error_trace: ErrorTrace,
},
#[error("String in range [{}, {}) is missing its null terminator.\n{error_trace}",
range.start, range.end)]
MissingNullTerminator {
range: Range<usize>,
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<usize>,
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::*;
Expand Down
7 changes: 7 additions & 0 deletions tests/RustTest.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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 ..
9 changes: 8 additions & 1 deletion tests/RustTest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions tests/rust_no_std_compilation_test/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[build]
target = "thumbv7m-none-eabi"

[unstable]
build-std = ["core", "alloc"]
13 changes: 13 additions & 0 deletions tests/rust_no_std_compilation_test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
34 changes: 34 additions & 0 deletions tests/rust_no_std_compilation_test/src/main.rs
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion tests/rust_usage_test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 5792623

Please sign in to comment.