aboutsummaryrefslogtreecommitdiff
path: root/syntax/trivial.rs
diff options
context:
space:
mode:
Diffstat (limited to 'syntax/trivial.rs')
-rw-r--r--syntax/trivial.rs257
1 files changed, 257 insertions, 0 deletions
diff --git a/syntax/trivial.rs b/syntax/trivial.rs
new file mode 100644
index 00000000..fe95e2b7
--- /dev/null
+++ b/syntax/trivial.rs
@@ -0,0 +1,257 @@
+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<TrivialReason<'a>>> {
+ 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 }
+}