mojo-bindgen 0.3.2__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/CHANGELOG.md +17 -0
  2. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/PKG-INFO +1 -1
  3. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/record_policies.py +144 -15
  4. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/cli.py +18 -0
  5. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/record_lowering.py +36 -2
  6. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/pyproject.toml +1 -1
  7. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/.gitignore +0 -0
  8. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/LICENSE +0 -0
  9. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/README.md +0 -0
  10. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/__init__.py +0 -0
  11. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/__init__.py +0 -0
  12. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/cir/__init__.py +0 -0
  13. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/cir/cir_canonicalizer.py +0 -0
  14. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/cir/reachability.py +0 -0
  15. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/cir/reference_validation.py +0 -0
  16. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/cir/validate_ir.py +0 -0
  17. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/common.py +0 -0
  18. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/__init__.py +0 -0
  19. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/alias_classification.py +0 -0
  20. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/bitfield_layout.py +0 -0
  21. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/context.py +0 -0
  22. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/dependency_graph.py +0 -0
  23. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/indexes.py +0 -0
  24. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/record_layout.py +0 -0
  25. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/record_storage.py +0 -0
  26. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/facts/type_layout.py +0 -0
  27. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/__init__.py +0 -0
  28. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/alias_mapping.py +0 -0
  29. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/const_expr_mapping.py +0 -0
  30. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/const_value_mapping.py +0 -0
  31. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/decl_mapping.py +0 -0
  32. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/macro_mapping.py +0 -0
  33. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/mapping_support.py +0 -0
  34. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/mojo_emit_options.py +0 -0
  35. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/struct_mapping.py +0 -0
  36. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/type_mapping.py +0 -0
  37. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/union_mapping.py +0 -0
  38. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/mojo/unit_mapping.py +0 -0
  39. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/pipeline.py +0 -0
  40. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/traversal.py +0 -0
  41. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/analysis/type_walk.py +0 -0
  42. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/codegen/__init__.py +0 -0
  43. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/codegen/mojo_ir_printer.py +0 -0
  44. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/codegen/mojo_support_templates.py +0 -0
  45. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/codegen/normalize_mojo_module.py +0 -0
  46. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/ir.py +0 -0
  47. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/layout_tests/__init__.py +0 -0
  48. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/layout_tests/generator.py +0 -0
  49. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/orchestrator.py +0 -0
  50. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/__init__.py +0 -0
  51. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/diagnostics.py +0 -0
  52. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/doc_comments.py +0 -0
  53. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/frontend.py +0 -0
  54. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/__init__.py +0 -0
  55. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/const_expr.py +0 -0
  56. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/decl_lowering.py +0 -0
  57. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/literal_resolver.py +0 -0
  58. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/macro_env.py +0 -0
  59. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/primitive.py +0 -0
  60. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/lowering/type_lowering.py +0 -0
  61. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/parser.py +0 -0
  62. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/registry.py +0 -0
  63. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/parsing/target_abi.py +0 -0
  64. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/py.typed +0 -0
  65. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/serde.py +0 -0
  66. {mojo_bindgen-0.3.2 → mojo_bindgen-0.3.3}/mojo_bindgen/utils.py +0 -0
@@ -2,10 +2,27 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.3.3] - 2026-06-27
6
+
7
+ ### Changed
8
+
9
+ - Add `mojo-bindgen --version` so scripts and users can inspect the installed
10
+ package version directly from the CLI.
11
+ - Make `mojo-bindgen` with no arguments print the help message instead of
12
+ failing on the missing header argument.
13
+
5
14
  ## [0.3.2] - 2026-06-23
6
15
 
7
16
  ### Changed
8
17
 
18
+ - Split struct trait inference from register-passability inference so generated
19
+ records now derive `Copyable` and `Movable` recursively through named types,
20
+ fixed arrays, and `UnsafeUnion` arms, keep atomics and flexible-tail records
21
+ non-copyable/non-movable, and document opaque-storage transfer traits as an
22
+ experimental default.
23
+ - Preserve typedef-backed integer bitfields such as `uint32_t flags:3` during
24
+ record lowering, keep their typedef surface names in generated accessors, and
25
+ stop silently dropping them before bitfield layout/codegen.
9
26
  - Simplify and clarify the CLI surface: keep `--public-header` and
