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,1308 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class SemanticAnalyzer
|
|
5
|
+
class Checker
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def check_block(statements, scopes:, return_type:, allow_return: true)
|
|
9
|
+
statements ||= []
|
|
10
|
+
with_return_context(return_type, allow_return:) do
|
|
11
|
+
with_nested_scope(scopes) do |nested_scopes|
|
|
12
|
+
with_type_resolution_scopes(nested_scopes) do
|
|
13
|
+
statements.each_with_index do |statement, idx|
|
|
14
|
+
begin
|
|
15
|
+
record_local_completion_snapshot(
|
|
16
|
+
statement.respond_to?(:line) ? statement.line : nil,
|
|
17
|
+
statement.respond_to?(:column) ? statement.column : 0,
|
|
18
|
+
nested_scopes,
|
|
19
|
+
)
|
|
20
|
+
refinements = check_statement(statement, scopes: nested_scopes, return_type:, allow_return:)
|
|
21
|
+
apply_continuation_refinements!(nested_scopes, refinements)
|
|
22
|
+
if @nullability_flow_result && idx + 1 < statements.length
|
|
23
|
+
apply_nullability_continuation_refinements!(nested_scopes, statements[idx + 1])
|
|
24
|
+
end
|
|
25
|
+
record_local_completion_snapshot(statement_end_line(statement), 1_000_000, nested_scopes)
|
|
26
|
+
rescue SemanticError => e
|
|
27
|
+
if @collecting_errors
|
|
28
|
+
@structural_errors << e
|
|
29
|
+
next
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
raise e unless e.line.nil?
|
|
33
|
+
|
|
34
|
+
stmt_line = statement.respond_to?(:line) ? statement.line : nil
|
|
35
|
+
raise e if stmt_line.nil?
|
|
36
|
+
|
|
37
|
+
raise_sema_error(e.message, statement)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cfg_block_always_terminates?(statements)
|
|
46
|
+
ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false })
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def check_statement(statement, scopes:, return_type:, allow_return: true)
|
|
50
|
+
with_error_node(statement) do
|
|
51
|
+
case statement
|
|
52
|
+
when AST::ErrorBlockStmt
|
|
53
|
+
if statement.header_type == :unsafe
|
|
54
|
+
@unsafe_statement_lines << statement.line
|
|
55
|
+
begin
|
|
56
|
+
with_unsafe do
|
|
57
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
58
|
+
end
|
|
59
|
+
ensure
|
|
60
|
+
@unsafe_statement_lines.pop
|
|
61
|
+
end
|
|
62
|
+
elsif statement.header_type == :if && statement.header_expression
|
|
63
|
+
validate_consuming_foreign_expression!(statement.header_expression, scopes:, root_allowed: false)
|
|
64
|
+
condition_type = infer_expression(statement.header_expression, scopes:, expected_type: @ctx.types.fetch("bool"))
|
|
65
|
+
ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if condition must be bool, got #{condition_type}", expression: statement.header_expression, line: statement.line, column: statement.column)
|
|
66
|
+
true_refinements = flow_refinements(statement.header_expression, truthy: true, scopes:)
|
|
67
|
+
check_block(statement.body, scopes: scopes_with_refinements(scopes, true_refinements), return_type:, allow_return:)
|
|
68
|
+
return flow_refinements(statement.header_expression, truthy: false, scopes:) if cfg_block_always_terminates?(statement.body)
|
|
69
|
+
elsif statement.header_type == :while && statement.header_expression
|
|
70
|
+
validate_consuming_foreign_expression!(statement.header_expression, scopes:, root_allowed: false)
|
|
71
|
+
condition_type = infer_expression(statement.header_expression, scopes:, expected_type: @ctx.types.fetch("bool"))
|
|
72
|
+
ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "while condition must be bool, got #{condition_type}", expression: statement.header_expression, line: statement.line, column: statement.column)
|
|
73
|
+
with_loop do
|
|
74
|
+
body_scopes = scopes_with_refinements(scopes, flow_refinements(statement.header_expression, truthy: true, scopes:))
|
|
75
|
+
check_block(statement.body, scopes: body_scopes, return_type:, allow_return:)
|
|
76
|
+
end
|
|
77
|
+
elsif statement.header_type == :for
|
|
78
|
+
if statement.header_bindings && statement.header_iterables
|
|
79
|
+
check_for_stmt(recovered_for_statement(statement), scopes:, return_type:, allow_return:)
|
|
80
|
+
else
|
|
81
|
+
with_loop do
|
|
82
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
87
|
+
end
|
|
88
|
+
when AST::LocalDecl
|
|
89
|
+
check_local_decl(statement, scopes:, return_type:, allow_return:)
|
|
90
|
+
when AST::ErrorStmt
|
|
91
|
+
nil
|
|
92
|
+
when AST::Assignment
|
|
93
|
+
check_assignment(statement, scopes:)
|
|
94
|
+
when AST::IfStmt
|
|
95
|
+
if statement.inline
|
|
96
|
+
check_inline_if_stmt(statement, scopes:, return_type:, allow_return:)
|
|
97
|
+
next
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
false_refinements = {}
|
|
101
|
+
branch_bodies_terminate = []
|
|
102
|
+
statement.branches.each do |branch|
|
|
103
|
+
branch_scopes = scopes_with_refinements(scopes, false_refinements)
|
|
104
|
+
if branch.condition.is_a?(AST::ErrorExpr)
|
|
105
|
+
check_block(branch.body, scopes: branch_scopes, return_type:, allow_return:)
|
|
106
|
+
branch_bodies_terminate << cfg_block_always_terminates?(branch.body)
|
|
107
|
+
next
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
validate_consuming_foreign_expression!(branch.condition, scopes: branch_scopes, root_allowed: false)
|
|
111
|
+
condition_type = infer_expression(branch.condition, scopes: branch_scopes, expected_type: @ctx.types.fetch("bool"))
|
|
112
|
+
ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if condition must be bool, got #{condition_type}", expression: branch.condition, line: branch.line, column: branch.column)
|
|
113
|
+
true_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: true, scopes: branch_scopes))
|
|
114
|
+
check_block(branch.body, scopes: scopes_with_refinements(scopes, true_refinements), return_type:, allow_return:)
|
|
115
|
+
branch_bodies_terminate << cfg_block_always_terminates?(branch.body)
|
|
116
|
+
false_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: false, scopes: branch_scopes))
|
|
117
|
+
end
|
|
118
|
+
check_block(statement.else_body, scopes: scopes_with_refinements(scopes, false_refinements), return_type:, allow_return:) if statement.else_body
|
|
119
|
+
return false_refinements if statement.else_body.nil? && branch_bodies_terminate.all?
|
|
120
|
+
when AST::MatchStmt
|
|
121
|
+
if statement.inline
|
|
122
|
+
check_inline_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
123
|
+
else
|
|
124
|
+
check_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
125
|
+
end
|
|
126
|
+
when AST::UnsafeStmt
|
|
127
|
+
@unsafe_statement_lines << statement.line
|
|
128
|
+
begin
|
|
129
|
+
with_unsafe do
|
|
130
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
131
|
+
end
|
|
132
|
+
ensure
|
|
133
|
+
@unsafe_statement_lines.pop
|
|
134
|
+
end
|
|
135
|
+
when AST::StaticAssert
|
|
136
|
+
check_static_assert(statement, scopes:)
|
|
137
|
+
when AST::EmitStmt
|
|
138
|
+
check_emit_stmt(statement)
|
|
139
|
+
when AST::ForStmt
|
|
140
|
+
if statement.inline
|
|
141
|
+
check_inline_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
142
|
+
else
|
|
143
|
+
check_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
144
|
+
end
|
|
145
|
+
when AST::ParallelBlockStmt
|
|
146
|
+
check_parallel_block_stmt(statement, scopes:, return_type:)
|
|
147
|
+
when AST::GatherStmt
|
|
148
|
+
check_gather_stmt(statement, scopes:)
|
|
149
|
+
when AST::WhileStmt
|
|
150
|
+
if statement.inline
|
|
151
|
+
check_inline_while_stmt(statement, scopes:, return_type:, allow_return:)
|
|
152
|
+
elsif statement.condition.is_a?(AST::ErrorExpr)
|
|
153
|
+
with_loop do
|
|
154
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
155
|
+
end
|
|
156
|
+
else
|
|
157
|
+
validate_consuming_foreign_expression!(statement.condition, scopes:, root_allowed: false)
|
|
158
|
+
condition_type = infer_expression(statement.condition, scopes:, expected_type: @ctx.types.fetch("bool"))
|
|
159
|
+
ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "while condition must be bool, got #{condition_type}", expression: statement.condition, line: statement.line, column: statement.column)
|
|
160
|
+
with_loop do
|
|
161
|
+
body_scopes = scopes_with_refinements(scopes, flow_refinements(statement.condition, truthy: true, scopes:))
|
|
162
|
+
check_block(statement.body, scopes: body_scopes, return_type:, allow_return:)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
when AST::PassStmt
|
|
166
|
+
nil
|
|
167
|
+
when AST::BreakStmt
|
|
168
|
+
raise_sema_error("break must be inside a loop") unless inside_loop?
|
|
169
|
+
when AST::ContinueStmt
|
|
170
|
+
raise_sema_error("continue must be inside a loop") unless inside_loop?
|
|
171
|
+
when AST::ReturnStmt
|
|
172
|
+
raise_sema_error("return is not allowed inside defer blocks") unless allow_return
|
|
173
|
+
|
|
174
|
+
validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false) if statement.value
|
|
175
|
+
value_type = statement.value ? infer_expression(statement.value, scopes:, expected_type: return_type) : @ctx.types.fetch("void")
|
|
176
|
+
ensure_assignable!(
|
|
177
|
+
value_type,
|
|
178
|
+
return_type,
|
|
179
|
+
"return type mismatch: expected #{return_type}, got #{value_type}",
|
|
180
|
+
expression: statement.value,
|
|
181
|
+
contextual_int_to_float: contextual_int_to_float_target?(return_type),
|
|
182
|
+
line: statement.line,
|
|
183
|
+
)
|
|
184
|
+
when AST::DeferStmt
|
|
185
|
+
if statement.body
|
|
186
|
+
with_loop_barrier do
|
|
187
|
+
check_block(statement.body, scopes:, return_type:, allow_return: false)
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: true)
|
|
191
|
+
validate_hoistable_foreign_expression!(statement.expression, scopes:, root_hoistable: false)
|
|
192
|
+
infer_expression(statement.expression, scopes:)
|
|
193
|
+
end
|
|
194
|
+
when AST::ExpressionStmt
|
|
195
|
+
validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: true)
|
|
196
|
+
if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?"
|
|
197
|
+
infer_propagate_expression(statement.expression.operand, scopes:, allow_void_success: true)
|
|
198
|
+
else
|
|
199
|
+
infer_expression(statement.expression, scopes:)
|
|
200
|
+
end
|
|
201
|
+
return consuming_foreign_call_refinements(statement.expression, scopes:)
|
|
202
|
+
when AST::WhenStmt
|
|
203
|
+
check_when_stmt(statement, scopes:, return_type:, allow_return:)
|
|
204
|
+
else
|
|
205
|
+
raise_sema_error("unsupported statement #{statement.class.name}")
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
nil
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def check_local_decl(statement, scopes:, return_type:, allow_return:)
|
|
213
|
+
if statement.destructure_bindings
|
|
214
|
+
check_local_decl_destructure(statement, scopes:, return_type:, allow_return:)
|
|
215
|
+
return
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
current_scope = current_actual_scope(scopes)
|
|
219
|
+
discard_binding = statement.name == "_" || let_else_discard_binding_syntax?(statement)
|
|
220
|
+
raise_sema_error("duplicate local #{statement.name}") if !discard_binding && current_scope.key?(statement.name)
|
|
221
|
+
ensure_non_reserved_primitive_name!(statement.name, kind_label: "local", line: statement.line, column: statement.column) unless discard_binding
|
|
222
|
+
|
|
223
|
+
declared_type = statement.type ? resolve_type_ref(statement.type) : nil
|
|
224
|
+
if statement.value
|
|
225
|
+
validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false)
|
|
226
|
+
inferred_type = infer_expression(statement.value, scopes:, expected_type: declared_type)
|
|
227
|
+
else
|
|
228
|
+
raise_sema_error("local #{statement.name} without initializer requires an explicit type") unless declared_type
|
|
229
|
+
|
|
230
|
+
begin
|
|
231
|
+
zero_initializable_type?(declared_type)
|
|
232
|
+
rescue SemanticError
|
|
233
|
+
raise_sema_error("local #{statement.name} without initializer requires a zero-initializable type, got #{declared_type}")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
inferred_type = declared_type
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
storage_type, final_type, const_value =
|
|
240
|
+
if statement.else_body || statement.recovered_else
|
|
241
|
+
check_local_decl_let_else(statement, scopes:, return_type:, allow_return:,
|
|
242
|
+
discard_binding:, declared_type:, inferred_type:)
|
|
243
|
+
else
|
|
244
|
+
check_local_decl_plain(statement, scopes:, discard_binding:,
|
|
245
|
+
declared_type:, inferred_type:)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
if noncopyable_event_storage_type?(final_type) && !fresh_noncopyable_event_initializer?(statement.value, final_type, scopes:)
|
|
249
|
+
raise_sema_error("local #{statement.name} cannot copy event storage type #{final_type}")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
unless discard_binding
|
|
253
|
+
current_scope[statement.name] = value_binding(
|
|
254
|
+
name: statement.name,
|
|
255
|
+
type: storage_type,
|
|
256
|
+
mutable: statement.kind == :var,
|
|
257
|
+
kind: statement.kind,
|
|
258
|
+
flow_type: final_type,
|
|
259
|
+
const_value:,
|
|
260
|
+
id: @preassigned_local_binding_ids[statement.object_id],
|
|
261
|
+
)
|
|
262
|
+
record_declaration_binding(statement, current_scope[statement.name])
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def check_local_decl_destructure(statement, scopes:, return_type:, allow_return:)
|
|
267
|
+
value_type = infer_expression(statement.value, scopes:)
|
|
268
|
+
|
|
269
|
+
if statement.destructure_type_name
|
|
270
|
+
check_local_decl_struct_destructure(statement, value_type, scopes:)
|
|
271
|
+
elsif value_type.is_a?(Types::Tuple)
|
|
272
|
+
check_local_decl_tuple_destructure(statement, value_type, scopes:)
|
|
273
|
+
else
|
|
274
|
+
raise_sema_error("destructure requires a tuple or struct, got #{value_type}")
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def check_local_decl_tuple_destructure(statement, value_type, scopes:)
|
|
279
|
+
raise_sema_error("destructure pattern has #{statement.destructure_bindings.length} bindings but tuple has #{value_type.element_types.length} elements") unless statement.destructure_bindings.length == value_type.element_types.length
|
|
280
|
+
|
|
281
|
+
current_scope = current_actual_scope(scopes)
|
|
282
|
+
statement.destructure_bindings.each_with_index do |name, index|
|
|
283
|
+
next if name == "_"
|
|
284
|
+
raise_sema_error("duplicate local #{name} in destructure") if current_scope.key?(name)
|
|
285
|
+
ensure_non_reserved_primitive_name!(name, kind_label: "local", line: statement.line, column: statement.column)
|
|
286
|
+
field_type = value_type.element_types[index]
|
|
287
|
+
current_scope[name] = value_binding(
|
|
288
|
+
name:,
|
|
289
|
+
type: field_type,
|
|
290
|
+
mutable: false,
|
|
291
|
+
kind: :let,
|
|
292
|
+
id: @preassigned_local_binding_ids[statement.object_id],
|
|
293
|
+
)
|
|
294
|
+
record_declaration_binding(statement, current_scope[name])
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def check_local_decl_struct_destructure(statement, value_type, scopes:)
|
|
299
|
+
type_name = statement.destructure_type_name
|
|
300
|
+
struct_type = if type_name.is_a?(Array)
|
|
301
|
+
resolve_qualified_type_name(type_name)
|
|
302
|
+
else
|
|
303
|
+
@ctx.types[type_name]
|
|
304
|
+
end
|
|
305
|
+
display_name = type_name.is_a?(Array) ? type_name.join(".") : type_name
|
|
306
|
+
raise_sema_error("unknown type #{display_name} for struct destructure") unless struct_type
|
|
307
|
+
raise_sema_error("#{display_name} is not a struct") unless struct_type.is_a?(Types::Struct) || struct_type.is_a?(Types::Tuple)
|
|
308
|
+
ensure_assignable!(value_type, struct_type, "cannot destructure #{value_type} as #{display_name}")
|
|
309
|
+
|
|
310
|
+
fields = struct_type.fields
|
|
311
|
+
raise_sema_error("destructure pattern has #{statement.destructure_bindings.length} bindings but #{display_name} has #{fields.length} fields") unless statement.destructure_bindings.length == fields.length
|
|
312
|
+
|
|
313
|
+
current_scope = current_actual_scope(scopes)
|
|
314
|
+
statement.destructure_bindings.each do |name|
|
|
315
|
+
next if name == "_"
|
|
316
|
+
raise_sema_error("duplicate local #{name} in destructure") if current_scope.key?(name)
|
|
317
|
+
raise_sema_error("unknown field #{display_name}.#{name}") unless fields.key?(name)
|
|
318
|
+
ensure_non_reserved_primitive_name!(name, kind_label: "local", line: statement.line, column: statement.column)
|
|
319
|
+
field_type = fields[name]
|
|
320
|
+
current_scope[name] = value_binding(
|
|
321
|
+
name:,
|
|
322
|
+
type: field_type,
|
|
323
|
+
mutable: false,
|
|
324
|
+
kind: :let,
|
|
325
|
+
id: @preassigned_local_binding_ids[statement.object_id],
|
|
326
|
+
)
|
|
327
|
+
record_declaration_binding(statement, current_scope[name])
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def resolve_qualified_type_name(parts)
|
|
332
|
+
return nil unless parts.length >= 2
|
|
333
|
+
|
|
334
|
+
if @ctx.imports.key?(parts.first)
|
|
335
|
+
imported_module = @ctx.imports.fetch(parts.first)
|
|
336
|
+
type = imported_module.types[parts.last]
|
|
337
|
+
if imported_module.private_type?(parts.last)
|
|
338
|
+
raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}")
|
|
339
|
+
end
|
|
340
|
+
raise_sema_error("unknown type #{parts.join('.')} for struct destructure") unless type
|
|
341
|
+
return type
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
parent_type = @ctx.types[parts.first]
|
|
345
|
+
if parent_type.respond_to?(:nested_types) && parent_type.nested_types.key?(parts.last)
|
|
346
|
+
return parent_type.nested_types[parts.last]
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
raise_sema_error("unknown type #{parts.join('.')} for struct destructure")
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def check_local_decl_let_else(statement, scopes:, return_type:, allow_return:,
|
|
353
|
+
discard_binding:, declared_type:, inferred_type:)
|
|
354
|
+
success_type = let_else_success_type(inferred_type)
|
|
355
|
+
error_type = let_else_error_type(inferred_type)
|
|
356
|
+
|
|
357
|
+
if statement.recovered_else
|
|
358
|
+
success_type ||= declared_type || @error_type
|
|
359
|
+
error_type ||= @error_type if statement.else_binding
|
|
360
|
+
else
|
|
361
|
+
raise_sema_error("let-else initializer for #{statement.name} must be nullable, Option[T], or Result[T, E], got #{inferred_type}") unless success_type
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
if discard_binding && declared_type
|
|
365
|
+
raise_sema_error("let-else discard binding _ cannot have a type annotation")
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
if discard_binding && statement.kind == :var
|
|
369
|
+
raise_sema_error("var-else discard binding _ is not allowed")
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
if statement.else_binding && !error_type
|
|
373
|
+
raise_sema_error("let-else error binding for #{statement.name} requires Result[T, E], got #{inferred_type}")
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
if declared_type && let_else_source_type?(declared_type)
|
|
377
|
+
raise_sema_error("let-else type annotation for #{statement.name} must be the success type, got #{declared_type}")
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
if declared_type
|
|
381
|
+
validate_local_ref_type!(declared_type, statement.name)
|
|
382
|
+
validate_local_proc_type!(declared_type, statement.name, initializer: statement.value)
|
|
383
|
+
ensure_assignable!(
|
|
384
|
+
success_type, declared_type,
|
|
385
|
+
"cannot assign #{success_type} to #{statement.name}: expected #{declared_type}",
|
|
386
|
+
expression: statement.value, scopes:,
|
|
387
|
+
contextual_int_to_float: contextual_int_to_float_target?(declared_type),
|
|
388
|
+
line: statement.line, column: statement.column,
|
|
389
|
+
)
|
|
390
|
+
final_type = declared_type
|
|
391
|
+
else
|
|
392
|
+
raise_sema_error("cannot bind void result to #{statement.name}") if success_type.void? && !discard_binding
|
|
393
|
+
final_type = success_type
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
unless discard_binding
|
|
397
|
+
validate_local_ref_type!(final_type, statement.name)
|
|
398
|
+
validate_local_proc_type!(final_type, statement.name, initializer: statement.value)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
else_scopes = scopes
|
|
402
|
+
if statement.else_binding
|
|
403
|
+
ensure_non_reserved_primitive_name!(statement.else_binding.name, kind_label: "let-else error binding", line: statement.else_binding.line, column: statement.else_binding.column)
|
|
404
|
+
else_binding = value_binding(
|
|
405
|
+
name: statement.else_binding.name, type: error_type, mutable: false, kind: :let,
|
|
406
|
+
id: preassigned_local_binding_id_for(statement.else_binding),
|
|
407
|
+
)
|
|
408
|
+
record_declaration_binding(statement.else_binding, else_binding)
|
|
409
|
+
else_scopes = scopes + [{ statement.else_binding.name => else_binding }]
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
check_block(statement.else_body, scopes: else_scopes, return_type:, allow_return:) if statement.else_body
|
|
413
|
+
if statement.else_body && !statement.recovered_else
|
|
414
|
+
terminator = if inside_loop?
|
|
415
|
+
ControlFlow::Termination.block_always_terminates_in_loop?(statement.else_body)
|
|
416
|
+
else
|
|
417
|
+
cfg_block_always_terminates?(statement.else_body)
|
|
418
|
+
end
|
|
419
|
+
unless terminator
|
|
420
|
+
raise_sema_error("else block for #{statement.name} must exit control flow at line #{statement.line}")
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
storage_type = statement.kind == :var ? final_type : inferred_type
|
|
425
|
+
[storage_type, final_type, nil]
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def check_local_decl_plain(statement, scopes:, discard_binding:, declared_type:, inferred_type:)
|
|
429
|
+
if declared_type
|
|
430
|
+
validate_local_ref_type!(declared_type, statement.name)
|
|
431
|
+
validate_local_proc_type!(declared_type, statement.name, initializer: statement.value)
|
|
432
|
+
ensure_assignable!(
|
|
433
|
+
inferred_type, declared_type,
|
|
434
|
+
"cannot assign #{inferred_type} to #{statement.name}: expected #{declared_type}",
|
|
435
|
+
expression: statement.value, scopes:,
|
|
436
|
+
contextual_int_to_float: contextual_int_to_float_target?(declared_type),
|
|
437
|
+
line: statement.line, column: statement.column,
|
|
438
|
+
)
|
|
439
|
+
final_type = declared_type
|
|
440
|
+
else
|
|
441
|
+
raise_sema_error("cannot infer type for #{statement.name} from null") if inferred_type.is_a?(Types::Null)
|
|
442
|
+
raise_sema_error("cannot bind void result to #{statement.name}") if inferred_type.void?
|
|
443
|
+
final_type = inferred_type
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
validate_local_ref_type!(final_type, statement.name)
|
|
447
|
+
validate_local_proc_type!(final_type, statement.name, initializer: statement.value)
|
|
448
|
+
|
|
449
|
+
const_value = statement.kind == :let && statement.value ? evaluate_compile_time_const_value(statement.value, scopes:) : nil
|
|
450
|
+
[final_type, final_type, const_value]
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def check_assignment(statement, scopes:)
|
|
454
|
+
if statement.operator == "=" &&
|
|
455
|
+
statement.target.is_a?(AST::IndexAccess) &&
|
|
456
|
+
statement.target.index.is_a?(AST::RangeExpr)
|
|
457
|
+
return check_range_index_assignment(statement, scopes:)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
target_type = infer_lvalue(statement.target, scopes:)
|
|
461
|
+
raise_sema_error("cannot assign to non-copyable event storage type #{target_type}") if noncopyable_event_storage_type?(target_type)
|
|
462
|
+
|
|
463
|
+
validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false)
|
|
464
|
+
value_type = infer_expression(statement.value, scopes:, expected_type: target_type)
|
|
465
|
+
|
|
466
|
+
case statement.operator
|
|
467
|
+
when "="
|
|
468
|
+
ensure_assignable!(
|
|
469
|
+
value_type,
|
|
470
|
+
target_type,
|
|
471
|
+
"cannot assign #{value_type} to #{target_type}",
|
|
472
|
+
expression: statement.value,
|
|
473
|
+
external_numeric: external_numeric_assignment_target?(statement.target, scopes:),
|
|
474
|
+
contextual_int_to_float: contextual_int_to_float_target?(target_type),
|
|
475
|
+
line: statement.line, column: statement.column,
|
|
476
|
+
)
|
|
477
|
+
when "+=", "-=", "*=", "/="
|
|
478
|
+
raise_sema_error("operator #{statement.operator} requires matching numeric types, got #{target_type} and #{value_type}") unless target_type.numeric? && value_type.numeric?
|
|
479
|
+
|
|
480
|
+
ensure_assignable!(
|
|
481
|
+
value_type,
|
|
482
|
+
target_type,
|
|
483
|
+
"operator #{statement.operator} requires matching numeric types, got #{target_type} and #{value_type}",
|
|
484
|
+
expression: statement.value,
|
|
485
|
+
contextual_int_to_float: contextual_int_to_float_target?(target_type),
|
|
486
|
+
line: statement.line, column: statement.column,
|
|
487
|
+
)
|
|
488
|
+
when "%="
|
|
489
|
+
unless common_integer_type(target_type, value_type) == target_type
|
|
490
|
+
raise_sema_error("operator #{statement.operator} requires compatible integer types, got #{target_type} and #{value_type}")
|
|
491
|
+
end
|
|
492
|
+
when "&=", "|=", "^="
|
|
493
|
+
unless target_type == value_type && bitwise_type?(target_type)
|
|
494
|
+
raise_sema_error("operator #{statement.operator} requires matching integer or flags types, got #{target_type} and #{value_type}")
|
|
495
|
+
end
|
|
496
|
+
when "<<=", ">>="
|
|
497
|
+
unless target_type.is_a?(Types::Primitive) && target_type.integer? && value_type.is_a?(Types::Primitive) && value_type.integer?
|
|
498
|
+
raise_sema_error("operator #{statement.operator} requires integer operands, got #{target_type} and #{value_type}")
|
|
499
|
+
end
|
|
500
|
+
else
|
|
501
|
+
raise_sema_error("unsupported assignment operator #{statement.operator}")
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def check_range_index_assignment(statement, scopes:)
|
|
506
|
+
target = statement.target
|
|
507
|
+
range = target.index
|
|
508
|
+
|
|
509
|
+
raise_sema_error("range index assignment requires an expression list on the right-hand side") unless statement.value.is_a?(AST::ExpressionList)
|
|
510
|
+
raise_sema_error("range index assignment requires integer literal bounds") unless range.start_expr.is_a?(AST::IntegerLiteral) && range.end_expr.is_a?(AST::IntegerLiteral)
|
|
511
|
+
|
|
512
|
+
start_val = range.start_expr.value
|
|
513
|
+
end_val = range.end_expr.value
|
|
514
|
+
raise_sema_error("range start must be less than end in range index assignment") unless start_val < end_val
|
|
515
|
+
|
|
516
|
+
count = end_val - start_val
|
|
517
|
+
raise_sema_error("range index assignment: range [#{start_val}..#{end_val}) spans #{count} elements but tuple has #{statement.value.elements.length}") unless statement.value.elements.length == count
|
|
518
|
+
|
|
519
|
+
receiver_type = infer_lvalue_receiver(
|
|
520
|
+
target.receiver,
|
|
521
|
+
scopes:,
|
|
522
|
+
allow_pointer_identifier: true,
|
|
523
|
+
require_mutable_pointer: true,
|
|
524
|
+
allow_span_param_identifier: true,
|
|
525
|
+
)
|
|
526
|
+
element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint"))
|
|
527
|
+
|
|
528
|
+
statement.value.elements.each_with_index do |elem, i|
|
|
529
|
+
elem = elem.is_a?(AST::Argument) ? elem.value : elem
|
|
530
|
+
validate_consuming_foreign_expression!(elem, scopes:, root_allowed: false)
|
|
531
|
+
elem_type = infer_expression(elem, scopes:, expected_type: element_type)
|
|
532
|
+
ensure_assignable!(
|
|
533
|
+
elem_type,
|
|
534
|
+
element_type,
|
|
535
|
+
"range index assignment element #{i}: cannot assign #{elem_type} to #{element_type}",
|
|
536
|
+
expression: elem,
|
|
537
|
+
contextual_int_to_float: contextual_int_to_float_target?(element_type),
|
|
538
|
+
line: statement.line,
|
|
539
|
+
)
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def check_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
544
|
+
validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: false)
|
|
545
|
+
scrutinee_type = infer_expression(statement.expression, scopes:)
|
|
546
|
+
if error_type?(scrutinee_type)
|
|
547
|
+
check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
548
|
+
elsif scrutinee_type.is_a?(Types::Enum)
|
|
549
|
+
check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
550
|
+
elsif scrutinee_type.is_a?(Types::Variant)
|
|
551
|
+
check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
552
|
+
elsif integer_type?(scrutinee_type)
|
|
553
|
+
check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
554
|
+
elsif scrutinee_type.is_a?(Types::StringView)
|
|
555
|
+
check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
556
|
+
elsif scrutinee_type.is_a?(Types::Tuple)
|
|
557
|
+
check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
558
|
+
else
|
|
559
|
+
raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}")
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
564
|
+
each_enum_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes|
|
|
565
|
+
check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:)
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def each_enum_match_arm(statement, scrutinee_type, scopes:)
|
|
570
|
+
covered_members = {}
|
|
571
|
+
wildcard_seen = false
|
|
572
|
+
statement.arms.each do |arm|
|
|
573
|
+
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
574
|
+
yield arm, scopes
|
|
575
|
+
next
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
if wildcard_pattern?(arm.pattern)
|
|
579
|
+
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
580
|
+
wildcard_seen = true
|
|
581
|
+
yield arm, scopes
|
|
582
|
+
next
|
|
583
|
+
end
|
|
584
|
+
validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false)
|
|
585
|
+
validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false)
|
|
586
|
+
pattern_type = infer_expression(arm.pattern, scopes:, expected_type: scrutinee_type)
|
|
587
|
+
ensure_assignable!(pattern_type, scrutinee_type, "match arm expects #{scrutinee_type}, got #{pattern_type}")
|
|
588
|
+
|
|
589
|
+
member_name = match_member_name(arm.pattern, scrutinee_type)
|
|
590
|
+
raise_sema_error("match arm must be an enum member of #{scrutinee_type}") unless member_name
|
|
591
|
+
raise_sema_error("duplicate match arm #{scrutinee_type}.#{member_name}") if covered_members.key?(member_name)
|
|
592
|
+
|
|
593
|
+
covered_members[member_name] = true
|
|
594
|
+
yield arm, scopes
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
return if wildcard_seen
|
|
598
|
+
|
|
599
|
+
missing_members = scrutinee_type.members - covered_members.keys
|
|
600
|
+
return if missing_members.empty?
|
|
601
|
+
|
|
602
|
+
raise_sema_error("match on #{scrutinee_type} is missing cases: #{missing_members.join(', ')}")
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
606
|
+
has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
607
|
+
raise_sema_error("match on integer type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
|
|
608
|
+
|
|
609
|
+
covered_values = {}
|
|
610
|
+
wildcard_seen = false
|
|
611
|
+
statement.arms.each do |arm|
|
|
612
|
+
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
613
|
+
check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:)
|
|
614
|
+
next
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
if wildcard_pattern?(arm.pattern)
|
|
618
|
+
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
619
|
+
wildcard_seen = true
|
|
620
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
621
|
+
next
|
|
622
|
+
end
|
|
623
|
+
unless arm.pattern.is_a?(AST::IntegerLiteral) || arm.pattern.is_a?(AST::CharLiteral)
|
|
624
|
+
raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, or _, got #{arm.pattern.class.name}")
|
|
625
|
+
end
|
|
626
|
+
value = arm.pattern.value
|
|
627
|
+
raise_sema_error("duplicate match arm value #{value}") if covered_values.key?(value)
|
|
628
|
+
covered_values[value] = true
|
|
629
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
634
|
+
has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
635
|
+
raise_sema_error("match on str requires a wildcard arm (_:)") unless has_wildcard
|
|
636
|
+
|
|
637
|
+
covered_values = {}
|
|
638
|
+
wildcard_seen = false
|
|
639
|
+
statement.arms.each do |arm|
|
|
640
|
+
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
641
|
+
check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:)
|
|
642
|
+
next
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
if wildcard_pattern?(arm.pattern)
|
|
646
|
+
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
647
|
+
wildcard_seen = true
|
|
648
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
649
|
+
next
|
|
650
|
+
end
|
|
651
|
+
unless arm.pattern.is_a?(AST::StringLiteral)
|
|
652
|
+
raise_sema_error("match arm for str scrutinee must be a string literal or _, got #{arm.pattern.class.name}")
|
|
653
|
+
end
|
|
654
|
+
value = arm.pattern.value
|
|
655
|
+
raise_sema_error("duplicate match arm value \"#{value}\"") if covered_values.key?(value)
|
|
656
|
+
covered_values[value] = true
|
|
657
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
662
|
+
has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
663
|
+
raise_sema_error("match on tuple type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
|
|
664
|
+
|
|
665
|
+
arity = scrutinee_type.element_types.length
|
|
666
|
+
covered_values = {}
|
|
667
|
+
wildcard_seen = false
|
|
668
|
+
|
|
669
|
+
statement.arms.each do |arm|
|
|
670
|
+
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
671
|
+
check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:)
|
|
672
|
+
next
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
if wildcard_pattern?(arm.pattern)
|
|
676
|
+
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
677
|
+
wildcard_seen = true
|
|
678
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
679
|
+
next
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
unless arm.pattern.is_a?(AST::ExpressionList)
|
|
683
|
+
raise_sema_error("match arm for tuple scrutinee must be a tuple literal or _, got #{arm.pattern.class.name}")
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
elements = arm.pattern.elements
|
|
687
|
+
raise_sema_error("tuple match arm has #{elements.length} elements but scrutinee has #{arity}") unless elements.length == arity
|
|
688
|
+
|
|
689
|
+
values = elements.map do |elem|
|
|
690
|
+
if elem.is_a?(AST::Identifier) && elem.name == "_"
|
|
691
|
+
:wildcard
|
|
692
|
+
elsif elem.is_a?(AST::IntegerLiteral) || elem.is_a?(AST::CharLiteral) ||
|
|
693
|
+
elem.is_a?(AST::StringLiteral) || elem.is_a?(AST::BooleanLiteral)
|
|
694
|
+
elem.value
|
|
695
|
+
else
|
|
696
|
+
raise_sema_error("tuple match arm element must be a literal or _, got #{elem.class.name}")
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
all_literal = values.all? { |v| v != :wildcard }
|
|
701
|
+
if all_literal
|
|
702
|
+
raise_sema_error("duplicate tuple match arm #{arm.pattern.elements.map { |e| e.respond_to?(:value) ? e.value.inspect : '_' }.join(', ')}") if covered_values.key?(values)
|
|
703
|
+
covered_values[values] = true
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
check_block(arm.body, scopes:, return_type:, allow_return:)
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
def wildcard_pattern?(expression)
|
|
711
|
+
expression.is_a?(AST::Identifier) && expression.name == "_"
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def let_else_discard_binding_syntax?(statement)
|
|
715
|
+
statement.is_a?(AST::LocalDecl) && (statement.else_body || statement.recovered_else) && statement.name == "_"
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def let_else_source_type?(type)
|
|
719
|
+
type.is_a?(Types::Nullable) || result_let_else_type?(type) || option_let_else_type?(type)
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
def let_else_success_type(type)
|
|
723
|
+
return @error_type if error_type?(type)
|
|
724
|
+
return type.base if type.is_a?(Types::Nullable)
|
|
725
|
+
return type.arm("some").fetch("value") if option_let_else_type?(type)
|
|
726
|
+
return unless result_let_else_type?(type)
|
|
727
|
+
|
|
728
|
+
type.arm("success").fetch("value")
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def let_else_error_type(type)
|
|
732
|
+
return @error_type if error_type?(type)
|
|
733
|
+
return unless result_let_else_type?(type)
|
|
734
|
+
|
|
735
|
+
type.arm("failure").fetch("error")
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
def option_let_else_type?(type)
|
|
739
|
+
return false unless type.is_a?(Types::Variant)
|
|
740
|
+
|
|
741
|
+
some_fields = type.arm("some")
|
|
742
|
+
none_fields = type.arm("none")
|
|
743
|
+
some_fields && some_fields.length == 1 && some_fields.key?("value") &&
|
|
744
|
+
none_fields && none_fields.empty?
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def result_let_else_type?(type)
|
|
748
|
+
return false unless type.is_a?(Types::Variant)
|
|
749
|
+
|
|
750
|
+
success_fields = type.arm("success")
|
|
751
|
+
failure_fields = type.arm("failure")
|
|
752
|
+
success_fields && success_fields.length == 1 && success_fields.key?("value") &&
|
|
753
|
+
failure_fields && failure_fields.length == 1 && failure_fields.key?("error")
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:)
|
|
757
|
+
each_variant_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes|
|
|
758
|
+
check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:)
|
|
759
|
+
end
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
def each_variant_match_arm(statement, scrutinee_type, scopes:)
|
|
763
|
+
covered_arms = {}
|
|
764
|
+
wildcard_seen = false
|
|
765
|
+
equality_cover_table = {}
|
|
766
|
+
statement.arms.each do |arm|
|
|
767
|
+
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
768
|
+
yield arm, scopes
|
|
769
|
+
next
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
if wildcard_pattern?(arm.pattern)
|
|
773
|
+
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
774
|
+
wildcard_seen = true
|
|
775
|
+
yield arm, scopes
|
|
776
|
+
next
|
|
777
|
+
end
|
|
778
|
+
validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false)
|
|
779
|
+
validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false)
|
|
780
|
+
|
|
781
|
+
arm_name = variant_match_arm_name(arm.pattern, scrutinee_type)
|
|
782
|
+
raise_sema_error("match arm must be a variant arm of #{scrutinee_type}") unless arm_name
|
|
783
|
+
|
|
784
|
+
arm_scopes = scopes.dup
|
|
785
|
+
has_guards = false
|
|
786
|
+
equality_cover = {}
|
|
787
|
+
|
|
788
|
+
if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty?
|
|
789
|
+
has_guards, equality_cover = check_struct_match_pattern(arm.pattern.arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:)
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
unless has_guards
|
|
793
|
+
raise_sema_error("duplicate match arm #{scrutinee_type}.#{arm_name}") if covered_arms.key?(arm_name)
|
|
794
|
+
covered_arms[arm_name] = true
|
|
795
|
+
else
|
|
796
|
+
equality_cover_table[arm_name] = merge_equality_cover(equality_cover_table[arm_name], equality_cover)
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
if arm.binding_name
|
|
800
|
+
ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column)
|
|
801
|
+
fields = scrutinee_type.arm(arm_name)
|
|
802
|
+
if fields.nil? || fields.empty?
|
|
803
|
+
raise_sema_error("variant arm #{scrutinee_type}.#{arm_name} has no payload; 'as' binding is not allowed")
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
|
|
807
|
+
binding = value_binding(
|
|
808
|
+
name: arm.binding_name,
|
|
809
|
+
type: payload_type,
|
|
810
|
+
mutable: true,
|
|
811
|
+
kind: :local,
|
|
812
|
+
id: @preassigned_local_binding_ids[arm.object_id],
|
|
813
|
+
)
|
|
814
|
+
arm_scopes = arm_scopes + [{ arm.binding_name => binding }]
|
|
815
|
+
record_declaration_binding(arm, binding)
|
|
816
|
+
end
|
|
817
|
+
yield arm, arm_scopes
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
equality_cover_table.each do |arm_name, field_covers|
|
|
821
|
+
next if covered_arms.key?(arm_name)
|
|
822
|
+
payload_fields = scrutinee_type.arm(arm_name)
|
|
823
|
+
next unless payload_fields
|
|
824
|
+
covered = field_covers.any? do |field_name, covered_members|
|
|
825
|
+
field_type = payload_fields[field_name]
|
|
826
|
+
next unless field_type.is_a?(Types::Enum)
|
|
827
|
+
all_members = field_type.members
|
|
828
|
+
all_members.any? && (all_members - covered_members).empty?
|
|
829
|
+
end
|
|
830
|
+
covered_arms[arm_name] = true if covered
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
return if wildcard_seen
|
|
834
|
+
|
|
835
|
+
missing_arms = scrutinee_type.arm_names - covered_arms.keys
|
|
836
|
+
return if missing_arms.empty?
|
|
837
|
+
|
|
838
|
+
raise_sema_error("match on #{scrutinee_type} is missing cases: #{missing_arms.join(', ')}")
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def merge_equality_cover(existing, new_cover)
|
|
842
|
+
return new_cover unless existing
|
|
843
|
+
merged = existing.dup
|
|
844
|
+
new_cover.each do |field, members|
|
|
845
|
+
merged[field] = (merged[field] || []) + members
|
|
846
|
+
end
|
|
847
|
+
merged
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
def check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
851
|
+
statement.arms.each do |arm|
|
|
852
|
+
check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:)
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:)
|
|
857
|
+
arm_scopes = scopes.dup
|
|
858
|
+
if arm.binding_name
|
|
859
|
+
ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column)
|
|
860
|
+
binding = value_binding(
|
|
861
|
+
name: arm.binding_name,
|
|
862
|
+
type: @error_type,
|
|
863
|
+
mutable: true,
|
|
864
|
+
kind: :local,
|
|
865
|
+
id: @preassigned_local_binding_ids.fetch(arm.object_id),
|
|
866
|
+
)
|
|
867
|
+
arm_scopes = arm_scopes + [{ arm.binding_name => binding }]
|
|
868
|
+
record_declaration_binding(arm, binding)
|
|
869
|
+
end
|
|
870
|
+
check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:)
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def variant_match_arm_name(pattern, scrutinee_type)
|
|
874
|
+
# Pattern must be `TypeName.arm_name` or `module.TypeName.arm_name`
|
|
875
|
+
# For struct patterns, the pattern is Call(MemberAccess(...), args) — unwrap the callee
|
|
876
|
+
callee = case pattern
|
|
877
|
+
when AST::Call
|
|
878
|
+
pattern.callee
|
|
879
|
+
else
|
|
880
|
+
pattern
|
|
881
|
+
end
|
|
882
|
+
return nil unless callee.is_a?(AST::MemberAccess)
|
|
883
|
+
|
|
884
|
+
member = callee.member
|
|
885
|
+
return nil unless scrutinee_type.arm_names.include?(member)
|
|
886
|
+
|
|
887
|
+
# Verify the receiver resolves to the scrutinee variant type
|
|
888
|
+
receiver_type = resolve_type_expression(callee.receiver)
|
|
889
|
+
return member if receiver_type == scrutinee_type
|
|
890
|
+
|
|
891
|
+
if scrutinee_type.is_a?(Types::VariantInstance) && receiver_type.is_a?(Types::GenericVariantDefinition)
|
|
892
|
+
return member if receiver_type == scrutinee_type.definition
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
return nil unless scrutinee_type.is_a?(Types::VariantInstance) && receiver_type.is_a?(Types::Variant)
|
|
896
|
+
return nil unless receiver_type.name == scrutinee_type.name && receiver_type.module_name == scrutinee_type.module_name
|
|
897
|
+
|
|
898
|
+
member
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
def check_struct_match_pattern(arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:)
|
|
902
|
+
payload_fields = scrutinee_type.arm(arm_name)
|
|
903
|
+
raise_sema_error("variant arm #{scrutinee_type}.#{arm_name} has no payload fields for struct pattern") if payload_fields.nil? || payload_fields.empty?
|
|
904
|
+
|
|
905
|
+
# Nested struct detection: if arm has exactly one field whose type is a struct,
|
|
906
|
+
# auto-destructure through to the struct's own fields, but only when none of
|
|
907
|
+
# the arguments reference the arm's own field name directly.
|
|
908
|
+
if payload_fields.size == 1
|
|
909
|
+
native_field = payload_fields.keys.first
|
|
910
|
+
has_native_reference = arguments.any? do |arg|
|
|
911
|
+
next false if !arg.name && arg.value.is_a?(AST::Identifier) && arg.value.name == "_"
|
|
912
|
+
name = arg.name || (arg.value.is_a?(AST::Identifier) ? arg.value.name : nil)
|
|
913
|
+
name == native_field
|
|
914
|
+
end
|
|
915
|
+
unless has_native_reference
|
|
916
|
+
single_field_type = payload_fields.values.first
|
|
917
|
+
payload_fields = single_field_type.fields if single_field_type.is_a?(Types::Struct)
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
has_guards = false
|
|
922
|
+
seen_fields = {}
|
|
923
|
+
equality_cover = {}
|
|
924
|
+
|
|
925
|
+
arguments.each do |arg|
|
|
926
|
+
if arg.value.is_a?(AST::Identifier) && arg.value.name == "_"
|
|
927
|
+
# _ discard: skip this field, no binding, no guard
|
|
928
|
+
next
|
|
929
|
+
elsif arg.name
|
|
930
|
+
# Equality pattern: kind = Kind.boss
|
|
931
|
+
field_name = arg.name
|
|
932
|
+
raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name)
|
|
933
|
+
raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name)
|
|
934
|
+
seen_fields[field_name] = true
|
|
935
|
+
has_guards = true
|
|
936
|
+
|
|
937
|
+
field_type = payload_fields[field_name]
|
|
938
|
+
actual_type = infer_expression(arg.value, scopes:, expected_type: field_type)
|
|
939
|
+
ensure_assignable!(actual_type, field_type, "field #{field_name} expects #{field_type}, got #{actual_type}", expression: arg.value)
|
|
940
|
+
|
|
941
|
+
if field_type.is_a?(Types::Enum) && arg.value.is_a?(AST::MemberAccess)
|
|
942
|
+
equality_cover[field_name] = [] unless equality_cover.key?(field_name)
|
|
943
|
+
equality_cover[field_name] << arg.value.member
|
|
944
|
+
end
|
|
945
|
+
elsif arg.value.is_a?(AST::Identifier)
|
|
946
|
+
# Binding: position
|
|
947
|
+
field_name = arg.value.name
|
|
948
|
+
raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name)
|
|
949
|
+
raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name)
|
|
950
|
+
seen_fields[field_name] = true
|
|
951
|
+
|
|
952
|
+
field_type = payload_fields[field_name]
|
|
953
|
+
binding = value_binding(
|
|
954
|
+
name: field_name,
|
|
955
|
+
type: field_type,
|
|
956
|
+
mutable: false,
|
|
957
|
+
kind: :local,
|
|
958
|
+
id: @preassigned_local_binding_ids[arg.object_id],
|
|
959
|
+
)
|
|
960
|
+
arm_scopes.last[field_name] = binding
|
|
961
|
+
elsif arg.value.is_a?(AST::BinaryOp) && arg.value.left.is_a?(AST::Identifier)
|
|
962
|
+
# Guard: hp > 0
|
|
963
|
+
field_name = arg.value.left.name
|
|
964
|
+
raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name)
|
|
965
|
+
raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name)
|
|
966
|
+
seen_fields[field_name] = true
|
|
967
|
+
has_guards = true
|
|
968
|
+
|
|
969
|
+
field_type = payload_fields[field_name]
|
|
970
|
+
comparison_operators = ["==", "!=", "<", "<=", ">", ">="]
|
|
971
|
+
unless comparison_operators.include?(arg.value.operator)
|
|
972
|
+
raise_sema_error("unsupported guard operator '#{arg.value.operator}' in struct pattern; use ==, !=, <, <=, >, or >=", arg.value)
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
operand_type = infer_expression(arg.value.right, scopes:, expected_type: field_type)
|
|
976
|
+
ensure_assignable!(operand_type, field_type, "guard comparison expects #{field_type}, got #{operand_type}", expression: arg.value.right)
|
|
977
|
+
else
|
|
978
|
+
raise_sema_error("invalid field pattern in struct match arm; expected field name, comparison, or equality", expression: arg.value)
|
|
979
|
+
end
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
[has_guards, equality_cover]
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def check_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
986
|
+
if statement.threaded
|
|
987
|
+
check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
988
|
+
return
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
statement.iterables.each do |iterable|
|
|
992
|
+
validate_consuming_foreign_expression!(iterable, scopes:, root_allowed: false)
|
|
993
|
+
end
|
|
994
|
+
|
|
995
|
+
raise_sema_error("for loop binder count must match iterable count") unless statement.bindings.length == statement.iterables.length
|
|
996
|
+
|
|
997
|
+
binding_infos = if statement.parallel?
|
|
998
|
+
check_parallel_for_bindings(statement, scopes:)
|
|
999
|
+
else
|
|
1000
|
+
iterable_type = nil
|
|
1001
|
+
loop_type = if range_expr?(statement.iterable)
|
|
1002
|
+
check_range_expr_loop(statement.iterable, scopes:)
|
|
1003
|
+
else
|
|
1004
|
+
iterable_type = infer_expression(statement.iterable, scopes:)
|
|
1005
|
+
collection_loop_type(iterable_type) || iterator_loop_type(iterable_type)
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
raise_sema_error("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next()") unless loop_type
|
|
1009
|
+
|
|
1010
|
+
binding_type = if iterable_type
|
|
1011
|
+
collection_loop_binding_type(iterable_type, loop_type) || loop_type
|
|
1012
|
+
else
|
|
1013
|
+
loop_type
|
|
1014
|
+
end
|
|
1015
|
+
[{ binding: statement.binding, type: binding_type }]
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
with_nested_scope(scopes) do |loop_scopes|
|
|
1019
|
+
binding_infos.each do |entry|
|
|
1020
|
+
binding = entry[:binding]
|
|
1021
|
+
ensure_non_reserved_primitive_name!(binding.name, kind_label: "for binding", line: binding.line, column: binding.column)
|
|
1022
|
+
current_actual_scope(loop_scopes)[binding.name] = value_binding(
|
|
1023
|
+
name: binding.name,
|
|
1024
|
+
type: entry[:type],
|
|
1025
|
+
mutable: false,
|
|
1026
|
+
kind: :let,
|
|
1027
|
+
id: @preassigned_local_binding_ids[binding.object_id],
|
|
1028
|
+
)
|
|
1029
|
+
record_declaration_binding(binding, current_actual_scope(loop_scopes)[binding.name])
|
|
1030
|
+
end
|
|
1031
|
+
with_loop do
|
|
1032
|
+
check_block(statement.body, scopes: loop_scopes, return_type:, allow_return:)
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
def check_parallel_for_bindings(statement, scopes:)
|
|
1038
|
+
raise_sema_error("parallel for loops currently support arrays and spans only") if statement.iterables.any? { |iterable| range_expr?(iterable) }
|
|
1039
|
+
|
|
1040
|
+
iterable_types = statement.iterables.map { |iterable| infer_expression(iterable, scopes:) }
|
|
1041
|
+
binding_infos = iterable_types.each_with_index.map do |iterable_type, index|
|
|
1042
|
+
loop_type = collection_loop_type(iterable_type)
|
|
1043
|
+
raise_sema_error("parallel for loops expect arrays or spans for each iterable") unless loop_type
|
|
1044
|
+
|
|
1045
|
+
binding_type = collection_loop_binding_type(iterable_type, loop_type) || loop_type
|
|
1046
|
+
{ binding: statement.bindings[index], iterable_type:, type: binding_type }
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
ensure_parallel_for_static_lengths_match!(binding_infos.map { |entry| entry[:iterable_type] })
|
|
1050
|
+
binding_infos
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def ensure_parallel_for_static_lengths_match!(iterable_types)
|
|
1054
|
+
lengths = iterable_types.filter_map { |iterable_type| array_type?(iterable_type) ? array_length(iterable_type) : nil }
|
|
1055
|
+
return if lengths.empty? || lengths.all? { |length| length == lengths.first }
|
|
1056
|
+
|
|
1057
|
+
raise_sema_error("parallel for iterables must have matching lengths")
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
def check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1061
|
+
@uses_parallel_for = true
|
|
1062
|
+
raise_sema_error("parallel for requires a range expression (start..end)") unless range_expr?(statement.iterable)
|
|
1063
|
+
|
|
1064
|
+
loop_type = check_range_expr_loop(statement.iterable, scopes:)
|
|
1065
|
+
validate_threaded_for_body!(statement.body)
|
|
1066
|
+
|
|
1067
|
+
with_nested_scope(scopes) do |loop_scopes|
|
|
1068
|
+
binding = statement.binding
|
|
1069
|
+
ensure_non_reserved_primitive_name!(binding.name, kind_label: "for binding", line: binding.line, column: binding.column)
|
|
1070
|
+
current_actual_scope(loop_scopes)[binding.name] = value_binding(
|
|
1071
|
+
name: binding.name,
|
|
1072
|
+
type: loop_type,
|
|
1073
|
+
mutable: false,
|
|
1074
|
+
kind: :let,
|
|
1075
|
+
id: @preassigned_local_binding_ids[binding.object_id],
|
|
1076
|
+
)
|
|
1077
|
+
record_declaration_binding(binding, current_actual_scope(loop_scopes)[binding.name])
|
|
1078
|
+
check_block(statement.body, scopes: loop_scopes, return_type:, allow_return: false)
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
def validate_threaded_for_body!(body)
|
|
1083
|
+
body.each { |stmt| validate_threaded_for_statement!(stmt) }
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
def validate_threaded_for_statement!(stmt)
|
|
1087
|
+
case stmt
|
|
1088
|
+
when AST::BreakStmt
|
|
1089
|
+
raise_sema_error("break is not allowed inside parallel for", line: stmt.line, column: stmt.column)
|
|
1090
|
+
when AST::ContinueStmt
|
|
1091
|
+
raise_sema_error("continue is not allowed inside parallel for", line: stmt.line, column: stmt.column)
|
|
1092
|
+
when AST::ReturnStmt
|
|
1093
|
+
raise_sema_error("return is not allowed inside parallel for", line: stmt.line, column: stmt.column)
|
|
1094
|
+
when AST::DeferStmt
|
|
1095
|
+
raise_sema_error("defer is not allowed inside parallel for", line: stmt.line, column: stmt.column)
|
|
1096
|
+
when AST::AwaitExpr
|
|
1097
|
+
raise_sema_error("await is not allowed inside parallel for", line: stmt.line, column: stmt.column)
|
|
1098
|
+
when AST::IfStmt
|
|
1099
|
+
validate_threaded_for_body!(stmt.then_body)
|
|
1100
|
+
stmt.else_if_clauses&.each { |clause| validate_threaded_for_body!(clause.body) }
|
|
1101
|
+
validate_threaded_for_body!(stmt.else_body) if stmt.else_body
|
|
1102
|
+
when AST::WhileStmt
|
|
1103
|
+
validate_threaded_for_body!(stmt.body)
|
|
1104
|
+
when AST::ForStmt
|
|
1105
|
+
raise_sema_error("nested for loops are not allowed inside parallel for", line: stmt.line, column: stmt.column) if stmt.threaded
|
|
1106
|
+
validate_threaded_for_body!(stmt.body)
|
|
1107
|
+
when AST::MatchStmt
|
|
1108
|
+
stmt.arms&.each { |arm| validate_threaded_for_body!(arm.body) }
|
|
1109
|
+
when AST::UnsafeStmt
|
|
1110
|
+
validate_threaded_for_body!(stmt.body) if stmt.body.is_a?(Array)
|
|
1111
|
+
end
|
|
1112
|
+
end
|
|
1113
|
+
|
|
1114
|
+
def check_parallel_block_stmt(statement, scopes:, return_type:)
|
|
1115
|
+
@uses_parallel_for = true
|
|
1116
|
+
raise_sema_error("parallel block must contain at least two statements", line: statement.line, column: statement.column) if statement.bodies.length < 2
|
|
1117
|
+
|
|
1118
|
+
statement.bodies.each do |body|
|
|
1119
|
+
validate_threaded_for_body!(body)
|
|
1120
|
+
check_block(body, scopes:, return_type:, allow_return: false)
|
|
1121
|
+
end
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
def check_gather_stmt(statement, scopes:)
|
|
1125
|
+
raise_sema_error("gather requires at least one handle", line: statement.line, column: statement.column) if statement.handles.empty?
|
|
1126
|
+
|
|
1127
|
+
statement.handles.each do |handle|
|
|
1128
|
+
handle_type = infer_expression(handle, scopes:)
|
|
1129
|
+
raise_sema_error("gather expects Handle, got #{handle_type}", line: statement.line, column: statement.column) unless handle_type.is_a?(Types::Handle)
|
|
1130
|
+
end
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
def check_static_assert(statement, scopes:)
|
|
1135
|
+
validate_consuming_foreign_expression!(statement.condition, scopes:, root_allowed: false)
|
|
1136
|
+
validate_consuming_foreign_expression!(statement.message, scopes:, root_allowed: false)
|
|
1137
|
+
validate_hoistable_foreign_expression!(statement.condition, scopes:, root_hoistable: false)
|
|
1138
|
+
validate_hoistable_foreign_expression!(statement.message, scopes:, root_hoistable: false)
|
|
1139
|
+
condition_type = infer_expression(statement.condition, scopes:, expected_type: @ctx.types.fetch("bool"))
|
|
1140
|
+
ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "static_assert condition must be bool, got #{condition_type}")
|
|
1141
|
+
condition_value = evaluate_compile_time_const_value(statement.condition, scopes:)
|
|
1142
|
+
raise_sema_error("static_assert condition must be a compile-time bool constant") unless condition_value == true || condition_value == false
|
|
1143
|
+
raise_sema_error("static_assert message must be a string literal") unless statement.message.is_a?(AST::StringLiteral)
|
|
1144
|
+
|
|
1145
|
+
message_type = infer_expression(statement.message, scopes:, expected_type: @ctx.types.fetch("str"))
|
|
1146
|
+
return if string_like_type?(message_type)
|
|
1147
|
+
|
|
1148
|
+
raise_sema_error("static_assert message must be str or cstr, got #{message_type}")
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def check_emit_stmt(statement)
|
|
1152
|
+
raise_sema_error("emit is only allowed inside const function or inline blocks") unless @compile_time_depth.positive?
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1155
|
+
def check_range_expr_loop(expression, scopes:)
|
|
1156
|
+
start_type = infer_expression(expression.start_expr, scopes:)
|
|
1157
|
+
stop_type = infer_expression(expression.end_expr, scopes:)
|
|
1158
|
+
|
|
1159
|
+
unless integer_type?(start_type) && integer_type?(stop_type)
|
|
1160
|
+
raise_sema_error("range bounds must be integer types, got #{start_type} and #{stop_type}")
|
|
1161
|
+
end
|
|
1162
|
+
|
|
1163
|
+
if start_type != stop_type
|
|
1164
|
+
if expression.start_expr.is_a?(AST::IntegerLiteral)
|
|
1165
|
+
start_type = infer_expression(expression.start_expr, scopes:, expected_type: stop_type)
|
|
1166
|
+
elsif expression.end_expr.is_a?(AST::IntegerLiteral)
|
|
1167
|
+
stop_type = infer_expression(expression.end_expr, scopes:, expected_type: start_type)
|
|
1168
|
+
end
|
|
1169
|
+
end
|
|
1170
|
+
|
|
1171
|
+
raise_sema_error("range bounds must use matching integer types, got #{start_type} and #{stop_type}") unless start_type == stop_type
|
|
1172
|
+
|
|
1173
|
+
start_type
|
|
1174
|
+
end
|
|
1175
|
+
|
|
1176
|
+
def when_chosen_body(decl)
|
|
1177
|
+
discriminant_value = evaluate_compile_time_const_value(decl.discriminant, scopes: [])
|
|
1178
|
+
return nil if discriminant_value.nil?
|
|
1179
|
+
|
|
1180
|
+
chosen_branch = decl.branches.find do |branch|
|
|
1181
|
+
pattern_value = evaluate_compile_time_const_value(branch.pattern, scopes: [])
|
|
1182
|
+
discriminant_value == pattern_value
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
chosen_branch&.body || decl.else_body
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
def check_when_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1189
|
+
discriminant_value = evaluate_when_discriminant(statement.discriminant)
|
|
1190
|
+
|
|
1191
|
+
chosen_branch = statement.branches.find do |branch|
|
|
1192
|
+
pattern_value = evaluate_compile_time_const_value(branch.pattern, scopes:)
|
|
1193
|
+
discriminant_value == pattern_value
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
if chosen_branch
|
|
1197
|
+
check_block(chosen_branch.body, scopes:, return_type:, allow_return:)
|
|
1198
|
+
elsif statement.else_body
|
|
1199
|
+
check_block(statement.else_body, scopes:, return_type:, allow_return:)
|
|
1200
|
+
else
|
|
1201
|
+
raise_sema_error("when discriminant value #{discriminant_value} does not match any branch and no else is provided")
|
|
1202
|
+
end
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
def evaluate_when_discriminant(expression)
|
|
1206
|
+
value = evaluate_compile_time_const_value(expression, scopes: [])
|
|
1207
|
+
raise_sema_error("when discriminant must be a compile-time constant", expression) if value.nil?
|
|
1208
|
+
|
|
1209
|
+
value
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
def check_inline_for_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1213
|
+
iterable = evaluate_compile_time_const_value(statement.iterables.first, scopes:)
|
|
1214
|
+
raise_sema_error("inline for iterable must be a compile-time constant") unless iterable.is_a?(Array)
|
|
1215
|
+
raise_sema_error("inline for iterable is empty") if iterable.empty?
|
|
1216
|
+
|
|
1217
|
+
loop_var_name = statement.bindings.first.name
|
|
1218
|
+
ensure_non_reserved_primitive_name!(loop_var_name, kind_label: "for binding", line: statement.bindings.first.line, column: statement.bindings.first.column)
|
|
1219
|
+
|
|
1220
|
+
# The loop is unrolled at compile time, so check the body once per element:
|
|
1221
|
+
# each iteration's element type is validated and any per-element
|
|
1222
|
+
# specialization (e.g. `equal[field.type]` for a struct field, or a
|
|
1223
|
+
# `format_value[field.type]` recursion) is discovered by sema rather than
|
|
1224
|
+
# first appearing during lowering.
|
|
1225
|
+
iterable.each do |element|
|
|
1226
|
+
with_nested_scope(scopes) do |loop_scopes|
|
|
1227
|
+
current_actual_scope(loop_scopes)[loop_var_name] = value_binding(
|
|
1228
|
+
name: loop_var_name,
|
|
1229
|
+
type: inline_for_element_type(element),
|
|
1230
|
+
mutable: false,
|
|
1231
|
+
kind: :let,
|
|
1232
|
+
id: @preassigned_local_binding_ids[statement.bindings.first.object_id],
|
|
1233
|
+
const_value: element,
|
|
1234
|
+
)
|
|
1235
|
+
with_loop do
|
|
1236
|
+
with_compile_time do
|
|
1237
|
+
check_block(statement.body, scopes: loop_scopes, return_type:, allow_return:)
|
|
1238
|
+
end
|
|
1239
|
+
end
|
|
1240
|
+
end
|
|
1241
|
+
end
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
def inline_for_element_type(element)
|
|
1245
|
+
if element.is_a?(Types::FieldHandle)
|
|
1246
|
+
builtin_field_handle_type
|
|
1247
|
+
elsif element.is_a?(Types::CallableHandle)
|
|
1248
|
+
builtin_callable_handle_type
|
|
1249
|
+
elsif element.is_a?(Types::AttributeHandle)
|
|
1250
|
+
builtin_attribute_handle_type
|
|
1251
|
+
elsif element.is_a?(Types::MemberHandle)
|
|
1252
|
+
builtin_member_handle_type
|
|
1253
|
+
elsif element.is_a?(Types::StructHandle)
|
|
1254
|
+
builtin_struct_handle_type
|
|
1255
|
+
else
|
|
1256
|
+
@ctx.types.fetch("int")
|
|
1257
|
+
end
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1260
|
+
def check_inline_while_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1261
|
+
condition = evaluate_compile_time_const_value(statement.condition, scopes:)
|
|
1262
|
+
raise_sema_error("inline while condition must be a compile-time constant") if condition.nil?
|
|
1263
|
+
|
|
1264
|
+
with_loop do
|
|
1265
|
+
with_compile_time do
|
|
1266
|
+
check_block(statement.body, scopes:, return_type:, allow_return:)
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
def check_inline_if_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1272
|
+
chosen_branch = statement.branches.find do |branch|
|
|
1273
|
+
condition_value = evaluate_compile_time_const_value(branch.condition, scopes:)
|
|
1274
|
+
raise_sema_error("inline if condition must be a compile-time constant") if condition_value.nil?
|
|
1275
|
+
raise_sema_error("inline if condition must be bool") unless condition_value == true || condition_value == false
|
|
1276
|
+
condition_value
|
|
1277
|
+
end
|
|
1278
|
+
|
|
1279
|
+
if chosen_branch
|
|
1280
|
+
with_compile_time do
|
|
1281
|
+
check_block(chosen_branch.body, scopes:, return_type:, allow_return:)
|
|
1282
|
+
end
|
|
1283
|
+
elsif statement.else_body
|
|
1284
|
+
with_compile_time do
|
|
1285
|
+
check_block(statement.else_body, scopes:, return_type:, allow_return:)
|
|
1286
|
+
end
|
|
1287
|
+
end
|
|
1288
|
+
end
|
|
1289
|
+
|
|
1290
|
+
def check_inline_match_stmt(statement, scopes:, return_type:, allow_return:)
|
|
1291
|
+
scrutinee = evaluate_compile_time_const_value(statement.expression, scopes:)
|
|
1292
|
+
raise_sema_error("inline match scrutinee must be a compile-time constant") if scrutinee.nil?
|
|
1293
|
+
|
|
1294
|
+
chosen_arm = statement.arms.find do |arm|
|
|
1295
|
+
pattern_value = evaluate_compile_time_const_value(arm.pattern, scopes:)
|
|
1296
|
+
scrutinee == pattern_value
|
|
1297
|
+
end
|
|
1298
|
+
|
|
1299
|
+
if chosen_arm
|
|
1300
|
+
with_compile_time do
|
|
1301
|
+
check_block(chosen_arm.body, scopes:, return_type:, allow_return:)
|
|
1302
|
+
end
|
|
1303
|
+
end
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
end
|
|
1307
|
+
end
|
|
1308
|
+
end
|