Introduce separate `SnippetKey` and `SnippetKind` types.
This is the 1st CL out of a series of 3 CLs that work toward returning
separate snippets from `format_fn` for A) declaration of the main API
function, and B) thunk declaration, function definition, and other
implementation details. After the 3 CLs land, adding support for
static methods should be fairly straightforward.
PiperOrigin-RevId: 508770737
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 7bbab31..4bf9ec8 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -19,6 +19,7 @@
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::PanicStrategy;
+use std::cmp::Ordering;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::iter::once;
use std::ops::AddAssign;
@@ -501,6 +502,39 @@
})
}
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+enum SnippetKind {
+ /// Main API - for example:
+ /// - A C++ declaration of a function (with a doc comment),
+ /// - A C++ definition of a struct (with a doc comment).
+ MainApi,
+
+ /// Implementation details - for example:
+ /// - A C++ declaration of an `extern "C"` thunk,
+ /// - A Rust implementation of an `extern "C"` thunk,
+ /// - C++ or Rust assertions about struct size and aligment.
+ ///
+ /// TODO(b/260725279): Split results of `format_fn` and `format_adt` into
+ /// multiple snippets - one `MainApi` snippet and 0-N `ImplDetails`
+ /// snippets,
+ _ImplDetails,
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+struct SnippetKey {
+ def_id: LocalDefId,
+ kind: SnippetKind,
+}
+
+fn preferred_snippet_order(tcx: TyCtxt) -> impl Fn(&SnippetKey, &SnippetKey) -> Ordering + '_ {
+ move |lhs: &SnippetKey, rhs: &SnippetKey| {
+ let to_ordering_key = |x: &SnippetKey| (x.kind.clone(), tcx.def_span(x.def_id));
+ let lhs = to_ordering_key(lhs);
+ let rhs = to_ordering_key(rhs);
+ lhs.cmp(&rhs)
+ }
+}
+
/// A C++ snippet (e.g. function declaration for `..._cc_api.h`) and a Rust
/// snippet (e.g. a thunk definition for `..._cc_api_impl.rs`).
#[derive(Debug)]
@@ -516,7 +550,7 @@
/// - doesn't identify a function,
/// - has generic parameters of any kind - lifetime parameters (see also
/// b/258235219), type parameters, or const parameters.
-fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<MixedSnippet> {
+fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<(SnippetKey, MixedSnippet)> {
let tcx = input.tcx;
let def_id: DefId = local_def_id.to_def_id(); // Convert LocalDefId to DefId.
@@ -672,7 +706,11 @@
}
}
};
- Ok(MixedSnippet { cc: CcSnippet { prereqs: cc_prereqs, tokens: cc_tokens }, rs: rs_tokens })
+
+ Ok((
+ SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi },
+ MixedSnippet { cc: CcSnippet { prereqs: cc_prereqs, tokens: cc_tokens }, rs: rs_tokens },
+ ))
}
/// Represents bindings for the "core" part of an algebraic data type (an ADT -
@@ -849,8 +887,9 @@
/// represented by `core`. This function is infallible - after
/// `format_adt_core` returns success we have committed to emitting C++ bindings
/// for the ADT.
-fn format_adt(tcx: TyCtxt, core: &AdtCoreBindings) -> MixedSnippet {
- assert!(core.def_id.is_local(), "`format_adt` should only be called for local ADTs");
+fn format_adt(tcx: TyCtxt, core: &AdtCoreBindings) -> (SnippetKey, MixedSnippet) {
+ // `format_adt` should only be called for local ADTs.
+ let local_def_id = core.def_id.expect_local();
// TODO(b/260725279): Add support for static methods.
let impl_item_decls = tcx
@@ -868,7 +907,7 @@
.sorted_by_key(|impl_item_def_id| tcx.def_span(*impl_item_def_id))
.map(|impl_item_def_id| {
let err = anyhow!("`impl` items are not supported yet");
- format_unsupported_def(tcx, impl_item_def_id, err).cc
+ format_unsupported_def(tcx, impl_item_def_id, err).1.cc
})
.collect_vec();
@@ -920,7 +959,7 @@
}
};
- MixedSnippet { cc, rs }
+ (SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi }, MixedSnippet { cc, rs })
}
/// Formats the forward declaration of an algebraic data type (an ADT - a
@@ -979,7 +1018,7 @@
/// can be ignored. Returns an `Err` if the definition couldn't be formatted.
///
/// Will panic if `def_id` is invalid (i.e. doesn't identify a HIR item).
-fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<MixedSnippet>> {
+fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<(SnippetKey, MixedSnippet)>> {
let tcx = input.tcx;
// TODO(b/262052635): When adding support for re-exports we may need to change
@@ -1017,7 +1056,7 @@
tcx: TyCtxt,
local_def_id: LocalDefId,
err: anyhow::Error,
-) -> MixedSnippet {
+) -> (SnippetKey, MixedSnippet) {
let source_loc = format_source_location(tcx, local_def_id);
let name = tcx.def_path_str(local_def_id.to_def_id());
@@ -1026,22 +1065,28 @@
let msg = format!("Error generating bindings for `{name}` defined at {source_loc}: {err:#}");
let cc = CcSnippet::new(quote! { __NEWLINE__ __NEWLINE__ __COMMENT__ #msg __NEWLINE__ });
- MixedSnippet { cc, rs: quote! {} }
+ (
+ SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi },
+ MixedSnippet { cc, rs: quote! {} },
+ )
}
/// Formats all public items from the Rust crate being compiled.
fn format_crate(input: &Input) -> Result<Output> {
let tcx = input.tcx;
- let mut bindings: HashMap<LocalDefId, MixedSnippet> = tcx
+ let mut bindings: HashMap<SnippetKey, MixedSnippet> = tcx
.hir()
.items()
.filter_map(|item_id| {
let def_id: LocalDefId = item_id.owner_id.def_id;
format_item(input, def_id)
.unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
- .map(|snippet| (def_id, snippet))
})
- .collect();
+ .fold(HashMap::new(), |mut map, (key, value)| {
+ let old_item = map.insert(key, value);
+ assert!(old_item.is_none(), "Duplicated key: {key:?}");
+ map
+ });
// Find the order of `bindings` that 1) meets the requirements of
// `CcPrerequisites::defs` and 2) makes a best effort attempt to keep the
@@ -1050,12 +1095,12 @@
let toposort::TopoSortResult { ordered: ordered_ids, failed: failed_ids } = {
let nodes = bindings.keys().copied();
let deps = bindings.iter().flat_map(|(&successor, snippet)| {
- let predecessors = snippet.cc.prereqs.defs.iter().copied();
+ let predecessors = snippet.cc.prereqs.defs.iter().map(|&def_id|
+ SnippetKey { def_id, kind: SnippetKind::MainApi }
+ );
predecessors.map(move |predecessor| toposort::Dependency { predecessor, successor })
});
- let preferred_order =
- |id1: &LocalDefId, id2: &LocalDefId| tcx.def_span(*id1).cmp(&tcx.def_span(*id2));
- toposort::toposort(nodes, deps, preferred_order)
+ toposort::toposort(nodes, deps, preferred_snippet_order(tcx))
};
assert_eq!(
0,
@@ -1075,8 +1120,8 @@
let mut includes = BTreeSet::new();
let mut ordered_cc = Vec::new();
let mut rs_body = quote! {};
- for local_def_id in ordered_ids.into_iter() {
- let mod_path = FullyQualifiedName::new(tcx, local_def_id.to_def_id()).mod_path;
+ for key in ordered_ids.into_iter() {
+ let mod_path = FullyQualifiedName::new(tcx, key.def_id.to_def_id()).mod_path;
let MixedSnippet {
rs: inner_rs,
cc: CcSnippet {
@@ -1087,10 +1132,10 @@
.. // `defs` have already been utilized by `toposort` above
}
}
- } = bindings.remove(&local_def_id).unwrap();
+ } = bindings.remove(&key).unwrap();
fwd_decls.extend(inner_fwd_decls.difference(&already_declared).copied());
- already_declared.insert(local_def_id);
+ already_declared.insert(key.def_id);
already_declared.extend(inner_fwd_decls.into_iter());
includes.append(&mut inner_includes);
@@ -3572,6 +3617,9 @@
// To print causes as well [...], use the alternate selector “{:#}”.
let result = result.map_err(|anyhow_err| format!("{anyhow_err:#}"));
+ // Ignore SnippetKey for now.
+ let result = result.map(|opt| opt.map(|(_key, snippet)| snippet));
+
test_function(result)
})
}