10
27
  `--clang-arg`, add conventional `-o`, `-I`, `-D`, and `-U` forms, make
11
28
  layout-test output explicit with `--layout-tests PATH`, and hide maintainer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mojo-bindgen
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Generate Mojo FFI bindings from C headers using libclang
5
5
  Project-URL: Homepage, https://github.com/MoSafi2/mojo_bindgen
6
6
  Project-URL: Repository, https://github.com/MoSafi2/mojo_bindgen
@@ -27,6 +27,7 @@ from mojo_bindgen.ir import (
27
27
  StructMember,
28
28
  StructTraits,
29
29
  Type,
30
+ TypeArg,
30
31
  )
31
32
 
32
33
 
@@ -34,6 +35,14 @@ class AssignRecordPoliciesError(ValueError):
34
35
  """Raised when late record policy derivation cannot complete safely."""
35
36
 
36
37
 
38
+ @dataclass(frozen=True)
39
+ class TransferTraits:
40
+ """Copy and move capability inferred separately from register passability."""
41
+
42
+ copyable: bool
43
+ movable: bool
44
+
45
+
37
46
  _TRIVIAL_BUILTINS = frozenset(
38
47
  builtin for builtin in MojoBuiltin if builtin != MojoBuiltin.UNSUPPORTED
39
48
  )
@@ -68,8 +77,10 @@ class PolicyInferencePass:
68
77
  def run(self, module: MojoModule) -> MojoModule:
69
78
  self._structs = {decl.name: decl for decl in module.decls if isinstance(decl, StructDecl)}
70
79
  self._aliases = {decl.name: decl for decl in module.decls if isinstance(decl, AliasDecl)}
71
- self._cache: dict[str, MojoPassability] = {}
72
- self._computing: set[str] = set()
80
+ self._passability_cache: dict[str, MojoPassability] = {}
81
+ self._passability_computing: set[str] = set()
82
+ self._transfer_cache: dict[str, TransferTraits] = {}
83
+ self._transfer_computing: set[str] = set()
73
84
 
