Skip to content

Commit 38a4561

Browse files
committed
Multiple constant generation evaluation improvements.
1 parent 66e9570 commit 38a4561

16 files changed

+238
-54
lines changed

libbindgen/src/clang.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -1265,36 +1265,56 @@ pub fn extract_clang_version() -> String {
12651265
unsafe { clang_getClangVersion().into() }
12661266
}
12671267

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

12731274
#[cfg(feature = "llvm_stable")]
12741275
impl EvalResult {
1276+
/// Create a dummy EvalResult.
12751277
pub fn new(_: Cursor) -> Self {
12761278
EvalResult {
12771279
x: ::std::ptr::null_mut(),
12781280
}
12791281
}
12801282

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

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

1294-
pub fn kind(&self) -> Enum_CXEvalResultKind {
1303+
fn kind(&self) -> Enum_CXEvalResultKind {
12951304
unsafe { clang_EvalResult_getKind(self.x) }
12961305
}
12971306

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

libbindgen/src/codegen/helpers.rs

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

+40-4
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ impl CodeGenerator for Var {
299299
ctx: &BindgenContext,
300300
result: &mut CodegenResult,
301301
item: &Item) {
302+
use ir::var::VarType;
303+
302304
let canonical_name = item.canonical_name(ctx);
303305

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

libbindgen/src/ir/item.rs

+1-5
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

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

+57-22
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

+3
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;
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];
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';
+7
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)