Skip to content

Commit dd174a8

Browse files
committed
Generate pointer-valued macro constants
The Clang macro fallback can evaluate macro forms that cexpr cannot parse, but it discards pointer-valued expressions. Emit data pointer macros as raw Rust pointer constants. Keep dependent expressions eligible for fallback evaluation. Materialize pointer macro types from a final fallback parse so deferred type resolution does not retain cursors invalidated by a later reparse. Related to #3347.
1 parent c8c9a5a commit dd174a8

7 files changed

Lines changed: 247 additions & 21 deletions

File tree

bindgen-tests/tests/expectations/tests/clang-macro-fallback-pointer.rs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/libclang-9/clang-macro-fallback-pointer.rs

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// bindgen-flags: --clang-macro-fallback
2+
3+
#define BEFORE_DECL ((struct later *)3UL)
4+
#define CONST_PTR ((const struct later *)4UL)
5+
6+
struct later;
7+
8+
typedef struct later *later_ptr;
9+
#define TYPEDEF_PTR ((later_ptr)5UL)
10+
#define MAP_FAILED ((void *)-1)
11+
#define MAP_FAILED_ALIAS MAP_FAILED
12+
#define MAP_FAILED_EQUALS_ITSELF (MAP_FAILED == MAP_FAILED)
13+
#define REDEFINED_FROM_INT 1
14+
#undef REDEFINED_FROM_INT
15+
#define REDEFINED_FROM_INT ((void *)2)
16+
#define REDEFINED_ALIAS REDEFINED_FROM_INT

