use crate::syntax::map::UnorderedMap; use crate::syntax::set::{OrderedSet as Set, UnorderedSet}; use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type}; use proc_macro2::Ident; use std::fmt::{self, Display}; #[derive(Copy, Clone)] pub enum TrivialReason<'a> { StructField(&'a Struct), FunctionArgument(&'a ExternFn), FunctionReturn(&'a ExternFn), BoxTarget, VecElement, UnpinnedMut(&'a ExternFn), } pub fn required_trivial_reasons<'a>( apis: &'a [Api], all: &Set<&'a Type>, structs: &UnorderedMap<&'a Ident, &'a Struct>, enums: &UnorderedMap<&'a Ident, &'a Enum>, cxx: &UnorderedSet<&'a Ident>, ) -> UnorderedMap<&'a Ident, Vec>> { let mut required_trivial = UnorderedMap::new(); let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| { if cxx.contains(&ident.rust) && !structs.contains_key(&ident.rust) && !enums.contains_key(&ident.rust) { required_trivial .entry(&ident.rust) .or_insert_with(Vec::new) .push(reason); } }; for api in apis { match api { Api::Struct(strct) => { for field in &strct.fields { if let Type::Ident(ident) = &field.ty { let reason = TrivialReason::StructField(strct); insist_extern_types_are_trivial(ident, reason); } } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { if let Some(receiver) = &efn.receiver { if receiver.mutable && !receiver.pinned { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(&receiver.ty, reason); } } for arg in &efn.args { match &arg.ty { Type::Ident(ident) => { let reason = TrivialReason::FunctionArgument(efn); insist_extern_types_are_trivial(ident, reason); } Type::Ref(ty) => { if ty.mutable && !ty.pinned { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(ident, reason); } } } _ => {} } } if let Some(ret) = &efn.ret { match ret { Type::Ident(ident) => { let reason = TrivialReason::FunctionReturn(efn); insist_extern_types_are_trivial(ident, reason); } Type::Ref(ty) => { if ty.mutable && !ty.pinned { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::UnpinnedMut(efn); insist_extern_types_are_trivial(ident, reason); } } } _ => {} } } } _ => {} } } for ty in all { match ty { Type::RustBox(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::BoxTarget; insist_extern_types_are_trivial(ident, reason); } } Type::RustVec(ty) => { if let Type::Ident(ident) = &ty.inner { let reason = TrivialReason::VecElement; insist_extern_types_are_trivial(ident, reason); } } _ => {} } } required_trivial } // Context: // "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust" // "needs a cxx::ExternType impl in order to be used as {what}" pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a { struct Description<'a> { name: &'a Pair, reasons: &'a [TrivialReason<'a>], } impl<'a> Display for Description<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut field_of = Set::new(); let mut argument_of = Set::new(); let mut return_of = Set::new(); let mut box_target = false; let mut vec_element = false; let mut unpinned_mut = Set::new(); for reason in self.reasons { match reason { TrivialReason::StructField(strct) => { field_of.insert(&strct.name.rust); } TrivialReason::FunctionArgument(efn) => { argument_of.insert(&efn.name.rust); } TrivialReason::FunctionReturn(efn) => { return_of.insert(&efn.name.rust); } TrivialReason::BoxTarget => box_target = true, TrivialReason::VecElement => vec_element = true, TrivialReason::UnpinnedMut(efn) => { unpinned_mut.insert(&efn.name.rust); } } } let mut clauses = Vec::new(); if !field_of.is_empty() { clauses.push(Clause::Set { article: "a", desc: "field of", set: &field_of, }); } if !argument_of.is_empty() { clauses.push(Clause::Set { article: "an", desc: "argument of", set: &argument_of, }); } if !return_of.is_empty() { clauses.push(Clause::Set { article: "a", desc: "return value of", set: &return_of, }); } if box_target { clauses.push(Clause::Ty1 { article: "type", desc: "Box", param: self.name, }); } if vec_element { clauses.push(Clause::Ty1 { article: "a", desc: "vector element in Vec", param: self.name, }); } if !unpinned_mut.is_empty() { clauses.push(Clause::Set { article: "a", desc: "non-pinned mutable reference in signature of", set: &unpinned_mut, }); } for (i, clause) in clauses.iter().enumerate() { if i == 0 { write!(f, "{} ", clause.article())?; } else if i + 1 < clauses.len() { write!(f, ", ")?; } else { write!(f, " or ")?; } clause.fmt(f)?; } Ok(()) } } enum Clause<'a> { Set { article: &'a str, desc: &'a str, set: &'a Set<&'a Ident>, }, Ty1 { article: &'a str, desc: &'a str, param: &'a Pair, }, } impl<'a> Clause<'a> { fn article(&self) -> &'a str { match self { Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article, } } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Clause::Set { article: _, desc, set, } => { write!(f, "{} ", desc)?; for (i, ident) in set.iter().take(3).enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "`{}`", ident)?; } Ok(()) } Clause::Ty1 { article: _, desc, param, } => write!(f, "{}<{}>", desc, param.rust), } } } Description { name, reasons } }