Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust fix compilation for no_std targets #2 #7553

Merged
merged 6 commits into from Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 5 additions & 6 deletions rust/flatbuffers/Cargo.toml
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
@@ -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")
danlapid marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 1 addition & 1 deletion rust/flatbuffers/src/builder.rs
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
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
@@ -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;
danlapid marked this conversation as resolved.
Show resolved Hide resolved
#[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
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
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
@@ -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
@@ -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
@@ -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
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