bindgen/codegen/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ impl CodeGenerator for Var {
807807
}
808808
VarType::Float(f) => helpers::ast_ty::float_expr(f).ok(),
809809
VarType::Char(c) => Some(c.to_token_stream()),
810+
VarType::Pointer(val) => Some(quote! { #val as usize as #ty }),
810811
};
811812

812813
if let Some(mut val) = const_expr {

bindgen/ir/context.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::module::{Module, ModuleKind};
1919
use super::template::{TemplateInstantiation, TemplateParameters};
2020
use super::traversal::{self, Edge, ItemTraversal};
2121
use super::ty::{FloatKind, Type, TypeKind};
22+
use super::var::PendingPointerMacro;
2223
use crate::clang::{self, ABIKind, Cursor};
2324
use crate::codegen::CodegenError;
2425
use crate::ir::item::ItemCanonicalName;
@@ -350,13 +351,18 @@ pub(crate) struct BindgenContext {
350351
/// potentially break that assumption.
351352
currently_parsed_types: Vec<PartialType>,
352353

353-
/// A map with all the already parsed macro names. This is done to avoid
354-
/// hard errors while parsing duplicated macros, as well to allow macro
355-
/// expression parsing.
354+
/// Parsed macro names whose current value cannot be provided to `cexpr`.
355+
non_cexpr_macros: HashSet<Vec<u8>>,
356+
357+
/// Values from parsed macros that `cexpr` can use while parsing derived
358+
/// macro expressions.
356359
///
357360
/// This needs to be an `std::HashMap` because the `cexpr` API requires it.
358361
parsed_macros: StdHashMap<Vec<u8>, cexpr::expr::EvalResult>,
359362

363+
/// Pointer-valued macros whose type is materialized after macro parsing.
364+
pending_pointer_macros: Vec<PendingPointerMacro>,
365+
360366
/// A map with all include locations.
361367
///
362368
/// This is needed so that items are created in the order they are defined in.
@@ -587,7 +593,9 @@ If you encounter an error missing from this list, please file an issue or a PR!"
587593
current_module: root_module_id,
588594
semantic_parents: Default::default(),
589595
currently_parsed_types: vec![],
596+
non_cexpr_macros: Default::default(),
590597
parsed_macros: Default::default(),
598+
pending_pointer_macros: vec![],
591599
replacements: Default::default(),
592600
collected_typerefs: false,
593601
in_codegen: false,
@@ -2130,7 +2138,8 @@ If you encounter an error missing from this list, please file an issue or a PR!"
21302138

21312139
/// Have we parsed the macro named `macro_name` already?
21322140
pub(crate) fn parsed_macro(&self, macro_name: &[u8]) -> bool {
2133-
self.parsed_macros.contains_key(macro_name)
2141+
self.parsed_macros.contains_key(macro_name) ||
2142+
self.non_cexpr_macros.contains(macro_name)
21342143
}
21352144

21362145
/// Get the currently parsed macros.
@@ -2141,13 +2150,34 @@ If you encounter an error missing from this list, please file an issue or a PR!"
21412150
&self.parsed_macros
21422151
}
21432152

2144-
/// Mark the macro named `macro_name` as parsed.
2153+
/// Mark a macro as parsed and update any value usable by `cexpr`.
21452154
pub(crate) fn note_parsed_macro(
21462155
&mut self,
21472156
id: Vec<u8>,
2148-
value: cexpr::expr::EvalResult,
2157+
value: Option<cexpr::expr::EvalResult>,
21492158
) {
2150-
self.parsed_macros.insert(id, value);
2159+
if let Some(value) = value {
2160+
self.non_cexpr_macros.remove(&id);
2161+
self.parsed_macros.insert(id, value);
2162+
} else {
2163+
self.parsed_macros.remove(&id);
2164+
self.non_cexpr_macros.insert(id);
2165+
}
2166+
}
2167+
2168+
/// Defer materialization of a pointer-valued macro until parsing finishes.
2169+
pub(crate) fn note_pending_pointer_macro(
2170+
&mut self,
2171+
pointer_macro: PendingPointerMacro,
2172+
) {
2173+
self.pending_pointer_macros.push(pointer_macro);
2174+
}
2175+
2176+
/// Take all pointer-valued macros waiting for final materialization.
2177+
pub(crate) fn take_pending_pointer_macros(
2178+
&mut self,
2179+
) -> Vec<PendingPointerMacro> {
2180+
mem::take(&mut self.pending_pointer_macros)
21512181
}
21522182

21532183
/// Are we in the codegen phase?

bindgen/ir/var.rs

Lines changed: 166 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//! Intermediate representation of variables.
22
33
use super::super::codegen::MacroTypeVariation;
4-
use super::context::{BindgenContext, TypeId};
4+
use super::annotations::Annotations;
5+
use super::context::{BindgenContext, ItemId, TypeId};
56
use super::dot::DotAttributes;
67
use super::function::cursor_mangling;
78
use super::int::IntKind;
89
use super::item::Item;
10+
use super::item_kind::ItemKind as IrItemKind;
911
use super::ty::{FloatKind, TypeKind};
1012
use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior};
1113
use crate::clang;
@@ -28,6 +30,27 @@ pub(crate) enum VarType {
2830
Char(u8),
2931
/// A string, not necessarily well-formed utf-8.
3032
String(Vec<u8>),
33+
/// A pointer represented as an integer constant.
34+
Pointer(u64),
35+
}
36+
37+
/// The value obtained when parsing a macro.
38+
enum MacroValue {
39+
/// A value parsed using `cexpr` or evaluated by Clang as an integer.
40+
Expr(cexpr::expr::EvalResult),
41+
/// A data pointer value recognized by Clang.
42+
Pointer,
43+
}
44+
45+
/// A pointer-valued macro waiting to have its type materialized.
46+
#[derive(Debug)]
47+
pub(crate) struct PendingPointerMacro {
48+
/// The item ID reserved at the macro's source position.
49+
id: ItemId,
50+
/// The macro name.
51+
name: String,
52+
/// The original macro cursor in the primary translation unit.
53+
cursor: clang::Cursor,
3154
}
3255

3356
/// A `Var` is our intermediate representation of a variable.
@@ -207,10 +230,13 @@ impl ClangSubItemParser for Var {
207230

208231
let previously_defined = ctx.parsed_macro(&id);
209232

210-
// NB: It's important to "note" the macro even if the result is
211-
// not an integer, otherwise we might loose other kind of
212-
// derived macros.
213-
ctx.note_parsed_macro(id.clone(), value.clone());
233+
// Keep pointer macros unknown to `cexpr`: derived expressions
234+
// must still be eligible for Clang fallback evaluation.
235+
let cexpr_value = match &value {
236+
MacroValue::Expr(value) => Some(value.clone()),
237+
MacroValue::Pointer => None,
238+
};
239+
ctx.note_parsed_macro(id.clone(), cexpr_value);
214240

215241
if previously_defined {
216242
let name = String::from_utf8(id).unwrap();
@@ -223,6 +249,18 @@ impl ClangSubItemParser for Var {
223249
// enforce utf8 there, so we should have already panicked at
224250
// this point.
225251
let name = String::from_utf8(id).unwrap();
252+
let value =
253+
match value {
254+
MacroValue::Expr(value) => value,
255+
MacroValue::Pointer => {
256+
let id = ctx.next_item_id();
257+
ctx.note_pending_pointer_macro(
258+
PendingPointerMacro { id, name, cursor },
259+
);
260+
return Err(ParseError::Continue);
261+
}
262+
};
263+
226264
let (type_kind, val) = match value {
227265
EvalResult::Invalid => return Err(ParseError::Continue),
228266
EvalResult::Float(f) => {
@@ -390,13 +428,18 @@ impl ClangSubItemParser for Var {
390428
fn parse_macro_clang_fallback(
391429
ctx: &mut BindgenContext,
392430
cursor: &clang::Cursor,
393-
) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
431+
) -> Option<(Vec<u8>, MacroValue)> {
432+
use clang_sys::{
433+
CXType_FunctionNoProto, CXType_FunctionProto, CXType_Pointer,
434+
};
435+
394436
if !ctx.options().clang_macro_fallback {
395437
return None;
396438
}
397439

398440
let ftu = ctx.try_ensure_fallback_translation_unit()?;
399-
let contents = format!("int main() {{ {}; }}", cursor.spelling());
441+
let name = cursor.spelling();
442+
let contents = format!("int main() {{ {name}; }}");
400443
ftu.reparse(&contents).ok()?;
401444
// Children of root node of AST
402445
let root_children = ftu.translation_unit().cursor().collect_children();
@@ -413,18 +456,33 @@ fn parse_macro_clang_fallback(
413456
// First child in all_exprs is the expression utilizing the given macro to be evaluated
414457
// Should be ParenExpr
415458
let paren = paren_exprs.first()?;
459+
let canonical_ty = paren.cur_type().canonical_type();
460+
461+
if canonical_ty.kind() != CXType_Pointer {
462+
return Some((
463+
name.into_bytes(),
464+
MacroValue::Expr(cexpr::expr::EvalResult::Int(Wrapping(
465+
paren.evaluate()?.as_int()?,
466+
))),
467+
));
468+
}
469+
470+
let pointee = canonical_ty.pointee_type()?;
471+
if matches!(
472+
pointee.kind(),
473+
CXType_FunctionNoProto | CXType_FunctionProto
474+
) {
475+
return None;
476+
}
416477

417-
Some((
418-
cursor.spelling().into_bytes(),
419-
cexpr::expr::EvalResult::Int(Wrapping(paren.evaluate()?.as_int()?)),
420-
))
478+
Some((name.into_bytes(), MacroValue::Pointer))
421479
}
422480

423481
/// Try and parse a macro using all the macros parsed until now.
424482
fn parse_macro(
425483
ctx: &mut BindgenContext,
426484
cursor: &clang::Cursor,
427-
) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
485+
) -> Option<(Vec<u8>, MacroValue)> {
428486
use cexpr::expr;
429487

430488
let mut cexpr_tokens = cursor.cexpr_tokens();
@@ -436,8 +494,102 @@ fn parse_macro(
436494
let parser = expr::IdentifierParser::new(ctx.parsed_macros());
437495

438496
match parser.macro_definition(&cexpr_tokens) {
439-
Ok((_, (id, val))) => Some((id.into(), val)),
440-
_ => parse_macro_clang_fallback(ctx, cursor),
497+
Ok((_, (id, value))) => Some((id.into(), MacroValue::Expr(value))),
498+
Err(_) => parse_macro_clang_fallback(ctx, cursor),
499+
}
500+
}
501+
502+
/// Materialize all pointer macro types from one final fallback translation
503+
/// unit, which remains valid throughout deferred type resolution.
504+
pub(crate) fn finish_pending_pointer_macros(ctx: &mut BindgenContext) {
505+
use clang_sys::{CXChildVisit_Break, CXChildVisit_Recurse, CXType_Pointer};
506+
507+
let pending = ctx.take_pending_pointer_macros();
508+
if pending.is_empty() {
509+
return;
510+
}
511+
512+
let statements = pending
513+
.iter()
514+
.map(|pointer_macro| {
515+
format!("{{ (unsigned long long)({}); }}", pointer_macro.name)
516+
})
517+
.collect::<Vec<_>>()
518+
.join(" ");
519+
let contents = format!("int main() {{ {statements} }}");
520+
let expressions = {
521+
let Some(ftu) = ctx.try_ensure_fallback_translation_unit() else {
522+
return;
523+
};
524+
if ftu.reparse(&contents).is_err() {
525+
return;
526+
}
527+
let root_children = ftu.translation_unit().cursor().collect_children();
528+
let Some(main_func) = root_children.last() else {
529+
return;
530+
};
531+
let all_stmts = main_func.collect_children();
532+
let Some(macro_stmt) = all_stmts.first() else {
533+
return;
534+
};
535+
macro_stmt
536+
.collect_children()
537+
.into_iter()
538+
.map(|statement| {
539+
let value_expression = *statement.collect_children().first()?;
540+
let value = value_expression.evaluate()?.as_int()? as u64;
541+
let mut pointer_expression = None;
542+
value_expression.visit(|child| {
543+
if child.cur_type().canonical_type().kind() ==
544+
CXType_Pointer
545+
{
546+
pointer_expression = Some(child);
547+
CXChildVisit_Break
548+
} else {
549+
CXChildVisit_Recurse
550+
}
551+
});
552+
Some((pointer_expression?, value))
553+
})
554+
.collect::<Vec<_>>()
555+
};
556+
if expressions.len() != pending.len() {
557+
return;
558+
}
559+
560+
for (pointer_macro, expression) in pending.into_iter().zip(expressions) {
561+
let Some((expression, value)) = expression else {
562+
continue;
563+
};
564+
let Ok(ty) = Item::from_ty(
565+
&expression.cur_type().canonical_type(),
566+
expression,
567+
None,
568+
ctx,
569+
) else {
570+
continue;
571+
};
572+
let cursor = pointer_macro.cursor;
573+
let var = Var::new(
574+
pointer_macro.name,
575+
None,
576+
None,
577+
ty,
578+
Some(VarType::Pointer(value)),
579+
true,
580+
);
581+
ctx.add_item(
582+
Item::new(
583+
pointer_macro.id,
584+
cursor.raw_comment(),
585+
Annotations::new(&cursor),
586+
ctx.root_module().into(),
587+
IrItemKind::Var(var),
588+
Some(cursor.location()),
589+
),
590+
Some(cursor),
591+
Some(cursor),
592+
);
441593
}
442594
}
443595

bindgen/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,7 @@ fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
11691169
context.root_module(),
11701170
"How did this happen?"
11711171
);
1172+
ir::var::finish_pending_pointer_macros(context);
11721173
Ok(())
11731174
}
11741175

0 commit comments

Comments
 (0)