| 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 | |