Skip to content

Commit

Permalink
constant: Add support for associated constant expressions.
Browse files Browse the repository at this point in the history
These are useful for bitflags.
  • Loading branch information
emilio committed Apr 14, 2022
1 parent 20ddfff commit ec6c659
Show file tree
Hide file tree
Showing 21 changed files with 169 additions and 24 deletions.
126 changes: 104 additions & 22 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
use crate::bindgen::Bindings;

fn member_to_ident(member: &syn::Member) -> String {
match member {
syn::Member::Named(ref name) => name.unraw().to_string(),
syn::Member::Unnamed(ref index) => format!("_{}", index.index),
}
}

#[derive(Debug, Clone)]
pub enum Literal {
Expr(String),
Path(String),
Path {
associated_to: Option<(Path, String)>,
name: String,
},
PostfixUnaryOp {
op: &'static str,
value: Box<Literal>,
Expand All @@ -33,6 +43,10 @@ pub enum Literal {
op: &'static str,
right: Box<Literal>,
},
FieldAccess {
base: Box<Literal>,
field: String,
},
Struct {
path: Path,
export_name: String,
Expand All @@ -58,6 +72,9 @@ impl Literal {
left.replace_self_with(self_ty);
right.replace_self_with(self_ty);
}
Literal::FieldAccess { ref mut base, .. } => {
base.replace_self_with(self_ty);
}
Literal::Struct {
ref mut path,
ref mut export_name,
Expand All @@ -77,35 +94,51 @@ impl Literal {
ty.replace_self_with(self_ty);
value.replace_self_with(self_ty);
}
Literal::Expr(..) | Literal::Path(..) => {}
Literal::Path {
ref mut associated_to,
..
} => {
if let Some((ref mut path, ref mut export_name)) = *associated_to {
if path.replace_self_with(self_ty) {
*export_name = self_ty.name().to_owned();
}
}
}
Literal::Expr(..) => {}
}
}

fn is_valid(&self, bindings: &Bindings) -> bool {
match *self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::Path { ref associated_to, .. } => {
if let Some((ref path, _export_name)) = associated_to {
return bindings.struct_exists(path);
}
true
},
Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
Literal::BinOp {
ref left,
ref right,
..
} => left.is_valid(bindings) && right.is_valid(bindings),
Literal::FieldAccess { ref base, .. } => base.is_valid(bindings),
Literal::Struct { ref path, .. } => bindings.struct_exists(path),
Literal::Cast { ref value, .. } => value.is_valid(bindings),
}
}

pub fn uses_only_primitive_types(&self) -> bool {
match self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::Expr(..) | Literal::Path { .. } => true,
Literal::PostfixUnaryOp { ref value, .. } => value.uses_only_primitive_types(),
Literal::BinOp {
ref left,
ref right,
..
} => left.uses_only_primitive_types() & right.uses_only_primitive_types(),
Literal::FieldAccess { ref base, .. } => base.uses_only_primitive_types(),
Literal::Struct { .. } => false,
Literal::Cast { ref value, ref ty } => {
value.uses_only_primitive_types() && ty.is_primitive_or_ptr_primitive()
Expand All @@ -127,8 +160,18 @@ impl Literal {
lit.rename_for_config(config);
}
}
Literal::Path(ref mut name) => {
config.export.rename(name);
Literal::FieldAccess { ref mut base, .. } => {
base.rename_for_config(config);
}
Literal::Path {
ref mut associated_to,
ref mut name,
} => {
if let Some((_path, ref mut export_name)) = associated_to {
config.export.rename(export_name);
} else {
config.export.rename(name);
}
}
Literal::PostfixUnaryOp { ref mut value, .. } => {
value.rename_for_config(config);
Expand Down Expand Up @@ -220,6 +263,15 @@ impl Literal {
}
}

syn::Expr::Field(syn::ExprField {
ref base,
ref member,
..
}) => Ok(Literal::FieldAccess {
base: Box::new(Literal::load(base)?),
field: member_to_ident(member),
}),

syn::Expr::Struct(syn::ExprStruct {
ref path,
ref fields,
Expand All @@ -228,10 +280,7 @@ impl Literal {
let struct_name = path.segments[0].ident.unraw().to_string();
let mut field_map = HashMap::<String, Literal>::default();
for field in fields {
let ident = match field.member {
syn::Member::Named(ref name) => name.unraw().to_string(),
syn::Member::Unnamed(ref index) => format!("_{}", index.index),
};
let ident = member_to_ident(&field.member);
let key = ident.to_string();
let value = Literal::load(&field.expr)?;
field_map.insert(key, value);
Expand All @@ -257,16 +306,23 @@ impl Literal {
},

// Match identifiers, like `5 << SHIFT`
syn::Expr::Path(syn::ExprPath {
path: syn::Path { ref segments, .. },
..
}) => {
// Handle only the simplest identifiers and error for anything else.
if segments.len() == 1 {
Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
} else {
Err(format!("Unsupported path expression. {:?}", *segments))
}
syn::Expr::Path(syn::ExprPath { ref path, .. }) => {
// Handle only the simplest identifiers and Associated::IDENT
// kind of syntax.
Ok(match path.segments.len() {
1 => Literal::Path {
associated_to: None,
name: path.segments[0].ident.to_string(),
},
2 => {
let struct_name = path.segments[0].ident.to_string();
Literal::Path {
associated_to: Some((Path::new(&struct_name), struct_name)),
name: path.segments[1].ident.to_string(),
}
}
_ => return Err(format!("Unsupported path expression. {:?}", path)),
})
}

syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
Expand Down Expand Up @@ -295,7 +351,33 @@ impl Literal {
("false", Language::Cython) => write!(out, "False"),
(v, _) => write!(out, "{}", v),
},
Literal::Path(v) => write!(out, "{}", v),
Literal::Path {
ref associated_to,
ref name,
} => {
if let Some((_, ref export_name)) = associated_to {
let path_separator = match config.language {
Language::Cython | Language::C => "_",
Language::Cxx => {
if config.structure.associated_constants_in_body {
"::"
} else {
"_"
}
}
};
write!(out, "{}{}", export_name, path_separator)
}
write!(out, "{}", name)
}
Literal::FieldAccess {
ref base,
ref field,
} => {
write!(out, "(");
base.write(config, out);
write!(out, ").{}", field);
}
Literal::PostfixUnaryOp { op, ref value } => {
write!(out, "{}", op);
value.write(config, out);
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.both.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ typedef struct StyleAlignFlags {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

void root(struct StyleAlignFlags flags);
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.both.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct StyleAlignFlags {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

#ifdef __cplusplus
extern "C" {
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ typedef struct {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

void root(StyleAlignFlags flags);
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

#ifdef __cplusplus
extern "C" {
Expand Down
6 changes: 6 additions & 0 deletions tests/expectations/associated_in_body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ struct StyleAlignFlags {
static const StyleAlignFlags NORMAL;
static const StyleAlignFlags START;
static const StyleAlignFlags END;
static const StyleAlignFlags ALIAS;
static const StyleAlignFlags FLEX_START;
static const StyleAlignFlags MIXED;
static const StyleAlignFlags MIXED_SELF;
};
/// 'auto'
inline const StyleAlignFlags StyleAlignFlags::AUTO = StyleAlignFlags{ /* .bits = */ (uint8_t)0 };
Expand All @@ -51,8 +54,11 @@ inline const StyleAlignFlags StyleAlignFlags::NORMAL = StyleAlignFlags{ /* .bits
inline const StyleAlignFlags StyleAlignFlags::START = StyleAlignFlags{ /* .bits = */ (uint8_t)(1 << 1) };
/// 'end'
inline const StyleAlignFlags StyleAlignFlags::END = StyleAlignFlags{ /* .bits = */ (uint8_t)(1 << 2) };
inline const StyleAlignFlags StyleAlignFlags::ALIAS = StyleAlignFlags{ /* .bits = */ (uint8_t)(StyleAlignFlags::END).bits };
/// 'flex-start'
inline const StyleAlignFlags StyleAlignFlags::FLEX_START = StyleAlignFlags{ /* .bits = */ (uint8_t)(1 << 3) };
inline const StyleAlignFlags StyleAlignFlags::MIXED = StyleAlignFlags{ /* .bits = */ (uint8_t)(((1 << 4) | (StyleAlignFlags::FLEX_START).bits) | (StyleAlignFlags::END).bits) };
inline const StyleAlignFlags StyleAlignFlags::MIXED_SELF = StyleAlignFlags{ /* .bits = */ (uint8_t)(((1 << 5) | (StyleAlignFlags::FLEX_START).bits) | (StyleAlignFlags::END).bits) };

extern "C" {

Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ cdef extern from *:
const StyleAlignFlags StyleAlignFlags_START # = <StyleAlignFlags>{ <uint8_t>(1 << 1) }
# 'end'
const StyleAlignFlags StyleAlignFlags_END # = <StyleAlignFlags>{ <uint8_t>(1 << 2) }
const StyleAlignFlags StyleAlignFlags_ALIAS # = <StyleAlignFlags>{ <uint8_t>(StyleAlignFlags_END).bits }
# 'flex-start'
const StyleAlignFlags StyleAlignFlags_FLEX_START # = <StyleAlignFlags>{ <uint8_t>(1 << 3) }
const StyleAlignFlags StyleAlignFlags_MIXED # = <StyleAlignFlags>{ <uint8_t>(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
const StyleAlignFlags StyleAlignFlags_MIXED_SELF # = <StyleAlignFlags>{ <uint8_t>(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

void root(StyleAlignFlags flags);
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ struct StyleAlignFlags {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

void root(struct StyleAlignFlags flags);
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.tag.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ struct StyleAlignFlags {
* 'end'
*/
#define StyleAlignFlags_END (StyleAlignFlags){ .bits = (uint8_t)(1 << 2) }
#define StyleAlignFlags_ALIAS (StyleAlignFlags){ .bits = (uint8_t)(StyleAlignFlags_END).bits }
/**
* 'flex-start'
*/
#define StyleAlignFlags_FLEX_START (StyleAlignFlags){ .bits = (uint8_t)(1 << 3) }
#define StyleAlignFlags_MIXED (StyleAlignFlags){ .bits = (uint8_t)(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
#define StyleAlignFlags_MIXED_SELF (StyleAlignFlags){ .bits = (uint8_t)(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

#ifdef __cplusplus
extern "C" {
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/associated_in_body.tag.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ cdef extern from *:
const StyleAlignFlags StyleAlignFlags_START # = <StyleAlignFlags>{ <uint8_t>(1 << 1) }
# 'end'
const StyleAlignFlags StyleAlignFlags_END # = <StyleAlignFlags>{ <uint8_t>(1 << 2) }
const StyleAlignFlags StyleAlignFlags_ALIAS # = <StyleAlignFlags>{ <uint8_t>(StyleAlignFlags_END).bits }
# 'flex-start'
const StyleAlignFlags StyleAlignFlags_FLEX_START # = <StyleAlignFlags>{ <uint8_t>(1 << 3) }
const StyleAlignFlags StyleAlignFlags_MIXED # = <StyleAlignFlags>{ <uint8_t>(((1 << 4) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }
const StyleAlignFlags StyleAlignFlags_MIXED_SELF # = <StyleAlignFlags>{ <uint8_t>(((1 << 5) | (StyleAlignFlags_FLEX_START).bits) | (StyleAlignFlags_END).bits) }

void root(StyleAlignFlags flags);
3 changes: 3 additions & 0 deletions tests/expectations/bitflags.both.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct AlignFlags {
* 'end'
*/
#define AlignFlags_END (AlignFlags){ .bits = (uint8_t)(1 << 2) }
#define AlignFlags_ALIAS (AlignFlags){ .bits = (uint8_t)(AlignFlags_END).bits }
/**
* 'flex-start'
*/
#define AlignFlags_FLEX_START (AlignFlags){ .bits = (uint8_t)(1 << 3) }
#define AlignFlags_MIXED (AlignFlags){ .bits = (uint8_t)(((1 << 4) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }
#define AlignFlags_MIXED_SELF (AlignFlags){ .bits = (uint8_t)(((1 << 5) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }

typedef struct DebugFlags {
uint32_t bits;
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/bitflags.both.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct AlignFlags {
* 'end'
*/
#define AlignFlags_END (AlignFlags){ .bits = (uint8_t)(1 << 2) }
#define AlignFlags_ALIAS (AlignFlags){ .bits = (uint8_t)(AlignFlags_END).bits }
/**
* 'flex-start'
*/
#define AlignFlags_FLEX_START (AlignFlags){ .bits = (uint8_t)(1 << 3) }
#define AlignFlags_MIXED (AlignFlags){ .bits = (uint8_t)(((1 << 4) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }
#define AlignFlags_MIXED_SELF (AlignFlags){ .bits = (uint8_t)(((1 << 5) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }

typedef struct DebugFlags {
uint32_t bits;
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/bitflags.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct {
* 'end'
*/
#define AlignFlags_END (AlignFlags){ .bits = (uint8_t)(1 << 2) }
#define AlignFlags_ALIAS (AlignFlags){ .bits = (uint8_t)(AlignFlags_END).bits }
/**
* 'flex-start'
*/
#define AlignFlags_FLEX_START (AlignFlags){ .bits = (uint8_t)(1 << 3) }
#define AlignFlags_MIXED (AlignFlags){ .bits = (uint8_t)(((1 << 4) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }
#define AlignFlags_MIXED_SELF (AlignFlags){ .bits = (uint8_t)(((1 << 5) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }

typedef struct {
uint32_t bits;
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations/bitflags.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ typedef struct {
* 'end'
*/
#define AlignFlags_END (AlignFlags){ .bits = (uint8_t)(1 << 2) }
#define AlignFlags_ALIAS (AlignFlags){ .bits = (uint8_t)(AlignFlags_END).bits }
/**
* 'flex-start'
*/
#define AlignFlags_FLEX_START (AlignFlags){ .bits = (uint8_t)(1 << 3) }
#define AlignFlags_MIXED (AlignFlags){ .bits = (uint8_t)(((1 << 4) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }
#define AlignFlags_MIXED_SELF (AlignFlags){ .bits = (uint8_t)(((1 << 5) | (AlignFlags_FLEX_START).bits) | (AlignFlags_END).bits) }

typedef struct {
uint32_t bits;
Expand Down
Loading

0 comments on commit ec6c659

Please sign in to comment.