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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,1453 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module LowererUtils
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def range_iterable?(expression)
|
|
9
|
+
range_expr?(expression)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def range_start_of(iterable)
|
|
13
|
+
iterable.start_expr
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def range_end_of(iterable)
|
|
17
|
+
iterable.end_expr
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def wildcard_arm_pattern?(expression)
|
|
21
|
+
expression.is_a?(AST::Identifier) && expression.name == "_"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def variant_match_arm_name_from_pattern(pattern)
|
|
25
|
+
callee = case pattern
|
|
26
|
+
when AST::Call
|
|
27
|
+
pattern.callee
|
|
28
|
+
else
|
|
29
|
+
pattern
|
|
30
|
+
end
|
|
31
|
+
callee.is_a?(AST::MemberAccess) ? callee.member : nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def async_variant_match_arm_binding(arm, scrutinee_expr, scrutinee_type, env:, frame_expr: nil, local_fields: nil)
|
|
35
|
+
arm_env = duplicate_env(env)
|
|
36
|
+
binding_decl = nil
|
|
37
|
+
|
|
38
|
+
if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
|
|
39
|
+
arm_name = variant_match_arm_name_from_pattern(arm.pattern)
|
|
40
|
+
if arm_name && scrutinee_type.has_payload?(arm_name)
|
|
41
|
+
fields = scrutinee_type.arm(arm_name)
|
|
42
|
+
payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
|
|
43
|
+
|
|
44
|
+
field_key = async_match_binding_field_key(arm)
|
|
45
|
+
field_info = local_fields&.fetch(field_key, nil)
|
|
46
|
+
if field_info && frame_expr
|
|
47
|
+
target = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
|
|
48
|
+
binding_c = async_frame_field_c_name(field_info[:field_name])
|
|
49
|
+
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
|
|
50
|
+
data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
|
|
51
|
+
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
52
|
+
binding_decl = IR::Assignment.new(target:, operator: "=", value: arm_expr)
|
|
53
|
+
else
|
|
54
|
+
data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
|
|
55
|
+
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
56
|
+
binding_c = c_local_name(arm.binding_name)
|
|
57
|
+
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
|
|
58
|
+
binding_decl = IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
[arm_env, binding_decl]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
|
|
67
|
+
return unless scrutinee_type.is_a?(Types::Variant)
|
|
68
|
+
return unless arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
|
|
69
|
+
|
|
70
|
+
arm_name = variant_match_arm_name_from_pattern(arm.pattern)
|
|
71
|
+
return unless arm_name && scrutinee_type.has_payload?(arm_name)
|
|
72
|
+
|
|
73
|
+
fields = scrutinee_type.arm(arm_name)
|
|
74
|
+
payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
|
|
75
|
+
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: c_local_name(arm.binding_name), mutable: true, pointer: false)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def array_type?(type)
|
|
79
|
+
type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
|
|
80
|
+
type.arguments[1].is_a?(Types::LiteralTypeArg)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def array_element_type(type)
|
|
84
|
+
return unless array_type?(type)
|
|
85
|
+
|
|
86
|
+
type.arguments.first
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def array_to_span_compatible?(actual_type, expected_type)
|
|
90
|
+
array_type?(actual_type) && expected_type.is_a?(Types::Span) && array_element_type(actual_type) == expected_type.element_type
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def cstr_trackable_type?(type)
|
|
94
|
+
type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def struct_contains_string_field?(type)
|
|
98
|
+
return false unless type.is_a?(Types::Struct)
|
|
99
|
+
|
|
100
|
+
type.fields.any? { |_name, field_type| cstr_trackable_type?(field_type) || struct_contains_string_field?(field_type) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def suppress_format_releases_for_assignment(cleanups, target_type)
|
|
104
|
+
return cleanups unless cstr_trackable_type?(target_type) || struct_contains_string_field?(target_type)
|
|
105
|
+
|
|
106
|
+
cleanups.reject do |items|
|
|
107
|
+
items.any? { |stmt| stmt.is_a?(IR::ExpressionStmt) && stmt.expression.is_a?(IR::Call) && stmt.expression.callee == "mt_format_str_release" }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def cstr_list_trackable_type?(type)
|
|
112
|
+
return false unless array_type?(type)
|
|
113
|
+
|
|
114
|
+
element_type = array_element_type(type)
|
|
115
|
+
element_type == @ctx.types.fetch("str") || element_type == @ctx.types.fetch("cstr")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def str_buffer_to_span_compatible?(actual_type, expected_type)
|
|
119
|
+
str_buffer_type?(actual_type) && expected_type.is_a?(Types::Span) && expected_type.element_type == @ctx.types.fetch("char")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def array_length(type)
|
|
123
|
+
return unless array_type?(type)
|
|
124
|
+
|
|
125
|
+
type.arguments[1].value
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def char_array_text_type?(type)
|
|
129
|
+
array_type?(type) && array_element_type(type) == @ctx.types.fetch("char")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def str_buffer_type?(type)
|
|
133
|
+
type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
|
|
134
|
+
type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def str_buffer_capacity(type)
|
|
138
|
+
type.arguments.first.value
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def str_buffer_storage_capacity(type)
|
|
142
|
+
str_buffer_capacity(type) + 1
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def addressable_storage_expression?(expression)
|
|
146
|
+
case expression
|
|
147
|
+
when AST::Identifier
|
|
148
|
+
true
|
|
149
|
+
when AST::MemberAccess, AST::IndexAccess
|
|
150
|
+
addressable_storage_expression?(expression.receiver)
|
|
151
|
+
when AST::Call
|
|
152
|
+
read_call?(expression)
|
|
153
|
+
else
|
|
154
|
+
false
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def read_call?(expression)
|
|
159
|
+
expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def infer_value_type(handle_expression, env:)
|
|
163
|
+
handle_type = infer_expression_type(handle_expression, env:)
|
|
164
|
+
return referenced_type(handle_type) if ref_type?(handle_type)
|
|
165
|
+
return pointee_type(handle_type) if pointer_type?(handle_type)
|
|
166
|
+
|
|
167
|
+
raise LoweringError, "read expects ref[...] or ptr[...], got #{handle_type}"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def infer_method_receiver_type(receiver_expression, env:, member_name: nil)
|
|
171
|
+
receiver_type = infer_expression_type(receiver_expression, env:)
|
|
172
|
+
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
173
|
+
|
|
174
|
+
if pointer_type?(receiver_type)
|
|
175
|
+
dispatch_receiver_type = method_dispatch_receiver_type(receiver_type)
|
|
176
|
+
return receiver_type if member_name && (@method_definitions.key?([receiver_type, member_name]) || @method_definitions.key?([dispatch_receiver_type, member_name]) || @method_definitions.key?([receiver_type, "static:#{member_name}"]) || @method_definitions.key?([dispatch_receiver_type, "static:#{member_name}"]))
|
|
177
|
+
|
|
178
|
+
return pointee_type(receiver_type)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
receiver_type
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def infer_field_receiver_type(receiver_expression, env:)
|
|
185
|
+
receiver_type = infer_expression_type(receiver_expression, env:)
|
|
186
|
+
return referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
187
|
+
return pointee_type(receiver_type) if pointer_type?(receiver_type)
|
|
188
|
+
|
|
189
|
+
receiver_type
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def collection_loop_type(type)
|
|
193
|
+
super
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def collection_loop_binding_type(iterable_type, element_type)
|
|
197
|
+
super
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def collection_loop_ref_element_type?(type)
|
|
201
|
+
super
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def iterator_loop_info(type, env:)
|
|
205
|
+
iter_name = "__mt_for_iterable__"
|
|
206
|
+
iterator_name = "__mt_for_iterator__"
|
|
207
|
+
probe_env = duplicate_env(env)
|
|
208
|
+
current_actual_scope(probe_env[:scopes])[iter_name] = local_binding(type:, linkage_name: iter_name, mutable: false, pointer: false)
|
|
209
|
+
|
|
210
|
+
iter_call = AST::Call.new(
|
|
211
|
+
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iter_name), member: "iter"),
|
|
212
|
+
arguments: [],
|
|
213
|
+
)
|
|
214
|
+
iterator_type = infer_expression_type(iter_call, env: probe_env)
|
|
215
|
+
|
|
216
|
+
current_actual_scope(probe_env[:scopes])[iterator_name] = local_binding(type: iterator_type, linkage_name: iterator_name, mutable: true, pointer: false)
|
|
217
|
+
next_call = AST::Call.new(
|
|
218
|
+
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
|
|
219
|
+
arguments: [],
|
|
220
|
+
)
|
|
221
|
+
item_storage_type = infer_expression_type(next_call, env: probe_env)
|
|
222
|
+
if item_storage_type.is_a?(Types::Nullable) && nullable_iterator_item_type?(item_storage_type.base)
|
|
223
|
+
return {
|
|
224
|
+
kind: :nullable_item,
|
|
225
|
+
iterator_type:,
|
|
226
|
+
item_storage_type:,
|
|
227
|
+
item_type: item_storage_type.base,
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
if item_storage_type == @ctx.types.fetch("bool")
|
|
232
|
+
current_call = AST::Call.new(
|
|
233
|
+
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
|
|
234
|
+
arguments: [],
|
|
235
|
+
)
|
|
236
|
+
current_type = infer_expression_type(current_call, env: probe_env)
|
|
237
|
+
return {
|
|
238
|
+
kind: :current_item,
|
|
239
|
+
iterator_type:,
|
|
240
|
+
item_storage_type: current_type,
|
|
241
|
+
item_type: current_type,
|
|
242
|
+
}
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
nil
|
|
246
|
+
rescue LoweringError
|
|
247
|
+
nil
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def nullable_iterator_item_type?(type)
|
|
251
|
+
type == @ctx.types.fetch("cstr") || pointer_type?(type)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
|
|
255
|
+
if array_type?(iterable_type)
|
|
256
|
+
IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
|
|
257
|
+
else
|
|
258
|
+
data_ref = IR::Member.new(receiver: iterable_ref, member: "data", type: pointer_to(element_type))
|
|
259
|
+
IR::Index.new(receiver: data_ref, index: index_ref, type: element_type)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def collection_loop_stop_value(iterable_ref, iterable_type)
|
|
264
|
+
if array_type?(iterable_type)
|
|
265
|
+
IR::IntegerLiteral.new(value: array_length(iterable_type), type: @ctx.types.fetch("ptr_uint"))
|
|
266
|
+
else
|
|
267
|
+
IR::Member.new(receiver: iterable_ref, member: "len", type: @ctx.types.fetch("ptr_uint"))
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def lower_fatal_statement(message, env:)
|
|
272
|
+
IR::ExpressionStmt.new(
|
|
273
|
+
expression: lower_expression(
|
|
274
|
+
AST::Call.new(
|
|
275
|
+
callee: AST::Identifier.new(name: "fatal"),
|
|
276
|
+
arguments: [AST::Argument.new(name: nil, value: AST::StringLiteral.new(lexeme: message.inspect, value: message, cstring: false))],
|
|
277
|
+
),
|
|
278
|
+
env:,
|
|
279
|
+
expected_type: @ctx.types.fetch("void"),
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def infer_range_loop_type(expression, env:)
|
|
285
|
+
start_expr = range_start_of(expression)
|
|
286
|
+
stop_expr = range_end_of(expression)
|
|
287
|
+
start_type = infer_expression_type(start_expr, env:)
|
|
288
|
+
stop_type = infer_expression_type(stop_expr, env:)
|
|
289
|
+
|
|
290
|
+
if start_type != stop_type
|
|
291
|
+
if start_expr.is_a?(AST::IntegerLiteral)
|
|
292
|
+
start_type = infer_expression_type(start_expr, env:, expected_type: stop_type)
|
|
293
|
+
elsif stop_expr.is_a?(AST::IntegerLiteral)
|
|
294
|
+
stop_type = infer_expression_type(stop_expr, env:, expected_type: start_type)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
raise LoweringError, "range bounds must use matching integer types, got #{start_type} and #{stop_type}" unless start_type == stop_type
|
|
299
|
+
|
|
300
|
+
start_type
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def integer_type?(type)
|
|
304
|
+
type.is_a?(Types::Primitive) && type.integer?
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def infer_index_result_type(receiver_type, index_type)
|
|
308
|
+
raise LoweringError, "index must be an integer type, got #{index_type}" unless integer_type?(index_type)
|
|
309
|
+
|
|
310
|
+
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
311
|
+
|
|
312
|
+
if array_type?(receiver_type)
|
|
313
|
+
return array_element_type(receiver_type)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
if receiver_type.is_a?(Types::Span)
|
|
317
|
+
return receiver_type.element_type
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
if receiver_type.is_a?(Types::SoA)
|
|
321
|
+
return receiver_type.element_type
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
if pointer_type?(receiver_type)
|
|
325
|
+
return pointee_type(receiver_type)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
raise LoweringError, "cannot index #{receiver_type}"
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def contains_type_var?(type)
|
|
332
|
+
super
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def stored_ref_supported_type?(type, visited = {})
|
|
337
|
+
return true unless type
|
|
338
|
+
|
|
339
|
+
visit_key = [type.class, type.object_id]
|
|
340
|
+
return true if visited[visit_key]
|
|
341
|
+
|
|
342
|
+
visited[visit_key] = true
|
|
343
|
+
case type
|
|
344
|
+
when Types::Nullable
|
|
345
|
+
stored_ref_supported_type?(type.base, visited)
|
|
346
|
+
when Types::GenericInstance
|
|
347
|
+
if ref_type?(type)
|
|
348
|
+
lt = ref_lifetime(type)
|
|
349
|
+
return !!lt # lifetime-ref is supported by default; bare ref is not
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
type.arguments.all? { |argument| argument.is_a?(Types::LiteralTypeArg) || stored_ref_supported_type?(argument, visited) }
|
|
353
|
+
when Types::Span
|
|
354
|
+
stored_ref_supported_type?(type.element_type, visited)
|
|
355
|
+
when Types::Task
|
|
356
|
+
stored_ref_supported_type?(type.result_type, visited)
|
|
357
|
+
when Types::StructInstance, Types::VariantInstance
|
|
358
|
+
type.arguments.all? { |argument| stored_ref_supported_type?(argument, visited) }
|
|
359
|
+
when Types::Proc, Types::Function
|
|
360
|
+
callable_param_ref_supported?(type)
|
|
361
|
+
else
|
|
362
|
+
!contains_ref_type?(type)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def pointer_to(type)
|
|
367
|
+
Types::Registry.generic_instance("ptr", [type])
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def with_analysis_context(analysis)
|
|
371
|
+
saved = @ctx.save
|
|
372
|
+
@ctx.install(analysis)
|
|
373
|
+
@ctx.module_prefix = module_c_prefix(@ctx.module_name)
|
|
374
|
+
yield
|
|
375
|
+
ensure
|
|
376
|
+
@ctx.restore(saved)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def lookup_value(name, env)
|
|
380
|
+
env[:scopes].reverse_each do |scope|
|
|
381
|
+
return scope[name] if scope.key?(name)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
if @ctx.values.key?(name)
|
|
385
|
+
binding = @ctx.values.fetch(name)
|
|
386
|
+
{
|
|
387
|
+
type: binding.type,
|
|
388
|
+
storage_type: binding.storage_type,
|
|
389
|
+
linkage_name: value_c_name(name),
|
|
390
|
+
mutable: binding.mutable,
|
|
391
|
+
pointer: false,
|
|
392
|
+
cstr_backed: cstr_trackable_type?(binding.type) && binding.const_value.is_a?(String),
|
|
393
|
+
cstr_list_backed: false,
|
|
394
|
+
const_value: binding.const_value,
|
|
395
|
+
}
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def lower_static_storage_initializer(expression, env:, expected_type: nil)
|
|
400
|
+
if expected_type && (literal = lower_compile_time_literal(compile_time_const_value(expression, env:), expected_type))
|
|
401
|
+
return literal
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
lower_expression(rewrite_static_storage_initializer(expression), env:, expected_type: expected_type)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def lower_compile_time_literal(value, type)
|
|
408
|
+
case value
|
|
409
|
+
when true, false
|
|
410
|
+
return IR::BooleanLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.boolean?
|
|
411
|
+
when Integer
|
|
412
|
+
return IR::IntegerLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.integer?
|
|
413
|
+
return IR::FloatLiteral.new(value: value.to_f, type:) if type.is_a?(Types::Primitive) && type.float?
|
|
414
|
+
when Float
|
|
415
|
+
return IR::FloatLiteral.new(value:, type:) if type.is_a?(Types::Primitive) && type.float?
|
|
416
|
+
when String
|
|
417
|
+
if type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr")
|
|
418
|
+
return IR::StringLiteral.new(value:, type:, cstring: type == @ctx.types.fetch("cstr"))
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
nil
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def compile_time_builtin_function_type(name, arguments, env)
|
|
426
|
+
return_type = case name
|
|
427
|
+
when "field_of"
|
|
428
|
+
@ctx.types.fetch("field_handle")
|
|
429
|
+
when "callable_of"
|
|
430
|
+
@ctx.types.fetch("callable_handle")
|
|
431
|
+
when "has_attribute"
|
|
432
|
+
@ctx.types.fetch("bool")
|
|
433
|
+
when "attribute_of"
|
|
434
|
+
@ctx.types.fetch("attribute_handle")
|
|
435
|
+
else
|
|
436
|
+
nil
|
|
437
|
+
end
|
|
438
|
+
raise LoweringError, "unsupported compile-time builtin #{name}" unless return_type
|
|
439
|
+
|
|
440
|
+
Types::Registry.function(name, params: [], return_type: return_type)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def compile_time_builtin_specialization_function_type(callee)
|
|
444
|
+
Types::Registry.function("attribute_arg", params: [], return_type: resolve_type_ref(callee.arguments.fetch(0).value))
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def rewrite_static_storage_initializer(expression)
|
|
449
|
+
case expression
|
|
450
|
+
when AST::Identifier
|
|
451
|
+
binding = @ctx.values[expression.name]
|
|
452
|
+
if binding&.kind == :const
|
|
453
|
+
declaration = const_declaration_for_module(@ctx.module_name, expression.name)
|
|
454
|
+
return rewrite_static_storage_initializer(declaration.value)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
expression
|
|
458
|
+
when AST::MemberAccess
|
|
459
|
+
if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name)
|
|
460
|
+
imported_module = @ctx.imports.fetch(expression.receiver.name)
|
|
461
|
+
if (binding = imported_module.values[expression.member])&.kind == :const
|
|
462
|
+
imported_analysis = analysis_for_module(imported_module.name)
|
|
463
|
+
declaration = const_declaration_for_module(imported_module.name, expression.member)
|
|
464
|
+
return with_analysis_context(imported_analysis) do
|
|
465
|
+
rewrite_static_storage_initializer(declaration.value)
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
AST::MemberAccess.new(
|
|
471
|
+
receiver: rewrite_static_storage_initializer(expression.receiver),
|
|
472
|
+
member: expression.member,
|
|
473
|
+
)
|
|
474
|
+
when AST::UnaryOp
|
|
475
|
+
AST::UnaryOp.new(operator: expression.operator, operand: rewrite_static_storage_initializer(expression.operand))
|
|
476
|
+
when AST::BinaryOp
|
|
477
|
+
AST::BinaryOp.new(
|
|
478
|
+
operator: expression.operator,
|
|
479
|
+
left: rewrite_static_storage_initializer(expression.left),
|
|
480
|
+
right: rewrite_static_storage_initializer(expression.right),
|
|
481
|
+
)
|
|
482
|
+
when AST::IfExpr
|
|
483
|
+
AST::IfExpr.new(
|
|
484
|
+
condition: rewrite_static_storage_initializer(expression.condition),
|
|
485
|
+
then_expression: rewrite_static_storage_initializer(expression.then_expression),
|
|
486
|
+
else_expression: rewrite_static_storage_initializer(expression.else_expression),
|
|
487
|
+
)
|
|
488
|
+
when AST::UnsafeExpr
|
|
489
|
+
AST::UnsafeExpr.new(expression: rewrite_static_storage_initializer(expression.expression))
|
|
490
|
+
when AST::Call
|
|
491
|
+
AST::Call.new(
|
|
492
|
+
callee: rewrite_static_storage_initializer(expression.callee),
|
|
493
|
+
arguments: expression.arguments.map do |argument|
|
|
494
|
+
AST::Argument.new(name: argument.name, value: rewrite_static_storage_initializer(argument.value))
|
|
495
|
+
end,
|
|
496
|
+
)
|
|
497
|
+
when AST::Specialization
|
|
498
|
+
AST::Specialization.new(
|
|
499
|
+
callee: rewrite_static_storage_initializer(expression.callee),
|
|
500
|
+
arguments: expression.arguments.map { |argument| AST::TypeArgument.new(value: argument.value) },
|
|
501
|
+
)
|
|
502
|
+
else
|
|
503
|
+
expression
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def local_binding(type:, linkage_name:, mutable:, pointer:, storage_type: nil, projection: nil, cstr_backed: false, cstr_list_backed: false, const_value: nil)
|
|
508
|
+
{ type:, storage_type: storage_type || type, linkage_name:, mutable:, pointer:, projection:, cstr_backed:, cstr_list_backed:, const_value: }
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def callable_type?(type)
|
|
512
|
+
type.is_a?(Types::Function) || proc_type?(type)
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def contains_proc_storage_type?(type, visited = Set.new)
|
|
516
|
+
return false if visited.include?(type.object_id)
|
|
517
|
+
|
|
518
|
+
case type
|
|
519
|
+
when Types::Proc
|
|
520
|
+
true
|
|
521
|
+
when Types::Struct, Types::StructInstance
|
|
522
|
+
visited.add(type.object_id)
|
|
523
|
+
type.fields.each_value.any? { |field_type| contains_proc_storage_type?(field_type, visited) }
|
|
524
|
+
when Types::Nullable
|
|
525
|
+
contains_proc_storage_type?(type.base, visited)
|
|
526
|
+
else
|
|
527
|
+
false
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def contains_task_type?(type, visited = Set.new)
|
|
532
|
+
return false if visited.include?(type.object_id)
|
|
533
|
+
|
|
534
|
+
case type
|
|
535
|
+
when Types::Task
|
|
536
|
+
true
|
|
537
|
+
when Types::Struct, Types::StructInstance, Types::Union, Types::GenericStructDefinition, Types::VariantArmPayload
|
|
538
|
+
visited.add(type.object_id)
|
|
539
|
+
type.fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
|
|
540
|
+
when Types::VariantInstance
|
|
541
|
+
type.arguments.any? { |arg| contains_task_type?(arg, visited) }
|
|
542
|
+
when Types::Variant, Types::GenericVariantDefinition
|
|
543
|
+
visited.add(type.object_id)
|
|
544
|
+
type.arms.each_value.any? do |arm_fields|
|
|
545
|
+
arm_fields.each_value.any? { |ft| contains_task_type?(ft, visited) }
|
|
546
|
+
end
|
|
547
|
+
when Types::GenericInstance
|
|
548
|
+
type.arguments.any? { |arg| contains_task_type?(arg, visited) }
|
|
549
|
+
when Types::Nullable
|
|
550
|
+
contains_task_type?(type.base, visited)
|
|
551
|
+
else
|
|
552
|
+
false
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def proc_env_pointer_type
|
|
557
|
+
@proc_env_pointer_type ||= pointer_to(@ctx.types.fetch("void"))
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def proc_invoke_function_type(proc_type)
|
|
561
|
+
Types::Registry.function(
|
|
562
|
+
nil,
|
|
563
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type), *proc_type.params],
|
|
564
|
+
return_type: proc_type.return_type,
|
|
565
|
+
)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def proc_release_function_type
|
|
569
|
+
@proc_release_function_type ||= Types::Registry.function(
|
|
570
|
+
nil,
|
|
571
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type)],
|
|
572
|
+
return_type: @ctx.types.fetch("void"),
|
|
573
|
+
)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def proc_retain_function_type
|
|
577
|
+
@proc_retain_function_type ||= Types::Registry.function(
|
|
578
|
+
nil,
|
|
579
|
+
params: [Types::Registry.parameter("env", proc_env_pointer_type)],
|
|
580
|
+
return_type: @ctx.types.fetch("void"),
|
|
581
|
+
)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def fresh_proc_symbol
|
|
585
|
+
@synthetic_proc_counter += 1
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def current_actual_scope(scopes)
|
|
589
|
+
scopes.reverse_each do |scope|
|
|
590
|
+
return scope unless scope.is_a?(FlowScope)
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
raise LoweringError, "missing lexical scope"
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def env_with_refinements(env, refinements)
|
|
597
|
+
updated = env.dup
|
|
598
|
+
updated[:scopes] = scopes_with_refinements(env[:scopes], refinements)
|
|
599
|
+
updated
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def scopes_with_refinements(scopes, refinements)
|
|
603
|
+
return scopes if refinements.nil? || refinements.empty?
|
|
604
|
+
|
|
605
|
+
base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
|
|
606
|
+
merged_refinements = scopes.last.is_a?(FlowScope) ? scopes.last.each_with_object({}) { |(name, binding), result| result[name] = binding[:type] } : {}
|
|
607
|
+
merged_refinements = merge_refinements(merged_refinements, refinements)
|
|
608
|
+
flow_scope = FlowScope.new
|
|
609
|
+
|
|
610
|
+
merged_refinements.each do |name, refined_type|
|
|
611
|
+
binding = lookup_value(name, { scopes: base_scopes })
|
|
612
|
+
next unless binding
|
|
613
|
+
|
|
614
|
+
flow_scope[name] = binding.merge(type: refined_type)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
return base_scopes if flow_scope.empty?
|
|
618
|
+
|
|
619
|
+
base_scopes + [flow_scope]
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def merge_refinements(existing, incoming)
|
|
623
|
+
merged = existing.dup
|
|
624
|
+
incoming.each do |name, refined_type|
|
|
625
|
+
if merged.key?(name) && merged[name] != refined_type
|
|
626
|
+
merged.delete(name)
|
|
627
|
+
else
|
|
628
|
+
merged[name] = refined_type
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
merged
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def flow_refinements(expression, truthy:, env:)
|
|
636
|
+
case expression
|
|
637
|
+
when AST::UnaryOp
|
|
638
|
+
return flow_refinements(expression.operand, truthy: !truthy, env:) if expression.operator == "not"
|
|
639
|
+
when AST::BinaryOp
|
|
640
|
+
case expression.operator
|
|
641
|
+
when "and"
|
|
642
|
+
if truthy
|
|
643
|
+
left_truthy = flow_refinements(expression.left, truthy: true, env:)
|
|
644
|
+
right_env = env_with_refinements(env, left_truthy)
|
|
645
|
+
right_truthy = flow_refinements(expression.right, truthy: true, env: right_env)
|
|
646
|
+
return merge_refinements(left_truthy, right_truthy)
|
|
647
|
+
end
|
|
648
|
+
when "or"
|
|
649
|
+
unless truthy
|
|
650
|
+
left_falsy = flow_refinements(expression.left, truthy: false, env:)
|
|
651
|
+
right_env = env_with_refinements(env, left_falsy)
|
|
652
|
+
right_falsy = flow_refinements(expression.right, truthy: false, env: right_env)
|
|
653
|
+
return merge_refinements(left_falsy, right_falsy)
|
|
654
|
+
end
|
|
655
|
+
when "==", "!="
|
|
656
|
+
return null_test_refinements(expression, truthy:, env:)
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
{}
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def null_test_refinements(expression, truthy:, env:)
|
|
664
|
+
identifier_expression = nil
|
|
665
|
+
if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
|
|
666
|
+
identifier_expression = expression.left
|
|
667
|
+
elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
|
|
668
|
+
identifier_expression = expression.right
|
|
669
|
+
else
|
|
670
|
+
return {}
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
binding = lookup_value(identifier_expression.name, env)
|
|
674
|
+
return {} unless binding && binding[:storage_type].is_a?(Types::Nullable)
|
|
675
|
+
|
|
676
|
+
null_result = expression.operator == "==" ? truthy : !truthy
|
|
677
|
+
refined_type = null_result ? null_type : binding[:storage_type].base
|
|
678
|
+
{ identifier_expression.name => refined_type }
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def cfg_block_always_terminates?(statements)
|
|
682
|
+
ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def conditional_common_type(then_type, else_type)
|
|
686
|
+
return then_type if then_type == else_type
|
|
687
|
+
|
|
688
|
+
numeric_type = common_numeric_type(then_type, else_type)
|
|
689
|
+
return numeric_type if numeric_type
|
|
690
|
+
|
|
691
|
+
if (nullable_type = conditional_null_common_type(then_type, else_type))
|
|
692
|
+
return nullable_type
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
if (nullable_type = conditional_null_common_type(else_type, then_type))
|
|
696
|
+
return nullable_type
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
return then_type if then_type.is_a?(Types::Nullable) && else_type == then_type.base
|
|
700
|
+
return else_type if else_type.is_a?(Types::Nullable) && then_type == else_type.base
|
|
701
|
+
|
|
702
|
+
nil
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def if_expression_branch_compatible?(actual_type, expected_type)
|
|
706
|
+
return true if actual_type == expected_type
|
|
707
|
+
return true if null_assignable_to?(actual_type, expected_type)
|
|
708
|
+
return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base
|
|
709
|
+
return true if common_numeric_type(actual_type, expected_type) == expected_type
|
|
710
|
+
|
|
711
|
+
false
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def nullable_candidate?(type)
|
|
715
|
+
!ref_type?(type) && type != @ctx.types.fetch("void")
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def conditional_null_common_type(null_type, other_type)
|
|
719
|
+
return unless null_type.is_a?(Types::Null)
|
|
720
|
+
|
|
721
|
+
if other_type.is_a?(Types::Nullable)
|
|
722
|
+
return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
|
|
723
|
+
|
|
724
|
+
return nil
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
return unless nullable_candidate?(other_type)
|
|
728
|
+
return if null_type.target_type && null_type.target_type != other_type
|
|
729
|
+
|
|
730
|
+
Types::Registry.nullable(other_type)
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def null_type
|
|
734
|
+
@null_type ||= Types::Null.new
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def loop_flow(break_target:, continue_target:, break_defers: [], continue_defers: [])
|
|
738
|
+
{
|
|
739
|
+
break_target:,
|
|
740
|
+
continue_target:,
|
|
741
|
+
break_defers:,
|
|
742
|
+
continue_defers:,
|
|
743
|
+
}
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
def nested_loop_flow(current_loop_flow, local_defers)
|
|
747
|
+
return nil unless current_loop_flow
|
|
748
|
+
|
|
749
|
+
loop_flow(
|
|
750
|
+
break_target: current_loop_flow[:break_target],
|
|
751
|
+
continue_target: current_loop_flow[:continue_target],
|
|
752
|
+
break_defers: current_loop_flow[:break_defers] + local_defers,
|
|
753
|
+
continue_defers: current_loop_flow[:continue_defers] + local_defers,
|
|
754
|
+
)
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
def switch_loop_target(target)
|
|
758
|
+
return target unless target && target[:label]
|
|
759
|
+
|
|
760
|
+
loop_exit_label(target[:label])
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
def switch_loop_flow(current_loop_flow, local_defers)
|
|
764
|
+
nested = nested_loop_flow(current_loop_flow, local_defers)
|
|
765
|
+
return nil unless nested
|
|
766
|
+
|
|
767
|
+
loop_flow(
|
|
768
|
+
break_target: switch_loop_target(nested[:break_target]),
|
|
769
|
+
continue_target: switch_loop_target(nested[:continue_target]),
|
|
770
|
+
break_defers: nested[:break_defers],
|
|
771
|
+
continue_defers: nested[:continue_defers],
|
|
772
|
+
)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
def cleanup_statements(local_defers, outer_defers)
|
|
776
|
+
local_defers.reverse.flat_map(&:itself) + outer_defers.reverse.flat_map(&:itself)
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
def loop_exit_break(label = nil)
|
|
780
|
+
{ kind: :break, label: }
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def loop_exit_continue(label = nil)
|
|
784
|
+
{ kind: :continue, label: }
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def loop_exit_label(label)
|
|
788
|
+
{ kind: :label, label: }
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def loop_exit_statement(target, local_defers:, outer_defers:)
|
|
792
|
+
case target[:kind]
|
|
793
|
+
when :break
|
|
794
|
+
IR::BreakStmt.new
|
|
795
|
+
when :continue
|
|
796
|
+
IR::ContinueStmt.new
|
|
797
|
+
when :label
|
|
798
|
+
return IR::GotoStmt.new(label: target[:label]) if target[:label]
|
|
799
|
+
|
|
800
|
+
IR::GotoStmt.new(label: target[:label])
|
|
801
|
+
else
|
|
802
|
+
raise LoweringError, "unsupported loop exit target #{target.inspect}"
|
|
803
|
+
end
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def lower_loop_exit(target, local_defers, outer_defers)
|
|
807
|
+
cleanup = cleanup_statements(local_defers, outer_defers)
|
|
808
|
+
if cleanup.empty?
|
|
809
|
+
[loop_exit_statement(target, local_defers:, outer_defers:)]
|
|
810
|
+
else
|
|
811
|
+
label = target[:label]
|
|
812
|
+
raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
|
|
813
|
+
|
|
814
|
+
cleanup + [IR::GotoStmt.new(label:)]
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def lower_async_loop_exit(target, local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
|
|
819
|
+
cleanup = lower_async_cleanup_entries(local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
|
|
820
|
+
if cleanup.empty?
|
|
821
|
+
[loop_exit_statement(target, local_defers:, outer_defers:)]
|
|
822
|
+
else
|
|
823
|
+
label = target[:label]
|
|
824
|
+
raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
|
|
825
|
+
|
|
826
|
+
cleanup + [IR::GotoStmt.new(label:)]
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def contains_label_target?(statements, label)
|
|
831
|
+
statements.any? do |statement|
|
|
832
|
+
case statement
|
|
833
|
+
when IR::GotoStmt
|
|
834
|
+
statement.label == label
|
|
835
|
+
when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
|
|
836
|
+
contains_label_target?(statement.body, label)
|
|
837
|
+
when IR::IfStmt
|
|
838
|
+
contains_label_target?(statement.then_body, label) || (statement.else_body && contains_label_target?(statement.else_body, label))
|
|
839
|
+
when IR::SwitchStmt
|
|
840
|
+
statement.cases.any? { |switch_case| contains_label_target?(switch_case.body, label) }
|
|
841
|
+
else
|
|
842
|
+
false
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
def lower_defer_cleanup_expression(expression, env:)
|
|
848
|
+
prepared_setup, prepared_expression, prepared_cleanups = prepare_expression_with_cleanups(
|
|
849
|
+
expression,
|
|
850
|
+
env:,
|
|
851
|
+
expected_type: infer_expression_type(expression, env:),
|
|
852
|
+
allow_root_statement_foreign: true,
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
lowered = []
|
|
856
|
+
lowered.concat(prepared_setup)
|
|
857
|
+
if (foreign_call = foreign_call_info(prepared_expression, env))
|
|
858
|
+
setup, = lower_foreign_call_statement(
|
|
859
|
+
foreign_call,
|
|
860
|
+
env:,
|
|
861
|
+
expected_type: foreign_call[:binding].type.return_type,
|
|
862
|
+
statement_position: true,
|
|
863
|
+
discard_result: true,
|
|
864
|
+
)
|
|
865
|
+
lowered.concat(setup)
|
|
866
|
+
else
|
|
867
|
+
lowered << IR::ExpressionStmt.new(expression: lower_expression(prepared_expression, env:))
|
|
868
|
+
end
|
|
869
|
+
lowered.concat(prepared_cleanups.flat_map(&:itself))
|
|
870
|
+
lowered
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def lower_defer_cleanup_body(statements, env:, return_type:)
|
|
874
|
+
lower_block(statements, env:, active_defers: [], return_type:, loop_flow: nil, allow_return: false)
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def terminating_ir_statement?(statement)
|
|
878
|
+
statement.is_a?(IR::ReturnStmt) || statement.is_a?(IR::GotoStmt)
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def empty_env
|
|
882
|
+
{ scopes: [{}], counter: { value: 0 } }
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def snapshot_env(env)
|
|
886
|
+
{ scopes: env[:scopes].map(&:dup), counter: env[:counter] }
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def duplicate_env(env)
|
|
890
|
+
duplicated = env.dup
|
|
891
|
+
duplicated[:scopes] = env[:scopes].map(&:dup) + [{}]
|
|
892
|
+
duplicated[:counter] = env[:counter]
|
|
893
|
+
duplicated.delete(:prepared_expression_cleanups)
|
|
894
|
+
duplicated
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
def let_else_discard_binding_syntax?(statement)
|
|
898
|
+
statement.is_a?(AST::LocalDecl) && statement.else_body && statement.name == "_"
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
def bind_let_else_local?(statement)
|
|
902
|
+
!let_else_discard_binding_syntax?(statement)
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
def async_local_decl_field_key(statement)
|
|
906
|
+
return "__discard_#{statement.line}" if statement.name == "_"
|
|
907
|
+
|
|
908
|
+
statement.name
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
def async_local_decl_field_name(statement)
|
|
912
|
+
return "local_discard_#{statement.line}" if statement.name == "_"
|
|
913
|
+
|
|
914
|
+
"local_#{statement.name}"
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
def async_match_binding_field_key(arm)
|
|
918
|
+
@async_binding_counter ||= 0
|
|
919
|
+
@async_binding_counter += 1
|
|
920
|
+
"match_binding_#{@async_binding_counter}"
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
def async_match_binding_field_name(arm)
|
|
924
|
+
"local_match_binding_#{@async_binding_counter}"
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
def let_else_storage_c_name(statement, env)
|
|
928
|
+
return fresh_c_temp_name(env, "let_else_discard") if let_else_discard_binding_syntax?(statement)
|
|
929
|
+
|
|
930
|
+
return fresh_c_temp_name(env, "_") if statement.name == "_"
|
|
931
|
+
|
|
932
|
+
c_local_name(statement.name)
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def let_else_success_type(type)
|
|
936
|
+
return type.base if type.is_a?(Types::Nullable)
|
|
937
|
+
return type.arm("some").fetch("value") if option_let_else_type?(type)
|
|
938
|
+
return unless result_let_else_type?(type)
|
|
939
|
+
|
|
940
|
+
type.arm("success").fetch("value")
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
def let_else_error_type(type)
|
|
944
|
+
return unless result_let_else_type?(type)
|
|
945
|
+
|
|
946
|
+
type.arm("failure").fetch("error")
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def let_else_binding_projection(type)
|
|
950
|
+
return :result_success_value if result_let_else_type?(type)
|
|
951
|
+
return :option_some_value if option_let_else_type?(type)
|
|
952
|
+
|
|
953
|
+
nil
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def option_let_else_type?(type)
|
|
957
|
+
return false unless type.is_a?(Types::Variant)
|
|
958
|
+
|
|
959
|
+
some_fields = type.arm("some")
|
|
960
|
+
none_fields = type.arm("none")
|
|
961
|
+
some_fields && some_fields.length == 1 && some_fields.key?("value") &&
|
|
962
|
+
none_fields && none_fields.empty?
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
def result_let_else_type?(type)
|
|
966
|
+
return false unless type.is_a?(Types::Variant)
|
|
967
|
+
|
|
968
|
+
success_fields = type.arm("success")
|
|
969
|
+
failure_fields = type.arm("failure")
|
|
970
|
+
success_fields && success_fields.length == 1 && success_fields.key?("value") &&
|
|
971
|
+
failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
def let_else_failure_condition(storage_expr, storage_type)
|
|
975
|
+
if storage_type.is_a?(Types::Nullable)
|
|
976
|
+
return IR::Binary.new(
|
|
977
|
+
operator: "==",
|
|
978
|
+
left: storage_expr,
|
|
979
|
+
right: IR::NullLiteral.new(type: storage_type),
|
|
980
|
+
type: @ctx.types.fetch("bool"),
|
|
981
|
+
)
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
if result_let_else_type?(storage_type)
|
|
985
|
+
kind_type = @ctx.types.fetch("int")
|
|
986
|
+
return IR::Binary.new(
|
|
987
|
+
operator: "==",
|
|
988
|
+
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
989
|
+
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_failure", type: kind_type, pointer: false),
|
|
990
|
+
type: @ctx.types.fetch("bool"),
|
|
991
|
+
)
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
if option_let_else_type?(storage_type)
|
|
995
|
+
kind_type = @ctx.types.fetch("int")
|
|
996
|
+
return IR::Binary.new(
|
|
997
|
+
operator: "==",
|
|
998
|
+
left: IR::Member.new(receiver: storage_expr, member: "kind", type: kind_type),
|
|
999
|
+
right: IR::Name.new(name: "#{c_type_name(storage_type)}_kind_none", type: kind_type, pointer: false),
|
|
1000
|
+
type: @ctx.types.fetch("bool"),
|
|
1001
|
+
)
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
raise LoweringError, "unsupported let-else storage type #{storage_type}"
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
def lower_bound_identifier(binding, expected_type: nil)
|
|
1008
|
+
storage_type = binding[:storage_type]
|
|
1009
|
+
visible_type = binding[:type]
|
|
1010
|
+
projection = binding[:projection]
|
|
1011
|
+
|
|
1012
|
+
if projection == :result_success_value
|
|
1013
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1014
|
+
return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
if projection == :result_failure_error
|
|
1018
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1019
|
+
return variant_binding_projection_expression(local_ref, storage_type, "failure", "error", visible_type)
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
if projection == :option_some_value
|
|
1023
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1024
|
+
return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
return IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer]) if visible_type == storage_type
|
|
1028
|
+
if storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type
|
|
1029
|
+
name = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1030
|
+
return name if pointer_like_type?(storage_type.base)
|
|
1031
|
+
return name if expected_type == storage_type
|
|
1032
|
+
return IR::Member.new(receiver: name, member: "value", type: visible_type)
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
if result_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
|
|
1036
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1037
|
+
return variant_binding_projection_expression(local_ref, storage_type, "success", "value", visible_type)
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
if option_let_else_type?(storage_type) && let_else_success_type(storage_type) == visible_type
|
|
1041
|
+
local_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1042
|
+
return variant_binding_projection_expression(local_ref, storage_type, "some", "value", visible_type)
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def variant_binding_projection_expression(storage_expr, storage_type, arm_name, field_name, field_type)
|
|
1049
|
+
payload_type = Types::VariantArmPayload.new(storage_type, arm_name, storage_type.arm(arm_name))
|
|
1050
|
+
data_expr = IR::Member.new(receiver: storage_expr, member: "data", type: nil)
|
|
1051
|
+
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
1052
|
+
IR::Member.new(receiver: arm_expr, member: field_name, type: field_type)
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
def infer_result_propagation_type(expression, env:)
|
|
1056
|
+
_storage_type, success_type, = infer_result_propagation_types(expression, env:)
|
|
1057
|
+
|
|
1058
|
+
success_type
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
def infer_result_propagation_types(expression, env:, allow_void_success: false)
|
|
1062
|
+
storage_type = infer_expression_type(expression.operand, env:)
|
|
1063
|
+
if result_let_else_type?(storage_type)
|
|
1064
|
+
infer_result_propagation_details(storage_type, env:, allow_void_success:)
|
|
1065
|
+
elsif option_let_else_type?(storage_type)
|
|
1066
|
+
infer_option_propagation_details(storage_type, env:, allow_void_success:)
|
|
1067
|
+
else
|
|
1068
|
+
raise LoweringError, "propagation expects Result[T, E] or Option[T], got #{storage_type}"
|
|
1069
|
+
end
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
def infer_result_propagation_details(storage_type, env:, allow_void_success:)
|
|
1073
|
+
success_type = let_else_success_type(storage_type)
|
|
1074
|
+
error_type = let_else_error_type(storage_type)
|
|
1075
|
+
raise LoweringError, "propagation requires a non-void Result success type" if success_type == @ctx.types.fetch("void") && !allow_void_success
|
|
1076
|
+
|
|
1077
|
+
context = env[:return_context]
|
|
1078
|
+
raise LoweringError, "propagation is only allowed inside function and proc bodies" unless context
|
|
1079
|
+
raise LoweringError, "propagation is not allowed inside defer blocks" unless context[:allow_return]
|
|
1080
|
+
|
|
1081
|
+
return_type = context[:return_type]
|
|
1082
|
+
unless result_let_else_type?(return_type)
|
|
1083
|
+
raise LoweringError, "propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}"
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
return_error_type = let_else_error_type(return_type)
|
|
1087
|
+
unless return_error_type == error_type
|
|
1088
|
+
raise LoweringError, "propagation error type #{error_type} must match enclosing Result error type #{return_error_type}"
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
[storage_type, success_type, return_type, error_type]
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
def infer_option_propagation_details(storage_type, env:, allow_void_success:)
|
|
1095
|
+
success_type = let_else_success_type(storage_type)
|
|
1096
|
+
raise LoweringError, "propagation requires a non-void Option success type" if success_type == @ctx.types.fetch("void") && !allow_void_success
|
|
1097
|
+
|
|
1098
|
+
context = env[:return_context]
|
|
1099
|
+
raise LoweringError, "propagation is only allowed inside function and proc bodies" unless context
|
|
1100
|
+
raise LoweringError, "propagation is not allowed inside defer blocks" unless context[:allow_return]
|
|
1101
|
+
|
|
1102
|
+
return_type = context[:return_type]
|
|
1103
|
+
unless option_let_else_type?(return_type)
|
|
1104
|
+
raise LoweringError, "propagation requires enclosing function/proc to return Option[_], got #{return_type}"
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
[storage_type, success_type, return_type, nil]
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1110
|
+
def prepare_result_propagation_for_inline_lowering(expression, env:, allow_void_success: false)
|
|
1111
|
+
storage_type, success_type, return_type, error_type = infer_result_propagation_types(expression, env:, allow_void_success:)
|
|
1112
|
+
is_option = option_let_else_type?(storage_type)
|
|
1113
|
+
|
|
1114
|
+
env[:prepared_expression_cleanups] ||= []
|
|
1115
|
+
cleanup_start = env[:prepared_expression_cleanups].length
|
|
1116
|
+
operand_setup, operand = prepare_expression_for_inline_lowering(expression.operand, env:, expected_type: storage_type)
|
|
1117
|
+
operand_cleanups = env[:prepared_expression_cleanups].drop(cleanup_start)
|
|
1118
|
+
|
|
1119
|
+
result_name = fresh_c_temp_name(env, "propagate")
|
|
1120
|
+
result_ref = IR::Name.new(name: result_name, type: storage_type, pointer: false)
|
|
1121
|
+
return_context = env.fetch(:return_context)
|
|
1122
|
+
failure_return = if storage_type == return_type
|
|
1123
|
+
result_ref
|
|
1124
|
+
elsif is_option
|
|
1125
|
+
IR::VariantLiteral.new(
|
|
1126
|
+
type: return_type,
|
|
1127
|
+
arm_name: "none",
|
|
1128
|
+
fields: [],
|
|
1129
|
+
)
|
|
1130
|
+
else
|
|
1131
|
+
IR::VariantLiteral.new(
|
|
1132
|
+
type: return_type,
|
|
1133
|
+
arm_name: "failure",
|
|
1134
|
+
fields: [
|
|
1135
|
+
IR::AggregateField.new(
|
|
1136
|
+
name: "error",
|
|
1137
|
+
value: variant_binding_projection_expression(result_ref, storage_type, "failure", "error", error_type),
|
|
1138
|
+
),
|
|
1139
|
+
],
|
|
1140
|
+
)
|
|
1141
|
+
end
|
|
1142
|
+
failure_cleanup = operand_cleanups.flat_map(&:itself)
|
|
1143
|
+
failure_terminator = if return_context[:async_info]
|
|
1144
|
+
failure_cleanup +
|
|
1145
|
+
lower_async_cleanup_entries(
|
|
1146
|
+
return_context[:local_defers],
|
|
1147
|
+
return_context[:active_defers],
|
|
1148
|
+
frame_expr: return_context.fetch(:frame_expr),
|
|
1149
|
+
raw_frame_expr: return_context.fetch(:raw_frame_expr),
|
|
1150
|
+
async_info: return_context.fetch(:async_info),
|
|
1151
|
+
) +
|
|
1152
|
+
async_complete_statements(
|
|
1153
|
+
frame_expr: return_context.fetch(:frame_expr),
|
|
1154
|
+
raw_frame_expr: return_context.fetch(:raw_frame_expr),
|
|
1155
|
+
async_info: return_context.fetch(:async_info),
|
|
1156
|
+
value: failure_return,
|
|
1157
|
+
)
|
|
1158
|
+
else
|
|
1159
|
+
failure_cleanup +
|
|
1160
|
+
cleanup_statements(return_context[:local_defers], return_context[:active_defers]) +
|
|
1161
|
+
[IR::ReturnStmt.new(value: failure_return, source_path: @ctx.current_analysis_path)]
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
if success_type == @ctx.types.fetch("void")
|
|
1165
|
+
return [
|
|
1166
|
+
operand_setup + [
|
|
1167
|
+
IR::LocalDecl.new(
|
|
1168
|
+
name: result_name,
|
|
1169
|
+
linkage_name: result_name,
|
|
1170
|
+
type: storage_type,
|
|
1171
|
+
value: lower_contextual_expression(operand, env:, expected_type: storage_type),
|
|
1172
|
+
),
|
|
1173
|
+
IR::IfStmt.new(
|
|
1174
|
+
condition: let_else_failure_condition(result_ref, storage_type),
|
|
1175
|
+
then_body: failure_terminator,
|
|
1176
|
+
else_body: nil,
|
|
1177
|
+
),
|
|
1178
|
+
],
|
|
1179
|
+
nil,
|
|
1180
|
+
]
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
projection = is_option ? :option_some_value : :result_success_value
|
|
1184
|
+
register_prepared_temp!(env, result_name, success_type, storage_type:, projection:)
|
|
1185
|
+
|
|
1186
|
+
[
|
|
1187
|
+
operand_setup + [
|
|
1188
|
+
IR::LocalDecl.new(
|
|
1189
|
+
name: result_name,
|
|
1190
|
+
linkage_name: result_name,
|
|
1191
|
+
type: storage_type,
|
|
1192
|
+
value: lower_contextual_expression(operand, env:, expected_type: storage_type),
|
|
1193
|
+
),
|
|
1194
|
+
IR::IfStmt.new(
|
|
1195
|
+
condition: let_else_failure_condition(result_ref, storage_type),
|
|
1196
|
+
then_body: failure_terminator,
|
|
1197
|
+
else_body: nil,
|
|
1198
|
+
),
|
|
1199
|
+
],
|
|
1200
|
+
AST::Identifier.new(name: result_name),
|
|
1201
|
+
]
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
def c_type_name(type)
|
|
1205
|
+
if type.is_a?(Types::Nullable)
|
|
1206
|
+
return "nullable_#{c_type_name(type.base)}"
|
|
1207
|
+
end
|
|
1208
|
+
|
|
1209
|
+
if type.respond_to?(:linkage_name) && type.linkage_name
|
|
1210
|
+
return type.linkage_name
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
if type.is_a?(Types::GenericInstance)
|
|
1214
|
+
base = if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
|
|
1215
|
+
type.name
|
|
1216
|
+
elsif type.respond_to?(:module_name) && !type.module_name.nil?
|
|
1217
|
+
"#{module_c_prefix(type.module_name)}_#{type.name}"
|
|
1218
|
+
else
|
|
1219
|
+
type.name
|
|
1220
|
+
end
|
|
1221
|
+
|
|
1222
|
+
return "#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
return type.name if type.respond_to?(:module_name) && type.module_name&.start_with?("std.c.")
|
|
1226
|
+
|
|
1227
|
+
base = (type.respond_to?(:module_name) && type.module_name) ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
|
|
1228
|
+
return base unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
|
|
1229
|
+
|
|
1230
|
+
"#{base}_#{sanitize_identifier(type.arguments.join('_'))}"
|
|
1231
|
+
end
|
|
1232
|
+
|
|
1233
|
+
def opaque_c_type_name(type)
|
|
1234
|
+
type.linkage_name || c_type_name(type)
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
def opaque_forward_declarable?(type)
|
|
1238
|
+
return false unless opaque_c_type_name(type).match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
1239
|
+
|
|
1240
|
+
!type.external || type.linkage_name.nil?
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
def forward_declarable_external_opaque?(type)
|
|
1244
|
+
type.external && opaque_forward_declarable?(type)
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
def validate_generic_type!(name, arguments)
|
|
1248
|
+
super(name, arguments) { |msg| raise LoweringError, msg }
|
|
1249
|
+
end
|
|
1250
|
+
|
|
1251
|
+
def integer_type_argument?(argument)
|
|
1252
|
+
argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
|
|
1253
|
+
end
|
|
1254
|
+
|
|
1255
|
+
def generic_integer_type_argument?(argument)
|
|
1256
|
+
integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
def enum_member_c_name(type, member_name)
|
|
1260
|
+
"#{c_type_name(type)}_#{member_name}"
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
def function_binding_c_name(binding, module_name:, receiver_type: nil)
|
|
1264
|
+
if receiver_type.nil? && binding.name == "main" && binding.type_arguments.empty?
|
|
1265
|
+
return binding.async ? module_function_c_name(module_name, "__async_main") : module_function_c_name(module_name, "main")
|
|
1266
|
+
end
|
|
1267
|
+
if receiver_type
|
|
1268
|
+
base = "#{c_type_name(receiver_type)}_#{binding.name}"
|
|
1269
|
+
base = "#{base}_static" if binding.type.receiver_type.nil?
|
|
1270
|
+
return binding.type_arguments.empty? ? base : "#{base}__#{generic_type_argument_suffix(binding.type_arguments)}"
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
module_function_c_name(module_name, binding.name, type_arguments: binding.type_arguments)
|
|
1274
|
+
end
|
|
1275
|
+
|
|
1276
|
+
def external_function_c_name(binding)
|
|
1277
|
+
return binding.ast.mapping.value if binding.external && binding.ast.is_a?(AST::ExternFunctionDecl) && binding.ast.mapping
|
|
1278
|
+
|
|
1279
|
+
binding.name
|
|
1280
|
+
end
|
|
1281
|
+
|
|
1282
|
+
def value_c_name(name)
|
|
1283
|
+
module_value_c_name(@ctx.module_name, name)
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1286
|
+
def imported_value_c_name(imported_module, name)
|
|
1287
|
+
imported_analysis = analysis_for_module(imported_module.name)
|
|
1288
|
+
return name if imported_analysis.module_kind == :raw_module
|
|
1289
|
+
|
|
1290
|
+
module_value_c_name(imported_module.name, name)
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
def module_function_c_name(module_name, name, type_arguments: [])
|
|
1294
|
+
base = "#{module_c_prefix(module_name)}_#{name}"
|
|
1295
|
+
return base if type_arguments.empty?
|
|
1296
|
+
|
|
1297
|
+
# A double-underscore separates a generic function instance's type
|
|
1298
|
+
# arguments so it cannot collide with a distinct regular function whose
|
|
1299
|
+
# name happens to be `<name>_<typearg>` (e.g. the instance
|
|
1300
|
+
# `expect_equal[str]` vs the function `expect_equal_str`). The method path
|
|
1301
|
+
# in `function_binding_c_name` uses the same scheme for consistency.
|
|
1302
|
+
"#{base}__#{generic_type_argument_suffix(type_arguments)}"
|
|
1303
|
+
end
|
|
1304
|
+
|
|
1305
|
+
# Joins resolved generic type arguments into the instance-name suffix used
|
|
1306
|
+
# by both the free-function (`module_function_c_name`) and method
|
|
1307
|
+
# (`function_binding_c_name`) paths, keeping the scheme consistent.
|
|
1308
|
+
def generic_type_argument_suffix(type_arguments)
|
|
1309
|
+
sanitize_identifier(type_arguments.join('_'))
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
def module_value_c_name(module_name, name)
|
|
1313
|
+
"#{module_c_prefix(module_name)}_#{name}"
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
def module_c_prefix(module_name)
|
|
1317
|
+
sanitize_identifier(module_name.to_s.tr('.', '_'))
|
|
1318
|
+
end
|
|
1319
|
+
|
|
1320
|
+
def c_local_name(name)
|
|
1321
|
+
return "value" unless name
|
|
1322
|
+
identifier = sanitize_identifier(name)
|
|
1323
|
+
return "#{identifier}_" if c_reserved_identifier?(identifier)
|
|
1324
|
+
|
|
1325
|
+
identifier
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
def c_reserved_identifier?(identifier)
|
|
1329
|
+
%w[
|
|
1330
|
+
auto break case char const continue default do double else enum extern
|
|
1331
|
+
float for goto if inline int long register restrict return short signed
|
|
1332
|
+
sizeof static struct switch typedef union unsigned void volatile while
|
|
1333
|
+
_Alignas _Alignof _Atomic _Bool _Complex _Generic _Imaginary _Noreturn
|
|
1334
|
+
_Static_assert _Thread_local
|
|
1335
|
+
].include?(identifier)
|
|
1336
|
+
end
|
|
1337
|
+
|
|
1338
|
+
def fresh_c_temp_name(env, prefix)
|
|
1339
|
+
env[:counter][:value] += 1
|
|
1340
|
+
"__mt_#{prefix}_#{env[:counter][:value]}"
|
|
1341
|
+
end
|
|
1342
|
+
|
|
1343
|
+
def cleanup_safe_return_expression?(expression)
|
|
1344
|
+
case expression
|
|
1345
|
+
when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral
|
|
1346
|
+
true
|
|
1347
|
+
else
|
|
1348
|
+
false
|
|
1349
|
+
end
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
def sanitize_identifier(text)
|
|
1353
|
+
return "value" unless text
|
|
1354
|
+
|
|
1355
|
+
cache = (@sanitize_identifier_cache ||= {})
|
|
1356
|
+
cached = cache[text]
|
|
1357
|
+
return cached if cached
|
|
1358
|
+
|
|
1359
|
+
identifier = text.gsub(/[^A-Za-z0-9_]+/, "_").gsub(/_+/, "_").sub(/_+$/, "").sub(/^_{2,}/, "_")
|
|
1360
|
+
cache[text] = identifier.empty? ? "value" : identifier
|
|
1361
|
+
end
|
|
1362
|
+
|
|
1363
|
+
def lower_assignment_target(expression, env:)
|
|
1364
|
+
case expression
|
|
1365
|
+
when AST::Identifier
|
|
1366
|
+
binding = lookup_value(expression.name, env)
|
|
1367
|
+
lower_assignment_binding_target(binding)
|
|
1368
|
+
when AST::MemberAccess
|
|
1369
|
+
if expression.receiver.is_a?(AST::IndexAccess)
|
|
1370
|
+
base_type = infer_expression_type(expression.receiver.receiver, env:)
|
|
1371
|
+
if base_type.is_a?(Types::SoA)
|
|
1372
|
+
soa_base = lower_expression(expression.receiver.receiver, env:)
|
|
1373
|
+
index = lower_expression(expression.receiver.index, env:)
|
|
1374
|
+
field_type = base_type.fields[expression.member]
|
|
1375
|
+
target_type = infer_expression_type(expression, env:)
|
|
1376
|
+
return IR::Index.new(
|
|
1377
|
+
receiver: IR::Member.new(receiver: soa_base, member: expression.member, type: field_type),
|
|
1378
|
+
index:,
|
|
1379
|
+
type: target_type,
|
|
1380
|
+
)
|
|
1381
|
+
end
|
|
1382
|
+
end
|
|
1383
|
+
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1384
|
+
receiver = lower_expression(expression.receiver, env:)
|
|
1385
|
+
type = infer_expression_type(expression, env:)
|
|
1386
|
+
IR::Member.new(receiver:, member: member_c_name(receiver_type, expression.member), type:)
|
|
1387
|
+
when AST::IndexAccess
|
|
1388
|
+
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1389
|
+
receiver = lower_expression(expression.receiver, env:)
|
|
1390
|
+
index = lower_expression(expression.index, env:)
|
|
1391
|
+
type = infer_expression_type(expression, env:)
|
|
1392
|
+
if array_type?(receiver_type)
|
|
1393
|
+
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1394
|
+
elsif receiver_type.is_a?(Types::Span)
|
|
1395
|
+
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1396
|
+
else
|
|
1397
|
+
IR::Index.new(receiver:, index:, type:)
|
|
1398
|
+
end
|
|
1399
|
+
when AST::Call
|
|
1400
|
+
if read_call?(expression)
|
|
1401
|
+
type = infer_expression_type(expression, env:)
|
|
1402
|
+
operand = lower_expression(expression.arguments.first.value, env:)
|
|
1403
|
+
return IR::Unary.new(operator: "*", operand:, type:)
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1406
|
+
raise LoweringError, "unsupported assignment target #{expression.class.name}"
|
|
1407
|
+
else
|
|
1408
|
+
raise LoweringError, "unsupported assignment target #{expression.class.name}"
|
|
1409
|
+
end
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
def lower_assignment_binding_target(binding)
|
|
1413
|
+
storage_type = binding[:storage_type]
|
|
1414
|
+
visible_type = binding[:type]
|
|
1415
|
+
storage_ref = IR::Name.new(name: binding[:linkage_name], type: storage_type, pointer: binding[:pointer])
|
|
1416
|
+
|
|
1417
|
+
case binding[:projection]
|
|
1418
|
+
when :result_success_value
|
|
1419
|
+
variant_binding_projection_expression(storage_ref, storage_type, "success", "value", visible_type)
|
|
1420
|
+
when :option_some_value
|
|
1421
|
+
variant_binding_projection_expression(storage_ref, storage_type, "some", "value", visible_type)
|
|
1422
|
+
else
|
|
1423
|
+
if visible_type == storage_type || (storage_type.is_a?(Types::Nullable) && storage_type.base == visible_type)
|
|
1424
|
+
IR::Name.new(name: binding[:linkage_name], type: visible_type, pointer: binding[:pointer])
|
|
1425
|
+
else
|
|
1426
|
+
storage_ref
|
|
1427
|
+
end
|
|
1428
|
+
end
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
def wrap_nullable_field_value(field_type, lowered_value, env)
|
|
1432
|
+
return lowered_value unless field_type.is_a?(Types::Nullable)
|
|
1433
|
+
return lowered_value if pointer_like_type?(field_type.base)
|
|
1434
|
+
return lowered_value if lowered_value.type.is_a?(Types::Nullable)
|
|
1435
|
+
|
|
1436
|
+
nullable_some_literal(field_type, lowered_value)
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
def nullable_some_literal(nullable_type, value)
|
|
1440
|
+
IR::AggregateLiteral.new(
|
|
1441
|
+
type: nullable_type,
|
|
1442
|
+
fields: [
|
|
1443
|
+
IR::AggregateField.new(name: "has_value", value: IR::BooleanLiteral.new(value: true, type: @ctx.types.fetch("bool"))),
|
|
1444
|
+
IR::AggregateField.new(name: "value", value:),
|
|
1445
|
+
],
|
|
1446
|
+
)
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
def pointer_like_type?(type)
|
|
1450
|
+
pointer_type?(type) || own_type?(type) || (type.is_a?(Types::Primitive) && type.name == "cstr") || type.is_a?(Types::Function) || type.is_a?(Types::Proc) || type.is_a?(Types::Opaque)
|
|
1451
|
+
end
|
|
1452
|
+
end
|
|
1453
|
+
end
|