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,992 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class SemanticAnalyzer
|
|
5
|
+
class Checker
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def resolved_attribute_applications_for_target(target)
|
|
9
|
+
target_id = case target
|
|
10
|
+
when Types::StructHandle then target.declaration.object_id
|
|
11
|
+
when Types::FieldHandle then target.field_declaration.object_id
|
|
12
|
+
when Types::CallableHandle then target.declaration.object_id
|
|
13
|
+
end
|
|
14
|
+
return [] unless target_id
|
|
15
|
+
|
|
16
|
+
applications = @ctx.resolved_attribute_applications[target_id]
|
|
17
|
+
return applications if applications
|
|
18
|
+
|
|
19
|
+
@ctx.imports.each_value do |imported_module|
|
|
20
|
+
applications = imported_module.attribute_applications[target_id]
|
|
21
|
+
return applications if applications
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
[]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_attribute_application(target, binding)
|
|
28
|
+
resolved_attribute_applications_for_target(target).find do |application|
|
|
29
|
+
same_attribute_binding?(application.binding, binding)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def same_attribute_binding?(left, right)
|
|
34
|
+
left.name == right.name && left.module_name == right.module_name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def qualified_attribute_name(binding)
|
|
38
|
+
binding.module_name ? "#{binding.module_name}.#{binding.name}" : binding.name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def attribute_presence_refinement_key(target, binding)
|
|
42
|
+
AttributePresenceKey.new(target, binding.module_name, binding.name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def attribute_presence_guard_active?(scopes, key)
|
|
46
|
+
scopes.any? { |scope| scope.key?(key) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def check_aggregate_construction(struct_type, arguments, scopes:)
|
|
50
|
+
require_unsafe!("str construction requires unsafe") if struct_type.is_a?(Types::StringView)
|
|
51
|
+
|
|
52
|
+
check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "aggregate construction")
|
|
53
|
+
struct_type
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def check_struct_with_call(struct_type, _receiver_expression, arguments, scopes:)
|
|
57
|
+
check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "struct.with()")
|
|
58
|
+
struct_type
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def check_aggregate_field_arguments(struct_type, arguments, scopes:, context:)
|
|
62
|
+
display_name = aggregate_display_name(struct_type)
|
|
63
|
+
|
|
64
|
+
raise_sema_error("#{context} for #{display_name} requires named arguments") unless arguments.all?(&:name)
|
|
65
|
+
|
|
66
|
+
provided = {}
|
|
67
|
+
arguments.each do |argument|
|
|
68
|
+
field_type = struct_type.field(argument.name)
|
|
69
|
+
raise_sema_error("unknown field #{display_name}.#{argument.name}") unless field_type
|
|
70
|
+
raise_sema_error("duplicate field #{display_name}.#{argument.name}") if provided.key?(argument.name)
|
|
71
|
+
|
|
72
|
+
actual_type = infer_expression(argument.value, scopes:, expected_type: field_type)
|
|
73
|
+
ensure_assignable!(
|
|
74
|
+
actual_type,
|
|
75
|
+
field_type,
|
|
76
|
+
"field #{display_name}.#{argument.name} expects #{field_type}, got #{actual_type}",
|
|
77
|
+
expression: argument.value,
|
|
78
|
+
external_numeric: struct_type.respond_to?(:external) && struct_type.external,
|
|
79
|
+
external_pointer_null: struct_type.respond_to?(:external) && struct_type.external,
|
|
80
|
+
contextual_int_to_float: contextual_int_to_float_target?(field_type),
|
|
81
|
+
)
|
|
82
|
+
provided[argument.name] = true
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def check_variant_arm_construction(callable, arguments, scopes:)
|
|
87
|
+
variant_type, arm_name = callable
|
|
88
|
+
fields = variant_type.arm(arm_name)
|
|
89
|
+
|
|
90
|
+
if fields.nil? || fields.empty?
|
|
91
|
+
raise_sema_error("variant arm #{variant_type}.#{arm_name} has no payload; construct it without arguments") unless arguments.empty?
|
|
92
|
+
|
|
93
|
+
return variant_type
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
raise_sema_error("variant arm construction requires named arguments") unless arguments.all?(&:name)
|
|
97
|
+
|
|
98
|
+
provided = {}
|
|
99
|
+
arguments.each do |argument|
|
|
100
|
+
field_type = fields[argument.name]
|
|
101
|
+
raise_sema_error("unknown field #{variant_type}.#{arm_name}.#{argument.name}") unless field_type
|
|
102
|
+
raise_sema_error("duplicate field #{variant_type}.#{arm_name}.#{argument.name}") if provided.key?(argument.name)
|
|
103
|
+
|
|
104
|
+
actual_type = infer_expression(argument.value, scopes:, expected_type: field_type)
|
|
105
|
+
ensure_assignable!(
|
|
106
|
+
actual_type,
|
|
107
|
+
field_type,
|
|
108
|
+
"field #{variant_type}.#{arm_name}.#{argument.name} expects #{field_type}, got #{actual_type}",
|
|
109
|
+
expression: argument.value,
|
|
110
|
+
contextual_int_to_float: contextual_int_to_float_target?(field_type),
|
|
111
|
+
)
|
|
112
|
+
provided[argument.name] = true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
missing = fields.keys - provided.keys
|
|
116
|
+
missing.reject! { |name| fields[name].void? }
|
|
117
|
+
raise_sema_error("variant arm #{variant_type}.#{arm_name} is missing fields: #{missing.join(', ')}") unless missing.empty?
|
|
118
|
+
|
|
119
|
+
variant_type
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_array_construction(array_type, arguments, scopes:)
|
|
123
|
+
raise_sema_error("array construction does not support named arguments") if arguments.any?(&:name)
|
|
124
|
+
|
|
125
|
+
element_type = array_element_type(array_type)
|
|
126
|
+
length = array_length(array_type)
|
|
127
|
+
raise_sema_error("array expects at most #{length} elements, got #{arguments.length}") if arguments.length > length
|
|
128
|
+
|
|
129
|
+
arguments.each do |argument|
|
|
130
|
+
actual_type = infer_expression(argument.value, scopes:, expected_type: element_type)
|
|
131
|
+
ensure_assignable!(
|
|
132
|
+
actual_type,
|
|
133
|
+
element_type,
|
|
134
|
+
"array element expects #{element_type}, got #{actual_type}",
|
|
135
|
+
expression: argument.value,
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
array_type
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def check_cast_call(target_type, arguments, scopes:)
|
|
143
|
+
raise_sema_error("cast requires exactly one argument") unless arguments.length == 1
|
|
144
|
+
raise_sema_error("cast does not support named arguments") if arguments.first.name
|
|
145
|
+
|
|
146
|
+
source_type = infer_expression(arguments.first.value, scopes:)
|
|
147
|
+
if source_type == target_type
|
|
148
|
+
return target_type
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
if pointer_cast?(source_type, target_type)
|
|
152
|
+
expression = arguments.first.value
|
|
153
|
+
require_unsafe!("pointer cast requires unsafe", line: source_line(expression), column: source_column(expression))
|
|
154
|
+
|
|
155
|
+
return target_type
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if ref_to_pointer_cast?(source_type, target_type)
|
|
159
|
+
expression = arguments.first.value
|
|
160
|
+
require_unsafe!("ref to pointer cast requires unsafe", line: source_line(expression), column: source_column(expression))
|
|
161
|
+
|
|
162
|
+
return target_type
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
source_numeric_type = cast_numeric_type(source_type)
|
|
166
|
+
target_numeric_type = cast_numeric_type(target_type)
|
|
167
|
+
|
|
168
|
+
unless source_numeric_type && target_numeric_type
|
|
169
|
+
raise_sema_error("cast requires compatible types, got #{source_type} -> #{target_type}")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
target_type
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def cast_numeric_type(type)
|
|
176
|
+
return type if type.is_a?(Types::Primitive) && (type.numeric? || type.name == "bool")
|
|
177
|
+
return type.backing_type if type.is_a?(Types::EnumBase) && type.backing_type.numeric?
|
|
178
|
+
return type if char_type?(type)
|
|
179
|
+
return type.backing_type if type.is_a?(Types::EnumBase) && char_type?(type.backing_type)
|
|
180
|
+
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def infer_null_literal(expression)
|
|
185
|
+
return @null_type unless expression.type
|
|
186
|
+
|
|
187
|
+
target_type = resolve_type_ref(expression.type)
|
|
188
|
+
unless typed_null_target_type?(target_type)
|
|
189
|
+
raise_sema_error("typed null requires pointer-like type, got #{target_type}")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
Types::Null.new(target_type)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def check_reinterpret_call(target_type, arguments, scopes:)
|
|
196
|
+
raise_sema_error("reinterpret requires exactly one argument") unless arguments.length == 1
|
|
197
|
+
raise_sema_error("reinterpret does not support named arguments") if arguments.first.name
|
|
198
|
+
require_unsafe!("reinterpret requires unsafe")
|
|
199
|
+
|
|
200
|
+
source_type = infer_expression(arguments.first.value, scopes:)
|
|
201
|
+
unless reinterpretable_type?(source_type) && reinterpretable_type?(target_type)
|
|
202
|
+
raise_sema_error("reinterpret requires non-array concrete sized types, got #{source_type} -> #{target_type}")
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
source_size = CompileTime::Layout.size_of(source_type)
|
|
206
|
+
target_size = CompileTime::Layout.size_of(target_type)
|
|
207
|
+
if source_size && target_size && source_size != target_size
|
|
208
|
+
raise_sema_error("reinterpret requires equal-size types, got #{source_type} (#{source_size} byte#{source_size == 1 ? '' : 's'}) -> #{target_type} (#{target_size} byte#{target_size == 1 ? '' : 's'})")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
target_type
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def check_zero_call(target_type, arguments, expected_type: nil, operation: "zero")
|
|
215
|
+
raise_sema_error("#{operation} expects 0 arguments, got #{arguments.length}") unless arguments.empty?
|
|
216
|
+
|
|
217
|
+
zero_initializable_type?(target_type, operation:)
|
|
218
|
+
if expected_type.is_a?(Types::Nullable) && typed_null_target_type?(expected_type.base) && types_compatible?(target_type, expected_type.base)
|
|
219
|
+
raise_sema_error("use null instead of #{operation}[#{target_type}] in nullable pointer-like context #{expected_type}")
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
target_type
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def resolve_default_specialization(callee)
|
|
226
|
+
raise_sema_error("default requires exactly one type argument") unless callee.arguments.length == 1
|
|
227
|
+
|
|
228
|
+
type_arg = callee.arguments.first.value
|
|
229
|
+
raise_sema_error("default type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
|
|
230
|
+
|
|
231
|
+
target_type = resolve_type_ref(type_arg)
|
|
232
|
+
binding = resolve_explicit_default_binding(target_type, context: "default[#{target_type}]")
|
|
233
|
+
raise_sema_error("default[#{target_type}] requires associated function #{target_type}.default()") unless binding
|
|
234
|
+
|
|
235
|
+
DefaultResolution.new(
|
|
236
|
+
target_type:,
|
|
237
|
+
binding:,
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def resolve_hash_specialization(callee)
|
|
242
|
+
raise_sema_error("hash requires exactly one type argument") unless callee.arguments.length == 1
|
|
243
|
+
|
|
244
|
+
type_arg = callee.arguments.first.value
|
|
245
|
+
raise_sema_error("hash type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
|
|
246
|
+
|
|
247
|
+
target_type = resolve_type_ref(type_arg)
|
|
248
|
+
binding = resolve_explicit_hash_binding(target_type, context: "hash[#{target_type}]")
|
|
249
|
+
raise_sema_error("hash[#{target_type}] requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint") unless binding
|
|
250
|
+
|
|
251
|
+
HashResolution.new(
|
|
252
|
+
target_type:,
|
|
253
|
+
binding:,
|
|
254
|
+
)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def resolve_equal_specialization(callee)
|
|
258
|
+
raise_sema_error("equal requires exactly one type argument") unless callee.arguments.length == 1
|
|
259
|
+
|
|
260
|
+
type_arg = callee.arguments.first.value
|
|
261
|
+
raise_sema_error("equal type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
|
|
262
|
+
|
|
263
|
+
target_type = resolve_type_ref(type_arg)
|
|
264
|
+
binding = resolve_explicit_equal_binding(target_type, context: "equal[#{target_type}]")
|
|
265
|
+
raise_sema_error("equal[#{target_type}] requires associated function #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool") unless binding
|
|
266
|
+
|
|
267
|
+
EqualResolution.new(
|
|
268
|
+
target_type:,
|
|
269
|
+
binding:,
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def resolve_order_specialization(callee)
|
|
274
|
+
raise_sema_error("order requires exactly one type argument") unless callee.arguments.length == 1
|
|
275
|
+
|
|
276
|
+
type_arg = callee.arguments.first.value
|
|
277
|
+
raise_sema_error("order type argument must be a type") unless type_arg.is_a?(AST::TypeRef)
|
|
278
|
+
|
|
279
|
+
target_type = resolve_type_ref(type_arg)
|
|
280
|
+
binding = resolve_explicit_order_binding(target_type, context: "order[#{target_type}]")
|
|
281
|
+
raise_sema_error("order[#{target_type}] requires associated function #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int") unless binding
|
|
282
|
+
|
|
283
|
+
OrderResolution.new(
|
|
284
|
+
target_type:,
|
|
285
|
+
binding:,
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def resolve_explicit_default_binding(target_type, context:)
|
|
290
|
+
requirement_message = "#{context} requires associated function #{target_type}.default()"
|
|
291
|
+
resolve_explicit_associated_binding(target_type, "default", requirement_message:) do |method|
|
|
292
|
+
raise_sema_error("#{context} requires #{target_type}.default() to take 0 arguments") unless method.type.params.empty?
|
|
293
|
+
unless types_compatible?(method.type.return_type, target_type)
|
|
294
|
+
raise_sema_error("#{context} requires #{target_type}.default() to return #{target_type}, got #{method.type.return_type}")
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def resolve_explicit_hash_binding(target_type, context:)
|
|
300
|
+
requirement_message = "#{context} requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint"
|
|
301
|
+
resolve_explicit_associated_binding(target_type, "hash", requirement_message:) do |method|
|
|
302
|
+
unless method.type.params.map(&:type) == [const_pointer_to(target_type)]
|
|
303
|
+
raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint")
|
|
304
|
+
end
|
|
305
|
+
unless method.type.return_type == @ctx.types.fetch("uint")
|
|
306
|
+
raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint, got #{method.type.return_type}")
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def resolve_explicit_equal_binding(target_type, context:)
|
|
312
|
+
requirement_message = "#{context} requires associated function #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool"
|
|
313
|
+
resolve_explicit_associated_binding(target_type, "equal", requirement_message:) do |method|
|
|
314
|
+
expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)]
|
|
315
|
+
unless method.type.params.map(&:type) == expected_param_types
|
|
316
|
+
raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool")
|
|
317
|
+
end
|
|
318
|
+
unless method.type.return_type == @ctx.types.fetch("bool")
|
|
319
|
+
raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool, got #{method.type.return_type}")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def resolve_explicit_order_binding(target_type, context:)
|
|
325
|
+
requirement_message = "#{context} requires associated function #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int"
|
|
326
|
+
resolve_explicit_associated_binding(target_type, "order", requirement_message:) do |method|
|
|
327
|
+
expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)]
|
|
328
|
+
unless method.type.params.map(&:type) == expected_param_types
|
|
329
|
+
raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int")
|
|
330
|
+
end
|
|
331
|
+
unless method.type.return_type == @ctx.types.fetch("int")
|
|
332
|
+
raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int, got #{method.type.return_type}")
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def resolve_explicit_format_binding(target_type, context:)
|
|
338
|
+
length_binding = resolve_explicit_format_len_binding(target_type, context:)
|
|
339
|
+
append_binding = resolve_explicit_format_append_binding(target_type, context:)
|
|
340
|
+
|
|
341
|
+
return [length_binding, append_binding] if length_binding && append_binding
|
|
342
|
+
|
|
343
|
+
if length_binding || append_binding
|
|
344
|
+
raise_sema_error("#{context} requires methods #{target_type}.format_len() -> ptr_uint and #{target_type}.append_format(output: ref[std.string.String]) -> void")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
nil
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def resolve_explicit_format_len_binding(target_type, context:)
|
|
351
|
+
requirement_message = "#{context} requires method #{target_type}.format_len() -> ptr_uint"
|
|
352
|
+
resolve_explicit_instance_binding(target_type, "format_len", requirement_message:) do |method|
|
|
353
|
+
raise_sema_error("#{context} requires #{target_type}.format_len() to take 0 arguments") unless method.type.params.empty?
|
|
354
|
+
raise_sema_error("#{context} requires #{target_type}.format_len() to be non-editable") if method.type.receiver_editable
|
|
355
|
+
unless method.type.return_type == @ctx.types.fetch("ptr_uint")
|
|
356
|
+
raise_sema_error("#{context} requires #{target_type}.format_len() -> ptr_uint, got #{method.type.return_type}")
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def resolve_explicit_format_append_binding(target_type, context:)
|
|
362
|
+
requirement_message = "#{context} requires method #{target_type}.append_format(output: ref[std.string.String]) -> void"
|
|
363
|
+
resolve_explicit_instance_binding(target_type, "append_format", requirement_message:) do |method|
|
|
364
|
+
raise_sema_error("#{context} requires #{target_type}.append_format() to be non-editable") if method.type.receiver_editable
|
|
365
|
+
unless method.type.params.length == 1 && string_builder_ref_type?(method.type.params.first.type)
|
|
366
|
+
raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void")
|
|
367
|
+
end
|
|
368
|
+
unless method.type.return_type == @ctx.types.fetch("void")
|
|
369
|
+
raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void, got #{method.type.return_type}")
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def resolve_explicit_associated_binding(target_type, method_name, requirement_message:)
|
|
375
|
+
method = lookup_static_method(target_type, method_name)
|
|
376
|
+
if method
|
|
377
|
+
raise_sema_error(requirement_message) unless method.type.receiver_type.nil?
|
|
378
|
+
|
|
379
|
+
method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any?
|
|
380
|
+
yield method
|
|
381
|
+
|
|
382
|
+
return method
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
if (imported_module = imported_module_with_private_method(target_type, method_name))
|
|
386
|
+
raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}")
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
nil
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def resolve_explicit_instance_binding(target_type, method_name, requirement_message:)
|
|
393
|
+
method = lookup_method(target_type, method_name)
|
|
394
|
+
if method
|
|
395
|
+
raise_sema_error(requirement_message) if method.type.receiver_type.nil?
|
|
396
|
+
|
|
397
|
+
method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any?
|
|
398
|
+
yield method
|
|
399
|
+
|
|
400
|
+
return method
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
if (imported_module = imported_module_with_private_method(target_type, method_name))
|
|
404
|
+
raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}")
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
nil
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def check_hash_call(resolution, arguments, scopes:)
|
|
411
|
+
raise_sema_error("hash does not support named arguments") if arguments.any?(&:name)
|
|
412
|
+
raise_sema_error("hash expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
413
|
+
|
|
414
|
+
validate_hash_operation_argument!(arguments.first.value, resolution.target_type, scopes:, operation: "hash")
|
|
415
|
+
@ctx.types.fetch("uint")
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def check_equal_call(resolution, arguments, scopes:)
|
|
419
|
+
raise_sema_error("equal does not support named arguments") if arguments.any?(&:name)
|
|
420
|
+
raise_sema_error("equal expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
421
|
+
|
|
422
|
+
arguments.each do |argument|
|
|
423
|
+
validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "equal")
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
@ctx.types.fetch("bool")
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def check_order_call(resolution, arguments, scopes:)
|
|
430
|
+
raise_sema_error("order does not support named arguments") if arguments.any?(&:name)
|
|
431
|
+
raise_sema_error("order expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
432
|
+
|
|
433
|
+
arguments.each do |argument|
|
|
434
|
+
validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "order")
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
@ctx.types.fetch("int")
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def validate_hash_operation_argument!(expression, target_type, scopes:, operation:)
|
|
441
|
+
actual_type = infer_expression(expression, scopes:)
|
|
442
|
+
expected_pointer_type = const_pointer_to(target_type)
|
|
443
|
+
return if argument_types_compatible?(actual_type, expected_pointer_type, external: false, expression:, scopes:)
|
|
444
|
+
return if ref_type?(actual_type) && types_compatible?(referenced_type(actual_type), target_type, expression:, scopes:)
|
|
445
|
+
return if safe_reference_source_expression?(expression, scopes:) && types_compatible?(actual_type, target_type, expression:, scopes:)
|
|
446
|
+
|
|
447
|
+
raise_sema_error("#{operation}[#{target_type}] expects a safe #{target_type} lvalue, ref[#{target_type}], ptr[#{target_type}], or const_ptr[#{target_type}], got #{actual_type}")
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def check_str_buffer_method_call(kind, receiver, arguments, scopes:)
|
|
451
|
+
method_name = str_buffer_method_name(kind)
|
|
452
|
+
receiver_type = infer_expression(receiver, scopes:)
|
|
453
|
+
raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless str_buffer_type?(receiver_type)
|
|
454
|
+
|
|
455
|
+
case kind
|
|
456
|
+
when :str_buffer_clear, :str_buffer_len, :str_buffer_capacity, :str_buffer_as_str, :str_buffer_as_cstr
|
|
457
|
+
raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
|
|
458
|
+
raise_sema_error("#{method_name} expects 0 arguments, got #{arguments.length}") unless arguments.empty?
|
|
459
|
+
when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format
|
|
460
|
+
raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
|
|
461
|
+
raise_sema_error("#{method_name} expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
462
|
+
else
|
|
463
|
+
raise_sema_error("unsupported str_buffer method #{kind}")
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
case kind
|
|
467
|
+
when :str_buffer_clear
|
|
468
|
+
record_editable_receiver_expression(receiver)
|
|
469
|
+
raise_sema_error("cannot call editable method #{receiver_type}.clear on an immutable receiver") unless assignable_receiver?(receiver, scopes)
|
|
470
|
+
|
|
471
|
+
@ctx.types.fetch("void")
|
|
472
|
+
when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format
|
|
473
|
+
record_editable_receiver_expression(receiver)
|
|
474
|
+
raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless assignable_receiver?(receiver, scopes)
|
|
475
|
+
|
|
476
|
+
actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str"))
|
|
477
|
+
ensure_argument_assignable!(
|
|
478
|
+
actual_type,
|
|
479
|
+
@ctx.types.fetch("str"),
|
|
480
|
+
external: false,
|
|
481
|
+
message: "argument value to #{receiver_type}.#{method_name} expects str, got #{actual_type}",
|
|
482
|
+
expression: arguments.first.value,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
@ctx.types.fetch("void")
|
|
486
|
+
when :str_buffer_len, :str_buffer_capacity
|
|
487
|
+
@ctx.types.fetch("ptr_uint")
|
|
488
|
+
when :str_buffer_as_str
|
|
489
|
+
raise_sema_error("#{receiver_type}.as_str requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:)
|
|
490
|
+
|
|
491
|
+
@ctx.types.fetch("str")
|
|
492
|
+
when :str_buffer_as_cstr
|
|
493
|
+
raise_sema_error("#{receiver_type}.as_cstr requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:)
|
|
494
|
+
|
|
495
|
+
@ctx.types.fetch("cstr")
|
|
496
|
+
else
|
|
497
|
+
raise_sema_error("unsupported str_buffer method #{kind}")
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def check_event_method_call(kind, receiver, arguments, scopes:)
|
|
502
|
+
method_name = event_method_name(kind)
|
|
503
|
+
receiver_type = infer_expression(receiver, scopes:)
|
|
504
|
+
raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless event_type?(receiver_type)
|
|
505
|
+
|
|
506
|
+
raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name)
|
|
507
|
+
raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless event_receiver_mutable?(receiver, scopes:)
|
|
508
|
+
if kind == :event_emit
|
|
509
|
+
raise_sema_error("#{receiver_type}.emit is only available inside module #{receiver_type.module_name}") unless receiver_type.module_name == @ctx.module_name
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
case kind
|
|
513
|
+
when :event_subscribe, :event_subscribe_once
|
|
514
|
+
if arguments.length == 2 && arguments.none?(&:name)
|
|
515
|
+
state_type = infer_expression(arguments[0].value, scopes:)
|
|
516
|
+
unless pointer_type?(state_type)
|
|
517
|
+
suggestion = if arguments[0].value.is_a?(AST::Identifier)
|
|
518
|
+
"use ptr_of(#{arguments[0].value.name})"
|
|
519
|
+
end
|
|
520
|
+
raise_sema_error("first argument to #{receiver_type}.#{method_name} stateful overload must be a non-null pointer, got #{state_type}", arguments[0].value, suggestion:)
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
state_pointed_type = state_type.arguments.first
|
|
524
|
+
expected_listener_type = event_stateful_listener_type(receiver_type, state_pointed_type)
|
|
525
|
+
actual_listener_type = infer_expression(arguments[1].value, scopes:, expected_type: expected_listener_type)
|
|
526
|
+
ensure_argument_assignable!(
|
|
527
|
+
actual_listener_type,
|
|
528
|
+
expected_listener_type,
|
|
529
|
+
external: false,
|
|
530
|
+
message: "listener argument to #{receiver_type}.#{method_name} expects #{expected_listener_type}, got #{actual_listener_type}",
|
|
531
|
+
expression: arguments[1].value,
|
|
532
|
+
)
|
|
533
|
+
elsif arguments.length == 1
|
|
534
|
+
listener_type = event_listener_type(receiver_type)
|
|
535
|
+
actual_type = infer_expression(arguments.first.value, scopes:, expected_type: listener_type)
|
|
536
|
+
ensure_argument_assignable!(
|
|
537
|
+
actual_type,
|
|
538
|
+
listener_type,
|
|
539
|
+
external: false,
|
|
540
|
+
message: "argument listener to #{receiver_type}.#{method_name} expects #{listener_type}, got #{actual_type}",
|
|
541
|
+
expression: arguments.first.value,
|
|
542
|
+
)
|
|
543
|
+
else
|
|
544
|
+
raise_sema_error("#{method_name} expects 1 or 2 arguments, got #{arguments.length}")
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
event_subscription_result_type
|
|
548
|
+
when :event_unsubscribe
|
|
549
|
+
raise_sema_error("unsubscribe expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
550
|
+
|
|
551
|
+
actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("Subscription"))
|
|
552
|
+
ensure_argument_assignable!(
|
|
553
|
+
actual_type,
|
|
554
|
+
@ctx.types.fetch("Subscription"),
|
|
555
|
+
external: false,
|
|
556
|
+
message: "argument subscription to #{receiver_type}.unsubscribe expects Subscription, got #{actual_type}",
|
|
557
|
+
expression: arguments.first.value,
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
@ctx.types.fetch("bool")
|
|
561
|
+
when :event_emit
|
|
562
|
+
if receiver_type.payload_type.nil?
|
|
563
|
+
raise_sema_error("emit expects 0 arguments, got #{arguments.length}") unless arguments.empty?
|
|
564
|
+
else
|
|
565
|
+
raise_sema_error("emit expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
566
|
+
|
|
567
|
+
actual_type = infer_expression(arguments.first.value, scopes:, expected_type: receiver_type.payload_type)
|
|
568
|
+
ensure_argument_assignable!(
|
|
569
|
+
actual_type,
|
|
570
|
+
receiver_type.payload_type,
|
|
571
|
+
external: false,
|
|
572
|
+
message: "argument value to #{receiver_type}.emit expects #{receiver_type.payload_type}, got #{actual_type}",
|
|
573
|
+
expression: arguments.first.value,
|
|
574
|
+
)
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
@ctx.types.fetch("void")
|
|
578
|
+
when :event_wait
|
|
579
|
+
raise_sema_error("wait expects 0 arguments, got #{arguments.length}") unless arguments.empty?
|
|
580
|
+
|
|
581
|
+
Types::Registry.task(event_wait_result_type(receiver_type))
|
|
582
|
+
else
|
|
583
|
+
raise_sema_error("unsupported event method #{kind}")
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def event_listener_type(event_type)
|
|
588
|
+
params = []
|
|
589
|
+
params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type
|
|
590
|
+
|
|
591
|
+
Types::Registry.function(
|
|
592
|
+
nil,
|
|
593
|
+
params:,
|
|
594
|
+
return_type: @ctx.types.fetch("void"),
|
|
595
|
+
external: false,
|
|
596
|
+
)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def event_stateful_listener_type(event_type, state_type)
|
|
600
|
+
params = [Types::Registry.parameter("state", Types::Registry.generic_instance("ptr", [state_type]))]
|
|
601
|
+
params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type
|
|
602
|
+
|
|
603
|
+
Types::Registry.function(
|
|
604
|
+
nil,
|
|
605
|
+
params:,
|
|
606
|
+
return_type: @ctx.types.fetch("void"),
|
|
607
|
+
external: false,
|
|
608
|
+
)
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def event_method_kind(receiver_type, name)
|
|
612
|
+
return unless event_type?(receiver_type)
|
|
613
|
+
|
|
614
|
+
EVENT_METHOD_KINDS[name]
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def atomic_method_kind(receiver_type, name)
|
|
618
|
+
return unless atomic_type?(receiver_type)
|
|
619
|
+
|
|
620
|
+
ATOMIC_METHOD_KINDS[name]
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:)
|
|
624
|
+
elem_type = atomic_element_type(receiver_type)
|
|
625
|
+
raise_sema_error("atomic methods do not support named arguments") if arguments.any?(&:name)
|
|
626
|
+
|
|
627
|
+
case kind
|
|
628
|
+
when :atomic_load
|
|
629
|
+
raise_sema_error("load expects 0 arguments, got #{arguments.length}") unless arguments.empty?
|
|
630
|
+
elem_type
|
|
631
|
+
when :atomic_store
|
|
632
|
+
raise_sema_error("store expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
633
|
+
record_editable_receiver_expression(receiver)
|
|
634
|
+
raise_sema_error("cannot call editable method store on an immutable receiver") unless assignable_receiver?(receiver, scopes)
|
|
635
|
+
arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type)
|
|
636
|
+
ensure_assignable!(arg_type, elem_type, "store expects #{elem_type}, got #{arg_type}")
|
|
637
|
+
@ctx.types.fetch("void")
|
|
638
|
+
when :atomic_add, :atomic_sub, :atomic_exchange
|
|
639
|
+
raise_sema_error("#{kind.to_s.delete_prefix("atomic_")} expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
640
|
+
record_editable_receiver_expression(receiver)
|
|
641
|
+
raise_sema_error("cannot call editable method #{kind.to_s.delete_prefix("atomic_")} on an immutable receiver") unless assignable_receiver?(receiver, scopes)
|
|
642
|
+
arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type)
|
|
643
|
+
ensure_assignable!(arg_type, elem_type, "#{kind.to_s.delete_prefix("atomic_")} expects #{elem_type}, got #{arg_type}")
|
|
644
|
+
elem_type
|
|
645
|
+
when :atomic_compare_exchange
|
|
646
|
+
raise_sema_error("compare_exchange expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
647
|
+
record_editable_receiver_expression(receiver)
|
|
648
|
+
raise_sema_error("cannot call editable method compare_exchange on an immutable receiver") unless assignable_receiver?(receiver, scopes)
|
|
649
|
+
exp_type = infer_expression(arguments[0].value, scopes:, expected_type: elem_type)
|
|
650
|
+
des_type = infer_expression(arguments[1].value, scopes:, expected_type: elem_type)
|
|
651
|
+
ensure_assignable!(exp_type, elem_type, "compare_exchange expected type #{elem_type}, got #{exp_type}")
|
|
652
|
+
ensure_assignable!(des_type, elem_type, "compare_exchange desired type #{elem_type}, got #{des_type}")
|
|
653
|
+
@ctx.types.fetch("bool")
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def event_method_name(kind)
|
|
658
|
+
EVENT_METHOD_NAMES.fetch(kind)
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def event_visible_from_current_module?(event_type)
|
|
662
|
+
event_type.module_name == @ctx.module_name || event_type.visibility == :public
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
def event_receiver_mutable?(receiver_expression, scopes:)
|
|
666
|
+
return true if top_level_event_receiver?(receiver_expression, scopes:)
|
|
667
|
+
return false unless receiver_expression.is_a?(AST::MemberAccess)
|
|
668
|
+
|
|
669
|
+
receiver_type = infer_lvalue_receiver(
|
|
670
|
+
receiver_expression.receiver,
|
|
671
|
+
scopes:,
|
|
672
|
+
allow_ref_identifier: true,
|
|
673
|
+
allow_pointer_identifier: true,
|
|
674
|
+
require_mutable_pointer: true,
|
|
675
|
+
allow_span_param_identifier: true,
|
|
676
|
+
)
|
|
677
|
+
receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer: true)
|
|
678
|
+
!event_member_type(receiver_type, receiver_expression.member).nil?
|
|
679
|
+
rescue SemanticError
|
|
680
|
+
false
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def top_level_event_receiver?(receiver_expression, scopes:)
|
|
684
|
+
case receiver_expression
|
|
685
|
+
when AST::Identifier
|
|
686
|
+
binding = lookup_value(receiver_expression.name, scopes)
|
|
687
|
+
binding&.kind == :event
|
|
688
|
+
when AST::MemberAccess
|
|
689
|
+
return false unless receiver_expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(receiver_expression.receiver.name)
|
|
690
|
+
|
|
691
|
+
imported_module = @ctx.imports.fetch(receiver_expression.receiver.name)
|
|
692
|
+
binding = imported_module.values[receiver_expression.member]
|
|
693
|
+
binding && binding.kind == :event
|
|
694
|
+
else
|
|
695
|
+
false
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
def event_member_type(receiver_type, name)
|
|
700
|
+
return unless receiver_type.respond_to?(:event)
|
|
701
|
+
|
|
702
|
+
receiver_type.event(name)
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def fresh_noncopyable_event_initializer?(expression, target_type, scopes:)
|
|
706
|
+
return true unless expression
|
|
707
|
+
|
|
708
|
+
case expression
|
|
709
|
+
when AST::Call
|
|
710
|
+
callable_kind, callable, _receiver = resolve_callable(expression.callee, scopes:)
|
|
711
|
+
callable_kind == :struct && callable == target_type
|
|
712
|
+
when AST::Specialization
|
|
713
|
+
return false unless expression.callee.is_a?(AST::Identifier)
|
|
714
|
+
return false unless %w[zero default].include?(expression.callee.name)
|
|
715
|
+
return false unless expression.arguments.length == 1
|
|
716
|
+
|
|
717
|
+
type_arg = expression.arguments.first.value
|
|
718
|
+
type_arg.is_a?(AST::TypeRef) && resolve_type_ref(type_arg) == target_type
|
|
719
|
+
else
|
|
720
|
+
false
|
|
721
|
+
end
|
|
722
|
+
rescue SemanticError
|
|
723
|
+
false
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def char_array_removed_text_method?(receiver_type, name)
|
|
727
|
+
return unless char_array_text_type?(receiver_type)
|
|
728
|
+
|
|
729
|
+
name == "as_str" || name == "as_cstr"
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
def str_buffer_method_kind(receiver_type, name)
|
|
733
|
+
return unless str_buffer_type?(receiver_type)
|
|
734
|
+
|
|
735
|
+
STR_BUFFER_METHOD_KINDS[name]
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
def str_buffer_method_name(kind)
|
|
739
|
+
STR_BUFFER_METHOD_NAMES.fetch(kind)
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
def check_function_call(binding, arguments, scopes:)
|
|
743
|
+
arguments = canonicalize_call_arguments(arguments, binding.type.params, binding.name)
|
|
744
|
+
expected_params = binding.type.params
|
|
745
|
+
unless call_arity_matches?(binding.type, arguments.length)
|
|
746
|
+
raise_sema_error(arity_error_message(binding.type, binding.name, arguments.length))
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
expected_params.each_with_index do |parameter, index|
|
|
750
|
+
argument = arguments.fetch(index)
|
|
751
|
+
actual_type = foreign_argument_actual_type(parameter, argument, scopes:, function_name: binding.name, expected_type: parameter.type)
|
|
752
|
+
record_mutating_argument_identifier(argument, parameter)
|
|
753
|
+
if foreign_cstr_boundary_parameter?(parameter)
|
|
754
|
+
unless foreign_cstr_argument_compatible?(actual_type, parameter, expression: foreign_argument_expression(argument))
|
|
755
|
+
raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}")
|
|
756
|
+
end
|
|
757
|
+
else
|
|
758
|
+
unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: binding.external, expression: foreign_argument_expression(argument))
|
|
759
|
+
suggestion = explicit_cast_suggestion(actual_type, parameter.type)
|
|
760
|
+
raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}", suggestion:)
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
arguments.drop(expected_params.length).each do |argument|
|
|
766
|
+
infer_expression(argument.value, scopes:)
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def record_mutating_argument_identifier(argument, parameter)
|
|
771
|
+
return unless %i[out inout].include?(parameter.passing_mode)
|
|
772
|
+
return unless argument.value.is_a?(AST::Identifier)
|
|
773
|
+
|
|
774
|
+
@mutating_argument_identifier_ids[argument.value.object_id] = true
|
|
775
|
+
end
|
|
776
|
+
def check_callable_value_call(function_type, arguments, scopes:, callee_expression:)
|
|
777
|
+
arguments = canonicalize_call_arguments(arguments, function_type.params, describe_expression(callee_expression))
|
|
778
|
+
|
|
779
|
+
unless call_arity_matches?(function_type, arguments.length)
|
|
780
|
+
raise_sema_error(arity_error_message(function_type, describe_expression(callee_expression), arguments.length))
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
function_type.params.each_with_index do |parameter, index|
|
|
784
|
+
argument = arguments.fetch(index)
|
|
785
|
+
actual_type = infer_expression(argument.value, scopes:, expected_type: parameter.type)
|
|
786
|
+
unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: false, expression: argument.value)
|
|
787
|
+
suggestion = explicit_cast_suggestion(actual_type, parameter.type)
|
|
788
|
+
raise_sema_error("argument #{parameter.name || index} to #{describe_expression(callee_expression)} expects #{parameter.type}, got #{actual_type}", argument, suggestion:)
|
|
789
|
+
end
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
arguments.drop(function_type.params.length).each do |argument|
|
|
793
|
+
infer_expression(argument.value, scopes:)
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
def check_fatal_call(arguments, scopes:)
|
|
797
|
+
raise_sema_error("fatal does not support named arguments") if arguments.any?(&:name)
|
|
798
|
+
raise_sema_error("fatal expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
799
|
+
|
|
800
|
+
message_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str"))
|
|
801
|
+
return @ctx.types.fetch("void") if string_like_type?(message_type)
|
|
802
|
+
|
|
803
|
+
raise_sema_error("fatal expects str or cstr, got #{message_type}")
|
|
804
|
+
end
|
|
805
|
+
def check_get_call(arguments, scopes:)
|
|
806
|
+
raise_sema_error("get does not support named arguments") if arguments.any?(&:name)
|
|
807
|
+
raise_sema_error("get expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
808
|
+
|
|
809
|
+
source_expr = arguments.first.value
|
|
810
|
+
index_expr = arguments[1].value
|
|
811
|
+
source_type = infer_expression(source_expr, scopes:)
|
|
812
|
+
index_type = infer_expression(index_expr, scopes:)
|
|
813
|
+
raise_sema_error("get index must be an integer type, got #{index_type}") unless integer_type?(index_type)
|
|
814
|
+
|
|
815
|
+
if array_type?(source_type)
|
|
816
|
+
raise_sema_error("get requires an addressable array value") unless addressable_storage_expression?(source_expr, scopes:)
|
|
817
|
+
|
|
818
|
+
Types::Registry.nullable(pointer_to(array_element_type(source_type)))
|
|
819
|
+
elsif source_type.is_a?(Types::Span)
|
|
820
|
+
Types::Registry.nullable(pointer_to(source_type.element_type))
|
|
821
|
+
else
|
|
822
|
+
raise_sema_error("get expects an array or span, got #{source_type}")
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
def check_ref_of_call(arguments, scopes:)
|
|
826
|
+
raise_sema_error("ref_of does not support named arguments") if arguments.any?(&:name)
|
|
827
|
+
raise_sema_error("ref_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
828
|
+
|
|
829
|
+
source_type = infer_addr_source_type(arguments.first.value, scopes:)
|
|
830
|
+
Types::Registry.generic_instance("ref", [source_type])
|
|
831
|
+
end
|
|
832
|
+
def check_const_ptr_of_call(arguments, scopes:)
|
|
833
|
+
raise_sema_error("const_ptr_of does not support named arguments") if arguments.any?(&:name)
|
|
834
|
+
raise_sema_error("const_ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
835
|
+
|
|
836
|
+
source_type = infer_ro_addr_source_type(arguments.first.value, scopes:)
|
|
837
|
+
const_pointer_to(source_type)
|
|
838
|
+
end
|
|
839
|
+
def check_read_call(arguments, scopes:)
|
|
840
|
+
validate_read_call_arguments!(arguments)
|
|
841
|
+
|
|
842
|
+
infer_reference_value_type(arguments.first.value, scopes:)
|
|
843
|
+
end
|
|
844
|
+
def check_ptr_of_call(arguments, scopes:)
|
|
845
|
+
raise_sema_error("ptr_of does not support named arguments") if arguments.any?(&:name)
|
|
846
|
+
raise_sema_error("ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
847
|
+
|
|
848
|
+
source_expression = arguments.first.value
|
|
849
|
+
source_type = infer_expression(source_expression, scopes:)
|
|
850
|
+
return pointer_to(referenced_type(source_type)) if ref_type?(source_type)
|
|
851
|
+
|
|
852
|
+
pointer_to(infer_addr_source_type(source_expression, scopes:))
|
|
853
|
+
end
|
|
854
|
+
def check_field_of_call(arguments, scopes:)
|
|
855
|
+
raise_sema_error("field_of does not support named arguments") if arguments.any?(&:name)
|
|
856
|
+
raise_sema_error("field_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
857
|
+
|
|
858
|
+
evaluate_field_of_call(arguments, scopes:)
|
|
859
|
+
builtin_field_handle_type
|
|
860
|
+
end
|
|
861
|
+
def check_callable_of_call(arguments, scopes:)
|
|
862
|
+
raise_sema_error("callable_of does not support named arguments") if arguments.any?(&:name)
|
|
863
|
+
raise_sema_error("callable_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
864
|
+
|
|
865
|
+
evaluate_callable_of_call(arguments, scopes:)
|
|
866
|
+
builtin_callable_handle_type
|
|
867
|
+
end
|
|
868
|
+
def check_has_attribute_call(arguments, scopes:)
|
|
869
|
+
raise_sema_error("has_attribute does not support named arguments") if arguments.any?(&:name)
|
|
870
|
+
raise_sema_error("has_attribute expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
871
|
+
|
|
872
|
+
target = resolve_reflection_target_argument(arguments.first.value, scopes:)
|
|
873
|
+
binding = resolve_attribute_name_argument(arguments[1].value)
|
|
874
|
+
validate_attribute_target_compatibility!(target, binding)
|
|
875
|
+
|
|
876
|
+
@ctx.types.fetch("bool")
|
|
877
|
+
end
|
|
878
|
+
def check_attribute_of_call(arguments, scopes:)
|
|
879
|
+
raise_sema_error("attribute_of does not support named arguments") if arguments.any?(&:name)
|
|
880
|
+
raise_sema_error("attribute_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
881
|
+
|
|
882
|
+
target = resolve_reflection_target_argument(arguments.first.value, scopes:)
|
|
883
|
+
binding = resolve_attribute_name_argument(arguments[1].value)
|
|
884
|
+
validate_attribute_target_compatibility!(target, binding)
|
|
885
|
+
|
|
886
|
+
application = find_attribute_application(target, binding)
|
|
887
|
+
unless application || attribute_presence_guard_active?(scopes, attribute_presence_refinement_key(target, binding))
|
|
888
|
+
raise_sema_error("attribute #{qualified_attribute_name(binding)} is not applied to #{target}")
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
builtin_attribute_handle_type
|
|
892
|
+
end
|
|
893
|
+
def check_attribute_arg_call(expected_type, arguments, scopes:)
|
|
894
|
+
raise_sema_error("attribute_arg does not support named arguments") if arguments.any?(&:name)
|
|
895
|
+
raise_sema_error("attribute_arg expects 2 arguments, got #{arguments.length}") unless arguments.length == 2
|
|
896
|
+
|
|
897
|
+
handle_value = evaluate_compile_time_const_value(arguments.first.value, scopes:)
|
|
898
|
+
parameter_source = if handle_value.is_a?(Types::AttributeHandle)
|
|
899
|
+
[handle_value.attribute_name, handle_value.params]
|
|
900
|
+
else
|
|
901
|
+
handle_type = infer_expression(arguments.first.value, scopes:)
|
|
902
|
+
raise_sema_error("attribute_arg expects an attribute handle") unless handle_type == builtin_attribute_handle_type
|
|
903
|
+
|
|
904
|
+
binding = attribute_binding_for_handle_expression(arguments.first.value)
|
|
905
|
+
raise_sema_error("attribute_arg expects an attribute handle") unless binding
|
|
906
|
+
|
|
907
|
+
[binding.name, binding.params]
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
param_name = reflection_identifier_name(arguments[1].value, context: "attribute_arg")
|
|
911
|
+
attribute_name, params = parameter_source
|
|
912
|
+
parameter = params.find { |candidate| candidate.name == param_name }
|
|
913
|
+
raise_sema_error("attribute #{attribute_name} has no parameter #{param_name}") unless parameter
|
|
914
|
+
raise_sema_error("attribute_arg[#{expected_type}] does not match declared type #{parameter.type} for #{attribute_name}.#{param_name}") unless parameter.type == expected_type
|
|
915
|
+
|
|
916
|
+
expected_type
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
def attribute_binding_for_handle_expression(expression)
|
|
920
|
+
return unless expression.is_a?(AST::Call)
|
|
921
|
+
return unless expression.callee.is_a?(AST::Identifier) && expression.callee.name == "attribute_of"
|
|
922
|
+
return unless expression.arguments.length == 2 && expression.arguments.none?(&:name)
|
|
923
|
+
|
|
924
|
+
resolve_attribute_name_argument(expression.arguments[1].value)
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
def check_adapt_call(interface, arguments, scopes:)
|
|
928
|
+
raise_sema_error("adapt does not support named arguments") if arguments.any?(&:name)
|
|
929
|
+
raise_sema_error("adapt expects 1 argument, got #{arguments.length}") unless arguments.length == 1
|
|
930
|
+
|
|
931
|
+
argument = arguments.first
|
|
932
|
+
concrete_type = infer_expression(argument.value, scopes:)
|
|
933
|
+
concrete_type = referenced_type(concrete_type) if ref_type?(concrete_type)
|
|
934
|
+
unless concrete_type.is_a?(Types::Struct) || concrete_type.is_a?(Types::Opaque) || concrete_type.is_a?(Types::StructInstance)
|
|
935
|
+
raise_sema_error("adapt requires a struct or opaque type, got #{concrete_type}")
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
unless type_implements_interface?(concrete_type, interface)
|
|
939
|
+
raise_sema_error("#{concrete_type} does not implement #{interface.name}")
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
Types::Dyn.new(interface, interface.type_arguments || [])
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
def check_dyn_method_call(method_binding, _receiver, arguments, scopes:)
|
|
946
|
+
raise_sema_error("#{method_binding.name} expects #{method_binding.params.length} arguments, got #{arguments.length}") unless arguments.length == method_binding.params.length
|
|
947
|
+
|
|
948
|
+
method_binding.params.each_with_index do |param, index|
|
|
949
|
+
actual_type = infer_expression(arguments[index].value, scopes:, expected_type: param.type)
|
|
950
|
+
ensure_assignable!(
|
|
951
|
+
actual_type,
|
|
952
|
+
param.type,
|
|
953
|
+
"argument #{index + 1} of #{method_binding.name} expects #{param.type}, got #{actual_type}",
|
|
954
|
+
expression: arguments[index].value,
|
|
955
|
+
)
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
def resolve_adapt_interface(type_arg)
|
|
960
|
+
parts = type_arg.name.parts
|
|
961
|
+
type_args = type_arg.arguments.map { |a| a.value }
|
|
962
|
+
interface_ref = AST::QualifiedName.new(parts:, type_arguments: type_args)
|
|
963
|
+
resolve_interface_ref(interface_ref)
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
def canonicalize_call_arguments(arguments, params, context_name)
|
|
967
|
+
return arguments unless arguments.any?(&:name)
|
|
968
|
+
|
|
969
|
+
named_seen = false
|
|
970
|
+
by_position = Array.new(params.length)
|
|
971
|
+
param_names = params.map(&:name)
|
|
972
|
+
|
|
973
|
+
arguments.each_with_index do |arg, _idx|
|
|
974
|
+
if arg.name
|
|
975
|
+
named_seen = true
|
|
976
|
+
raise_sema_error("duplicate named argument '#{arg.name}' in call to #{context_name}") if by_position.any? { |slot| slot&.name == arg.name }
|
|
977
|
+
param_idx = param_names.index(arg.name)
|
|
978
|
+
raise_sema_error("unknown parameter '#{arg.name}' for #{context_name}") unless param_idx
|
|
979
|
+
by_position[param_idx] = arg
|
|
980
|
+
else
|
|
981
|
+
raise_sema_error("positional argument after named argument in call to #{context_name}") if named_seen
|
|
982
|
+
param_idx = _idx
|
|
983
|
+
raise_sema_error("#{context_name} expects #{params.length} arguments, got #{arguments.length}") if param_idx >= params.length
|
|
984
|
+
by_position[param_idx] = arg
|
|
985
|
+
end
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
by_position.reject(&:nil?)
|
|
989
|
+
end
|
|
990
|
+
end
|
|
991
|
+
end
|
|
992
|
+
end
|