Skip to content

Commit e197216

Browse files
committed
Extract is_implicit_impl_ok from Types::collect into a method of ImplKey.
1 parent e81ef78 commit e197216

File tree

3 files changed

+55
-24
lines changed

3 files changed

+55
-24
lines changed

syntax/instantiate.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::syntax::types::Types;
12
use crate::syntax::{NamedType, Ty1, Type};
23
use proc_macro2::{Ident, Span};
34
use std::hash::{Hash, Hasher};
@@ -13,6 +14,36 @@ pub(crate) enum ImplKey<'a> {
1314
CxxVector(NamedImplKey<'a>),
1415
}
1516

17+
impl<'a> ImplKey<'a> {
18+
/// Whether to generate an implicit instantiation/monomorphization of a given generic type
19+
/// binding. ("implicit" = without an explicit `impl Foo<T> {}` - see
20+
/// <https://cxx.rs/extern-c++.html?highlight=explicit#explicit-shim-trait-impls>).
21+
///
22+
/// The main consideration is avoiding introducing conflicting/overlapping impls:
23+
///
24+
/// * The `cxx` crate already provides impls for cases where `T` is a primitive
25+
/// type like `u32`
26+
/// * Some generics (e.g. Rust bindings for C++ templates like `CxxVector<T>`, `UniquePtr<T>`,
27+
/// etc.) require an `impl` of a `trait` provided by the `cxx` crate (such as
28+
/// [`cxx::vector::VectorElement`] or [`cxx::memory::UniquePtrTarget`]). To avoid violating
29+
/// [Rust orphan rule](https://doc.rust-lang.org/reference/items/implementations.html#r-items.impl.trait.orphan-rule.intro)
30+
/// we restrict `T` to be a local type
31+
/// (TODO: or a fundamental type like `Box<LocalType>`).
32+
/// * Other generics (e.g. C++ bindings for Rust generics like `Vec<T>` or `Box<T>`)
33+
/// don't necessarily need to follow the orphan rule, but we conservatively also
34+
/// only generate implicit impls if `T` is a local type. TODO: revisit?
35+
pub(crate) fn is_implicit_impl_ok(&self, types: &Types) -> bool {
36+
match self {
37+
ImplKey::RustBox(ident)
38+
| ImplKey::RustVec(ident)
39+
| ImplKey::UniquePtr(ident)
40+
| ImplKey::SharedPtr(ident)
41+
| ImplKey::WeakPtr(ident)
42+
| ImplKey::CxxVector(ident) => types.is_local(ident.rust),
43+
}
44+
}
45+
}
46+
1647
pub(crate) struct NamedImplKey<'a> {
1748
#[cfg_attr(not(proc_macro), expect(dead_code))]
1849
pub begin_span: Span,

syntax/map.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ mod ordered {
2727
{
2828
self.0.contains_key(key)
2929
}
30+
31+
pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a K, &'a V)> {
32+
self.0.iter()
33+
}
3034
}
3135

3236
impl<K, V> OrderedMap<K, V>

syntax/types.rs

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -216,30 +216,6 @@ impl<'a> Types<'a> {
216216
}
217217
}
218218

219-
for (ty, cfg) in &all {
220-
let Some(impl_key) = ty.impl_key() else {
221-
continue;
222-
};
223-
let implicit_impl = match &impl_key {
224-
ImplKey::RustBox(ident)
225-
| ImplKey::RustVec(ident)
226-
| ImplKey::UniquePtr(ident)
227-
| ImplKey::SharedPtr(ident)
228-
| ImplKey::WeakPtr(ident)
229-
| ImplKey::CxxVector(ident) => {
230-
Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
231-
}
232-
};
233-
if implicit_impl {
234-
match impls.entry(impl_key) {
235-
Entry::Vacant(entry) => {
236-
entry.insert(ConditionalImpl::from(cfg.clone()));
237-
}
238-
Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()),
239-
}
240-
}
241-
}
242-
243219
// All these APIs may contain types passed by value. We need to ensure
244220
// we check that this is permissible. We do this _after_ scanning all
245221
// the APIs above, in case some function or struct references a type
@@ -269,6 +245,21 @@ impl<'a> Types<'a> {
269245

270246
types.toposorted_structs = toposort::sort(cx, apis, &types);
271247

248+
let implicit_impls = types
249+
.all
250+
.iter()
251+
.filter_map(|(ty, cfg)| Type::impl_key(ty).map(|impl_key| (impl_key, cfg)))
252+
.filter(|(impl_key, _cfg)| impl_key.is_implicit_impl_ok(&types))
253+
.collect::<Vec<_>>();
254+
for (impl_key, cfg) in implicit_impls {
255+
match types.impls.entry(impl_key) {
256+
Entry::Vacant(entry) => {
257+
entry.insert(ConditionalImpl::from(cfg.clone()));
258+
}
259+
Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()),
260+
}
261+
}
262+
272263
let mut unresolved_structs = types.structs.keys();
273264
let mut new_information = true;
274265
while new_information {
@@ -353,6 +344,11 @@ impl<'a> Types<'a> {
353344
Type::Fn(_) | Type::Void(_) => false,
354345
}
355346
}
347+
348+
/// Returns `true` if `ident` is defined or declared within the current `#[cxx::bridge]`.
349+
pub(crate) fn is_local(&self, ident: &Ident) -> bool {
350+
Atom::from(ident).is_none() && !self.aliases.contains_key(ident)
351+
}
356352
}
357353

358354
impl<'t, 'a> IntoIterator for &'t Types<'a> {

0 commit comments

Comments
 (0)