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
20 changes: 19 additions & 1 deletion cc_bindings_from_rs/generate_bindings/database/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ extern crate rustc_span;

use crate::adt_core_bindings::{AdtCoreBindings, CopyCtorStyle, MoveCtorStyle, NoMoveOrAssign};
use crate::code_snippet::{ApiSnippets, CcSnippet, CrubitAbiTypeWithCcPrereqs};
use crate::fully_qualified_name::{FullyQualifiedName, PublicPaths, UnqualifiedName};
use crate::fully_qualified_name::{
FullyQualifiedName, PublicPaths, TransitiveReexport, UnqualifiedName,
};
use crate::include_guard::IncludeGuard;
use crate::type_location::TypeLocation;
use arc_anyhow::Result;
Expand Down Expand Up @@ -164,6 +166,19 @@ memoized::query_group! {
/// Implementation: cc_bindings_from_rs/generate_bindings/lib.rs?q=function:public_paths_by_def_id
fn public_paths_by_def_id(&self, crate_num: CrateNum) -> HashMap<DefId, PublicPaths>;

/// Computes a mapping from a `DefId` to a list of public paths that reference it in a given
/// crate. Unlike `public_paths_by_def_id()`, this only returns paths for definitions that are
/// not defined in the given crate (re-exports).
///
/// Implementation: cc_bindings_from_rs/generate_bindings/lib.rs?q=function:transitive_reexports_by_def_id
fn transitive_reexports_by_def_id(&self, krate: CrateNum) -> HashMap<DefId, PublicPaths>;

/// Computes a mapping from a `DefId` to the list of reexports for that `DefId` that exist in
/// the crate graph. This is determined by collecting the `DefId` of each includable crate.
///
/// Implementation: cc_bindings_from_rs/generate_bindings/lib.rs?q=function:transitive_reexports
fn transitive_reexports(&self) -> HashMap<DefId, Vec<TransitiveReexport>>;

/// Formats a C++ identifier, if possible.
///
/// Implementation: cc_bindings_from_rs/generate_bindings/format_type.rs?q=function:format_cc_ident
Expand Down Expand Up @@ -307,5 +322,8 @@ memoized::query_group! {
///
/// Implementation: cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs?q=function:local_from_trait_impls_by_argument
fn from_trait_impls_by_argument(&self, crate_num: CrateNum) -> Rc<HashMap<Ty<'tcx>, Vec<DefId>>>;

// Returns the original name of a crate, if it has been renamed.
fn renamed_crate_original_name(&self, crate_num: CrateNum) -> Option<Rc<str>>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use arc_anyhow::Result;
use code_gen_utils::{format_cc_type_name, make_rs_ident, NamespaceQualifier};
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use rustc_span::def_id::DefId;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::symbol::Symbol;
use std::rc::Rc;

Expand Down Expand Up @@ -204,3 +204,9 @@ impl PublicPaths {
self.aliases
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransitiveReexport {
pub krate: CrateNum,
pub paths: PublicPaths,
}
4 changes: 3 additions & 1 deletion cc_bindings_from_rs/generate_bindings/database/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ pub use db::BindingsGenerator;
mod fine_grained_feature;
pub use fine_grained_feature::FineGrainedFeature;
mod fully_qualified_name;
pub use fully_qualified_name::{ExportedPath, FullyQualifiedName, PublicPaths, UnqualifiedName};
pub use fully_qualified_name::{
ExportedPath, FullyQualifiedName, PublicPaths, TransitiveReexport, UnqualifiedName,
};
mod include_guard;
pub use include_guard::IncludeGuard;
mod type_location;
Expand Down
122 changes: 105 additions & 17 deletions cc_bindings_from_rs/generate_bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use database::code_snippet::{
};
use database::{
AdtCoreBindings, ExportedPath, FineGrainedFeature, FullyQualifiedName, NoMoveOrAssign,
PublicPaths, TypeLocation, UnqualifiedName,
PublicPaths, TransitiveReexport, TypeLocation, UnqualifiedName,
};
pub use database::{BindingsGenerator, CopyCtorStyle, IncludeGuard, MoveCtorStyle};
use error_report::{anyhow, bail, ErrorReporting, ReportFatalError};
Expand Down Expand Up @@ -214,6 +214,8 @@ pub fn new_database<'db>(
symbol_unqualified_name,
symbol_canonical_name,
public_paths_by_def_id,
transitive_reexports_by_def_id,
transitive_reexports,
format_cc_ident_symbol,
format_top_level_ns_for_crate,
format_type::format_ty_for_cc,
Expand All @@ -230,6 +232,7 @@ pub fn new_database<'db>(
generate_adt_core,
crubit_abi_type_from_ty,
from_trait_impls_by_argument,
renamed_crate_original_name,
)
}

Expand Down Expand Up @@ -374,10 +377,10 @@ fn format_with_cc_body(
Ok(tokens)
}

/// Implementation of `BindingsGenerator::public_paths_by_def_id`.
fn public_paths_by_def_id(
fn public_paths_by_def_id_impl(
db: &BindingsGenerator<'_>,
crate_num: CrateNum,
filter: impl Fn(DefId, Option<DefId>) -> bool,
) -> HashMap<DefId, PublicPaths> {
/// This is retooled logic from rustc's `visible_parent_map` function. Except where that only
/// selects the shortest visible path, we track all paths and defer selecting the correct one
Expand Down Expand Up @@ -460,12 +463,14 @@ fn public_paths_by_def_id(
type_alias_def_id,
is_doc_hidden,
};
use std::collections::hash_map::Entry;
match visible_parent_map.entry(def_id) {
Entry::Vacant(vacant) => {
vacant.insert(PublicPaths::new(path));
if filter(def_id, type_alias_def_id) {
use std::collections::hash_map::Entry;
match visible_parent_map.entry(def_id) {
Entry::Vacant(vacant) => {
vacant.insert(PublicPaths::new(path));
}
Entry::Occupied(mut occupied) => occupied.get_mut().insert(path),
}
Entry::Occupied(mut occupied) => occupied.get_mut().insert(path),
}
// TODO: b/459865403 - Support bindings for `pub extern crate core`.
let is_extern_crate_core = |child: &ModChild| {
Expand Down Expand Up @@ -495,6 +500,57 @@ fn public_paths_by_def_id(
visible_parent_map
}

/// Implementation of `BindingsGenerator::public_paths_by_def_id`.
fn public_paths_by_def_id(
db: &BindingsGenerator<'_>,
crate_num: CrateNum,
) -> HashMap<DefId, PublicPaths> {
public_paths_by_def_id_impl(db, crate_num, |_, _| true)
}

/// Implementation of `BindingsGenerator::transitive_reexports_by_def_id`.
fn transitive_reexports_by_def_id(
db: &BindingsGenerator<'_>,
crate_num: CrateNum,
) -> HashMap<DefId, PublicPaths> {
let tcx = db.tcx();
public_paths_by_def_id_impl(db, crate_num, |def_id, type_alias_def_id| {
type_alias_def_id.is_none() && def_id.krate != crate_num
})
}

/// Implementation of `BindingsGenerator::transitive_reexports`.
fn transitive_reexports(db: &BindingsGenerator<'_>) -> HashMap<DefId, Vec<TransitiveReexport>> {
let mut output = HashMap::new();
let tcx = db.tcx();
db.crate_name_to_include_paths()
.keys()
.filter_map(|crate_name| {
tcx.used_crates(()).iter().find(|&&krate| {
crate_name.as_ref()
== db
.renamed_crate_original_name(krate)
.unwrap_or_else(|| Rc::from(tcx.crate_name(krate).as_str()))
.as_ref()
})
})
.flat_map(|&krate| {
let reexports = db.transitive_reexports_by_def_id(krate);
reexports
.into_iter()
.map(move |(def_id, paths)| (def_id, TransitiveReexport { krate, paths }))
})
.for_each(|(def_id, reexport)| {
let vec: &mut Vec<TransitiveReexport> = output.entry(def_id).or_default();
// We sort by crate num to ensure our ordering here is consistent.
if let Err(index) = vec.binary_search_by_key(&reexport.krate, |reexport| reexport.krate)
{
vec.insert(index, reexport);
}
});
output
}

fn module_children(tcx: TyCtxt<'_>, parent: DefId) -> &[ModChild] {
match parent.as_local() {
None => tcx.module_children(parent),
Expand Down Expand Up @@ -543,6 +599,17 @@ fn symbol_unqualified_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<
Some(UnqualifiedName { cpp_name, rs_name, cpp_type })
}

fn renamed_crate_original_name(db: &BindingsGenerator<'_>, krate_id: CrateNum) -> Option<Rc<str>> {
let tcx = db.tcx();
let crate_name = tcx.crate_name(krate_id);
for (name, renamed) in db.crate_renames().iter() {
if renamed.as_ref() == crate_name.as_str() {
return Some(name.clone());
}
}
return None;
}

/// Implementation of `BindingsGenerator::symbol_canonical_name`.
fn symbol_canonical_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<FullyQualifiedName> {
let tcx = db.tcx();
Expand All @@ -552,13 +619,33 @@ fn symbol_canonical_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<Fu
// canonical name.
let def_id = resolve_if_use(db, def_id).unwrap_or(def_id);

let (full_path_strs, type_alias_def_id) = {
// If our definition is at a path that can't be spelled, we have to pick a path from our
// aliases.
let paths = db.public_paths_by_def_id(def_id.krate);

// If our definition has no public spellings, we can't give it a canonical name.
let paths = paths.get(&def_id)?;
let (full_path_strs, type_alias_def_id, krate_id) = {
let is_importable_crate = def_id.krate == db.source_crate_num()
|| db.crate_name_to_include_paths().contains_key(db.renamed_crate_original_name(def_id.krate)
.unwrap_or_else(|| Rc::from(tcx.crate_name(def_id.krate).as_str())).as_ref())
// TODO - b/391443811: We don't need this workaround once we bind `std`, `core`, and `alloc`.
|| {
let sym = tcx.crate_name(def_id.krate);
let name = sym.as_str();
name == "std" || name == "core" || name == "alloc" || name == "proc_macro"
};
let (paths, krate) = if is_importable_crate {
// If our definition is at a path that can't be spelled, we have to pick a path from our
// aliases.
let paths = db.public_paths_by_def_id(def_id.krate);

// If our definition has no public spellings, we can't give it a canonical name.
(paths.get(&def_id).cloned()?, def_id.krate)
} else {
// Our definition might be a transitive reexport, so we need to check if any of the
// dependencies have a spelling for this definition. This is expensive, so we only do it
// for DefIds we know aren't in the source crate or a direct dependency.
let trans_reexports = db.transitive_reexports();
trans_reexports
.get(&def_id)
.and_then(|reexports| reexports.first())
.map(|reexport| (reexport.paths.clone(), reexport.krate))?
};

// Select a canonical path for this symbol from available paths.
// Our paths are kept in sorted order, so the canonical path will be the first one.
Expand All @@ -569,6 +656,7 @@ fn symbol_canonical_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<Fu
(
canonical_path.path.iter().map(|s| Rc::<str>::from(s.as_str())).collect::<Vec<_>>(),
canonical_path.type_alias_def_id,
krate,
)
};

Expand All @@ -593,7 +681,7 @@ fn symbol_canonical_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<Fu
.then_some(())
.and_then(|_| db.source_crate_name())
.map(|source_crate_name| Symbol::intern(source_crate_name.as_ref()))
.unwrap_or_else(|| tcx.crate_name(def_id.krate));
.unwrap_or_else(|| tcx.crate_name(krate_id));

if krate.as_str() == "polars_plan"
&& matches!(unqualified.rs_name.as_str(), "date_range" | "time_range")
Expand Down Expand Up @@ -625,7 +713,7 @@ fn symbol_canonical_name(db: &BindingsGenerator<'_>, def_id: DefId) -> Option<Fu
let rs_mod_path = NamespaceQualifier::new(full_path_strs.clone());
let cpp_ns_path =
NamespaceQualifier::new(full_path_strs.into_iter().map(rename_clang_builtin_macros));
let cpp_top_level_ns = format_top_level_ns_for_crate(db, def_id.krate);
let cpp_top_level_ns = format_top_level_ns_for_crate(db, krate_id);
Some(FullyQualifiedName { krate, cpp_top_level_ns, cpp_ns_path, rs_mod_path, unqualified })
}

Expand Down
60 changes: 60 additions & 0 deletions cc_bindings_from_rs/test/uses/trasitive_reexports/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""End-to-end tests of `cc_bindings_from_rs`, focusing on use statements."""

load(
"@rules_rust//rust:defs.bzl",
"rust_library",
)
load(
"//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
"cc_bindings_from_rust",
)
load(
"//cc_bindings_from_rs/test/golden:golden_test.bzl",
"golden_test",
)
load("//common:crubit_wrapper_macros_oss.bzl", "crubit_cc_test")

package(default_applicable_licenses = ["//:license"])

rust_library(
name = "transitive",
testonly = 1,
srcs = ["transitive.rs"],
)

rust_library(
name = "direct",
testonly = 1,
srcs = ["direct.rs"],
deps = [":transitive"],
)

rust_library(
name = "transitive_reexports",
testonly = 1,
srcs = ["transitive_reexports.rs"],
deps = [":direct"],
)

cc_bindings_from_rust(
name = "transitive_reexports_cc_api",
testonly = 1,
crate = ":transitive_reexports",
)

crubit_cc_test(
name = "transitive_reexports_test",
srcs = ["transitive_reexports_test.cc"],
deps = [
":transitive_reexports_cc_api",
"//testing/base/public:gunit_main",
],
)

golden_test(
name = "transitive_reexports_golden_test",
basename = "transitive_reexports",
golden_h = "transitive_reexports_cc_api.h",
golden_rs = "transitive_reexports_cc_api_impl.rs",
rust_library = "transitive_reexports",
)
16 changes: 16 additions & 0 deletions cc_bindings_from_rs/test/uses/trasitive_reexports/direct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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
extern crate transitive;

pub use transitive::Transitive;

pub struct Direct {
pub value: i32,
}

impl Direct {
pub fn new(train: Transitive) -> Self {
Direct { value: train.value }
}
}
12 changes: 12 additions & 0 deletions cc_bindings_from_rs/test/uses/trasitive_reexports/transitive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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
pub struct Transitive {
pub value: i32,
}

impl Transitive {
pub fn new(value: i32) -> Self {
Transitive { value }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// 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
extern crate direct;

pub fn direct_to_transitive(direct: &direct::Direct) -> direct::Transitive {
direct::Transitive { value: direct.value }
}
Loading