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,355 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class SemanticAnalyzer
|
|
5
|
+
class Checker
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def current_actual_scope(scopes)
|
|
9
|
+
scopes.reverse_each do |scope|
|
|
10
|
+
return scope unless scope.is_a?(FlowScope)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
raise_sema_error("missing lexical scope")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def apply_continuation_refinements!(scopes, refinements)
|
|
17
|
+
return if refinements.nil? || refinements.empty?
|
|
18
|
+
|
|
19
|
+
scopes.replace(scopes_with_refinements(scopes, refinements))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def scopes_with_refinements(scopes, refinements)
|
|
23
|
+
return scopes if refinements.nil? || refinements.empty?
|
|
24
|
+
|
|
25
|
+
base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes
|
|
26
|
+
merged_refinements = if scopes.last.is_a?(FlowScope)
|
|
27
|
+
scopes.last.each_with_object({}) do |(name, binding), result|
|
|
28
|
+
result[name] = if name.is_a?(String) && binding.respond_to?(:type)
|
|
29
|
+
binding.type
|
|
30
|
+
else
|
|
31
|
+
binding
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
{}
|
|
36
|
+
end
|
|
37
|
+
merged_refinements = merge_refinements(merged_refinements, refinements)
|
|
38
|
+
flow_scope = FlowScope.new
|
|
39
|
+
|
|
40
|
+
merged_refinements.each do |name, refined_type|
|
|
41
|
+
unless name.is_a?(String)
|
|
42
|
+
flow_scope[name] = refined_type
|
|
43
|
+
next
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
binding = lookup_value(name, base_scopes)
|
|
47
|
+
next unless binding
|
|
48
|
+
|
|
49
|
+
flow_scope[name] = binding.with_flow_type(refined_type)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
return base_scopes if flow_scope.empty?
|
|
53
|
+
|
|
54
|
+
base_scopes + [flow_scope]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def merge_refinements(existing, incoming)
|
|
58
|
+
merged = existing.dup
|
|
59
|
+
incoming.each do |name, refined_type|
|
|
60
|
+
if merged.key?(name) && merged[name] != refined_type
|
|
61
|
+
merged.delete(name)
|
|
62
|
+
else
|
|
63
|
+
merged[name] = refined_type
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
merged
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def flow_refinements(expression, truthy:, scopes:)
|
|
71
|
+
case expression
|
|
72
|
+
when AST::Call
|
|
73
|
+
if truthy && has_attribute_refinement_call?(expression)
|
|
74
|
+
key = attribute_presence_key_from_call(expression, scopes:)
|
|
75
|
+
return key ? { key => true } : {}
|
|
76
|
+
end
|
|
77
|
+
when AST::UnaryOp
|
|
78
|
+
return flow_refinements(expression.operand, truthy: !truthy, scopes:) if expression.operator == "not"
|
|
79
|
+
when AST::BinaryOp
|
|
80
|
+
case expression.operator
|
|
81
|
+
when "and"
|
|
82
|
+
if truthy
|
|
83
|
+
left_truthy = flow_refinements(expression.left, truthy: true, scopes:)
|
|
84
|
+
right_scopes = scopes_with_refinements(scopes, left_truthy)
|
|
85
|
+
right_truthy = flow_refinements(expression.right, truthy: true, scopes: right_scopes)
|
|
86
|
+
return merge_refinements(left_truthy, right_truthy)
|
|
87
|
+
end
|
|
88
|
+
when "or"
|
|
89
|
+
unless truthy
|
|
90
|
+
left_falsy = flow_refinements(expression.left, truthy: false, scopes:)
|
|
91
|
+
right_scopes = scopes_with_refinements(scopes, left_falsy)
|
|
92
|
+
right_falsy = flow_refinements(expression.right, truthy: false, scopes: right_scopes)
|
|
93
|
+
return merge_refinements(left_falsy, right_falsy)
|
|
94
|
+
end
|
|
95
|
+
when "==", "!="
|
|
96
|
+
return null_test_refinements(expression, truthy:, scopes:)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
{}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def start_local_completion_frame(binding, scopes)
|
|
104
|
+
frame = {
|
|
105
|
+
function_name: binding.name,
|
|
106
|
+
receiver_type: binding.type.receiver_type,
|
|
107
|
+
snapshots: [],
|
|
108
|
+
}
|
|
109
|
+
@active_local_completion_stack << frame
|
|
110
|
+
record_local_completion_snapshot(binding.ast.respond_to?(:line) ? binding.ast.line : nil, 0, scopes)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def finish_local_completion_frame(binding)
|
|
114
|
+
return if @active_local_completion_stack.empty?
|
|
115
|
+
|
|
116
|
+
frame = @active_local_completion_stack.pop
|
|
117
|
+
snapshots = frame[:snapshots]
|
|
118
|
+
if snapshots.empty?
|
|
119
|
+
return
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
start_line = [binding.ast.respond_to?(:line) ? binding.ast.line : nil, snapshots.first.line].compact.min
|
|
123
|
+
end_line = snapshots.last.line
|
|
124
|
+
|
|
125
|
+
@local_completion_frames << LocalCompletionFrame.new(
|
|
126
|
+
start_line:,
|
|
127
|
+
end_line:,
|
|
128
|
+
function_name: frame[:function_name],
|
|
129
|
+
receiver_type: frame[:receiver_type],
|
|
130
|
+
snapshots: snapshots.freeze,
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def record_local_completion_snapshot(line, column, scopes)
|
|
135
|
+
return if @active_local_completion_stack.empty?
|
|
136
|
+
return if line.nil?
|
|
137
|
+
|
|
138
|
+
snapshot = LocalCompletionSnapshot.new(
|
|
139
|
+
line:,
|
|
140
|
+
column: (column || 0),
|
|
141
|
+
bindings: merged_scope_bindings(scopes).freeze,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
snapshots = @active_local_completion_stack.last[:snapshots]
|
|
145
|
+
prev = snapshots.last
|
|
146
|
+
if prev && prev.line == snapshot.line && prev.column == snapshot.column
|
|
147
|
+
snapshots[-1] = snapshot
|
|
148
|
+
else
|
|
149
|
+
snapshots << snapshot
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def merged_scope_bindings(scopes)
|
|
154
|
+
scopes.each_with_object({}) do |scope, bindings|
|
|
155
|
+
scope.each do |name, binding|
|
|
156
|
+
next unless name.is_a?(String)
|
|
157
|
+
|
|
158
|
+
bindings[name] = binding
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def has_attribute_refinement_call?(expression)
|
|
164
|
+
expression.callee.is_a?(AST::Identifier) && expression.callee.name == "has_attribute" && expression.arguments.length == 2 && expression.arguments.none?(&:name)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def attribute_presence_key_from_call(expression, scopes:)
|
|
168
|
+
target = resolve_reflection_target_argument(expression.arguments.first.value, scopes:)
|
|
169
|
+
binding = resolve_attribute_name_argument(expression.arguments[1].value)
|
|
170
|
+
validate_attribute_target_compatibility!(target, binding)
|
|
171
|
+
attribute_presence_refinement_key(target, binding)
|
|
172
|
+
rescue SemanticError
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def statement_end_line(statement)
|
|
177
|
+
return nil unless statement
|
|
178
|
+
|
|
179
|
+
lines = [statement.respond_to?(:line) ? statement.line : nil]
|
|
180
|
+
|
|
181
|
+
case statement
|
|
182
|
+
when AST::ErrorBlockStmt
|
|
183
|
+
lines.concat(statement_list_lines(statement.body))
|
|
184
|
+
when AST::LocalDecl
|
|
185
|
+
lines << expression_end_line(statement.value) if statement.value
|
|
186
|
+
lines.concat(statement_list_lines(statement.else_body)) if statement.else_body
|
|
187
|
+
when AST::IfStmt
|
|
188
|
+
statement.branches.each do |branch|
|
|
189
|
+
lines << expression_end_line(branch.condition)
|
|
190
|
+
lines.concat(statement_list_lines(branch.body))
|
|
191
|
+
end
|
|
192
|
+
lines.concat(statement_list_lines(statement.else_body)) if statement.else_body
|
|
193
|
+
when AST::UnsafeStmt, AST::ForStmt, AST::WhileStmt
|
|
194
|
+
lines.concat(statement_list_lines(statement.body))
|
|
195
|
+
when AST::MatchStmt
|
|
196
|
+
statement.arms.each do |arm|
|
|
197
|
+
lines.concat(statement_list_lines(arm.body))
|
|
198
|
+
end
|
|
199
|
+
when AST::DeferStmt
|
|
200
|
+
lines.concat(statement_list_lines(statement.body)) if statement.body
|
|
201
|
+
when AST::Assignment
|
|
202
|
+
lines << expression_end_line(statement.value)
|
|
203
|
+
when AST::ReturnStmt
|
|
204
|
+
lines << expression_end_line(statement.value)
|
|
205
|
+
when AST::ExpressionStmt
|
|
206
|
+
lines << expression_end_line(statement.expression)
|
|
207
|
+
when AST::StaticAssert
|
|
208
|
+
lines << expression_end_line(statement.condition)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
lines.compact.max
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def statement_list_lines(statements)
|
|
215
|
+
return [] unless statements
|
|
216
|
+
|
|
217
|
+
statements.each_with_object([]) do |stmt, lines|
|
|
218
|
+
end_line = statement_end_line(stmt)
|
|
219
|
+
lines << end_line if end_line
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def expression_end_line(node)
|
|
224
|
+
return nil unless node
|
|
225
|
+
|
|
226
|
+
lines = [node.respond_to?(:line) ? node.line : nil]
|
|
227
|
+
|
|
228
|
+
case node
|
|
229
|
+
when AST::MemberAccess
|
|
230
|
+
lines << expression_end_line(node.receiver)
|
|
231
|
+
when AST::IndexAccess
|
|
232
|
+
lines << expression_end_line(node.receiver)
|
|
233
|
+
lines << expression_end_line(node.index)
|
|
234
|
+
when AST::Specialization
|
|
235
|
+
lines << expression_end_line(node.callee)
|
|
236
|
+
when AST::Call
|
|
237
|
+
lines << expression_end_line(node.callee)
|
|
238
|
+
node.arguments.each { |argument| lines << expression_end_line(argument.value) }
|
|
239
|
+
when AST::Argument
|
|
240
|
+
lines << expression_end_line(node.value)
|
|
241
|
+
when AST::UnaryOp
|
|
242
|
+
lines << expression_end_line(node.operand)
|
|
243
|
+
when AST::BinaryOp
|
|
244
|
+
lines << expression_end_line(node.left)
|
|
245
|
+
lines << expression_end_line(node.right)
|
|
246
|
+
when AST::IfExpr
|
|
247
|
+
lines << expression_end_line(node.condition)
|
|
248
|
+
lines << expression_end_line(node.then_expression)
|
|
249
|
+
lines << expression_end_line(node.else_expression)
|
|
250
|
+
when AST::MatchExpr
|
|
251
|
+
lines << expression_end_line(node.expression)
|
|
252
|
+
node.arms.each do |arm|
|
|
253
|
+
lines << expression_end_line(arm.pattern)
|
|
254
|
+
lines << expression_end_line(arm.value)
|
|
255
|
+
end
|
|
256
|
+
when AST::AwaitExpr
|
|
257
|
+
lines << expression_end_line(node.expression)
|
|
258
|
+
when AST::FormatExprPart
|
|
259
|
+
lines << expression_end_line(node.expression)
|
|
260
|
+
when AST::PrefixCast
|
|
261
|
+
lines << expression_end_line(node.expression)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
lines.compact.max
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def recovered_for_statement(statement)
|
|
268
|
+
AST::ForStmt.new(
|
|
269
|
+
bindings: Array(statement.header_bindings),
|
|
270
|
+
iterables: Array(statement.header_iterables),
|
|
271
|
+
body: statement.body,
|
|
272
|
+
line: statement.line,
|
|
273
|
+
column: statement.column,
|
|
274
|
+
)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def null_test_refinements(expression, truthy:, scopes:)
|
|
278
|
+
identifier_expression = nil
|
|
279
|
+
if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral)
|
|
280
|
+
identifier_expression = expression.left
|
|
281
|
+
elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier)
|
|
282
|
+
identifier_expression = expression.right
|
|
283
|
+
else
|
|
284
|
+
return {}
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
binding = lookup_value(identifier_expression.name, scopes)
|
|
288
|
+
return {} unless binding&.storage_type.is_a?(Types::Nullable)
|
|
289
|
+
|
|
290
|
+
null_result = expression.operator == "==" ? truthy : !truthy
|
|
291
|
+
refined_type = null_result ? @null_type : binding.storage_type.base
|
|
292
|
+
{ identifier_expression.name => refined_type }
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def conditional_common_type(then_type, else_type, then_expression:, else_expression:)
|
|
296
|
+
return then_type if then_type == else_type
|
|
297
|
+
|
|
298
|
+
numeric_type = common_numeric_type(then_type, else_type)
|
|
299
|
+
return numeric_type if numeric_type
|
|
300
|
+
|
|
301
|
+
if (nullable_type = conditional_null_common_type(then_type, else_type))
|
|
302
|
+
return nullable_type
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
if (nullable_type = conditional_null_common_type(else_type, then_type))
|
|
306
|
+
return nullable_type
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
return then_type if types_compatible?(else_type, then_type, expression: else_expression)
|
|
310
|
+
return else_type if types_compatible?(then_type, else_type, expression: then_expression)
|
|
311
|
+
|
|
312
|
+
nil
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def nullable_candidate?(type)
|
|
316
|
+
return false if ref_type?(type)
|
|
317
|
+
|
|
318
|
+
sized_layout_type?(type) || pointer_type?(type) || type.is_a?(Types::Nullable)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def conditional_null_common_type(null_type, other_type)
|
|
322
|
+
return unless null_type.is_a?(Types::Null)
|
|
323
|
+
|
|
324
|
+
if other_type.is_a?(Types::Nullable)
|
|
325
|
+
return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base
|
|
326
|
+
|
|
327
|
+
return nil
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
return unless nullable_candidate?(other_type)
|
|
331
|
+
return if null_type.target_type && null_type.target_type != other_type
|
|
332
|
+
|
|
333
|
+
Types::Registry.nullable(other_type)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def describe_expression(expression)
|
|
337
|
+
case expression
|
|
338
|
+
when AST::Identifier
|
|
339
|
+
expression.name
|
|
340
|
+
when AST::MemberAccess
|
|
341
|
+
"#{describe_expression(expression.receiver)}.#{expression.member}"
|
|
342
|
+
when AST::IndexAccess
|
|
343
|
+
"#{describe_expression(expression.receiver)}[...]"
|
|
344
|
+
when AST::Specialization
|
|
345
|
+
"#{describe_expression(expression.callee)}[...]"
|
|
346
|
+
when AST::FormatString
|
|
347
|
+
'f"..."'
|
|
348
|
+
else
|
|
349
|
+
expression.class.name.split("::").last
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class SemanticAnalyzer::Checker
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def validate_consuming_foreign_parameter!(type, function_name:, parameter_name:)
|
|
8
|
+
if type.is_a?(Types::Nullable) || !(opaque_type?(type) || pointer_type?(type))
|
|
9
|
+
raise_sema_error("consuming parameter #{parameter_name} of #{function_name} must use a non-null opaque or ptr[...] type")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def foreign_boundary_nullable_pointer_like_base?(base)
|
|
14
|
+
return true if base.is_a?(Types::Function) || base.is_a?(Types::Proc)
|
|
15
|
+
return true if opaque_type?(base)
|
|
16
|
+
return true if base == @ctx.types.fetch("cstr")
|
|
17
|
+
|
|
18
|
+
pointer_type?(base) || const_pointer_type?(base) || ref_type?(base)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reject_foreign_nullable_value_type!(type, function_name:, parameter_name:)
|
|
22
|
+
return unless type.is_a?(Types::Nullable)
|
|
23
|
+
return if foreign_boundary_nullable_pointer_like_base?(type.base)
|
|
24
|
+
|
|
25
|
+
location = parameter_name ? "parameter #{parameter_name}" : "return type"
|
|
26
|
+
raise_sema_error("#{location} of foreign/external function #{function_name} cannot use nullable value type #{type}; nullable at an FFI boundary is only supported for pointer-like types (use ptr[...]? or pass an explicit struct)")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def foreign_cstr_boundary_parameter?(parameter)
|
|
30
|
+
parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def foreign_cstr_argument_compatible?(actual_type, parameter, expression:)
|
|
34
|
+
types_compatible?(actual_type, parameter.type, expression:) || actual_type == @ctx.types.fetch("cstr")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints)
|
|
38
|
+
return resolve_type_ref(param.boundary_type, type_params:, type_param_constraints:) if param.boundary_type
|
|
39
|
+
return const_pointer_to(public_type) if param.mode == :in
|
|
40
|
+
return pointer_to(foreign_slot_boundary_value_type(public_type)) if [:out, :inout].include?(param.mode)
|
|
41
|
+
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def foreign_slot_boundary_value_type(public_type)
|
|
46
|
+
if public_type.is_a?(Types::Nullable) && pointer_type?(public_type.base)
|
|
47
|
+
return public_type.base
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
public_type
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:)
|
|
54
|
+
unless const_pointer_type?(boundary_type)
|
|
55
|
+
raise_sema_error("in parameter #{parameter_name} of #{function_name} must lower to const_ptr[...], got #{boundary_type || public_type}")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
expected_public_type = pointee_type(boundary_type)
|
|
59
|
+
return if expected_public_type == public_type
|
|
60
|
+
return if expected_public_type == @ctx.types.fetch("void")
|
|
61
|
+
return if foreign_identity_projection_compatible?(public_type, expected_public_type)
|
|
62
|
+
|
|
63
|
+
raise_sema_error("in parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def foreign_mapping_public_alias_name(name)
|
|
67
|
+
"#{name}_public"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:)
|
|
71
|
+
return if boundary_type == public_type
|
|
72
|
+
return if boundary_type == @ctx.types.fetch("cstr") && public_type == @ctx.types.fetch("str")
|
|
73
|
+
return if foreign_span_boundary_compatible?(public_type, boundary_type)
|
|
74
|
+
return if foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type)
|
|
75
|
+
return if foreign_identity_projection_compatible?(public_type, boundary_type)
|
|
76
|
+
|
|
77
|
+
raise_sema_error("foreign parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def foreign_function_binding?(binding)
|
|
81
|
+
binding.ast.is_a?(AST::ForeignFunctionDecl)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def foreign_mapping_expression(decl)
|
|
85
|
+
return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping)
|
|
86
|
+
|
|
87
|
+
AST::Call.new(
|
|
88
|
+
callee: decl.mapping,
|
|
89
|
+
arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) },
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def foreign_mapping_auto_call_shorthand?(expression)
|
|
94
|
+
case expression
|
|
95
|
+
when AST::Identifier
|
|
96
|
+
true
|
|
97
|
+
when AST::MemberAccess
|
|
98
|
+
foreign_mapping_auto_call_shorthand?(expression.receiver)
|
|
99
|
+
when AST::Specialization
|
|
100
|
+
foreign_mapping_auto_call_shorthand?(expression.callee)
|
|
101
|
+
else
|
|
102
|
+
false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def foreign_argument_expression(argument)
|
|
107
|
+
if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
|
|
108
|
+
argument.value.operand
|
|
109
|
+
else
|
|
110
|
+
argument.value
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def foreign_argument_legacy_passing_mode(argument)
|
|
115
|
+
return nil unless argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
|
|
116
|
+
|
|
117
|
+
argument.value.operator
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type)
|
|
121
|
+
case parameter.passing_mode
|
|
122
|
+
when :plain
|
|
123
|
+
infer_expression(argument.value, scopes:, expected_type:)
|
|
124
|
+
when :consuming
|
|
125
|
+
foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
|
|
126
|
+
parameter.type
|
|
127
|
+
when :in, :out, :inout
|
|
128
|
+
if (legacy_passing_mode = foreign_argument_legacy_passing_mode(argument))
|
|
129
|
+
raise_sema_error("argument #{parameter.name} to #{function_name} must not use #{legacy_passing_mode}; directional passing is declared on #{function_name}")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if parameter.passing_mode == :in
|
|
133
|
+
infer_expression(argument.value, scopes:, expected_type: expected_type)
|
|
134
|
+
else
|
|
135
|
+
infer_lvalue(argument.value, scopes:)
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
raise_sema_error("unsupported foreign passing mode #{parameter.passing_mode}")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
|
|
143
|
+
unless argument.value.is_a?(AST::Identifier)
|
|
144
|
+
raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
binding = lookup_value(argument.value.name, scopes)
|
|
148
|
+
unless binding && %i[let var param].include?(binding.kind) && binding.storage_type.is_a?(Types::Nullable) && binding.storage_type.base == parameter.type
|
|
149
|
+
raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
binding
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|