Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constant: Add support for associated constant expressions. #752

Merged
merged 2 commits into from Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
210 changes: 186 additions & 24 deletions src/bindgen/ir/constant.rs
Expand Up @@ -20,10 +20,70 @@ 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),
}
}

// TODO: Maybe add support to more std associated constants.
fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
use crate::bindgen::ir::{IntKind, PrimitiveType};

if name != "MAX" && name != "MIN" {
return None;
}

let prim = PrimitiveType::maybe(associated_to.name())?;
let prefix = match prim {
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match kind {
IntKind::B8 => {
if signed {
"INT8"
} else {
"UINT8"
}
}
IntKind::B16 => {
if signed {
"INT16"
} else {
"UINT16"
}
}
IntKind::B32 => {
if signed {
"INT32"
} else {
"UINT32"
}
}
IntKind::B64 => {
if signed {
"INT64"
} else {
"UINT64"
}
}
_ => return None,
},
_ => return None,
};
Some(format!("{}_{}", prefix, name))
}

#[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 +93,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 +122,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 +144,55 @@ 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,
ref name,
} => {
if let Some((ref path, _export_name)) = associated_to {
return bindings.struct_exists(path)
|| to_known_assoc_constant(path, name).is_some();
}
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 +214,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 +317,39 @@ 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::Call(syn::ExprCall {
ref func, ref args, ..
}) => {
let struct_name = match Literal::load(func)? {
Literal::Path {
associated_to: None,
name,
} => name,
_ => return Err(format!("Unsupported call expression. {:?}", *expr)),
};
let mut fields = HashMap::<String, Literal>::default();
for (index, arg) in args.iter().enumerate() {
let ident =
member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string();
let value = Literal::load(arg)?;
fields.insert(ident, value);
}
Ok(Literal::Struct {
path: Path::new(struct_name.clone()),
export_name: struct_name,
fields,
})
}

syn::Expr::Struct(syn::ExprStruct {
ref path,
ref fields,
Expand All @@ -228,13 +358,9 @@ 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 key = ident.to_string();
let ident = member_to_ident(&field.member).to_string();
let value = Literal::load(&field.expr)?;
field_map.insert(key, value);
field_map.insert(ident, value);
}
Ok(Literal::Struct {
path: Path::new(struct_name.clone()),
Expand All @@ -257,16 +383,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 +428,36 @@ 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 path, ref export_name)) = associated_to {
if let Some(known) = to_known_assoc_constant(path, name) {
return write!(out, "{}", known);
}
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
26 changes: 25 additions & 1 deletion tests/expectations/associated_in_body.both.c
Expand Up @@ -27,9 +27,33 @@ 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);
/**
* An arbitrary identifier for a native (OS compositor) surface
*/
typedef struct StyleNativeSurfaceId {
uint64_t _0;
} StyleNativeSurfaceId;
/**
* A special id for the native surface that is used for debug / profiler overlays.
*/
#define StyleNativeSurfaceId_DEBUG_OVERLAY (StyleNativeSurfaceId){ ._0 = UINT64_MAX }

typedef struct StyleNativeTileId {
struct StyleNativeSurfaceId surface_id;
int32_t x;
int32_t y;
} StyleNativeTileId;
/**
* A special id for the native surface that is used for debug / profiler overlays.
*/
#define StyleNativeTileId_DEBUG_OVERLAY (StyleNativeTileId){ .surface_id = StyleNativeSurfaceId_DEBUG_OVERLAY, .x = 0, .y = 0 }

void root(struct StyleAlignFlags flags, struct StyleNativeTileId tile);
26 changes: 25 additions & 1 deletion tests/expectations/associated_in_body.both.compat.c
Expand Up @@ -27,16 +27,40 @@ 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) }

/**
* An arbitrary identifier for a native (OS compositor) surface
*/
typedef struct StyleNativeSurfaceId {
uint64_t _0;
} StyleNativeSurfaceId;
/**
* A special id for the native surface that is used for debug / profiler overlays.
*/
#define StyleNativeSurfaceId_DEBUG_OVERLAY (StyleNativeSurfaceId){ ._0 = UINT64_MAX }

typedef struct StyleNativeTileId {
struct StyleNativeSurfaceId surface_id;
int32_t x;
int32_t y;
} StyleNativeTileId;
/**
* A special id for the native surface that is used for debug / profiler overlays.
*/
#define StyleNativeTileId_DEBUG_OVERLAY (StyleNativeTileId){ .surface_id = StyleNativeSurfaceId_DEBUG_OVERLAY, .x = 0, .y = 0 }

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void root(struct StyleAlignFlags flags);
void root(struct StyleAlignFlags flags, struct StyleNativeTileId tile);

#ifdef __cplusplus
} // extern "C"
Expand Down