blob: 99cfe529b4820f5ebb052841258c36fe194abbe4 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//! This crate is used as a test input for `cc_bindings_from_rs` and the
//! generated C++ bindings are then tested via `structs_test.cc`.
/// Test for a `#[repr(C)` struct.
pub mod repr_c {
#[repr(C)]
pub struct Point {
pub x: i32,
pub y: i32,
}
pub fn create(x: i32, y: i32) -> Point {
Point { x, y }
}
pub fn get_x(p: Point) -> i32 {
p.x
}
}
/// Test for a struct using default layout (i.e. one without an explicit
/// `#[repr(C)]` or similar attribute). Among other things, it tests that
/// building generated `..._cc_api_impl.rs` will not warn about
/// `improper_ctypes_definitions` (search for this warning name in `bindings.rs`
/// for a longer explanation of why suppressing this warning is okay).
pub mod default_repr {
pub struct Point {
pub x: i32,
pub y: i32,
}
pub fn create(x: i32, y: i32) -> Point {
Point { x, y }
}
pub fn get_x(p: Point) -> i32 {
p.x
}
}
/// Test of ABI classification.
///
/// System V ABI can classify function parameter and return types into broad
/// categories like "integer", "sse2", or "memory". Classification impacts how
/// a given value is passed (e.g. by value in `eax` or `xmm0` register, or by
/// pointer). ABI classification of C++ structs generated
/// by `cc_bindings_from_rs` needs to match exactly the classification of the
/// Rust structs in the input crate (e.g. from this test). Mismatched ABI
/// classification will lead to Undefined Behavior.
///
/// This is a regression test for b/270454629 - replacing fields with an opaque
/// blob of bytes (e.g. using `[u8; N]` instead of the actual field type) may
/// change the ABI classification of a struct. The fields of structs below are
/// private (i.e. non-`pub`) to encourage `cc_bindings_from_rs` to treat them as
/// an opaque blob of bytes.
///
/// Optimizing compiler can make the disassembly of the `create` methods quite
/// empty (probably because the input argument uses the same register as the
/// return value. To make the tests more sensitive to ABI choices, the
/// `multiply` method is used (to actually operate on the input arguments and to
/// have to calculate a *new* return value).
pub mod abi_classification {
/// Expected ABI classification: integer. (For indirect confirmation, see
/// the disassembly at https://godbolt.org/z/b7eeGcrGn).
pub struct StructInteger(i32);
/// Expected ABI classification: SSE. (For indirect confirmation, see the
/// disassembly at https://godbolt.org/z/b7eeGcrGn).
pub struct StructFloat(f32);
/// Expected ABI classification: memory. (For indirect confirmation, see
/// the disassembly at https://godbolt.org/z/b7eeGcrGn).
#[repr(packed(1))]
pub struct StructMemory {
_padding: u8,
i: i32,
}
impl StructInteger {
pub fn create(i: i32) -> Self {
Self(i)
}
pub fn multiply(x: Self, y: Self) -> Self {
Self(x.0 * y.0)
}
pub fn inspect(s: Self) -> i32 {
s.0
}
}
impl StructFloat {
pub fn create(f: f32) -> Self {
Self(f)
}
pub fn multiply(x: Self, y: Self) -> Self {
Self(x.0 * y.0)
}
pub fn inspect(s: Self) -> f32 {
s.0
}
}
impl StructMemory {
pub fn create(i: i32) -> Self {
Self { _padding: 0, i }
}
pub fn multiply(x: Self, y: Self) -> Self {
Self::create(x.i * y.i)
}
pub fn inspect(s: Self) -> i32 {
s.i
}
}
}
/// This module provides test coverage for reordering the generated bindings in
/// a way that ensures that C++ structs are defined *before* being referring to
/// them when (say) declaring a function that returns the struct by value, or
/// takes it by value as an argument.
///
/// This module has been structured in a way that forces at least one submodule
/// to be broken up into 2 separate chunks. Definition dependencies force
/// bindings from one of the structs to come first - let's assume that `m1::S1`
/// comes first (the case where `m2::S2` comes first is symmetrical -
/// all the same conclusions apply). Before `m1::create_S2` can be declared,
/// `m1::S2` needs to be defined. This means that the order will be: `m1::S1`,
/// ..., `m2::S2`, ..., `m1::create_S2` - the `m1` module has to be split into
/// two non-contiguous chunks (in the generated bindings):
///
/// ```cpp
/// namespace m1 { // <- FIRST CHUNK OF `mod m1`
/// struct S1 { ... };
/// }
///
/// namespace m2 {
/// struct S2 { ... };
/// }
///
/// namespace m1 { // <- SECOND CHUNK OF `mod m1`
/// S2 create_s2();
/// }
/// ```
pub mod reordering_defs {
pub mod m1 {
use super::m2::S2;
pub struct S1(pub i32);
pub fn create_s2() -> S2 {
S2(123)
}
pub fn get_int_from_s2(s2: S2) -> i32 {
s2.0
}
}
pub mod m2 {
use super::m1::S1;
pub struct S2(pub i32);
pub fn create_s1() -> S1 {
S1(456)
}
pub fn get_int_from_s1(s1: S1) -> i32 {
s1.0
}
}
}
/// This module provides coverage for emitting forward declarations. In
/// particular, if we assume that the C++ bindings are emitted in the same order
/// as the Rust items below, then `S1` needs to be forward-declared (because
/// `get_int_from_s1` is *before* `S1`).
///
/// TODO(b/260725687): Using a cycle below should avoid the assumption above
/// about preserving the same order (because a cycle can't be
/// toposorted/reordered). OTOH forming a cycle seems to depend on supporting
/// bindings for additional language features - either static methods
/// (b/260725279):
/// ```
/// // Cycle!:
/// pub struct S1(i32);
/// pub struct S2(i32);
/// impl S1 {
/// pub fn get_int_from_s2(s2: *const S2) { ... }
/// }
/// impl S2 {
/// pub fn get_int_from_s1(s1: *const S1) { ... }
/// }
/// ```
/// or fields (b/258233850):
/// ```
/// // Cycle!:
/// pub struct S1 {
/// ptr_to_s2: *const S2,
/// }
/// pub struct S2 {
/// ptr_to_s1: *const S1,
/// }
/// ```
pub mod fwd_decls {
pub fn get_int_from_s1(s1: *const S1) -> i32 {
#![allow(clippy::not_unsafe_ptr_arg_deref)]
unsafe { (*s1).0 }
}
pub fn create_s1() -> S1 {
S1(456)
}
pub struct S1(i32);
}