Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ quote.workspace = true
syn.workspace = true
crubit_feature = { path = "../../../../cargo/common/crubit_feature"}
flagset.workspace = true
heck.workspace = true
rustversion.workspace = true
1 change: 1 addition & 0 deletions cc_bindings_from_rs/generate_bindings/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ rust_library(
"//common:dyn_format",
"//common:error_report",
"@crate_index//:flagset", # v0_4
"@crate_index//:heck", # v0_5
"@crate_index//:itertools", # v0_13
"@crate_index//:proc-macro2",
"@crate_index//:quote", # v1
Expand Down
66 changes: 56 additions & 10 deletions cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

extern crate rustc_abi;
extern crate rustc_apfloat;
extern crate rustc_hir;
extern crate rustc_middle;
extern crate rustc_span;
Expand Down Expand Up @@ -104,7 +105,7 @@ fn cpp_enum_cpp_underlying_type(db: &dyn BindingsGenerator, def_id: DefId) -> Re
}

/// Returns a string representation of the value of a given numeric Scalar having a given TyKind.
pub fn scalar_value_to_string(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Result<String> {
pub fn scalar_value_to_snippet(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Result<CcSnippet> {
let scalar = match scalar {
Scalar::Int(i) => i,
Scalar::Ptr(..) => bail!("Pointer values cannot be used as scalar constants."),
Expand All @@ -115,7 +116,7 @@ pub fn scalar_value_to_string(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Resu
if matches!(kind, TyKind::Uint(_)) {
let value: u128 = scalar.to_bits_unchecked();
if value < (i16::MAX as u128) {
return Ok((value as i16).to_string());
return Ok(CcSnippet::new((value as i16).to_string().parse::<TokenStream>().unwrap()));
}
}

Expand All @@ -124,7 +125,7 @@ pub fn scalar_value_to_string(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Resu
use ty::TyKind;
use ty::UintTy::*;

Ok(match kind {
let string_value = match kind {
TyKind::Bool => scalar.try_to_bool().unwrap().to_string(),
TyKind::Int(I8) => scalar.to_i8().to_string(),
TyKind::Int(I16) => scalar.to_i16().to_string(),
Expand All @@ -133,8 +134,52 @@ pub fn scalar_value_to_string(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Resu
TyKind::Uint(U16) => format!("UINT16_C({})", scalar.to_u16()),
TyKind::Uint(U32) => format!("UINT32_C({})", scalar.to_u32()),
TyKind::Uint(U64) => format!("UINT64_C({})", scalar.to_u64()),
TyKind::Float(F32) => format!("{}f", scalar.to_f32()),
TyKind::Float(F64) => format!("{}L", scalar.to_f64()),
TyKind::Float(F32) => {
use rustc_apfloat::Float;
let value = scalar.to_f32();
if value.is_pos_infinity() {
return Ok(CcSnippet::with_include(
quote! { std::numeric_limits<float>::infinity() },
CcInclude::limits(),
));
}
if value.is_neg_infinity() {
return Ok(CcSnippet::with_include(
quote! { -std::numeric_limits<float>::infinity() },
CcInclude::limits(),
));
}
if value.is_nan() {
return Ok(CcSnippet::with_include(
quote! { std::numeric_limits<float>::quiet_NaN() },
CcInclude::limits(),
));
}
format!("{}f", value)
}
TyKind::Float(F64) => {
use rustc_apfloat::Float;
let value = scalar.to_f64();
if value.is_pos_infinity() {
return Ok(CcSnippet::with_include(
quote! { std::numeric_limits<double>::infinity() },
CcInclude::limits(),
));
}
if value.is_neg_infinity() {
return Ok(CcSnippet::with_include(
quote! { -std::numeric_limits<double>::infinity() },
CcInclude::limits(),
));
}
if value.is_nan() {
return Ok(CcSnippet::with_include(
quote! { std::numeric_limits<double>::quiet_NaN() },
CcInclude::limits(),
));
}
format!("{}L", value)
}
TyKind::Uint(Usize) => format!("UINT64_C({})", scalar.to_target_usize(tcx)),

// Signed integer minimums cannot be expressed with literals, as `-<int>` parses as a unary
Expand All @@ -156,7 +201,10 @@ pub fn scalar_value_to_string(tcx: TyCtxt, scalar: Scalar, kind: TyKind) -> Resu
}
}
_ => bail!("Unsupported constant type: {:?}", kind),
})
};

let tokens = string_value.parse::<TokenStream>().unwrap();
Ok(CcSnippet::new(tokens))
}

/// Formats a struct that is annotated with the `cpp_enum` attribute.
Expand Down Expand Up @@ -231,10 +279,8 @@ fn generate_cpp_enum<'tcx>(
panic!("Unexpected non-scalar ConstValue type in cpp_enum: {other:?}")
}
};
let enumerator_value = scalar_value_to_string(tcx, scalar, value_kind)
.unwrap()
.parse::<TokenStream>()
.unwrap();
let enumerator_value = scalar_value_to_snippet(tcx, scalar, value_kind).unwrap();
let enumerator_value = enumerator_value.into_tokens(&mut main_api_prereqs);

Some(quote! { #enumerator_name = #enumerator_value, })
})
Expand Down
51 changes: 33 additions & 18 deletions cc_bindings_from_rs/generate_bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ use crate::format_type::{
use crate::generate_function::{generate_function, must_use_attr_of};
use crate::generate_function_thunk::{generate_trait_thunks, TraitThunks};
use crate::generate_struct_and_union::{
from_trait_impls_by_argument, generate_adt, generate_adt_core, scalar_value_to_string,
from_trait_impls_by_argument, generate_adt, generate_adt_core, scalar_value_to_snippet,
};
use arc_anyhow::{Context, Error, Result};
use code_gen_utils::{format_cc_includes, CcConstQualifier, CcInclude, NamespaceQualifier};
use code_gen_utils::{
format_cc_includes, format_cc_type_name, CcConstQualifier, CcInclude, NamespaceQualifier,
};
use database::code_snippet::{ApiSnippets, CcPrerequisites, CcSnippet, ExternCDecl, RsSnippet};
use database::{
AdtCoreBindings, BindingsGenerator, ExportedPath, FineGrainedFeature, FullyQualifiedName,
Expand Down Expand Up @@ -447,14 +449,21 @@ fn symbol_unqualified_name(
let attributes = crubit_attr::get_attrs(tcx, def_id)
.unwrap_or_else(|_| panic!("Expected crubit_attrs on {def_id:?}"));
let cpp_name = attributes.cpp_name.map(|s| Symbol::intern(s.as_str())).unwrap_or_else(|| {
// If the rs_name is going to be used for the cpp_name, then we need to unkeyword it.
// This prevents silly Rust names like "reinterpret_cast" from trying to be named
// "reinterpret_cast" in C++, which would be an error.
// If the user has opted in to one of these reserved names by setting cpp_name, however,
// we should _not_ implicitly change it, and should instead given them an error.
// Hence, this unkeywording behavior only happens in the case where we implicitly
// delegate to the Rust name.
Symbol::intern(code_gen_utils::unkeyword_cpp_ident(rs_name.as_str()).as_ref())
match tcx.def_kind(def_id) {
DefKind::Const | DefKind::AssocConst => {
use heck::ToUpperCamelCase;
// Because we prepend a k here we can be confident this won't be a keyword
Symbol::intern(format!("k{}", rs_name.as_str().to_upper_camel_case()).as_str())
}
// If the rs_name is going to be used for the cpp_name, then we need to unkeyword it.
// This prevents silly Rust names like "reinterpret_cast" from trying to be named
// "reinterpret_cast" in C++, which would be an error.
// If the user has opted in to one of these reserved names by setting cpp_name, however,
// we should _not_ implicitly change it, and should instead given them an error.
// Hence, this unkeywording behavior only happens in the case where we implicitly
// delegate to the Rust name.
_ => Symbol::intern(code_gen_utils::unkeyword_cpp_ident(rs_name.as_str()).as_ref()),
}
});
let cpp_type = attributes.cpp_type;
Some(UnqualifiedName { cpp_name, rs_name, cpp_type })
Expand Down Expand Up @@ -690,16 +699,20 @@ fn generate_const(db: &dyn BindingsGenerator<'_>, def_id: DefId) -> Result<ApiSn
}
});
let rust_type = SugaredTy::new(ty, hir_ty);
let cc_type_snippet = db.format_ty_for_cc(rust_type, TypeLocation::Const)?;
let mut prereqs = CcPrerequisites::default();
let unqualified = db.symbol_unqualified_name(def_id).expect("Expected constant to have a name");
let cc_type = match unqualified.cpp_type {
Some(cpp_type) => format_cc_type_name(cpp_type.as_str())?,
None => db.format_ty_for_cc(rust_type, TypeLocation::Const)?.into_tokens(&mut prereqs),
};

let cc_type = cc_type_snippet.tokens;
let cc_name = format_cc_ident(db, tcx.item_name(def_id).as_str())?;
let cc_name = format_cc_ident(db, unqualified.cpp_name.as_str())?;

// Note that `&str` constants may appear as either `ConstValue::Slice` or
// `ConstValue::Indirect`.
let const_value: ConstValue = tcx.const_eval_poly(def_id).unwrap();
let cc_value = match const_value {
ConstValue::Scalar(scalar) => scalar_value_to_string(tcx, scalar, *ty.kind()),
ConstValue::Scalar(scalar) => scalar_value_to_snippet(tcx, scalar, *ty.kind()),
ConstValue::ZeroSized => bail!("const of type `{rust_type}` cannot be generated as zero-sized consts are not supported in C++."),
ConstValue::Slice { .. } | ConstValue::Indirect { .. } => {
let string_literal = match ty.kind() {
Expand All @@ -719,17 +732,19 @@ fn generate_const(db: &dyn BindingsGenerator<'_>, def_id: DefId) -> Result<ApiSn
string_literal.ok_or_else(|| {
anyhow!("const of type `{rust_type}` cannot be generated as only scalar consts are supported.")
})
.map(|string_literal| {
CcSnippet::new(string_literal.parse::<TokenStream>().unwrap())
})
}
}?
.parse::<TokenStream>()
.unwrap();
}?;
let cc_value = cc_value.into_tokens(&mut prereqs);

Ok(ApiSnippets {
main_api: CcSnippet {
tokens: quote! {
static constexpr #cc_type #cc_name = #cc_value;
},
..cc_type_snippet
prereqs,
},
cc_details: CcSnippet::default(),
rs_details: RsSnippet::default(),
Expand Down
72 changes: 36 additions & 36 deletions cc_bindings_from_rs/test/consts/consts_cc_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,41 @@
#include <type_traits>

namespace consts {
static constexpr bool RUST_TRUE = true;
static constexpr bool RUST_FALSE = false;
static constexpr std::int8_t RUST_INT8_MIN = -128;
static constexpr std::int8_t RUST_INT8_MAX = 127;
static constexpr std::int16_t RUST_INT16_MIN = -32768;
static constexpr std::int16_t RUST_INT16_MAX = 32767;
static constexpr std::int32_t RUST_INT32_MIN = INT32_C(-2147483648);
static constexpr std::int32_t RUST_INT32_MAX = INT32_C(2147483647);
static constexpr std::int64_t RUST_INT64_MIN = INT64_MIN;
static constexpr std::int64_t RUST_INT64_MAX = INT64_C(9223372036854775807);
static constexpr std::uint8_t RUST_UINT8_MIN = 0;
static constexpr std::uint8_t RUST_UINT8_MAX = 255;
static constexpr std::uint16_t RUST_UINT16_MIN = 0;
static constexpr std::uint16_t RUST_UINT16_MAX = UINT16_C(65535);
static constexpr std::uint32_t RUST_UINT32_MIN = 0;
static constexpr std::uint32_t RUST_UINT32_MAX = UINT32_C(4294967295);
static constexpr std::uint64_t RUST_UINT64_MIN = 0;
static constexpr std::uint64_t RUST_UINT64_MAX = UINT64_C(18446744073709551615);
static constexpr std::intptr_t RUST_ISIZE_MIN = INT64_MIN;
static constexpr std::intptr_t RUST_ISIZE_MAX = INT64_C(9223372036854775807);
static constexpr std::intptr_t RUST_USIZE_MIN = INT64_MIN;
static constexpr std::intptr_t RUST_USIZE_MAX = INT64_C(9223372036854775807);
static constexpr float RUST_F32_MIN = -3.40282347E+38f;
static constexpr float RUST_F32_MAX = 3.40282347E+38f;
static constexpr double RUST_F64_MIN = -1.7976931348623157E+308L;
static constexpr double RUST_F64_MAX = 1.7976931348623157E+308L;
static constexpr std::int32_t INT_POS = INT32_C(42);
static constexpr std::int32_t INT_NEG = INT32_C(-17);
static constexpr float FLOAT_32 = 0.125f;
static constexpr double FLOAT_64 = 0.0078125L;
static constexpr std::int64_t LARGE_INT = INT64_C(9223372036854775807);
static constexpr std::uint32_t UNSIGNED_INT = UINT32_C(4294967295);
static constexpr std::uintptr_t SLICE_LENGTH = 11;
static constexpr std::intptr_t ISIZE = INT64_C(42);
static constexpr char CHAR = 42;
static constexpr bool kRustTrue = true;
static constexpr bool kRustFalse = false;
static constexpr std::int8_t kRustInt8Min = -128;
static constexpr std::int8_t kRustInt8Max = 127;
static constexpr std::int16_t kRustInt16Min = -32768;
static constexpr std::int16_t kRustInt16Max = 32767;
static constexpr std::int32_t kRustInt32Min = INT32_C(-2147483648);
static constexpr std::int32_t kRustInt32Max = INT32_C(2147483647);
static constexpr std::int64_t kRustInt64Min = INT64_MIN;
static constexpr std::int64_t kRustInt64Max = INT64_C(9223372036854775807);
static constexpr std::uint8_t kRustUint8Min = 0;
static constexpr std::uint8_t kRustUint8Max = 255;
static constexpr std::uint16_t kRustUint16Min = 0;
static constexpr std::uint16_t kRustUint16Max = UINT16_C(65535);
static constexpr std::uint32_t kRustUint32Min = 0;
static constexpr std::uint32_t kRustUint32Max = UINT32_C(4294967295);
static constexpr std::uint64_t kRustUint64Min = 0;
static constexpr std::uint64_t kRustUint64Max = UINT64_C(18446744073709551615);
static constexpr std::intptr_t kRustIsizeMin = INT64_MIN;
static constexpr std::intptr_t kRustIsizeMax = INT64_C(9223372036854775807);
static constexpr std::intptr_t kRustUsizeMin = INT64_MIN;
static constexpr std::intptr_t kRustUsizeMax = INT64_C(9223372036854775807);
static constexpr float kRustF32Min = -3.40282347E+38f;
static constexpr float kRustF32Max = 3.40282347E+38f;
static constexpr double kRustF64Min = -1.7976931348623157E+308L;
static constexpr double kRustF64Max = 1.7976931348623157E+308L;
static constexpr std::int32_t kIntPos = INT32_C(42);
static constexpr std::int32_t kIntNeg = INT32_C(-17);
static constexpr float kFloat32 = 0.125f;
static constexpr double kFloat64 = 0.0078125L;
static constexpr std::int64_t kLargeInt = INT64_C(9223372036854775807);
static constexpr std::uint32_t kUnsignedInt = UINT32_C(4294967295);
static constexpr std::uintptr_t kSliceLength = 11;
static constexpr std::intptr_t kIsize = INT64_C(42);
static constexpr char kChar = 42;
// Generated from:
// cc_bindings_from_rs/test/consts/consts.rs;l=49
struct CRUBIT_INTERNAL_RUST_TYPE(
Expand All @@ -74,7 +74,7 @@ TyWithAssocConsts final {
TyWithAssocConsts(::crubit::UnsafeRelocateTag, TyWithAssocConsts&& value) {
memcpy(this, &value, sizeof(value));
}
static constexpr std::int32_t ASSOC_42 = INT32_C(42);
static constexpr std::int32_t kAssoc42 = INT32_C(42);

private:
union {
Expand Down
42 changes: 21 additions & 21 deletions cc_bindings_from_rs/test/consts/consts_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@
namespace {

TEST(ConstsTest, AllAreExpected) {
static_assert(consts::RUST_TRUE);
static_assert(!consts::RUST_FALSE);
static_assert(consts::RUST_INT8_MIN == INT8_MIN);
static_assert(consts::RUST_INT8_MAX == INT8_MAX);
static_assert(consts::RUST_INT16_MIN == INT16_MIN);
static_assert(consts::RUST_INT16_MAX == INT16_MAX);
static_assert(consts::RUST_INT32_MIN == INT32_MIN);
static_assert(consts::RUST_INT32_MAX == INT32_MAX);
static_assert(consts::RUST_INT64_MIN == INT64_MIN);
static_assert(consts::RUST_INT64_MAX == INT64_MAX);
static_assert(consts::RUST_UINT8_MIN == 0);
static_assert(consts::RUST_UINT8_MAX == UINT8_MAX);
static_assert(consts::RUST_UINT16_MIN == 0);
static_assert(consts::RUST_UINT16_MAX == UINT16_MAX);
static_assert(consts::RUST_UINT32_MIN == 0);
static_assert(consts::RUST_UINT32_MAX == UINT32_MAX);
static_assert(consts::RUST_UINT64_MIN == 0);
static_assert(consts::RUST_UINT64_MAX == UINT64_MAX);
static_assert(consts::kRustTrue);
static_assert(!consts::kRustFalse);
static_assert(consts::kRustInt8Min == INT8_MIN);
static_assert(consts::kRustInt8Max == INT8_MAX);
static_assert(consts::kRustInt16Min == INT16_MIN);
static_assert(consts::kRustInt16Max == INT16_MAX);
static_assert(consts::kRustInt32Min == INT32_MIN);
static_assert(consts::kRustInt32Max == INT32_MAX);
static_assert(consts::kRustInt64Min == INT64_MIN);
static_assert(consts::kRustInt64Max == INT64_MAX);
static_assert(consts::kRustUint8Min == 0);
static_assert(consts::kRustUint8Max == UINT8_MAX);
static_assert(consts::kRustUint16Min == 0);
static_assert(consts::kRustUint16Max == UINT16_MAX);
static_assert(consts::kRustUint32Min == 0);
static_assert(consts::kRustUint32Max == UINT32_MAX);
static_assert(consts::kRustUint64Min == 0);
static_assert(consts::kRustUint64Max == UINT64_MAX);
// NOTE: FLT_MIN and DBL_MIN in C++ are not the furthest negative numbers,
// they're the smallest positive values. There are no standard constants for
// the furthest negative floating-point values, so no assertion is made here.
static_assert(consts::RUST_F32_MAX == FLT_MAX);
static_assert(consts::RUST_F64_MAX == DBL_MAX);
static_assert(consts::kRustF32Max == FLT_MAX);
static_assert(consts::kRustF64Max == DBL_MAX);

static_assert(consts::TyWithAssocConsts::ASSOC_42 == 42);
static_assert(consts::TyWithAssocConsts::kAssoc42 == 42);
}

} // namespace
2 changes: 1 addition & 1 deletion cc_bindings_from_rs/test/str/str_cc_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ std::uint8_t const* get_str_data(rs_std::StrRef s);
// Generated from:
// cc_bindings_from_rs/test/str/str.rs;l=37
rs_std::StrRef foo_as_str();
static constexpr rs_std::StrRef CONST_STR_FOO = rs_std::StrRef("foo");
static constexpr rs_std::StrRef kConstStrFoo = rs_std::StrRef("foo");

// Error generating bindings for `STATIC_STR_FOO` defined at
// cc_bindings_from_rs/test/str/str.rs;l=43:
Expand Down
2 changes: 1 addition & 1 deletion cc_bindings_from_rs/test/str/str_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ TEST(StrTest, StrAsPotentiallyAliasingArgument) {
str::str_checked_as_potentially_aliasing(StrRef("hello"), x);
}

constexpr static StrRef kConst = str::CONST_STR_FOO;
constexpr static StrRef kConst = str::kConstStrFoo;
static_assert(kConst == "foo");
static_assert(kConst != "bar");

Expand Down
7 changes: 7 additions & 0 deletions common/code_gen_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,13 @@ impl CcInclude {
Self::SystemHeader("type_traits".into())
}

/// Creates a `CcInclude` that represents `#include <limits>` and
/// provides C++ APIs like `std::numeric_limits`.
/// See https://en.cppreference.com/w/cpp/header/limits
pub fn limits() -> Self {
Self::SystemHeader("limits".into())
}

/// Creates a user include: `#include "some/path/to/header.h"`.
pub fn user_header(path: Rc<str>) -> Self {
Self::UserHeader(path)
Expand Down