1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | |
5 | use std::borrow::Cow; |
6 | use std::collections::HashMap; |
7 | use std::io::Write; |
8 | |
9 | use syn::ext::IdentExt; |
10 | use syn::{self, UnOp}; |
11 | |
12 | use crate::bindgen::config::{Config, Language}; |
13 | use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; |
14 | use crate::bindgen::dependencies::Dependencies; |
15 | use crate::bindgen::ir::{ |
16 | AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, |
17 | Struct, ToCondition, Type, |
18 | }; |
19 | use crate::bindgen::library::Library; |
20 | use crate::bindgen::writer::{Source, SourceWriter}; |
21 | use crate::bindgen::Bindings; |
22 | |
23 | fn member_to_ident(member: &syn::Member) -> String { |
24 | match member { |
25 | syn::Member::Named(ref name: &Ident) => name.unraw().to_string(), |
26 | syn::Member::Unnamed(ref index: &Index) => format!("_ {}" , index.index), |
27 | } |
28 | } |
29 | |
30 | // TODO: Maybe add support to more std associated constants. |
31 | fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> { |
32 | use crate::bindgen::ir::{IntKind, PrimitiveType}; |
33 | |
34 | if name != "MAX" && name != "MIN" { |
35 | return None; |
36 | } |
37 | |
38 | let prim = PrimitiveType::maybe(associated_to.name())?; |
39 | let prefix = match prim { |
40 | PrimitiveType::Integer { |
41 | kind, |
42 | signed, |
43 | zeroable: _, |
44 | } => match kind { |
45 | IntKind::B8 => { |
46 | if signed { |
47 | "INT8" |
48 | } else { |
49 | "UINT8" |
50 | } |
51 | } |
52 | IntKind::B16 => { |
53 | if signed { |
54 | "INT16" |
55 | } else { |
56 | "UINT16" |
57 | } |
58 | } |
59 | IntKind::B32 => { |
60 | if signed { |
61 | "INT32" |
62 | } else { |
63 | "UINT32" |
64 | } |
65 | } |
66 | IntKind::B64 => { |
67 | if signed { |
68 | "INT64" |
69 | } else { |
70 | "UINT64" |
71 | } |
72 | } |
73 | _ => return None, |
74 | }, |
75 | _ => return None, |
76 | }; |
77 | Some(format!(" {}_ {}" , prefix, name)) |
78 | } |
79 | |
80 | #[derive (Debug, Clone)] |
81 | pub enum Literal { |
82 | Expr(String), |
83 | Path { |
84 | associated_to: Option<(Path, String)>, |
85 | name: String, |
86 | }, |
87 | PostfixUnaryOp { |
88 | op: &'static str, |
89 | value: Box<Literal>, |
90 | }, |
91 | BinOp { |
92 | left: Box<Literal>, |
93 | op: &'static str, |
94 | right: Box<Literal>, |
95 | }, |
96 | FieldAccess { |
97 | base: Box<Literal>, |
98 | field: String, |
99 | }, |
100 | Struct { |
101 | path: Path, |
102 | export_name: String, |
103 | fields: HashMap<String, Literal>, |
104 | }, |
105 | Cast { |
106 | ty: Type, |
107 | value: Box<Literal>, |
108 | }, |
109 | } |
110 | |
111 | impl Literal { |
112 | fn replace_self_with(&mut self, self_ty: &Path) { |
113 | match *self { |
114 | Literal::PostfixUnaryOp { ref mut value, .. } => { |
115 | value.replace_self_with(self_ty); |
116 | } |
117 | Literal::BinOp { |
118 | ref mut left, |
119 | ref mut right, |
120 | .. |
121 | } => { |
122 | left.replace_self_with(self_ty); |
123 | right.replace_self_with(self_ty); |
124 | } |
125 | Literal::FieldAccess { ref mut base, .. } => { |
126 | base.replace_self_with(self_ty); |
127 | } |
128 | Literal::Struct { |
129 | ref mut path, |
130 | ref mut export_name, |
131 | ref mut fields, |
132 | } => { |
133 | if path.replace_self_with(self_ty) { |
134 | *export_name = self_ty.name().to_owned(); |
135 | } |
136 | for ref mut expr in fields.values_mut() { |
137 | expr.replace_self_with(self_ty); |
138 | } |
139 | } |
140 | Literal::Cast { |
141 | ref mut ty, |
142 | ref mut value, |
143 | } => { |
144 | ty.replace_self_with(self_ty); |
145 | value.replace_self_with(self_ty); |
146 | } |
147 | Literal::Path { |
148 | ref mut associated_to, |
149 | .. |
150 | } => { |
151 | if let Some((ref mut path, ref mut export_name)) = *associated_to { |
152 | if path.replace_self_with(self_ty) { |
153 | *export_name = self_ty.name().to_owned(); |
154 | } |
155 | } |
156 | } |
157 | Literal::Expr(..) => {} |
158 | } |
159 | } |
160 | |
161 | fn is_valid(&self, bindings: &Bindings) -> bool { |
162 | match *self { |
163 | Literal::Expr(..) => true, |
164 | Literal::Path { |
165 | ref associated_to, |
166 | ref name, |
167 | } => { |
168 | if let Some((ref path, _export_name)) = associated_to { |
169 | return bindings.struct_exists(path) |
170 | || to_known_assoc_constant(path, name).is_some(); |
171 | } |
172 | true |
173 | } |
174 | Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings), |
175 | Literal::BinOp { |
176 | ref left, |
177 | ref right, |
178 | .. |
179 | } => left.is_valid(bindings) && right.is_valid(bindings), |
180 | Literal::FieldAccess { ref base, .. } => base.is_valid(bindings), |
181 | Literal::Struct { ref path, .. } => bindings.struct_exists(path), |
182 | Literal::Cast { ref value, .. } => value.is_valid(bindings), |
183 | } |
184 | } |
185 | |
186 | fn can_be_constexpr(&self) -> bool { |
187 | !self.has_pointer_casts() |
188 | } |
189 | |
190 | fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool { |
191 | if !visitor(self) { |
192 | return false; |
193 | } |
194 | match self { |
195 | Literal::Expr(..) | Literal::Path { .. } => true, |
196 | Literal::PostfixUnaryOp { ref value, .. } => value.visit(visitor), |
197 | Literal::BinOp { |
198 | ref left, |
199 | ref right, |
200 | .. |
201 | } => left.visit(visitor) && right.visit(visitor), |
202 | Literal::FieldAccess { ref base, .. } => base.visit(visitor), |
203 | Literal::Struct { ref fields, .. } => { |
204 | for (_name, field) in fields.iter() { |
205 | if !field.visit(visitor) { |
206 | return false; |
207 | } |
208 | } |
209 | true |
210 | } |
211 | Literal::Cast { ref value, .. } => value.visit(visitor), |
212 | } |
213 | } |
214 | |
215 | fn has_pointer_casts(&self) -> bool { |
216 | let mut has_pointer_casts = false; |
217 | self.visit(&mut |lit| { |
218 | if let Literal::Cast { ref ty, .. } = *lit { |
219 | has_pointer_casts = has_pointer_casts || ty.is_ptr(); |
220 | } |
221 | !has_pointer_casts |
222 | }); |
223 | has_pointer_casts |
224 | } |
225 | |
226 | pub fn uses_only_primitive_types(&self) -> bool { |
227 | let mut uses_only_primitive_types = true; |
228 | self.visit(&mut |lit| { |
229 | // XXX This is a bit sketchy, but alas. |
230 | uses_only_primitive_types = uses_only_primitive_types |
231 | && match *lit { |
232 | Literal::Struct { .. } => false, |
233 | Literal::Cast { ref ty, .. } => ty.is_primitive_or_ptr_primitive(), |
234 | _ => true, |
235 | }; |
236 | uses_only_primitive_types |
237 | }); |
238 | uses_only_primitive_types |
239 | } |
240 | } |
241 | |
242 | impl Literal { |
243 | pub fn rename_for_config(&mut self, config: &Config) { |
244 | match self { |
245 | Literal::Struct { |
246 | ref mut export_name, |
247 | fields, |
248 | .. |
249 | } => { |
250 | config.export.rename(export_name); |
251 | for lit in fields.values_mut() { |
252 | lit.rename_for_config(config); |
253 | } |
254 | } |
255 | Literal::FieldAccess { ref mut base, .. } => { |
256 | base.rename_for_config(config); |
257 | } |
258 | Literal::Path { |
259 | ref mut associated_to, |
260 | ref mut name, |
261 | } => { |
262 | if let Some((_path, ref mut export_name)) = associated_to { |
263 | config.export.rename(export_name); |
264 | } else { |
265 | config.export.rename(name); |
266 | } |
267 | } |
268 | Literal::PostfixUnaryOp { ref mut value, .. } => { |
269 | value.rename_for_config(config); |
270 | } |
271 | Literal::BinOp { |
272 | ref mut left, |
273 | ref mut right, |
274 | .. |
275 | } => { |
276 | left.rename_for_config(config); |
277 | right.rename_for_config(config); |
278 | } |
279 | Literal::Expr(_) => {} |
280 | Literal::Cast { |
281 | ref mut ty, |
282 | ref mut value, |
283 | } => { |
284 | ty.rename_for_config(config, &GenericParams::default()); |
285 | value.rename_for_config(config); |
286 | } |
287 | } |
288 | } |
289 | |
290 | // Translate from full blown `syn::Expr` into a simpler `Literal` type |
291 | pub fn load(expr: &syn::Expr) -> Result<Literal, String> { |
292 | match *expr { |
293 | // Match binary expressions of the form `a * b` |
294 | syn::Expr::Binary(ref bin_expr) => { |
295 | let l = Self::load(&bin_expr.left)?; |
296 | let r = Self::load(&bin_expr.right)?; |
297 | let op = match bin_expr.op { |
298 | syn::BinOp::Add(..) => "+" , |
299 | syn::BinOp::Sub(..) => "-" , |
300 | syn::BinOp::Mul(..) => "*" , |
301 | syn::BinOp::Div(..) => "/" , |
302 | syn::BinOp::Rem(..) => "%" , |
303 | syn::BinOp::And(..) => "&&" , |
304 | syn::BinOp::Or(..) => "||" , |
305 | syn::BinOp::BitXor(..) => "^" , |
306 | syn::BinOp::BitAnd(..) => "&" , |
307 | syn::BinOp::BitOr(..) => "|" , |
308 | syn::BinOp::Shl(..) => "<<" , |
309 | syn::BinOp::Shr(..) => ">>" , |
310 | syn::BinOp::Eq(..) => "==" , |
311 | syn::BinOp::Lt(..) => "<" , |
312 | syn::BinOp::Le(..) => "<=" , |
313 | syn::BinOp::Ne(..) => "!=" , |
314 | syn::BinOp::Ge(..) => ">=" , |
315 | syn::BinOp::Gt(..) => ">" , |
316 | syn::BinOp::AddEq(..) => "+=" , |
317 | syn::BinOp::SubEq(..) => "-=" , |
318 | syn::BinOp::MulEq(..) => "*=" , |
319 | syn::BinOp::DivEq(..) => "/=" , |
320 | syn::BinOp::RemEq(..) => "%=" , |
321 | syn::BinOp::BitXorEq(..) => "^=" , |
322 | syn::BinOp::BitAndEq(..) => "&=" , |
323 | syn::BinOp::BitOrEq(..) => "|=" , |
324 | syn::BinOp::ShlEq(..) => "<<=" , |
325 | syn::BinOp::ShrEq(..) => ">>=" , |
326 | }; |
327 | Ok(Literal::BinOp { |
328 | left: Box::new(l), |
329 | op, |
330 | right: Box::new(r), |
331 | }) |
332 | } |
333 | |
334 | // Match literals like true, 'a', 32 etc |
335 | syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => { |
336 | match lit { |
337 | syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!(" {}" , value.value()))), |
338 | syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 { |
339 | 0..=255 => format!("' {}'" , value.value().escape_default()), |
340 | other_code => format!(r"U'\U {:08X}'" , other_code), |
341 | })), |
342 | syn::Lit::Int(ref value) => { |
343 | let suffix = match value.suffix() { |
344 | "u64" => "ull" , |
345 | "i64" => "ll" , |
346 | "u32" => "u" , |
347 | _ if value.base10_parse::<i64>().is_err() => "ull" , |
348 | _ => "" , |
349 | }; |
350 | Ok(Literal::Expr(format!( |
351 | " {}{}" , |
352 | value.base10_digits(), |
353 | suffix |
354 | ))) |
355 | } |
356 | syn::Lit::Float(ref value) => { |
357 | Ok(Literal::Expr(value.base10_digits().to_string())) |
358 | } |
359 | syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!(" {}" , value.value))), |
360 | // TODO: Add support for byte string and Verbatim |
361 | _ => Err(format!("Unsupported literal expression. {:?}" , *lit)), |
362 | } |
363 | } |
364 | |
365 | syn::Expr::Field(syn::ExprField { |
366 | ref base, |
367 | ref member, |
368 | .. |
369 | }) => Ok(Literal::FieldAccess { |
370 | base: Box::new(Literal::load(base)?), |
371 | field: member_to_ident(member), |
372 | }), |
373 | |
374 | syn::Expr::Call(syn::ExprCall { |
375 | ref func, ref args, .. |
376 | }) => { |
377 | let struct_name = match Literal::load(func)? { |
378 | Literal::Path { |
379 | associated_to: None, |
380 | name, |
381 | } => name, |
382 | _ => return Err(format!("Unsupported call expression. {:?}" , *expr)), |
383 | }; |
384 | let mut fields = HashMap::<String, Literal>::default(); |
385 | for (index, arg) in args.iter().enumerate() { |
386 | let ident = |
387 | member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string(); |
388 | let value = Literal::load(arg)?; |
389 | fields.insert(ident, value); |
390 | } |
391 | Ok(Literal::Struct { |
392 | path: Path::new(struct_name.clone()), |
393 | export_name: struct_name, |
394 | fields, |
395 | }) |
396 | } |
397 | |
398 | syn::Expr::Struct(syn::ExprStruct { |
399 | ref path, |
400 | ref fields, |
401 | .. |
402 | }) => { |
403 | let struct_name = path.segments[0].ident.unraw().to_string(); |
404 | let mut field_map = HashMap::<String, Literal>::default(); |
405 | for field in fields { |
406 | let ident = member_to_ident(&field.member).to_string(); |
407 | let value = Literal::load(&field.expr)?; |
408 | field_map.insert(ident, value); |
409 | } |
410 | Ok(Literal::Struct { |
411 | path: Path::new(struct_name.clone()), |
412 | export_name: struct_name, |
413 | fields: field_map, |
414 | }) |
415 | } |
416 | |
417 | syn::Expr::Unary(syn::ExprUnary { |
418 | ref op, ref expr, .. |
419 | }) => match *op { |
420 | UnOp::Not(_) => { |
421 | let val = Self::load(expr)?; |
422 | Ok(Literal::PostfixUnaryOp { |
423 | op: "~" , |
424 | value: Box::new(val), |
425 | }) |
426 | } |
427 | UnOp::Neg(_) => { |
428 | let val = Self::load(expr)?; |
429 | Ok(Literal::PostfixUnaryOp { |
430 | op: "-" , |
431 | value: Box::new(val), |
432 | }) |
433 | } |
434 | _ => Err(format!("Unsupported Unary expression. {:?}" , *op)), |
435 | }, |
436 | |
437 | // Match identifiers, like `5 << SHIFT` |
438 | syn::Expr::Path(syn::ExprPath { ref path, .. }) => { |
439 | // Handle only the simplest identifiers and Associated::IDENT |
440 | // kind of syntax. |
441 | Ok(match path.segments.len() { |
442 | 1 => Literal::Path { |
443 | associated_to: None, |
444 | name: path.segments[0].ident.to_string(), |
445 | }, |
446 | 2 => { |
447 | let struct_name = path.segments[0].ident.to_string(); |
448 | Literal::Path { |
449 | associated_to: Some((Path::new(&struct_name), struct_name)), |
450 | name: path.segments[1].ident.to_string(), |
451 | } |
452 | } |
453 | _ => return Err(format!("Unsupported path expression. {:?}" , path)), |
454 | }) |
455 | } |
456 | |
457 | syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr), |
458 | |
459 | syn::Expr::Cast(syn::ExprCast { |
460 | ref expr, ref ty, .. |
461 | }) => { |
462 | let val = Self::load(expr)?; |
463 | match Type::load(ty)? { |
464 | Some(ty) => Ok(Literal::Cast { |
465 | ty, |
466 | value: Box::new(val), |
467 | }), |
468 | None => Err("Cannot cast to zero sized type." .to_owned()), |
469 | } |
470 | } |
471 | |
472 | _ => Err(format!("Unsupported expression. {:?}" , *expr)), |
473 | } |
474 | } |
475 | |
476 | pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { |
477 | match self { |
478 | Literal::Expr(v) => match (&**v, config.language) { |
479 | ("true" , Language::Cython) => write!(out, "True" ), |
480 | ("false" , Language::Cython) => write!(out, "False" ), |
481 | (v, _) => write!(out, " {}" , v), |
482 | }, |
483 | Literal::Path { |
484 | ref associated_to, |
485 | ref name, |
486 | } => { |
487 | if let Some((ref path, ref export_name)) = associated_to { |
488 | if let Some(known) = to_known_assoc_constant(path, name) { |
489 | return write!(out, " {}" , known); |
490 | } |
491 | let path_separator = match config.language { |
492 | Language::Cython | Language::C => "_" , |
493 | Language::Cxx => { |
494 | if config.structure.associated_constants_in_body { |
495 | "::" |
496 | } else { |
497 | "_" |
498 | } |
499 | } |
500 | }; |
501 | write!(out, " {}{}" , export_name, path_separator) |
502 | } |
503 | write!(out, " {}" , name) |
504 | } |
505 | Literal::FieldAccess { |
506 | ref base, |
507 | ref field, |
508 | } => { |
509 | write!(out, "(" ); |
510 | base.write(config, out); |
511 | write!(out, "). {}" , field); |
512 | } |
513 | Literal::PostfixUnaryOp { op, ref value } => { |
514 | write!(out, " {}" , op); |
515 | value.write(config, out); |
516 | } |
517 | Literal::BinOp { |
518 | ref left, |
519 | op, |
520 | ref right, |
521 | } => { |
522 | write!(out, "(" ); |
523 | left.write(config, out); |
524 | write!(out, " {} " , op); |
525 | right.write(config, out); |
526 | write!(out, ")" ); |
527 | } |
528 | Literal::Cast { ref ty, ref value } => { |
529 | out.write(if config.language == Language::Cython { |
530 | "<" |
531 | } else { |
532 | "(" |
533 | }); |
534 | ty.write(config, out); |
535 | out.write(if config.language == Language::Cython { |
536 | ">" |
537 | } else { |
538 | ")" |
539 | }); |
540 | value.write(config, out); |
541 | } |
542 | Literal::Struct { |
543 | export_name, |
544 | fields, |
545 | path, |
546 | } => { |
547 | match config.language { |
548 | Language::C => write!(out, "( {})" , export_name), |
549 | Language::Cxx => write!(out, " {}" , export_name), |
550 | Language::Cython => write!(out, "< {}>" , export_name), |
551 | } |
552 | |
553 | write!(out, " {{ " ); |
554 | let mut is_first_field = true; |
555 | // In C++, same order as defined is required. |
556 | let ordered_fields = out.bindings().struct_field_names(path); |
557 | for ordered_key in ordered_fields.iter() { |
558 | if let Some(lit) = fields.get(ordered_key) { |
559 | if !is_first_field { |
560 | write!(out, ", " ); |
561 | } else { |
562 | is_first_field = false; |
563 | } |
564 | match config.language { |
565 | Language::Cxx => write!(out, "/* . {} = */ " , ordered_key), |
566 | Language::C => write!(out, ". {} = " , ordered_key), |
567 | Language::Cython => {} |
568 | } |
569 | lit.write(config, out); |
570 | } |
571 | } |
572 | write!(out, " }}" ); |
573 | } |
574 | } |
575 | } |
576 | } |
577 | |
578 | #[derive (Debug, Clone)] |
579 | pub struct Constant { |
580 | pub path: Path, |
581 | pub export_name: String, |
582 | pub ty: Type, |
583 | pub value: Literal, |
584 | pub cfg: Option<Cfg>, |
585 | pub annotations: AnnotationSet, |
586 | pub documentation: Documentation, |
587 | pub associated_to: Option<Path>, |
588 | } |
589 | |
590 | impl Constant { |
591 | pub fn load( |
592 | path: Path, |
593 | mod_cfg: Option<&Cfg>, |
594 | ty: &syn::Type, |
595 | expr: &syn::Expr, |
596 | attrs: &[syn::Attribute], |
597 | associated_to: Option<Path>, |
598 | ) -> Result<Constant, String> { |
599 | let ty = Type::load(ty)?; |
600 | let mut ty = match ty { |
601 | Some(ty) => ty, |
602 | None => { |
603 | return Err("Cannot have a zero sized const definition." .to_owned()); |
604 | } |
605 | }; |
606 | |
607 | let mut lit = Literal::load(expr)?; |
608 | |
609 | if let Some(ref associated_to) = associated_to { |
610 | ty.replace_self_with(associated_to); |
611 | lit.replace_self_with(associated_to); |
612 | } |
613 | |
614 | Ok(Constant::new( |
615 | path, |
616 | ty, |
617 | lit, |
618 | Cfg::append(mod_cfg, Cfg::load(attrs)), |
619 | AnnotationSet::load(attrs)?, |
620 | Documentation::load(attrs), |
621 | associated_to, |
622 | )) |
623 | } |
624 | |
625 | pub fn new( |
626 | path: Path, |
627 | ty: Type, |
628 | value: Literal, |
629 | cfg: Option<Cfg>, |
630 | annotations: AnnotationSet, |
631 | documentation: Documentation, |
632 | associated_to: Option<Path>, |
633 | ) -> Self { |
634 | let export_name = path.name().to_owned(); |
635 | Self { |
636 | path, |
637 | export_name, |
638 | ty, |
639 | value, |
640 | cfg, |
641 | annotations, |
642 | documentation, |
643 | associated_to, |
644 | } |
645 | } |
646 | |
647 | pub fn uses_only_primitive_types(&self) -> bool { |
648 | self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive() |
649 | } |
650 | } |
651 | |
652 | impl Item for Constant { |
653 | fn path(&self) -> &Path { |
654 | &self.path |
655 | } |
656 | |
657 | fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { |
658 | self.ty.add_dependencies(library, out); |
659 | } |
660 | |
661 | fn export_name(&self) -> &str { |
662 | &self.export_name |
663 | } |
664 | |
665 | fn cfg(&self) -> Option<&Cfg> { |
666 | self.cfg.as_ref() |
667 | } |
668 | |
669 | fn annotations(&self) -> &AnnotationSet { |
670 | &self.annotations |
671 | } |
672 | |
673 | fn annotations_mut(&mut self) -> &mut AnnotationSet { |
674 | &mut self.annotations |
675 | } |
676 | |
677 | fn container(&self) -> ItemContainer { |
678 | ItemContainer::Constant(self.clone()) |
679 | } |
680 | |
681 | fn rename_for_config(&mut self, config: &Config) { |
682 | if self.associated_to.is_none() { |
683 | config.export.rename(&mut self.export_name); |
684 | } |
685 | self.value.rename_for_config(config); |
686 | self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here |
687 | } |
688 | |
689 | fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { |
690 | self.ty.resolve_declaration_types(resolver); |
691 | } |
692 | } |
693 | |
694 | impl Constant { |
695 | pub fn write_declaration<F: Write>( |
696 | &self, |
697 | config: &Config, |
698 | out: &mut SourceWriter<F>, |
699 | associated_to_struct: &Struct, |
700 | ) { |
701 | debug_assert!(self.associated_to.is_some()); |
702 | debug_assert!(config.language == Language::Cxx); |
703 | debug_assert!(!associated_to_struct.is_transparent); |
704 | debug_assert!(config.structure.associated_constants_in_body); |
705 | debug_assert!(config.constant.allow_static_const); |
706 | |
707 | if let Type::Ptr { is_const: true, .. } = self.ty { |
708 | out.write("static " ); |
709 | } else { |
710 | out.write("static const " ); |
711 | } |
712 | self.ty.write(config, out); |
713 | write!(out, " {};" , self.export_name()) |
714 | } |
715 | |
716 | pub fn write<F: Write>( |
717 | &self, |
718 | config: &Config, |
719 | out: &mut SourceWriter<F>, |
720 | associated_to_struct: Option<&Struct>, |
721 | ) { |
722 | if let Some(assoc) = associated_to_struct { |
723 | if assoc.is_generic() { |
724 | return; // Not tested / implemented yet, so bail out. |
725 | } |
726 | } |
727 | |
728 | if !self.value.is_valid(out.bindings()) { |
729 | return; |
730 | } |
731 | |
732 | let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent); |
733 | |
734 | let in_body = associated_to_struct.is_some() |
735 | && config.language == Language::Cxx |
736 | && config.structure.associated_constants_in_body |
737 | && config.constant.allow_static_const |
738 | && !associated_to_transparent; |
739 | |
740 | let condition = self.cfg.to_condition(config); |
741 | condition.write_before(config, out); |
742 | |
743 | let name = if in_body { |
744 | Cow::Owned(format!( |
745 | " {}:: {}" , |
746 | associated_to_struct.unwrap().export_name(), |
747 | self.export_name(), |
748 | )) |
749 | } else if self.associated_to.is_none() { |
750 | Cow::Borrowed(self.export_name()) |
751 | } else { |
752 | let associated_name = match associated_to_struct { |
753 | Some(s) => Cow::Borrowed(s.export_name()), |
754 | None => { |
755 | let mut name = self.associated_to.as_ref().unwrap().name().to_owned(); |
756 | config.export.rename(&mut name); |
757 | Cow::Owned(name) |
758 | } |
759 | }; |
760 | |
761 | Cow::Owned(format!(" {}_ {}" , associated_name, self.export_name())) |
762 | }; |
763 | |
764 | let value = match self.value { |
765 | Literal::Struct { |
766 | ref fields, |
767 | ref path, |
768 | .. |
769 | } if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1, |
770 | _ => &self.value, |
771 | }; |
772 | |
773 | self.documentation.write(config, out); |
774 | |
775 | let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr(); |
776 | match config.language { |
777 | Language::Cxx if config.constant.allow_static_const || allow_constexpr => { |
778 | if allow_constexpr { |
779 | out.write("constexpr " ) |
780 | } |
781 | |
782 | if config.constant.allow_static_const { |
783 | out.write(if in_body { "inline " } else { "static " }); |
784 | } |
785 | |
786 | if let Type::Ptr { is_const: true, .. } = self.ty { |
787 | // Nothing. |
788 | } else { |
789 | out.write("const " ); |
790 | } |
791 | |
792 | self.ty.write(config, out); |
793 | write!(out, " {} = " , name); |
794 | value.write(config, out); |
795 | write!(out, ";" ); |
796 | } |
797 | Language::Cxx | Language::C => { |
798 | write!(out, "#define {} " , name); |
799 | value.write(config, out); |
800 | } |
801 | Language::Cython => { |
802 | out.write("const " ); |
803 | self.ty.write(config, out); |
804 | // For extern Cython declarations the initializer is ignored, |
805 | // but still useful as documentation, so we write it as a comment. |
806 | write!(out, " {} # = " , name); |
807 | value.write(config, out); |
808 | } |
809 | } |
810 | |
811 | condition.write_after(config, out); |
812 | } |
813 | } |
814 | |