Translate Rust `const` items to C++ `constexpr` for simple types.
Translates simple types `uN`, `iN`, `f32`, `f64`, `{u,i}size`.
PiperOrigin-RevId: 665522031
Change-Id: I1c7c3a55cd2aede085af2e4ba9bb14350ba5f148
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 52802f7..a0da3b3 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -27,12 +27,14 @@
use rustc_hir::{AssocItemKind, HirId, Item, ItemKind, Node, Safety, UseKind, UsePath};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::dep_graph::DepContext;
+use rustc_middle::mir::interpret::InterpResult;
+use rustc_middle::mir::ConstValue;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions
use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::{
- Abi, AddressSpace, FieldsShape, Integer, Layout, Pointer, Primitive, Scalar,
+ Abi, AddressSpace, FieldsShape, HasDataLayout, Integer, Layout, Pointer, Primitive, Scalar,
};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_type_ir::RegionKind;
@@ -1420,6 +1422,109 @@
}
}
+fn format_const_value_to_cc(
+ cx: &impl HasDataLayout,
+ kind: &ty::TyKind,
+ value: &ConstValue,
+) -> Result<String> {
+ macro_rules! convert {
+ ($kind:expr, $value:expr, $(($rust_type:pat, $to_method:ident)),*) => {
+ match $kind {
+ $(
+ $rust_type => {
+ Ok(match $value {
+ ConstValue::Scalar(scalar) => {
+ if let InterpResult::Ok(value) = scalar.$to_method() {
+ value.to_string()
+ } else {
+ panic!("Failed to evaluate constant scalar.")
+ }
+ },
+ _ => panic!("Unexpected ConstValue type."),
+ })
+ },
+ )*
+ ty::TyKind::Uint(ty::UintTy::Usize) => {
+ Ok(match value {
+ ConstValue::Scalar(scalar) => {
+ if let InterpResult::Ok(value) = scalar.to_target_usize(cx) {
+ value.to_string()
+ } else {
+ panic!("Failed to evaluate constant scalar (usize).")
+ }
+ },
+ _ => panic!("Unexpected ConstValue type."),
+ })
+ },
+ ty::TyKind::Int(ty::IntTy::Isize) => {
+ Ok(match value {
+ ConstValue::Scalar(scalar) => {
+ if let InterpResult::Ok(value) = scalar.to_target_isize(cx) {
+ value.to_string()
+ } else {
+ panic!("Failed to evaluate constant scalar (isize).")
+ }
+ },
+ _ => panic!("Unexpected ConstValue type."),
+ })
+ },
+ _ => Err(anyhow!("Unsupported constant type.")),
+ }
+ };
+ }
+
+ convert!(
+ kind,
+ value,
+ (ty::TyKind::Bool, to_bool),
+ (ty::TyKind::Int(ty::IntTy::I8), to_i8),
+ (ty::TyKind::Int(ty::IntTy::I16), to_i16),
+ (ty::TyKind::Int(ty::IntTy::I32), to_i32),
+ (ty::TyKind::Int(ty::IntTy::I64), to_i64),
+ (ty::TyKind::Uint(ty::UintTy::U8), to_u8),
+ (ty::TyKind::Uint(ty::UintTy::U16), to_u16),
+ (ty::TyKind::Uint(ty::UintTy::U32), to_u32),
+ (ty::TyKind::Uint(ty::UintTy::U64), to_u64),
+ (ty::TyKind::Float(ty::FloatTy::F32), to_f32),
+ (ty::TyKind::Float(ty::FloatTy::F64), to_f64)
+ )
+}
+
+fn format_const(db: &dyn BindingsGenerator<'_>, local_def_id: LocalDefId) -> Result<ApiSnippets> {
+ let tcx = db.tcx();
+ let def_id: DefId = local_def_id.to_def_id();
+ let node = tcx.hir_node_by_def_id(local_def_id);
+
+ if let Node::Item(item) = node {
+ if let ItemKind::Const(hir_ty, ..) = item.kind {
+ let ty = tcx.type_of(def_id).instantiate_identity();
+
+ let value = tcx.const_eval_poly(def_id).unwrap();
+
+ let cc_type_snippet =
+ format_ty_for_cc(db, SugaredTy::new(ty, Some(hir_ty)), TypeLocation::Other)?;
+ let cc_value = format_const_value_to_cc(&tcx, ty.kind(), &value)?;
+
+ let cc_type = cc_type_snippet.tokens;
+ let cc_value: TokenStream = cc_value.parse().unwrap();
+ let cc_name: TokenStream = format_cc_ident(tcx.item_name(def_id).as_str())?;
+
+ return Ok(ApiSnippets {
+ main_api: CcSnippet {
+ tokens: quote! {
+ constexpr #cc_type #cc_name = #cc_value;
+ },
+ ..cc_type_snippet
+ },
+ cc_details: CcSnippet::default(),
+ rs_details: quote! {},
+ });
+ }
+ panic!("Called format_const on a non-constant");
+ }
+ panic!("Called format_const on a non-item");
+}
+
fn format_type_alias(
db: &dyn BindingsGenerator<'_>,
local_def_id: LocalDefId,
@@ -2927,6 +3032,7 @@
Item { ident, kind: ItemKind::Use(use_path, use_kind), ..} => {
format_use(db, ident.as_str(), use_path, use_kind).map(Some)
},
+ Item { kind: ItemKind::Const(..), .. } => format_const(db, def_id).map(Some),
Item { kind: ItemKind::Impl(_), .. } | // Handled by `format_adt`
Item { kind: ItemKind::Mod(_), .. } => // Handled by `format_crate`
Ok(None),
@@ -3936,6 +4042,148 @@
});
}
+ #[test]
+ fn test_format_const() {
+ let test_src = r#"
+ pub const BOOL_TRUE: bool = true;
+ pub const BOOL_FALSE: bool = false;
+ pub const INT_POS: i32 = 42;
+ pub const INT_NEG: i32 = -17;
+ pub const FLOAT_32: f32 = 0.125; // 2^(-3)
+ pub const FLOAT_64: f64 = 0.0078125; // 2^(-7)
+ pub const LARGE_INT: i64 = 9223372036854775807;
+ pub const UNSIGNED_INT: u32 = 4294967295;
+ pub const SLICE_LENGTH: usize = "hello world".len();
+ pub const ISIZE: isize = 42;
+ use core::ffi::c_char;
+ pub const CHAR: c_char = 42;
+ "#;
+
+ test_format_item(test_src, "BOOL_TRUE", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr bool BOOL_TRUE = true;
+ }
+ );
+ });
+
+ test_format_item(test_src, "BOOL_FALSE", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr bool BOOL_FALSE = false;
+ }
+ );
+ });
+
+ test_format_item(test_src, "INT_POS", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::int32_t INT_POS = 42;
+ }
+ );
+ });
+
+ test_format_item(test_src, "LARGE_INT", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::int64_t LARGE_INT = 9223372036854775807;
+ }
+ );
+ });
+
+ test_format_item(test_src, "UNSIGNED_INT", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::uint32_t UNSIGNED_INT = 4294967295;
+ }
+ );
+ });
+
+ test_format_item(test_src, "INT_NEG", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::int32_t INT_NEG = -17;
+ }
+ );
+ });
+
+ test_format_item(test_src, "FLOAT_32", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr float FLOAT_32 = 0.125;
+ }
+ );
+ });
+
+ test_format_item(test_src, "FLOAT_64", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr double FLOAT_64 = 0.0078125;
+ }
+ );
+ });
+
+ test_format_item(test_src, "SLICE_LENGTH", |result| {
+ let result = result.unwrap().unwrap();
+ assert_eq!("hello world".len(), 11);
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::uintptr_t SLICE_LENGTH = 11;
+ }
+ );
+ });
+
+ test_format_item(test_src, "ISIZE", |result| {
+ let result = result.unwrap().unwrap();
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr std::intptr_t ISIZE = 42;
+ }
+ );
+ });
+
+ test_format_item(test_src, "CHAR", |result| {
+ let result = result.unwrap().unwrap();
+ assert_eq!("hello world".len(), 11);
+ assert_cc_matches!(
+ result.main_api.tokens,
+ quote! {
+ constexpr char CHAR = 42;
+ }
+ );
+ });
+
+ let test_src = r#"
+ #![allow(nonstandard_style)]
+ pub const reinterpret_cast: u32 = 42;
+ "#;
+ test_format_item(test_src, "reinterpret_cast", |result| {
+ let err = result.unwrap_err();
+ assert_eq!(
+ err,
+ "`reinterpret_cast` is a C++ reserved keyword and can't be used as a C++ identifier"
+ );
+ });
+ }
+
/// The `test_format_item_fn_explicit_unit_return_type` test below is very
/// similar to the
/// `test_format_item_fn_extern_c_no_mangle_no_params_no_return_type` above,
@@ -7299,17 +7547,6 @@
}
#[test]
- fn test_format_item_unsupported_const_value() {
- let test_src = r#"
- pub const CONST_VALUE: i32 = 42;
- "#;
- test_format_item(test_src, "CONST_VALUE", |result| {
- let err = result.unwrap_err();
- assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: constant item");
- });
- }
-
- #[test]
fn test_format_item_use_normal_type() {
let test_src = r#"
pub mod test_mod {