1use crate::cpp_data::CppPath;
4use crate::cpp_ffi_data::{CppFfiType, CppTypeConversionToFfi};
5use ritual_common::errors::{bail, Result, ResultExt};
6use serde_derive::{Deserialize, Serialize};
7use std::hash::Hash;
8use std::hash::Hasher;
9
10#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
11pub enum CppPointerLikeTypeKind {
12 Pointer,
13 Reference,
14 RValueReference,
15}
16
17#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
22pub enum CppBuiltInNumericType {
23 Bool,
24 Char,
25 SChar,
26 UChar,
27 WChar,
28 Char16,
29 Char32,
30 Short,
31 UShort,
32 Int,
33 UInt,
34 Long,
35 ULong,
36 LongLong,
37 ULongLong,
38 Int128,
39 UInt128,
40 Float,
41 Double,
42 LongDouble,
43}
44
45#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
47#[allow(dead_code)]
48pub enum CppSpecificNumericTypeKind {
49 Integer { is_signed: bool },
50 FloatingPoint,
51}
52
53#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
55pub struct CppFunctionPointerType {
56 pub return_type: Box<CppType>,
58 pub arguments: Vec<CppType>,
60 pub allows_variadic_arguments: bool,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct CppSpecificNumericType {
69 pub path: CppPath,
71 pub bits: usize,
73 pub kind: CppSpecificNumericTypeKind,
76}
77
78#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
82pub enum CppType {
83 Void,
85 BuiltInNumeric(CppBuiltInNumericType),
87 SpecificNumeric(CppSpecificNumericType),
90 PointerSizedInteger { path: CppPath, is_signed: bool },
93 Enum {
95 path: CppPath,
97 },
98 Class(CppPath),
100 TemplateParameter {
103 nested_level: usize,
110 index: usize,
113
114 name: String,
116 },
117 FunctionPointer(CppFunctionPointerType),
119 PointerLike {
120 kind: CppPointerLikeTypeKind,
121 is_const: bool,
122 target: Box<CppType>,
123 },
124}
125
126impl CppBuiltInNumericType {
127 pub fn to_cpp_code(&self) -> &'static str {
129 use self::CppBuiltInNumericType::*;
130 match *self {
131 Bool => "bool",
132 Char => "char",
133 SChar => "signed char",
134 UChar => "unsigned char",
135 WChar => "wchar_t",
136 Char16 => "char16_t",
137 Char32 => "char32_t",
138 Short => "short",
139 UShort => "unsigned short",
140 Int => "int",
141 UInt => "unsigned int",
142 Long => "long",
143 ULong => "unsigned long",
144 LongLong => "long long",
145 ULongLong => "unsigned long long",
146 Int128 => "__int128_t",
147 UInt128 => "__uint128_t",
148 Float => "float",
149 Double => "double",
150 LongDouble => "long double",
151 }
152 }
153
154 pub fn is_float(&self) -> bool {
156 use self::CppBuiltInNumericType::*;
157 match *self {
158 Float | Double | LongDouble => true,
159 _ => false,
160 }
161 }
162
163 pub fn is_signed_integer(&self) -> bool {
165 use self::CppBuiltInNumericType::*;
166 match *self {
167 SChar | Short | Int | Long | LongLong | Int128 => true,
168 _ => false,
169 }
170 }
171
172 pub fn is_unsigned_integer(&self) -> bool {
174 use self::CppBuiltInNumericType::*;
175 match *self {
176 UChar | Char16 | Char32 | UShort | UInt | ULong | ULongLong | UInt128 => true,
177 _ => false,
178 }
179 }
180
181 pub fn is_integer_with_undefined_signedness(&self) -> bool {
184 use self::CppBuiltInNumericType::*;
185 match *self {
186 Char | WChar => true,
187 _ => false,
188 }
189 }
190
191 pub fn all() -> &'static [CppBuiltInNumericType] {
193 use self::CppBuiltInNumericType::*;
194 &[
195 Bool, Char, SChar, UChar, WChar, Char16, Char32, Short, UShort, Int, UInt, Long, ULong,
196 LongLong, ULongLong, Int128, UInt128, Float, Double, LongDouble,
197 ]
198 }
199}
200
201impl CppPath {
202 pub fn instantiate(
205 &self,
206 nested_level1: usize,
207 template_arguments1: &[CppType],
208 ) -> Result<CppPath> {
209 let mut new_path = self.clone();
210 for path_item in &mut new_path.items {
211 if let Some(ref mut template_arguments) = path_item.template_arguments {
212 for arg in template_arguments {
213 *arg = arg.instantiate(nested_level1, template_arguments1)?;
214 }
215 }
216 }
217 Ok(new_path)
218 }
219}
220
221impl CppType {
222 pub fn new_pointer(is_const: bool, target: CppType) -> Self {
223 CppType::PointerLike {
224 kind: CppPointerLikeTypeKind::Pointer,
225 is_const,
226 target: Box::new(target),
227 }
228 }
229
230 pub fn new_reference(is_const: bool, target: CppType) -> Self {
231 CppType::PointerLike {
232 kind: CppPointerLikeTypeKind::Reference,
233 is_const,
234 target: Box::new(target),
235 }
236 }
237
238 #[allow(dead_code)]
239 pub fn is_void(&self) -> bool {
241 match *self {
242 CppType::Void => true,
243 _ => false,
244 }
245 }
246 pub fn is_class(&self) -> bool {
248 match *self {
249 CppType::Class(..) => true,
250 _ => false,
251 }
252 }
253 pub fn is_template_parameter(&self) -> bool {
255 match *self {
256 CppType::TemplateParameter { .. } => true,
257 _ => false,
258 }
259 }
260 pub fn is_function_pointer(&self) -> bool {
262 match *self {
263 CppType::FunctionPointer(..) => true,
264 _ => false,
265 }
266 }
267 pub fn is_or_contains_template_parameter(&self) -> bool {
270 match *self {
271 CppType::TemplateParameter { .. } => true,
272 CppType::PointerLike { ref target, .. } => target.is_or_contains_template_parameter(),
273 CppType::FunctionPointer(ref type1) => {
274 type1.return_type.is_or_contains_template_parameter()
275 || type1
276 .arguments
277 .iter()
278 .any(|arg| arg.is_or_contains_template_parameter())
279 }
280 CppType::Class(ref path) => path.items.iter().any(|item| {
281 if let Some(ref template_arguments) = item.template_arguments {
282 template_arguments
283 .iter()
284 .any(|arg| arg.is_or_contains_template_parameter())
285 } else {
286 false
287 }
288 }),
289 _ => false,
290 }
291 }
292
293 pub fn to_cpp_code(&self, function_pointer_inner_text: Option<&str>) -> Result<String> {
295 if !self.is_function_pointer() && function_pointer_inner_text.is_some() {
296 bail!("unexpected function_pointer_inner_text");
297 }
298 match *self {
299 CppType::Void => Ok("void".to_string()),
300 CppType::BuiltInNumeric(ref t) => Ok(t.to_cpp_code().to_string()),
301 CppType::Enum { ref path }
302 | CppType::SpecificNumeric(CppSpecificNumericType { ref path, .. })
303 | CppType::PointerSizedInteger { ref path, .. } => path.to_cpp_code(),
304 CppType::Class(ref path) => path.to_cpp_code(),
307 CppType::TemplateParameter { .. } => {
308 bail!("template parameters are not allowed in C++ code generator");
309 }
310 CppType::FunctionPointer(CppFunctionPointerType {
311 ref return_type,
312 ref arguments,
313 ref allows_variadic_arguments,
314 }) => {
315 if *allows_variadic_arguments {
316 bail!("function pointers with variadic arguments are not supported");
317 }
318 let mut arg_texts = Vec::new();
319 for arg in arguments {
320 arg_texts.push(arg.to_cpp_code(None)?);
321 }
322 if let Some(function_pointer_inner_text) = function_pointer_inner_text {
323 Ok(format!(
324 "{} (*{})({})",
325 return_type.as_ref().to_cpp_code(None)?,
326 function_pointer_inner_text,
327 arg_texts.join(", ")
328 ))
329 } else {
330 bail!("function_pointer_inner_text argument is missing");
331 }
332 }
333 CppType::PointerLike {
334 ref kind,
335 ref is_const,
336 ref target,
337 } => Ok(format!(
338 "{}{}{}",
339 if *is_const { "const " } else { "" },
340 target.to_cpp_code(function_pointer_inner_text)?,
341 match *kind {
342 CppPointerLikeTypeKind::Pointer => "*",
343 CppPointerLikeTypeKind::Reference => "&",
344 CppPointerLikeTypeKind::RValueReference => "&&",
345 }
346 )),
347 }
348 }
349
350 pub fn to_cpp_pseudo_code(&self) -> String {
353 match *self {
354 CppType::TemplateParameter { ref name, .. } => {
355 return name.to_string(); }
357 CppType::Class(ref base) => return base.to_cpp_pseudo_code(),
358 CppType::FunctionPointer(..) => {
359 return self
360 .to_cpp_code(Some(&"FN_PTR".to_string()))
361 .unwrap_or_else(|_| "[?]".to_string())
362 }
363 _ => {}
364 };
365 self.to_cpp_code(None).unwrap_or_else(|_| "[?]".to_string())
366 }
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub enum CppTypeRole {
372 ReturnType,
374 NotReturnType,
376}
377
378pub fn is_qflags(path: &CppPath) -> bool {
379 path.items.len() == 1 && &path.items[0].name == "QFlags"
380}
381
382impl CppType {
383 fn contains_reference(&self) -> bool {
384 if let CppType::PointerLike {
385 ref kind,
386 ref target,
387 ..
388 } = *self
389 {
390 match *kind {
391 CppPointerLikeTypeKind::Pointer => target.contains_reference(),
392 CppPointerLikeTypeKind::Reference | CppPointerLikeTypeKind::RValueReference => true,
393 }
394 } else {
395 false
396 }
397 }
398
399 #[allow(clippy::collapsible_if)]
403 pub fn to_cpp_ffi_type(&self, role: CppTypeRole) -> Result<CppFfiType> {
404 let inner = || -> Result<CppFfiType> {
405 if self.is_or_contains_template_parameter() {
406 bail!("template parameters cannot be expressed in FFI");
407 }
408 match self {
409 CppType::FunctionPointer(CppFunctionPointerType {
410 ref return_type,
411 ref arguments,
412 ref allows_variadic_arguments,
413 }) => {
414 if *allows_variadic_arguments {
415 bail!("function pointers with variadic arguments are not supported");
416 }
417 let mut all_types: Vec<&CppType> = arguments.iter().collect();
418 all_types.push(return_type.as_ref());
419 for arg in all_types {
420 match *arg {
421 CppType::FunctionPointer(..) => {
422 bail!(
424 "function pointers containing nested function pointers are \
425 not supported"
426 );
427 }
428 CppType::Class(..) => {
429 bail!(
430 "Function pointers containing classes by value are not \
431 supported"
432 );
433 }
434 _ => {}
435 }
436 if arg.contains_reference() {
437 bail!("Function pointers containing references are not supported");
438 }
439 }
440 return Ok(CppFfiType {
441 ffi_type: self.clone(),
442 conversion: CppTypeConversionToFfi::NoChange,
443 original_type: self.clone(),
444 });
445 }
446 CppType::Class(ref path) => {
447 if is_qflags(&path) {
448 return Ok(CppFfiType {
449 ffi_type: CppType::BuiltInNumeric(CppBuiltInNumericType::UInt),
450 conversion: CppTypeConversionToFfi::QFlagsToUInt,
451 original_type: self.clone(),
452 });
453 } else {
454 return Ok(CppFfiType {
455 ffi_type: CppType::PointerLike {
456 is_const: role != CppTypeRole::ReturnType,
457 kind: CppPointerLikeTypeKind::Pointer,
458 target: Box::new(self.clone()),
459 },
460 conversion: CppTypeConversionToFfi::ValueToPointer,
461 original_type: self.clone(),
462 });
463 }
464 }
465 CppType::PointerLike {
466 ref kind,
467 ref is_const,
468 ref target,
469 } => {
470 match *kind {
471 CppPointerLikeTypeKind::Pointer => {}
472 CppPointerLikeTypeKind::Reference => {
473 if *is_const {
474 if let CppType::Class(ref path) = **target {
475 if is_qflags(path) {
476 return Ok(CppFfiType {
477 ffi_type: CppType::BuiltInNumeric(
478 CppBuiltInNumericType::UInt,
479 ),
480 conversion: CppTypeConversionToFfi::QFlagsToUInt,
482 original_type: self.clone(),
483 });
484 }
485 }
486 }
487 return Ok(CppFfiType {
488 ffi_type: CppType::PointerLike {
489 is_const: *is_const,
490 kind: CppPointerLikeTypeKind::Pointer,
491 target: target.clone(),
492 },
493 conversion: CppTypeConversionToFfi::ReferenceToPointer,
494 original_type: self.clone(),
495 });
496 }
497 CppPointerLikeTypeKind::RValueReference => {
498 bail!("rvalue references are not supported");
499 }
500 }
501 }
502 _ => {}
503 }
504 Ok(CppFfiType {
505 ffi_type: self.clone(),
506 conversion: CppTypeConversionToFfi::NoChange,
507 original_type: self.clone(),
508 })
509 };
510 Ok(inner().with_context(|_| format!("Can't express type to FFI: {:?}", self))?)
511 }
512
513 #[allow(clippy::if_not_else)]
516 pub fn instantiate(
517 &self,
518 nested_level1: usize,
519 template_arguments1: &[CppType],
520 ) -> Result<CppType> {
521 match self {
522 CppType::TemplateParameter {
523 nested_level,
524 index,
525 ..
526 } => {
527 if *nested_level == nested_level1 {
528 if *index >= template_arguments1.len() {
529 bail!("not enough template arguments");
530 }
531 Ok(template_arguments1[*index].clone())
532 } else {
533 Ok(self.clone())
534 }
535 }
536 CppType::Class(ref type1) => Ok(CppType::Class(
537 type1.instantiate(nested_level1, template_arguments1)?,
538 )),
539 CppType::PointerLike {
540 ref kind,
541 ref is_const,
542 ref target,
543 } => Ok(CppType::PointerLike {
544 kind: kind.clone(),
545 is_const: *is_const,
546 target: Box::new(target.instantiate(nested_level1, template_arguments1)?),
547 }),
548 _ => Ok(self.clone()),
549 }
550 }
551}
552
553impl PartialEq for CppSpecificNumericType {
554 fn eq(&self, other: &CppSpecificNumericType) -> bool {
555 self.bits == other.bits && self.kind == other.kind
557 }
558}
559impl Eq for CppSpecificNumericType {}
560impl Hash for CppSpecificNumericType {
561 fn hash<H: Hasher>(&self, state: &mut H) {
562 self.bits.hash(state);
563 self.kind.hash(state);
564 }
565}