mt-lang 0.2.0

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,622 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module Bindgen
5
+ class Generator
6
+ module GeneratorTypeMapper
7
+ private
8
+
9
+ def map_c_type(qual_type, context:)
10
+ normalized = normalize_c_type(qual_type)
11
+ raise BindgenError, "missing C type for #{context}" if normalized.empty?
12
+ normalized, nullability = extract_top_level_nullability(normalized)
13
+ mapped_type = if array_type?(normalized)
14
+ map_array_type(normalized, context:)
15
+ elsif function_pointer_type?(normalized)
16
+ map_function_pointer_type(normalized, context:)
17
+ else
18
+ pointer_candidate = strip_pointer_suffix_qualifiers(normalized)
19
+ if pointer_type?(pointer_candidate)
20
+ if va_list_pointer?(pointer_candidate)
21
+ synthesize_typedef_dependency("va_list")
22
+ "va_list"
23
+ elsif c_string_pointer?(pointer_candidate)
24
+ "cstr"
25
+ else
26
+ pointee = pointer_candidate.sub(/\s*\*\z/, "")
27
+ pointer_name = top_level_const_qualified?(pointee) ? "const_ptr" : "ptr"
28
+ "#{pointer_name}[#{map_c_type(pointee, context:)}]"
29
+ end
30
+ else
31
+ unqualified = strip_qualifiers(normalized)
32
+ if unqualified == "__va_list_tag" || unqualified == "struct __va_list_tag"
33
+ synthesize_typedef_dependency("__va_list_tag")
34
+ "__va_list_tag"
35
+ elsif standard_typedef_primitive(unqualified)
36
+ standard_typedef_primitive(unqualified)
37
+ elsif PRIMITIVE_TYPE_MAP.key?(unqualified)
38
+ PRIMITIVE_TYPE_MAP.fetch(unqualified)
39
+ elsif @type_overrides.key?(unqualified)
40
+ @type_overrides.fetch(unqualified)
41
+ elsif unqualified.start_with?("long") || unqualified.start_with?("unsigned long") || unqualified.start_with?("signed long")
42
+ map_long_type(unqualified, context:)
43
+ elsif unqualified.start_with?("struct ") || unqualified.start_with?("union ") || unqualified.start_with?("enum ")
44
+ tag_name = unqualified.split.last
45
+ @type_overrides.fetch(tag_name) do
46
+ if unqualified.start_with?("struct ")
47
+ record_name_for(unqualified)
48
+ elsif unqualified.start_with?("union ")
49
+ record_name_for(unqualified)
50
+ else
51
+ enum_name_for(unqualified)
52
+ end
53
+ end
54
+ elsif unqualified.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
55
+ if known_generated_type_name?(unqualified, @visible_typedef_names)
56
+ visible_type_name(unqualified)
57
+ else
58
+ raise BindgenError, "unknown referenced C type #{unqualified.inspect} for #{context}"
59
+ end
60
+ else
61
+ raise BindgenError, "unsupported C type #{qual_type.inspect} for #{context}"
62
+ end
63
+ end
64
+ end
65
+
66
+ apply_nullability(mapped_type, nullability)
67
+ end
68
+
69
+ def array_type?(qual_type)
70
+ qual_type.match?(/\[[0-9]+\]\z/)
71
+ end
72
+
73
+ def map_array_type(qual_type, context:)
74
+ match = qual_type.match(/\A(.+)\[([0-9]+)\]\z/)
75
+ raise BindgenError, "unsupported array type #{qual_type.inspect} for #{context}" unless match
76
+
77
+ element_type = map_c_type(match[1], context: "element type of #{context}")
78
+ length = Integer(match[2], 10)
79
+ "array[#{element_type}, #{length}]"
80
+ end
81
+
82
+ def va_list_pointer?(qual_type)
83
+ stripped = strip_qualifiers(qual_type)
84
+ stripped == "struct __va_list_tag *" || stripped == "__va_list_tag *"
85
+ end
86
+
87
+ def function_pointer_type?(qual_type)
88
+ qual_type.match?(/\A.+\(\s*\*.*\)\s*\(.*\)\z/)
89
+ end
90
+
91
+ def map_function_pointer_typedef(node, context:)
92
+ function_proto = extract_function_proto(node)
93
+ raise BindgenError, "unsupported function pointer type #{node.dig("type", "qualType").inspect} for #{context}" unless function_proto
94
+
95
+ map_function_proto_node(function_proto, context:, nullability: function_pointer_surface_nullability(type_qual_type(node)))
96
+ end
97
+
98
+ def map_function_proto_node(function_proto, context:, nullability: nil)
99
+ inner_types = Array(function_proto["inner"])
100
+ raise BindgenError, "unsupported function pointer type #{function_proto.inspect} for #{context}" if inner_types.empty?
101
+
102
+ return_type = map_type_node(inner_types.first, context: "return type of #{context}")
103
+ param_types = inner_types.drop(1)
104
+ params = if param_types.empty? || (param_types.length == 1 && void_type_node?(param_types.first))
105
+ []
106
+ else
107
+ param_types.each_with_index.map do |param_type, index|
108
+ "arg#{index}: #{map_type_node(param_type, context: "parameter #{index} of #{context}")}"
109
+ end
110
+ end
111
+ apply_nullability("fn(#{params.join(', ')}) -> #{return_type}", nullability)
112
+ end
113
+
114
+ def map_function_pointer_type(qual_type, context:)
115
+ return_type_source, declarator_source, params_source = parse_function_pointer_signature(qual_type, context:)
116
+
117
+ # Handles C forms like `void (*)(...)` and pointer-wrapped variants like `void (**)(...)`.
118
+ pointer_depth_match = declarator_source.match(/\A((?:\*\s*)+)((?:_Nullable|_Nonnull|_Null_unspecified|_Nullable_result)?)\s*\z/)
119
+ if pointer_depth_match
120
+ pointer_depth = pointer_depth_match[1].count("*")
121
+ base_nullability = nullability_for_token(pointer_depth_match[2])
122
+ function_type = build_function_type(
123
+ return_type_source:,
124
+ params_source:,
125
+ nullability: base_nullability,
126
+ context:,
127
+ )
128
+
129
+ wrapped_type = function_type
130
+ (pointer_depth - 1).times do
131
+ wrapped_type = "ptr[#{wrapped_type}]"
132
+ end
133
+ return wrapped_type
134
+ end
135
+
136
+ # Handles C forms like `void (*(*)(...))(...)` (function pointer returning function pointer).
137
+ nested_match = declarator_source.match(/\A\*\s*((?:_Nullable|_Nonnull|_Null_unspecified|_Nullable_result)?)\s*\(\s*\*\s*((?:_Nullable|_Nonnull|_Null_unspecified|_Nullable_result)?)\s*\)\s*\((.*)\)\s*\z/)
138
+ if nested_match
139
+ outer_nullability = nullability_for_token(nested_match[1])
140
+ returned_fn_nullability = nullability_for_token(nested_match[2])
141
+ outer_params_source = nested_match[3]
142
+
143
+ returned_fn_type = build_function_type(
144
+ return_type_source:,
145
+ params_source:,
146
+ nullability: returned_fn_nullability,
147
+ context: "return type of #{context}",
148
+ )
149
+
150
+ outer_params = function_params_from_source(outer_params_source, context:)
151
+ return apply_nullability("fn(#{outer_params.join(', ')}) -> #{returned_fn_type}", outer_nullability)
152
+ end
153
+
154
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}"
155
+ end
156
+
157
+ def parse_function_pointer_signature(qual_type, context:)
158
+ source = qual_type.strip
159
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}" unless source.end_with?(")")
160
+
161
+ params_start = matching_open_paren_index(source, source.length - 1)
162
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}" unless params_start
163
+
164
+ params_source = source[(params_start + 1)...-1]
165
+ prefix = source[0...params_start].rstrip
166
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}" unless prefix.end_with?(")")
167
+
168
+ declarator_end = prefix.length - 1
169
+ declarator_start = matching_open_paren_index(prefix, declarator_end)
170
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}" unless declarator_start
171
+
172
+ declarator_source = prefix[(declarator_start + 1)...declarator_end].strip
173
+ return_type_source = prefix[0...declarator_start].rstrip
174
+ raise BindgenError, "unsupported function pointer type #{qual_type.inspect} for #{context}" if return_type_source.empty?
175
+
176
+ [return_type_source, declarator_source, params_source]
177
+ end
178
+
179
+ def matching_open_paren_index(source, close_index)
180
+ depth = 0
181
+ index = close_index
182
+
183
+ while index >= 0
184
+ char = source[index]
185
+ if char == ")"
186
+ depth += 1
187
+ elsif char == "("
188
+ depth -= 1
189
+ return index if depth.zero?
190
+ end
191
+ index -= 1
192
+ end
193
+
194
+ nil
195
+ end
196
+
197
+ def function_params_from_source(params_source, context:)
198
+ param_list = split_top_level_csv(params_source)
199
+ return [] if param_list.empty? || (param_list.length == 1 && strip_qualifiers(param_list.first) == "void")
200
+
201
+ param_list.each_with_index.map do |param_type, index|
202
+ "arg#{index}: #{map_c_type(param_type, context: "parameter #{index} of #{context}")}"
203
+ end
204
+ end
205
+
206
+ def build_function_type(return_type_source:, params_source:, nullability:, context:)
207
+ return_type = map_c_type(return_type_source, context: "return type of #{context}")
208
+ params = function_params_from_source(params_source, context:)
209
+ apply_nullability("fn(#{params.join(', ')}) -> #{return_type}", nullability)
210
+ end
211
+
212
+ def split_top_level_csv(source)
213
+ return [] if source.strip.empty?
214
+
215
+ parts = []
216
+ current = +""
217
+ depth = 0
218
+
219
+ source.each_char do |char|
220
+ case char
221
+ when "(", "["
222
+ depth += 1
223
+ current << char
224
+ when ")", "]"
225
+ depth -= 1 if depth.positive?
226
+ current << char
227
+ when ","
228
+ if depth.zero?
229
+ parts << current.strip
230
+ current = +""
231
+ else
232
+ current << char
233
+ end
234
+ else
235
+ current << char
236
+ end
237
+ end
238
+
239
+ parts << current.strip unless current.strip.empty?
240
+ parts
241
+ end
242
+
243
+ def extract_function_proto(node)
244
+ queue = Array(node["inner"]).dup
245
+ until queue.empty?
246
+ current = queue.shift
247
+ next unless current.is_a?(Hash)
248
+ return current if current["kind"] == "FunctionProtoType"
249
+
250
+ queue.concat(Array(current["inner"]))
251
+ end
252
+ nil
253
+ end
254
+
255
+ def map_type_node(node, context:)
256
+ alias_name = typedef_name_from_type_node(node)
257
+ return @type_overrides.fetch(alias_name) if alias_name && @type_overrides.key?(alias_name)
258
+
259
+ if alias_name && preserve_typedef_name?(alias_name) && direct_typedef_surface?(node, alias_name)
260
+ synthesize_typedef_dependency(alias_name)
261
+ return visible_type_name(alias_name)
262
+ end
263
+
264
+ qual_type = type_qual_type(node)
265
+ if function_pointer_type?(qual_type)
266
+ function_proto = extract_function_proto(node)
267
+ return map_function_proto_node(function_proto, context:, nullability: function_pointer_surface_nullability(qual_type)) if function_proto
268
+ end
269
+
270
+ map_c_type(qual_type, context:)
271
+ end
272
+
273
+ def preserve_typedef_name?(name)
274
+ name == "va_list" || @visible_typedef_names.include?(name)
275
+ end
276
+
277
+ def direct_typedef_surface?(node, alias_name)
278
+ spelled_type = normalize_c_type(node.dig("type", "qualType"))
279
+ strip_qualifiers(spelled_type) == alias_name
280
+ end
281
+
282
+ def unresolved_alias_target?(mapped_type, alias_names)
283
+ match = mapped_type.match(/\A([A-Za-z_][A-Za-z0-9_]*)\z/)
284
+ return false unless match
285
+
286
+ name = match[1]
287
+ return false unless name.start_with?("__")
288
+
289
+ !known_generated_type_name?(name, alias_names)
290
+ end
291
+
292
+ def known_generated_type_name?(name, alias_names)
293
+ return true if PRIMITIVE_TYPE_MAP.value?(name)
294
+ return true if alias_names.include?(name)
295
+ return true if @record_visible_names.value?(name)
296
+ return true if @enum_visible_names.value?(name)
297
+ return true if @synthetic_declarations.any? { |declaration| declaration[:name] == name }
298
+
299
+ false
300
+ end
301
+
302
+ def typedef_name_from_type_node(node)
303
+ queue = [node]
304
+ until queue.empty?
305
+ current = queue.shift
306
+ next unless current.is_a?(Hash)
307
+
308
+ if current["kind"] == "TypedefType"
309
+ name = current.dig("decl", "name")
310
+ return name if name && name != "__builtin_va_list"
311
+ end
312
+
313
+ queue.concat(Array(current["inner"]))
314
+ end
315
+ nil
316
+ end
317
+
318
+ def void_type_node?(node)
319
+ node["kind"] == "BuiltinType" && normalize_c_type(node.dig("type", "qualType")) == "void"
320
+ end
321
+
322
+ def synthesize_typedef_dependency(name)
323
+ case name
324
+ when "va_list"
325
+ return if @synthetic_declarations.any? { |declaration| declaration[:name] == "va_list" }
326
+
327
+ @synthetic_declarations << { kind: "opaque", name: "va_list", linkage_name: "va_list" }
328
+ when "__va_list_tag"
329
+ return if @synthetic_declarations.any? { |declaration| declaration[:name] == "__va_list_tag" }
330
+
331
+ @synthetic_declarations << { kind: "opaque", name: "__va_list_tag", linkage_name: "__va_list_tag" }
332
+ end
333
+ end
334
+
335
+ def record_c_name(node)
336
+ return unless node
337
+
338
+ typedef_name = @record_aliases[node["id"]]
339
+ typedef_name ||= @record_aliases_by_tag_name[node["name"]] if node["name"]
340
+ return typedef_name if typedef_name
341
+
342
+ tag_name = node["name"]
343
+ return "#{node["tagUsed"]} #{tag_name}" if tag_name && !tag_name.empty?
344
+
345
+ nil
346
+ end
347
+
348
+ def aggregate_explicit_c_name(name, node)
349
+ c_name = record_c_name(node)
350
+ return if c_name.nil? || c_name == name
351
+
352
+ c_name
353
+ end
354
+
355
+ def synthetic_declarations_for(declarations)
356
+ existing_names = declarations.filter_map { |declaration| declaration[:name] }.to_h { |name| [name, true] }
357
+ @synthetic_declarations.reject { |declaration| existing_names.key?(declaration[:name]) }
358
+ end
359
+
360
+ def normalize_c_type(qual_type)
361
+ qual_type.to_s.gsub(/\s+/, " ").strip
362
+ end
363
+
364
+ def extract_top_level_nullability(qual_type)
365
+ result = qual_type
366
+ nullability = nil
367
+ qualifier_pattern = NULLABILITY_QUALIFIERS.join("|")
368
+
369
+ loop do
370
+ match = result.match(/\s*(#{qualifier_pattern})\z/)
371
+ break unless match
372
+
373
+ nullability ||= nullability_for_token(match[1])
374
+ result = result[0...match.begin(0)].rstrip
375
+ end
376
+
377
+ [result, nullability]
378
+ end
379
+
380
+ def function_pointer_surface_nullability(qual_type)
381
+ match = normalize_c_type(qual_type).match(/\(\s*\*\s*(_Nullable|_Nonnull|_Null_unspecified|_Nullable_result)?\s*\)\s*\(/)
382
+ nullability_for_token(match && match[1])
383
+ end
384
+
385
+ def nullability_for_token(token)
386
+ return :nullable if token == "_Nullable" || token == "_Nullable_result"
387
+ return :nonnull if token == "_Nonnull"
388
+ return :unspecified if token == "_Null_unspecified"
389
+
390
+ nil
391
+ end
392
+
393
+ def apply_nullability(mapped_type, nullability)
394
+ return mapped_type unless nullability == :nullable
395
+ return mapped_type if mapped_type.end_with?("?")
396
+
397
+ "#{mapped_type}?"
398
+ end
399
+
400
+ def nullable_policy_type?(type)
401
+ type.include?("?")
402
+ end
403
+
404
+ def record_nullable_param_override(function_name, param_name, param_node, override_type)
405
+ return unless nullable_policy_type?(override_type)
406
+
407
+ auto_type, auto_error = infer_bindgen_type do
408
+ map_type_node(param_node, context: "parameter #{param_name} of #{function_name}")
409
+ end
410
+ return if auto_error.nil? && auto_type == override_type
411
+
412
+ @manual_nullable_param_overrides << {
413
+ function: function_name,
414
+ parameter: param_name,
415
+ override_type: override_type,
416
+ auto_type: auto_type,
417
+ c_type: type_qual_type(param_node),
418
+ auto_error: auto_error,
419
+ }.compact
420
+ end
421
+
422
+ def record_nullable_return_override(node, override_type)
423
+ return unless nullable_policy_type?(override_type)
424
+
425
+ auto_type, auto_error = infer_bindgen_type do
426
+ map_c_type(function_return_type(node), context: "return type of #{node["name"]}")
427
+ end
428
+ return if auto_error.nil? && auto_type == override_type
429
+
430
+ @manual_nullable_return_overrides << {
431
+ function: node["name"],
432
+ override_type: override_type,
433
+ auto_type: auto_type,
434
+ c_type: function_return_type(node),
435
+ auto_error: auto_error,
436
+ }.compact
437
+ end
438
+
439
+ def infer_bindgen_type
440
+ [yield, nil]
441
+ rescue BindgenError => e
442
+ [nil, e.message]
443
+ end
444
+
445
+ def strip_pointer_suffix_qualifiers(qual_type)
446
+ result = qual_type
447
+ qualifier_pattern = QUALIFIERS.join("|")
448
+
449
+ loop do
450
+ updated = result.sub(/\s*(?:#{qualifier_pattern})\z/, "")
451
+ break if updated == result
452
+
453
+ result = updated
454
+ end
455
+
456
+ result
457
+ end
458
+
459
+ def top_level_const_qualified?(qual_type)
460
+ normalized = normalize_c_type(qual_type)
461
+ pointer_candidate = strip_pointer_suffix_qualifiers(normalized)
462
+ if pointer_type?(pointer_candidate)
463
+ return pointer_suffix_qualifiers(normalized).include?("const")
464
+ end
465
+
466
+ normalized.split.include?("const")
467
+ end
468
+
469
+ def pointer_suffix_qualifiers(qual_type)
470
+ result = qual_type
471
+ qualifier_pattern = QUALIFIERS.join("|")
472
+ qualifiers = []
473
+
474
+ loop do
475
+ match = result.match(/\s*(#{qualifier_pattern})\z/)
476
+ break unless match
477
+
478
+ qualifiers << match[1]
479
+ result = result[0...match.begin(0)]
480
+ end
481
+
482
+ qualifiers
483
+ end
484
+
485
+ def standard_typedef_primitive(unqualified)
486
+ return "ptr_uint" if unqualified == "size_t"
487
+ return "ptr_int" if unqualified == "ssize_t" || unqualified == "ptrdiff_t"
488
+ return "int" if unqualified == "wchar_t"
489
+
490
+ integer_typedef_primitive(unqualified)
491
+ end
492
+
493
+ def integer_typedef_primitive(unqualified)
494
+ match = unqualified.match(/\A(?:__)?(u_?)?int(8|16|32|64)_t\z/)
495
+ return unless match
496
+
497
+ signed_prefix = match[1]
498
+ width = match[2]
499
+ if signed_prefix
500
+ {
501
+ "8" => "ubyte",
502
+ "16" => "ushort",
503
+ "32" => "uint",
504
+ "64" => "ulong",
505
+ }.fetch(width)
506
+ else
507
+ {
508
+ "8" => "byte",
509
+ "16" => "short",
510
+ "32" => "int",
511
+ "64" => "long",
512
+ }.fetch(width)
513
+ end
514
+ end
515
+
516
+ def pointer_type?(qual_type)
517
+ qual_type.end_with?("*")
518
+ end
519
+
520
+ def c_string_pointer?(qual_type)
521
+ pointee = qual_type.sub(/\s*\*\z/, "")
522
+ unqualified = strip_qualifiers(pointee)
523
+ unqualified == "char" && pointee.split.include?("const")
524
+ end
525
+
526
+ def string_literal_macro_compatible_c_type?(qual_type)
527
+ pointer_candidate = strip_pointer_suffix_qualifiers(qual_type)
528
+ return true if c_string_pointer?(pointer_candidate)
529
+
530
+ match = qual_type.match(/\A(.+)\[[0-9]+\]\z/)
531
+ return false unless match
532
+
533
+ strip_qualifiers(match[1]) == "char"
534
+ end
535
+
536
+ def strip_qualifiers(qual_type)
537
+ qual_type.split.reject { |token| QUALIFIERS.include?(token) }.join(" ")
538
+ end
539
+
540
+ def map_long_type(unqualified, context:)
541
+ case unqualified
542
+ when "long", "long int", "signed long", "signed long int"
543
+ long_width_type(signed: true)
544
+ when "unsigned long", "unsigned long int"
545
+ long_width_type(signed: false)
546
+ else
547
+ raise BindgenError, "unsupported C type #{context}: #{unqualified.inspect}"
548
+ end
549
+ end
550
+
551
+ def long_width_type(signed:)
552
+ width = long_width_bytes
553
+ mapping = signed ? { 4 => "int", 8 => "long" } : { 4 => "uint", 8 => "ulong" }
554
+ mapping.fetch(width) do
555
+ raise BindgenError, "unsupported C long width #{width} bytes"
556
+ end
557
+ end
558
+
559
+ def long_width_bytes
560
+ return @long_width_bytes if defined?(@long_width_bytes)
561
+
562
+ stdout, stderr, status = Open3.capture3(@clang, "-x", "c", "-dM", "-E", "-", *@clang_args, stdin_data: "")
563
+ unless status.success?
564
+ details = [stdout, stderr].reject(&:empty?).join
565
+ raise BindgenError, details.empty? ? "failed to query clang target macros" : "failed to query clang target macros:\n#{details}"
566
+ end
567
+
568
+ define = stdout.lines.find { |line| line.start_with?("#define __SIZEOF_LONG__ ") }
569
+ raise BindgenError, "clang did not report __SIZEOF_LONG__" unless define
570
+
571
+ @long_width_bytes = Integer(define.split.last, 10)
572
+ end
573
+
574
+ def record_name_for(unqualified)
575
+ synthesize_record_dependency(unqualified)
576
+ tag_name = unqualified.split.last
577
+ @record_visible_names[tag_name] || tag_name
578
+ end
579
+
580
+ def enum_name_for(unqualified)
581
+ tag_name = unqualified.split.last
582
+ @enum_visible_names[tag_name] || tag_name
583
+ end
584
+
585
+ def synthesize_record_dependency(unqualified)
586
+ kind, tag_name = unqualified.split(" ", 2)
587
+ return unless %w[struct union].include?(kind)
588
+ return if tag_name.nil? || tag_name.empty?
589
+ return if @record_visible_names.key?(tag_name) || @record_visible_names.value?(tag_name)
590
+
591
+ record_node = @referenceable_record_declarations[tag_name]
592
+ original_name = record_node ? (@record_aliases[record_node["id"]] || tag_name) : tag_name
593
+ visible_name = visible_type_name(original_name)
594
+ return if @synthetic_declarations.any? { |declaration| declaration[:name] == visible_name }
595
+
596
+ @record_visible_names[tag_name] = visible_name
597
+ @record_visible_names[visible_name] = visible_name
598
+
599
+ if record_node && record_complete_definition?(record_node)
600
+ @synthetic_declarations << {
601
+ kind: record_node.fetch("tagUsed"),
602
+ name: visible_name,
603
+ node: record_node,
604
+ }
605
+ @aggregate_declarations[visible_name] = record_node
606
+ return
607
+ end
608
+
609
+ @synthetic_declarations << {
610
+ kind: "opaque",
611
+ name: visible_name,
612
+ linkage_name: "#{kind} #{tag_name}",
613
+ }
614
+ end
615
+
616
+ def visible_type_name(name)
617
+ @type_name_overrides.fetch(name, name)
618
+ end
619
+ end
620
+ end
621
+ end
622
+ end