Skip to content

Commit 824f99a

Browse files
committed
Multiple constant generation evaluation improvements.
1 parent 91faa76 commit 824f99a

16 files changed

+237
-54
lines changed

libbindgen/src/clang.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,36 +1267,56 @@ pub fn extract_clang_version() -> String {
12671267
unsafe { clang_getClangVersion().into() }
12681268
}
12691269

1270+
/// A wrapper for the result of evaluating an expression.
12701271
#[derive(Debug)]
12711272
pub struct EvalResult {
12721273
x: CXEvalResult,
12731274
}
12741275

12751276
#[cfg(feature = "llvm_stable")]
12761277
impl EvalResult {
1278+
/// Create a dummy EvalResult.
12771279
pub fn new(_: Cursor) -> Self {
12781280
EvalResult {
12791281
x: ::std::ptr::null_mut(),
12801282
}
12811283
}
12821284

1285+
/// Not useful in llvm 3.8.
1286+
pub fn as_double(&self) -> Option<f64> {
1287+
None
1288+
}
1289+
1290+
/// Not useful in llvm 3.8.
12831291
pub fn as_int(&self) -> Option<i32> {
12841292
None
12851293
}
12861294
}
12871295

12881296
#[cfg(not(feature = "llvm_stable"))]
12891297
impl EvalResult {
1298+
/// Evaluate `cursor` and return the result.
12901299
pub fn new(cursor: Cursor) -> Self {
12911300
EvalResult {
12921301
x: unsafe { clang_Cursor_Evaluate(cursor.x) },
12931302
}
12941303
}
12951304

1296-
pub fn kind(&self) -> Enum_CXEvalResultKind {
1305+
fn kind(&self) -> Enum_CXEvalResultKind {
12971306
unsafe { clang_EvalResult_getKind(self.x) }
12981307
}
12991308

1309+
/// Try to get back the result as a double.
1310+
pub fn as_double(&self) -> Option<f64> {
1311+
match self.kind() {
1312+
CXEval_Float => {
1313+
Some(unsafe { clang_EvalResult_getAsDouble(self.x) } as f64)
1314+
}
1315+
_ => None,
1316+
}
1317+
}
1318+
1319+
/// Try to get back the result as an integer.
13001320
pub fn as_int(&self) -> Option<i32> {
13011321
match self.kind() {
13021322
CXEval_Int => {

libbindgen/src/codegen/helpers.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,37 @@ pub mod ast_ty {
132132
expr.int(val)
133133
}
134134
}
135+
136+
pub fn byte_array_expr(bytes: &[u8]) -> P<ast::Expr> {
137+
let mut vec = Vec::with_capacity(bytes.len() + 1);
138+
for byte in bytes {
139+
vec.push(int_expr(*byte as i64));
140+
}
141+
vec.push(int_expr(0));
142+
143+
let kind = ast::ExprKind::Vec(vec);
144+
145+
aster::AstBuilder::new().expr().build_expr_kind(kind)
146+
}
147+
148+
pub fn cstr_expr(mut string: String) -> P<ast::Expr> {
149+
string.push('\0');
150+
aster::AstBuilder::new()
151+
.expr()
152+
.build_lit(aster::AstBuilder::new().lit().byte_str(string))
153+
}
154+
155+
pub fn float_expr(f: f64) -> P<ast::Expr> {
156+
use aster::str::ToInternedString;
157+
let mut string = f.to_string();
158+
159+
// So it gets properly recognised as a floating point constant.
160+
if !string.contains('.') {
161+
string.push('.');
162+
}
163+
164+
let interned_str = string.as_str().to_interned_string();
165+
let kind = ast::LitKind::FloatUnsuffixed(interned_str);
166+
aster::AstBuilder::new().expr().lit().build_lit(kind)
167+
}
135168
}

libbindgen/src/codegen/mod.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ impl CodeGenerator for Var {
304304
ctx: &BindgenContext,
305305
result: &mut CodegenResult,
306306
item: &Item) {
307+
use ir::var::VarType;
307308
debug!("<Var as CodeGenerator>::codegen: item = {:?}", item);
308309

309310
let canonical_name = item.canonical_name(ctx);
@@ -320,10 +321,44 @@ impl CodeGenerator for Var {
320321
.item()
321322
.pub_()
322323
.const_(canonical_name)
323-
.expr()
324-
.build(helpers::ast_ty::int_expr(val))
325-
.build(ty);
326-
result.push(const_item)
324+
.expr();
325+
let item = match *val {
326+
VarType::Int(val) => {
327+
const_item.build(helpers::ast_ty::int_expr(val))
328+
.build(ty)
329+
}
330+
VarType::String(ref bytes) => {
331+
// Account the trailing zero.
332+
//
333+
// TODO: Here we ignore the type we just made up, probably
334+
// we should refactor how the variable type and ty id work.
335+
let len = bytes.len() + 1;
336+
let ty = quote_ty!(ctx.ext_cx(), [u8; $len]);
337+
338+
match String::from_utf8(bytes.clone()) {
339+
Ok(string) => {
340+
const_item.build(helpers::ast_ty::cstr_expr(string))
341+
.build(quote_ty!(ctx.ext_cx(), &'static $ty))
342+
}
343+
Err(..) => {
344+
const_item
345+
.build(helpers::ast_ty::byte_array_expr(bytes))
346+
.build(ty)
347+
}
348+
}
349+
}
350+
VarType::Float(f) => {
351+
const_item.build(helpers::ast_ty::float_expr(f))
352+
.build(ty)
353+
}
354+
VarType::Char(c) => {
355+
const_item
356+
.build(aster::AstBuilder::new().expr().lit().byte(c))
357+
.build(ty)
358+
}
359+
};
360+
361+
result.push(item);
327362
} else {
328363
let mut attrs = vec![];
329364
if let Some(mangled) = self.mangled_name() {

libbindgen/src/ir/item.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -840,11 +840,7 @@ impl ClangItemParser for Item {
840840
ctx: &mut BindgenContext)
841841
-> ItemId {
842842
let id = ctx.next_item_id();
843-
Self::from_ty_or_ref_with_id(id,
844-
ty,
845-
location,
846-
parent_id,
847-
ctx)
843+
Self::from_ty_or_ref_with_id(id, ty, location, parent_id, ctx)
848844
}
849845

850846
/// Parse a C++ type. If we find a reference to a type that has not been

libbindgen/src/ir/ty.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ impl Type {
123123
Self::new(Some(name), None, kind, false)
124124
}
125125

126+
/// Is this an floating point type?
127+
pub fn is_float(&self) -> bool {
128+
match self.kind {
129+
TypeKind::Float(..) => true,
130+
_ => false,
131+
}
132+
}
133+
126134
/// Is this an integer type?
127135
pub fn is_integer(&self) -> bool {
128136
match self.kind {

libbindgen/src/ir/var.rs

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ use super::context::{BindgenContext, ItemId};
88
use super::function::cursor_mangling;
99
use super::int::IntKind;
1010
use super::item::Item;
11-
use super::ty::TypeKind;
11+
use super::ty::{FloatKind, TypeKind};
12+
13+
#[derive(Debug)]
14+
pub enum VarType {
15+
Int(i64),
16+
Float(f64),
17+
Char(u8),
18+
String(Vec<u8>),
19+
}
1220

1321
/// A `Var` is our intermediate representation of a variable.
1422
#[derive(Debug)]
@@ -19,9 +27,8 @@ pub struct Var {
1927
mangled_name: Option<String>,
2028
/// The type of the variable.
2129
ty: ItemId,
22-
/// TODO: support non-integer constants?
23-
/// The integer value of the variable.
24-
val: Option<i64>,
30+
/// The value of the variable, that needs to be suitable for `ty`.
31+
val: Option<VarType>,
2532
/// Whether this variable is const.
2633
is_const: bool,
2734
}
@@ -31,7 +38,7 @@ impl Var {
3138
pub fn new(name: String,
3239
mangled: Option<String>,
3340
ty: ItemId,
34-
val: Option<i64>,
41+
val: Option<VarType>,
3542
is_const: bool)
3643
-> Var {
3744
assert!(!name.is_empty());
@@ -50,8 +57,8 @@ impl Var {
5057
}
5158

5259
/// The value of this constant variable, if any.
53-
pub fn val(&self) -> Option<i64> {
54-
self.val
60+
pub fn val(&self) -> Option<&VarType> {
61+
self.val.as_ref()
5562
}
5663

5764
/// Get this variable's type.
@@ -76,6 +83,7 @@ impl ClangSubItemParser for Var {
7683
-> Result<ParseResult<Self>, ParseError> {
7784
use clangll::*;
7885
use cexpr::expr::EvalResult;
86+
use cexpr::literal::CChar;
7987
match cursor.kind() {
8088
CXCursor_MacroDefinition => {
8189
let value = parse_macro(ctx, &cursor, ctx.translation_unit());
@@ -105,13 +113,32 @@ impl ClangSubItemParser for Var {
105113
// enforce utf8 there, so we should have already panicked at
106114
// this point.
107115
let name = String::from_utf8(id).unwrap();
108-
let (int_kind, val) = match value {
109-
// TODO(emilio): Handle the non-invalid ones!
110-
EvalResult::Float(..) |
111-
EvalResult::Char(..) |
112-
EvalResult::Str(..) |
116+
let (type_kind, val) = match value {
113117
EvalResult::Invalid => return Err(ParseError::Continue),
114-
118+
EvalResult::Float(f) => {
119+
(TypeKind::Float(FloatKind::Float), VarType::Float(f))
120+
}
121+
EvalResult::Char(c) => {
122+
let c = match c {
123+
CChar::Char(c) => {
124+
assert_eq!(c.len_utf8(), 1);
125+
c as u8
126+
}
127+
CChar::Raw(c) => {
128+
assert!(c <= ::std::u8::MAX as u64);
129+
c as u8
130+
}
131+
};
132+
133+
(TypeKind::Int(IntKind::U8), VarType::Char(c))
134+
}
135+
EvalResult::Str(val) => {
136+
let char_ty =
137+
Item::builtin_type(TypeKind::Int(IntKind::U8),
138+
true,
139+
ctx);
140+
(TypeKind::Pointer(char_ty), VarType::String(val))
141+
}
115142
EvalResult::Int(Wrapping(value)) => {
116143
let kind = ctx.options()
117144
.type_chooser
@@ -131,11 +158,11 @@ impl ClangSubItemParser for Var {
131158
}
132159
});
133160

134-
(kind, value)
161+
(TypeKind::Int(kind), VarType::Int(value))
135162
}
136163
};
137164

138-
let ty = Item::builtin_type(TypeKind::Int(int_kind), true, ctx);
165+
let ty = Item::builtin_type(type_kind, true, ctx);
139166

140167
Ok(ParseResult::New(Var::new(name, None, ty, Some(val), true),
141168
Some(cursor)))
@@ -159,11 +186,16 @@ impl ClangSubItemParser for Var {
159186
// tests/headers/inner_const.hpp
160187
//
161188
// That's fine because in that case we know it's not a literal.
162-
let is_integer = ctx.safe_resolve_type(ty)
163-
.and_then(|t| t.safe_canonical_type(ctx))
164-
.map(|t| t.is_integer())
165-
.unwrap_or(false);
189+
let canonical_ty = ctx.safe_resolve_type(ty)
190+
.and_then(|t| t.safe_canonical_type(ctx));
166191

192+
let is_integer = canonical_ty.map_or(false, |t| t.is_integer());
193+
let is_float = canonical_ty.map_or(false, |t| t.is_float());
194+
195+
// TODO: We could handle `char` more gracefully.
196+
// TODO: Strings, though the lookup is a bit more hard (we need
197+
// to look at the canonical type of the pointee too, and check
198+
// is char, u8, or i8 I guess).
167199
let value = if is_integer {
168200
cursor.evaluate()
169201
.as_int()
@@ -172,16 +204,19 @@ impl ClangSubItemParser for Var {
172204
let tu = ctx.translation_unit();
173205
get_integer_literal_from_cursor(&cursor, tu)
174206
})
207+
.map(VarType::Int)
208+
} else if is_float {
209+
cursor.evaluate()
210+
.as_double()
211+
.map(VarType::Float)
175212
} else {
176213
None
177214
};
178215

179-
180216
let mangling = cursor_mangling(&cursor);
181-
182217
let var = Var::new(name, mangling, ty, value, is_const);
183-
Ok(ParseResult::New(var, Some(cursor)))
184218

219+
Ok(ParseResult::New(var, Some(cursor)))
185220
}
186221
_ => {
187222
/* TODO */

tests/expectations/tests/constant-evaluate.rs renamed to libbindgen/tests/expectations/tests/constant-evaluate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ pub const bar: _bindgen_ty_1 = _bindgen_ty_1::bar;
1010
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1111
pub enum _bindgen_ty_1 { foo = 4, bar = 8, }
1212
pub const BAZ: ::std::os::raw::c_longlong = 24;
13+
pub const fuzz: f64 = 51.;
14+
pub const BAZZ: ::std::os::raw::c_char = 53;
15+
pub const WAT: ::std::os::raw::c_char = 0;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub const foo: &'static [u8; 4usize] = b"bar\x00";
8+
pub const CHAR: u8 = b'b';
9+
pub const CHARR: u8 = b'\x00';
10+
pub const FLOAT: f32 = 5.09;
11+
pub const FLOAT_EXPR: f32 = 0.005;
12+
pub const INVALID_UTF8: [u8; 5usize] = [240, 40, 140, 40, 0];
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-unstable
2+
3+
enum {
4+
foo = 4,
5+
bar = 8,
6+
};
7+
8+
const long long BAZ = (1 << foo) | bar;
9+
const double fuzz = (1 + 50.0f);
10+
const char BAZZ = '5';
11+
const char WAT = '\0';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#define foo "bar"
2+
#define CHAR 'b'
3+
#define CHARR '\0'
4+
#define FLOAT 5.09f
5+
#define FLOAT_EXPR (5 / 1000.0f)
6+
7+
#define INVALID_UTF8 "\xf0\x28\x8c\x28"

0 commit comments

Comments
 (0)