blob: 85184d849b7d3fd650a3b6835e4ca34099d1244d [file] [log] [blame]
David Tolnay0634b1f2020-12-20 21:04:04 -08001use crate::syntax::set::OrderedSet as Set;
David Tolnay5b1d8632020-12-20 22:05:11 -08002use crate::syntax::{Api, Enum, ExternFn, Pair, RustName, Struct, Type};
David Tolnay0634b1f2020-12-20 21:04:04 -08003use proc_macro2::Ident;
4use std::collections::BTreeMap as Map;
David Tolnay5b1d8632020-12-20 22:05:11 -08005use std::fmt::{self, Display};
David Tolnay0634b1f2020-12-20 21:04:04 -08006
7#[derive(Copy, Clone)]
8pub enum TrivialReason<'a> {
9 StructField(&'a Struct),
10 FunctionArgument(&'a ExternFn),
11 FunctionReturn(&'a ExternFn),
12 BoxTarget,
13 VecElement,
14 UnpinnedMutArg(&'a ExternFn),
15}
16
17pub fn required_trivial_reasons<'a>(
18 apis: &'a [Api],
19 all: &Set<&'a Type>,
20 structs: &Map<&'a Ident, &'a Struct>,
21 enums: &Map<&'a Ident, &'a Enum>,
22 cxx: &Set<&'a Ident>,
23) -> Map<&'a Ident, Vec<TrivialReason<'a>>> {
24 let mut required_trivial = Map::new();
25
26 let mut insist_extern_types_are_trivial = |ident: &'a RustName, reason| {
27 if cxx.contains(&ident.rust)
28 && !structs.contains_key(&ident.rust)
29 && !enums.contains_key(&ident.rust)
30 {
31 required_trivial
32 .entry(&ident.rust)
33 .or_insert_with(Vec::new)
34 .push(reason);
35 }
36 };
37
38 for api in apis {
39 match api {
40 Api::Struct(strct) => {
41 for field in &strct.fields {
42 if let Type::Ident(ident) = &field.ty {
43 let reason = TrivialReason::StructField(strct);
44 insist_extern_types_are_trivial(ident, reason);
45 }
46 }
47 }
48 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
49 if let Some(receiver) = &efn.receiver {
50 if receiver.mutable && !receiver.pinned {
51 let reason = TrivialReason::UnpinnedMutArg(efn);
52 insist_extern_types_are_trivial(&receiver.ty, reason);
53 }
54 }
55 for arg in &efn.args {
56 match &arg.ty {
57 Type::Ident(ident) => {
58 let reason = TrivialReason::FunctionArgument(efn);
59 insist_extern_types_are_trivial(ident, reason);
60 }
61 Type::Ref(ty) => {
62 if ty.mutable && !ty.pinned {
63 if let Type::Ident(ident) = &ty.inner {
64 let reason = TrivialReason::UnpinnedMutArg(efn);
65 insist_extern_types_are_trivial(ident, reason);
66 }
67 }
68 }
69 _ => {}
70 }
71 }
72 if let Some(ret) = &efn.ret {
73 if let Type::Ident(ident) = &ret {
74 let reason = TrivialReason::FunctionReturn(efn);
75 insist_extern_types_are_trivial(ident, reason);
76 }
77 }
78 }
79 _ => {}
80 }
81 }
82
83 for ty in all {
84 match ty {
85 Type::RustBox(ty) => {
86 if let Type::Ident(ident) = &ty.inner {
87 let reason = TrivialReason::BoxTarget;
88 insist_extern_types_are_trivial(ident, reason);
89 }
90 }
91 Type::RustVec(ty) => {
92 if let Type::Ident(ident) = &ty.inner {
93 let reason = TrivialReason::VecElement;
94 insist_extern_types_are_trivial(ident, reason);
95 }
96 }
97 _ => {}
98 }
99 }
100
101 required_trivial
102}
103
David Tolnay5b1d8632020-12-20 22:05:11 -0800104// Context:
105// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
106// "needs a cxx::ExternType impl in order to be used as {what}"
107pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
108 struct Description<'a> {
109 name: &'a Pair,
110 reasons: &'a [TrivialReason<'a>],
David Tolnay0634b1f2020-12-20 21:04:04 -0800111 }
David Tolnay0634b1f2020-12-20 21:04:04 -0800112
David Tolnay5b1d8632020-12-20 22:05:11 -0800113 impl<'a> Display for Description<'a> {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 let mut field_of = Set::new();
116 let mut argument_of = Set::new();
117 let mut return_of = Set::new();
118 let mut box_target = false;
119 let mut vec_element = false;
120 let mut unpinned_mut = Set::new();
121
122 for reason in self.reasons {
123 match reason {
124 TrivialReason::StructField(strct) => {
125 field_of.insert(&strct.name.rust);
126 }
127 TrivialReason::FunctionArgument(efn) => {
128 argument_of.insert(&efn.name.rust);
129 }
130 TrivialReason::FunctionReturn(efn) => {
131 return_of.insert(&efn.name.rust);
132 }
133 TrivialReason::BoxTarget => box_target = true,
134 TrivialReason::VecElement => vec_element = true,
135 TrivialReason::UnpinnedMutArg(efn) => {
136 unpinned_mut.insert(&efn.name.rust);
137 }
138 }
David Tolnay0634b1f2020-12-20 21:04:04 -0800139 }
David Tolnay5b1d8632020-12-20 22:05:11 -0800140
141 let mut clauses = Vec::new();
142 if !field_of.is_empty() {
143 clauses.push(Clause::Set {
144 article: "a",
145 desc: "field of",
146 set: &field_of,
147 });
148 }
149 if !argument_of.is_empty() {
150 clauses.push(Clause::Set {
151 article: "an",
152 desc: "argument of",
153 set: &argument_of,
154 });
155 }
156 if !return_of.is_empty() {
157 clauses.push(Clause::Set {
158 article: "a",
159 desc: "return value of",
160 set: &return_of,
161 });
162 }
163 if box_target {
164 clauses.push(Clause::Ty1 {
165 article: "type",
166 desc: "Box",
167 param: self.name,
168 });
169 }
170 if vec_element {
171 clauses.push(Clause::Ty1 {
172 article: "a",
173 desc: "vector element in Vec",
174 param: self.name,
175 });
176 }
177 if !unpinned_mut.is_empty() {
178 clauses.push(Clause::Set {
179 article: "a",
180 desc: "non-pinned mutable reference argument of",
181 set: &unpinned_mut,
182 });
183 }
184
185 for (i, clause) in clauses.iter().enumerate() {
186 if i == 0 {
187 write!(f, "{} ", clause.article())?;
188 } else if i + 1 < clauses.len() {
189 write!(f, ", ")?;
190 } else {
191 write!(f, " or ")?;
192 }
193 clause.fmt(f)?;
194 }
195
196 Ok(())
David Tolnay0634b1f2020-12-20 21:04:04 -0800197 }
198 }
David Tolnay5b1d8632020-12-20 22:05:11 -0800199
200 enum Clause<'a> {
201 Set {
202 article: &'a str,
203 desc: &'a str,
204 set: &'a Set<&'a Ident>,
205 },
206 Ty1 {
207 article: &'a str,
208 desc: &'a str,
209 param: &'a Pair,
210 },
211 }
212
213 impl<'a> Clause<'a> {
214 fn article(&self) -> &'a str {
215 match self {
216 Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article,
217 }
218 }
219
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 match self {
222 Clause::Set {
223 article: _,
224 desc,
225 set,
226 } => {
227 write!(f, "{} ", desc)?;
228 for (i, ident) in set.iter().take(3).enumerate() {
229 if i > 0 {
230 write!(f, ", ")?;
231 }
232 write!(f, "`{}`", ident)?;
233 }
234 Ok(())
235 }
236 Clause::Ty1 {
237 article: _,
238 desc,
239 param,
240 } => write!(f, "{}<{}>", desc, param.rust),
241 }
242 }
243 }
244
245 Description { name, reasons }
David Tolnay0634b1f2020-12-20 21:04:04 -0800246}