74
85
  return replace(
75
86
  module,
@@ -80,7 +91,8 @@ class PolicyInferencePass:
80
91
  if not isinstance(decl, StructDecl):
81
92
  return decl
82
93
  passability = self._record_passability(decl.name)
83
- if self._has_representable_atomic_storage(decl):
94
+ transfer_traits = self._record_transfer_traits(decl.name)
95
+ if self._has_representable_atomic_storage(decl) or decl.flexible_tail is not None:
84
96
  return replace(
85
97
  decl,
86
98
  passability=passability,
@@ -91,7 +103,7 @@ class PolicyInferencePass:
91
103
  return replace(
92
104
  decl,
93
105
  passability=passability,
94
- traits=self._traits_for_passability(passability),
106
+ traits=self._traits_for_decl(decl, passability, transfer_traits),
95
107
  fieldwise_init=self._is_fieldwise_init_eligible(decl),
96
108
  )
97
109
 
@@ -109,27 +121,27 @@ class PolicyInferencePass:
109
121
  return True
110
122
 
111
123
  def _record_passability(self, name: str) -> MojoPassability:
112
- if name in self._cache:
113
- return self._cache[name]
114
- if name in self._computing:
124
+ if name in self._passability_cache:
125
+ return self._passability_cache[name]
126
+ if name in self._passability_computing:
115
127
  return MojoPassability.MEMORY_ONLY
116
128
 
117
129
  decl = self._structs.get(name)
118
130
  if decl is None or decl.kind != StructKind.PLAIN:
119
- self._cache[name] = MojoPassability.MEMORY_ONLY
131
+ self._passability_cache[name] = MojoPassability.MEMORY_ONLY
120
132
  return MojoPassability.MEMORY_ONLY
121
133
  if decl.flexible_tail is not None:
122
- self._cache[name] = MojoPassability.MEMORY_ONLY
134
+ self._passability_cache[name] = MojoPassability.MEMORY_ONLY
123
135
  return MojoPassability.MEMORY_ONLY
124
136
  if self._has_representable_atomic_storage(decl):
125
- self._cache[name] = MojoPassability.MEMORY_ONLY
137
+ self._passability_cache[name] = MojoPassability.MEMORY_ONLY
126
138
  return MojoPassability.MEMORY_ONLY
127
139
 
128
- self._computing.add(name)
140
+ self._passability_computing.add(name)
129
141
  try:
130
142
  member_passabilities = [self._member_passability(member) for member in decl.members]
131
143
  finally:
132
- self._computing.remove(name)
144
+ self._passability_computing.remove(name)
133
145
 
134
146
  if all(
135
147
  passability == MojoPassability.TRIVIAL_REGISTER_PASSABLE
@@ -148,7 +160,46 @@ class PolicyInferencePass:
148
160
  else:
149
161
  result = MojoPassability.MEMORY_ONLY
150
162
 
151
- self._cache[name] = result
163
+ self._passability_cache[name] = result
164
+ return result
165
+
166
+ def _record_transfer_traits(self, name: str) -> TransferTraits:
167
+ if name in self._transfer_cache:
168
+ return self._transfer_cache[name]
169
+ if name in self._transfer_computing:
170
+ return TransferTraits(copyable=True, movable=True)
171
+
172
+ decl = self._structs.get(name)
173
+ if decl is None:
174
+ return TransferTraits(copyable=False, movable=False)
175
+ if decl.flexible_tail is not None or self._has_representable_atomic_storage(decl):
176
+ result = TransferTraits(copyable=False, movable=False)
177
+ self._transfer_cache[name] = result
178
+ return result
179
+ if decl.kind != StructKind.PLAIN:
180
+ result = TransferTraits(copyable=True, movable=True)
181
+ self._transfer_cache[name] = result
182
+ return result
183
+
184
+ if self._has_opaque_storage(decl):
185
+ # Experimental default: byte-backed fallback storage stays movable/copyable
186
+ # so generated wrappers remain usable while opaque record semantics are still
187
+ # being validated across real headers.
188
+ result = TransferTraits(copyable=True, movable=True)
189
+ self._transfer_cache[name] = result
190
+ return result
191
+
192
+ self._transfer_computing.add(name)
193
+ try:
194
+ member_traits = [self._member_transfer_traits(member) for member in decl.members]
195
+ finally:
196
+ self._transfer_computing.remove(name)
197
+
198
+ result = TransferTraits(
199
+ copyable=all(traits.copyable for traits in member_traits),
200
+ movable=all(traits.movable for traits in member_traits),
201
+ )
202
+ self._transfer_cache[name] = result
152
203
  return result
153
204
 
154
205
  def _member_passability(self, member: StructMember) -> MojoPassability:
@@ -164,6 +215,19 @@ class PolicyInferencePass:
164
215
  f"unsupported StructMember for passability: {type(member).__name__!r}"
165
216
  )
166
217
 
218
+ def _member_transfer_traits(self, member: StructMember) -> TransferTraits:
219
+ if isinstance(member, StoredMember):
220
+ return self._type_transfer_traits(member.type)
221
+ if isinstance(member, BitfieldGroupMember):
222
+ return self._type_transfer_traits(member.storage_type)
223
+ if isinstance(member, PaddingMember):
224
+ return TransferTraits(copyable=True, movable=True)
225
+ if isinstance(member, OpaqueStorageMember):
226
+ return TransferTraits(copyable=True, movable=True)
227
+ raise AssignRecordPoliciesError(
228
+ f"unsupported StructMember for transfer traits: {type(member).__name__!r}"
229
+ )
230
+
167
231
  def _type_passability(self, t: Type) -> MojoPassability:
168
232
  if isinstance(t, BuiltinType):
169
233
  if t.name in _TRIVIAL_BUILTINS:
@@ -202,12 +266,77 @@ class PolicyInferencePass:
202
266
  raise AssignRecordPoliciesError(f"unsupported Type for passability: {type(t).__name__!r}")
203
267
 
204
268
  @staticmethod
205
- def _traits_for_passability(passability: MojoPassability) -> list[StructTraits]:
269
+ def _passability_traits(passability: MojoPassability) -> list[StructTraits]:
206
270
  if passability == MojoPassability.TRIVIAL_REGISTER_PASSABLE:
207
271
  return [StructTraits.TRIVIAL_REGISTER_PASSABLE]
208
272
  if passability == MojoPassability.REGISTER_PASSABLE:
209
273
  return [StructTraits.REGISTER_PASSABLE]
210
- return [StructTraits.COPYABLE, StructTraits.MOVABLE]
274
+ return []
275
+
276
+ def _type_transfer_traits(self, t: Type) -> TransferTraits:
277
+ if isinstance(t, BuiltinType):
278
+ supported = t.name in _TRIVIAL_BUILTINS
279
+ return TransferTraits(copyable=supported, movable=supported)
280
+ if isinstance(t, FunctionPtr):
281
+ return TransferTraits(copyable=True, movable=True)
282
+ if isinstance(t, Pointer):
283
+ return TransferTraits(copyable=True, movable=True)
284
+ if isinstance(t, Array):
285
+ if t.array_kind != "fixed" or t.size is None:
286
+ return TransferTraits(copyable=False, movable=False)
287
+ return self._type_transfer_traits(t.element)
288
+ if isinstance(t, ParametricType):
289
+ if t.base == ParametricBase.ATOMIC:
290
+ return TransferTraits(copyable=False, movable=False)
291
+ if t.base in (ParametricBase.SIMD, ParametricBase.COMPLEX_SIMD):
292
+ return TransferTraits(copyable=True, movable=True)
293
+ if t.base == ParametricBase.UNSAFE_UNION:
294
+ arg_traits = [self._transfer_traits_for_parametric_arg(arg) for arg in t.args]
295
+ return TransferTraits(
296
+ copyable=all(traits.copyable for traits in arg_traits),
297
+ movable=all(traits.movable for traits in arg_traits),
298
+ )
299
+ return TransferTraits(copyable=False, movable=False)
300
+ if isinstance(t, NamedType):
301
+ if t.name in _TRIVIAL_NAMED_TYPES:
302
+ return TransferTraits(copyable=True, movable=True)
303
+
304
+ struct_decl = self._structs.get(t.name)
305
+ if struct_decl is not None:
306
+ return self._record_transfer_traits(struct_decl.name)
307
+
308
+ alias_decl = self._aliases.get(t.name)
309
+ if alias_decl is None:
310
+ return TransferTraits(copyable=False, movable=False)
311
+ if alias_decl.kind == AliasKind.CALLBACK_SIGNATURE:
312
+ return TransferTraits(copyable=True, movable=True)
313
+ if alias_decl.kind in (AliasKind.TYPE_ALIAS, AliasKind.UNION_LAYOUT):
314
+ if alias_decl.type_value is None:
315
+ return TransferTraits(copyable=False, movable=False)
316
+ return self._type_transfer_traits(alias_decl.type_value)
317
+ return TransferTraits(copyable=False, movable=False)
318
+ raise AssignRecordPoliciesError(
319
+ f"unsupported Type for transfer traits: {type(t).__name__!r}"
320
+ )
321
+
322
+ def _transfer_traits_for_parametric_arg(self, arg) -> TransferTraits:
323
+ if isinstance(arg, TypeArg):
324
+ return self._type_transfer_traits(arg.type)
325
+ return TransferTraits(copyable=True, movable=True)
326
+
327
+ def _traits_for_decl(
328
+ self,
329
+ decl: StructDecl,
330
+ passability: MojoPassability,
331
+ transfer_traits: TransferTraits,
332
+ ) -> list[StructTraits]:
333
+ traits = list(self._passability_traits(passability))
334
+ if decl.flexible_tail is None and not self._has_representable_atomic_storage(decl):
335
+ if transfer_traits.copyable:
336
+ traits.append(StructTraits.COPYABLE)
337
+ if transfer_traits.movable:
338
+ traits.append(StructTraits.MOVABLE)
339
+ return traits
211
340
 
212
341
  @staticmethod
213
342
  def _has_opaque_storage(decl: StructDecl) -> bool:
@@ -10,6 +10,7 @@ from typing import Annotated, Literal
10
10
  import typer
11
11
  from rich.console import Console
12
12
 
13
+ from mojo_bindgen import __version__
13
14
  from mojo_bindgen.orchestrator import BindgenOptions, BindgenOrchestrator
14
15
  from mojo_bindgen.parsing.frontend import ClangOptions
15
16
  from mojo_bindgen.parsing.parser import ParseError
@@ -20,6 +21,12 @@ LinkModeOption = Literal["external-call", "owned-dl-handle"]
20
21
  DiagnosticsMode = Literal["text", "json", "silent"]
21
22
 
22
23
 
24
+ def _version_callback(value: bool) -> None:
25
+ if value:
26
+ sys.stdout.write(f"mojo-bindgen {__version__}\n")
27
+ raise typer.Exit()
28
+
29
+
23
30
  def run(
24
31
  header: Annotated[
25
32
  Path,
@@ -237,6 +244,15 @@ def run(
237
244
  rich_help_panel="Output",
238
245
  ),
239
246
  ] = False,
247
+ version: Annotated[
248
+ bool,
249
+ typer.Option(
250
+ "--version",
251
+ callback=_version_callback,
252
+ is_eager=True,
253
+ help="Show the mojo-bindgen version and exit.",
254
+ ),
255
+ ] = False,
240
256
  ) -> int:
241
257
  """Generate Mojo FFI from a C header using libclang.
242
258
 
@@ -373,6 +389,8 @@ def _format_diagnostic(d) -> str:
373
389
 
374
390
  def main(argv: list[str] | None = None) -> int:
375
391
  cli_args = argv if argv is not None else sys.argv[1:]
392
+ if not cli_args:
393
+ cli_args = ["--help"]
376
394
  previous_argv = sys.argv[:]
377
395
  sys.argv = ["mojo-bindgen", *cli_args]
378
396
  try:
@@ -12,7 +12,18 @@ from typing import cast
12
12
 
13
13
  import clang.cindex as cx
14
14
 
15
- from mojo_bindgen.ir import Array, FamPattern, Field, IntType, Struct, StructRef, Type
15
+ from mojo_bindgen.ir import (
16
+ Array,
17
+ AtomicType,
18
+ FamPattern,
19
+ Field,
20
+ IntType,
21
+ QualifiedType,
22
+ Struct,
23
+ StructRef,
24
+ Type,
25
+ TypeRef,
26
+ )
16
27
  from mojo_bindgen.parsing.diagnostics import ParserDiagnosticSink
17
28
  from mojo_bindgen.parsing.doc_comments import cursor_doc_comment
18
29
  from mojo_bindgen.parsing.lowering.type_lowering import TypeContext, TypeLowerer
@@ -307,7 +318,14 @@ class RecordLowerer:
307
318
  """Lower one normalized field site into one IR field."""
308
319
  if site.is_bitfield:
309
320
  backing = self.type_lowerer.lower(site.field_type, TypeContext.FIELD)
310
- if not isinstance(backing, IntType):
321
+ integer_backing = self._peel_bitfield_backing(backing)
322
+ if integer_backing is None:
323
+ if site.field_cursor is not None:
324
+ self.diagnostics.add_cursor_diag(
325
+ "warning",
326
+ site.field_cursor,
327
+ "bitfield backing type is not an integer after typedef/cv/atomic peeling; field skipped",
328
+ )
311
329
  return None
312
330
  return Field(
313
331
  name=site.name,
@@ -353,6 +371,22 @@ class RecordLowerer:
353
371
  ),
354
372
  )
355
373
 
374
+ @staticmethod
375
+ def _peel_bitfield_backing(field_type: Type) -> IntType | None:
376
+ """Unwrap layout-transparent wrappers to find the integer bitfield backing type."""
377
+ while True:
378
+ if isinstance(field_type, TypeRef):
379
+ field_type = field_type.canonical
380
+ continue
381
+ if isinstance(field_type, QualifiedType):
382
+ field_type = field_type.unqualified
383
+ continue
384
+ if isinstance(field_type, AtomicType):
385
+ field_type = field_type.value_type
386
+ continue
387
+ break
388
+ return field_type if isinstance(field_type, IntType) else None
389
+
356
390
  def _lower_field_site_type(self, site: FieldSite) -> Type:
357
391
  """Lower the type of one normalized field site."""
358
392
  if site.attached_record is not None:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mojo-bindgen"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Generate Mojo FFI bindings from C headers using libclang"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes