Class: MilkTea::SemanticAnalyzer::Checker
- Inherits:
-
Object
- Object
- MilkTea::SemanticAnalyzer::Checker
- Includes:
- CompatibilityHelpers
- Defined in:
- lib/milk_tea/core/semantic_analyzer.rb,
lib/milk_tea/core/semantic_analyzer/calls.rb,
lib/milk_tea/core/semantic_analyzer/generics.rb,
lib/milk_tea/core/semantic_analyzer/top_level.rb,
lib/milk_tea/core/semantic_analyzer/attributes.rb,
lib/milk_tea/core/semantic_analyzer/statements.rb,
lib/milk_tea/core/semantic_analyzer/expressions.rb,
lib/milk_tea/core/semantic_analyzer/nullability.rb,
lib/milk_tea/core/semantic_analyzer/flow_refinement.rb,
lib/milk_tea/core/semantic_analyzer/name_resolution.rb,
lib/milk_tea/core/semantic_analyzer/analysis_context.rb,
lib/milk_tea/core/semantic_analyzer/function_binding.rb,
lib/milk_tea/core/semantic_analyzer/type_declaration.rb,
lib/milk_tea/core/semantic_analyzer/foreign_functions.rb,
lib/milk_tea/core/semantic_analyzer/type_compatibility.rb,
lib/milk_tea/core/semantic_analyzer/interface_conformance.rb
Constant Summary collapse
- INTEGER_SUFFIX_TYPES =
{ "ub" => "ubyte", "b" => "byte", "us" => "ushort", "s" => "short", "u" => "uint", "i" => "int", "ul" => "ulong", "l" => "long", "z" => "ptr_uint", "iz" => "ptr_int", }.freeze
- BUILTIN_CALLABLE_NAMES =
Names recognised as builtin callable / type-constructor identifiers by resolve_callable and the compile-time evaluation path. We allow these so the generic-name check does not flag them as unknown before the full type checker has a chance to handle the surrounding Specialization / Call context.
%w[ fatal ref_of const_ptr_of read ptr_of field_of fields_of callable_of attribute_of has_attribute members_of attributes_of get reinterpret array span zero default hash equal order adapt attribute_arg Task Option Result SoA str_buffer atomic ].to_set.freeze
- PRELUDE_MODULE_PATHS =
%w[std.option std.result].freeze
- PRELUDE_TYPE_DEFINING_MODULES =
{ "Option" => "std.option", "Result" => "std.result", }.freeze
Constants included from CompatibilityHelpers
CompatibilityHelpers::ATOMIC_METHOD_KINDS, CompatibilityHelpers::BUILTIN_CSTR, CompatibilityHelpers::EVENT_METHOD_KINDS, CompatibilityHelpers::EVENT_METHOD_NAMES, CompatibilityHelpers::SIMD_METHOD_KINDS, CompatibilityHelpers::STR_BUFFER_METHOD_KINDS, CompatibilityHelpers::STR_BUFFER_METHOD_NAMES
Instance Attribute Summary collapse
-
#ctx ⇒ Object
readonly
Returns the value of attribute ctx.
Instance Method Summary collapse
- #addressable_storage_expression?(expression, scopes:) ⇒ Boolean
- #aggregate_display_name(type) ⇒ Object
- #aggregate_type?(type) ⇒ Boolean
- #allocate_binding_id ⇒ Object
- #apply_continuation_refinements!(scopes, refinements) ⇒ Object
-
#apply_nullability_continuation_refinements!(scopes, next_stmt) ⇒ Object
After processing a statement, apply ControlFlow-derived non-null refinements to the scopes so the next statement benefits from cross-branch narrowing.
- #argument_types_compatible?(actual_type, expected_type, external:, expression: nil, scopes: nil, contextual_int_to_float: false) ⇒ Boolean
- #array_element_type(type) ⇒ Object
- #array_length(type) ⇒ Object
- #array_to_span_call_argument_compatible?(actual_type, expected_type, expression:, scopes:) ⇒ Boolean
- #array_type?(type) ⇒ Boolean
- #assignable_receiver?(receiver_expression, scopes) ⇒ Boolean
- #atomic_element_type(type) ⇒ Object
- #atomic_method_kind(receiver_type, name) ⇒ Object
- #atomic_type?(type) ⇒ Boolean
- #attribute_binding_for_handle_expression(expression) ⇒ Object
- #attribute_presence_guard_active?(scopes, key) ⇒ Boolean
- #attribute_presence_key_from_call(expression, scopes:) ⇒ Object
- #attribute_presence_refinement_key(target, binding) ⇒ Object
- #attribute_target_kind(target) ⇒ Object
- #automatic_foreign_cstr_list_temp_needed?(parameter) ⇒ Boolean
- #automatic_foreign_cstr_temp_needed?(parameter, expression, scopes:) ⇒ Boolean
- #binding_resolution_snapshot ⇒ Object
- #bitwise_type?(type) ⇒ Boolean
- #build_analysis ⇒ Object
- #builtin_attribute_handle_type ⇒ Object
- #builtin_callable_handle_type ⇒ Object
- #builtin_event_error_type ⇒ Object
- #builtin_field_handle_type ⇒ Object
- #builtin_member_handle_type ⇒ Object
- #builtin_struct_handle_type ⇒ Object
- #builtin_subscription_type ⇒ Object
- #builtin_type_meta_type ⇒ Object
- #c_natively_equality_comparable_type?(type) ⇒ Boolean
- #call_argument_compatible?(actual_type, expected_type, scopes:, external:, expression: nil) ⇒ Boolean
- #callable_receiver_type_for_specialization(callee, scopes:) ⇒ Object
- #callable_type?(type) ⇒ Boolean
- #canonicalize_call_arguments(arguments, params, context_name, binding = nil) ⇒ Object
- #cast_numeric_type(type) ⇒ Object
- #castable_primitive?(type) ⇒ Boolean
- #catch_structural ⇒ Object
- #cfg_block_always_terminates?(statements) ⇒ Boolean
- #char_array_removed_text_method?(receiver_type, name) ⇒ Boolean
- #char_array_text_type?(type) ⇒ Boolean
- #check ⇒ Object
- #check_adapt_call(interface, arguments, scopes:) ⇒ Object
- #check_aggregate_construction(struct_type, arguments, scopes:) ⇒ Object
- #check_aggregate_field_arguments(struct_type, arguments, scopes:, context:) ⇒ Object
- #check_array_construction(array_type, arguments, scopes:) ⇒ Object
- #check_assignment(statement, scopes:) ⇒ Object
- #check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:) ⇒ Object
- #check_attribute_applications ⇒ Object
- #check_attribute_arg_call(expected_type, arguments, scopes:) ⇒ Object
- #check_attribute_arguments!(binding, application) ⇒ Object
- #check_attribute_of_call(arguments, scopes:) ⇒ Object
- #check_block(statements, scopes:, return_type:, allow_return: true) ⇒ Object
- #check_block_body_const(decl) ⇒ Object
- #check_callable_of_call(arguments, scopes:) ⇒ Object
- #check_callable_value_call(function_type, arguments, scopes:, callee_expression:) ⇒ Object
- #check_cast_call(target_type, arguments, scopes:) ⇒ Object
-
#check_collecting_errors ⇒ Object
Like check, but collects per-function errors instead of raising at first.
- #check_const_ptr_of_call(arguments, scopes:) ⇒ Object
- #check_decl_attribute_applications!(applications, target_kind:, target_label:, target_node:) ⇒ Object
- #check_definite_assignment(binding) ⇒ Object
- #check_dyn_method_call(method_binding, _receiver, arguments, scopes:) ⇒ Object
- #check_emit_stmt(statement) ⇒ Object
- #check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
- #check_equal_call(resolution, arguments, scopes:) ⇒ Object
- #check_event_method_call(kind, receiver, arguments, scopes:) ⇒ Object
- #check_expr_const(decl) ⇒ Object
- #check_expr_names(expr, scopes, binding, names) ⇒ Object
- #check_fatal_call(arguments, scopes:) ⇒ Object
- #check_field_of_call(arguments, scopes:) ⇒ Object
- #check_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_format_string_literal(format_string, scopes:) ⇒ Object
- #check_function(binding) ⇒ Object
- #check_function_call(binding, arguments, scopes:) ⇒ Object
- #check_functions ⇒ Object
-
#check_functions_collecting(errors) ⇒ Object
Per-function error collection used by check_collecting_errors.
- #check_gather_stmt(statement, scopes:) ⇒ Object
-
#check_generic_method_immutable_this(binding, scopes) ⇒ Object
Scans generic method bodies for assignments to this through a non-editable receiver.
-
#check_generic_method_names(binding, scopes) ⇒ Object
Verifies that every bare identifier used as a value expression inside a generic method body resolves to a known name (parameter, local, top-level value, function, type, import, or type parameter).
- #check_generic_name(name, scopes, binding, node, names) ⇒ Object
- #check_get_call(arguments, scopes:) ⇒ Object
- #check_has_attribute_call(arguments, scopes:) ⇒ Object
- #check_hash_call(resolution, arguments, scopes:) ⇒ Object
- #check_inline_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_inline_if_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_inline_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_inline_while_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
- #check_interface_conformance!(receiver_type, interface) ⇒ Object
- #check_interface_conformances ⇒ Object
- #check_interface_method_match!(receiver_type, interface, interface_method, method) ⇒ Object
- #check_layout_type_via_ct(type_ref, context:, scopes:) ⇒ Object
- #check_local_decl(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_local_decl_destructure(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_local_decl_let_else(statement, scopes:, return_type:, allow_return:, discard_binding:, declared_type:, inferred_type:) ⇒ Object
- #check_local_decl_plain(statement, scopes:, discard_binding:, declared_type:, inferred_type:) ⇒ Object
- #check_local_decl_struct_destructure(statement, value_type, scopes:) ⇒ Object
- #check_local_decl_tuple_destructure(statement, value_type, scopes:) ⇒ Object
- #check_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_order_call(resolution, arguments, scopes:) ⇒ Object
- #check_parallel_block_stmt(statement, scopes:, return_type:) ⇒ Object
- #check_parallel_for_bindings(statement, scopes:) ⇒ Object
- #check_ptr_of_call(arguments, scopes:) ⇒ Object
- #check_range_expr_loop(expression, scopes:) ⇒ Object
- #check_range_index_assignment(statement, scopes:) ⇒ Object
- #check_read_call(arguments, scopes:) ⇒ Object
- #check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:) ⇒ Object
- #check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_ref_of_call(arguments, scopes:) ⇒ Object
- #check_reinterpret_call(target_type, arguments, scopes:) ⇒ Object
- #check_simd_construction(simd_type, arguments, scopes:) ⇒ Object
- #check_simd_method_call(kind, receiver_type, _receiver, arguments, scopes:) ⇒ Object
- #check_stack_escape_call(expression, scopes:) ⇒ Object
- #check_stack_local_source(expression, scopes:, builtin:) ⇒ Object
-
#check_stack_pointer_escape_in_return(expression, scopes:) ⇒ Object
Walks the returned expression tree to detect ptr_of/ref_of/const_ptr_of applied to a stack-local variable whose address must not escape the function.
- #check_statement(statement, scopes:, return_type:, allow_return: true) ⇒ Object
- #check_static_assert(statement, scopes:) ⇒ Object
- #check_stmt_names(stmt, scopes, binding, names) ⇒ Object
- #check_str_buffer_method_call(kind, receiver, arguments, scopes:) ⇒ Object
- #check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
- #check_struct_match_pattern(arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:) ⇒ Object
- #check_struct_with_call(struct_type, _receiver_expression, arguments, scopes:) ⇒ Object
- #check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_top_level_static_asserts ⇒ Object
- #check_top_level_values ⇒ Object
- #check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
- #check_variant_arm_construction(callable, arguments, scopes:) ⇒ Object
- #check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
- #check_when_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
- #check_zero_call(target_type, arguments, expected_type: nil, operation: "zero") ⇒ Object
- #collect_emit_declarations ⇒ Object
- #collect_emit_from_declarations(declarations) ⇒ Object
- #collect_emit_from_node(node) ⇒ Object
- #collect_emit_from_statements(statements) ⇒ Object
- #collect_structural_error(error) ⇒ Object
- #collect_type_substitutions(pattern_type, actual_type, substitutions, function_name) ⇒ Object
- #collection_loop_binding_type(iterable_type, element_type) ⇒ Object
- #collection_loop_ref_element_type?(type) ⇒ Boolean
- #collection_loop_type(type) ⇒ Object
- #common_integer_type(left_type, right_type) ⇒ Object
- #common_numeric_type(left_type, right_type) ⇒ Object
- #conditional_common_type(then_type, else_type, then_expression:, else_expression:) ⇒ Object
- #conditional_null_common_type(null_type, other_type) ⇒ Object
- #consuming_foreign_call_refinements(expression, scopes:) ⇒ Object
- #contains_callable_ref_type?(type, visited = {}) ⇒ Boolean
- #contains_proc_type?(type, visited = {}) ⇒ Boolean
- #contains_type_var?(type) ⇒ Boolean
- #contextual_int_to_float_fits?(expression, expected_type, scopes) ⇒ Boolean
- #count_required_params(binding) ⇒ Object
- #current_actual_scope(scopes) ⇒ Object
- #current_error_node ⇒ Object
- #current_return_context ⇒ Object
- #current_type_param_constraints ⇒ Object
- #current_type_params ⇒ Object
- #declare_attributes ⇒ Object
- #declare_function_binding(decl, receiver_type: nil, declared_receiver_type: nil, receiver_type_param_names: [], receiver_type_param_constraints: {}, external: false) ⇒ Object
- #declare_functions ⇒ Object
- #declare_interface_binding(decl) ⇒ Object
- #declare_named_types ⇒ Object
- #declare_top_level_values ⇒ Object
- #define_nested_type_bindings_recursive(parent_decl, parent_name: parent_decl.name) ⇒ Object
- #describe_expression(expression) ⇒ Object
- #direct_function_identity_expression?(expression, scopes) ⇒ Boolean
- #direct_function_to_proc_argument_compatible?(actual_type, expected_type, expression, scopes) ⇒ Boolean
- #direct_task_to_proc_argument_compatible?(actual_type, expected_type) ⇒ Boolean
- #each_enum_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
- #each_integer_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
- #each_string_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
- #each_tuple_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
- #each_variant_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
- #empty_module_binding(module_path) ⇒ Object
- #ensure_argument_assignable!(actual_type, expected_type, external:, message:, expression: nil, scopes: nil) ⇒ Object
- #ensure_assignable!(actual_type, expected_type, message, expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false, line: nil, column: nil) ⇒ Object
- #ensure_available_type_name!(name, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_available_value_name!(name, kind_label: "value", line: nil, column: nil, length: nil) ⇒ Object
- #ensure_non_reserved_import_alias_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_non_reserved_name!(name, reserved_names, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_non_reserved_primitive_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_non_reserved_type_binding_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_non_reserved_value_type_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
- #ensure_parallel_for_static_lengths_match!(iterable_types) ⇒ Object
- #error_type?(type) ⇒ Boolean
- #evaluate_attribute_arg_call(arguments, scopes:) ⇒ Object
- #evaluate_attribute_of_call(arguments, scopes:) ⇒ Object
- #evaluate_attributes_of_call(arguments, scopes:) ⇒ Object
- #evaluate_block_body_const(decl, name) ⇒ Object
- #evaluate_callable_of_call(arguments, scopes:) ⇒ Object
- #evaluate_compile_time_block(statements, scopes: nil) ⇒ Object
- #evaluate_compile_time_call(expression, scopes: nil) ⇒ Object
- #evaluate_compile_time_const_value(expression, scopes: nil) ⇒ Object
- #evaluate_const_function_body(func, arguments, scopes:) ⇒ Object
- #evaluate_enum_member_const_value(expression, enum_type:, member_values:) ⇒ Object
- #evaluate_field_of_call(arguments, scopes:) ⇒ Object
- #evaluate_fields_of_call(arguments) ⇒ Object
- #evaluate_has_attribute_call(arguments, scopes:) ⇒ Object
- #evaluate_members_of_call(arguments) ⇒ Object
- #evaluate_reflection_target_argument(expression, scopes:) ⇒ Object
- #evaluate_top_level_const_value(name) ⇒ Object
- #evaluate_type_returning_call(expression, scopes:) ⇒ Object
- #evaluate_type_returning_function_body(func, type_args) ⇒ Object
- #evaluate_when_discriminant(expression) ⇒ Object
- #event_carrier_type?(type) ⇒ Boolean
- #event_listener_type(event_type) ⇒ Object
- #event_member_type(receiver_type, name) ⇒ Object
- #event_method_kind(receiver_type, name) ⇒ Object
- #event_method_name(kind) ⇒ Object
- #event_receiver_mutable?(receiver_expression, scopes:) ⇒ Boolean
- #event_stateful_listener_type(event_type, state_type) ⇒ Object
- #event_type?(type) ⇒ Boolean
- #event_visible_from_current_module?(event_type) ⇒ Boolean
- #exact_compile_time_numeric_compatibility?(actual_type, expression, expected_type, scopes: nil) ⇒ Boolean
- #expanded_declarations ⇒ Object
- #explicit_cast_suggestion(actual_type, expected_type) ⇒ Object
- #explicit_declaration_type_for(stmt) ⇒ Object
- #expression_contains_await?(expression) ⇒ Boolean
- #expression_end_line(node) ⇒ Object
- #extern_enum_integer_argument_compatibility?(actual_type, expected_type) ⇒ Boolean
- #external_numeric_assignment_target?(expression, scopes:) ⇒ Boolean
- #external_void_pointer_argument_compatibility?(actual_type, expected_type) ⇒ Boolean
- #extract_type_callee_info(expression) ⇒ Object
- #fallback_completion_type ⇒ Object
- #fill_parameter_defaults!(by_position, params, binding, context_name) ⇒ Object
- #fill_positional_defaults!(arguments, binding) ⇒ Object
- #finalize_top_level_const_values ⇒ Object
- #find_attribute_application(target, binding) ⇒ Object
- #find_reachable_imported_module(module_name) ⇒ Object
- #find_reachable_imported_module_from(module_binding, module_name, visited) ⇒ Object
- #finish_local_completion_frame(binding) ⇒ Object
- #float_literal_expression?(expression) ⇒ Boolean
- #flow_refinements(expression, truthy:, scopes:) ⇒ Object
- #foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type) ⇒ Object
- #foreign_argument_expression(argument) ⇒ Object
- #foreign_argument_legacy_passing_mode(argument) ⇒ Object
- #foreign_boundary_nullable_pointer_like_base?(base) ⇒ Boolean
- #foreign_call_consumes_binding?(binding) ⇒ Boolean
- #foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:) ⇒ Object
- #foreign_cstr_argument_compatible?(actual_type, parameter, expression:) ⇒ Boolean
- #foreign_cstr_boundary_parameter?(parameter) ⇒ Boolean
- #foreign_function_binding?(binding) ⇒ Boolean
- #foreign_mapping_auto_call_shorthand?(expression) ⇒ Boolean
- #foreign_mapping_context? ⇒ Boolean
- #foreign_mapping_expression(decl) ⇒ Object
- #foreign_mapping_public_alias_name(name) ⇒ Object
- #foreign_mapping_reference_counts(expression, counts = Hash.new(0)) ⇒ Object
- #foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints) ⇒ Object
- #foreign_slot_boundary_value_type(public_type) ⇒ Object
- #format_string_integer_base_spec_supported?(type) ⇒ Boolean
- #format_string_interpolation_supported?(type, context:) ⇒ Boolean
- #freeze_scope_bindings(scope) ⇒ Object
- #fresh_noncopyable_event_initializer?(expression, target_type, scopes:) ⇒ Boolean
- #function_pointer_type?(type) ⇒ Boolean
- #function_type_for_name(name) ⇒ Object
- #generic_integer_type_argument?(argument) ⇒ Boolean
- #harmonize_binary_float_literal_types(left_expression, right_expression, left_type, right_type, scopes:) ⇒ Object
- #harmonize_binary_integer_literal_types(left_expression, right_expression, left_type, right_type, scopes:) ⇒ Object
- #has_attribute_refinement_call?(expression) ⇒ Boolean
- #implicit_ref_argument_compatible?(actual_type, expected_type, expression, scopes) ⇒ Boolean
- #import_suggestion_for_type(type_name) ⇒ Object
- #imported_module_binding_for_name(module_name) ⇒ Object
- #imported_module_with_private_method(receiver_type, method_name) ⇒ Object
- #imported_module_with_private_method_local(receiver_type, method_name) ⇒ Object
- #infer_addr_source_type(expression, scopes:) ⇒ Object
- #infer_await_expression(expression, scopes:) ⇒ Object
- #infer_binary(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_call(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_call_return_type(call, scopes) ⇒ Object
- #infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
- #infer_expression(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_field_handle_member(expression, scopes:) ⇒ Object
- #infer_field_receiver_type(receiver_expression, scopes:, require_mutable_pointer: false) ⇒ Object
- #infer_float_literal(expression, expected_type) ⇒ Object
- #infer_function_type_arguments(binding, arguments, scopes:, receiver_type: nil) ⇒ Object
- #infer_identifier(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_if_expression(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_index_access(expression, scopes:) ⇒ Object
- #infer_index_result_type(receiver_type, index_type) ⇒ Object
- #infer_integer_literal(expression, expected_type) ⇒ Object
- #infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
- #infer_layout_query_type(type_ref, context:) ⇒ Object
- #infer_local_initializer_type(stmt, scopes) ⇒ Object
- #infer_lvalue(expression, scopes:) ⇒ Object
- #infer_lvalue_receiver(expression, scopes:, allow_ref_identifier: false, allow_pointer_identifier: false, require_mutable_pointer: false, allow_span_param_identifier: false) ⇒ Object
- #infer_match_expression(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_match_expression_arm_value(arm, scopes:, expected_type:) ⇒ Object
- #infer_member_access(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_member_access_field_type(value, scopes) ⇒ Object
- #infer_member_handle_member(expression) ⇒ Object
- #infer_method_receiver_type(receiver_expression, scopes:, member_name: nil) ⇒ Object
- #infer_null_literal(expression) ⇒ Object
- #infer_offsetof_type(type_ref, field_name, scopes: nil) ⇒ Object
- #infer_option_propagation(source_type, allow_void_success:) ⇒ Object
- #infer_proc_expression(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_propagate_expression(operand, scopes:, allow_void_success: false) ⇒ Object
- #infer_receiver_type_substitutions(binding, receiver_type) ⇒ Object
- #infer_recovered_match_expression(expression, scopes:, expected_type:) ⇒ Object
- #infer_reference_value_type(handle_expression, scopes:) ⇒ Object
- #infer_result_propagation(source_type, allow_void_success:) ⇒ Object
- #infer_ro_addr_source_type(expression, scopes:) ⇒ Object
- #infer_simple_local_type(value, scopes) ⇒ Object
- #infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
- #infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
- #infer_unary(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_unsafe_expression(expression, scopes:, expected_type: nil) ⇒ Object
- #infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
-
#initialize(ast, imported_modules: {}, allow_missing_imports: false, path: nil, global_import_index: {}) ⇒ Checker
constructor
A new instance of Checker.
- #inline_for_element_type(element) ⇒ Object
- #inline_foreign_call_requires_hoisting_message(foreign_call, scopes:) ⇒ Object
- #inline_foreign_hoisting_message(binding_name, parameter_name, reason:) ⇒ Object
- #inside_async_function? ⇒ Boolean
- #inside_loop? ⇒ Boolean
- #install_builtin_attributes ⇒ Object
- #install_builtin_types ⇒ Object
- #install_fallback_builtin_type(module_path) ⇒ Object
- #install_imports ⇒ Object
- #install_prelude_types ⇒ Object
- #instantiate_function_binding(binding, type_arguments) ⇒ Object
- #instantiate_function_binding_with_receiver(binding, explicit_type_arguments, receiver_type: nil) ⇒ Object
- #integer_constant_fits_in_char?(expression, scopes) ⇒ Boolean
- #integer_literal_expression?(expression) ⇒ Boolean
- #integer_suffix_type(lexeme) ⇒ Object
- #integer_type?(type) ⇒ Boolean
- #integer_type_argument?(argument) ⇒ Boolean
- #interface_implementation_key(type) ⇒ Object
- #iterator_loop_type(type) ⇒ Object
-
#last_ast_line(statements) ⇒ Object
Returns the highest line number across a list of AST statement nodes, recursing into nested bodies (if/else/match/while/for/defer/unsafe).
- #layout_aggregate_type?(type) ⇒ Boolean
- #let_else_discard_binding_syntax?(statement) ⇒ Boolean
- #let_else_error_type(type) ⇒ Object
- #let_else_source_type?(type) ⇒ Boolean
- #let_else_success_type(type) ⇒ Object
- #levenshtein(s, t) ⇒ Object
- #literal_type_argument_name_candidate?(type_ref) ⇒ Boolean
- #lookup_local_binding(name, scopes) ⇒ Object
- #lookup_local_method_for_interface(receiver_type, name) ⇒ Object
- #lookup_method(receiver_type, name) ⇒ Object
- #lookup_method_local_or_imported(receiver_type, name) ⇒ Object
- #lookup_static_method(receiver_type, name) ⇒ Object
- #lookup_value(name, scopes) ⇒ Object
- #mark_current_unsafe_required! ⇒ Object
- #match_expression_common_type(arm_entries, expected_type) ⇒ Object
- #match_member_name(expression, enum_type) ⇒ Object
- #matrix_op_result(operator, left_type, right_type) ⇒ Object
- #matrix_type?(type) ⇒ Boolean
- #merge_equality_cover(existing, new_cover) ⇒ Object
- #merge_refinements(existing, incoming) ⇒ Object
- #merged_scope_bindings(scopes) ⇒ Object
- #methods_receiver_type_argument_names!(type_ref) ⇒ Object
- #module_binding_method(module_binding, receiver_type, dispatch_receiver_type, name) ⇒ Object
- #module_name ⇒ Object
- #noncopyable_event_storage_type?(type) ⇒ Boolean
- #null_test_refinements(expression, truthy:, scopes:) ⇒ Object
- #nullable_candidate?(type) ⇒ Boolean
- #option_let_else_type?(type) ⇒ Boolean
- #pointer_arithmetic_result(operator, left_type, right_type) ⇒ Object
- #pointer_cast?(source_type, target_type) ⇒ Boolean
- #pointer_cast_type?(type) ⇒ Boolean
- #pointer_to(type) ⇒ Object
- #power_of_two?(value) ⇒ Boolean
- #preassign_local_binding_ids(statements) ⇒ Object
- #preassign_local_binding_ids_in_expression(expression) ⇒ Object
- #preassign_local_binding_ids_in_statements(statements) ⇒ Object
- #preassigned_local_binding_id_for(node) ⇒ Object
- #precheck_binding_resolution(statements, scopes) ⇒ Object
- #proc_storage_supported_type?(type, visited = {}) ⇒ Boolean
- #proc_type_compatible?(actual_type, expected_type) ⇒ Boolean
- #project_field_receiver_type(receiver_type, require_mutable_pointer: false) ⇒ Object
- #project_method_receiver_type(receiver_type, member_name: nil) ⇒ Object
- #propagating_expected_type(operator, expected_type) ⇒ Object
- #qualified_attribute_name(binding) ⇒ Object
- #quat_vec4_compatible?(a, b) ⇒ Boolean
- #quaternion_op_result(operator, left_type, right_type) ⇒ Object
- #quaternion_type?(type) ⇒ Boolean
- #raise_sema_error(message, node = nil, line: nil, column: nil, length: nil, suggestion: nil) ⇒ Object
- #raw_module? ⇒ Boolean
- #reachable_module_binding_for_type(receiver_type) ⇒ Object
- #read_call?(expression) ⇒ Boolean
- #receiver_type_for_fields(type) ⇒ Object
- #receiver_type_module_name(receiver_type) ⇒ Object
- #record_callable_value_expression_site(expression) ⇒ Object
- #record_callable_value_identifier_site(expression) ⇒ Object
- #record_callable_value_member_access_site(expression) ⇒ Object
- #record_declaration_binding(node, binding) ⇒ Object
- #record_editable_receiver_expression(receiver) ⇒ Object
- #record_identifier_binding(expression, binding) ⇒ Object
- #record_local_completion_snapshot(line, column, scopes) ⇒ Object
- #record_mutable_lvalue_argument_identifier(expression) ⇒ Object
- #record_mutating_argument_identifier(argument, parameter) ⇒ Object
- #recovered_for_statement(statement) ⇒ Object
- #ref_to_pointer_cast?(source_type, target_type) ⇒ Boolean
- #ref_types_compatible?(actual_type, expected_type) ⇒ Boolean
- #reflection_identifier_name(expression, context:) ⇒ Object
- #register_nested_struct_types(parent_decl, parent_name: parent_decl.name) ⇒ Object
- #reinterpretable_type?(type) ⇒ Boolean
- #reject_foreign_nullable_value_type!(type, function_name:, parameter_name:) ⇒ Object
- #require_unsafe!(message, line: nil, column: nil) ⇒ Object
- #resolve_adapt_interface(type_arg) ⇒ Object
- #resolve_aggregate_fields ⇒ Object
- #resolve_and_store_offset(expression, scopes:) ⇒ Object
- #resolve_attribute_binding(name) ⇒ Object
- #resolve_attribute_name_argument(expression) ⇒ Object
- #resolve_callable(callee, scopes:) ⇒ Object
- #resolve_callable_handle_argument(expression, scopes:) ⇒ Object
-
#resolve_compile_time_type_ref(type_ref) ⇒ Object
Resolves a bare dotted reflection type expression such as
field.type(wherefieldis a compile-timefield_handlebound in scope) to the concreteTypes::Baseit denotes. - #resolve_current_module_const_value(name) ⇒ Object
- #resolve_default_specialization(callee) ⇒ Object
- #resolve_enum_member_const_value(receiver_type, member_name, local_enum_type: nil, local_member_values: nil) ⇒ Object
- #resolve_enum_members ⇒ Object
- #resolve_equal_specialization(callee) ⇒ Object
- #resolve_event_decl_type(decl, type_params: {}, type_param_constraints: {}, owner_type_name: nil, nested_types: nil) ⇒ Object
- #resolve_explicit_associated_binding(target_type, method_name, requirement_message:) ⇒ Object
- #resolve_explicit_default_binding(target_type, context:) ⇒ Object
- #resolve_explicit_equal_binding(target_type, context:) ⇒ Object
- #resolve_explicit_format_append_binding(target_type, context:) ⇒ Object
- #resolve_explicit_format_binding(target_type, context:) ⇒ Object
- #resolve_explicit_format_len_binding(target_type, context:) ⇒ Object
- #resolve_explicit_hash_binding(target_type, context:) ⇒ Object
- #resolve_explicit_instance_binding(target_type, method_name, requirement_message:) ⇒ Object
- #resolve_explicit_order_binding(target_type, context:) ⇒ Object
- #resolve_foreign_call_expression(expression, scopes:) ⇒ Object
- #resolve_generic_type_param_constraints ⇒ Object
- #resolve_hash_specialization(callee) ⇒ Object
- #resolve_imported_module_const_value(import_name, value_name) ⇒ Object
- #resolve_interface_method_binding(method_decl, type_params: {}, type_param_constraints: {}) ⇒ Object
- #resolve_interface_ref(interface_ref) ⇒ Object
- #resolve_methods_receiver_target(type_ref) ⇒ Object
- #resolve_name_in_precheck_scopes(name, scopes) ⇒ Object
- #resolve_named_generic_type(parts) ⇒ Object
- #resolve_named_literal_type_argument(type_ref) ⇒ Object
- #resolve_nested_struct_fields(parent_decl, parent_name: parent_decl.name, type_params:, type_param_constraints:, external_nested_scope: {}) ⇒ Object
- #resolve_nested_type_bindings(parent_decl, parent_name: parent_decl.name) ⇒ Object
- #resolve_nested_type_ref(parts) ⇒ Object
- #resolve_non_nullable_type(type_ref, type_params: {}, type_param_constraints: {}, nested_types: nil) ⇒ Object
- #resolve_order_specialization(callee) ⇒ Object
- #resolve_qualified_type_name(parts) ⇒ Object
- #resolve_receiver_name_for_call_inference(callee, scopes) ⇒ Object
- #resolve_reflection_target_argument(expression, scopes:) ⇒ Object
- #resolve_specialization_type_arguments(expression) ⇒ Object
- #resolve_specialized_callable_binding(expression, scopes:) ⇒ Object
- #resolve_struct_handle_argument(expression, scopes:) ⇒ Object
- #resolve_type_aliases ⇒ Object
- #resolve_type_argument(argument, type_params: current_type_params, type_param_constraints: current_type_param_constraints) ⇒ Object
- #resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:) ⇒ Object
- #resolve_type_expression(expression) ⇒ Object
- #resolve_type_member(type, name) ⇒ Object
- #resolve_type_param_constraints(type_params) ⇒ Object
- #resolve_type_ref(type_ref, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: nil) ⇒ Object
- #resolve_variant_arms ⇒ Object
- #resolved_attribute_applications_for_target(target) ⇒ Object
- #result_let_else_type?(type) ⇒ Boolean
-
#run_nullability_pre_pass(binding, scopes) ⇒ Object
Runs a strict binding-ID nullability pass before statement checks.
- #safe_reference_source_expression?(expression, scopes:) ⇒ Boolean
- #same_attribute_binding?(left, right) ⇒ Boolean
- #scopes_with_refinements(scopes, refinements) ⇒ Object
- #set_const_value(name, value) ⇒ Object
- #simd_bitwise_result(left_type, right_type) ⇒ Object
- #simd_method_kind(receiver_type, name) ⇒ Object
- #simd_mod_result(left_type, right_type) ⇒ Object
- #simd_op_result(operator, left_type, right_type) ⇒ Object
- #simd_shift_result(left_type, right_type) ⇒ Object
- #simd_type?(type) ⇒ Boolean
- #simple_foreign_argument_expression?(expression) ⇒ Boolean
- #sized_layout_type?(type) ⇒ Boolean
- #snapshot_attribute_applications ⇒ Object
- #snapshot_attributes ⇒ Object
- #snapshot_implemented_interfaces ⇒ Object
- #snapshot_methods ⇒ Object
- #soa_type?(type) ⇒ Boolean
- #source_column(node) ⇒ Object
- #source_length(node) ⇒ Object
- #source_line(node) ⇒ Object
- #span_type?(type) ⇒ Boolean
- #specialization_lookup_owner ⇒ Object
- #specialize_function_binding(binding, arguments, scopes:, receiver_type: nil) ⇒ Object
- #stack_safe_pointer_source?(binding) ⇒ Boolean
- #start_local_completion_frame(binding, scopes) ⇒ Object
- #statement_contains_await?(statement) ⇒ Boolean
- #statement_end_line(statement) ⇒ Object
- #statement_list_lines(statements) ⇒ Object
- #statements_contain_await?(statements) ⇒ Boolean
- #static_storage_function_value?(binding) ⇒ Boolean
- #static_storage_member_initializer?(expression, scopes:) ⇒ Boolean
- #stored_ref_supported_type?(type, visited = {}, allow_lifetimes: []) ⇒ Boolean
- #str_buffer_capacity(type) ⇒ Object
- #str_buffer_method_kind(receiver_type, name) ⇒ Object
- #str_buffer_method_name(kind) ⇒ Object
- #str_buffer_type?(type) ⇒ Boolean
- #string_like_type?(type) ⇒ Boolean
- #string_view_type?(type) ⇒ Boolean
- #struct_declaration_for_type(type) ⇒ Object
- #struct_handle_for_type(type) ⇒ Object
- #struct_instance_type?(type) ⇒ Boolean
- #subscription_type?(type) ⇒ Boolean
- #substitute_type(type, substitutions) ⇒ Object
- #substitute_value_binding(binding, substitutions) ⇒ Object
- #suggest_name(wrong, candidates, max_distance: 2) ⇒ Object
- #top_level_event_receiver?(receiver_expression, scopes:) ⇒ Boolean
- #top_level_function(name) ⇒ Object
-
#try_record_generic_local_snapshot(stmt, scopes, binding) ⇒ Object
When a generic method body contains a let/var declaration, always record a completion snapshot so hover and completion can discover the local, even when full type inference is impossible.
- #type_implements_interface?(type, interface) ⇒ Boolean
- #type_satisfies_interface_constraint?(type, interface, available_type_param_constraints: current_type_param_constraints) ⇒ Boolean
- #typed_null_target_type?(type) ⇒ Boolean
- #types_compatible?(actual_type, expected_type, expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false) ⇒ Boolean
- #unsafe_context? ⇒ Boolean
- #unsupported_async_await_context(expression) ⇒ Object
- #validate_async_expression_support!(expression, context:) ⇒ Object
- #validate_async_function_body!(statements) ⇒ Object
- #validate_async_statement!(statement) ⇒ Object
- #validate_attribute_target_compatibility!(target, binding) ⇒ Object
- #validate_consuming_foreign_expression!(expression, scopes:, root_allowed: false) ⇒ Object
- #validate_consuming_foreign_parameter!(type, function_name:, parameter_name:) ⇒ Object
- #validate_detach_argument!(expression, scopes:) ⇒ Object
- #validate_detach_expression!(expression, scopes:) ⇒ Object
- #validate_explicit_aggregate_c_name!(decl) ⇒ Object
- #validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:) ⇒ Object
- #validate_function_type_param_constraints!(binding, substitutions) ⇒ Object
- #validate_generic_type!(name, arguments) ⇒ Object
- #validate_generic_type_param_constraints!(generic_type, arguments, context:, available_type_param_constraints: current_type_param_constraints) ⇒ Object
- #validate_hash_operation_argument!(expression, target_type, scopes:, operation:) ⇒ Object
- #validate_hoistable_foreign_expression!(expression, scopes:, root_hoistable: false) ⇒ Object
- #validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:) ⇒ Object
- #validate_local_proc_type!(type, local_name, initializer:) ⇒ Object
- #validate_local_ref_type!(type, local_name) ⇒ Object
- #validate_methods_receiver_type_arguments!(type_ref, generic_type) ⇒ Object
- #validate_parameter_proc_type!(type, function_name:, parameter_name:, external:, foreign:) ⇒ Object
- #validate_parameter_ref_type!(type, function_name:, parameter_name:, external:) ⇒ Object
- #validate_read_call_arguments!(arguments) ⇒ Object
- #validate_return_proc_type!(type, function_name:) ⇒ Object
- #validate_return_ref_type!(type, function_name:) ⇒ Object
- #validate_specialized_function_binding!(function_name, function_type, body_params) ⇒ Object
-
#validate_specialized_function_body(binding) ⇒ Object
Validates the body of a specialized (instantiated) function or method binding.
- #validate_specialized_function_type!(type, function_name:, context:) ⇒ Object
- #validate_static_storage_call_initializer!(expression, scopes:) ⇒ Object
- #validate_static_storage_initializer!(expression, scopes:) ⇒ Object
- #validate_stored_ref_type!(type, context, allow_lifetimes: []) ⇒ Object
- #validate_struct_layout!(decl) ⇒ Object
- #validate_threaded_for_body!(body) ⇒ Object
- #validate_threaded_for_statement!(stmt) ⇒ Object
- #validate_type_param_constraint_binding!(constraints, actual_type, context:, available_type_param_constraints: current_type_param_constraints) ⇒ Object
- #value_binding(name:, type:, mutable:, kind:, flow_type: nil, const_value: nil, id: nil) ⇒ Object
- #variant_match_arm_name(pattern, scrutinee_type) ⇒ Object
- #vector_arithmetic_result(operator, left_type, right_type) ⇒ Object
- #vector_op_result(operator, left_type, right_type) ⇒ Object
- #vector_type?(type) ⇒ Boolean
- #walk_assignment_target_reads_for_precheck_resolution(target, operator, scopes, identifier_ids, declaration_ids = nil) ⇒ Object
- #walk_expression_for_precheck_resolution(expression, scopes, identifier_ids, declaration_ids = nil) ⇒ Object
- #walk_statements_for_precheck_resolution(statements, scopes, declaration_ids, identifier_ids) ⇒ Object
- #when_chosen_body(decl) ⇒ Object
- #wider_float_type(left_type, right_type) ⇒ Object
- #wildcard_pattern?(expression) ⇒ Boolean
- #with_async_function ⇒ Object
- #with_compile_time ⇒ Object
- #with_error_node(node) ⇒ Object
- #with_foreign_mapping_context ⇒ Object
- #with_loop ⇒ Object
- #with_loop_barrier ⇒ Object
- #with_nested_scope(scopes) {|nested_scopes| ... } ⇒ Object
- #with_return_context(return_type, allow_return:) ⇒ Object
- #with_scope(bindings) {|[scope]| ... } ⇒ Object
-
#with_type_resolution_scopes(scopes) ⇒ Object
Tracks the innermost value scopes during body checking so that
resolve_type_refcan resolve a compile-time reflection type expression (e.g.field.typeinside aninline for) by evaluating it against the local bindings in scope. - #with_unsafe ⇒ Object
- #zero_initializable_type?(type, operation: "zero") ⇒ Boolean
Methods included from CompatibilityHelpers
#event_subscription_result_type, #event_wait_result_type, #range_expr?, #string_literal_cstr_compatibility?, #type_ref_from_specialization
Methods included from Types::Predicates
#arity_error_message, #call_arity_matches?, #callable_param_ref_supported?, #char_pointer_type?, #char_type?, #const_pointer_to, #const_pointer_type?, #contains_ref_type?, #contextual_int_to_float_compatibility?, #contextual_int_to_float_target?, #dyn_type?, #exact_integer_constant_value, #exactly_representable_float32?, #external_numeric_compatibility?, #external_typed_null_pointer_compatibility?, #flatten_field_types, #float_constant_fits_type?, #foreign_boundary_element_compatible?, #foreign_char_pointer_buffer_boundary_compatible?, #foreign_external_layout_compatible?, #foreign_external_layout_field_compatible?, #foreign_function_type_projection_compatible?, #foreign_identity_projection_cast_compatible?, #foreign_identity_projection_compatible?, #foreign_identity_projection_reinterpret_compatible?, #foreign_opaque_c_name, #foreign_span_boundary_compatible?, #function_type_matches_proc_type?, #integer_constant_fits_type?, #integer_like_char_source_type?, #integer_to_char_compatibility?, #lossless_external_integer_compatibility?, #lossless_integer_compatibility?, #method_dispatch_receiver_type, #mutable_pointer_type?, #mutable_to_const_pointer_compatibility?, #native_foreign_layout_compatible?, #null_assignable_to?, #numeric_constant_fits_type?, #opaque_type?, #own_to_raw_pointer_compatibility?, #own_type?, #owned_referent_type, #pointee_type, #pointer_type?, #proc_type?, #quat_vec4_layout_compatible?, #ref_lifetime, #ref_type?, #ref_type_without_lifetime?, #referenced_type, #same_external_opaque_c_name?, #same_external_opaque_handle_pointer_compatibility?, #string_builder_ref_type?, #string_builder_type?, #struct_with_target_type?, #task_root_proc_type?, #task_type?, #value_fits_integer_type?, #void_pointer_type?
Constructor Details
#initialize(ast, imported_modules: {}, allow_missing_imports: false, path: nil, global_import_index: {}) ⇒ Checker
Returns a new instance of Checker.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 140 def initialize(ast, imported_modules: {}, allow_missing_imports: false, path: nil, global_import_index: {}) @path = path @allow_missing_imports = allow_missing_imports @ctx = ModuleContext.new( ast:, module_name: ast.module_name&.to_s, module_kind: ast.module_kind, imported_modules:, global_import_index:, const_declarations: ast.declarations.grep(AST::ConstDecl).each_with_object({}) { |decl, result| result[decl.name] = decl }, ) @null_type = Types::Null.new @error_type = Types::Error.new @loop_depth = 0 @unsafe_depth = 0 @compile_time_depth = 0 @foreign_mapping_depth = 0 @async_function_depth = 0 @checked_function_bindings = {} @checking_function_bindings = {} @evaluating_const_values = [] @evaluated_const_values = {} @error_node_stack = [] @local_completion_frames = [] @active_local_completion_stack = [] @resolved_expr_types = {} @resolved_call_kinds = {} @const_values = {} @next_binding_id = 1 @binding_name_by_id = {} @binding_type_by_id = {} @identifier_binding_ids = {} @declaration_binding_ids = {} @mutating_argument_identifier_ids = {} @mutable_lvalue_argument_identifier_ids = {} @editable_receiver_expression_ids = {} @preassigned_local_binding_ids = {} @nullability_flow_result = nil @unsafe_statement_lines = [] @callable_value_identifier_sites = {} @callable_value_member_access_sites = {} @required_unsafe_lines = [] @uses_parallel_for = false @current_specialization_owner = nil @return_context_stack = [] end |
Instance Attribute Details
#ctx ⇒ Object (readonly)
Returns the value of attribute ctx.
134 135 136 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 134 def ctx @ctx end |
Instance Method Details
#addressable_storage_expression?(expression, scopes:) ⇒ Boolean
950 951 952 953 954 955 956 957 958 959 960 961 962 963 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 950 def addressable_storage_expression?(expression, scopes:) case expression when AST::Identifier true when AST::MemberAccess, AST::IndexAccess addressable_storage_expression?(expression.receiver, scopes:) when AST::Call return false unless expression.arguments.length == 1 && expression.arguments.first.name.nil? read_call?(expression) && ref_type?(infer_expression(expression.arguments.first.value, scopes:)) else false end end |
#aggregate_display_name(type) ⇒ Object
831 832 833 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 831 def aggregate_display_name(type) type.is_a?(Types::StructInstance) ? type.to_s : type.name end |
#aggregate_type?(type) ⇒ Boolean
701 702 703 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 701 def aggregate_type?(type) type.is_a?(Types::Struct) || type.is_a?(Types::Tuple) || span_type?(type) || string_view_type?(type) || task_type?(type) || vector_type?(type) || matrix_type?(type) || quaternion_type?(type) end |
#allocate_binding_id ⇒ Object
1380 1381 1382 1383 1384 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1380 def allocate_binding_id id = @next_binding_id @next_binding_id += 1 id end |
#apply_continuation_refinements!(scopes, refinements) ⇒ Object
14 15 16 17 18 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 14 def apply_continuation_refinements!(scopes, refinements) return if refinements.nil? || refinements.empty? scopes.replace(scopes_with_refinements(scopes, refinements)) end |
#apply_nullability_continuation_refinements!(scopes, next_stmt) ⇒ Object
After processing a statement, apply ControlFlow-derived non-null refinements to the scopes so the next statement benefits from cross-branch narrowing.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 74 def apply_nullability_continuation_refinements!(scopes, next_stmt) return unless @nullability_flow_result nonnull_binding_ids = @nullability_flow_result.nonnull_before(next_stmt) return if nonnull_binding_ids.empty? refinements = {} nonnull_binding_ids.each do |binding_id| next unless binding_id.is_a?(Integer) name = @binding_name_by_id[binding_id] next unless name binding = lookup_value(name, scopes) next unless binding&.id == binding_id next unless binding&.storage_type.is_a?(Types::Nullable) refinements[name] = binding.storage_type.base end apply_continuation_refinements!(scopes, refinements) unless refinements.empty? end |
#argument_types_compatible?(actual_type, expected_type, external:, expression: nil, scopes: nil, contextual_int_to_float: false) ⇒ Boolean
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 65 def argument_types_compatible?(actual_type, expected_type, external:, expression: nil, scopes: nil, contextual_int_to_float: false) return true if types_compatible?(actual_type, expected_type, expression:, scopes:, external_numeric: external, contextual_int_to_float:) return true if external && external_void_pointer_argument_compatibility?(actual_type, expected_type) return true if external && extern_enum_integer_argument_compatibility?(actual_type, expected_type) if external && foreign_mapping_context? && foreign_identity_projection_compatible?(actual_type, expected_type) return false if actual_type == @ctx.types.fetch("cstr") && char_pointer_type?(expected_type) return true end false end |
#array_element_type(type) ⇒ Object
730 731 732 733 734 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 730 def array_element_type(type) return unless array_type?(type) type.arguments.first end |
#array_length(type) ⇒ Object
736 737 738 739 740 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 736 def array_length(type) return unless array_type?(type) type.arguments[1].value end |
#array_to_span_call_argument_compatible?(actual_type, expected_type, expression:, scopes:) ⇒ Boolean
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1223 def array_to_span_call_argument_compatible?(actual_type, expected_type, expression:, scopes:) return false unless expected_type.is_a?(Types::Span) if array_type?(actual_type) return false unless array_element_type(actual_type) == expected_type.element_type infer_addr_source_type(expression, scopes:) record_mutable_lvalue_argument_identifier(expression) return true end if str_buffer_type?(actual_type) return false unless expected_type.element_type == @ctx.types.fetch("char") infer_addr_source_type(expression, scopes:) record_mutable_lvalue_argument_identifier(expression) return true end false rescue SemanticError false end |
#array_type?(type) ⇒ Boolean
725 726 727 728 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 725 def array_type?(type) type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 && !type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments[1].is_a?(Types::LiteralTypeArg) end |
#assignable_receiver?(receiver_expression, scopes) ⇒ Boolean
1355 1356 1357 1358 1359 1360 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1355 def assignable_receiver?(receiver_expression, scopes) infer_lvalue_receiver(receiver_expression, scopes:, allow_ref_identifier: true, allow_pointer_identifier: true, require_mutable_pointer: true) true rescue SemanticError false end |
#atomic_element_type(type) ⇒ Object
759 760 761 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 759 def atomic_element_type(type) type.arguments.first end |
#atomic_method_kind(receiver_type, name) ⇒ Object
654 655 656 657 658 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 654 def atomic_method_kind(receiver_type, name) return unless atomic_type?(receiver_type) ATOMIC_METHOD_KINDS[name] end |
#atomic_type?(type) ⇒ Boolean
755 756 757 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 755 def atomic_type?(type) type.is_a?(Types::GenericInstance) && type.name == "atomic" && type.arguments.length == 1 end |
#attribute_binding_for_handle_expression(expression) ⇒ Object
966 967 968 969 970 971 972 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 966 def attribute_binding_for_handle_expression(expression) return unless expression.is_a?(AST::Call) return unless expression.callee.is_a?(AST::Identifier) && expression.callee.name == "attribute_of" return unless expression.arguments.length == 2 && expression.arguments.none?(&:name) resolve_attribute_name_argument(expression.arguments[1].value) end |
#attribute_presence_guard_active?(scopes, key) ⇒ Boolean
43 44 45 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 43 def attribute_presence_guard_active?(scopes, key) scopes.any? { |scope| scope.key?(key) } end |
#attribute_presence_key_from_call(expression, scopes:) ⇒ Object
175 176 177 178 179 180 181 182 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 175 def attribute_presence_key_from_call(expression, scopes:) target = resolve_reflection_target_argument(expression.arguments.first.value, scopes:) binding = resolve_attribute_name_argument(expression.arguments[1].value) validate_attribute_target_compatibility!(target, binding) attribute_presence_refinement_key(target, binding) rescue SemanticError nil end |
#attribute_presence_refinement_key(target, binding) ⇒ Object
39 40 41 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 39 def attribute_presence_refinement_key(target, binding) AttributePresenceKey.new(target, binding.module_name, binding.name) end |
#attribute_target_kind(target) ⇒ Object
1638 1639 1640 1641 1642 1643 1644 1645 1646 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1638 def attribute_target_kind(target) case target when Types::StructHandle then :struct when Types::FieldHandle then :field when Types::CallableHandle then :callable else raise_sema_error("unsupported attribute reflection target #{target}") end end |
#automatic_foreign_cstr_list_temp_needed?(parameter) ⇒ Boolean
1216 1217 1218 1219 1220 1221 1222 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1216 def automatic_foreign_cstr_list_temp_needed?(parameter) return false unless parameter.type.is_a?(Types::Span) && parameter.type.element_type == @ctx.types.fetch("str") return false unless parameter.boundary_type.is_a?(Types::Span) boundary_element_type = parameter.boundary_type.element_type boundary_element_type == @ctx.types.fetch("cstr") || char_pointer_type?(boundary_element_type) end |
#automatic_foreign_cstr_temp_needed?(parameter, expression, scopes:) ⇒ Boolean
1224 1225 1226 1227 1228 1229 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1224 def automatic_foreign_cstr_temp_needed?(parameter, expression, scopes:) return false unless parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str") return false if expression.is_a?(AST::StringLiteral) && !expression.cstring infer_expression(expression, scopes:) != @ctx.types.fetch("cstr") end |
#binding_resolution_snapshot ⇒ Object
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1369 def binding_resolution_snapshot BindingResolution.new( identifier_binding_ids: @identifier_binding_ids.dup.freeze, declaration_binding_ids: @declaration_binding_ids.dup.freeze, mutating_argument_identifier_ids: @mutating_argument_identifier_ids.dup.freeze, editable_receiver_expression_ids: @editable_receiver_expression_ids.dup.freeze, mutable_lvalue_argument_identifier_ids: @mutable_lvalue_argument_identifier_ids.dup.freeze, binding_types: @binding_type_by_id.dup.freeze, ) end |
#bitwise_type?(type) ⇒ Boolean
414 415 416 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 414 def bitwise_type?(type) type.respond_to?(:bitwise?) && type.bitwise? end |
#build_analysis ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 6 def build_analysis Analysis.new( ast: @ctx.ast, module_name: @ctx.module_name, module_kind: @ctx.module_kind, directives: @ctx.ast.directives, imports: @ctx.imports, types: @ctx.types, interfaces: @ctx.interfaces, attributes: snapshot_attributes, attribute_applications: snapshot_attribute_applications, values: @ctx.top_level_values, functions: @ctx.top_level_functions, methods: snapshot_methods, implemented_interfaces: snapshot_implemented_interfaces, local_completion_frames: @local_completion_frames.dup.freeze, binding_resolution: binding_resolution_snapshot, callable_value_identifier_sites: @callable_value_identifier_sites.dup.freeze, callable_value_member_access_sites: @callable_value_member_access_sites.dup.freeze, required_unsafe_lines: @required_unsafe_lines.uniq.freeze, uses_parallel_for: @uses_parallel_for, resolved_expr_types: @resolved_expr_types.dup.freeze, resolved_call_kinds: @resolved_call_kinds.dup.freeze, const_values: @const_values.dup.freeze, ) end |
#builtin_attribute_handle_type ⇒ Object
98 99 100 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 98 def builtin_attribute_handle_type Types::BUILTIN_ATTRIBUTE_HANDLE_TYPE end |
#builtin_callable_handle_type ⇒ Object
94 95 96 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 94 def builtin_callable_handle_type Types::BUILTIN_CALLABLE_HANDLE_TYPE end |
#builtin_event_error_type ⇒ Object
82 83 84 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 82 def builtin_event_error_type Types::Enum.new("EventError").define_members(@ctx.types.fetch("int"), ["full"]).define_member_values("full" => 0) end |
#builtin_field_handle_type ⇒ Object
90 91 92 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 90 def builtin_field_handle_type Types::BUILTIN_FIELD_HANDLE_TYPE end |
#builtin_member_handle_type ⇒ Object
102 103 104 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 102 def builtin_member_handle_type Types::BUILTIN_MEMBER_HANDLE_TYPE end |
#builtin_struct_handle_type ⇒ Object
86 87 88 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 86 def builtin_struct_handle_type Types::BUILTIN_STRUCT_HANDLE_TYPE end |
#builtin_subscription_type ⇒ Object
78 79 80 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 78 def builtin_subscription_type Types::Subscription.new end |
#builtin_type_meta_type ⇒ Object
106 107 108 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 106 def Types::BUILTIN_TYPE_META_TYPE end |
#c_natively_equality_comparable_type?(type) ⇒ Boolean
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 847 def c_natively_equality_comparable_type?(type) return true if type.is_a?(Types::Primitive) return true if type.is_a?(Types::EnumBase) return true if type.is_a?(Types::Opaque) return true if type.is_a?(Types::Nullable) return true if type.is_a?(Types::Null) return true if type.is_a?(Types::Function) return true if type.is_a?(Types::Error) return true if type.is_a?(Types::StringView) return true if pointer_type?(type) return true if ref_type?(type) return true if type.is_a?(Types::Variant) return true if type.is_a?(Types::VariantArmPayload) false end |
#call_argument_compatible?(actual_type, expected_type, scopes:, external:, expression: nil) ⇒ Boolean
6 7 8 9 10 11 12 13 14 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 6 def call_argument_compatible?(actual_type, expected_type, scopes:, external:, expression: nil) return true if array_to_span_call_argument_compatible?(actual_type, expected_type, expression:, scopes:) return true if argument_types_compatible?(actual_type, expected_type, external:, expression:, scopes:, contextual_int_to_float: !external) return true if implicit_ref_argument_compatible?(actual_type, expected_type, expression, scopes) return true if direct_task_to_proc_argument_compatible?(actual_type, expected_type) return true if direct_function_to_proc_argument_compatible?(actual_type, expected_type, expression, scopes) false end |
#callable_receiver_type_for_specialization(callee, scopes:) ⇒ Object
92 93 94 95 96 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 92 def callable_receiver_type_for_specialization(callee, scopes:) return unless callee.is_a?(AST::MemberAccess) resolve_type_expression(callee.receiver) end |
#callable_type?(type) ⇒ Boolean
418 419 420 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 418 def callable_type?(type) type.is_a?(Types::Function) || type.is_a?(Types::Proc) end |
#canonicalize_call_arguments(arguments, params, context_name, binding = nil) ⇒ Object
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 1013 def canonicalize_call_arguments(arguments, params, context_name, binding = nil) return arguments unless arguments.any?(&:name) named_seen = false by_position = Array.new(params.length) param_names = params.map(&:name) arguments.each_with_index do |arg, _idx| if arg.name named_seen = true raise_sema_error("duplicate named argument '#{arg.name}' in call to #{context_name}") if by_position.any? { |slot| slot&.name == arg.name } param_idx = param_names.index(arg.name) raise_sema_error("unknown parameter '#{arg.name}' for #{context_name}") unless param_idx by_position[param_idx] = arg else raise_sema_error("positional argument after named argument in call to #{context_name}") if named_seen param_idx = _idx raise_sema_error("#{context_name} expects #{params.length} arguments, got #{arguments.length}") if param_idx >= params.length by_position[param_idx] = arg end end fill_parameter_defaults!(by_position, params, binding, context_name) by_position.reject(&:nil?) end |
#cast_numeric_type(type) ⇒ Object
191 192 193 194 195 196 197 198 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 191 def cast_numeric_type(type) return type if type.is_a?(Types::Primitive) && (type.numeric? || type.name == "bool") return type.backing_type if type.is_a?(Types::EnumBase) && type.backing_type.numeric? return type if char_type?(type) return type.backing_type if type.is_a?(Types::EnumBase) && char_type?(type.backing_type) nil end |
#castable_primitive?(type) ⇒ Boolean
477 478 479 480 481 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 477 def castable_primitive?(type) return castable_primitive?(type.base) if type.is_a?(Types::Nullable) type.is_a?(Types::Primitive) || type.is_a?(Types::Enum) || type.is_a?(Types::Flags) end |
#catch_structural ⇒ Object
323 324 325 326 327 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 323 def catch_structural yield rescue SemanticError => e @structural_errors << e end |
#cfg_block_always_terminates?(statements) ⇒ Boolean
55 56 57 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 55 def cfg_block_always_terminates?(statements) ControlFlow::Termination.block_always_terminates?(statements, ignore_name: ->(_name) { false }) end |
#char_array_removed_text_method?(receiver_type, name) ⇒ Boolean
769 770 771 772 773 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 769 def char_array_removed_text_method?(receiver_type, name) return unless char_array_text_type?(receiver_type) name == "as_str" || name == "as_cstr" end |
#char_array_text_type?(type) ⇒ Boolean
742 743 744 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 742 def char_array_text_type?(type) array_type?(type) && array_element_type(type) == @ctx.types.fetch("char") end |
#check ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 187 def check install_builtin_types install_builtin_attributes install_imports install_prelude_types declare_named_types resolve_generic_type_param_constraints resolve_type_aliases declare_attributes resolve_aggregate_fields resolve_enum_members resolve_variant_arms collect_emit_declarations declare_top_level_values check_attribute_applications declare_functions check_interface_conformances check_top_level_values finalize_top_level_const_values check_top_level_static_asserts check_functions build_analysis end |
#check_adapt_call(interface, arguments, scopes:) ⇒ Object
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 974 def check_adapt_call(interface, arguments, scopes:) raise_sema_error("adapt does not support named arguments") if arguments.any?(&:name) raise_sema_error("adapt expects 1 argument, got #{arguments.length}") unless arguments.length == 1 argument = arguments.first concrete_type = infer_expression(argument.value, scopes:) concrete_type = referenced_type(concrete_type) if ref_type?(concrete_type) unless concrete_type.is_a?(Types::Struct) || concrete_type.is_a?(Types::Opaque) || concrete_type.is_a?(Types::StructInstance) raise_sema_error("adapt requires a struct or opaque type, got #{concrete_type}") end unless type_implements_interface?(concrete_type, interface) raise_sema_error("#{concrete_type} does not implement #{interface.name}") end Types::Dyn.new(interface, interface.type_arguments || []) end |
#check_aggregate_construction(struct_type, arguments, scopes:) ⇒ Object
47 48 49 50 51 52 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 47 def check_aggregate_construction(struct_type, arguments, scopes:) require_unsafe!("str construction requires unsafe") if struct_type.is_a?(Types::StringView) check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "aggregate construction") struct_type end |
#check_aggregate_field_arguments(struct_type, arguments, scopes:, context:) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 77 def check_aggregate_field_arguments(struct_type, arguments, scopes:, context:) display_name = aggregate_display_name(struct_type) raise_sema_error("#{context} for #{display_name} requires named arguments") unless arguments.all?(&:name) provided = {} arguments.each do |argument| field_type = struct_type.field(argument.name) raise_sema_error("unknown field #{display_name}.#{argument.name}") unless field_type raise_sema_error("duplicate field #{display_name}.#{argument.name}") if provided.key?(argument.name) actual_type = infer_expression(argument.value, scopes:, expected_type: field_type) ensure_assignable!( actual_type, field_type, "field #{display_name}.#{argument.name} expects #{field_type}, got #{actual_type}", expression: argument.value, external_numeric: struct_type.respond_to?(:external) && struct_type.external, external_pointer_null: struct_type.respond_to?(:external) && struct_type.external, contextual_int_to_float: contextual_int_to_float_target?(field_type), ) provided[argument.name] = true end end |
#check_array_construction(array_type, arguments, scopes:) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 138 def check_array_construction(array_type, arguments, scopes:) raise_sema_error("array construction does not support named arguments") if arguments.any?(&:name) element_type = array_element_type(array_type) length = array_length(array_type) raise_sema_error("array expects at most #{length} elements, got #{arguments.length}") if arguments.length > length arguments.each do |argument| actual_type = infer_expression(argument.value, scopes:, expected_type: element_type) ensure_assignable!( actual_type, element_type, "array element expects #{element_type}, got #{actual_type}", expression: argument.value, ) end array_type end |
#check_assignment(statement, scopes:) ⇒ Object
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 469 def check_assignment(statement, scopes:) if statement.operator == "=" && statement.target.is_a?(AST::IndexAccess) && statement.target.index.is_a?(AST::RangeExpr) return check_range_index_assignment(statement, scopes:) end target_type = infer_lvalue(statement.target, scopes:) raise_sema_error("cannot assign to non-copyable event storage type #{target_type}") if noncopyable_event_storage_type?(target_type) validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false) value_type = infer_expression(statement.value, scopes:, expected_type: target_type) case statement.operator when "=" ensure_assignable!( value_type, target_type, "cannot assign #{value_type} to #{target_type}", expression: statement.value, external_numeric: external_numeric_assignment_target?(statement.target, scopes:), contextual_int_to_float: contextual_int_to_float_target?(target_type), line: statement.line, column: statement.column, ) when "+=", "-=", "*=", "/=" binary_op = statement.operator[0...-1] if (result_type = vector_arithmetic_result(binary_op, target_type, value_type)) raise_sema_error("operator #{statement.operator} on #{target_type} and #{value_type} produces #{result_type}, expected #{target_type}") unless result_type == target_type else raise_sema_error("operator #{statement.operator} requires matching numeric types, got #{target_type} and #{value_type}") unless target_type.numeric? && value_type.numeric? ensure_assignable!( value_type, target_type, "operator #{statement.operator} requires matching numeric types, got #{target_type} and #{value_type}", expression: statement.value, contextual_int_to_float: contextual_int_to_float_target?(target_type), line: statement.line, column: statement.column, ) end when "%=" unless common_integer_type(target_type, value_type) == target_type || simd_mod_result(target_type, value_type) raise_sema_error("operator #{statement.operator} requires compatible integer types, got #{target_type} and #{value_type}") end when "&=", "|=", "^=" unless (target_type == value_type && bitwise_type?(target_type)) || (simd_type?(target_type) && simd_type?(value_type) && target_type == value_type) raise_sema_error("operator #{statement.operator} requires matching integer or flags types, got #{target_type} and #{value_type}") end when "<<=", ">>=" unless (target_type.is_a?(Types::Primitive) && target_type.integer? && value_type.is_a?(Types::Primitive) && value_type.integer?) || simd_shift_result(target_type, value_type) raise_sema_error("operator #{statement.operator} requires integer operands, got #{target_type} and #{value_type}") end else raise_sema_error("unsupported assignment operator #{statement.operator}") end end |
#check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:) ⇒ Object
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 666 def check_atomic_method_call(kind, receiver_type, receiver, arguments, scopes:) elem_type = atomic_element_type(receiver_type) raise_sema_error("atomic methods do not support named arguments") if arguments.any?(&:name) case kind when :atomic_load raise_sema_error("load expects 0 arguments, got #{arguments.length}") unless arguments.empty? elem_type when :atomic_store raise_sema_error("store expects 1 argument, got #{arguments.length}") unless arguments.length == 1 record_editable_receiver_expression(receiver) raise_sema_error("cannot call editable method store on an immutable receiver") unless assignable_receiver?(receiver, scopes) arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type) ensure_assignable!(arg_type, elem_type, "store expects #{elem_type}, got #{arg_type}") @ctx.types.fetch("void") when :atomic_add, :atomic_sub, :atomic_exchange raise_sema_error("#{kind.to_s.delete_prefix("atomic_")} expects 1 argument, got #{arguments.length}") unless arguments.length == 1 record_editable_receiver_expression(receiver) raise_sema_error("cannot call editable method #{kind.to_s.delete_prefix("atomic_")} on an immutable receiver") unless assignable_receiver?(receiver, scopes) arg_type = infer_expression(arguments.first.value, scopes:, expected_type: elem_type) ensure_assignable!(arg_type, elem_type, "#{kind.to_s.delete_prefix("atomic_")} expects #{elem_type}, got #{arg_type}") elem_type when :atomic_compare_exchange raise_sema_error("compare_exchange expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 record_editable_receiver_expression(receiver) raise_sema_error("cannot call editable method compare_exchange on an immutable receiver") unless assignable_receiver?(receiver, scopes) exp_type = infer_expression(arguments[0].value, scopes:, expected_type: elem_type) des_type = infer_expression(arguments[1].value, scopes:, expected_type: elem_type) ensure_assignable!(exp_type, elem_type, "compare_exchange expected type #{elem_type}, got #{exp_type}") ensure_assignable!(des_type, elem_type, "compare_exchange desired type #{elem_type}, got #{des_type}") @ctx.types.fetch("bool") end end |
#check_attribute_applications ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/milk_tea/core/semantic_analyzer/attributes.rb', line 6 def check_attribute_applications .each do |decl| with_error_node(decl) do case decl when AST::StructDecl packed, alignment = check_decl_attribute_applications!(decl.attributes, target_kind: :struct, target_label: "struct #{decl.name}", target_node: decl) @ctx.types.fetch(decl.name).set_layout(packed:, alignment:) decl.fields.each do |field| with_error_node(field) do if raw_module? && field.attributes.any? raise_sema_error("attributes are not allowed on fields in external files") end check_decl_attribute_applications!(field.attributes, target_kind: :field, target_label: "field #{decl.name}.#{field.name}", target_node: field) end end when AST::FunctionDef, AST::ExternFunctionDecl, AST::ForeignFunctionDecl check_decl_attribute_applications!(decl.attributes, target_kind: :callable, target_label: "callable #{decl.name}", target_node: decl) when AST::InterfaceDecl decl.methods.each do |method| with_error_node(method) do check_decl_attribute_applications!(method.attributes, target_kind: :callable, target_label: "callable #{decl.name}.#{method.name}", target_node: method) end end when AST::ExtendingBlock decl.methods.each do |method| with_error_node(method) do check_decl_attribute_applications!(method.attributes, target_kind: :callable, target_label: "callable #{decl.type_name}.#{method.name}", target_node: method) end end when AST::ConstDecl check_decl_attribute_applications!(decl.attributes, target_kind: :const, target_label: "const #{decl.name}", target_node: decl) when AST::EventDecl check_decl_attribute_applications!(decl.attributes, target_kind: :event, target_label: "event #{decl.name}", target_node: decl) when AST::UnionDecl check_decl_attribute_applications!(decl.attributes, target_kind: :union, target_label: "union #{decl.name}", target_node: decl) when AST::EnumDecl check_decl_attribute_applications!(decl.attributes, target_kind: :enum, target_label: "enum #{decl.name}", target_node: decl) when AST::FlagsDecl check_decl_attribute_applications!(decl.attributes, target_kind: :flags, target_label: "flags #{decl.name}", target_node: decl) when AST::VariantDecl check_decl_attribute_applications!(decl.attributes, target_kind: :variant, target_label: "variant #{decl.name}", target_node: decl) end end end end |
#check_attribute_arg_call(expected_type, arguments, scopes:) ⇒ Object
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 940 def check_attribute_arg_call(expected_type, arguments, scopes:) raise_sema_error("attribute_arg does not support named arguments") if arguments.any?(&:name) raise_sema_error("attribute_arg expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 handle_value = evaluate_compile_time_const_value(arguments.first.value, scopes:) parameter_source = if handle_value.is_a?(Types::AttributeHandle) [handle_value.attribute_name, handle_value.params] else handle_type = infer_expression(arguments.first.value, scopes:) raise_sema_error("attribute_arg expects an attribute handle") unless handle_type == builtin_attribute_handle_type binding = attribute_binding_for_handle_expression(arguments.first.value) raise_sema_error("attribute_arg expects an attribute handle") unless binding [binding.name, binding.params] end param_name = reflection_identifier_name(arguments[1].value, context: "attribute_arg") attribute_name, params = parameter_source parameter = params.find { |candidate| candidate.name == param_name } raise_sema_error("attribute #{attribute_name} has no parameter #{param_name}") unless parameter raise_sema_error("attribute_arg[#{expected_type}] does not match declared type #{parameter.type} for #{attribute_name}.#{param_name}") unless parameter.type == expected_type expected_type end |
#check_attribute_arguments!(binding, application) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/milk_tea/core/semantic_analyzer/attributes.rb', line 100 def check_attribute_arguments!(binding, application) params = binding.params arguments = application.arguments if params.empty? raise_sema_error("attribute #{binding.name} does not take arguments") if arguments.any? return {} end bound_arguments = {} next_position = 0 arguments.each do |argument| param = if argument.name params.find { |candidate| candidate.name == argument.name }.tap do |candidate| raise_sema_error("unknown attribute argument #{binding.name}.#{argument.name}") unless candidate end else while next_position < params.length && bound_arguments.key?(params[next_position].name) next_position += 1 end raise_sema_error("attribute #{binding.name} expects #{params.length} arguments, got #{arguments.length}") if next_position >= params.length param = params[next_position] next_position += 1 param end raise_sema_error("duplicate attribute argument #{binding.name}.#{param.name}") if bound_arguments.key?(param.name) bound_arguments[param.name] = argument.value end missing = params.reject { |param| bound_arguments.key?(param.name) } unless missing.empty? names = missing.map(&:name).join(", ") raise_sema_error("attribute #{binding.name} is missing required arguments: #{names}") end params.each_with_object({}) do |param, values| argument_expression = bound_arguments.fetch(param.name) actual_type = infer_expression(argument_expression, scopes: [], expected_type: param.type) ensure_assignable!( actual_type, param.type, "attribute #{binding.name} argument #{param.name} expects #{param.type}, got #{actual_type}", expression: argument_expression, ) const_value = evaluate_compile_time_const_value(argument_expression) raise_sema_error("attribute #{binding.name} argument #{param.name} must be a compile-time constant") if const_value.nil? values[param.name] = const_value end end |
#check_attribute_of_call(arguments, scopes:) ⇒ Object
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 925 def check_attribute_of_call(arguments, scopes:) raise_sema_error("attribute_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("attribute_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 target = resolve_reflection_target_argument(arguments.first.value, scopes:) binding = resolve_attribute_name_argument(arguments[1].value) validate_attribute_target_compatibility!(target, binding) application = find_attribute_application(target, binding) unless application || attribute_presence_guard_active?(scopes, attribute_presence_refinement_key(target, binding)) raise_sema_error("attribute #{qualified_attribute_name(binding)} is not applied to #{target}") end builtin_attribute_handle_type end |
#check_block(statements, scopes:, return_type:, allow_return: true) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 6 def check_block(statements, scopes:, return_type:, allow_return: true) statements ||= [] with_return_context(return_type, allow_return:) do with_nested_scope(scopes) do |nested_scopes| with_type_resolution_scopes(nested_scopes) do statements.each_with_index do |statement, idx| begin record_local_completion_snapshot( statement.line, statement.column || 0, nested_scopes, ) refinements = check_statement(statement, scopes: nested_scopes, return_type:, allow_return:) apply_continuation_refinements!(nested_scopes, refinements) if @nullability_flow_result && idx + 1 < statements.length apply_nullability_continuation_refinements!(nested_scopes, statements[idx + 1]) end end_line = statement_end_line(statement) # When the statement spans multiple lines (e.g. let … else:), # record an extra snapshot at the declaration line so that # hover and completion lookups on the declaration line itself # can still find the new binding. if end_line && statement.line && end_line > statement.line record_local_completion_snapshot( statement.line, statement.column || 0, nested_scopes, ) end record_local_completion_snapshot(end_line, 1_000_000, nested_scopes) rescue SemanticError => e if @collecting_errors @structural_errors << e next end raise e unless e.line.nil? stmt_line = statement.line raise e if stmt_line.nil? raise_sema_error(e., statement) end end end end end end |
#check_block_body_const(decl) ⇒ Object
60 61 62 63 64 65 66 67 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 60 def check_block_body_const(decl) return unless decl.block_body last_stmt = decl.block_body.last unless last_stmt.is_a?(AST::ReturnStmt) && last_stmt.value raise_sema_error("block-bodied const must end with a return statement") end end |
#check_callable_of_call(arguments, scopes:) ⇒ Object
908 909 910 911 912 913 914 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 908 def check_callable_of_call(arguments, scopes:) raise_sema_error("callable_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("callable_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1 evaluate_callable_of_call(arguments, scopes:) builtin_callable_handle_type end |
#check_callable_value_call(function_type, arguments, scopes:, callee_expression:) ⇒ Object
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 823 def check_callable_value_call(function_type, arguments, scopes:, callee_expression:) arguments = canonicalize_call_arguments(arguments, function_type.params, describe_expression(callee_expression)) unless call_arity_matches?(function_type, arguments.length) raise_sema_error((function_type, describe_expression(callee_expression), arguments.length)) end function_type.params.each_with_index do |parameter, index| argument = arguments.fetch(index) actual_type = infer_expression(argument.value, scopes:, expected_type: parameter.type) unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: false, expression: argument.value) suggestion = explicit_cast_suggestion(actual_type, parameter.type) raise_sema_error("argument #{parameter.name || index} to #{describe_expression(callee_expression)} expects #{parameter.type}, got #{actual_type}", argument, suggestion:) end end arguments.drop(function_type.params.length).each do |argument| infer_expression(argument.value, scopes:) end end |
#check_cast_call(target_type, arguments, scopes:) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 158 def check_cast_call(target_type, arguments, scopes:) raise_sema_error("cast requires exactly one argument") unless arguments.length == 1 raise_sema_error("cast does not support named arguments") if arguments.first.name source_type = infer_expression(arguments.first.value, scopes:) if source_type == target_type return target_type end if pointer_cast?(source_type, target_type) expression = arguments.first.value require_unsafe!("pointer cast requires unsafe", line: source_line(expression), column: source_column(expression)) return target_type end if ref_to_pointer_cast?(source_type, target_type) expression = arguments.first.value require_unsafe!("ref to pointer cast requires unsafe", line: source_line(expression), column: source_column(expression)) return target_type end source_numeric_type = cast_numeric_type(source_type) target_numeric_type = cast_numeric_type(target_type) unless source_numeric_type && target_numeric_type raise_sema_error("cast requires compatible types, got #{source_type} -> #{target_type}") end target_type end |
#check_collecting_errors ⇒ Object
Like check, but collects per-function errors instead of raising at first. Structural phases (imports, type resolution, declaration) collect errors per declaration so that the maximum number of diagnostics are surfaced. Returns { analysis: Analysis, errors: [SemanticError] }.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 274 def check_collecting_errors @collecting_errors = true @structural_errors = [] catch_structural { install_builtin_types } catch_structural { install_builtin_attributes } catch_structural { install_imports } catch_structural { install_prelude_types } catch_structural { declare_named_types } catch_structural { resolve_generic_type_param_constraints } catch_structural { resolve_type_aliases } catch_structural { declare_attributes } catch_structural { resolve_aggregate_fields } catch_structural { resolve_enum_members } catch_structural { resolve_variant_arms } catch_structural { collect_emit_declarations } catch_structural { declare_top_level_values } catch_structural { check_attribute_applications } catch_structural { declare_functions } catch_structural { check_interface_conformances } errors = @structural_errors.dup begin check_top_level_values rescue SemanticError => e errors << e end errors.concat(@structural_errors.drop(errors.length)) begin finalize_top_level_const_values rescue SemanticError => e errors << e end begin check_top_level_static_asserts rescue SemanticError => e errors << e end check_functions_collecting(errors) analysis = build_analysis { analysis: analysis, errors: errors.uniq { |e| [e., e.line, e.column, e.length] } } end |
#check_const_ptr_of_call(arguments, scopes:) ⇒ Object
879 880 881 882 883 884 885 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 879 def check_const_ptr_of_call(arguments, scopes:) raise_sema_error("const_ptr_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("const_ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1 source_type = infer_ro_addr_source_type(arguments.first.value, scopes:) const_pointer_to(source_type) end |
#check_decl_attribute_applications!(applications, target_kind:, target_label:, target_node:) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/milk_tea/core/semantic_analyzer/attributes.rb', line 54 def check_decl_attribute_applications!(applications, target_kind:, target_label:, target_node:) seen = {} packed = false alignment = nil resolved_applications = [] applications.each do |application| with_error_node(application) do binding = resolve_attribute_binding(application.name) if raw_module? raise_sema_error("only built-in struct attributes are allowed in external files") unless binding.builtin && target_kind == :struct end unless binding.targets.include?(target_kind) raise_sema_error("attribute #{binding.name} cannot target #{target_kind}") end binding_key = [binding.module_name, binding.name] raise_sema_error("duplicate attribute #{binding.name} on #{target_label}") if seen.key?(binding_key) seen[binding_key] = true argument_values = check_attribute_arguments!(binding, application) argument_values = argument_values.freeze @ctx.attribute_application_bindings[application.object_id] = binding @ctx.validated_attribute_arguments[application.object_id] = argument_values resolved_applications << ResolvedAttributeApplication.new(binding:, argument_values:) case binding.name when "packed" packed = true when "align" bytes = argument_values.fetch("bytes") raise_sema_error("align(...) requires a positive alignment") unless bytes.is_a?(Integer) && bytes.positive? raise_sema_error("align(...) requires a power-of-two alignment, got #{bytes}") unless power_of_two?(bytes) alignment = bytes end end end @ctx.resolved_attribute_applications[target_node.object_id] = resolved_applications.freeze [packed, alignment] end |
#check_definite_assignment(binding) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 6 def check_definite_assignment(binding) return unless binding.ast.respond_to?(:body) resolution = binding_resolution_snapshot graph = ControlFlow::Builder.new( binding_resolution: ControlFlow::BindingResolution.new( identifier_binding_ids: resolution.identifier_binding_ids, declaration_binding_ids: resolution.declaration_binding_ids, mutating_argument_identifier_ids: resolution.mutating_argument_identifier_ids, ), strict_binding_ids: true, local_decl_without_initializer_writes: true, ).build(binding.ast.body) local_declared_ids = Set.new graph.each_node do |node| node.writes_info.each do |write| origin = write[:origin] next unless %i[declaration for_binding match_binding].include?(origin) local_declared_ids << write[:binding_key] end end initially_assigned = binding.body_params.each_with_object(Set.new) do |param, set| set << param.id if param.id end # Any binding read but never defined in this function body is treated as # preassigned (for example module-level const/var bindings). initially_assigned.merge(graph.read_bindings - local_declared_ids) result = ControlFlow::DefiniteAssignment.solve(graph, initially_assigned:) first_issue = result.read_before_assignment.min_by do |issue| [issue.line || Float::INFINITY, issue.column || Float::INFINITY, issue.node_id] end return unless first_issue name = @binding_name_by_id[first_issue.binding_key] || first_issue.binding_key raise SemanticError.new( "read of '#{name}' before definite assignment", line: first_issue.line, column: first_issue.column, length: first_issue.length || name.to_s.length, path: @path, ) end |
#check_dyn_method_call(method_binding, _receiver, arguments, scopes:) ⇒ Object
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 992 def check_dyn_method_call(method_binding, _receiver, arguments, scopes:) raise_sema_error("#{method_binding.name} expects #{method_binding.params.length} arguments, got #{arguments.length}") unless arguments.length == method_binding.params.length method_binding.params.each_with_index do |param, index| actual_type = infer_expression(arguments[index].value, scopes:, expected_type: param.type) ensure_assignable!( actual_type, param.type, "argument #{index + 1} of #{method_binding.name} expects #{param.type}, got #{actual_type}", expression: arguments[index].value, ) end end |
#check_emit_stmt(statement) ⇒ Object
1214 1215 1216 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1214 def check_emit_stmt(statement) raise_sema_error("emit is only allowed inside const function or inline blocks") unless @compile_time_depth.positive? end |
#check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
584 585 586 587 588 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 584 def check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) each_enum_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes| check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:) end end |
#check_equal_call(resolution, arguments, scopes:) ⇒ Object
434 435 436 437 438 439 440 441 442 443 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 434 def check_equal_call(resolution, arguments, scopes:) raise_sema_error("equal does not support named arguments") if arguments.any?(&:name) raise_sema_error("equal expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 arguments.each do |argument| validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "equal") end @ctx.types.fetch("bool") end |
#check_event_method_call(kind, receiver, arguments, scopes:) ⇒ Object
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 517 def check_event_method_call(kind, receiver, arguments, scopes:) method_name = event_method_name(kind) receiver_type = infer_expression(receiver, scopes:) raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless event_type?(receiver_type) raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name) raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless event_receiver_mutable?(receiver, scopes:) if kind == :event_emit raise_sema_error("#{receiver_type}.emit is only available inside module #{receiver_type.module_name}") unless receiver_type.module_name == @ctx.module_name end case kind when :event_subscribe, :event_subscribe_once if arguments.length == 2 && arguments.none?(&:name) state_type = infer_expression(arguments[0].value, scopes:) unless pointer_type?(state_type) suggestion = if arguments[0].value.is_a?(AST::Identifier) "use ptr_of(#{arguments[0].value.name})" end 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:) end state_pointed_type = state_type.arguments.first expected_listener_type = event_stateful_listener_type(receiver_type, state_pointed_type) actual_listener_type = infer_expression(arguments[1].value, scopes:, expected_type: expected_listener_type) ensure_argument_assignable!( actual_listener_type, expected_listener_type, external: false, message: "listener argument to #{receiver_type}.#{method_name} expects #{expected_listener_type}, got #{actual_listener_type}", expression: arguments[1].value, ) elsif arguments.length == 1 listener_type = event_listener_type(receiver_type) actual_type = infer_expression(arguments.first.value, scopes:, expected_type: listener_type) ensure_argument_assignable!( actual_type, listener_type, external: false, message: "argument listener to #{receiver_type}.#{method_name} expects #{listener_type}, got #{actual_type}", expression: arguments.first.value, ) else raise_sema_error("#{method_name} expects 1 or 2 arguments, got #{arguments.length}") end event_subscription_result_type when :event_unsubscribe raise_sema_error("unsubscribe expects 1 argument, got #{arguments.length}") unless arguments.length == 1 actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("Subscription")) ensure_argument_assignable!( actual_type, @ctx.types.fetch("Subscription"), external: false, message: "argument subscription to #{receiver_type}.unsubscribe expects Subscription, got #{actual_type}", expression: arguments.first.value, ) @ctx.types.fetch("bool") when :event_emit if receiver_type.payload_type.nil? raise_sema_error("emit expects 0 arguments, got #{arguments.length}") unless arguments.empty? else raise_sema_error("emit expects 1 argument, got #{arguments.length}") unless arguments.length == 1 actual_type = infer_expression(arguments.first.value, scopes:, expected_type: receiver_type.payload_type) ensure_argument_assignable!( actual_type, receiver_type.payload_type, external: false, message: "argument value to #{receiver_type}.emit expects #{receiver_type.payload_type}, got #{actual_type}", expression: arguments.first.value, ) end @ctx.types.fetch("void") when :event_wait raise_sema_error("wait expects 0 arguments, got #{arguments.length}") unless arguments.empty? Types::Registry.task(event_wait_result_type(receiver_type)) else raise_sema_error("unsupported event method #{kind}") end end |
#check_expr_const(decl) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 41 def check_expr_const(decl) binding = @ctx.top_level_values.fetch(decl.name) validate_consuming_foreign_expression!(decl.value, scopes: [], root_allowed: false) validate_hoistable_foreign_expression!(decl.value, scopes: [], root_hoistable: false) if binding.type == evaluate_compile_time_const_value(decl.value) return end actual_type = infer_expression(decl.value, scopes: [], expected_type: binding.type) ensure_assignable!( actual_type, binding.type, "cannot assign #{actual_type} to constant #{decl.name}: expected #{binding.type}", expression: decl.value, ) end |
#check_expr_names(expr, scopes, binding, names) ⇒ Object
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 397 def check_expr_names(expr, scopes, binding, names) case expr when AST::Identifier check_generic_name(expr.name, scopes, binding, expr, names) when AST::MemberAccess check_expr_names(expr.receiver, scopes, binding, names) when AST::Call check_expr_names(expr.callee, scopes, binding, names) expr.arguments.each { |arg| check_expr_names(arg.value, scopes, binding, names) } when AST::BinaryOp check_expr_names(expr.left, scopes, binding, names) check_expr_names(expr.right, scopes, binding, names) when AST::UnaryOp check_expr_names(expr.operand, scopes, binding, names) when AST::IfExpr check_expr_names(expr.condition, scopes, binding, names) check_expr_names(expr.then_expression, scopes, binding, names) check_expr_names(expr.else_expression, scopes, binding, names) when AST::MatchExpr check_expr_names(expr.expression, scopes, binding, names) expr.arms.each do |arm| arm_names = names.dup arm_names.add(arm.binding_name) if arm.binding_name check_expr_names(arm.value, scopes, binding, arm_names) end when AST::IndexAccess check_expr_names(expr.receiver, scopes, binding, names) check_expr_names(expr.index, scopes, binding, names) when AST::Specialization check_expr_names(expr.callee, scopes, binding, names) when AST::PrefixCast check_expr_names(expr.expression, scopes, binding, names) when AST::AwaitExpr check_expr_names(expr.expression, scopes, binding, names) when AST::UnsafeExpr check_expr_names(expr.expression, scopes, binding, names) when AST::RangeExpr check_expr_names(expr.start_expr, scopes, binding, names) if expr.start_expr check_expr_names(expr.end_expr, scopes, binding, names) if expr.end_expr when AST::ExpressionList expr.elements.each { |elem| check_expr_names(elem, scopes, binding, names) } when AST::ProcExpr body_names = names.dup Array(expr.params).each { |p| body_names.add(p.name) if p.respond_to?(:name) } expr.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::DetachExpr check_expr_names(expr.body, scopes, binding, names) when AST::FormatExprPart check_expr_names(expr.expression, scopes, binding, names) when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral, AST::CharLiteral, AST::FormatString, AST::FormatTextPart nil end end |
#check_fatal_call(arguments, scopes:) ⇒ Object
843 844 845 846 847 848 849 850 851 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 843 def check_fatal_call(arguments, scopes:) raise_sema_error("fatal does not support named arguments") if arguments.any?(&:name) raise_sema_error("fatal expects 1 argument, got #{arguments.length}") unless arguments.length == 1 = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str")) return @ctx.types.fetch("void") if string_like_type?() raise_sema_error("fatal expects str or cstr, got #{}") end |
#check_field_of_call(arguments, scopes:) ⇒ Object
901 902 903 904 905 906 907 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 901 def check_field_of_call(arguments, scopes:) raise_sema_error("field_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("field_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 evaluate_field_of_call(arguments, scopes:) builtin_field_handle_type end |
#check_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1048 def check_for_stmt(statement, scopes:, return_type:, allow_return:) if statement.threaded check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:) return end statement.iterables.each do |iterable| validate_consuming_foreign_expression!(iterable, scopes:, root_allowed: false) end raise_sema_error("for loop binder count must match iterable count") unless statement.bindings.length == statement.iterables.length binding_infos = if statement.parallel? check_parallel_for_bindings(statement, scopes:) else iterable_type = nil loop_type = if range_expr?(statement.iterable) check_range_expr_loop(statement.iterable, scopes:) else iterable_type = infer_expression(statement.iterable, scopes:) collection_loop_type(iterable_type) || iterator_loop_type(iterable_type) end raise_sema_error("for loop expects start..stop, array[T, N], span[T], or an iterable with iter()/next()") unless loop_type binding_type = if iterable_type collection_loop_binding_type(iterable_type, loop_type) || loop_type else loop_type end [{ binding: statement.binding, type: binding_type }] end with_nested_scope(scopes) do |loop_scopes| binding_infos.each do |entry| binding = entry[:binding] ensure_non_reserved_primitive_name!(binding.name, kind_label: "for binding", line: binding.line, column: binding.column) current_actual_scope(loop_scopes)[binding.name] = value_binding( name: binding.name, type: entry[:type], mutable: false, kind: :let, id: @preassigned_local_binding_ids[binding.object_id], ) record_declaration_binding(binding, current_actual_scope(loop_scopes)[binding.name]) end with_loop do check_block(statement.body, scopes: loop_scopes, return_type:, allow_return:) end end end |
#check_format_string_literal(format_string, scopes:) ⇒ Object
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1485 def check_format_string_literal(format_string, scopes:) format_string.parts.each do |part| next unless part.is_a?(AST::FormatExprPart) value_type = infer_expression(part.expression, scopes:) if part.format_spec case part.format_spec[:kind] when :precision unless value_type.is_a?(Types::Primitive) && value_type.float? raise_sema_error("format spec ':.N' is only valid for float and double, got #{value_type}") end when :hex unless format_string_integer_base_spec_supported?(value_type) raise_sema_error("format spec ':x' and ':X' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}") end when :oct unless format_string_integer_base_spec_supported?(value_type) raise_sema_error("format spec ':o' and ':O' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}") end when :bin unless format_string_integer_base_spec_supported?(value_type) raise_sema_error("format spec ':b' and ':B' are only valid for integer primitives and integer-backed enums/flags, got #{value_type}") end else raise_sema_error("unsupported format spec #{part.format_spec.inspect}") end else next if format_string_interpolation_supported?(value_type, context: "formatted string interpolation of #{value_type}") raise_sema_error("formatted string interpolation supports str, cstr, bool, numeric primitives, integer-backed enums/flags, and types implementing format_len()/append_format(output: ref[std.string.String]), got #{value_type}") end end end |
#check_function(binding) ⇒ Object
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 614 def check_function(binding) @local_completion_frames = @local_completion_frames.dup if @local_completion_frames.frozen? previous_type_substitutions = @current_type_substitutions previous_specialization_owner = @current_specialization_owner started_check = false return if binding.external return if @checked_function_bindings[binding.object_id] return if @checking_function_bindings[binding.object_id] @checking_function_bindings[binding.object_id] = true started_check = true @current_type_substitutions = binding.type_substitutions @current_specialization_owner = binding.specialization_owner with_error_node(binding.ast) do with_scope(binding.body_params) do |scopes| start_local_completion_frame(binding, scopes) if binding.type_params.any? if binding.ast.respond_to?(:body) check_generic_method_immutable_this(binding, scopes) check_generic_method_names(binding, scopes) end return end if binding.ast.is_a?(AST::ForeignFunctionDecl) record_callable_value_expression_site(binding.ast.mapping) unless binding.ast.mapping.is_a?(AST::Call) expression = foreign_mapping_expression(binding.ast) actual_type = with_foreign_mapping_context do infer_expression(expression, scopes:, expected_type: binding.type.return_type) end unless types_compatible?(actual_type, binding.type.return_type, expression:) || foreign_identity_projection_compatible?(actual_type, binding.type.return_type) raise_sema_error("foreign mapping #{binding.name} expects #{binding.type.return_type}, got #{actual_type}") end else validate_async_function_body!(binding.ast.body) if binding.async preassign_local_binding_ids(binding.ast.body) run_nullability_pre_pass(binding, scopes) if binding.ast.respond_to?(:const) && binding.ast.const with_compile_time do if binding.async with_async_function do check_block(binding.ast.body, scopes:, return_type: binding.body_return_type) end else check_block(binding.ast.body, scopes:, return_type: binding.type.return_type) end end elsif binding.async with_async_function do check_block(binding.ast.body, scopes:, return_type: binding.body_return_type) end else check_block(binding.ast.body, scopes:, return_type: binding.type.return_type) end check_definite_assignment(binding) end end end ensure return unless started_check @checked_function_bindings[binding.object_id] = true finish_local_completion_frame(binding) @preassigned_local_binding_ids = {} @nullability_flow_result = nil @current_type_substitutions = previous_type_substitutions @current_specialization_owner = previous_specialization_owner @checking_function_bindings.delete(binding.object_id) end |
#check_function_call(binding, arguments, scopes:) ⇒ Object
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 785 def check_function_call(binding, arguments, scopes:) arguments = canonicalize_call_arguments(arguments, binding.type.params, binding.name, binding) fill_positional_defaults!(arguments, binding) expected_params = binding.type.params required_count = count_required_params(binding) variadic = binding.type.is_a?(Types::Function) && binding.type.variadic unless variadic ? arguments.length >= required_count : arguments.length <= expected_params.length && arguments.length >= required_count raise_sema_error((binding.type, binding.name, arguments.length, required_count:)) end expected_params.each_with_index do |parameter, index| argument = arguments.fetch(index) actual_type = foreign_argument_actual_type(parameter, argument, scopes:, function_name: binding.name, expected_type: parameter.type) record_mutating_argument_identifier(argument, parameter) if foreign_cstr_boundary_parameter?(parameter) unless foreign_cstr_argument_compatible?(actual_type, parameter, expression: foreign_argument_expression(argument)) raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}") end else unless call_argument_compatible?(actual_type, parameter.type, scopes:, external: binding.external, expression: foreign_argument_expression(argument)) suggestion = explicit_cast_suggestion(actual_type, parameter.type) raise_sema_error("argument #{parameter.name} to #{binding.name} expects #{parameter.type}, got #{actual_type}", suggestion:) end end end arguments.drop(expected_params.length).each do |argument| infer_expression(argument.value, scopes:) end end |
#check_functions ⇒ Object
252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 252 def check_functions @ctx.top_level_functions.each_value do |binding| check_function(binding) end @ctx.methods.each_value do |method_map| method_map.each_value do |binding| check_function(binding) end end end |
#check_functions_collecting(errors) ⇒ Object
Per-function error collection used by check_collecting_errors. Continues past individual function failures, accumulating SemanticErrors.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 282 def check_functions_collecting(errors) @ctx.top_level_functions.each_value do |binding| next if @checked_function_bindings[binding.object_id] prev_count = @structural_errors.length begin check_function(binding) rescue SemanticError => e errors << e end errors.concat(@structural_errors[prev_count..].to_a) end @ctx.methods.each_value do |method_map| method_map.each_value do |binding| next if @checked_function_bindings[binding.object_id] prev_count = @structural_errors.length begin check_function(binding) rescue SemanticError => e errors << e end errors.concat(@structural_errors[prev_count..].to_a) end end end |
#check_gather_stmt(statement, scopes:) ⇒ Object
1187 1188 1189 1190 1191 1192 1193 1194 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1187 def check_gather_stmt(statement, scopes:) raise_sema_error("gather requires at least one handle", line: statement.line, column: statement.column) if statement.handles.empty? statement.handles.each do |handle| handle_type = infer_expression(handle, scopes:) raise_sema_error("gather expects Handle, got #{handle_type}", line: statement.line, column: statement.column) unless handle_type.is_a?(Types::Handle) end end |
#check_generic_method_immutable_this(binding, scopes) ⇒ Object
Scans generic method bodies for assignments to this through a non-editable receiver. Full body checking is deferred to call-site specialization, but immutable-this violations are type-independent.
601 602 603 604 605 606 607 608 609 610 611 612 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 601 def check_generic_method_immutable_this(binding, scopes) this_binding = scopes.first&.values&.find { |v| v.name == "this" } return unless this_binding return if this_binding.mutable binding.ast.body&.each do |statement| next unless statement.is_a?(AST::Assignment) && statement.operator == "=" next unless statement.target.is_a?(AST::MemberAccess) && statement.target.receiver.is_a?(AST::Identifier) && statement.target.receiver.name == "this" raise_sema_error("cannot assign through immutable this", statement.target) end end |
#check_generic_method_names(binding, scopes) ⇒ Object
Verifies that every bare identifier used as a value expression inside a generic method body resolves to a known name (parameter, local, top-level value, function, type, import, or type parameter). This catches misspelled variables and undeclared names without touching any type-dependent logic, so it is safe to run during structural analysis before concrete types are substituted.
330 331 332 333 334 335 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 330 def check_generic_method_names(binding, scopes) names = Set.new binding.ast.body&.each do |statement| check_stmt_names(statement, scopes, binding, names) end end |
#check_generic_name(name, scopes, binding, node, names) ⇒ Object
453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 453 def check_generic_name(name, scopes, binding, node, names) return if name == "_" || name == "this" return if names.include?(name) return if BUILTIN_CALLABLE_NAMES.include?(name) return if lookup_value(name, scopes) return if @ctx.top_level_functions.key?(name) return if @ctx.types.key?(name) return if @ctx.imports.key?(name) return if binding.type_params.include?(name) raise_sema_error("unknown name #{name}", node) end |
#check_get_call(arguments, scopes:) ⇒ Object
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 852 def check_get_call(arguments, scopes:) raise_sema_error("get does not support named arguments") if arguments.any?(&:name) raise_sema_error("get expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 source_expr = arguments.first.value index_expr = arguments[1].value source_type = infer_expression(source_expr, scopes:) index_type = infer_expression(index_expr, scopes:) raise_sema_error("get index must be an integer type, got #{index_type}") unless integer_type?(index_type) if array_type?(source_type) raise_sema_error("get requires an addressable array value") unless addressable_storage_expression?(source_expr, scopes:) Types::Registry.nullable(pointer_to(array_element_type(source_type))) elsif source_type.is_a?(Types::Span) Types::Registry.nullable(pointer_to(source_type.element_type)) else raise_sema_error("get expects an array or span, got #{source_type}") end end |
#check_has_attribute_call(arguments, scopes:) ⇒ Object
915 916 917 918 919 920 921 922 923 924 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 915 def check_has_attribute_call(arguments, scopes:) raise_sema_error("has_attribute does not support named arguments") if arguments.any?(&:name) raise_sema_error("has_attribute expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 target = resolve_reflection_target_argument(arguments.first.value, scopes:) binding = resolve_attribute_name_argument(arguments[1].value) validate_attribute_target_compatibility!(target, binding) @ctx.types.fetch("bool") end |
#check_hash_call(resolution, arguments, scopes:) ⇒ Object
426 427 428 429 430 431 432 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 426 def check_hash_call(resolution, arguments, scopes:) raise_sema_error("hash does not support named arguments") if arguments.any?(&:name) raise_sema_error("hash expects 1 argument, got #{arguments.length}") unless arguments.length == 1 validate_hash_operation_argument!(arguments.first.value, resolution.target_type, scopes:, operation: "hash") @ctx.types.fetch("uint") end |
#check_inline_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1275 def check_inline_for_stmt(statement, scopes:, return_type:, allow_return:) iterable = evaluate_compile_time_const_value(statement.iterables.first, scopes:) raise_sema_error("inline for iterable must be a compile-time constant") unless iterable.is_a?(Array) raise_sema_error("inline for iterable is empty") if iterable.empty? loop_var_name = statement.bindings.first.name ensure_non_reserved_primitive_name!(loop_var_name, kind_label: "for binding", line: statement.bindings.first.line, column: statement.bindings.first.column) # The loop is unrolled at compile time, so check the body once per element: # each iteration's element type is validated and any per-element # specialization (e.g. `equal[field.type]` for a struct field, or a # `format_value[field.type]` recursion) is discovered by sema rather than # first appearing during lowering. iterable.each do |element| with_nested_scope(scopes) do |loop_scopes| current_actual_scope(loop_scopes)[loop_var_name] = value_binding( name: loop_var_name, type: inline_for_element_type(element), mutable: false, kind: :let, id: @preassigned_local_binding_ids[statement.bindings.first.object_id], const_value: element, ) with_loop do with_compile_time do check_block(statement.body, scopes: loop_scopes, return_type:, allow_return:) end end end end end |
#check_inline_if_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1334 def check_inline_if_stmt(statement, scopes:, return_type:, allow_return:) chosen_branch = statement.branches.find do |branch| condition_value = evaluate_compile_time_const_value(branch.condition, scopes:) raise_sema_error("inline if condition must be a compile-time constant") if condition_value.nil? raise_sema_error("inline if condition must be bool") unless condition_value == true || condition_value == false condition_value end if chosen_branch with_compile_time do check_block(chosen_branch.body, scopes:, return_type:, allow_return:) end elsif statement.else_body with_compile_time do check_block(statement.else_body, scopes:, return_type:, allow_return:) end end end |
#check_inline_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1353 def check_inline_match_stmt(statement, scopes:, return_type:, allow_return:) scrutinee = evaluate_compile_time_const_value(statement.expression, scopes:) raise_sema_error("inline match scrutinee must be a compile-time constant") if scrutinee.nil? chosen_arm = statement.arms.find do |arm| pattern_value = evaluate_compile_time_const_value(arm.pattern, scopes:) scrutinee == pattern_value end if chosen_arm with_compile_time do check_block(chosen_arm.body, scopes:, return_type:, allow_return:) end end end |
#check_inline_while_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1323 def check_inline_while_stmt(statement, scopes:, return_type:, allow_return:) condition = evaluate_compile_time_const_value(statement.condition, scopes:) raise_sema_error("inline while condition must be a compile-time constant") if condition.nil? with_loop do with_compile_time do check_block(statement.body, scopes:, return_type:, allow_return:) end end end |
#check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
755 756 757 758 759 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 755 def check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) each_integer_match_arm(statement, scrutinee_type, scopes:) do |arm| check_block(arm.body, scopes:, return_type:, allow_return:) end end |
#check_interface_conformance!(receiver_type, interface) ⇒ Object
30 31 32 33 34 35 36 37 |
# File 'lib/milk_tea/core/semantic_analyzer/interface_conformance.rb', line 30 def check_interface_conformance!(receiver_type, interface) interface.methods.each_value do |interface_method| method = lookup_local_method_for_interface(receiver_type, interface_method.name) raise_sema_error("type #{receiver_type} implements interface #{interface.name} but is missing method #{interface_method.name}") unless method check_interface_method_match!(receiver_type, interface, interface_method, method) end end |
#check_interface_conformances ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/milk_tea/core/semantic_analyzer/interface_conformance.rb', line 6 def check_interface_conformances .each do |decl| with_error_node(decl) do next unless decl.is_a?(AST::StructDecl) || decl.is_a?(AST::OpaqueDecl) next if decl.implements.empty? receiver_type = @ctx.types.fetch(decl.name) resolved_interfaces = [] seen = {} decl.implements.each do |interface_ref| interface = resolve_interface_ref(interface_ref) raise_sema_error("duplicate interface #{decl.name} implements #{interface.name}") if seen.key?(interface) seen[interface] = true resolved_interfaces << interface check_interface_conformance!(receiver_type, interface) end @ctx.implemented_interfaces[interface_implementation_key(receiver_type)] = resolved_interfaces.freeze end end end |
#check_interface_method_match!(receiver_type, interface, interface_method, method) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/milk_tea/core/semantic_analyzer/interface_conformance.rb', line 50 def check_interface_method_match!(receiver_type, interface, interface_method, method) if method.ast.is_a?(AST::MethodDef) && method.ast.type_params.any? raise_sema_error("type #{receiver_type} method #{method.name} does not satisfy interface #{interface.name}: interface methods cannot be implemented by generic methods") end if interface_method.kind == :static raise_sema_error("type #{receiver_type} method #{method.name} does not satisfy interface #{interface.name}: method kind does not match") unless method.type.receiver_type.nil? else raise_sema_error("type #{receiver_type} method #{method.name} does not satisfy interface #{interface.name}: method kind does not match") if method.type.receiver_type.nil? expected_editable = interface_method.kind == :editable actual_editable = method.type.receiver_editable if actual_editable != expected_editable raise_sema_error("type #{receiver_type} method #{method.name} does not satisfy interface #{interface.name}: receiver editability does not match") end end method_params = method.type.params.map(&:type) interface_params = interface_method.params.map(&:type) unless method_params == interface_params && method.type.return_type == interface_method.return_type && method.async == interface_method.async raise_sema_error("type #{receiver_type} method #{method.name} does not satisfy interface #{interface.name}: signature does not match") end end |
#check_layout_type_via_ct(type_ref, context:, scopes:) ⇒ Object
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 591 def check_layout_type_via_ct(type_ref, context:, scopes:) return false unless scopes && type_ref.name.parts.length >= 1 first_part = type_ref.name.parts.first binding = lookup_value(first_part, scopes) return false unless binding && !binding.const_value.nil? expression = AST.build_chain_from_parts(type_ref.name.parts) return false unless expression ct_value = evaluate_compile_time_const_value(expression, scopes:) if ct_value.is_a?(Types::Struct) || ct_value.is_a?(Types::Primitive) || ct_value.is_a?(Types::Union) || ct_value.is_a?(Types::Nullable) || ct_value.is_a?(Types::StructInstance) if sized_layout_type?(ct_value) return true end end false end |
#check_local_decl(statement, scopes:, return_type:, allow_return:) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 223 def check_local_decl(statement, scopes:, return_type:, allow_return:) if statement.destructure_bindings check_local_decl_destructure(statement, scopes:, return_type:, allow_return:) return end current_scope = current_actual_scope(scopes) discard_binding = statement.name == "_" || let_else_discard_binding_syntax?(statement) raise_sema_error("duplicate local #{statement.name}") if !discard_binding && current_scope.key?(statement.name) ensure_non_reserved_primitive_name!(statement.name, kind_label: "local", line: statement.line, column: statement.column) unless discard_binding declared_type = statement.type ? resolve_type_ref(statement.type) : nil if statement.value validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false) inferred_type = infer_expression(statement.value, scopes:, expected_type: declared_type) else raise_sema_error("local #{statement.name} without initializer requires an explicit type") unless declared_type begin zero_initializable_type?(declared_type) rescue SemanticError raise_sema_error("local #{statement.name} without initializer requires a zero-initializable type, got #{declared_type}") end inferred_type = declared_type end storage_type, final_type, const_value = if statement.else_body || statement.recovered_else check_local_decl_let_else(statement, scopes:, return_type:, allow_return:, discard_binding:, declared_type:, inferred_type:) else check_local_decl_plain(statement, scopes:, discard_binding:, declared_type:, inferred_type:) end if noncopyable_event_storage_type?(final_type) && !fresh_noncopyable_event_initializer?(statement.value, final_type, scopes:) raise_sema_error("local #{statement.name} cannot copy event storage type #{final_type}") end unless discard_binding current_scope[statement.name] = value_binding( name: statement.name, type: storage_type, mutable: statement.kind == :var, kind: statement.kind, flow_type: final_type, const_value:, id: @preassigned_local_binding_ids[statement.object_id], ) record_declaration_binding(statement, current_scope[statement.name]) end end |
#check_local_decl_destructure(statement, scopes:, return_type:, allow_return:) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 275 def check_local_decl_destructure(statement, scopes:, return_type:, allow_return:) value_type = infer_expression(statement.value, scopes:) if statement.destructure_type_name check_local_decl_struct_destructure(statement, value_type, scopes:) elsif value_type.is_a?(Types::Tuple) check_local_decl_tuple_destructure(statement, value_type, scopes:) else raise_sema_error("destructure requires a tuple or struct, got #{value_type}") end end |
#check_local_decl_let_else(statement, scopes:, return_type:, allow_return:, discard_binding:, declared_type:, inferred_type:) ⇒ Object
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 368 def check_local_decl_let_else(statement, scopes:, return_type:, allow_return:, discard_binding:, declared_type:, inferred_type:) success_type = let_else_success_type(inferred_type) error_type = let_else_error_type(inferred_type) if statement.recovered_else success_type ||= declared_type || @error_type error_type ||= @error_type if statement.else_binding else raise_sema_error("let-else initializer for #{statement.name} must be nullable, Option[T], or Result[T, E], got #{inferred_type}") unless success_type end if discard_binding && declared_type raise_sema_error("let-else discard binding _ cannot have a type annotation") end if discard_binding && statement.kind == :var raise_sema_error("var-else discard binding _ is not allowed") end if statement.else_binding && !error_type raise_sema_error("let-else error binding for #{statement.name} requires Result[T, E], got #{inferred_type}") end if declared_type && let_else_source_type?(declared_type) raise_sema_error("let-else type annotation for #{statement.name} must be the success type, got #{declared_type}") end if declared_type validate_local_ref_type!(declared_type, statement.name) validate_local_proc_type!(declared_type, statement.name, initializer: statement.value) ensure_assignable!( success_type, declared_type, "cannot assign #{success_type} to #{statement.name}: expected #{declared_type}", expression: statement.value, scopes:, contextual_int_to_float: contextual_int_to_float_target?(declared_type), line: statement.line, column: statement.column, ) final_type = declared_type else raise_sema_error("cannot bind void result to #{statement.name}") if success_type.void? && !discard_binding final_type = success_type end unless discard_binding validate_local_ref_type!(final_type, statement.name) validate_local_proc_type!(final_type, statement.name, initializer: statement.value) end else_scopes = scopes if statement.else_binding ensure_non_reserved_primitive_name!(statement.else_binding.name, kind_label: "let-else error binding", line: statement.else_binding.line, column: statement.else_binding.column) else_binding = value_binding( name: statement.else_binding.name, type: error_type, mutable: false, kind: :let, id: preassigned_local_binding_id_for(statement.else_binding), ) record_declaration_binding(statement.else_binding, else_binding) else_scopes = scopes + [{ statement.else_binding.name => else_binding }] end check_block(statement.else_body, scopes: else_scopes, return_type:, allow_return:) if statement.else_body if statement.else_body && !statement.recovered_else terminator = if inside_loop? ControlFlow::Termination.block_always_terminates_in_loop?(statement.else_body) else cfg_block_always_terminates?(statement.else_body) end unless terminator raise_sema_error("else block for #{statement.name} must exit control flow at line #{statement.line}") end end storage_type = statement.kind == :var ? final_type : inferred_type [storage_type, final_type, nil] end |
#check_local_decl_plain(statement, scopes:, discard_binding:, declared_type:, inferred_type:) ⇒ Object
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 444 def check_local_decl_plain(statement, scopes:, discard_binding:, declared_type:, inferred_type:) if declared_type validate_local_ref_type!(declared_type, statement.name) validate_local_proc_type!(declared_type, statement.name, initializer: statement.value) ensure_assignable!( inferred_type, declared_type, "cannot assign #{inferred_type} to #{statement.name}: expected #{declared_type}", expression: statement.value, scopes:, contextual_int_to_float: contextual_int_to_float_target?(declared_type), line: statement.line, column: statement.column, ) final_type = declared_type else raise_sema_error("cannot infer type for #{statement.name} from null") if inferred_type.is_a?(Types::Null) raise_sema_error("cannot bind void result to #{statement.name}") if inferred_type.void? final_type = inferred_type end validate_local_ref_type!(final_type, statement.name) validate_local_proc_type!(final_type, statement.name, initializer: statement.value) const_value = statement.kind == :let && statement.value ? evaluate_compile_time_const_value(statement.value, scopes:) : nil [final_type, final_type, const_value] end |
#check_local_decl_struct_destructure(statement, value_type, scopes:) ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 307 def check_local_decl_struct_destructure(statement, value_type, scopes:) type_name = statement.destructure_type_name struct_type = if type_name.is_a?(Array) resolve_qualified_type_name(type_name) else @ctx.types[type_name] end display_name = type_name.is_a?(Array) ? type_name.join(".") : type_name raise_sema_error("unknown type #{display_name} for struct destructure") unless struct_type if struct_type.is_a?(Types::GenericStructDefinition) if value_type.is_a?(Types::StructInstance) && value_type.definition == struct_type fields = value_type.fields else raise_sema_error("generic type #{display_name} requires type arguments") end else raise_sema_error("#{display_name} is not a struct") unless struct_type.is_a?(Types::Struct) || struct_type.is_a?(Types::Tuple) ensure_assignable!(value_type, struct_type, "cannot destructure #{value_type} as #{display_name}") fields = struct_type.fields end raise_sema_error("destructure pattern has #{statement.destructure_bindings.length} bindings but #{display_name} has #{fields.length} fields") unless statement.destructure_bindings.length == fields.length current_scope = current_actual_scope(scopes) statement.destructure_bindings.each do |name| next if name == "_" raise_sema_error("duplicate local #{name} in destructure") if current_scope.key?(name) raise_sema_error("unknown field #{display_name}.#{name}") unless fields.key?(name) ensure_non_reserved_primitive_name!(name, kind_label: "local", line: statement.line, column: statement.column) field_type = fields[name] current_scope[name] = value_binding( name:, type: field_type, mutable: false, kind: :let, id: @preassigned_local_binding_ids[statement.object_id], ) record_declaration_binding(statement, current_scope[name]) end end |
#check_local_decl_tuple_destructure(statement, value_type, scopes:) ⇒ Object
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 287 def check_local_decl_tuple_destructure(statement, value_type, scopes:) raise_sema_error("destructure pattern has #{statement.destructure_bindings.length} bindings but tuple has #{value_type.element_types.length} elements") unless statement.destructure_bindings.length == value_type.element_types.length current_scope = current_actual_scope(scopes) statement.destructure_bindings.each_with_index do |name, index| next if name == "_" raise_sema_error("duplicate local #{name} in destructure") if current_scope.key?(name) ensure_non_reserved_primitive_name!(name, kind_label: "local", line: statement.line, column: statement.column) field_type = value_type.element_types[index] current_scope[name] = value_binding( name:, type: field_type, mutable: false, kind: :let, id: @preassigned_local_binding_ids[statement.object_id], ) record_declaration_binding(statement, current_scope[name]) end end |
#check_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 564 def check_match_stmt(statement, scopes:, return_type:, allow_return:) validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: false) scrutinee_type = infer_expression(statement.expression, scopes:) if error_type?(scrutinee_type) check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:) elsif scrutinee_type.is_a?(Types::Enum) check_enum_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) elsif scrutinee_type.is_a?(Types::Variant) check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) elsif integer_type?(scrutinee_type) check_integer_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) elsif scrutinee_type.is_a?(Types::StringView) check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) elsif scrutinee_type.is_a?(Types::Tuple) check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) else raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}") end end |
#check_order_call(resolution, arguments, scopes:) ⇒ Object
445 446 447 448 449 450 451 452 453 454 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 445 def check_order_call(resolution, arguments, scopes:) raise_sema_error("order does not support named arguments") if arguments.any?(&:name) raise_sema_error("order expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 arguments.each do |argument| validate_hash_operation_argument!(argument.value, resolution.target_type, scopes:, operation: "order") end @ctx.types.fetch("int") end |
#check_parallel_block_stmt(statement, scopes:, return_type:) ⇒ Object
1177 1178 1179 1180 1181 1182 1183 1184 1185 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1177 def check_parallel_block_stmt(statement, scopes:, return_type:) @uses_parallel_for = true raise_sema_error("parallel block must contain at least two statements", line: statement.line, column: statement.column) if statement.bodies.length < 2 statement.bodies.each do |body| validate_threaded_for_body!(body) check_block(body, scopes:, return_type:, allow_return: false) end end |
#check_parallel_for_bindings(statement, scopes:) ⇒ Object
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1100 def check_parallel_for_bindings(statement, scopes:) raise_sema_error("parallel for loops currently support arrays and spans only") if statement.iterables.any? { |iterable| range_expr?(iterable) } iterable_types = statement.iterables.map { |iterable| infer_expression(iterable, scopes:) } binding_infos = iterable_types.each_with_index.map do |iterable_type, index| loop_type = collection_loop_type(iterable_type) raise_sema_error("parallel for loops expect arrays or spans for each iterable") unless loop_type binding_type = collection_loop_binding_type(iterable_type, loop_type) || loop_type { binding: statement.bindings[index], iterable_type:, type: binding_type } end ensure_parallel_for_static_lengths_match!(binding_infos.map { |entry| entry[:iterable_type] }) binding_infos end |
#check_ptr_of_call(arguments, scopes:) ⇒ Object
891 892 893 894 895 896 897 898 899 900 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 891 def check_ptr_of_call(arguments, scopes:) raise_sema_error("ptr_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("ptr_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1 source_expression = arguments.first.value source_type = infer_expression(source_expression, scopes:) return pointer_to(referenced_type(source_type)) if ref_type?(source_type) pointer_to(infer_addr_source_type(source_expression, scopes:)) end |
#check_range_expr_loop(expression, scopes:) ⇒ Object
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1218 def check_range_expr_loop(expression, scopes:) start_type = infer_expression(expression.start_expr, scopes:) stop_type = infer_expression(expression.end_expr, scopes:) unless integer_type?(start_type) && integer_type?(stop_type) raise_sema_error("range bounds must be integer types, got #{start_type} and #{stop_type}") end if start_type != stop_type if expression.start_expr.is_a?(AST::IntegerLiteral) start_type = infer_expression(expression.start_expr, scopes:, expected_type: stop_type) elsif expression.end_expr.is_a?(AST::IntegerLiteral) stop_type = infer_expression(expression.end_expr, scopes:, expected_type: start_type) end end raise_sema_error("range bounds must use matching integer types, got #{start_type} and #{stop_type}") unless start_type == stop_type start_type end |
#check_range_index_assignment(statement, scopes:) ⇒ Object
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 526 def check_range_index_assignment(statement, scopes:) target = statement.target range = target.index raise_sema_error("range index assignment requires an expression list on the right-hand side") unless statement.value.is_a?(AST::ExpressionList) raise_sema_error("range index assignment requires integer literal bounds") unless range.start_expr.is_a?(AST::IntegerLiteral) && range.end_expr.is_a?(AST::IntegerLiteral) start_val = range.start_expr.value end_val = range.end_expr.value raise_sema_error("range start must be less than end in range index assignment") unless start_val < end_val count = end_val - start_val raise_sema_error("range index assignment: range [#{start_val}..#{end_val}) spans #{count} elements but tuple has #{statement.value.elements.length}") unless statement.value.elements.length == count receiver_type = infer_lvalue_receiver( target.receiver, scopes:, allow_pointer_identifier: true, require_mutable_pointer: true, allow_span_param_identifier: true, ) element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint")) statement.value.elements.each_with_index do |elem, i| elem = elem.is_a?(AST::Argument) ? elem.value : elem validate_consuming_foreign_expression!(elem, scopes:, root_allowed: false) elem_type = infer_expression(elem, scopes:, expected_type: element_type) ensure_assignable!( elem_type, element_type, "range index assignment element #{i}: cannot assign #{elem_type} to #{element_type}", expression: elem, contextual_int_to_float: contextual_int_to_float_target?(element_type), line: statement.line, ) end end |
#check_read_call(arguments, scopes:) ⇒ Object
886 887 888 889 890 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 886 def check_read_call(arguments, scopes:) validate_read_call_arguments!(arguments) infer_reference_value_type(arguments.first.value, scopes:) end |
#check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:) ⇒ Object
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 919 def check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:) arm_scopes = scopes.dup if arm.binding_name ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column) binding = value_binding( name: arm.binding_name, type: @error_type, mutable: true, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id), ) arm_scopes = arm_scopes + [{ arm.binding_name => binding }] record_declaration_binding(arm, binding) end check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:) end |
#check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
913 914 915 916 917 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 913 def check_recovered_match_stmt(statement, scopes:, return_type:, allow_return:) statement.arms.each do |arm| check_recovered_match_arm_body(arm, scopes:, return_type:, allow_return:) end end |
#check_ref_of_call(arguments, scopes:) ⇒ Object
872 873 874 875 876 877 878 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 872 def check_ref_of_call(arguments, scopes:) raise_sema_error("ref_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("ref_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1 source_type = infer_addr_source_type(arguments.first.value, scopes:) Types::Registry.generic_instance("ref", [source_type]) end |
#check_reinterpret_call(target_type, arguments, scopes:) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 211 def check_reinterpret_call(target_type, arguments, scopes:) raise_sema_error("reinterpret requires exactly one argument") unless arguments.length == 1 raise_sema_error("reinterpret does not support named arguments") if arguments.first.name require_unsafe!("reinterpret requires unsafe") source_type = infer_expression(arguments.first.value, scopes:) unless reinterpretable_type?(source_type) && reinterpretable_type?(target_type) raise_sema_error("reinterpret requires non-array concrete sized types, got #{source_type} -> #{target_type}") end source_size = CompileTime::Layout.size_of(source_type) target_size = CompileTime::Layout.size_of(target_type) if source_size && target_size && source_size != target_size 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'})") end target_type end |
#check_simd_construction(simd_type, arguments, scopes:) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 54 def check_simd_construction(simd_type, arguments, scopes:) raise_sema_error("simd construction requires exactly #{simd_type.lane_count} positional arguments, got #{arguments.length}") unless arguments.length == simd_type.lane_count raise_sema_error("simd construction does not accept named arguments") if arguments.any?(&:name) element_type = simd_type.element_type arguments.each do |argument| actual_type = infer_expression(argument.value, scopes:, expected_type: element_type) ensure_assignable!( actual_type, element_type, "simd lane expects #{element_type}, got #{actual_type}", expression: argument.value, contextual_int_to_float: contextual_int_to_float_target?(element_type), ) end simd_type end |
#check_simd_method_call(kind, receiver_type, _receiver, arguments, scopes:) ⇒ Object
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 603 def check_simd_method_call(kind, receiver_type, _receiver, arguments, scopes:) raise_sema_error("simd methods do not support named arguments") if arguments.any?(&:name) case kind when :simd_lane_with raise_sema_error("with expects exactly 2 arguments, got #{arguments.length}") unless arguments.length == 2 index_arg = arguments[0].value index_type = infer_expression(index_arg, scopes:) raise_sema_error("with index must be an integer, got #{index_type}") unless index_type.integer? value_type = infer_expression(arguments[1].value, scopes:, expected_type: receiver_type.element_type) ensure_assignable!( value_type, receiver_type.element_type, "with expects lane value of type #{receiver_type.element_type}, got #{value_type}", ) receiver_type end end |
#check_stack_escape_call(expression, scopes:) ⇒ Object
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1386 def check_stack_escape_call(expression, scopes:) callee = expression.callee return unless callee.is_a?(AST::Identifier) return unless %w[ptr_of ref_of const_ptr_of].include?(callee.name) return if expression.arguments.empty? arg = expression.arguments.first source = arg.respond_to?(:value) ? arg.value : arg check_stack_local_source(source, scopes:, builtin: callee.name) end |
#check_stack_local_source(expression, scopes:, builtin:) ⇒ Object
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1397 def check_stack_local_source(expression, scopes:, builtin:) case expression when AST::Identifier binding = lookup_local_binding(expression.name, scopes) return unless binding return unless %i[let var param].include?(binding.kind) return if stack_safe_pointer_source?(binding) raise_sema_error( "#{builtin}(#{expression.name}) cannot be returned: pointer to function-local storage", expression, ) when AST::MemberAccess check_stack_local_source(expression.receiver, scopes:, builtin:) when AST::IndexAccess check_stack_local_source(expression.receiver, scopes:, builtin:) end end |
#check_stack_pointer_escape_in_return(expression, scopes:) ⇒ Object
Walks the returned expression tree to detect ptr_of/ref_of/const_ptr_of applied to a stack-local variable whose address must not escape the function. Only flags when the pointer value itself (not a computed result through a function call) reaches the return.
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1373 def check_stack_pointer_escape_in_return(expression, scopes:) return unless expression if expression.is_a?(AST::Call) check_stack_escape_call(expression, scopes:) elsif expression.is_a?(AST::BinaryOp) check_stack_pointer_escape_in_return(expression.left, scopes:) check_stack_pointer_escape_in_return(expression.right, scopes:) elsif expression.is_a?(AST::UnaryOp) check_stack_pointer_escape_in_return(expression.operand, scopes:) end end |
#check_statement(statement, scopes:, return_type:, allow_return: true) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 59 def check_statement(statement, scopes:, return_type:, allow_return: true) with_error_node(statement) do case statement when AST::ErrorBlockStmt if statement.header_type == :unsafe @unsafe_statement_lines << statement.line begin with_unsafe do check_block(statement.body, scopes:, return_type:, allow_return:) end ensure @unsafe_statement_lines.pop end elsif statement.header_type == :if && statement.header_expression validate_consuming_foreign_expression!(statement.header_expression, scopes:, root_allowed: false) condition_type = infer_expression(statement.header_expression, scopes:, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if condition must be bool, got #{condition_type}", expression: statement.header_expression, line: statement.line, column: statement.column) true_refinements = flow_refinements(statement.header_expression, truthy: true, scopes:) check_block(statement.body, scopes: scopes_with_refinements(scopes, true_refinements), return_type:, allow_return:) return flow_refinements(statement.header_expression, truthy: false, scopes:) if cfg_block_always_terminates?(statement.body) elsif statement.header_type == :while && statement.header_expression validate_consuming_foreign_expression!(statement.header_expression, scopes:, root_allowed: false) condition_type = infer_expression(statement.header_expression, scopes:, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "while condition must be bool, got #{condition_type}", expression: statement.header_expression, line: statement.line, column: statement.column) with_loop do body_scopes = scopes_with_refinements(scopes, flow_refinements(statement.header_expression, truthy: true, scopes:)) check_block(statement.body, scopes: body_scopes, return_type:, allow_return:) end elsif statement.header_type == :for if statement.header_bindings && statement.header_iterables check_for_stmt(recovered_for_statement(statement), scopes:, return_type:, allow_return:) else with_loop do check_block(statement.body, scopes:, return_type:, allow_return:) end end else check_block(statement.body, scopes:, return_type:, allow_return:) end when AST::LocalDecl check_local_decl(statement, scopes:, return_type:, allow_return:) when AST::ErrorStmt nil when AST::Assignment check_assignment(statement, scopes:) when AST::IfStmt if statement.inline check_inline_if_stmt(statement, scopes:, return_type:, allow_return:) next end false_refinements = {} branch_bodies_terminate = [] statement.branches.each do |branch| branch_scopes = scopes_with_refinements(scopes, false_refinements) if branch.condition.is_a?(AST::ErrorExpr) check_block(branch.body, scopes: branch_scopes, return_type:, allow_return:) branch_bodies_terminate << cfg_block_always_terminates?(branch.body) next end validate_consuming_foreign_expression!(branch.condition, scopes: branch_scopes, root_allowed: false) condition_type = infer_expression(branch.condition, scopes: branch_scopes, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if condition must be bool, got #{condition_type}", expression: branch.condition, line: branch.line, column: branch.column) true_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: true, scopes: branch_scopes)) check_block(branch.body, scopes: scopes_with_refinements(scopes, true_refinements), return_type:, allow_return:) branch_bodies_terminate << cfg_block_always_terminates?(branch.body) false_refinements = merge_refinements(false_refinements, flow_refinements(branch.condition, truthy: false, scopes: branch_scopes)) end check_block(statement.else_body, scopes: scopes_with_refinements(scopes, false_refinements), return_type:, allow_return:) if statement.else_body return false_refinements if statement.else_body.nil? && branch_bodies_terminate.all? when AST::MatchStmt if statement.inline check_inline_match_stmt(statement, scopes:, return_type:, allow_return:) else check_match_stmt(statement, scopes:, return_type:, allow_return:) end when AST::UnsafeStmt @unsafe_statement_lines << statement.line begin with_unsafe do check_block(statement.body, scopes:, return_type:, allow_return:) end ensure @unsafe_statement_lines.pop end when AST::StaticAssert check_static_assert(statement, scopes:) when AST::EmitStmt check_emit_stmt(statement) when AST::ForStmt if statement.inline check_inline_for_stmt(statement, scopes:, return_type:, allow_return:) else check_for_stmt(statement, scopes:, return_type:, allow_return:) end when AST::ParallelBlockStmt check_parallel_block_stmt(statement, scopes:, return_type:) when AST::GatherStmt check_gather_stmt(statement, scopes:) when AST::WhileStmt if statement.inline check_inline_while_stmt(statement, scopes:, return_type:, allow_return:) elsif statement.condition.is_a?(AST::ErrorExpr) with_loop do check_block(statement.body, scopes:, return_type:, allow_return:) end else validate_consuming_foreign_expression!(statement.condition, scopes:, root_allowed: false) condition_type = infer_expression(statement.condition, scopes:, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "while condition must be bool, got #{condition_type}", expression: statement.condition, line: statement.line, column: statement.column) with_loop do body_scopes = scopes_with_refinements(scopes, flow_refinements(statement.condition, truthy: true, scopes:)) check_block(statement.body, scopes: body_scopes, return_type:, allow_return:) end end when AST::PassStmt nil when AST::BreakStmt raise_sema_error("break must be inside a loop") unless inside_loop? when AST::ContinueStmt raise_sema_error("continue must be inside a loop") unless inside_loop? when AST::ReturnStmt raise_sema_error("return is not allowed inside defer blocks") unless allow_return validate_consuming_foreign_expression!(statement.value, scopes:, root_allowed: false) if statement.value value_type = statement.value ? infer_expression(statement.value, scopes:, expected_type: return_type) : @ctx.types.fetch("void") check_stack_pointer_escape_in_return(statement.value, scopes:) if statement.value ensure_assignable!( value_type, return_type, "return type mismatch: expected #{return_type}, got #{value_type}", expression: statement.value, contextual_int_to_float: contextual_int_to_float_target?(return_type), line: statement.line, ) when AST::DeferStmt if statement.body do check_block(statement.body, scopes:, return_type:, allow_return: false) end else validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: true) validate_hoistable_foreign_expression!(statement.expression, scopes:, root_hoistable: false) infer_expression(statement.expression, scopes:) end when AST::ExpressionStmt validate_consuming_foreign_expression!(statement.expression, scopes:, root_allowed: true) if statement.expression.is_a?(AST::UnaryOp) && statement.expression.operator == "?" infer_propagate_expression(statement.expression.operand, scopes:, allow_void_success: true) else infer_expression(statement.expression, scopes:) end return consuming_foreign_call_refinements(statement.expression, scopes:) when AST::WhenStmt check_when_stmt(statement, scopes:, return_type:, allow_return:) else raise_sema_error("unsupported statement #{statement.class.name}") end nil end end |
#check_static_assert(statement, scopes:) ⇒ Object
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1197 def check_static_assert(statement, scopes:) validate_consuming_foreign_expression!(statement.condition, scopes:, root_allowed: false) validate_consuming_foreign_expression!(statement., scopes:, root_allowed: false) validate_hoistable_foreign_expression!(statement.condition, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(statement., scopes:, root_hoistable: false) condition_type = infer_expression(statement.condition, scopes:, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "static_assert condition must be bool, got #{condition_type}") condition_value = evaluate_compile_time_const_value(statement.condition, scopes:) raise_sema_error("static_assert condition must be a compile-time bool constant") unless condition_value == true || condition_value == false raise_sema_error("static_assert message must be a string literal") unless statement..is_a?(AST::StringLiteral) = infer_expression(statement., scopes:, expected_type: @ctx.types.fetch("str")) return if string_like_type?() raise_sema_error("static_assert message must be str or cstr, got #{}") end |
#check_stmt_names(stmt, scopes, binding, names) ⇒ Object
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 337 def check_stmt_names(stmt, scopes, binding, names) case stmt when AST::LocalDecl check_expr_names(stmt.value, scopes, binding, names) if stmt.value names.add(stmt.name) if stmt.name try_record_generic_local_snapshot(stmt, scopes, binding) if stmt.name && stmt.value when AST::Assignment check_expr_names(stmt.target, scopes, binding, names) check_expr_names(stmt.value, scopes, binding, names) when AST::ExpressionStmt check_expr_names(stmt.expression, scopes, binding, names) when AST::ReturnStmt check_expr_names(stmt.value, scopes, binding, names) if stmt.value when AST::IfStmt stmt.branches.each do |b| check_expr_names(b.condition, scopes, binding, names) branch_names = names.dup b.body&.each { |s| check_stmt_names(s, scopes, binding, branch_names) } end else_names = names.dup stmt.else_body&.each { |s| check_stmt_names(s, scopes, binding, else_names) } when AST::WhileStmt check_expr_names(stmt.condition, scopes, binding, names) body_names = names.dup stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::ForStmt Array(stmt.iterables).each { |i| check_expr_names(i, scopes, binding, names) } body_names = names.dup Array(stmt.bindings).each { |b| body_names.add(b.respond_to?(:name) ? b.name : b) } stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::MatchStmt check_expr_names(stmt.expression, scopes, binding, names) stmt.arms.each do |arm| arm_names = names.dup arm_names.add(arm.binding_name) if arm.binding_name arm.body&.each { |s| check_stmt_names(s, scopes, binding, arm_names) } end when AST::UnsafeStmt, AST::ParallelBlockStmt body_names = names.dup stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::DeferStmt check_expr_names(stmt.expression, scopes, binding, names) if stmt.expression body_names = names.dup stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::WhenStmt check_expr_names(stmt.discriminant, scopes, binding, names) stmt.branches.each do |b| branch_names = names.dup b.body&.each { |s| check_stmt_names(s, scopes, binding, branch_names) } end else_names = names.dup stmt.else_body&.each { |s| check_stmt_names(s, scopes, binding, else_names) } when AST::ErrorBlockStmt body_names = names.dup stmt.body&.each { |s| check_stmt_names(s, scopes, binding, body_names) } when AST::BreakStmt, AST::ContinueStmt, AST::PassStmt, AST::StaticAssert, AST::EmitStmt nil end end |
#check_str_buffer_method_call(kind, receiver, arguments, scopes:) ⇒ Object
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 466 def check_str_buffer_method_call(kind, receiver, arguments, scopes:) method_name = str_buffer_method_name(kind) receiver_type = infer_expression(receiver, scopes:) raise_sema_error("unknown method #{receiver_type}.#{method_name}") unless str_buffer_type?(receiver_type) case kind when :str_buffer_clear, :str_buffer_len, :str_buffer_capacity, :str_buffer_as_str, :str_buffer_as_cstr raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name) raise_sema_error("#{method_name} expects 0 arguments, got #{arguments.length}") unless arguments.empty? when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format raise_sema_error("#{method_name} does not support named arguments") if arguments.any?(&:name) raise_sema_error("#{method_name} expects 1 argument, got #{arguments.length}") unless arguments.length == 1 else raise_sema_error("unsupported str_buffer method #{kind}") end case kind when :str_buffer_clear record_editable_receiver_expression(receiver) raise_sema_error("cannot call editable method #{receiver_type}.clear on an immutable receiver") unless assignable_receiver?(receiver, scopes) @ctx.types.fetch("void") when :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format record_editable_receiver_expression(receiver) raise_sema_error("cannot call editable method #{receiver_type}.#{method_name} on an immutable receiver") unless assignable_receiver?(receiver, scopes) actual_type = infer_expression(arguments.first.value, scopes:, expected_type: @ctx.types.fetch("str")) ensure_argument_assignable!( actual_type, @ctx.types.fetch("str"), external: false, message: "argument value to #{receiver_type}.#{method_name} expects str, got #{actual_type}", expression: arguments.first.value, ) @ctx.types.fetch("void") when :str_buffer_len, :str_buffer_capacity @ctx.types.fetch("ptr_uint") when :str_buffer_as_str raise_sema_error("#{receiver_type}.as_str requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:) @ctx.types.fetch("str") when :str_buffer_as_cstr raise_sema_error("#{receiver_type}.as_cstr requires a safe stored receiver") unless safe_reference_source_expression?(receiver, scopes:) @ctx.types.fetch("cstr") else raise_sema_error("unsupported str_buffer method #{kind}") end end |
#check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
761 762 763 764 765 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 761 def check_string_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) each_string_match_arm(statement, scrutinee_type, scopes:) do |arm| check_block(arm.body, scopes:, return_type:, allow_return:) end end |
#check_struct_match_pattern(arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:) ⇒ Object
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 964 def check_struct_match_pattern(arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:) payload_fields = scrutinee_type.arm(arm_name) raise_sema_error("variant arm #{scrutinee_type}.#{arm_name} has no payload fields for struct pattern") if payload_fields.nil? || payload_fields.empty? # Nested struct detection: if arm has exactly one field whose type is a struct, # auto-destructure through to the struct's own fields, but only when none of # the arguments reference the arm's own field name directly. if payload_fields.size == 1 native_field = payload_fields.keys.first has_native_reference = arguments.any? do |arg| next false if !arg.name && arg.value.is_a?(AST::Identifier) && arg.value.name == "_" name = arg.name || (arg.value.is_a?(AST::Identifier) ? arg.value.name : nil) name == native_field end unless has_native_reference single_field_type = payload_fields.values.first payload_fields = single_field_type.fields if single_field_type.is_a?(Types::Struct) end end has_guards = false seen_fields = {} equality_cover = {} arguments.each do |arg| if arg.value.is_a?(AST::Identifier) && arg.value.name == "_" # _ discard: skip this field, no binding, no guard next elsif arg.name # Equality pattern: kind = Kind.boss field_name = arg.name raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name) raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name) seen_fields[field_name] = true has_guards = true field_type = payload_fields[field_name] actual_type = infer_expression(arg.value, scopes:, expected_type: field_type) ensure_assignable!(actual_type, field_type, "field #{field_name} expects #{field_type}, got #{actual_type}", expression: arg.value) if field_type.is_a?(Types::Enum) && arg.value.is_a?(AST::MemberAccess) equality_cover[field_name] = [] unless equality_cover.key?(field_name) equality_cover[field_name] << arg.value.member end elsif arg.value.is_a?(AST::Identifier) # Binding: position field_name = arg.value.name raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name) raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name) seen_fields[field_name] = true field_type = payload_fields[field_name] binding = value_binding( name: field_name, type: field_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids[arg.object_id], ) arm_scopes.last[field_name] = binding elsif arg.value.is_a?(AST::BinaryOp) && arg.value.left.is_a?(AST::Identifier) # Guard: hp > 0 field_name = arg.value.left.name raise_sema_error("unknown field #{scrutinee_type}.#{arm_name}.#{field_name}") unless payload_fields.key?(field_name) raise_sema_error("duplicate field #{field_name} in struct pattern") if seen_fields.key?(field_name) seen_fields[field_name] = true has_guards = true field_type = payload_fields[field_name] comparison_operators = ["==", "!=", "<", "<=", ">", ">="] unless comparison_operators.include?(arg.value.operator) raise_sema_error("unsupported guard operator '#{arg.value.operator}' in struct pattern; use ==, !=, <, <=, >, or >=", arg.value) end operand_type = infer_expression(arg.value.right, scopes:, expected_type: field_type) ensure_assignable!(operand_type, field_type, "guard comparison expects #{field_type}, got #{operand_type}", expression: arg.value.right) else raise_sema_error("invalid field pattern in struct match arm; expected field name, comparison, or equality", expression: arg.value) end end [has_guards, equality_cover] end |
#check_struct_with_call(struct_type, _receiver_expression, arguments, scopes:) ⇒ Object
72 73 74 75 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 72 def check_struct_with_call(struct_type, _receiver_expression, arguments, scopes:) check_aggregate_field_arguments(struct_type, arguments, scopes:, context: "struct.with()") struct_type end |
#check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1123 def check_threaded_for_stmt(statement, scopes:, return_type:, allow_return:) @uses_parallel_for = true raise_sema_error("parallel for requires a range expression (start..end)") unless range_expr?(statement.iterable) loop_type = check_range_expr_loop(statement.iterable, scopes:) validate_threaded_for_body!(statement.body) with_nested_scope(scopes) do |loop_scopes| binding = statement.binding ensure_non_reserved_primitive_name!(binding.name, kind_label: "for binding", line: binding.line, column: binding.column) current_actual_scope(loop_scopes)[binding.name] = value_binding( name: binding.name, type: loop_type, mutable: false, kind: :let, id: @preassigned_local_binding_ids[binding.object_id], ) record_declaration_binding(binding, current_actual_scope(loop_scopes)[binding.name]) check_block(statement.body, scopes: loop_scopes, return_type:, allow_return: false) end end |
#check_top_level_static_asserts ⇒ Object
628 629 630 631 632 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 628 def check_top_level_static_asserts .grep(AST::StaticAssert).each do |statement| check_static_assert(statement, scopes: []) end end |
#check_top_level_values ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 6 def check_top_level_values .each do |decl| with_error_node(decl) do case decl when AST::ConstDecl begin if decl.block_body check_block_body_const(decl) else check_expr_const(decl) end rescue SemanticError => e collect_structural_error(e) end when AST::VarDecl binding = @ctx.top_level_values.fetch(decl.name) if decl.value validate_consuming_foreign_expression!(decl.value, scopes: [], root_allowed: false) validate_hoistable_foreign_expression!(decl.value, scopes: [], root_hoistable: false) actual_type = infer_expression(decl.value, scopes: [], expected_type: binding.type) ensure_assignable!( actual_type, binding.type, "cannot assign #{actual_type} to module variable #{decl.name}: expected #{binding.type}", expression: decl.value, ) validate_static_storage_initializer!(decl.value, scopes: []) else zero_initializable_type?(binding.type) end end end end end |
#check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
767 768 769 770 771 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 767 def check_tuple_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) each_tuple_match_arm(statement, scrutinee_type, scopes:) do |arm| check_block(arm.body, scopes:, return_type:, allow_return:) end end |
#check_variant_arm_construction(callable, arguments, scopes:) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 102 def check_variant_arm_construction(callable, arguments, scopes:) variant_type, arm_name = callable fields = variant_type.arm(arm_name) if fields.nil? || fields.empty? raise_sema_error("variant arm #{variant_type}.#{arm_name} has no payload; construct it without arguments") unless arguments.empty? return variant_type end raise_sema_error("variant arm construction requires named arguments") unless arguments.all?(&:name) provided = {} arguments.each do |argument| field_type = fields[argument.name] raise_sema_error("unknown field #{variant_type}.#{arm_name}.#{argument.name}") unless field_type raise_sema_error("duplicate field #{variant_type}.#{arm_name}.#{argument.name}") if provided.key?(argument.name) actual_type = infer_expression(argument.value, scopes:, expected_type: field_type) ensure_assignable!( actual_type, field_type, "field #{variant_type}.#{arm_name}.#{argument.name} expects #{field_type}, got #{actual_type}", expression: argument.value, contextual_int_to_float: contextual_int_to_float_target?(field_type), ) provided[argument.name] = true end missing = fields.keys - provided.keys missing.reject! { |name| fields[name].void? } raise_sema_error("variant arm #{variant_type}.#{arm_name} is missing fields: #{missing.join(', ')}") unless missing.empty? variant_type end |
#check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) ⇒ Object
819 820 821 822 823 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 819 def check_variant_match_stmt(statement, scrutinee_type, scopes:, return_type:, allow_return:) each_variant_match_arm(statement, scrutinee_type, scopes:) do |arm, arm_scopes| check_block(arm.body, scopes: arm_scopes, return_type:, allow_return:) end end |
#check_when_stmt(statement, scopes:, return_type:, allow_return:) ⇒ Object
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1251 def check_when_stmt(statement, scopes:, return_type:, allow_return:) discriminant_value = evaluate_when_discriminant(statement.discriminant) chosen_branch = statement.branches.find do |branch| pattern_value = evaluate_compile_time_const_value(branch.pattern, scopes:) discriminant_value == pattern_value end if chosen_branch check_block(chosen_branch.body, scopes:, return_type:, allow_return:) elsif statement.else_body check_block(statement.else_body, scopes:, return_type:, allow_return:) else raise_sema_error("when discriminant value #{discriminant_value} does not match any branch and no else is provided") end end |
#check_zero_call(target_type, arguments, expected_type: nil, operation: "zero") ⇒ Object
230 231 232 233 234 235 236 237 238 239 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 230 def check_zero_call(target_type, arguments, expected_type: nil, operation: "zero") raise_sema_error("#{operation} expects 0 arguments, got #{arguments.length}") unless arguments.empty? zero_initializable_type?(target_type, operation:) if expected_type.is_a?(Types::Nullable) && typed_null_target_type?(expected_type.base) && types_compatible?(target_type, expected_type.base) raise_sema_error("use null instead of #{operation}[#{target_type}] in nullable pointer-like context #{expected_type}") end target_type end |
#collect_emit_declarations ⇒ Object
212 213 214 215 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 212 def collect_emit_declarations collect_emit_from_declarations() @ctx.ast.declarations.grep(AST::ConstDecl).each { |decl| @ctx.const_declarations[decl.name] ||= decl } end |
#collect_emit_from_declarations(declarations) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 217 def collect_emit_from_declarations(declarations) declarations.each do |decl| case decl when AST::FunctionDef next unless decl.const && decl.body collect_emit_from_statements(decl.body) when AST::WhenStmt body = when_chosen_body(decl) collect_emit_from_declarations(body) if body when AST::StructDecl, AST::ExtendingBlock # no emit in structs/extending blocks end end end |
#collect_emit_from_node(node) ⇒ Object
255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 255 def collect_emit_from_node(node) case node when AST::FunctionDef node.body&.each do |stmt| if stmt.is_a?(AST::EmitStmt) nested = stmt.declaration next if nested.is_a?(AST::ErrorExpr) @ctx.ast.declarations << nested collect_emit_from_node(nested) end end end end |
#collect_emit_from_statements(statements) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 233 def collect_emit_from_statements(statements) statements.each do |stmt| case stmt when AST::EmitStmt emit_decl = stmt.declaration next if emit_decl.is_a?(AST::ErrorExpr) @ctx.ast.declarations << emit_decl collect_emit_from_node(emit_decl) when AST::WhenStmt body = when_chosen_body(stmt) || [] body.each { |nested| collect_emit_from_statements([nested]) } when AST::ForStmt, AST::WhileStmt, AST::IfStmt, AST::MatchStmt next unless stmt.inline stmt.body&.each { |s| collect_emit_from_statements([s]) } if stmt.is_a?(AST::IfStmt) stmt.else_body&.each { |s| collect_emit_from_statements([s]) } end end end end |
#collect_structural_error(error) ⇒ Object
329 330 331 332 333 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 329 def collect_structural_error(error) raise error unless @collecting_errors @structural_errors << error end |
#collect_type_substitutions(pattern_type, actual_type, substitutions, function_name) ⇒ Object
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 286 def collect_type_substitutions(pattern_type, actual_type, substitutions, function_name) case pattern_type when Types::TypeVar existing = substitutions[pattern_type.name] if existing && existing != actual_type raise_sema_error("conflicting type argument #{pattern_type.name} for function #{function_name}: got #{existing} and #{actual_type}") end substitutions[pattern_type.name] ||= actual_type when Types::Nullable candidate = actual_type.is_a?(Types::Nullable) ? actual_type.base : actual_type collect_type_substitutions(pattern_type.base, candidate, substitutions, function_name) when Types::GenericInstance if ref_type?(pattern_type) && !ref_type?(actual_type) collect_type_substitutions(referenced_type(pattern_type), actual_type, substitutions, function_name) return end if own_type?(actual_type) && (mutable_pointer_type?(pattern_type) || const_pointer_type?(pattern_type)) collect_type_substitutions(pointee_type(pattern_type), owned_referent_type(actual_type), substitutions, function_name) return end return unless actual_type.is_a?(Types::GenericInstance) return unless actual_type.name == pattern_type.name && actual_type.arguments.length == pattern_type.arguments.length pattern_type.arguments.zip(actual_type.arguments).each do |expected_argument, actual_argument| next if expected_argument.is_a?(Types::LiteralTypeArg) collect_type_substitutions(expected_argument, actual_argument, substitutions, function_name) end when Types::VariantInstance return unless actual_type.is_a?(Types::VariantInstance) return unless actual_type.definition == pattern_type.definition || (actual_type.name == pattern_type.name && actual_type.arguments.length == pattern_type.arguments.length) pattern_type.arguments.zip(actual_type.arguments).each do |expected_argument, actual_argument| next if expected_argument.is_a?(Types::LiteralTypeArg) collect_type_substitutions(expected_argument, actual_argument, substitutions, function_name) end when Types::Span return unless actual_type.is_a?(Types::Span) collect_type_substitutions(pattern_type.element_type, actual_type.element_type, substitutions, function_name) when Types::Task return unless actual_type.is_a?(Types::Task) collect_type_substitutions(pattern_type.result_type, actual_type.result_type, substitutions, function_name) when Types::Proc if task_root_proc_type?(pattern_type) && actual_type.is_a?(Types::Task) collect_type_substitutions(pattern_type.return_type, actual_type, substitutions, function_name) return end actual_params = case actual_type when Types::Proc return unless actual_type.params.length == pattern_type.params.length actual_type.params when Types::Function return if actual_type.receiver_type || actual_type.variadic return unless actual_type.params.length == pattern_type.params.length actual_type.params else return end pattern_type.params.zip(actual_params).each do |expected_param, actual_param| collect_type_substitutions(expected_param.type, actual_param.type, substitutions, function_name) end collect_type_substitutions(pattern_type.return_type, actual_type.return_type, substitutions, function_name) when Types::StructInstance return unless actual_type.is_a?(Types::StructInstance) return unless actual_type.definition == pattern_type.definition && actual_type.arguments.length == pattern_type.arguments.length pattern_type.arguments.zip(actual_type.arguments).each do |expected_argument, actual_argument| collect_type_substitutions(expected_argument, actual_argument, substitutions, function_name) end when Types::Function return unless actual_type.is_a?(Types::Function) return unless actual_type.params.length == pattern_type.params.length pattern_type.params.zip(actual_type.params).each do |expected_param, actual_param| collect_type_substitutions(expected_param.type, actual_param.type, substitutions, function_name) end collect_type_substitutions(pattern_type.return_type, actual_type.return_type, substitutions, function_name) end end |
#collection_loop_binding_type(iterable_type, element_type) ⇒ Object
868 869 870 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 868 def collection_loop_binding_type(iterable_type, element_type) super end |
#collection_loop_ref_element_type?(type) ⇒ Boolean
872 873 874 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 872 def collection_loop_ref_element_type?(type) super end |
#collection_loop_type(type) ⇒ Object
864 865 866 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 864 def collection_loop_type(type) super end |
#common_integer_type(left_type, right_type) ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 183 def common_integer_type(left_type, right_type) left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase) right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase) return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive) return unless left_type.integer? && right_type.integer? return left_type if left_type == right_type return unless left_type.fixed_width_integer? && right_type.fixed_width_integer? return unless left_type.signed_integer? == right_type.signed_integer? left_type.integer_width >= right_type.integer_width ? left_type : right_type end |
#common_numeric_type(left_type, right_type) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 167 def common_numeric_type(left_type, right_type) left_type = left_type.backing_type if left_type.is_a?(Types::EnumBase) right_type = right_type.backing_type if right_type.is_a?(Types::EnumBase) return unless left_type.is_a?(Types::Primitive) && right_type.is_a?(Types::Primitive) return unless left_type.numeric? && right_type.numeric? return left_type if left_type == right_type return common_integer_type(left_type, right_type) if left_type.integer? && right_type.integer? return wider_float_type(left_type, right_type) if left_type.float? && right_type.float? float_type, integer_type = left_type.float? ? [left_type, right_type] : [right_type, left_type] return unless integer_type.integer? && integer_type.fixed_width_integer? float_type end |
#conditional_common_type(then_type, else_type, then_expression:, else_expression:) ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 307 def conditional_common_type(then_type, else_type, then_expression:, else_expression:) return then_type if then_type == else_type numeric_type = common_numeric_type(then_type, else_type) return numeric_type if numeric_type if (nullable_type = conditional_null_common_type(then_type, else_type)) return nullable_type end if (nullable_type = conditional_null_common_type(else_type, then_type)) return nullable_type end return then_type if types_compatible?(else_type, then_type, expression: else_expression) return else_type if types_compatible?(then_type, else_type, expression: then_expression) nil end |
#conditional_null_common_type(null_type, other_type) ⇒ Object
333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 333 def conditional_null_common_type(null_type, other_type) return unless null_type.is_a?(Types::Null) if other_type.is_a?(Types::Nullable) return other_type if null_type.target_type.nil? || null_type.target_type == other_type.base return nil end return unless nullable_candidate?(other_type) return if null_type.target_type && null_type.target_type != other_type Types::Registry.nullable(other_type) end |
#consuming_foreign_call_refinements(expression, scopes:) ⇒ Object
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1231 def consuming_foreign_call_refinements(expression, scopes:) foreign_call = resolve_foreign_call_expression(expression, scopes:) return {} unless foreign_call binding = foreign_call[:binding] return {} unless foreign_call_consumes_binding?(binding) binding.type.params.each_with_index.each_with_object({}) do |(parameter, index), refinements| next unless parameter.passing_mode == :consuming argument = foreign_call[:call].arguments.fetch(index) argument_binding = foreign_consuming_argument_binding(parameter, argument, scopes:, function_name: binding.name) refinements[argument.value.name] = @null_type if argument_binding.storage_type.is_a?(Types::Nullable) end end |
#contains_callable_ref_type?(type, visited = {}) ⇒ Boolean
1141 1142 1143 1144 1145 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1141 def contains_callable_ref_type?(type, visited = {}) visitor = ContainsCallableRefTypeVisitor.new visitor.visit(type) visitor.found? end |
#contains_proc_type?(type, visited = {}) ⇒ Boolean
1147 1148 1149 1150 1151 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1147 def contains_proc_type?(type, visited = {}) visitor = ContainsProcTypeVisitor.new visitor.visit(type) visitor.found? end |
#contains_type_var?(type) ⇒ Boolean
798 799 800 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 798 def contains_type_var?(type) super end |
#contextual_int_to_float_fits?(expression, expected_type, scopes) ⇒ Boolean
150 151 152 153 154 155 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 150 def contextual_int_to_float_fits?(expression, expected_type, scopes) value = evaluate_compile_time_const_value(expression, scopes:) return true unless value.is_a?(Numeric) float_constant_fits_type?(value, expected_type) end |
#count_required_params(binding) ⇒ Object
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 1071 def count_required_params(binding) return binding.type.params.length unless binding.ast.params.any? { |p| p.respond_to?(:default_value) && p.default_value } seen_default = false binding.ast.params.count do |p| has_default = p.respond_to?(:default_value) && p.default_value seen_default = true if has_default !has_default && !seen_default end end |
#current_actual_scope(scopes) ⇒ Object
6 7 8 9 10 11 12 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 6 def current_actual_scope(scopes) scopes.reverse_each do |scope| return scope unless scope.is_a?(FlowScope) end raise_sema_error("missing lexical scope") end |
#current_error_node ⇒ Object
499 500 501 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 499 def current_error_node @error_node_stack.reverse_each.find { |node| !node.nil? } end |
#current_return_context ⇒ Object
105 106 107 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 105 def current_return_context @return_context_stack.last end |
#current_type_param_constraints ⇒ Object
10 11 12 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 10 def current_type_param_constraints @current_type_param_constraints || {} end |
#current_type_params ⇒ Object
6 7 8 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 6 def current_type_params @current_type_substitutions || {} end |
#declare_attributes ⇒ Object
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 313 def declare_attributes .grep(AST::AttributeDecl).each do |decl| with_error_node(decl) do raise_sema_error("duplicate attribute #{decl.name}") if @ctx.attributes.key?(decl.name) params = [] seen = {} decl.params.each do |param| raise_sema_error("duplicate attribute parameter #{decl.name}.#{param.name}") if seen.key?(param.name) seen[param.name] = true params << Types::Registry.parameter(param.name, resolve_type_ref(param.type)) end @ctx.attributes[decl.name] = AttributeBinding.new( name: decl.name, targets: decl.targets.freeze, params: params.freeze, module_name: @ctx.module_name, builtin: false, ast: decl, ) end end end |
#declare_function_binding(decl, receiver_type: nil, declared_receiver_type: nil, receiver_type_param_names: [], receiver_type_param_constraints: {}, external: false) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 52 def declare_function_binding(decl, receiver_type: nil, declared_receiver_type: nil, receiver_type_param_names: [], receiver_type_param_constraints: {}, external: false) foreign = decl.is_a?(AST::ForeignFunctionDecl) async_function = decl.respond_to?(:async) ? decl.async : false if decl.is_a?(AST::MethodDef) ensure_non_reserved_primitive_name!(decl.name, kind_label: "function", line: decl.line, column: decl.column) end type_param_names = receiver_type_param_names + decl.type_params.map(&:name) type_param_constraints = receiver_type_param_constraints.merge(resolve_type_param_constraints(decl.type_params)) raise_sema_error("external function #{decl.name} cannot be generic") if external && type_param_names.any? raise_sema_error("main cannot be generic") if decl.name == "main" && type_param_names.any? raise_sema_error("external function #{decl.name} cannot be async") if external && async_function raise_sema_error("foreign function #{decl.name} cannot be async") if foreign && async_function method_kind = decl.is_a?(AST::MethodDef) ? decl.kind : nil instance_method = receiver_type && method_kind != :static type_params = {} type_param_names.each do |name| raise_sema_error("duplicate type parameter #{decl.name}[#{name}]") if type_params.key?(name) type_params[name] = Types::TypeVar.new(name) end body_params = [] if instance_method body_params << value_binding( name: "this", type: receiver_type, mutable: method_kind == :editable, kind: :param, ) end public_params = [] any_default = false decl.params.each do |param| begin ensure_non_reserved_primitive_name!(param.name, kind_label: "parameter", line: param.line || decl.line, column: param.column) type = resolve_type_ref(param.type, type_params:, type_param_constraints:) validate_parameter_ref_type!(type, function_name: decl.name, parameter_name: param.name, external:) reject_foreign_nullable_value_type!(type, function_name: decl.name, parameter_name: param.name) if foreign || external validate_parameter_proc_type!(type, function_name: decl.name, parameter_name: param.name, external:, foreign:) raise_sema_error("parameter #{param.name} of #{decl.name} must pass event storage through ref[...] or pointers, got #{type}") if noncopyable_event_storage_type?(type) has_default = param.respond_to?(:default_value) && param.default_value raise_sema_error("external function #{decl.name} cannot have default parameter values") if external && has_default raise_sema_error("foreign function #{decl.name} cannot have default parameter values") if foreign && has_default raise_sema_error("default parameter #{param.name} of #{decl.name} must appear after required parameters") if !has_default && any_default any_default = true if has_default if external && array_type?(type) raise_sema_error("external function #{decl.name} cannot take array parameters") end if param.is_a?(AST::ForeignParam) if external raise_sema_error("external parameter #{param.name} of #{decl.name} cannot use `as`") if param.boundary_type unless %i[plain out inout].include?(param.mode) raise_sema_error("external parameter #{param.name} of #{decl.name} cannot use `#{param.mode}`") end elsif foreign raise_sema_error("foreign parameter #{param.name} cannot use `as` with #{param.mode}") if ![:plain, :in].include?(param.mode) && param.boundary_type validate_consuming_foreign_parameter!(type, function_name: decl.name, parameter_name: param.name) if param.mode == :consuming end boundary_type = foreign_parameter_boundary_type(param, type, type_params:, type_param_constraints:) validate_foreign_boundary_type!(type, boundary_type, function_name: decl.name, parameter_name: param.name) if foreign && param.boundary_type && param.mode != :in validate_in_foreign_parameter!(type, boundary_type, function_name: decl.name, parameter_name: param.name) if foreign && param.mode == :in param_binding = value_binding(name: param.name, type: boundary_type || type, mutable: false, kind: :param) body_params << param_binding record_declaration_binding(param, param_binding) if foreign && param.boundary_type body_params << value_binding( name: foreign_mapping_public_alias_name(param.name), type:, mutable: false, kind: :param, ) end public_params << Types::Registry.parameter(param.name, type, passing_mode: param.mode, boundary_type: boundary_type) else param_binding = value_binding(name: param.name, type:, mutable: false, kind: :param) body_params << param_binding record_declaration_binding(param, param_binding) public_params << Types::Registry.parameter(param.name, type) if external end rescue SemanticError => e collect_structural_error(e) param_binding = value_binding(name: param.name, type: @error_type, mutable: false, kind: :param) body_params << param_binding end end receiver_editable = false call_params = body_params function_receiver_type = nil if instance_method receiver_editable = method_kind == :editable call_params = body_params.drop(1) function_receiver_type = receiver_type end call_params = public_params if foreign || external seen = {} body_params.each do |param| raise_sema_error("duplicate parameter #{param.name} in #{decl.name}") if seen.key?(param.name) seen[param.name] = true end body_return_type = decl.return_type ? resolve_type_ref(decl.return_type, type_params:, type_param_constraints:) : @ctx.types.fetch("void") validate_return_ref_type!(body_return_type, function_name: decl.name) reject_foreign_nullable_value_type!(body_return_type, function_name: decl.name, parameter_name: nil) if foreign || external validate_return_proc_type!(body_return_type, function_name: decl.name) raise_sema_error("function #{decl.name} cannot return event storage type #{body_return_type}") if noncopyable_event_storage_type?(body_return_type) if decl.name == "main" && async_function && body_return_type != @ctx.types.fetch("int") && body_return_type != @ctx.types.fetch("void") raise_sema_error("async main must return int or void") end if foreign && public_params.any? { |param| param.passing_mode == :consuming } && body_return_type != @ctx.types.fetch("void") raise_sema_error("foreign function #{decl.name} with consuming parameters must return void") end if external && array_type?(body_return_type) raise_sema_error("external function #{decl.name} cannot return arrays") end function_return_type = async_function ? Types::Registry.task(body_return_type) : body_return_type function_type = Types::Registry.function( decl.name, params: (foreign || external) ? call_params : call_params.map { |param| Types::Registry.parameter(param.name, param.type) }, return_type: function_return_type, receiver_type: function_receiver_type, receiver_editable:, variadic: decl.respond_to?(:variadic) ? decl.variadic : false, external:, ) FunctionBinding.new( name: decl.name, type: function_type, body_params:, body_return_type: body_return_type, ast: decl, external:, async: async_function, type_params: type_param_names.freeze, type_param_constraints: type_param_constraints.freeze, instances: {}, type_arguments: [].freeze, owner: self, specialization_owner: nil, type_substitutions: {}.freeze, declared_receiver_type: declared_receiver_type, ) end |
#declare_functions ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 6 def declare_functions .each do |decl| with_error_node(decl) do case decl when AST::FunctionDef ensure_available_value_name!(decl.name, kind_label: "function", line: decl.line, column: decl.column) @ctx.top_level_functions[decl.name] = declare_function_binding(decl) when AST::ExternFunctionDecl ensure_available_value_name!(decl.name, kind_label: "function", line: decl.line, column: decl.column) if decl.mapping && !decl.mapping.is_a?(AST::StringLiteral) raise_sema_error("external function #{decl.name} mapping must be a c-string literal") end @ctx.top_level_functions[decl.name] = declare_function_binding(decl, external: true) when AST::ForeignFunctionDecl ensure_available_value_name!(decl.name, kind_label: "function", line: decl.line, column: decl.column) @ctx.top_level_functions[decl.name] = declare_function_binding(decl) when AST::ExtendingBlock dispatch_receiver_type, receiver_type, receiver_type_param_names, receiver_type_param_constraints = resolve_methods_receiver_target(decl.type_name) decl.methods.each do |method| begin binding = with_error_node(method) do declare_function_binding( method, receiver_type:, declared_receiver_type: receiver_type, receiver_type_param_names:, receiver_type_param_constraints:, ) end instance_method = receiver_type && method.kind != :static method_key = instance_method ? binding.name : "static:#{binding.name}" raise_sema_error("duplicate method #{decl.type_name}.#{binding.name}") if @ctx.methods[dispatch_receiver_type].key?(method_key) @ctx.methods[dispatch_receiver_type][method_key] = binding rescue SemanticError => e collect_structural_error(e) end end end end rescue SemanticError => e collect_structural_error(e) end end |
#declare_interface_binding(decl) ⇒ Object
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 339 def declare_interface_binding(decl) if decl.type_params.any? type_param_names = decl.type_params.map(&:name) type_params_map = type_param_names.map { |n| [n, Types::TypeVar.new(n)] }.to_h methods = {} decl.methods.each do |method_decl| method = resolve_interface_method_binding(method_decl, type_params: type_params_map, type_param_constraints: {}) raise_sema_error("duplicate method #{decl.name}.#{method.name}") if methods.key?(method.name) methods[method.name] = method end GenericInterfaceBinding.new( name: decl.name, type_params: type_param_names.freeze, type_param_constraints: {}, methods: methods.freeze, ast: decl, module_name: @ctx.module_name, ) else methods = {} decl.methods.each do |method_decl| method = resolve_interface_method_binding(method_decl) raise_sema_error("duplicate method #{decl.name}.#{method.name}") if methods.key?(method.name) methods[method.name] = method end InterfaceBinding.new( name: decl.name, methods: methods.freeze, ast: decl, module_name: @ctx.module_name, ) end end |
#declare_named_types ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 218 def declare_named_types .each do |decl| with_error_node(decl) do case decl when AST::StructDecl validate_struct_layout!(decl) validate_explicit_aggregate_c_name!(decl) ensure_available_type_name!(decl.name) @ctx.types[decl.name] = if decl.type_params.empty? Types::Struct.new( decl.name, module_name: @ctx.module_name, external: raw_module?, packed: decl.packed, alignment: decl.alignment, linkage_name: decl.c_name, lifetime_params: decl.lifetime_params, ) else Types::GenericStructDefinition.new( decl.name, decl.type_params.map(&:name), module_name: @ctx.module_name, external: raw_module?, packed: decl.packed, alignment: decl.alignment, linkage_name: decl.c_name, lifetime_params: decl.lifetime_params, ) end register_nested_struct_types(decl) when AST::UnionDecl validate_explicit_aggregate_c_name!(decl) ensure_available_type_name!(decl.name) @ctx.types[decl.name] = Types::Union.new(decl.name, module_name: @ctx.module_name, external: raw_module?, linkage_name: decl.c_name) when AST::VariantDecl ensure_available_type_name!(decl.name) @ctx.types[decl.name] = if decl.type_params.empty? Types::Variant.new(decl.name, module_name: @ctx.module_name) else Types::GenericVariantDefinition.new( decl.name, decl.type_params.map(&:name), module_name: @ctx.module_name, ) end when AST::EnumDecl ensure_available_type_name!(decl.name) @ctx.types[decl.name] = Types::Enum.new(decl.name, module_name: @ctx.module_name, external: raw_module?) when AST::FlagsDecl ensure_available_type_name!(decl.name) @ctx.types[decl.name] = Types::Flags.new(decl.name, module_name: @ctx.module_name, external: raw_module?) when AST::OpaqueDecl ensure_available_type_name!(decl.name) @ctx.types[decl.name] = Types::Opaque.new( decl.name, module_name: @ctx.module_name, external: raw_module?, linkage_name: decl.c_name, ) when AST::InterfaceDecl ensure_available_type_name!(decl.name) @ctx.interfaces[decl.name] = declare_interface_binding(decl) end end rescue SemanticError => e collect_structural_error(e) end end |
#declare_top_level_values ⇒ Object
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 735 def declare_top_level_values .each do |decl| with_error_node(decl) do case decl when AST::ConstDecl begin ensure_available_value_name!(decl.name, kind_label: "constant", line: decl.line, column: decl.column) type = resolve_type_ref(decl.type) validate_stored_ref_type!(type, "constant #{decl.name}") raise_sema_error("constant #{decl.name} cannot store proc values") if contains_proc_type?(type) @ctx.top_level_values[decl.name] = value_binding( name: decl.name, type: type, mutable: false, kind: :const, ) rescue SemanticError => e collect_structural_error(e) @ctx.top_level_values[decl.name] = value_binding( name: decl.name, type: @error_type, mutable: false, kind: :const, ) unless @ctx.top_level_values.key?(decl.name) end when AST::VarDecl ensure_available_value_name!(decl.name, kind_label: "module variable", line: decl.line, column: decl.column) raise_sema_error("module variable #{decl.name} requires an explicit type") unless decl.type type = resolve_type_ref(decl.type) validate_stored_ref_type!(type, "module variable #{decl.name}") @ctx.top_level_values[decl.name] = value_binding( name: decl.name, type: type, mutable: true, kind: :var, ) when AST::EventDecl ensure_available_value_name!(decl.name, kind_label: "event", line: decl.line, column: decl.column) @ctx.top_level_values[decl.name] = value_binding( name: decl.name, type: resolve_event_decl_type(decl), mutable: false, kind: :event, ) end end rescue SemanticError => e collect_structural_error(e) end end |
#define_nested_type_bindings_recursive(parent_decl, parent_name: parent_decl.name) ⇒ Object
792 793 794 795 796 797 798 799 800 801 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 792 def define_nested_type_bindings_recursive(parent_decl, parent_name: parent_decl.name) parent_decl.nested_types.each do |nested| qualified_name = "#{parent_name}.#{nested.name}" nested_type = @ctx.types[qualified_name] next unless nested_type.is_a?(Types::Struct) nested_scope = resolve_nested_type_bindings(nested, parent_name: qualified_name) nested_type.define_nested_types(nested_scope) define_nested_type_bindings_recursive(nested, parent_name: qualified_name) end end |
#describe_expression(expression) ⇒ Object
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 348 def describe_expression(expression) case expression when AST::Identifier expression.name when AST::MemberAccess "#{describe_expression(expression.receiver)}.#{expression.member}" when AST::IndexAccess "#{describe_expression(expression.receiver)}[...]" when AST::Specialization "#{describe_expression(expression.callee)}[...]" when AST::FormatString 'f"..."' else expression.class.name.split("::").last end end |
#direct_function_identity_expression?(expression, scopes) ⇒ Boolean
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 93 def direct_function_identity_expression?(expression, scopes) case expression when AST::Identifier return false if lookup_value(expression.name, scopes) return false unless @ctx.top_level_functions.key?(expression.name) binding = @ctx.top_level_functions.fetch(expression.name) !binding.type_params.any? && !foreign_function_binding?(binding) when AST::MemberAccess return false unless expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name) imported_module = @ctx.imports.fetch(expression.receiver.name) return false unless imported_module.functions.key?(expression.member) binding = imported_module.functions.fetch(expression.member) !binding.type_params.any? && !foreign_function_binding?(binding) when AST::Specialization binding = resolve_specialized_function_binding(expression) binding && !foreign_function_binding?(binding) else false end end |
#direct_function_to_proc_argument_compatible?(actual_type, expected_type, expression, scopes) ⇒ Boolean
78 79 80 81 82 83 84 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 78 def direct_function_to_proc_argument_compatible?(actual_type, expected_type, expression, scopes) return false unless expression return false unless actual_type.is_a?(Types::Function) && proc_type?(expected_type) return false unless direct_function_identity_expression?(expression, scopes) function_type_matches_proc_type?(actual_type, expected_type) end |
#direct_task_to_proc_argument_compatible?(actual_type, expected_type) ⇒ Boolean
86 87 88 89 90 91 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 86 def direct_task_to_proc_argument_compatible?(actual_type, expected_type) return false unless actual_type.is_a?(Types::Task) return false unless task_root_proc_type?(expected_type) actual_type == expected_type.return_type end |
#each_enum_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 590 def each_enum_match_arm(statement, scrutinee_type, scopes:) covered_members = {} wildcard_seen = false statement.arms.each do |arm| if arm.pattern.is_a?(AST::ErrorExpr) yield arm, scopes next end if wildcard_pattern?(arm.pattern) raise_sema_error("duplicate wildcard arm in match") if wildcard_seen wildcard_seen = true yield arm, scopes next end validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false) validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false) pattern_type = infer_expression(arm.pattern, scopes:, expected_type: scrutinee_type) ensure_assignable!(pattern_type, scrutinee_type, "match arm expects #{scrutinee_type}, got #{pattern_type}") member_name = match_member_name(arm.pattern, scrutinee_type) raise_sema_error("match arm must be an enum member of #{scrutinee_type}") unless member_name raise_sema_error("duplicate match arm #{scrutinee_type}.#{member_name}") if covered_members.key?(member_name) covered_members[member_name] = true yield arm, scopes end return if wildcard_seen missing_members = scrutinee_type.members - covered_members.keys return if missing_members.empty? raise_sema_error("match on #{scrutinee_type} is missing cases: #{missing_members.join(', ')}") end |
#each_integer_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 626 def each_integer_match_arm(statement, scrutinee_type, scopes:) has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) } raise_sema_error("match on integer type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard covered_values = {} wildcard_seen = false statement.arms.each do |arm| if arm.pattern.is_a?(AST::ErrorExpr) yield arm next end if wildcard_pattern?(arm.pattern) raise_sema_error("duplicate wildcard arm in match") if wildcard_seen wildcard_seen = true yield arm next end if arm.pattern.is_a?(AST::RangeExpr) start_val = arm.pattern.start_expr end_val = arm.pattern.end_expr unless (start_val.is_a?(AST::IntegerLiteral) || start_val.is_a?(AST::CharLiteral)) && (end_val.is_a?(AST::IntegerLiteral) || end_val.is_a?(AST::CharLiteral)) raise_sema_error("range match arm bounds must be integer or char literals, got #{start_val.class.name}..#{end_val.class.name}") end raise_sema_error("range match arm start #{start_val.value} must be <= end #{end_val.value}") if start_val.value > end_val.value range_key = [start_val.value, end_val.value] raise_sema_error("duplicate match arm range #{range_key[0]}..#{range_key[1]}") if covered_values.key?(range_key) covered_values[range_key] = true yield arm next end unless arm.pattern.is_a?(AST::IntegerLiteral) || arm.pattern.is_a?(AST::CharLiteral) raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, range, or _, got #{arm.pattern.class.name}") end value = arm.pattern.value raise_sema_error("duplicate match arm value #{value}") if covered_values.key?(value) covered_values[value] = true yield arm end end |
#each_string_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 675 def each_string_match_arm(statement, scrutinee_type, scopes:) has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) } raise_sema_error("match on str requires a wildcard arm (_:)") unless has_wildcard covered_values = {} wildcard_seen = false statement.arms.each do |arm| if arm.pattern.is_a?(AST::ErrorExpr) yield arm next end if wildcard_pattern?(arm.pattern) raise_sema_error("duplicate wildcard arm in match") if wildcard_seen wildcard_seen = true yield arm next end unless arm.pattern.is_a?(AST::StringLiteral) raise_sema_error("match arm for str scrutinee must be a string literal or _, got #{arm.pattern.class.name}") end value = arm.pattern.value raise_sema_error("duplicate match arm value \"#{value}\"") if covered_values.key?(value) covered_values[value] = true yield arm end end |
#each_tuple_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 706 def each_tuple_match_arm(statement, scrutinee_type, scopes:) has_wildcard = statement.arms.any? { |arm| wildcard_pattern?(arm.pattern) } raise_sema_error("match on tuple type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard arity = scrutinee_type.element_types.length covered_values = {} wildcard_seen = false statement.arms.each do |arm| if arm.pattern.is_a?(AST::ErrorExpr) yield arm next end if wildcard_pattern?(arm.pattern) raise_sema_error("duplicate wildcard arm in match") if wildcard_seen wildcard_seen = true yield arm next end unless arm.pattern.is_a?(AST::ExpressionList) raise_sema_error("match arm for tuple scrutinee must be a tuple literal or _, got #{arm.pattern.class.name}") end elements = arm.pattern.elements raise_sema_error("tuple match arm has #{elements.length} elements but scrutinee has #{arity}") unless elements.length == arity values = elements.map do |elem| if elem.is_a?(AST::Identifier) && elem.name == "_" :wildcard elsif elem.is_a?(AST::IntegerLiteral) || elem.is_a?(AST::CharLiteral) || elem.is_a?(AST::StringLiteral) || elem.is_a?(AST::BooleanLiteral) elem.value else raise_sema_error("tuple match arm element must be a literal or _, got #{elem.class.name}") end end all_literal = values.all? { |v| v != :wildcard } if all_literal raise_sema_error("duplicate tuple match arm #{arm.pattern.elements.map { |e| e.respond_to?(:value) ? e.value.inspect : '_' }.join(', ')}") if covered_values.key?(values) covered_values[values] = true end yield arm end end |
#each_variant_match_arm(statement, scrutinee_type, scopes:) ⇒ Object
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 825 def each_variant_match_arm(statement, scrutinee_type, scopes:) covered_arms = {} wildcard_seen = false equality_cover_table = {} statement.arms.each do |arm| if arm.pattern.is_a?(AST::ErrorExpr) yield arm, scopes next end if wildcard_pattern?(arm.pattern) raise_sema_error("duplicate wildcard arm in match") if wildcard_seen wildcard_seen = true yield arm, scopes next end validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false) validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false) arm_name = variant_match_arm_name(arm.pattern, scrutinee_type) raise_sema_error("match arm must be a variant arm of #{scrutinee_type}") unless arm_name arm_scopes = scopes.dup has_guards = false equality_cover = {} if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty? has_guards, equality_cover = check_struct_match_pattern(arm.pattern.arguments, arm_name, scrutinee_type, arm_scopes, scopes:, arm:) end unless has_guards raise_sema_error("duplicate match arm #{scrutinee_type}.#{arm_name}") if covered_arms.key?(arm_name) covered_arms[arm_name] = true else equality_cover_table[arm_name] = merge_equality_cover(equality_cover_table[arm_name], equality_cover) end if arm.binding_name ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column) fields = scrutinee_type.arm(arm_name) if fields.nil? || fields.empty? raise_sema_error("variant arm #{scrutinee_type}.#{arm_name} has no payload; 'as' binding is not allowed") end payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields) binding = value_binding( name: arm.binding_name, type: payload_type, mutable: true, kind: :local, id: @preassigned_local_binding_ids[arm.object_id], ) arm_scopes = arm_scopes + [{ arm.binding_name => binding }] record_declaration_binding(arm, binding) end yield arm, arm_scopes end equality_cover_table.each do |arm_name, field_covers| next if covered_arms.key?(arm_name) payload_fields = scrutinee_type.arm(arm_name) next unless payload_fields covered = field_covers.any? do |field_name, covered_members| field_type = payload_fields[field_name] next unless field_type.is_a?(Types::Enum) all_members = field_type.members all_members.any? && (all_members - covered_members).empty? end covered_arms[arm_name] = true if covered end return if wildcard_seen missing_arms = scrutinee_type.arm_names - covered_arms.keys return if missing_arms.empty? raise_sema_error("match on #{scrutinee_type} is missing cases: #{missing_arms.join(', ')}") end |
#empty_module_binding(module_path) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 180 def empty_module_binding(module_path) ModuleBinding.new( name: module_path, types: {}, type_declarations: {}, interfaces: {}, attributes: {}, attribute_applications: {}, values: {}, functions: {}, methods: {}, implemented_interfaces: {}, imports: {}, private_types: {}, private_interfaces: {}, private_attributes: {}, private_values: {}, private_functions: {}, private_methods: {}, private_implemented_interfaces: {}, ) end |
#ensure_argument_assignable!(actual_type, expected_type, external:, message:, expression: nil, scopes: nil) ⇒ Object
483 484 485 486 487 488 489 490 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 483 def ensure_argument_assignable!(actual_type, expected_type, external:, message:, expression: nil, scopes: nil) line = source_line(expression) column = source_column(expression) unless argument_types_compatible?(actual_type, expected_type, external:, expression:, scopes:) suggestion = explicit_cast_suggestion(actual_type, expected_type) raise SemanticError.new(, line:, column:, path: @path, suggestion:) end end |
#ensure_assignable!(actual_type, expected_type, message, expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false, line: nil, column: nil) ⇒ Object
449 450 451 452 453 454 455 456 457 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 449 def ensure_assignable!(actual_type, expected_type, , expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false, line: nil, column: nil) line ||= source_line(expression) column ||= source_column(expression) unless types_compatible?(actual_type, expected_type, expression:, scopes:, external_numeric:, external_pointer_null:, contextual_int_to_float:) suggestion = explicit_cast_suggestion(actual_type, expected_type) raise SemanticError.new(, line:, column:, path: @path, suggestion:) end end |
#ensure_available_type_name!(name, line: nil, column: nil, length: nil) ⇒ Object
705 706 707 708 709 710 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 705 def ensure_available_type_name!(name, line: nil, column: nil, length: nil) unless raw_module? || (PRELUDE_TYPE_DEFINING_MODULES[name] == @ctx.module_name) ensure_non_reserved_type_binding_name!(name, kind_label: "type", line:, column:, length:) end raise_sema_error("duplicate type #{name}") if @ctx.types.key?(name) || @ctx.interfaces.key?(name) end |
#ensure_available_value_name!(name, kind_label: "value", line: nil, column: nil, length: nil) ⇒ Object
712 713 714 715 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 712 def ensure_available_value_name!(name, kind_label: "value", line: nil, column: nil, length: nil) ensure_non_reserved_value_type_name!(name, kind_label:, line:, column:, length:) raise_sema_error("duplicate value #{name}") if @ctx.top_level_values.key?(name) || @ctx.top_level_functions.key?(name) end |
#ensure_non_reserved_import_alias_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
727 728 729 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 727 def ensure_non_reserved_import_alias_name!(name, kind_label:, line: nil, column: nil, length: nil) ensure_non_reserved_name!(name, Types::RESERVED_IMPORT_ALIAS_NAMES, kind_label:, line:, column:, length:) end |
#ensure_non_reserved_name!(name, reserved_names, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
717 718 719 720 721 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 717 def ensure_non_reserved_name!(name, reserved_names, kind_label:, line: nil, column: nil, length: nil) return unless reserved_names.include?(name) raise_sema_error("#{kind_label} #{name} uses reserved built-in type name #{name}", line:, column:, length:) end |
#ensure_non_reserved_primitive_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
787 788 789 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 787 def ensure_non_reserved_primitive_name!(name, kind_label:, line: nil, column: nil, length: nil) ensure_non_reserved_value_type_name!(name, kind_label:, line:, column:, length:) end |
#ensure_non_reserved_type_binding_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
731 732 733 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 731 def ensure_non_reserved_type_binding_name!(name, kind_label:, line: nil, column: nil, length: nil) ensure_non_reserved_name!(name, Types::RESERVED_TYPE_BINDING_NAMES, kind_label:, line:, column:, length:) end |
#ensure_non_reserved_value_type_name!(name, kind_label:, line: nil, column: nil, length: nil) ⇒ Object
723 724 725 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 723 def ensure_non_reserved_value_type_name!(name, kind_label:, line: nil, column: nil, length: nil) ensure_non_reserved_name!(name, Types::RESERVED_VALUE_TYPE_NAMES, kind_label:, line:, column:, length:) end |
#ensure_parallel_for_static_lengths_match!(iterable_types) ⇒ Object
1116 1117 1118 1119 1120 1121 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1116 def ensure_parallel_for_static_lengths_match!(iterable_types) lengths = iterable_types.filter_map { |iterable_type| array_type?(iterable_type) ? array_length(iterable_type) : nil } return if lengths.empty? || lengths.all? { |length| length == lengths.first } raise_sema_error("parallel for iterables must have matching lengths") end |
#error_type?(type) ⇒ Boolean
1219 1220 1221 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1219 def error_type?(type) type.is_a?(Types::Error) end |
#evaluate_attribute_arg_call(arguments, scopes:) ⇒ Object
525 526 527 528 529 530 531 532 533 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 525 def evaluate_attribute_arg_call(arguments, scopes:) attribute_handle = evaluate_compile_time_const_value(arguments.first.value, scopes:) return nil unless attribute_handle.is_a?(Types::AttributeHandle) param_name = reflection_identifier_name(arguments[1].value, context: "attribute_arg") return nil unless attribute_handle.argument_values attribute_handle.argument_values[param_name] end |
#evaluate_attribute_of_call(arguments, scopes:) ⇒ Object
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 508 def evaluate_attribute_of_call(arguments, scopes:) target = evaluate_reflection_target_argument(arguments.first.value, scopes:) binding = resolve_attribute_name_argument(arguments[1].value) validate_attribute_target_compatibility!(target, binding) application = find_attribute_application(target, binding) return nil unless application Types::AttributeHandle.new( binding.name, binding.module_name, target, binding.params, application.argument_values, ) end |
#evaluate_attributes_of_call(arguments, scopes:) ⇒ Object
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 572 def evaluate_attributes_of_call(arguments, scopes:) raise_sema_error("attributes_of expects 1 or 2 arguments") unless (1..2).include?(arguments.length) target = evaluate_reflection_target_argument(arguments.first.value, scopes:) if arguments.length == 2 attribute_binding = resolve_attribute_name_argument(arguments[1].value) application = find_attribute_application(target, attribute_binding) return [] unless application [Types::AttributeHandle.new( attribute_binding.name, attribute_binding.module_name, target, attribute_binding.params, application.argument_values, )] else resolved_attribute_applications_for_target(target).map do |application| Types::AttributeHandle.new( application.binding.name, application.binding.module_name, target, application.binding.params, application.argument_values, ) end end end |
#evaluate_block_body_const(decl, name) ⇒ Object
231 232 233 234 235 236 237 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 231 def evaluate_block_body_const(decl, name) @evaluating_const_values << name value = evaluate_compile_time_block(decl.block_body) @evaluating_const_values.pop set_const_value(name, value) value end |
#evaluate_callable_of_call(arguments, scopes:) ⇒ Object
1548 1549 1550 1551 1552 1553 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1548 def evaluate_callable_of_call(arguments, scopes:) raise_sema_error("callable_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("callable_of expects 1 argument, got #{arguments.length}") unless arguments.length == 1 resolve_callable_handle_argument(arguments.first.value, scopes:) end |
#evaluate_compile_time_block(statements, scopes: nil) ⇒ Object
253 254 255 256 257 258 259 260 261 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 253 def evaluate_compile_time_block(statements, scopes: nil) ctx = CompileTime::BlockContext.new(self) result = ctx.evaluate_block(statements, scopes:) result rescue CompileTime::ReturnValue => e e.value rescue CompileTime::Error => e raise_sema_error(e.) end |
#evaluate_compile_time_call(expression, scopes: nil) ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 322 def evaluate_compile_time_call(expression, scopes: nil) case expression.callee when AST::Identifier if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct) fields = {} expression.arguments.each do |argument| val = CompileTime.evaluate(argument.value, resolve_identifier: lambda { |id| if scopes binding = lookup_value(id.name, scopes) return binding.const_value unless binding&.const_value.nil? end resolve_current_module_const_value(id.name) }, resolve_member_access: lambda { |ma| if (receiver_type = resolve_type_expression(ma.receiver)) next resolve_enum_member_const_value(receiver_type, ma.member) end nil }, resolve_call: lambda { |inner_call| evaluate_compile_time_call(inner_call, scopes:) }) return nil unless val fields[argument.name] = val end return fields end case expression.callee.name when "field_of" evaluate_field_of_call(expression.arguments, scopes: scopes || []) when "fields_of" evaluate_fields_of_call(expression.arguments) when "callable_of" evaluate_callable_of_call(expression.arguments, scopes: scopes || []) when "has_attribute" evaluate_has_attribute_call(expression.arguments, scopes: scopes || []) when "attribute_of" evaluate_attribute_of_call(expression.arguments, scopes: scopes || []) when "members_of" evaluate_members_of_call(expression.arguments) when "attributes_of" evaluate_attributes_of_call(expression.arguments, scopes: scopes || []) else func = @ctx.top_level_functions[expression.callee.name] if func&.ast&.respond_to?(:const) && func.ast.const evaluate_const_function_body(func, expression.arguments, scopes:) else evaluate_type_returning_call(expression, scopes:) end end when AST::Specialization if expression.callee.callee.is_a?(AST::Identifier) && expression.callee.callee.name == "attribute_arg" evaluate_attribute_arg_call(expression.arguments, scopes: scopes || []) else callee_name = expression.callee.callee.is_a?(AST::Identifier) ? expression.callee.callee.name : nil if callee_name resolved_type = begin resolve_type_expression(expression.callee) rescue SemanticError nil end if resolved_type && resolved_type.is_a?(Types::Struct) fields = {} expression.arguments.each do |argument| val = CompileTime.evaluate(argument.value, resolve_identifier: lambda { |id| if scopes binding = lookup_value(id.name, scopes) return binding.const_value unless binding&.const_value.nil? end resolve_current_module_const_value(id.name) }, resolve_member_access: lambda { |ma| if (receiver_type = resolve_type_expression(ma.receiver)) next resolve_enum_member_const_value(receiver_type, ma.member) end nil }, resolve_call: lambda { |inner_call| evaluate_compile_time_call(inner_call, scopes:) }) return nil unless val fields[argument.name] = val end return fields end func = @ctx.top_level_functions[callee_name] if func&.ast&.respond_to?(:const) && func.ast.const evaluate_const_function_body(func, expression.arguments, scopes:) else evaluate_type_returning_call(expression, scopes:) end else evaluate_type_returning_call(expression, scopes:) end end else nil end end |
#evaluate_compile_time_const_value(expression, scopes: nil) ⇒ Object
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 263 def evaluate_compile_time_const_value(expression, scopes: nil) CompileTime.evaluate( expression, resolve_identifier: lambda do |identifier_expression| if scopes binding = lookup_value(identifier_expression.name, scopes) return binding.const_value unless binding&.const_value.nil? end value = resolve_current_module_const_value(identifier_expression.name) return value if value # Resolve a bare type-parameter name (e.g. `T`) to its substituted # concrete type so `inline if T == int` can fold at compile time. current_type_params[identifier_expression.name] || @ctx.types[identifier_expression.name] end, resolve_member_access: lambda do |member_access_expression| if member_access_expression.receiver.is_a?(AST::Identifier) && scopes binding = lookup_value(member_access_expression.receiver.name, scopes) if binding && binding.const_value receiver_value = binding.const_value case receiver_value when Types::FieldHandle case member_access_expression.member when "name" then next receiver_value.field_name when "type" then next receiver_value.struct_handle.struct_type.field(receiver_value.field_name) end when Types::MemberHandle case member_access_expression.member when "name" then next receiver_value.name when "value" then next receiver_value.value end when Types::AttributeHandle case member_access_expression.member when "name" then next receiver_value.name end end end end if (receiver_type = resolve_type_expression(member_access_expression.receiver)) next resolve_enum_member_const_value(receiver_type, member_access_expression.member) end next unless member_access_expression.receiver.is_a?(AST::Identifier) resolve_imported_module_const_value(member_access_expression.receiver.name, member_access_expression.member) end, resolve_type_ref: lambda do |type_ref| resolve_type_ref(type_ref) end, resolve_call: lambda do |call_expression| evaluate_compile_time_call(call_expression, scopes:) end, ) rescue CompileTime::Error => e raise_sema_error(e.) end |
#evaluate_const_function_body(func, arguments, scopes:) ⇒ Object
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 471 def evaluate_const_function_body(func, arguments, scopes:) return nil unless func.ast.params.length == arguments.length initial_vars = {} func.ast.params.each_with_index do |param, idx| arg_expr = arguments[idx].value arg_value = if scopes evaluate_compile_time_const_value(arg_expr, scopes:) else CompileTime.evaluate( arg_expr, resolve_identifier: lambda { |id| resolve_current_module_const_value(id.name) }, resolve_member_access: nil, resolve_type_ref: lambda { |tr| resolve_type_ref(tr) }, resolve_call: nil, ) end return nil unless arg_value initial_vars[param.name] = arg_value end ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars) ctx.evaluate_block(func.ast.body, scopes: nil) rescue CompileTime::ReturnValue => e e.value rescue CompileTime::Error => e raise_sema_error(e.) end |
#evaluate_enum_member_const_value(expression, enum_type:, member_values:) ⇒ Object
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 602 def evaluate_enum_member_const_value(expression, enum_type:, member_values:) CompileTime.evaluate( expression, resolve_identifier: lambda do |identifier_expression| resolve_current_module_const_value(identifier_expression.name) end, resolve_member_access: lambda do |member_access_expression| if (receiver_type = resolve_type_expression(member_access_expression.receiver)) next resolve_enum_member_const_value( receiver_type, member_access_expression.member, local_enum_type: enum_type, local_member_values: member_values, ) end next unless member_access_expression.receiver.is_a?(AST::Identifier) resolve_imported_module_const_value(member_access_expression.receiver.name, member_access_expression.member) end, resolve_type_ref: lambda do |type_ref| resolve_type_ref(type_ref) end, ) end |
#evaluate_field_of_call(arguments, scopes:) ⇒ Object
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1536 def evaluate_field_of_call(arguments, scopes:) raise_sema_error("field_of does not support named arguments") if arguments.any?(&:name) raise_sema_error("field_of expects 2 arguments, got #{arguments.length}") unless arguments.length == 2 struct_handle = resolve_struct_handle_argument(arguments.first.value, scopes:) field_name = reflection_identifier_name(arguments[1].value, context: "field_of") field_handle = CompileTime::Reflection.core_field_handle(struct_handle, field_name) raise_sema_error("unknown field #{struct_handle.struct_type}.#{field_name}") unless field_handle field_handle end |
#evaluate_fields_of_call(arguments) ⇒ Object
547 548 549 550 551 552 553 554 555 556 557 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 547 def evaluate_fields_of_call(arguments) raise_sema_error("fields_of expects 1 argument") unless arguments.length == 1 type = resolve_type_expression(arguments.first.value) raise_sema_error("fields_of requires a type argument") unless type handle = struct_handle_for_type(type) raise_sema_error("fields_of requires a struct type, got #{type}") unless handle CompileTime::Reflection.core_field_handles(handle) end |
#evaluate_has_attribute_call(arguments, scopes:) ⇒ Object
501 502 503 504 505 506 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 501 def evaluate_has_attribute_call(arguments, scopes:) target = evaluate_reflection_target_argument(arguments.first.value, scopes:) binding = resolve_attribute_name_argument(arguments[1].value) validate_attribute_target_compatibility!(target, binding) !find_attribute_application(target, binding).nil? end |
#evaluate_members_of_call(arguments) ⇒ Object
559 560 561 562 563 564 565 566 567 568 569 570 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 559 def evaluate_members_of_call(arguments) raise_sema_error("members_of expects 1 argument") unless arguments.length == 1 type = resolve_type_expression(arguments.first.value) raise_sema_error("members_of requires a type argument") unless type unless type.is_a?(Types::Enum) || type.is_a?(Types::Flags) raise_sema_error("members_of requires an enum or flags type, got #{type}") end CompileTime::Reflection.core_member_handles(type) end |
#evaluate_reflection_target_argument(expression, scopes:) ⇒ Object
535 536 537 538 539 540 541 542 543 544 545 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 535 def evaluate_reflection_target_argument(expression, scopes:) if (type_expr = resolve_type_expression(expression)) handle = struct_handle_for_type(type_expr) return handle if handle end value = evaluate_compile_time_const_value(expression, scopes:) return value if value.is_a?(Types::FieldHandle) || value.is_a?(Types::CallableHandle) nil end |
#evaluate_top_level_const_value(name) ⇒ Object
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 191 def evaluate_top_level_const_value(name) return @ctx.top_level_values.fetch(name).const_value if @evaluated_const_values.key?(name) raise_sema_error("cyclic constant value dependency involving #{name}") if @evaluating_const_values.include?(name) decl = @ctx.const_declarations.fetch(name) if decl.block_body return evaluate_block_body_const(decl, name) end if decl.value.is_a?(AST::ErrorExpr) @evaluated_const_values[name] = true return nil end @evaluating_const_values << name value = evaluate_compile_time_const_value(decl.value) @evaluating_const_values.pop if value.is_a?(Integer) binding_ = @ctx.top_level_values.fetch(name) check_type = binding_.type check_type = check_type.backing_type if check_type.respond_to?(:backing_type) if check_type.is_a?(Types::Primitive) && check_type.integer? && (width = check_type.integer_width) max_value = if check_type.signed_integer? (1 << (width - 1)) - 1 else (1 << width) - 1 end if value > max_value raise_sema_error("constant #{name} value #{value} does not fit in type #{binding_.type}") end end end set_const_value(name, value) value end |
#evaluate_type_returning_call(expression, scopes:) ⇒ Object
419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 419 def evaluate_type_returning_call(expression, scopes:) callee_name, type_args = extract_type_callee_info(expression) return nil unless callee_name CompileTime::Reflection.core_evaluate_type_returning( callee_name, type_args, evaluate_value: ->(v) { evaluate_compile_time_const_value(v, scopes:) }, resolve_type_ref: ->(tr) { resolve_type_ref(tr) }, pointer_to: ->(t) { pointer_to(t) }, const_pointer_to: ->(t) { const_pointer_to(t) }, top_level_functions: ->(name) { @ctx.top_level_functions[name] }, evaluate_type_returning_function_body: ->(func, targs) { evaluate_type_returning_function_body(func, targs) }, ) end |
#evaluate_type_returning_function_body(func, type_args) ⇒ Object
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 446 def evaluate_type_returning_function_body(func, type_args) value_params = func.ast.type_params.select { |p| p.is_a?(AST::ValueTypeParam) } return nil if value_params.empty? initial_vars = {} value_params.zip(type_args).each do |param, arg| arg_value = arg.value case arg_value when AST::IntegerLiteral initial_vars[param.name] = arg_value.value when AST::TypeRef initial_vars[param.name] = resolve_type_ref(arg_value) else return nil end end ctx = CompileTime::BlockContext.new(self, initial_variables: initial_vars) ctx.evaluate_block(func.ast.body, scopes: nil) rescue CompileTime::ReturnValue => e e.value rescue CompileTime::Error => e raise_sema_error(e.) end |
#evaluate_when_discriminant(expression) ⇒ Object
1268 1269 1270 1271 1272 1273 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1268 def evaluate_when_discriminant(expression) value = evaluate_compile_time_const_value(expression, scopes: []) raise_sema_error("when discriminant must be a compile-time constant", expression) if value.nil? value end |
#event_carrier_type?(type) ⇒ Boolean
767 768 769 770 771 772 773 774 775 776 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 767 def event_carrier_type?(type) case type when Types::StructInstance type.definition.respond_to?(:has_events?) && type.definition.has_events? when Types::Struct type.respond_to?(:has_events?) && type.has_events? else false end end |
#event_listener_type(event_type) ⇒ Object
624 625 626 627 628 629 630 631 632 633 634 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 624 def event_listener_type(event_type) params = [] params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type Types::Registry.function( nil, params:, return_type: @ctx.types.fetch("void"), external: false, ) end |
#event_member_type(receiver_type, name) ⇒ Object
742 743 744 745 746 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 742 def event_member_type(receiver_type, name) return unless receiver_type.respond_to?(:event) receiver_type.event(name) end |
#event_method_kind(receiver_type, name) ⇒ Object
648 649 650 651 652 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 648 def event_method_kind(receiver_type, name) return unless event_type?(receiver_type) EVENT_METHOD_KINDS[name] end |
#event_method_name(kind) ⇒ Object
700 701 702 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 700 def event_method_name(kind) EVENT_METHOD_NAMES.fetch(kind) end |
#event_receiver_mutable?(receiver_expression, scopes:) ⇒ Boolean
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 708 def event_receiver_mutable?(receiver_expression, scopes:) return true if top_level_event_receiver?(receiver_expression, scopes:) return false unless receiver_expression.is_a?(AST::MemberAccess) receiver_type = infer_lvalue_receiver( receiver_expression.receiver, scopes:, allow_ref_identifier: true, allow_pointer_identifier: true, require_mutable_pointer: true, allow_span_param_identifier: true, ) receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer: true) !event_member_type(receiver_type, receiver_expression.member).nil? rescue SemanticError false end |
#event_stateful_listener_type(event_type, state_type) ⇒ Object
636 637 638 639 640 641 642 643 644 645 646 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 636 def event_stateful_listener_type(event_type, state_type) params = [Types::Registry.parameter("state", Types::Registry.generic_instance("ptr", [state_type]))] params << Types::Registry.parameter("value", event_type.payload_type) if event_type.payload_type Types::Registry.function( nil, params:, return_type: @ctx.types.fetch("void"), external: false, ) end |
#event_type?(type) ⇒ Boolean
751 752 753 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 751 def event_type?(type) type.is_a?(Types::Event) end |
#event_visible_from_current_module?(event_type) ⇒ Boolean
704 705 706 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 704 def event_visible_from_current_module?(event_type) event_type.module_name == @ctx.module_name || event_type.visibility == :public end |
#exact_compile_time_numeric_compatibility?(actual_type, expression, expected_type, scopes: nil) ⇒ Boolean
133 134 135 136 137 138 139 140 141 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 133 def exact_compile_time_numeric_compatibility?(actual_type, expression, expected_type, scopes: nil) return false unless expected_type.is_a?(Types::Primitive) && expected_type.numeric? return false if actual_type.is_a?(Types::EnumBase) value = evaluate_compile_time_const_value(expression, scopes:) return false unless value.is_a?(Numeric) numeric_constant_fits_type?(value, expected_type) end |
#expanded_declarations ⇒ Object
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 202 def Enumerator.new do |yielder| @ctx.ast.declarations.each do |decl| case decl when AST::WhenStmt body = when_chosen_body(decl) if body body.each { |nested| yielder << nested } end else yielder << decl end end end end |
#explicit_cast_suggestion(actual_type, expected_type) ⇒ Object
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 459 def explicit_cast_suggestion(actual_type, expected_type) if expected_type.is_a?(Types::Span) && array_type?(actual_type) && array_element_type(actual_type) == expected_type.element_type return "a span is a mutable view; the array must be a mutable addressable value — bind it with `var` (a `let` array does not coerce)" end if castable_primitive?(actual_type) && castable_primitive?(expected_type) return nil if actual_type == expected_type "use an explicit cast: `#{expected_type}<-(value)`" end if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable) explicit_cast_suggestion(actual_type.base, expected_type.base) else nil end end |
#explicit_declaration_type_for(stmt) ⇒ Object
489 490 491 492 493 494 495 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 489 def explicit_declaration_type_for(stmt) annotation = stmt.respond_to?(:type) ? stmt.type : nil return nil unless annotation name = annotation.respond_to?(:name) ? annotation.name : annotation.to_s @ctx.types[name] end |
#expression_contains_await?(expression) ⇒ Boolean
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 245 def expression_contains_await?(expression) case expression when AST::AwaitExpr true when AST::Call, AST::Specialization expression_contains_await?(expression.callee) || expression.arguments.any? { |argument| expression_contains_await?(argument.value) } when AST::UnaryOp expression_contains_await?(expression.operand) when AST::BinaryOp expression_contains_await?(expression.left) || expression_contains_await?(expression.right) when AST::IfExpr expression_contains_await?(expression.condition) || expression_contains_await?(expression.then_expression) || expression_contains_await?(expression.else_expression) when AST::MatchExpr expression_contains_await?(expression.expression) || expression.arms.any? { |arm| expression_contains_await?(arm.pattern) || expression_contains_await?(arm.value) } when AST::UnsafeExpr expression_contains_await?(expression.expression) when AST::PrefixCast expression_contains_await?(expression.expression) when AST::MemberAccess expression_contains_await?(expression.receiver) when AST::IndexAccess expression_contains_await?(expression.receiver) || expression_contains_await?(expression.index) when AST::FormatString expression.parts.any? { |part| part.is_a?(AST::FormatExprPart) && expression_contains_await?(part.expression) } else false end end |
#expression_end_line(node) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 235 def expression_end_line(node) return nil unless node lines = [node.line] case node when AST::MemberAccess lines << expression_end_line(node.receiver) when AST::IndexAccess lines << expression_end_line(node.receiver) lines << expression_end_line(node.index) when AST::Specialization lines << expression_end_line(node.callee) when AST::Call lines << expression_end_line(node.callee) node.arguments.each { |argument| lines << expression_end_line(argument.value) } when AST::Argument lines << expression_end_line(node.value) when AST::UnaryOp lines << expression_end_line(node.operand) when AST::BinaryOp lines << expression_end_line(node.left) lines << expression_end_line(node.right) when AST::IfExpr lines << expression_end_line(node.condition) lines << expression_end_line(node.then_expression) lines << expression_end_line(node.else_expression) when AST::MatchExpr lines << expression_end_line(node.expression) node.arms.each do |arm| lines << expression_end_line(arm.pattern) lines << expression_end_line(arm.value) end when AST::AwaitExpr lines << expression_end_line(node.expression) when AST::FormatExprPart lines << expression_end_line(node.expression) when AST::PrefixCast lines << expression_end_line(node.expression) end lines.compact.max end |
#extern_enum_integer_argument_compatibility?(actual_type, expected_type) ⇒ Boolean
157 158 159 160 161 162 163 164 165 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 157 def extern_enum_integer_argument_compatibility?(actual_type, expected_type) return unless actual_type.is_a?(Types::EnumBase) return unless expected_type.is_a?(Types::Primitive) && expected_type.integer? && expected_type.fixed_width_integer? backing_type = actual_type.backing_type return unless backing_type.is_a?(Types::Primitive) && backing_type.integer? && backing_type.fixed_width_integer? backing_type.integer_width == expected_type.integer_width end |
#external_numeric_assignment_target?(expression, scopes:) ⇒ Boolean
124 125 126 127 128 129 130 131 132 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 124 def external_numeric_assignment_target?(expression, scopes:) case expression when AST::MemberAccess receiver_type = infer_field_receiver_type(expression.receiver, scopes:, require_mutable_pointer: true) receiver_type.respond_to?(:external) && receiver_type.external else false end end |
#external_void_pointer_argument_compatibility?(actual_type, expected_type) ⇒ Boolean
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 117 def external_void_pointer_argument_compatibility?(actual_type, expected_type) if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable) return external_void_pointer_argument_compatibility?(actual_type.base, expected_type.base) end return external_void_pointer_argument_compatibility?(actual_type, expected_type.base) if expected_type.is_a?(Types::Nullable) return false if actual_type.is_a?(Types::Nullable) return false unless pointer_type?(actual_type) && pointer_type?(expected_type) return false if const_pointer_type?(actual_type) && mutable_pointer_type?(expected_type) actual_pointee = pointee_type(actual_type) expected_pointee = pointee_type(expected_type) actual_pointee == @ctx.types.fetch("void") || expected_pointee == @ctx.types.fetch("void") end |
#extract_type_callee_info(expression) ⇒ Object
434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 434 def extract_type_callee_info(expression) if expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) [expression.callee.name, nil] elsif expression.is_a?(AST::Specialization) if expression.callee.is_a?(AST::Identifier) [expression.callee.name, expression.arguments] elsif expression.callee.is_a?(AST::Specialization) && expression.callee.callee.is_a?(AST::Identifier) [expression.callee.callee.name, expression.callee.arguments] end end end |
#fallback_completion_type ⇒ Object
497 498 499 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 497 def fallback_completion_type @ctx.types["int"] || @ctx.types.values.first end |
#fill_parameter_defaults!(by_position, params, binding, context_name) ⇒ Object
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 1040 def fill_parameter_defaults!(by_position, params, binding, context_name) return unless binding ast_params = binding.ast.params return unless ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value } params.each_with_index do |param, idx| next unless by_position[idx].nil? next unless idx < ast_params.length ast_param = ast_params[idx] next unless ast_param.respond_to?(:default_value) && ast_param.default_value by_position[idx] = AST::Argument.new(name: nil, value: ast_param.default_value) end end |
#fill_positional_defaults!(arguments, binding) ⇒ Object
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 1057 def fill_positional_defaults!(arguments, binding) return unless binding.ast.params.any? { |p| p.respond_to?(:default_value) && p.default_value } ast_params = binding.ast.params while arguments.length < ast_params.length ast_param = ast_params[arguments.length] if ast_param.respond_to?(:default_value) && ast_param.default_value arguments << AST::Argument.new(name: nil, value: ast_param.default_value) else break end end end |
#finalize_top_level_const_values ⇒ Object
187 188 189 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 187 def finalize_top_level_const_values @ctx.const_declarations.each_key { |name| evaluate_top_level_const_value(name) } end |
#find_attribute_application(target, binding) ⇒ Object
25 26 27 28 29 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 25 def find_attribute_application(target, binding) resolved_attribute_applications_for_target(target).find do |application| same_attribute_binding?(application.binding, binding) end end |
#find_reachable_imported_module(module_name) ⇒ Object
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 98 def find_reachable_imported_module(module_name) visited = {} @ctx.imports.each_value do |module_binding| found = find_reachable_imported_module_from(module_binding, module_name, visited) return found if found end nil end |
#find_reachable_imported_module_from(module_binding, module_name, visited) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 109 def find_reachable_imported_module_from(module_binding, module_name, visited) return nil unless module_binding return module_binding if module_binding.name == module_name return nil if visited[module_binding.name] visited[module_binding.name] = true module_binding.imports.each_value do |imported_module| found = find_reachable_imported_module_from(imported_module, module_name, visited) return found if found end nil end |
#finish_local_completion_frame(binding) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 111 def finish_local_completion_frame(binding) return if @active_local_completion_stack.empty? frame = @active_local_completion_stack.pop snapshots = frame[:snapshots] if snapshots.empty? return end start_line = [binding.ast.line, snapshots.first.line].compact.min end_line = snapshots.last.line # Extend end_line to cover the actual function body so that completion # and hover lookups resolve correctly even when snapshots don't reach # the final body line (common for generic functions whose bodies are # only partially analysed, and functions whose last statement does not # record a snapshot). if binding.ast.respond_to?(:body) && binding.ast.body body_end = last_ast_line(binding.ast.body) end_line = body_end if body_end && body_end > end_line end @local_completion_frames << LocalCompletionFrame.new( start_line:, end_line:, function_name: frame[:function_name], receiver_type: frame[:receiver_type], snapshots: snapshots.freeze, ) end |
#float_literal_expression?(expression) ⇒ Boolean
888 889 890 891 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 888 def float_literal_expression?(expression) expression.is_a?(AST::FloatLiteral) || (expression.is_a?(AST::UnaryOp) && ["+", "-"].include?(expression.operator) && float_literal_expression?(expression.operand)) end |
#flow_refinements(expression, truthy:, scopes:) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 68 def flow_refinements(expression, truthy:, scopes:) case expression when AST::Call if truthy && has_attribute_refinement_call?(expression) key = attribute_presence_key_from_call(expression, scopes:) return key ? { key => true } : {} end when AST::UnaryOp return flow_refinements(expression.operand, truthy: !truthy, scopes:) if expression.operator == "not" when AST::BinaryOp case expression.operator when "and" if truthy left_truthy = flow_refinements(expression.left, truthy: true, scopes:) right_scopes = scopes_with_refinements(scopes, left_truthy) right_truthy = flow_refinements(expression.right, truthy: true, scopes: right_scopes) return merge_refinements(left_truthy, right_truthy) end when "or" unless truthy left_falsy = flow_refinements(expression.left, truthy: false, scopes:) right_scopes = scopes_with_refinements(scopes, left_falsy) right_falsy = flow_refinements(expression.right, truthy: false, scopes: right_scopes) return merge_refinements(left_falsy, right_falsy) end when "==", "!=" return null_test_refinements(expression, truthy:, scopes:) end end {} end |
#foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 119 def foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type) case parameter.passing_mode when :plain infer_expression(argument.value, scopes:, expected_type:) when :consuming foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:) parameter.type when :in, :out, :inout if (legacy_passing_mode = foreign_argument_legacy_passing_mode(argument)) raise_sema_error("argument #{parameter.name} to #{function_name} must not use #{legacy_passing_mode}; directional passing is declared on #{function_name}") end if parameter.passing_mode == :in infer_expression(argument.value, scopes:, expected_type: expected_type) else infer_lvalue(argument.value, scopes:) end else raise_sema_error("unsupported foreign passing mode #{parameter.passing_mode}") end end |
#foreign_argument_expression(argument) ⇒ Object
105 106 107 108 109 110 111 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 105 def foreign_argument_expression(argument) if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator) argument.value.operand else argument.value end end |
#foreign_argument_legacy_passing_mode(argument) ⇒ Object
113 114 115 116 117 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 113 def foreign_argument_legacy_passing_mode(argument) return nil unless argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator) argument.value.operator end |
#foreign_boundary_nullable_pointer_like_base?(base) ⇒ Boolean
12 13 14 15 16 17 18 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 12 def foreign_boundary_nullable_pointer_like_base?(base) return true if base.is_a?(Types::Function) || base.is_a?(Types::Proc) return true if opaque_type?(base) return true if base == @ctx.types.fetch("cstr") pointer_type?(base) || const_pointer_type?(base) || ref_type?(base) end |
#foreign_call_consumes_binding?(binding) ⇒ Boolean
1171 1172 1173 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1171 def foreign_call_consumes_binding?(binding) binding.type.params.any? { |parameter| parameter.passing_mode == :consuming } end |
#foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:) ⇒ Object
141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 141 def foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:) unless argument.value.is_a?(AST::Identifier) raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding") end binding = lookup_value(argument.value.name, scopes) unless binding && %i[let var param].include?(binding.kind) && binding.storage_type.is_a?(Types::Nullable) && binding.storage_type.base == parameter.type raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding") end binding end |
#foreign_cstr_argument_compatible?(actual_type, parameter, expression:) ⇒ Boolean
32 33 34 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 32 def foreign_cstr_argument_compatible?(actual_type, parameter, expression:) types_compatible?(actual_type, parameter.type, expression:) || actual_type == @ctx.types.fetch("cstr") end |
#foreign_cstr_boundary_parameter?(parameter) ⇒ Boolean
28 29 30 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 28 def foreign_cstr_boundary_parameter?(parameter) parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str") end |
#foreign_function_binding?(binding) ⇒ Boolean
79 80 81 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 79 def foreign_function_binding?(binding) binding.ast.is_a?(AST::ForeignFunctionDecl) end |
#foreign_mapping_auto_call_shorthand?(expression) ⇒ Boolean
92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 92 def foreign_mapping_auto_call_shorthand?(expression) case expression when AST::Identifier true when AST::MemberAccess foreign_mapping_auto_call_shorthand?(expression.receiver) when AST::Specialization foreign_mapping_auto_call_shorthand?(expression.callee) else false end end |
#foreign_mapping_context? ⇒ Boolean
94 95 96 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 94 def foreign_mapping_context? @foreign_mapping_depth.positive? end |
#foreign_mapping_expression(decl) ⇒ Object
83 84 85 86 87 88 89 90 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 83 def foreign_mapping_expression(decl) return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping) AST::Call.new( callee: decl.mapping, arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) }, ) end |
#foreign_mapping_public_alias_name(name) ⇒ Object
65 66 67 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 65 def foreign_mapping_public_alias_name(name) "#{name}_public" end |
#foreign_mapping_reference_counts(expression, counts = Hash.new(0)) ⇒ Object
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1175 def foreign_mapping_reference_counts(expression, counts = Hash.new(0)) case expression when AST::Identifier counts[expression.name] += 1 when AST::MemberAccess foreign_mapping_reference_counts(expression.receiver, counts) when AST::IndexAccess foreign_mapping_reference_counts(expression.receiver, counts) foreign_mapping_reference_counts(expression.index, counts) when AST::Specialization, AST::Call foreign_mapping_reference_counts(expression.callee, counts) expression.arguments.each { |argument| foreign_mapping_reference_counts(argument.value, counts) } when AST::UnaryOp foreign_mapping_reference_counts(expression.operand, counts) when AST::BinaryOp foreign_mapping_reference_counts(expression.left, counts) foreign_mapping_reference_counts(expression.right, counts) when AST::IfExpr foreign_mapping_reference_counts(expression.condition, counts) foreign_mapping_reference_counts(expression.then_expression, counts) foreign_mapping_reference_counts(expression.else_expression, counts) when AST::UnsafeExpr foreign_mapping_reference_counts(expression.expression, counts) when AST::PrefixCast foreign_mapping_reference_counts(expression.expression, counts) end counts end |
#foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints) ⇒ Object
36 37 38 39 40 41 42 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 36 def foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints) return resolve_type_ref(param.boundary_type, type_params:, type_param_constraints:) if param.boundary_type return const_pointer_to(public_type) if param.mode == :in return pointer_to(foreign_slot_boundary_value_type(public_type)) if [:out, :inout].include?(param.mode) nil end |
#foreign_slot_boundary_value_type(public_type) ⇒ Object
44 45 46 47 48 49 50 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 44 def foreign_slot_boundary_value_type(public_type) if public_type.is_a?(Types::Nullable) && pointer_type?(public_type.base) return public_type.base end public_type end |
#format_string_integer_base_spec_supported?(type) ⇒ Boolean
1519 1520 1521 1522 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1519 def format_string_integer_base_spec_supported?(type) resolved = type.is_a?(Types::EnumBase) ? type.backing_type : type resolved.is_a?(Types::Primitive) && resolved.integer? end |
#format_string_interpolation_supported?(type, context:) ⇒ Boolean
1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1524 def format_string_interpolation_supported?(type, context:) return true if type == @ctx.types.fetch("str") return true if type == @ctx.types.fetch("cstr") return true if type == @ctx.types.fetch("bool") return true if type.is_a?(Types::Primitive) && type.integer? return true if type.is_a?(Types::Primitive) && type.float? return true if type.is_a?(Types::EnumBase) && type.backing_type.is_a?(Types::Primitive) && type.backing_type.integer? return true if resolve_explicit_format_binding(type, context:) false end |
#freeze_scope_bindings(scope) ⇒ Object
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1104 def freeze_scope_bindings(scope) frozen_scope = scope.is_a?(FlowScope) ? FlowScope.new : {} scope.each do |name, binding| frozen_scope[name] = ValueBinding.new( id: binding.id, name: binding.name, storage_type: binding.storage_type, flow_type: binding.flow_type, mutable: false, kind: binding.kind, const_value: binding.const_value, ) end frozen_scope end |
#fresh_noncopyable_event_initializer?(expression, target_type, scopes:) ⇒ Boolean
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 748 def fresh_noncopyable_event_initializer?(expression, target_type, scopes:) return true unless expression case expression when AST::Call callable_kind, callable, _receiver = resolve_callable(expression.callee, scopes:) callable_kind == :struct && callable == target_type when AST::Specialization return false unless expression.callee.is_a?(AST::Identifier) return false unless %w[zero default].include?(expression.callee.name) return false unless expression.arguments.length == 1 type_arg = expression.arguments.first.value type_arg.is_a?(AST::TypeRef) && resolve_type_ref(type_arg) == target_type else false end rescue SemanticError false end |
#function_pointer_type?(type) ⇒ Boolean
333 334 335 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 333 def function_pointer_type?(type) type.is_a?(Types::Function) end |
#function_type_for_name(name) ⇒ Object
109 110 111 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 109 def function_type_for_name(name) @ctx.top_level_functions.fetch(name).type end |
#generic_integer_type_argument?(argument) ⇒ Boolean
790 791 792 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 790 def generic_integer_type_argument?(argument) integer_type_argument?(argument) || argument.is_a?(Types::TypeVar) end |
#harmonize_binary_float_literal_types(left_expression, right_expression, left_type, right_type, scopes:) ⇒ Object
876 877 878 879 880 881 882 883 884 885 886 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 876 def harmonize_binary_float_literal_types(left_expression, right_expression, left_type, right_type, scopes:) if float_literal_expression?(left_expression) && right_type.is_a?(Types::Primitive) && right_type.float? left_type = infer_expression(left_expression, scopes:, expected_type: right_type) end if float_literal_expression?(right_expression) && left_type.is_a?(Types::Primitive) && left_type.float? right_type = infer_expression(right_expression, scopes:, expected_type: left_type) end [left_type, right_type] end |
#harmonize_binary_integer_literal_types(left_expression, right_expression, left_type, right_type, scopes:) ⇒ Object
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 893 def harmonize_binary_integer_literal_types(left_expression, right_expression, left_type, right_type, scopes:) if integer_literal_expression?(left_expression) && right_type.is_a?(Types::Primitive) && right_type.integer? if exact_compile_time_numeric_compatibility?(left_type, left_expression, right_type, scopes:) left_type = infer_expression(left_expression, scopes:, expected_type: right_type) end end if integer_literal_expression?(right_expression) && left_type.is_a?(Types::Primitive) && left_type.integer? if exact_compile_time_numeric_compatibility?(right_type, right_expression, left_type, scopes:) right_type = infer_expression(right_expression, scopes:, expected_type: left_type) end end [left_type, right_type] end |
#has_attribute_refinement_call?(expression) ⇒ Boolean
171 172 173 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 171 def has_attribute_refinement_call?(expression) expression.callee.is_a?(AST::Identifier) && expression.callee.name == "has_attribute" && expression.arguments.length == 2 && expression.arguments.none?(&:name) end |
#implicit_ref_argument_compatible?(actual_type, expected_type, expression, scopes) ⇒ Boolean
16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 16 def implicit_ref_argument_compatible?(actual_type, expected_type, expression, scopes) return false unless expression && scopes return false unless ref_type?(expected_type) return false unless actual_type == referenced_type(expected_type) return false unless safe_reference_source_expression?(expression, scopes:) infer_lvalue(expression, scopes:) true rescue SemanticError false end |
#import_suggestion_for_type(type_name) ⇒ Object
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1426 def import_suggestion_for_type(type_name) return nil if @ctx.global_import_index.nil? || @ctx.global_import_index.empty? type_str = type_name.to_s return nil if type_str.empty? if @ctx.global_import_index.key?(type_str) entry = @ctx.global_import_index[type_str] return nil if entry.nil? module_path = entry.is_a?(Array) ? entry.first : entry if module_path && !@ctx.imports.key?(module_path.to_s.split(".").first) && !@ctx.imports.key?(module_path.to_s) return "type '#{type_str}' is available via 'import #{module_path}'" end end nil end |
#imported_module_binding_for_name(module_name) ⇒ Object
1629 1630 1631 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1629 def imported_module_binding_for_name(module_name) @ctx.imports.each_value.find { |binding| binding.name == module_name } end |
#imported_module_with_private_method(receiver_type, method_name) ⇒ Object
1005 1006 1007 1008 1009 1010 1011 1012 1013 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1005 def imported_module_with_private_method(receiver_type, method_name) imported_module = imported_module_with_private_method_local(receiver_type, method_name) return imported_module if imported_module fallback_owner = specialization_lookup_owner return nil unless fallback_owner fallback_owner.imported_module_with_private_method_local(receiver_type, method_name) end |
#imported_module_with_private_method_local(receiver_type, method_name) ⇒ Object
1015 1016 1017 1018 1019 1020 1021 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1015 def imported_module_with_private_method_local(receiver_type, method_name) @ctx.imports.each_value do |module_binding| return module_binding if module_binding.private_method?(receiver_type, method_name) end nil end |
#infer_addr_source_type(expression, scopes:) ⇒ Object
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1282 def infer_addr_source_type(expression, scopes:) raise_sema_error("ref_of requires a mutable safe lvalue source") unless safe_reference_source_expression?(expression, scopes:) source_type = infer_lvalue(expression, scopes:) if contains_ref_type?(source_type) unless (source_type.is_a?(Types::Struct) || source_type.is_a?(Types::StructInstance)) && source_type.lifetime_params&.any? raise_sema_error("ref_of cannot target ref values") end end source_type end |
#infer_await_expression(expression, scopes:) ⇒ Object
867 868 869 870 871 872 873 874 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 867 def infer_await_expression(expression, scopes:) raise_sema_error("await is only allowed inside async functions") unless inside_async_function? task_type = infer_expression(expression.expression, scopes:) raise_sema_error("await expects Task[T], got #{task_type}") unless task_type.is_a?(Types::Task) task_type.result_type end |
#infer_binary(expression, scopes:, expected_type: nil) ⇒ Object
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 558 def infer_binary(expression, scopes:, expected_type: nil) propagated_type = propagating_expected_type(expression.operator, expected_type) left_type = infer_expression(expression.left, scopes:, expected_type: propagated_type) right_scopes = case expression.operator when "and" scopes_with_refinements(scopes, flow_refinements(expression.left, truthy: true, scopes:)) when "or" scopes_with_refinements(scopes, flow_refinements(expression.left, truthy: false, scopes:)) else scopes end right_expected_type = case expression.operator when "<<", ">>" propagated_type || left_type when "+", "-", "*", "/", "%" propagated_type || left_type when "|", "&", "^" left_type else left_type end right_type = infer_expression(expression.right, scopes: right_scopes, expected_type: right_expected_type) left_type, right_type = harmonize_binary_float_literal_types( expression.left, expression.right, left_type, right_type, scopes: right_scopes, ) left_type, right_type = harmonize_binary_integer_literal_types( expression.left, expression.right, left_type, right_type, scopes: right_scopes, ) case expression.operator when "and", "or" ensure_assignable!(left_type, @ctx.types.fetch("bool"), "operator #{expression.operator} requires bool operands") ensure_assignable!(right_type, @ctx.types.fetch("bool"), "operator #{expression.operator} requires bool operands") @ctx.types.fetch("bool") when "|", "&", "^" simd_result = simd_bitwise_result(left_type, right_type) return simd_result if simd_result # For Flags/Enum types, the operands must match and be bitwise-capable. unless left_type == right_type && (bitwise_type?(left_type) || left_type.is_a?(Types::Flags)) raise_sema_error("operator #{expression.operator} requires matching integer or flags types, got #{left_type} and #{right_type}") end left_type when "+", "-", "*", "/" if expression.operator == "+" && (string_like_type?(left_type) || string_like_type?(right_type)) raise_sema_error("operator + does not support str/cstr concatenation; use continued string literals for static text or string.String/str_buffer for dynamic text") end pointer_result = pointer_arithmetic_result(expression.operator, left_type, right_type) return pointer_result if pointer_result vector_result = vector_arithmetic_result(expression.operator, left_type, right_type) return vector_result if vector_result result_type = common_numeric_type(left_type, right_type) unless result_type if left_type.respond_to?(:signed_integer?) && right_type.respond_to?(:signed_integer?) && left_type.integer? && right_type.integer? && left_type.signed_integer? != right_type.signed_integer? raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type} (use an explicit cast to resolve signed/unsigned mismatch)") else raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type}") end end result_type when "%" simd_result = simd_mod_result(left_type, right_type) return simd_result if simd_result result_type = common_integer_type(left_type, right_type) unless result_type raise_sema_error("operator % requires compatible integer types, got #{left_type} and #{right_type}") end result_type when "<<", ">>" simd_result = simd_shift_result(left_type, right_type) return simd_result if simd_result unless left_type.is_a?(Types::Primitive) && left_type.integer? && right_type.is_a?(Types::Primitive) && right_type.integer? raise_sema_error("operator #{expression.operator} requires integer operands, got #{left_type} and #{right_type}") end left_type when "<", "<=", ">", ">=" unless common_numeric_type(left_type, right_type) raise_sema_error("operator #{expression.operator} requires compatible numeric types, got #{left_type} and #{right_type}") end @ctx.types.fetch("bool") when "==", "!=" unless c_natively_equality_comparable_type?(left_type) && c_natively_equality_comparable_type?(right_type) bad_type = c_natively_equality_comparable_type?(right_type) ? left_type : right_type if struct_instance_type?(bad_type) raise_sema_error("operator #{expression.operator} is not supported for struct type #{bad_type}; use equal[#{bad_type}](...) instead") else raise_sema_error("operator #{expression.operator} is not supported for type #{bad_type}") end end unless common_numeric_type(left_type, right_type) || types_compatible?(left_type, right_type) || types_compatible?(right_type, left_type) raise_sema_error("operator #{expression.operator} requires comparable types, got #{left_type} and #{right_type}") end @ctx.types.fetch("bool") else raise_sema_error("unsupported binary operator #{expression.operator}") end end |
#infer_call(expression, scopes:, expected_type: nil) ⇒ Object
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 925 def infer_call(expression, scopes:, expected_type: nil) callable_kind, callable, receiver = resolve_callable(expression.callee, scopes:) @resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind case callable_kind when :function callable = specialize_function_binding( callable, expression.arguments, scopes:, receiver_type: callable_receiver_type_for_specialization(expression.callee, scopes:), ) check_function_call(callable, expression.arguments, scopes:) validate_specialized_function_body(callable) unless callable.type_arguments.empty? callable.type.return_type when :method callable = specialize_function_binding( callable, expression.arguments, scopes:, receiver_type: infer_method_receiver_type(receiver, scopes:, member_name: expression.callee.member), ) if callable.type_params.any? record_editable_receiver_expression(receiver) if callable.type.receiver_editable raise_sema_error("cannot call editable method #{callable.name} on an immutable receiver") if callable.type.receiver_editable && !assignable_receiver?(receiver, scopes) check_function_call(callable, expression.arguments, scopes:) validate_specialized_function_body(callable) unless callable.type_arguments.empty? callable.type.return_type when :callable_value check_callable_value_call(callable, expression.arguments, scopes:, callee_expression: expression.callee) callable.return_type when :str_buffer_clear, :str_buffer_assign, :str_buffer_append, :str_buffer_assign_format, :str_buffer_append_format, :str_buffer_len, :str_buffer_capacity, :str_buffer_as_str, :str_buffer_as_cstr check_str_buffer_method_call(callable_kind, receiver, expression.arguments, scopes:) when :array_as_span raise_sema_error("as_span does not support named arguments") if expression.arguments.any?(&:name) raise_sema_error("as_span expects 0 arguments, got #{expression.arguments.length}") unless expression.arguments.empty? Types::Registry.span(array_element_type(callable)) when :event_subscribe, :event_subscribe_once, :event_unsubscribe, :event_emit, :event_wait check_event_method_call(callable_kind, receiver, expression.arguments, scopes:) when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange check_atomic_method_call(callable_kind, callable, receiver, expression.arguments, scopes:) when :simd_lane_with check_simd_method_call(callable_kind, callable, receiver, expression.arguments, scopes:) when :struct check_aggregate_construction(callable, expression.arguments, scopes:) when :simd check_simd_construction(callable, expression.arguments, scopes:) when :struct_with check_struct_with_call(callable, receiver, expression.arguments, scopes:) when :variant_arm_ctor check_variant_arm_construction(callable, expression.arguments, scopes:) when :array check_array_construction(callable, expression.arguments, scopes:) when :reinterpret check_reinterpret_call(callable, expression.arguments, scopes:) when :hash check_hash_call(callable, expression.arguments, scopes:) when :equal check_equal_call(callable, expression.arguments, scopes:) when :order check_order_call(callable, expression.arguments, scopes:) when :fatal check_fatal_call(expression.arguments, scopes:) when :ref_of check_ref_of_call(expression.arguments, scopes:) when :const_ptr_of check_const_ptr_of_call(expression.arguments, scopes:) when :read check_read_call(expression.arguments, scopes:) when :ptr_of check_ptr_of_call(expression.arguments, scopes:) when :field_of check_field_of_call(expression.arguments, scopes:) when :callable_of check_callable_of_call(expression.arguments, scopes:) when :attribute_of check_attribute_of_call(expression.arguments, scopes:) when :has_attribute check_has_attribute_call(expression.arguments, scopes:) when :get check_get_call(expression.arguments, scopes:) when :attribute_arg check_attribute_arg_call(callable, expression.arguments, scopes:) when :dyn_method check_dyn_method_call(callable, receiver, expression.arguments, scopes:) callable.return_type when :adapt check_adapt_call(callable, expression.arguments, scopes:) else raise_sema_error("#{describe_expression(expression.callee)} is not callable") end end |
#infer_call_return_type(call, scopes) ⇒ Object
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 543 def infer_call_return_type(call, scopes) callee = call.callee callee = callee.callee while callee.is_a?(AST::Specialization) case callee when AST::Identifier func = @ctx.top_level_functions[callee.name] return func.type.return_type if func member = callee.name this_binding = lookup_value("this", scopes) if this_binding && (rt = receiver_type_for_fields(this_binding.type)) method = @ctx.methods.dig(rt, member) return method.type.return_type if method end when AST::MemberAccess name = resolve_receiver_name_for_call_inference(callee, scopes) member = callee.member return nil unless name if (import = @ctx.imports[name]) func = import.functions[member] return func.type.return_type if func end receiver_type = @ctx.types[name] if receiver_type && @ctx.methods.key?(receiver_type) method = @ctx.methods[receiver_type]["static:#{member}"] || @ctx.methods[receiver_type][member] return method.type.return_type if method end end nil end |
#infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
775 776 777 778 779 780 781 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 775 def infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:) arm_entries = [] each_enum_match_arm(expression, scrutinee_type, scopes:) do |arm, arm_scopes| arm_entries << [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#infer_expression(expression, scopes:, expected_type: nil) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 134 def infer_expression(expression, scopes:, expected_type: nil) with_error_node(expression) do type = case expression when AST::ErrorExpr expected_type || @error_type when AST::IntegerLiteral infer_integer_literal(expression, expected_type) when AST::FloatLiteral infer_float_literal(expression, expected_type) when AST::CharLiteral @ctx.types.fetch("ubyte") when AST::SizeofExpr unless check_layout_type_via_ct(expression.type, context: "size_of", scopes:) infer_layout_query_type(expression.type, context: "size_of") end @ctx.types.fetch("ptr_uint") when AST::AlignofExpr unless check_layout_type_via_ct(expression.type, context: "align_of", scopes:) infer_layout_query_type(expression.type, context: "align_of") end @ctx.types.fetch("ptr_uint") when AST::OffsetofExpr infer_offsetof_type(expression.type, expression.field, scopes:) resolve_and_store_offset(expression, scopes:) @ctx.types.fetch("ptr_uint") when AST::StringLiteral @ctx.types.fetch(expression.cstring ? "cstr" : "str") when AST::FormatString check_format_string_literal(expression, scopes:) @ctx.types.fetch("str") when AST::BooleanLiteral @ctx.types.fetch("bool") when AST::NullLiteral infer_null_literal(expression) when AST::Identifier infer_identifier(expression, scopes:, expected_type:) when AST::MemberAccess infer_member_access(expression, scopes:, expected_type:) when AST::IndexAccess infer_index_access(expression, scopes:) when AST::UnaryOp infer_unary(expression, scopes:, expected_type:) when AST::BinaryOp infer_binary(expression, scopes:, expected_type:) when AST::IfExpr infer_if_expression(expression, scopes:, expected_type:) when AST::MatchExpr infer_match_expression(expression, scopes:, expected_type:) when AST::UnsafeExpr infer_unsafe_expression(expression, scopes:, expected_type:) when AST::ProcExpr infer_proc_expression(expression, scopes:, expected_type:) when AST::AwaitExpr infer_await_expression(expression, scopes:) when AST::DetachExpr @uses_parallel_for = true validate_detach_expression!(expression.body.first&.expression || expression.body, scopes:) check_block(expression.body, scopes:, return_type: @ctx.types.fetch("void"), allow_return: false) Types::Handle.new when AST::Call infer_call(expression, scopes:, expected_type:) when AST::PrefixCast target_type = resolve_type_ref(expression.target_type) check_cast_call(target_type, [AST::Argument.new(name: nil, value: expression.expression)], scopes:) target_type when AST::Specialization if expression.callee.is_a?(AST::Identifier) if expression.callee.name == "zero" callable_kind, callable, = resolve_callable(expression, scopes:) return check_zero_call(callable, [], expected_type:) if callable_kind == :zero end if expression.callee.name == "default" resolution = resolve_default_specialization(expression) return resolution.target_type end end if (callable_resolution = resolve_specialized_callable_binding(expression, scopes:)) callable_kind, function_binding, = callable_resolution raise_sema_error("specialized method #{describe_expression(expression)} must be called") if callable_kind == :method return function_binding.type end raise_sema_error("specialized name #{describe_expression(expression)} must be called") when AST::RangeExpr raise_sema_error("range expression can only be used as a for-loop iterable or range index target") when AST::ExpressionList if expected_type && expected_type.is_a?(Types::GenericInstance) && expected_type.name == "array" element_type = expected_type.arguments.first expression.elements.each do |element| value = element.is_a?(AST::Argument) ? element.value : element actual = infer_expression(value, scopes:, expected_type: element_type) ensure_assignable!(actual, element_type, "array element type mismatch: expected #{element_type}, got #{actual}", expression: value) end expected_type else names = [] element_types = [] expression.elements.each do |element| if element.is_a?(AST::Argument) names << element.name element_types << infer_expression(element.value, scopes:) else names << nil element_types << infer_expression(element, scopes:) end end has_named = names.any? Types::Registry.tuple(element_types, field_names: has_named ? names : nil) end else raise_sema_error("unsupported expression #{expression.class.name}") end @resolved_expr_types[@ctx.ast.node_ids[expression.object_id]] = type type end end |
#infer_field_handle_member(expression, scopes:) ⇒ Object
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1648 def infer_field_handle_member(expression, scopes:) case expression.member when "name" @ctx.types.fetch("str") when "type" handle = evaluate_compile_time_const_value(expression.receiver, scopes:) return @error_type unless handle.is_a?(Types::FieldHandle) resolve_type_ref(handle.field_declaration.type) else raise_sema_error("unknown member #{expression.member} of field_handle") end end |
#infer_field_receiver_type(receiver_expression, scopes:, require_mutable_pointer: false) ⇒ Object
1319 1320 1321 1322 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1319 def infer_field_receiver_type(receiver_expression, scopes:, require_mutable_pointer: false) receiver_type = infer_expression(receiver_expression, scopes:) project_field_receiver_type(receiver_type, require_mutable_pointer:) end |
#infer_float_literal(expression, expected_type) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 296 def infer_float_literal(expression, expected_type) if expression.lexeme.end_with?("f") @ctx.types.fetch("float") elsif expression.lexeme.end_with?("d") @ctx.types.fetch("double") elsif expected_type.is_a?(Types::Primitive) && expected_type.float? expected_type else @ctx.types.fetch("float") end end |
#infer_function_type_arguments(binding, arguments, scopes:, receiver_type: nil) ⇒ Object
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 255 def infer_function_type_arguments(binding, arguments, scopes:, receiver_type: nil) expected_params = binding.type.params unless call_arity_matches?(binding.type, arguments.length) raise_sema_error((binding.type, binding.name, arguments.length)) end substitutions = infer_receiver_type_substitutions(binding, receiver_type) expected_params.each_with_index do |parameter, index| argument = arguments.fetch(index) candidate_type = substitute_type(parameter.type, substitutions) expected_argument_type = if callable_type?(candidate_type) candidate_type elsif contains_type_var?(candidate_type) nil else candidate_type end actual_type = foreign_argument_actual_type(parameter, argument, scopes:, function_name: binding.name, expected_type: expected_argument_type) collect_type_substitutions(parameter.type, actual_type, substitutions, binding.name) end binding.type_params.map do |name| inferred = substitutions[name] raise_sema_error("cannot infer type argument #{name} for function #{binding.name}") unless inferred raise_sema_error("generic function #{binding.name} cannot be instantiated with ref types") if contains_ref_type?(inferred) inferred end end |
#infer_identifier(expression, scopes:, expected_type: nil) ⇒ Object
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 308 def infer_identifier(expression, scopes:, expected_type: nil) binding = lookup_value(expression.name, scopes) if binding record_identifier_binding(expression, binding) return binding.type end if @ctx.top_level_functions.key?(expression.name) raise_sema_error("generic function #{expression.name} must be called") if @ctx.top_level_functions.fetch(expression.name).type_params.any? function_type = function_type_for_name(expression.name) if expected_type record_callable_value_identifier_site(expression) return function_type end raise_sema_error("function #{expression.name} must be called") end raise_sema_error("module #{expression.name} cannot be used as a value") if @ctx.imports.key?(expression.name) if @ctx.types.key?(expression.name) return Types::BUILTIN_TYPE_META_TYPE if expected_type.is_a?(Types::TypeType) return @ctx.types.fetch(expression.name) end if @ctx.top_level_functions && @ctx.types && scopes func_names = @ctx.top_level_functions.keys.map(&:to_s) type_names = @ctx.types.keys.map(&:to_s) scoped_names = scopes.flat_map { |s| s.is_a?(Hash) ? s.keys : [] }.map(&:to_s) suggestion = suggest_name(expression.name, func_names + type_names + scoped_names) end raise_sema_error("unknown name #{expression.name}", expression, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil) end |
#infer_if_expression(expression, scopes:, expected_type: nil) ⇒ Object
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 681 def infer_if_expression(expression, scopes:, expected_type: nil) condition_type = infer_expression(expression.condition, scopes:, expected_type: @ctx.types.fetch("bool")) ensure_assignable!(condition_type, @ctx.types.fetch("bool"), "if expression condition must be bool, got #{condition_type}") then_scopes = scopes_with_refinements(scopes, flow_refinements(expression.condition, truthy: true, scopes:)) else_scopes = scopes_with_refinements(scopes, flow_refinements(expression.condition, truthy: false, scopes:)) then_type = infer_expression(expression.then_expression, scopes: then_scopes, expected_type:) else_type = infer_expression(expression.else_expression, scopes: else_scopes, expected_type:) return expected_type if expected_type && types_compatible?(then_type, expected_type, expression: expression.then_expression) && types_compatible?(else_type, expected_type, expression: expression.else_expression) common_type = conditional_common_type( then_type, else_type, then_expression: expression.then_expression, else_expression: expression.else_expression, ) return common_type if common_type raise_sema_error("if expression branches require compatible types, got #{then_type} and #{else_type}") end |
#infer_index_access(expression, scopes:) ⇒ Object
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 439 def infer_index_access(expression, scopes:) receiver_type = infer_expression(expression.receiver, scopes:) index_type = infer_expression(expression.index, scopes:) if soa_type?(receiver_type) return receiver_type.element_type end if simd_type?(receiver_type) return receiver_type.element_type end if array_type?(receiver_type) && !unsafe_context? && !addressable_storage_expression?(expression.receiver, scopes:) raise_sema_error("safe array indexing requires an addressable array value; bind it to a local first") end if array_type?(receiver_type) && expression.index.is_a?(AST::IntegerLiteral) length = array_length(receiver_type) index_val = expression.index.value if index_val >= length || index_val < 0 raise_sema_error("array index #{index_val} is out of bounds for array[T, #{length}]", expression) end end if array_type?(receiver_type) && expression.index.is_a?(AST::UnaryOp) && expression.index.operator == "-" && expression.index.operand.is_a?(AST::IntegerLiteral) length = array_length(receiver_type) index_val = -expression.index.operand.value raise_sema_error("array index #{index_val} is out of bounds for array[T, #{length}]", expression) end infer_index_result_type(receiver_type, index_type) end |
#infer_index_result_type(receiver_type, index_type) ⇒ Object
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 922 def infer_index_result_type(receiver_type, index_type) raise_sema_error("index must be an integer type, got #{index_type}") unless integer_type?(index_type) if array_type?(receiver_type) return array_element_type(receiver_type) end if span_type?(receiver_type) return receiver_type.element_type end if soa_type?(receiver_type) return receiver_type.element_type end if simd_type?(receiver_type) return receiver_type.element_type end if pointer_type?(receiver_type) require_unsafe!("pointer indexing requires unsafe") unless own_type?(receiver_type) return pointee_type(receiver_type) end raise_sema_error("cannot index #{receiver_type}") end |
#infer_integer_literal(expression, expected_type) ⇒ Object
266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 266 def infer_integer_literal(expression, expected_type) # Integer type suffixes: 42u8, 0xFFub, 100uz, etc. suffix_type = integer_suffix_type(expression.lexeme) return @ctx.types.fetch(suffix_type) if suffix_type if expected_type.is_a?(Types::Primitive) && expected_type.integer? && value_fits_integer_type?(expression.value, expected_type) expected_type else @ctx.types.fetch("int") end end |
#infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
783 784 785 786 787 788 789 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 783 def infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:) arm_entries = [] each_integer_match_arm(expression, scrutinee_type, scopes:) do |arm| arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#infer_layout_query_type(type_ref, context:) ⇒ Object
584 585 586 587 588 589 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 584 def infer_layout_query_type(type_ref, context:) type = resolve_type_ref(type_ref) return type if sized_layout_type?(type) raise_sema_error("#{context} requires a concrete sized type, got #{type_ref}") end |
#infer_local_initializer_type(stmt, scopes) ⇒ Object
483 484 485 486 487 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 483 def infer_local_initializer_type(stmt, scopes) return nil unless stmt.value infer_simple_local_type(stmt.value, scopes) end |
#infer_lvalue(expression, scopes:) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 6 def infer_lvalue(expression, scopes:) case expression when AST::Identifier binding = lookup_value(expression.name, scopes) unless binding scoped_names = scopes.flat_map { |s| s.is_a?(Hash) ? s.keys : [] }.map(&:to_s) suggestion = suggest_name(expression.name, scoped_names) raise_sema_error("unknown name #{expression.name}", expression, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil) end record_identifier_binding(expression, binding) raise_sema_error("cannot assign to immutable #{expression.name}") unless binding.mutable binding.storage_type when AST::MemberAccess receiver_type = infer_lvalue_receiver(expression.receiver, scopes:, allow_ref_identifier: true, allow_pointer_identifier: true, allow_span_param_identifier: true) receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer: true) unless aggregate_type?(receiver_type) raise_sema_error("cannot assign to member #{expression.member} of #{receiver_type}") end field_type = receiver_type.field(expression.member) raise_sema_error("unknown field #{receiver_type}.#{expression.member}") unless field_type field_type when AST::IndexAccess receiver_type = infer_lvalue_receiver( expression.receiver, scopes:, allow_pointer_identifier: true, require_mutable_pointer: true, allow_span_param_identifier: true, ) index_type = infer_expression(expression.index, scopes:) infer_index_result_type(receiver_type, index_type) when AST::Call if read_call?(expression) validate_read_call_arguments!(expression.arguments) return infer_reference_value_type(expression.arguments.first.value, scopes:) end raise_sema_error("invalid assignment target") when AST::BinaryOp raise_sema_error("invalid assignment target") else raise_sema_error("invalid assignment target") end end |
#infer_lvalue_receiver(expression, scopes:, allow_ref_identifier: false, allow_pointer_identifier: false, require_mutable_pointer: false, allow_span_param_identifier: false) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 55 def infer_lvalue_receiver(expression, scopes:, allow_ref_identifier: false, allow_pointer_identifier: false, require_mutable_pointer: false, allow_span_param_identifier: false) case expression when AST::Identifier binding = lookup_value(expression.name, scopes) raise_sema_error("unknown name #{expression.name}") unless binding record_identifier_binding(expression, binding) return referenced_type(binding.type) if allow_ref_identifier && ref_type?(binding.type) if allow_pointer_identifier && pointer_type?(binding.type) require_unsafe!("raw pointer dereference requires unsafe") unless own_type?(binding.type) raise_sema_error("cannot assign through read-only raw pointer #{binding.type}") if require_mutable_pointer && const_pointer_type?(binding.type) return binding.type end if allow_span_param_identifier && binding.kind == :param && span_type?(binding.type) return binding.type end raise_sema_error("cannot assign through immutable #{expression.name}") unless binding.mutable binding.type when AST::MemberAccess receiver_type = infer_lvalue_receiver( expression.receiver, scopes:, allow_ref_identifier:, allow_pointer_identifier:, require_mutable_pointer:, allow_span_param_identifier:, ) receiver_type = project_field_receiver_type(receiver_type, require_mutable_pointer:) unless aggregate_type?(receiver_type) raise_sema_error("cannot access member #{expression.member} of #{receiver_type}") end field_type = receiver_type.field(expression.member) raise_sema_error("unknown field #{receiver_type}.#{expression.member}") unless field_type field_type when AST::IndexAccess receiver_type = infer_lvalue_receiver( expression.receiver, scopes:, allow_ref_identifier:, allow_pointer_identifier:, require_mutable_pointer:, allow_span_param_identifier:, ) index_type = infer_expression(expression.index, scopes:) infer_index_result_type(receiver_type, index_type) when AST::Call if read_call?(expression) validate_read_call_arguments!(expression.arguments) return infer_reference_value_type(expression.arguments.first.value, scopes:) end raise_sema_error("invalid assignment target") when AST::BinaryOp require_unsafe!("raw pointer arithmetic as lvalue receiver requires unsafe") type = infer_expression(expression, scopes:) raise_sema_error("binary op lvalue receiver must be a pointer") unless pointer_type?(type) raise_sema_error("cannot assign through read-only raw pointer #{type}") if require_mutable_pointer && const_pointer_type?(type) type else raise_sema_error("invalid assignment target") end end |
#infer_match_expression(expression, scopes:, expected_type: nil) ⇒ Object
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 732 def infer_match_expression(expression, scopes:, expected_type: nil) validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false) validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false) scrutinee_type = infer_expression(expression.expression, scopes:) if error_type?(scrutinee_type) infer_recovered_match_expression(expression, scopes:, expected_type:) elsif scrutinee_type.is_a?(Types::Enum) infer_enum_match_expression(expression, scrutinee_type, scopes:, expected_type:) elsif scrutinee_type.is_a?(Types::Variant) infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:) elsif integer_type?(scrutinee_type) infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:) elsif scrutinee_type.is_a?(Types::StringView) infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:) elsif scrutinee_type.is_a?(Types::Tuple) infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:) else raise_sema_error("match requires an enum, variant, or integer scrutinee, got #{scrutinee_type}") end end |
#infer_match_expression_arm_value(arm, scopes:, expected_type:) ⇒ Object
815 816 817 818 819 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 815 def infer_match_expression_arm_value(arm, scopes:, expected_type:) validate_consuming_foreign_expression!(arm.value, scopes:, root_allowed: false) validate_hoistable_foreign_expression!(arm.value, scopes:, root_hoistable: false) infer_expression(arm.value, scopes:, expected_type:) end |
#infer_member_access(expression, scopes:, expected_type: nil) ⇒ Object
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 342 def infer_member_access(expression, scopes:, expected_type: nil) type = resolve_type_expression(expression.receiver) if type return @error_type if error_type?(type) member_type = resolve_type_member(type, expression.member) return member_type if member_type if type.is_a?(Types::Variant) && type.arm_names.include?(expression.member) raise_sema_error("variant arm #{type}.#{expression.member} has payload; construct it with #{type}.#{expression.member}(field: value, ...)") end if (method = lookup_method(type, expression.member)) raise_sema_error("associated function #{type}.#{expression.member} must be called") unless method.type.receiver_type.nil? raise_sema_error("method #{type}.#{expression.member} must be called") end raise_sema_error("unknown member #{type}.#{expression.member}") end if expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.receiver.name) imported_module = @ctx.imports.fetch(expression.receiver.name) value = imported_module.values[expression.member] return value.type if value if imported_module.functions.key?(expression.member) function = imported_module.functions.fetch(expression.member) raise_sema_error("generic function #{expression.receiver.name}.#{expression.member} must be called") if function.type_params.any? if expected_type record_callable_value_member_access_site(expression) return function.type end raise_sema_error("function #{expression.receiver.name}.#{expression.member} must be called") end if imported_module.types.key?(expression.member) raise_sema_error("type #{expression.receiver.name}.#{expression.member} cannot be used as a value") end if imported_module.private_value?(expression.member) || imported_module.private_function?(expression.member) || imported_module.private_type?(expression.member) raise_sema_error("#{expression.receiver.name}.#{expression.member} is private to module #{imported_module.name}") end raise_sema_error("unknown member #{expression.receiver.name}.#{expression.member}") end field_receiver_type = infer_field_receiver_type(expression.receiver, scopes:) method_receiver_type = infer_method_receiver_type(expression.receiver, scopes:, member_name: expression.member) return @error_type if error_type?(field_receiver_type) || error_type?(method_receiver_type) if array_type?(field_receiver_type) && expression.member == "as_span" element_type = array_element_type(field_receiver_type) return Types::Registry.span(element_type) end if char_array_removed_text_method?(method_receiver_type, expression.member) raise_sema_error("#{method_receiver_type}.#{expression.member} is not available; array[char, N] is raw storage, use str_buffer[N] or an explicit helper") end if str_buffer_type?(method_receiver_type) && str_buffer_method_kind(method_receiver_type, expression.member) raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called") end if event_type?(method_receiver_type) && event_method_kind(method_receiver_type, expression.member) raise_sema_error("method #{method_receiver_type}.#{expression.member} must be called") end unless aggregate_type?(field_receiver_type) if field_receiver_type == builtin_field_handle_type return infer_field_handle_member(expression, scopes:) end if field_receiver_type == builtin_member_handle_type return infer_member_handle_member(expression) end raise_sema_error("cannot access member #{expression.member} of #{field_receiver_type}") end field_type = field_receiver_type.field(expression.member) return field_type if field_type if (event_type = event_member_type(field_receiver_type, expression.member)) unless event_visible_from_current_module?(event_type) raise_sema_error("event #{field_receiver_type}.#{expression.member} is private to module #{event_type.module_name}") end return event_type end if lookup_method(method_receiver_type, expression.member) raise_sema_error("method #{method_receiver_type.name}.#{expression.member} must be called") end if (imported_module = imported_module_with_private_method(method_receiver_type, expression.member)) raise_sema_error("#{method_receiver_type}.#{expression.member} is private to module #{imported_module.name}") end raise_sema_error("unknown field #{field_receiver_type}.#{expression.member}") end |
#infer_member_access_field_type(value, scopes) ⇒ Object
528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 528 def infer_member_access_field_type(value, scopes) receiver_type = case value.receiver when AST::Identifier if value.receiver.name == "this" (binding = lookup_value("this", scopes)) ? binding.type : nil else (binding = lookup_value(value.receiver.name, scopes)) ? binding.type : nil end end return nil unless receiver_type field_receiver = receiver_type_for_fields(receiver_type) field_receiver&.respond_to?(:fields) ? field_receiver.fields[value.member] : nil end |
#infer_member_handle_member(expression) ⇒ Object
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1662 def infer_member_handle_member(expression) case expression.member when "name" @ctx.types.fetch("str") when "value" @ctx.types.fetch("int") else raise_sema_error("unknown member #{expression.member} of member_handle") end end |
#infer_method_receiver_type(receiver_expression, scopes:, member_name: nil) ⇒ Object
1314 1315 1316 1317 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1314 def infer_method_receiver_type(receiver_expression, scopes:, member_name: nil) receiver_type = infer_expression(receiver_expression, scopes:) project_method_receiver_type(receiver_type, member_name:) end |
#infer_null_literal(expression) ⇒ Object
200 201 202 203 204 205 206 207 208 209 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 200 def infer_null_literal(expression) return @null_type unless expression.type target_type = resolve_type_ref(expression.type) unless typed_null_target_type?(target_type) raise_sema_error("typed null requires pointer-like type, got #{target_type}") end Types::Null.new(target_type) end |
#infer_offsetof_type(type_ref, field_name, scopes: nil) ⇒ Object
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 629 def infer_offsetof_type(type_ref, field_name, scopes: nil) type = resolve_type_ref(type_ref) unless layout_aggregate_type?(type) raise_sema_error("offset_of requires a struct, union, span, or str type, got #{type}") end field_type = type.field(field_name) return type if field_type if scopes binding = lookup_value(field_name, scopes) if binding && binding.storage_type.is_a?(Types::ReflectionHandleType) && binding.storage_type.name == "field_handle" return type end end raise_sema_error("unknown field #{type}.#{field_name}") end |
#infer_option_propagation(source_type, allow_void_success:) ⇒ Object
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 541 def infer_option_propagation(source_type, allow_void_success:) success_type = let_else_success_type(source_type) raise_sema_error("propagation requires a non-void Option success type") if success_type == @ctx.types.fetch("void") && !allow_void_success context = current_return_context raise_sema_error("propagation is only allowed inside function and proc bodies") unless context raise_sema_error("propagation is not allowed inside defer blocks") unless context[:allow_return] return_type = context[:return_type] unless option_let_else_type?(return_type) raise_sema_error("propagation requires enclosing function/proc to return Option[_], got #{return_type}") end success_type end |
#infer_proc_expression(expression, scopes:, expected_type: nil) ⇒ Object
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 847 def infer_proc_expression(expression, scopes:, expected_type: nil) proc_type = resolve_type_ref(AST::ProcType.new(params: expression.params, return_type: expression.return_type)) if expected_type && !proc_type_compatible?(proc_type, expected_type) raise_sema_error("proc expression expects #{proc_type}, got #{expected_type}") end proc_scopes = scopes.map { |scope| freeze_scope_bindings(scope) } proc_scope = {} expression.params.each do |param| ensure_non_reserved_primitive_name!(param.name, kind_label: "parameter", line: param.line, column: param.column) param_type = resolve_type_ref(param.type) validate_parameter_ref_type!(param_type, function_name: "proc", parameter_name: param.name, external: false) validate_parameter_proc_type!(param_type, function_name: "proc", parameter_name: param.name, external: false, foreign: false) proc_scope[param.name] = value_binding(name: param.name, type: param_type, mutable: false, kind: :param) end check_block(expression.body, scopes: proc_scopes + [proc_scope], return_type: proc_type.return_type, allow_return: true) proc_type end |
#infer_propagate_expression(operand, scopes:, allow_void_success: false) ⇒ Object
508 509 510 511 512 513 514 515 516 517 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 508 def infer_propagate_expression(operand, scopes:, allow_void_success: false) source_type = infer_expression(operand, scopes:) if result_let_else_type?(source_type) infer_result_propagation(source_type, allow_void_success:) elsif option_let_else_type?(source_type) infer_option_propagation(source_type, allow_void_success:) else raise_sema_error("propagation expects Result[T, E] or Option[T], got #{source_type}") end end |
#infer_receiver_type_substitutions(binding, receiver_type) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 6 def infer_receiver_type_substitutions(binding, receiver_type) declared_receiver_type = binding.declared_receiver_type return {} unless declared_receiver_type case declared_receiver_type when Types::Nullable unless receiver_type.is_a?(Types::Nullable) raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end infer_receiver_type_substitutions( binding.with(declared_receiver_type: declared_receiver_type.base), receiver_type.base, ) when Types::StructInstance return {} unless declared_receiver_type.definition.is_a?(Types::GenericStructDefinition) unless receiver_type.is_a?(Types::StructInstance) && receiver_type.definition == declared_receiver_type.definition raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end declared_receiver_type.definition.type_params.zip(receiver_type.arguments).to_h when Types::VariantInstance return {} unless declared_receiver_type.definition.is_a?(Types::GenericVariantDefinition) unless receiver_type.is_a?(Types::VariantInstance) && receiver_type.definition == declared_receiver_type.definition raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end declared_receiver_type.definition.type_params.zip(receiver_type.arguments).to_h when Types::GenericInstance unless receiver_type.is_a?(Types::GenericInstance) && receiver_type.name == declared_receiver_type.name && receiver_type.arguments.length == declared_receiver_type.arguments.length raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end declared_receiver_type.arguments.zip(receiver_type.arguments).each_with_object({}) do |(declared_argument, actual_argument), substitutions| if declared_argument.is_a?(Types::TypeVar) substitutions[declared_argument.name] = actual_argument elsif declared_argument != actual_argument raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end end when Types::Span return {} unless receiver_type.is_a?(Types::Span) substitutions = {} if declared_receiver_type.element_type.is_a?(Types::TypeVar) substitutions[declared_receiver_type.element_type.name] = receiver_type.element_type elsif declared_receiver_type.element_type != receiver_type.element_type raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end substitutions when Types::Task return {} unless receiver_type.is_a?(Types::Task) substitutions = {} if declared_receiver_type.result_type.is_a?(Types::TypeVar) substitutions[declared_receiver_type.result_type.name] = receiver_type.result_type elsif declared_receiver_type.result_type != receiver_type.result_type raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end substitutions when Types::SoA return {} unless receiver_type.is_a?(Types::SoA) substitutions = {} if declared_receiver_type.element_type.is_a?(Types::TypeVar) substitutions[declared_receiver_type.element_type.name] = receiver_type.element_type elsif declared_receiver_type.element_type != receiver_type.element_type raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end substitutions when Types::Simd return {} unless receiver_type.is_a?(Types::Simd) substitutions = {} if declared_receiver_type.element_type.is_a?(Types::TypeVar) substitutions[declared_receiver_type.element_type.name] = receiver_type.element_type elsif declared_receiver_type.element_type != receiver_type.element_type raise_sema_error("cannot use method #{binding.name} with receiver #{receiver_type}") end substitutions else {} end end |
#infer_recovered_match_expression(expression, scopes:, expected_type:) ⇒ Object
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 754 def infer_recovered_match_expression(expression, scopes:, expected_type:) arm_entries = expression.arms.map do |arm| arm_scopes = scopes if arm.binding_name ensure_non_reserved_primitive_name!(arm.binding_name, kind_label: "match binding", line: arm.binding_line, column: arm.binding_column) binding = value_binding( name: arm.binding_name, type: @error_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id), ) arm_scopes = scopes + [{ arm.binding_name => binding }] record_declaration_binding(arm, binding) end [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#infer_reference_value_type(handle_expression, scopes:) ⇒ Object
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1300 def infer_reference_value_type(handle_expression, scopes:) handle_type = infer_expression(handle_expression, scopes:) return referenced_type(handle_type) if ref_type?(handle_type) pointee = pointee_type(handle_type) if pointee require_unsafe!("raw pointer dereference requires unsafe") unless own_type?(handle_type) return pointee end raise_sema_error("read expects ref[...] or ptr[...], got #{handle_type}") end |
#infer_result_propagation(source_type, allow_void_success:) ⇒ Object
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 519 def infer_result_propagation(source_type, allow_void_success:) success_type = let_else_success_type(source_type) error_type = let_else_error_type(source_type) raise_sema_error("propagation requires a non-void Result success type") if success_type == @ctx.types.fetch("void") && !allow_void_success context = current_return_context raise_sema_error("propagation is only allowed inside function and proc bodies") unless context raise_sema_error("propagation is not allowed inside defer blocks") unless context[:allow_return] return_type = context[:return_type] unless result_let_else_type?(return_type) raise_sema_error("propagation requires enclosing function/proc to return Result[_, #{error_type}], got #{return_type}") end return_error_type = let_else_error_type(return_type) unless return_error_type == error_type raise_sema_error("propagation error type #{error_type} must match enclosing Result error type #{return_error_type}") end success_type end |
#infer_ro_addr_source_type(expression, scopes:) ⇒ Object
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1269 def infer_ro_addr_source_type(expression, scopes:) raise_sema_error("const_ptr_of requires a safe lvalue source") unless safe_reference_source_expression?(expression, scopes:) source_type = infer_expression(expression, scopes:) if contains_ref_type?(source_type) unless (source_type.is_a?(Types::Struct) || source_type.is_a?(Types::StructInstance)) && source_type.lifetime_params&.any? raise_sema_error("const_ptr_of cannot target ref values") end end source_type end |
#infer_simple_local_type(value, scopes) ⇒ Object
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 501 def infer_simple_local_type(value, scopes) case value when AST::Call infer_call_return_type(value, scopes) when AST::MemberAccess infer_member_access_field_type(value, scopes) when AST::Identifier (binding = lookup_value(value.name, scopes)) ? binding.type : nil when AST::IntegerLiteral @ctx.types["int"] when AST::FloatLiteral @ctx.types["float"] || @ctx.types["double"] when AST::CharLiteral @ctx.types["ubyte"] when AST::BooleanLiteral @ctx.types["bool"] when AST::StringLiteral @ctx.types["str"] when AST::PrefixCast if value.target_type.respond_to?(:name) @ctx.types[value.target_type.name] || @ctx.types["int"] else @ctx.types["int"] end end end |
#infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
791 792 793 794 795 796 797 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 791 def infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:) arm_entries = [] each_string_match_arm(expression, scrutinee_type, scopes:) do |arm| arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
799 800 801 802 803 804 805 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 799 def infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:) arm_entries = [] each_tuple_match_arm(expression, scrutinee_type, scopes:) do |arm| arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#infer_unary(expression, scopes:, expected_type: nil) ⇒ Object
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 473 def infer_unary(expression, scopes:, expected_type: nil) return infer_propagate_expression(expression.operand, scopes:) if expression.operator == "?" operand_type = infer_expression(expression.operand, scopes:, expected_type:) case expression.operator when "not" ensure_assignable!(operand_type, @ctx.types.fetch("bool"), "operator not requires bool, got #{operand_type}") @ctx.types.fetch("bool") when "+", "-" raise_sema_error("operator #{expression.operator} requires a numeric operand, got #{operand_type}") unless operand_type.numeric? if expression.operator == "-" && operand_type.integer? ct_value = evaluate_compile_time_const_value(expression, scopes:) if ct_value.is_a?(Integer) && !value_fits_integer_type?(ct_value, operand_type) raise_sema_error("negated value #{ct_value} does not fit in type #{operand_type}", expression) end end operand_type when "~" if simd_type?(operand_type) raise_sema_error("operator ~ on simd requires integer element type, got #{operand_type.element_type}") unless operand_type.element_type.integer? return operand_type end raise_sema_error("operator ~ requires an integer or flags operand, got #{operand_type}") unless bitwise_type?(operand_type) operand_type when "out", "in", "inout" raise_sema_error("#{expression.operator} is only allowed for foreign call arguments") else raise_sema_error("unsupported unary operator #{expression.operator}") end end |
#infer_unsafe_expression(expression, scopes:, expected_type: nil) ⇒ Object
255 256 257 258 259 260 261 262 263 264 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 255 def infer_unsafe_expression(expression, scopes:, expected_type: nil) @unsafe_statement_lines << expression.line begin with_unsafe do infer_expression(expression.expression, scopes:, expected_type:) end ensure @unsafe_statement_lines.pop end end |
#infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:) ⇒ Object
807 808 809 810 811 812 813 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 807 def infer_variant_match_expression(expression, scrutinee_type, scopes:, expected_type:) arm_entries = [] each_variant_match_arm(expression, scrutinee_type, scopes:) do |arm, arm_scopes| arm_entries << [infer_match_expression_arm_value(arm, scopes: arm_scopes, expected_type:), arm.value] end match_expression_common_type(arm_entries, expected_type) end |
#inline_for_element_type(element) ⇒ Object
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1307 def inline_for_element_type(element) if element.is_a?(Types::FieldHandle) builtin_field_handle_type elsif element.is_a?(Types::CallableHandle) builtin_callable_handle_type elsif element.is_a?(Types::AttributeHandle) builtin_attribute_handle_type elsif element.is_a?(Types::MemberHandle) builtin_member_handle_type elsif element.is_a?(Types::StructHandle) builtin_struct_handle_type else @ctx.types.fetch("int") end end |
#inline_foreign_call_requires_hoisting_message(foreign_call, scopes:) ⇒ Object
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1122 def (foreign_call, scopes:) binding = foreign_call[:binding] call = foreign_call[:call] reference_counts = foreign_mapping_reference_counts(foreign_mapping_expression(binding.ast)) binding.ast.params.each_with_index do |param_ast, index| public_alias = param_ast.boundary_type ? foreign_mapping_public_alias_name(param_ast.name) : nil total_references = reference_counts.fetch(param_ast.name, 0) total_references += reference_counts.fetch(public_alias, 0) if public_alias next if total_references <= 1 || simple_foreign_argument_expression?(call.arguments.fetch(index).value) return (binding.name, param_ast.name, reason: "is referenced multiple times in its mapping") end binding.ast.params.each_with_index do |param_ast, index| parameter = binding.type.params.fetch(index) argument_expression = call.arguments.fetch(index).value next unless automatic_foreign_cstr_temp_needed?(parameter, argument_expression, scopes:) || automatic_foreign_cstr_list_temp_needed?(parameter) return (binding.name, param_ast.name, reason: "needs temporary foreign text storage") end nil end |
#inline_foreign_hoisting_message(binding_name, parameter_name, reason:) ⇒ Object
1147 1148 1149 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1147 def (binding_name, parameter_name, reason:) "foreign call #{binding_name} cannot be used inline because #{parameter_name} #{reason}; use it as a statement, local initializer, assignment, or return expression" end |
#inside_async_function? ⇒ Boolean
86 87 88 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 86 def inside_async_function? @async_function_depth.positive? end |
#inside_loop? ⇒ Boolean
90 91 92 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 90 def inside_loop? @loop_depth.positive? end |
#install_builtin_attributes ⇒ Object
110 111 112 113 114 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 110 def install_builtin_attributes BUILTIN_ATTRIBUTE_NAMES.each do |name| @ctx.attributes[name] = MilkTea.builtin_attribute_binding(name, @ctx.types) end end |
#install_builtin_types ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 33 def install_builtin_types INSTALLABLE_BUILTIN_TYPE_NAMES.each do |name| @ctx.types[name] = case name when "str" Types::Registry.string_view when "Subscription" builtin_subscription_type when "EventError" builtin_event_error_type when "struct_handle" builtin_struct_handle_type when "field_handle" builtin_field_handle_type when "callable_handle" builtin_callable_handle_type when "attribute_handle" builtin_attribute_handle_type when "member_handle" builtin_member_handle_type when "type" when "vec2" Types::Vector.new("vec2", element_type: Types::BUILTIN_VECTOR_ELEMENT, width: 2) when "vec3" Types::Vector.new("vec3", element_type: Types::BUILTIN_VECTOR_ELEMENT, width: 3) when "vec4" Types::Vector.new("vec4", element_type: Types::BUILTIN_VECTOR_ELEMENT, width: 4) when "ivec2" Types::Vector.new("ivec2", element_type: Types::BUILTIN_IVECTOR_ELEMENT, width: 2) when "ivec3" Types::Vector.new("ivec3", element_type: Types::BUILTIN_IVECTOR_ELEMENT, width: 3) when "ivec4" Types::Vector.new("ivec4", element_type: Types::BUILTIN_IVECTOR_ELEMENT, width: 4) when "mat3" Types::Matrix.new("mat3", dim: 3) when "mat4" Types::Matrix.new("mat4", dim: 4) when "quat" Types::Quaternion.new("quat") else Types::Registry.primitive(name) end end end |
#install_fallback_builtin_type(module_path) ⇒ Object
193 194 195 196 197 198 199 200 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 193 def install_fallback_builtin_type(module_path) case module_path when "std.option" @ctx.types["Option"] = Types::BUILTIN_OPTION_TYPE unless @ctx.types.key?("Option") when "std.result" @ctx.types["Result"] = Types::BUILTIN_RESULT_TYPE unless @ctx.types.key?("Result") end end |
#install_imports ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 142 def install_imports @ctx.ast.imports.each do |import| with_error_node(import) do alias_name = import.alias_name || import.path.parts.last ensure_non_reserved_import_alias_name!(alias_name, kind_label: "import alias", line: import.line, column: import.column) raise_sema_error("duplicate import alias #{alias_name}") if @ctx.imports.key?(alias_name) module_binding = @ctx.imported_modules[import.path.to_s] next if @allow_missing_imports && module_binding.nil? raise_sema_error("unknown import #{import.path}") unless module_binding @ctx.imports[alias_name] = module_binding end end end |
#install_prelude_types ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 160 def install_prelude_types PRELUDE_MODULE_PATHS.each do |module_path| next if module_path == @ctx.module_name module_binding = @ctx.imported_modules[module_path] unless module_binding install_fallback_builtin_type(module_path) @ctx.imports[module_path] = empty_module_binding(module_path) unless @ctx.imports.key?(module_path) next end module_binding.types.each do |type_name, type| @ctx.types[type_name] = type unless @ctx.types.key?(type_name) end already_imported = @ctx.imports.any? { |_, existing_binding| existing_binding == module_binding } @ctx.imports[module_path] = module_binding unless @ctx.imports.key?(module_path) || already_imported end end |
#instantiate_function_binding(binding, type_arguments) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 204 def instantiate_function_binding(binding, type_arguments) if binding.type_params.empty? raise_sema_error("function #{binding.name} is not generic and cannot be specialized") end unless binding.type_params.length == type_arguments.length raise_sema_error("function #{binding.name} expects #{binding.type_params.length} type arguments, got #{type_arguments.length}") end if type_arguments.any? { |type_argument| contains_ref_type?(type_argument) } raise_sema_error("generic function #{binding.name} cannot be instantiated with ref types") end key = type_arguments.freeze return binding.instances.fetch(key) if binding.instances.key?(key) substitutions = binding.type_params.zip(type_arguments).to_h validate_function_type_param_constraints!(binding, substitutions) type = substitute_type(binding.type, substitutions) body_params = binding.body_params.map { |param| substitute_value_binding(param, substitutions) } validate_specialized_function_binding!(binding.name, type, body_params) instance = FunctionBinding.new( name: binding.name, type:, body_params:, body_return_type: substitute_type(binding.body_return_type, substitutions), ast: binding.ast, external: binding.external, async: binding.async, type_params: [].freeze, type_param_constraints: {}.freeze, instances: {}, type_arguments: key, owner: binding.owner, specialization_owner: @current_specialization_owner || (binding.owner == self ? nil : self), type_substitutions: substitutions.freeze, declared_receiver_type: binding.declared_receiver_type ? substitute_type(binding.declared_receiver_type, substitutions) : nil, ) binding.instances[key] = instance end |
#instantiate_function_binding_with_receiver(binding, explicit_type_arguments, receiver_type: nil) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 176 def instantiate_function_binding_with_receiver(binding, explicit_type_arguments, receiver_type: nil) if binding.type_params.empty? raise_sema_error("function #{binding.name} is not generic and cannot be specialized") end receiver_substitutions = infer_receiver_type_substitutions(binding, receiver_type) remaining_type_params = binding.type_params.reject { |name| receiver_substitutions.key?(name) } unless remaining_type_params.length == explicit_type_arguments.length raise_sema_error("function #{binding.name} expects #{remaining_type_params.length} type arguments, got #{explicit_type_arguments.length}") end substitutions = receiver_substitutions.dup remaining_type_params.zip(explicit_type_arguments).each do |name, type_argument| raise_sema_error("generic function #{binding.name} cannot be instantiated with ref types") if contains_ref_type?(type_argument) substitutions[name] = type_argument end type_arguments = binding.type_params.map do |name| inferred = substitutions[name] raise_sema_error("cannot infer type argument #{name} for function #{binding.name}") unless inferred inferred end instantiate_function_binding(binding, type_arguments) end |
#integer_constant_fits_in_char?(expression, scopes) ⇒ Boolean
143 144 145 146 147 148 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 143 def integer_constant_fits_in_char?(expression, scopes) value = evaluate_compile_time_const_value(expression, scopes:) return true unless value.is_a?(Integer) value >= 0 && value <= 255 end |
#integer_literal_expression?(expression) ⇒ Boolean
909 910 911 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 909 def integer_literal_expression?(expression) expression.is_a?(AST::IntegerLiteral) end |
#integer_suffix_type(lexeme) ⇒ Object
292 293 294 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 292 def integer_suffix_type(lexeme) INTEGER_SUFFIX_TYPES.sort_by { |k, _| -k.length }.find { |suffix, _| lexeme.end_with?(suffix) }&.last end |
#integer_type?(type) ⇒ Boolean
839 840 841 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 839 def integer_type?(type) type.is_a?(Types::Primitive) && type.integer? end |
#integer_type_argument?(argument) ⇒ Boolean
786 787 788 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 786 def integer_type_argument?(argument) argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer) end |
#interface_implementation_key(type) ⇒ Object
158 159 160 161 162 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 158 def interface_implementation_key(type) return type.definition if type.is_a?(Types::StructInstance) type end |
#iterator_loop_type(type) ⇒ Object
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 876 def iterator_loop_type(type) type = referenced_type(type) if ref_type?(type) iter_method = lookup_method(type, "iter") return nil unless iter_method iter_method = instantiate_function_binding_with_receiver(iter_method, [], receiver_type: type) if iter_method.type_params.any? unless iter_method.type.params.empty? raise_sema_error("for iterator #{type}.iter expects 0 arguments") end if iter_method.type.receiver_editable raise_sema_error("for iterator #{type}.iter cannot be an editable method") end iterator_type = iter_method.type.return_type next_method = lookup_method(iterator_type, "next") raise_sema_error("for iterator #{iterator_type} must define next()") unless next_method next_method = instantiate_function_binding_with_receiver(next_method, [], receiver_type: iterator_type) if next_method.type_params.any? unless next_method.type.params.empty? raise_sema_error("for iterator #{iterator_type}.next expects 0 arguments") end next_type = next_method.type.return_type if next_type.is_a?(Types::Nullable) && typed_null_target_type?(next_type.base) return next_type.base end if next_type == @ctx.types.fetch("bool") current_method = lookup_method(iterator_type, "current") raise_sema_error("for iterator #{iterator_type} must define current() when next() returns bool") unless current_method current_method = instantiate_function_binding_with_receiver(current_method, [], receiver_type: iterator_type) if current_method.type_params.any? unless current_method.type.params.empty? raise_sema_error("for iterator #{iterator_type}.current expects 0 arguments") end raise_sema_error("for iterator #{iterator_type}.current cannot return void") if current_method.type.return_type == @ctx.types.fetch("void") return current_method.type.return_type end raise_sema_error("for iterator #{iterator_type}.next must return bool or a nullable pointer-like item, got #{next_type}") end |
#last_ast_line(statements) ⇒ Object
Returns the highest line number across a list of AST statement nodes, recursing into nested bodies (if/else/match/while/for/defer/unsafe).
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 367 def last_ast_line(statements) return nil if statements.nil? || statements.empty? statements.filter_map do |stmt| case stmt when AST::IfStmt branch_lines = stmt.branches.filter_map { |b| last_ast_line(b.body) } else_lines = last_ast_line(stmt.else_body) [stmt.line, *branch_lines, else_lines].compact.max when AST::WhileStmt, AST::ForStmt, AST::UnsafeStmt, AST::ErrorBlockStmt [stmt.line, last_ast_line(stmt.body)].compact.max when AST::MatchStmt arm_lines = stmt.arms.filter_map do |arm| if arm.respond_to?(:body) last_ast_line(arm.body) elsif arm.respond_to?(:value) arm.value.line end end [stmt.line, *arm_lines].compact.max when AST::DeferStmt [stmt.line, last_ast_line(stmt.body)].compact.max when AST::WhenStmt branch_lines = stmt.branches.filter_map { |b| last_ast_line(b.body) } else_lines = last_ast_line(stmt.else_body) [stmt.line, *branch_lines, else_lines].compact.max else stmt.line end end.compact.max end |
#layout_aggregate_type?(type) ⇒ Boolean
693 694 695 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 693 def layout_aggregate_type?(type) type.respond_to?(:field) && !type.is_a?(Types::Opaque) && !type.is_a?(Types::EnumBase) end |
#let_else_discard_binding_syntax?(statement) ⇒ Boolean
777 778 779 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 777 def let_else_discard_binding_syntax?(statement) statement.is_a?(AST::LocalDecl) && (statement.else_body || statement.recovered_else) && statement.name == "_" end |
#let_else_error_type(type) ⇒ Object
794 795 796 797 798 799 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 794 def let_else_error_type(type) return @error_type if error_type?(type) return unless result_let_else_type?(type) type.arm("failure").fetch("error") end |
#let_else_source_type?(type) ⇒ Boolean
781 782 783 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 781 def let_else_source_type?(type) type.is_a?(Types::Nullable) || result_let_else_type?(type) || option_let_else_type?(type) end |
#let_else_success_type(type) ⇒ Object
785 786 787 788 789 790 791 792 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 785 def let_else_success_type(type) return @error_type if error_type?(type) return type.base if type.is_a?(Types::Nullable) return type.arm("some").fetch("value") if option_let_else_type?(type) return unless result_let_else_type?(type) type.arm("success").fetch("value") end |
#levenshtein(s, t) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 293 def levenshtein(s, t) m = s.length n = t.length return m if n.zero? return n if m.zero? d = Array.new(m + 1) { Array.new(n + 1, 0) } (0..m).each { |i| d[i][0] = i } (0..n).each { |j| d[0][j] = j } (1..m).each do |i| (1..n).each do |j| cost = s[i - 1] == t[j - 1] ? 0 : 1 d[i][j] = [d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost].min end end d[m][n] end |
#literal_type_argument_name_candidate?(type_ref) ⇒ Boolean
397 398 399 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 397 def literal_type_argument_name_candidate?(type_ref) type_ref.arguments.empty? && !type_ref.nullable end |
#lookup_local_binding(name, scopes) ⇒ Object
1415 1416 1417 1418 1419 1420 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1415 def lookup_local_binding(name, scopes) scopes.reverse_each do |scope| return scope[name] if scope.key?(name) end nil end |
#lookup_local_method_for_interface(receiver_type, name) ⇒ Object
39 40 41 42 43 44 45 46 47 48 |
# File 'lib/milk_tea/core/semantic_analyzer/interface_conformance.rb', line 39 def lookup_local_method_for_interface(receiver_type, name) dispatch_receiver_type = method_dispatch_receiver_type(receiver_type) method = @ctx.methods.fetch(receiver_type, {})[name] method ||= @ctx.methods.fetch(dispatch_receiver_type, {})[name] unless dispatch_receiver_type == receiver_type static_name = "static:#{name}" method ||= @ctx.methods.fetch(receiver_type, {})[static_name] method ||= @ctx.methods.fetch(dispatch_receiver_type, {})[static_name] unless dispatch_receiver_type == receiver_type method end |
#lookup_method(receiver_type, name) ⇒ Object
22 23 24 25 26 27 28 29 30 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 22 def lookup_method(receiver_type, name) method = lookup_method_local_or_imported(receiver_type, name) return method if method fallback_owner = specialization_lookup_owner return nil unless fallback_owner fallback_owner.lookup_method_local_or_imported(receiver_type, name) end |
#lookup_method_local_or_imported(receiver_type, name) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 43 def lookup_method_local_or_imported(receiver_type, name) dispatch_receiver_type = method_dispatch_receiver_type(receiver_type) method = @ctx.methods.fetch(receiver_type, {})[name] method ||= @ctx.methods.fetch(dispatch_receiver_type, {})[name] unless dispatch_receiver_type == receiver_type return method if method imported_candidates = [] @ctx.imports.each_value do |module_binding| imported_method = module_binding.methods.fetch(receiver_type, {})[name] if imported_method.nil? && dispatch_receiver_type != receiver_type imported_method = module_binding.methods.fetch(dispatch_receiver_type, {})[name] end imported_candidates << [module_binding, imported_method] if imported_method end if imported_candidates.empty? owner_module = reachable_module_binding_for_type(receiver_type) return nil unless owner_module return module_binding_method(owner_module, receiver_type, dispatch_receiver_type, name) end if imported_candidates.length > 1 modules = imported_candidates.map { |module_binding, _binding| module_binding.name }.join(", ") display_name = name.delete_prefix("static:") raise_sema_error("ambiguous imported method #{receiver_type}.#{display_name}; found in modules #{modules}") end imported_candidates.first.last end |
#lookup_static_method(receiver_type, name) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 32 def lookup_static_method(receiver_type, name) static_name = "static:#{name}" method = lookup_method_local_or_imported(receiver_type, static_name) return method if method fallback_owner = specialization_lookup_owner return nil unless fallback_owner fallback_owner.lookup_method_local_or_imported(receiver_type, static_name) end |
#lookup_value(name, scopes) ⇒ Object
14 15 16 17 18 19 20 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 14 def lookup_value(name, scopes) scopes.reverse_each do |scope| return scope[name] if scope.key?(name) end @ctx.top_level_values[name] end |
#mark_current_unsafe_required! ⇒ Object
13 14 15 16 17 18 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 13 def mark_current_unsafe_required! current_line = @unsafe_statement_lines.last return unless current_line @required_unsafe_lines << current_line end |
#match_expression_common_type(arm_entries, expected_type) ⇒ Object
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 821 def match_expression_common_type(arm_entries, expected_type) return expected_type || @error_type if arm_entries.empty? if expected_type && arm_entries.all? { |type, expr| types_compatible?(type, expected_type, expression: expr) } return expected_type end common_type, common_expression = arm_entries.first arm_entries.drop(1).each do |type, expr| next if type == common_type merged_type = conditional_common_type( common_type, type, then_expression: common_expression, else_expression: expr, ) raise_sema_error("match expression arms require compatible types, got #{common_type} and #{type}") unless merged_type common_type = merged_type common_expression = expr end common_type end |
#match_member_name(expression, enum_type) ⇒ Object
965 966 967 968 969 970 971 972 973 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 965 def match_member_name(expression, enum_type) return unless expression.is_a?(AST::MemberAccess) receiver_type = resolve_type_expression(expression.receiver) return unless receiver_type == enum_type return expression.member if enum_type.member(expression.member) nil end |
#matrix_op_result(operator, left_type, right_type) ⇒ Object
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 277 def matrix_op_result(operator, left_type, right_type) if matrix_type?(left_type) && matrix_type?(right_type) && left_type.dim == right_type.dim return left_type if operator == "+" || operator == "-" return left_type if operator == "*" end if matrix_type?(left_type) && vector_type?(right_type) && left_type.dim == right_type.width && right_type.element_type == Types::BUILTIN_VECTOR_ELEMENT return Types::Vector.new(right_type.name, element_type: right_type.element_type, width: right_type.width) if operator == "*" end if matrix_type?(left_type) && right_type.numeric? return left_type if operator == "*" || operator == "/" end nil end |
#matrix_type?(type) ⇒ Boolean
709 710 711 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 709 def matrix_type?(type) type.is_a?(Types::Matrix) end |
#merge_equality_cover(existing, new_cover) ⇒ Object
904 905 906 907 908 909 910 911 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 904 def merge_equality_cover(existing, new_cover) return new_cover unless existing merged = existing.dup new_cover.each do |field, members| merged[field] = (merged[field] || []) + members end merged end |
#merge_refinements(existing, incoming) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 55 def merge_refinements(existing, incoming) merged = existing.dup incoming.each do |name, refined_type| if merged.key?(name) && merged[name] != refined_type merged.delete(name) else merged[name] = refined_type end end merged end |
#merged_scope_bindings(scopes) ⇒ Object
161 162 163 164 165 166 167 168 169 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 161 def merged_scope_bindings(scopes) scopes.each_with_object({}) do |scope, bindings| scope.each do |name, binding| next unless name.is_a?(String) bindings[name] = binding end end end |
#methods_receiver_type_argument_names!(type_ref) ⇒ Object
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1061 def methods_receiver_type_argument_names!(type_ref) names = type_ref.arguments.map do |argument| value = argument.value next unless value.is_a?(AST::TypeRef) next unless value.arguments.empty? && !value.nullable && value.name.parts.length == 1 value.name.parts.first end raise_sema_error("extending target #{type_ref} must use the receiver type parameters directly") if names.any?(&:nil?) names end |
#module_binding_method(module_binding, receiver_type, dispatch_receiver_type, name) ⇒ Object
77 78 79 80 81 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 77 def module_binding_method(module_binding, receiver_type, dispatch_receiver_type, name) method = module_binding.methods.fetch(receiver_type, {})[name] method ||= module_binding.methods.fetch(dispatch_receiver_type, {})[name] unless dispatch_receiver_type == receiver_type method end |
#module_name ⇒ Object
136 137 138 |
# File 'lib/milk_tea/core/semantic_analyzer.rb', line 136 def module_name @ctx.module_name end |
#noncopyable_event_storage_type?(type) ⇒ Boolean
778 779 780 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 778 def noncopyable_event_storage_type?(type) event_type?(type) || event_carrier_type?(type) end |
#null_test_refinements(expression, truthy:, scopes:) ⇒ Object
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 289 def null_test_refinements(expression, truthy:, scopes:) identifier_expression = nil if expression.left.is_a?(AST::Identifier) && expression.right.is_a?(AST::NullLiteral) identifier_expression = expression.left elsif expression.left.is_a?(AST::NullLiteral) && expression.right.is_a?(AST::Identifier) identifier_expression = expression.right else return {} end binding = lookup_value(identifier_expression.name, scopes) return {} unless binding&.storage_type.is_a?(Types::Nullable) null_result = expression.operator == "==" ? truthy : !truthy refined_type = null_result ? @null_type : binding.storage_type.base { identifier_expression.name => refined_type } end |
#nullable_candidate?(type) ⇒ Boolean
327 328 329 330 331 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 327 def nullable_candidate?(type) return false if ref_type?(type) sized_layout_type?(type) || pointer_type?(type) || type.is_a?(Types::Nullable) end |
#option_let_else_type?(type) ⇒ Boolean
801 802 803 804 805 806 807 808 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 801 def option_let_else_type?(type) return false unless type.is_a?(Types::Variant) some_fields = type.arm("some") none_fields = type.arm("none") some_fields && some_fields.length == 1 && some_fields.key?("value") && none_fields && none_fields.empty? end |
#pointer_arithmetic_result(operator, left_type, right_type) ⇒ Object
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 199 def pointer_arithmetic_result(operator, left_type, right_type) if pointer_type?(left_type) && integer_type?(right_type) require_unsafe!("pointer arithmetic requires unsafe") unless own_type?(left_type) return left_type if operator == "+" || operator == "-" end if operator == "+" && integer_type?(left_type) && pointer_type?(right_type) require_unsafe!("pointer arithmetic requires unsafe") unless own_type?(right_type) return right_type end nil end |
#pointer_cast?(source_type, target_type) ⇒ Boolean
307 308 309 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 307 def pointer_cast?(source_type, target_type) pointer_cast_type?(source_type) && pointer_cast_type?(target_type) end |
#pointer_cast_type?(type) ⇒ Boolean
315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 315 def pointer_cast_type?(type) return typed_null_target_type?(type.target_type) if type.is_a?(Types::Null) return true if type == @ctx.types.fetch("cstr") if type.is_a?(Types::Nullable) return true if function_pointer_type?(type.base) return pointer_type?(type.base) end return true if function_pointer_type?(type) pointer_type?(type) end |
#pointer_to(type) ⇒ Object
794 795 796 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 794 def pointer_to(type) Types.pointer_to(type) end |
#power_of_two?(value) ⇒ Boolean
697 698 699 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 697 def power_of_two?(value) (value & (value - 1)).zero? end |
#preassign_local_binding_ids(statements) ⇒ Object
96 97 98 99 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 96 def preassign_local_binding_ids(statements) @preassigned_local_binding_ids = {} preassign_local_binding_ids_in_statements(statements || []) end |
#preassign_local_binding_ids_in_expression(expression) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 170 def preassign_local_binding_ids_in_expression(expression) case expression when nil, AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral, AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr, AST::ErrorExpr nil when AST::MemberAccess preassign_local_binding_ids_in_expression(expression.receiver) when AST::IndexAccess preassign_local_binding_ids_in_expression(expression.receiver) preassign_local_binding_ids_in_expression(expression.index) when AST::Specialization, AST::Call preassign_local_binding_ids_in_expression(expression.callee) expression.arguments.each { |argument| preassign_local_binding_ids_in_expression(argument.value) } when AST::UnaryOp preassign_local_binding_ids_in_expression(expression.operand) when AST::BinaryOp preassign_local_binding_ids_in_expression(expression.left) preassign_local_binding_ids_in_expression(expression.right) when AST::RangeExpr preassign_local_binding_ids_in_expression(expression.start_expr) preassign_local_binding_ids_in_expression(expression.end_expr) when AST::IfExpr preassign_local_binding_ids_in_expression(expression.condition) preassign_local_binding_ids_in_expression(expression.then_expression) preassign_local_binding_ids_in_expression(expression.else_expression) when AST::MatchExpr preassign_local_binding_ids_in_expression(expression.expression) expression.arms.each do |arm| preassign_local_binding_ids_in_expression(arm.pattern) @preassigned_local_binding_ids[arm.object_id] ||= allocate_binding_id if arm.binding_name preassign_local_binding_ids_in_expression(if arm.respond_to?(:value) then arm.value else arm.body end) end when AST::UnsafeExpr preassign_local_binding_ids_in_expression(expression.expression) when AST::PrefixCast preassign_local_binding_ids_in_expression(expression.expression) when AST::AwaitExpr preassign_local_binding_ids_in_expression(expression.expression) when AST::FormatString expression.parts.each do |part| preassign_local_binding_ids_in_expression(part.expression) if part.is_a?(AST::FormatExprPart) end when AST::ProcExpr preassign_local_binding_ids_in_statements(expression.body) end end |
#preassign_local_binding_ids_in_statements(statements) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 101 def preassign_local_binding_ids_in_statements(statements) statements.each do |statement| case statement when AST::ErrorBlockStmt preassign_local_binding_ids_in_expression(statement.header_expression) if statement.header_expression Array(statement.header_iterables).each { |iterable| preassign_local_binding_ids_in_expression(iterable) } Array(statement.header_bindings).each do |binding| @preassigned_local_binding_ids[binding.object_id] ||= allocate_binding_id end if statement.header_type == :for preassign_local_binding_ids_in_statements(statement.body || []) when AST::LocalDecl @preassigned_local_binding_ids[statement.object_id] ||= allocate_binding_id preassign_local_binding_ids_in_expression(statement.value) if statement.value if statement.else_binding && (statement.else_body || statement.recovered_else) @preassigned_local_binding_ids[statement.else_binding.object_id] ||= allocate_binding_id end preassign_local_binding_ids_in_statements(statement.else_body || []) when AST::Assignment preassign_local_binding_ids_in_expression(statement.target) preassign_local_binding_ids_in_expression(statement.value) when AST::IfStmt statement.branches.each do |branch| preassign_local_binding_ids_in_expression(branch.condition) preassign_local_binding_ids_in_statements(branch.body || []) end preassign_local_binding_ids_in_statements(statement.else_body || []) when AST::MatchStmt preassign_local_binding_ids_in_expression(statement.expression) statement.arms.each do |arm| preassign_local_binding_ids_in_expression(arm.pattern) @preassigned_local_binding_ids[arm.object_id] ||= allocate_binding_id if arm.binding_name # Preassign binding IDs for struct pattern bindings if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty? arm.pattern.arguments.each do |arg| next if arg.name next unless arg.value.is_a?(AST::Identifier) @preassigned_local_binding_ids[arg.object_id] ||= allocate_binding_id end end preassign_local_binding_ids_in_statements(if arm.respond_to?(:body) then (arm.body || []) else [arm.value].compact end) end when AST::UnsafeStmt preassign_local_binding_ids_in_statements(statement.body || []) when AST::WhileStmt preassign_local_binding_ids_in_expression(statement.condition) preassign_local_binding_ids_in_statements(statement.body || []) when AST::ForStmt statement.bindings.each do |binding| @preassigned_local_binding_ids[binding.object_id] ||= allocate_binding_id end statement.iterables.each { |iterable| preassign_local_binding_ids_in_expression(iterable) } preassign_local_binding_ids_in_statements(statement.body || []) when AST::DeferStmt preassign_local_binding_ids_in_expression(statement.expression) if statement.expression preassign_local_binding_ids_in_statements(statement.body || []) if statement.body when AST::ReturnStmt preassign_local_binding_ids_in_expression(statement.value) if statement.value when AST::StaticAssert preassign_local_binding_ids_in_expression(statement.condition) preassign_local_binding_ids_in_expression(statement.) when AST::ExpressionStmt preassign_local_binding_ids_in_expression(statement.expression) end end end |
#preassigned_local_binding_id_for(node) ⇒ Object
1215 1216 1217 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1215 def preassigned_local_binding_id_for(node) @preassigned_local_binding_ids[node.object_id] ||= allocate_binding_id end |
#precheck_binding_resolution(statements, scopes) ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 218 def precheck_binding_resolution(statements, scopes) declaration_binding_ids = {} identifier_binding_ids = {} initial_scope = {} scopes.each do |scope| scope.each do |name, binding| initial_scope[name] = binding.id if binding.respond_to?(:id) && binding.id end end walk_statements_for_precheck_resolution( statements || [], [initial_scope], declaration_binding_ids, identifier_binding_ids, ) BindingResolution.new( identifier_binding_ids: identifier_binding_ids, declaration_binding_ids: declaration_binding_ids, mutating_argument_identifier_ids: {}, editable_receiver_expression_ids: {}, mutable_lvalue_argument_identifier_ids: {}, binding_types: {}, ) end |
#proc_storage_supported_type?(type, visited = {}) ⇒ Boolean
1153 1154 1155 1156 1157 1158 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1153 def proc_storage_supported_type?(type, visited = {}) return true unless contains_proc_type?(type) visitor = ProcStorageSupportedVisitor.new visitor.visit(type) visitor.result? end |
#proc_type_compatible?(actual_type, expected_type) ⇒ Boolean
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1092 def proc_type_compatible?(actual_type, expected_type) return true unless expected_type if proc_type?(expected_type) return true if expected_type.is_a?(Types::Proc) && actual_type.is_a?(Types::Proc) && contains_type_var?(expected_type) return actual_type == expected_type end false end |
#project_field_receiver_type(receiver_type, require_mutable_pointer: false) ⇒ Object
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1324 def project_field_receiver_type(receiver_type, require_mutable_pointer: false) return referenced_type(receiver_type) if ref_type?(receiver_type) return receiver_type unless pointer_type?(receiver_type) require_unsafe!("raw pointer dereference requires unsafe") unless own_type?(receiver_type) if require_mutable_pointer && const_pointer_type?(receiver_type) raise_sema_error("cannot assign through read-only raw pointer #{receiver_type}") end pointee_type(receiver_type) end |
#project_method_receiver_type(receiver_type, member_name: nil) ⇒ Object
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1336 def project_method_receiver_type(receiver_type, member_name: nil) receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type) return receiver_type unless pointer_type?(receiver_type) return receiver_type if member_name && lookup_method(receiver_type, member_name) require_unsafe!("raw pointer dereference requires unsafe") unless own_type?(receiver_type) pointee_type(receiver_type) end |
#propagating_expected_type(operator, expected_type) ⇒ Object
913 914 915 916 917 918 919 920 921 922 923 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 913 def propagating_expected_type(operator, expected_type) case operator when "+", "-", "*", "/", "%", "<<", ">>" return expected_type if expected_type.is_a?(Types::Primitive) && expected_type.numeric? when "|", "&", "^" return expected_type if expected_type.is_a?(Types::Primitive) && expected_type.integer? return expected_type if expected_type.is_a?(Types::Flags) end nil end |
#qualified_attribute_name(binding) ⇒ Object
35 36 37 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 35 def qualified_attribute_name(binding) binding.module_name ? "#{binding.module_name}.#{binding.name}" : binding.name end |
#quat_vec4_compatible?(a, b) ⇒ Boolean
60 61 62 63 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 60 def quat_vec4_compatible?(a, b) (a.is_a?(Types::Quaternion) && b.is_a?(Types::Vector) && b.width == 4 && b.element_type.name == "float") || (b.is_a?(Types::Quaternion) && a.is_a?(Types::Vector) && a.width == 4 && a.element_type.name == "float") end |
#quaternion_op_result(operator, left_type, right_type) ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 294 def quaternion_op_result(operator, left_type, right_type) if quaternion_type?(left_type) && quaternion_type?(right_type) return left_type if operator == "+" || operator == "-" return left_type if operator == "*" end if quaternion_type?(left_type) && vector_type?(right_type) && right_type.width == 3 && right_type.element_type == Types::BUILTIN_VECTOR_ELEMENT return right_type if operator == "*" end nil end |
#quaternion_type?(type) ⇒ Boolean
713 714 715 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 713 def quaternion_type?(type) type.is_a?(Types::Quaternion) end |
#raise_sema_error(message, node = nil, line: nil, column: nil, length: nil, suggestion: nil) ⇒ Object
503 504 505 506 507 508 509 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 503 def raise_sema_error(, node = nil, line: nil, column: nil, length: nil, suggestion: nil) target = node || current_error_node line ||= source_line(target) column ||= source_column(target) length ||= source_length(target) raise SemanticError.new(, line:, column:, length:, path: @path, suggestion:) end |
#raw_module? ⇒ Boolean
1351 1352 1353 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1351 def raw_module? @ctx.module_kind == :raw_module end |
#reachable_module_binding_for_type(receiver_type) ⇒ Object
83 84 85 86 87 88 89 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 83 def reachable_module_binding_for_type(receiver_type) module_name = receiver_type_module_name(receiver_type) return nil unless module_name return nil if module_name == @ctx.module_name find_reachable_imported_module(module_name) end |
#read_call?(expression) ⇒ Boolean
1347 1348 1349 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1347 def read_call?(expression) expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "read" end |
#receiver_type_for_fields(type) ⇒ Object
591 592 593 594 595 596 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 591 def receiver_type_for_fields(type) type = type.base while type.is_a?(Types::Nullable) type = type.arguments.first if type.respond_to?(:name) && type.name == 'ref' && type.respond_to?(:arguments) && type.arguments&.length == 1 type = type.definition if type.respond_to?(:definition) type end |
#receiver_type_module_name(receiver_type) ⇒ Object
91 92 93 94 95 96 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 91 def receiver_type_module_name(receiver_type) return receiver_type_module_name(receiver_type.base) if receiver_type.is_a?(Types::Nullable) return receiver_type.module_name if receiver_type.respond_to?(:module_name) nil end |
#record_callable_value_expression_site(expression) ⇒ Object
1400 1401 1402 1403 1404 1405 1406 1407 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1400 def record_callable_value_expression_site(expression) case expression when AST::Identifier record_callable_value_identifier_site(expression) when AST::MemberAccess record_callable_value_member_access_site(expression) end end |
#record_callable_value_identifier_site(expression) ⇒ Object
1393 1394 1395 1396 1397 1398 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1393 def record_callable_value_identifier_site(expression) return unless expression.is_a?(AST::Identifier) return unless expression.line && expression.column @callable_value_identifier_sites[[expression.line, expression.column]] = true end |
#record_callable_value_member_access_site(expression) ⇒ Object
1409 1410 1411 1412 1413 1414 1415 1416 1417 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1409 def record_callable_value_member_access_site(expression) return unless expression.is_a?(AST::MemberAccess) return unless expression.receiver.is_a?(AST::Identifier) return unless expression.receiver.line && expression.receiver.column @callable_value_member_access_sites[ [expression.receiver.name, expression.receiver.line, expression.receiver.column, expression.member] ] = true end |
#record_declaration_binding(node, binding) ⇒ Object
1419 1420 1421 1422 1423 1424 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1419 def record_declaration_binding(node, binding) return unless node return unless binding&.id @declaration_binding_ids[node.object_id] = binding.id end |
#record_editable_receiver_expression(receiver) ⇒ Object
1473 1474 1475 1476 1477 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1473 def record_editable_receiver_expression(receiver) return unless receiver @editable_receiver_expression_ids[receiver.object_id] = true end |
#record_identifier_binding(expression, binding) ⇒ Object
1386 1387 1388 1389 1390 1391 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1386 def record_identifier_binding(expression, binding) return unless expression.is_a?(AST::Identifier) return unless binding&.id @identifier_binding_ids[expression.object_id] = binding.id end |
#record_local_completion_snapshot(line, column, scopes) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 142 def record_local_completion_snapshot(line, column, scopes) return if @active_local_completion_stack.empty? return if line.nil? snapshot = LocalCompletionSnapshot.new( line:, column: (column || 0), bindings: merged_scope_bindings(scopes).freeze, ) snapshots = @active_local_completion_stack.last[:snapshots] prev = snapshots.last if prev && prev.line == snapshot.line && prev.column == snapshot.column snapshots[-1] = snapshot else snapshots << snapshot end end |
#record_mutable_lvalue_argument_identifier(expression) ⇒ Object
1479 1480 1481 1482 1483 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1479 def record_mutable_lvalue_argument_identifier(expression) return unless expression.is_a?(AST::Identifier) @mutable_lvalue_argument_identifier_ids[expression.object_id] = true end |
#record_mutating_argument_identifier(argument, parameter) ⇒ Object
817 818 819 820 821 822 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 817 def record_mutating_argument_identifier(argument, parameter) return unless %i[out inout].include?(parameter.passing_mode) return unless argument.value.is_a?(AST::Identifier) @mutating_argument_identifier_ids[argument.value.object_id] = true end |
#recovered_for_statement(statement) ⇒ Object
279 280 281 282 283 284 285 286 287 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 279 def recovered_for_statement(statement) AST::ForStmt.new( bindings: Array(statement.header_bindings), iterables: Array(statement.header_iterables), body: statement.body, line: statement.line, column: statement.column, ) end |
#ref_to_pointer_cast?(source_type, target_type) ⇒ Boolean
311 312 313 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 311 def ref_to_pointer_cast?(source_type, target_type) ref_type?(source_type) && pointer_cast_type?(target_type) end |
#ref_types_compatible?(actual_type, expected_type) ⇒ Boolean
54 55 56 57 58 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 54 def ref_types_compatible?(actual_type, expected_type) return false unless ref_type?(actual_type) && ref_type?(expected_type) referenced_type(actual_type) == referenced_type(expected_type) end |
#reflection_identifier_name(expression, context:) ⇒ Object
1596 1597 1598 1599 1600 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1596 def reflection_identifier_name(expression, context:) raise_sema_error("#{context} expects an identifier argument") unless expression.is_a?(AST::Identifier) expression.name end |
#register_nested_struct_types(parent_decl, parent_name: parent_decl.name) ⇒ Object
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 848 def register_nested_struct_types(parent_decl, parent_name: parent_decl.name) parent_decl.nested_types.each do |nested| qualified_name = "#{parent_name}.#{nested.name}" ensure_available_type_name!(qualified_name) nested_c_name = if @ctx.module_name && !raw_module? "#{@ctx.module_name.to_s.tr('.', '_')}_#{qualified_name.tr('.', '_')}" else qualified_name.tr('.', '_') end if nested.type_params.empty? @ctx.types[qualified_name] = Types::Struct.new(nested.name, module_name: @ctx.module_name, external: raw_module?, packed: nested.packed, alignment: nested.alignment, linkage_name: nested_c_name, lifetime_params: nested.lifetime_params) else @ctx.types[qualified_name] = Types::GenericStructDefinition.new(nested.name, nested.type_params.map(&:name), module_name: @ctx.module_name, external: raw_module?, packed: nested.packed, alignment: nested.alignment, linkage_name: nested_c_name, lifetime_params: nested.lifetime_params) end register_nested_struct_types(nested, parent_name: qualified_name) end end |
#reinterpretable_type?(type) ⇒ Boolean
661 662 663 664 665 666 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 661 def reinterpretable_type?(type) return false if array_type?(type) return false if type.is_a?(Types::Primitive) && type.void? sized_layout_type?(type) end |
#reject_foreign_nullable_value_type!(type, function_name:, parameter_name:) ⇒ Object
20 21 22 23 24 25 26 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 20 def reject_foreign_nullable_value_type!(type, function_name:, parameter_name:) return unless type.is_a?(Types::Nullable) return if foreign_boundary_nullable_pointer_like_base?(type.base) location = parameter_name ? "parameter #{parameter_name}" : "return type" 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)") end |
#require_unsafe!(message, line: nil, column: nil) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 20 def require_unsafe!(, line: nil, column: nil) if unsafe_context? mark_current_unsafe_required! return end suggestion = "wrap in an unsafe block: `unsafe: <expression>`" if line || column raise SemanticError.new(, line:, column:, path: @path, suggestion:) end raise_sema_error(, suggestion:) end |
#resolve_adapt_interface(type_arg) ⇒ Object
1006 1007 1008 1009 1010 1011 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 1006 def resolve_adapt_interface(type_arg) parts = type_arg.name.parts type_args = type_arg.arguments.map { |a| a.value } interface_ref = AST::QualifiedName.new(parts:, type_arguments: type_args) resolve_interface_ref(interface_ref) end |
#resolve_aggregate_fields ⇒ Object
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 447 def resolve_aggregate_fields .each do |decl| with_error_node(decl) do next unless decl.is_a?(AST::StructDecl) || decl.is_a?(AST::UnionDecl) struct_type = @ctx.types.fetch(decl.name) struct_type.ast_declaration = decl if struct_type.respond_to?(:ast_declaration=) type_params = if struct_type.is_a?(Types::GenericStructDefinition) seen = {} struct_type.type_params.each_with_object({}) do |name, params| raise_sema_error("duplicate type parameter #{decl.name}[#{name}]") if seen.key?(name) seen[name] = true params[name] = Types::TypeVar.new(name) end else {} end decl.lifetime_params.each do |lt| type_params[lt] = Types::LifetimeRef.new(lt) end if decl.respond_to?(:lifetime_params) type_param_constraints = struct_type.is_a?(Types::GenericStructDefinition) ? struct_type.type_param_constraints : {} fields = {} events = {} field_errors = [] auto_lifetimes = [] nested_scope = decl.is_a?(AST::StructDecl) ? resolve_nested_type_bindings(decl) : {} if decl.is_a?(AST::StructDecl) && struct_type.respond_to?(:define_nested_types) struct_type.define_nested_types(nested_scope) define_nested_type_bindings_recursive(decl, parent_name: decl.name) end decl.fields.each do |field| raise_sema_error("duplicate field #{decl.name}.#{field.name}") if fields.key?(field.name) raise_sema_error("duplicate member #{decl.name}.#{field.name}") if events.key?(field.name) unless raw_module? ensure_non_reserved_type_binding_name!( field.name, kind_label: "field #{decl.name}", line: field.line || decl.line, column: field.column, ) end begin field_type = resolve_type_ref(field.type, type_params:, type_param_constraints:, nested_types: nested_scope) # Auto-generate implicit lifetimes for bare ref[T] in struct fields if ref_type_without_lifetime?(field_type) auto_lt_name = "@_mt_#{auto_lifetimes.length}" auto_lifetimes << auto_lt_name type_params[auto_lt_name] = Types::LifetimeRef.new(auto_lt_name) field_type = Types::Registry.generic_instance("ref", [auto_lt_name] + field_type.arguments) end if decl.respond_to?(:lifetime_params) && (lt = ref_lifetime(field_type)) unless decl.lifetime_params.include?(lt) || auto_lifetimes.include?(lt) raise_sema_error("field #{decl.name}.#{field.name} uses lifetime #{lt} not declared on struct") end end allow_lts = decl.respond_to?(:lifetime_params) ? (decl.lifetime_params + auto_lifetimes) : [] validate_stored_ref_type!(field_type, "field #{decl.name}.#{field.name}", allow_lifetimes: allow_lts) unless proc_storage_supported_type?(field_type) raise_sema_error("field #{decl.name}.#{field.name} uses unsupported proc nesting") end fields[field.name] = field_type rescue SemanticError => e fields[field.name] = @error_type field_errors << e @structural_errors << e if @structural_errors end end unless field_errors.empty? struct_type.define_fields(fields) raise field_errors.first end if decl.is_a?(AST::StructDecl) decl.events.each do |event_decl| raise_sema_error("duplicate event #{decl.name}.#{event_decl.name}") if events.key?(event_decl.name) raise_sema_error("duplicate member #{decl.name}.#{event_decl.name}") if fields.key?(event_decl.name) unless raw_module? ensure_non_reserved_type_binding_name!( event_decl.name, kind_label: "event #{decl.name}", line: event_decl.line, column: event_decl.column, ) end begin events[event_decl.name] = resolve_event_decl_type( event_decl, type_params:, type_param_constraints:, owner_type_name: decl.name, nested_types: nested_scope, ) rescue SemanticError => e collect_structural_error(e) end end end if decl.is_a?(AST::StructDecl) resolve_nested_struct_fields(decl, type_params:, type_param_constraints:, external_nested_scope: nested_scope) end struct_type.define_fields(fields) struct_type.define_events(events) if struct_type.respond_to?(:define_events) end rescue SemanticError => e collect_structural_error(e) end end |
#resolve_and_store_offset(expression, scopes:) ⇒ Object
616 617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 616 def resolve_and_store_offset(expression, scopes:) type = resolve_type_ref(expression.type) return unless layout_aggregate_type?(type) binding = lookup_value(expression.field, scopes) return unless binding && binding.const_value.is_a?(Types::FieldHandle) offset = CompileTime::Layout.offset_of(type, binding.const_value.field_name) return unless offset @const_values[@ctx.ast.node_ids[expression.object_id]] = offset end |
#resolve_attribute_binding(name) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/milk_tea/core/semantic_analyzer/attributes.rb', line 158 def resolve_attribute_binding(name) parts = name.parts if parts.length == 1 binding = @ctx.attributes[parts.first] raise_sema_error("unknown attribute #{name}") unless binding return binding end if parts.length == 2 && @ctx.imports.key?(parts.first) imported_module = @ctx.imports.fetch(parts.first) raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}") if imported_module.private_attribute?(parts.last) binding = imported_module.attributes[parts.last] raise_sema_error("unknown attribute #{name}") unless binding return binding end raise_sema_error("unknown attribute #{name}") end |
#resolve_attribute_name_argument(expression) ⇒ Object
1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1583 def resolve_attribute_name_argument(expression) case expression when AST::Identifier resolve_attribute_binding(AST::QualifiedName.new(parts: [expression.name])) when AST::MemberAccess raise_sema_error("attribute name must use a module qualifier") unless expression.receiver.is_a?(AST::Identifier) resolve_attribute_binding(AST::QualifiedName.new(parts: [expression.receiver.name, expression.member])) else raise_sema_error("attribute name must be an identifier or module-qualified attribute name") end end |
#resolve_callable(callee, scopes:) ⇒ Object
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1247 def resolve_callable(callee, scopes:) case callee when AST::Identifier if (binding = lookup_value(callee.name, scopes)) return [:callable_value, binding.type, nil] if callable_type?(binding.type) raise_sema_error("#{callee.name} is not callable") end return [:function, @ctx.top_level_functions.fetch(callee.name), nil] if @ctx.top_level_functions.key?(callee.name) return [:fatal, nil, nil] if callee.name == "fatal" return [:ref_of, nil, nil] if callee.name == "ref_of" return [:const_ptr_of, nil, nil] if callee.name == "const_ptr_of" return [:read, nil, nil] if callee.name == "read" return [:ptr_of, nil, nil] if callee.name == "ptr_of" return [:field_of, nil, nil] if callee.name == "field_of" return [:callable_of, nil, nil] if callee.name == "callable_of" return [:attribute_of, nil, nil] if callee.name == "attribute_of" return [:has_attribute, nil, nil] if callee.name == "has_attribute" return [:get, nil, nil] if callee.name == "get" type = @ctx.types[callee.name] return [:struct, type, nil] if type.is_a?(Types::Struct) || type.is_a?(Types::StringView) || task_type?(type) || type.is_a?(Types::Vector) || type.is_a?(Types::Matrix) || type.is_a?(Types::Quaternion) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) raise_sema_error("generic type #{callee.name} requires type arguments") end raise_sema_error("unknown callable #{callee.name}") when AST::MemberAccess if callee.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(callee.receiver.name) imported_module = @ctx.imports.fetch(callee.receiver.name) return [:function, imported_module.functions.fetch(callee.member), nil] if imported_module.functions.key?(callee.member) imported_type = imported_module.types[callee.member] if imported_type.is_a?(Types::GenericStructDefinition) || imported_type.is_a?(Types::GenericVariantDefinition) raise_sema_error("generic type #{callee.receiver.name}.#{callee.member} requires type arguments", callee) end if imported_type.is_a?(Types::Struct) || imported_type.is_a?(Types::StringView) || task_type?(imported_type) || imported_type.is_a?(Types::Vector) || imported_type.is_a?(Types::Matrix) || imported_type.is_a?(Types::Quaternion) return [:struct, imported_module.types.fetch(callee.member), nil] end if imported_type.is_a?(Types::Variant) arm_name = callee.member unless imported_type.arm_names.include?(arm_name) raise_sema_error("unknown arm #{arm_name} for variant #{imported_type}") end return [:variant_arm_ctor, [imported_type, arm_name], nil] end if imported_module.private_function?(callee.member) || imported_module.private_type?(callee.member) || imported_module.private_value?(callee.member) raise_sema_error("#{callee.receiver.name}.#{callee.member} is private to module #{imported_module.name}") end raise_sema_error("unknown callable #{callee.receiver.name}.#{callee.member}") unless @ctx.types.key?(callee.receiver.name) end if (type_expr = resolve_type_expression(callee.receiver)) if type_expr.is_a?(Types::Variant) arm_name = callee.member unless type_expr.arm_names.include?(arm_name) raise_sema_error("unknown arm #{arm_name} for variant #{type_expr}") end return [:variant_arm_ctor, [type_expr, arm_name], nil] end if type_expr.respond_to?(:nested_types) && type_expr.nested_types.key?(callee.member) return [:struct, type_expr.nested_types[callee.member], nil] end method = lookup_method(type_expr, callee.member) method ||= lookup_static_method(type_expr, callee.member) return [:function, method, nil] if method && method.type.receiver_type.nil? raise_sema_error("unknown associated function #{type_expr}.#{callee.member}") end method_receiver_type = infer_method_receiver_type(callee.receiver, scopes:, member_name: callee.member) if dyn_type?(method_receiver_type) interface = method_receiver_type.interface_binding method_binding = interface.methods[callee.member] raise_sema_error("no method '#{callee.member}' on interface #{interface.name}") unless method_binding raise_sema_error("cannot call static method '#{callee.member}' on dyn value") if method_binding.kind == :static return [:dyn_method, method_binding, callee.receiver] end method = lookup_method(method_receiver_type, callee.member) return [:method, method, callee.receiver] if method if callee.member == "with" && struct_with_target_type?(method_receiver_type) return [:struct_with, method_receiver_type, callee.receiver] end if char_array_removed_text_method?(method_receiver_type, callee.member) raise_sema_error("#{method_receiver_type}.#{callee.member} is not available; array[char, N] is raw storage, use str_buffer[N] or an explicit helper") end if (str_buffer_method = str_buffer_method_kind(method_receiver_type, callee.member)) return [str_buffer_method, method_receiver_type, callee.receiver] end if (event_method = event_method_kind(method_receiver_type, callee.member)) return [event_method, method_receiver_type, callee.receiver] end if (atomic_method = atomic_method_kind(method_receiver_type, callee.member)) return [atomic_method, method_receiver_type, callee.receiver] end if (simd_method = simd_method_kind(method_receiver_type, callee.member)) return [simd_method, method_receiver_type, callee.receiver] end field_receiver_type = infer_field_receiver_type(callee.receiver, scopes:) if array_type?(field_receiver_type) && callee.member == "as_span" return [:array_as_span, field_receiver_type, callee.receiver] end return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member)) return [:callable_value, field_receiver_type.field(callee.member), nil] if aggregate_type?(field_receiver_type) && callable_type?(field_receiver_type.field(callee.member)) if (imported_module = imported_module_with_private_method(method_receiver_type, callee.member)) raise_sema_error("#{method_receiver_type}.#{callee.member} is private to module #{imported_module.name}") end raise_sema_error("unknown method #{method_receiver_type}.#{callee.member}") when AST::Specialization if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "reinterpret" raise_sema_error("reinterpret requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("reinterpret type argument must be a type") unless type_arg.is_a?(AST::TypeRef) return [:reinterpret, resolve_type_ref(type_arg), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "array" raise_sema_error("array requires exactly two type arguments") unless callee.arguments.length == 2 array_type = resolve_type_ref(AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["array"]), arguments: callee.arguments, nullable: false)) raise_sema_error("array specialization must be array[T, N]") unless array_type?(array_type) return [:array, array_type, nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "simd" raise_sema_error("simd requires exactly two type arguments") unless callee.arguments.length == 2 simd_type = resolve_type_ref(AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["simd"]), arguments: callee.arguments, nullable: false)) return [:simd, simd_type, nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "span" raise_sema_error("span requires exactly one type argument") unless callee.arguments.length == 1 span_type = resolve_type_ref(AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: callee.arguments, nullable: false)) raise_sema_error("span specialization must be span[T]") unless span_type?(span_type) return [:struct, span_type, nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "zero" raise_sema_error("zero requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("zero type argument must be a type") unless type_arg.is_a?(AST::TypeRef) return [:zero, resolve_type_ref(type_arg), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "default" return [:default, resolve_default_specialization(callee), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "hash" return [:hash, resolve_hash_specialization(callee), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "equal" return [:equal, resolve_equal_specialization(callee), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "order" return [:order, resolve_order_specialization(callee), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "attribute_arg" raise_sema_error("attribute_arg requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("attribute_arg type argument must be a type") unless type_arg.is_a?(AST::TypeRef) return [:attribute_arg, resolve_type_ref(type_arg), nil] end if callee.callee.is_a?(AST::Identifier) && callee.callee.name == "adapt" raise_sema_error("adapt requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("adapt type argument must be a type") unless type_arg.is_a?(AST::TypeRef) interface = resolve_adapt_interface(type_arg) return [:adapt, interface, nil] end if (callable_resolution = resolve_specialized_callable_binding(callee, scopes:)) return callable_resolution end if (type_ref = type_ref_from_specialization(callee)) specialized_type = resolve_type_ref(type_ref) return [:struct, specialized_type, nil] if specialized_type.is_a?(Types::Struct) || task_type?(specialized_type) || specialized_type.is_a?(Types::Vector) || specialized_type.is_a?(Types::Matrix) || specialized_type.is_a?(Types::Quaternion) || specialized_type.is_a?(Types::Simd) end raise_sema_error("unsupported callable specialization #{describe_expression(callee)}") else callee_type = infer_expression(callee, scopes:) return [:callable_value, callee_type, nil] if callable_type?(callee_type) raise_sema_error("unsupported callee #{describe_expression(callee)}") end end |
#resolve_callable_handle_argument(expression, scopes:) ⇒ Object
1576 1577 1578 1579 1580 1581 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1576 def resolve_callable_handle_argument(expression, scopes:) callable_kind, callable, _receiver = resolve_callable(expression, scopes:) raise_sema_error("callable_of expects a callable declaration name") unless callable_kind == :function Types::CallableHandle.new(describe_expression(expression), callable.ast) end |
#resolve_compile_time_type_ref(type_ref) ⇒ Object
Resolves a bare dotted reflection type expression such as field.type
(where field is a compile-time field_handle bound in scope) to the
concrete Types::Base it denotes. Returns nil when the type_ref is not a
compile-time type expression resolvable in the current scopes. Only the
plain <local>.member form is supported (no calls/indexing/type args).
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 356 def resolve_compile_time_type_ref(type_ref) return nil unless @type_resolution_scopes return nil if type_ref.arguments.any? || type_ref.nullable parts = type_ref.name.parts return nil unless parts.length >= 2 binding = lookup_value(parts.first, @type_resolution_scopes) return nil unless binding && !binding.const_value.nil? expression = AST.build_chain_from_parts(parts) return nil unless expression value = evaluate_compile_time_const_value(expression, scopes: @type_resolution_scopes) value.is_a?(Types::Base) ? value : nil end |
#resolve_current_module_const_value(name) ⇒ Object
414 415 416 417 418 419 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 414 def resolve_current_module_const_value(name) binding = @ctx.top_level_values[name] return unless binding&.kind == :const evaluate_top_level_const_value(name) end |
#resolve_default_specialization(callee) ⇒ Object
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 241 def resolve_default_specialization(callee) raise_sema_error("default requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("default type argument must be a type") unless type_arg.is_a?(AST::TypeRef) target_type = resolve_type_ref(type_arg) binding = resolve_explicit_default_binding(target_type, context: "default[#{target_type}]") raise_sema_error("default[#{target_type}] requires associated function #{target_type}.default()") unless binding DefaultResolution.new( target_type:, binding:, ) end |
#resolve_enum_member_const_value(receiver_type, member_name, local_enum_type: nil, local_member_values: nil) ⇒ Object
438 439 440 441 442 443 444 445 446 447 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 438 def resolve_enum_member_const_value(receiver_type, member_name, local_enum_type: nil, local_member_values: nil) return unless receiver_type.is_a?(Types::EnumBase) return unless receiver_type.member(member_name) if local_enum_type && receiver_type == local_enum_type && local_member_values&.key?(member_name) return local_member_values[member_name] end receiver_type.member_value(member_name) end |
#resolve_enum_members ⇒ Object
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 564 def resolve_enum_members .each do |decl| with_error_node(decl) do next unless decl.is_a?(AST::EnumDecl) || decl.is_a?(AST::FlagsDecl) enum_type = @ctx.types.fetch(decl.name) backing_type = resolve_type_ref(decl.backing_type) unless backing_type.is_a?(Types::Primitive) && backing_type.integer? raise_sema_error("#{decl.name} backing type must be an integer primitive, got #{backing_type}") end member_names = [] decl.members.each do |member| raise_sema_error("duplicate member #{decl.name}.#{member.name}") if member_names.include?(member.name) unless raw_module? ensure_non_reserved_type_binding_name!( member.name, kind_label: "member #{decl.name}", line: member.line || decl.line, column: member.column, ) end member_names << member.name end enum_type.define_members(backing_type, member_names) member_values = {} decl.members.each do |member| begin if member.value actual_type = infer_expression(member.value, scopes: [], expected_type: backing_type) const_value = evaluate_enum_member_const_value(member.value, enum_type:, member_values:) else const_value = (member_values.values.last&.succ || 0) actual_type = backing_type end compatible = types_compatible?(actual_type, backing_type, expression: member.value, scopes: []) compatible ||= actual_type.is_a?(Types::EnumBase) && types_compatible?(actual_type.backing_type, backing_type, expression: member.value, scopes: []) compatible ||= const_value.is_a?(Integer) && numeric_constant_fits_type?(const_value, backing_type) raise_sema_error("member #{decl.name}.#{member.name} expects #{backing_type}, got #{actual_type}", member) unless compatible raise_sema_error("member #{decl.name}.#{member.name} must be a compile-time integer constant", member) unless const_value.is_a?(Integer) member_values[member.name] = const_value rescue SemanticError => e collect_structural_error(e) end end enum_type.define_member_values(member_values) end rescue SemanticError => e collect_structural_error(e) end end |
#resolve_equal_specialization(callee) ⇒ Object
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 273 def resolve_equal_specialization(callee) raise_sema_error("equal requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("equal type argument must be a type") unless type_arg.is_a?(AST::TypeRef) target_type = resolve_type_ref(type_arg) binding = resolve_explicit_equal_binding(target_type, context: "equal[#{target_type}]") 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 EqualResolution.new( target_type:, binding:, ) end |
#resolve_event_decl_type(decl, type_params: {}, type_param_constraints: {}, owner_type_name: nil, nested_types: nil) ⇒ Object
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 426 def resolve_event_decl_type(decl, type_params: {}, type_param_constraints: {}, owner_type_name: nil, nested_types: nil) raise_sema_error("event #{decl.name} capacity must be positive") unless decl.capacity.is_a?(Integer) && decl.capacity.positive? payload_type = decl.payload_type ? resolve_type_ref(decl.payload_type, type_params:, type_param_constraints:, nested_types:) : nil if payload_type raise_sema_error("event #{decl.name} payload cannot be ref[T] in v1") if ref_type?(payload_type) validate_stored_ref_type!(payload_type, "event #{decl.name} payload") raise_sema_error("event #{decl.name} payload uses unsupported proc nesting") unless proc_storage_supported_type?(payload_type) raise_sema_error("event #{decl.name} payload cannot use event storage type #{payload_type}") if noncopyable_event_storage_type?(payload_type) end Types::Event.new( decl.name, capacity: decl.capacity, payload_type: payload_type, module_name: @ctx.module_name, visibility: decl.visibility, owner_type_name: owner_type_name, ) end |
#resolve_explicit_associated_binding(target_type, method_name, requirement_message:) ⇒ Object
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 390 def resolve_explicit_associated_binding(target_type, method_name, requirement_message:) method = lookup_static_method(target_type, method_name) if method raise_sema_error() unless method.type.receiver_type.nil? method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any? yield method return method end if (imported_module = imported_module_with_private_method(target_type, method_name)) raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}") end nil end |
#resolve_explicit_default_binding(target_type, context:) ⇒ Object
305 306 307 308 309 310 311 312 313 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 305 def resolve_explicit_default_binding(target_type, context:) = "#{context} requires associated function #{target_type}.default()" resolve_explicit_associated_binding(target_type, "default", requirement_message:) do |method| raise_sema_error("#{context} requires #{target_type}.default() to take 0 arguments") unless method.type.params.empty? unless types_compatible?(method.type.return_type, target_type) raise_sema_error("#{context} requires #{target_type}.default() to return #{target_type}, got #{method.type.return_type}") end end end |
#resolve_explicit_equal_binding(target_type, context:) ⇒ Object
327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 327 def resolve_explicit_equal_binding(target_type, context:) = "#{context} requires associated function #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool" resolve_explicit_associated_binding(target_type, "equal", requirement_message:) do |method| expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)] unless method.type.params.map(&:type) == expected_param_types raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool") end unless method.type.return_type == @ctx.types.fetch("bool") raise_sema_error("#{context} requires #{target_type}.equal(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> bool, got #{method.type.return_type}") end end end |
#resolve_explicit_format_append_binding(target_type, context:) ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 377 def resolve_explicit_format_append_binding(target_type, context:) = "#{context} requires method #{target_type}.append_format(output: ref[std.string.String]) -> void" resolve_explicit_instance_binding(target_type, "append_format", requirement_message:) do |method| raise_sema_error("#{context} requires #{target_type}.append_format() to be non-editable") if method.type.receiver_editable unless method.type.params.length == 1 && string_builder_ref_type?(method.type.params.first.type) raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void") end unless method.type.return_type == @ctx.types.fetch("void") raise_sema_error("#{context} requires #{target_type}.append_format(output: ref[std.string.String]) -> void, got #{method.type.return_type}") end end end |
#resolve_explicit_format_binding(target_type, context:) ⇒ Object
353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 353 def resolve_explicit_format_binding(target_type, context:) length_binding = resolve_explicit_format_len_binding(target_type, context:) append_binding = resolve_explicit_format_append_binding(target_type, context:) return [length_binding, append_binding] if length_binding && append_binding if length_binding || append_binding raise_sema_error("#{context} requires methods #{target_type}.format_len() -> ptr_uint and #{target_type}.append_format(output: ref[std.string.String]) -> void") end nil end |
#resolve_explicit_format_len_binding(target_type, context:) ⇒ Object
366 367 368 369 370 371 372 373 374 375 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 366 def resolve_explicit_format_len_binding(target_type, context:) = "#{context} requires method #{target_type}.format_len() -> ptr_uint" resolve_explicit_instance_binding(target_type, "format_len", requirement_message:) do |method| raise_sema_error("#{context} requires #{target_type}.format_len() to take 0 arguments") unless method.type.params.empty? raise_sema_error("#{context} requires #{target_type}.format_len() to be non-editable") if method.type.receiver_editable unless method.type.return_type == @ctx.types.fetch("ptr_uint") raise_sema_error("#{context} requires #{target_type}.format_len() -> ptr_uint, got #{method.type.return_type}") end end end |
#resolve_explicit_hash_binding(target_type, context:) ⇒ Object
315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 315 def resolve_explicit_hash_binding(target_type, context:) = "#{context} requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint" resolve_explicit_associated_binding(target_type, "hash", requirement_message:) do |method| unless method.type.params.map(&:type) == [const_pointer_to(target_type)] raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint") end unless method.type.return_type == @ctx.types.fetch("uint") raise_sema_error("#{context} requires #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint, got #{method.type.return_type}") end end end |
#resolve_explicit_instance_binding(target_type, method_name, requirement_message:) ⇒ Object
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 408 def resolve_explicit_instance_binding(target_type, method_name, requirement_message:) method = lookup_method(target_type, method_name) if method raise_sema_error() if method.type.receiver_type.nil? method = instantiate_function_binding_with_receiver(method, [], receiver_type: target_type) if method.type_params.any? yield method return method end if (imported_module = imported_module_with_private_method(target_type, method_name)) raise_sema_error("#{target_type}.#{method_name} is private to module #{imported_module.name}") end nil end |
#resolve_explicit_order_binding(target_type, context:) ⇒ Object
340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 340 def resolve_explicit_order_binding(target_type, context:) = "#{context} requires associated function #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int" resolve_explicit_associated_binding(target_type, "order", requirement_message:) do |method| expected_param_types = [const_pointer_to(target_type), const_pointer_to(target_type)] unless method.type.params.map(&:type) == expected_param_types raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int") end unless method.type.return_type == @ctx.types.fetch("int") raise_sema_error("#{context} requires #{target_type}.order(left: const_ptr[#{target_type}], right: const_ptr[#{target_type}]) -> int, got #{method.type.return_type}") end end end |
#resolve_foreign_call_expression(expression, scopes:) ⇒ Object
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1151 def resolve_foreign_call_expression(expression, scopes:) call = expression return unless call.is_a?(AST::Call) callable_kind, callable, _receiver = resolve_callable(call.callee, scopes:) return unless callable_kind == :function callable = specialize_function_binding( callable, call.arguments, scopes:, receiver_type: callable_receiver_type_for_specialization(call.callee, scopes:), ) if callable.type_params.any? return unless foreign_function_binding?(callable) { call:, binding: callable } rescue SemanticError nil end |
#resolve_generic_type_param_constraints ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 288 def resolve_generic_type_param_constraints .each do |decl| with_error_node(decl) do next unless decl.is_a?(AST::StructDecl) || decl.is_a?(AST::VariantDecl) || decl.is_a?(AST::InterfaceDecl) next if decl.type_params.empty? constraints = resolve_type_param_constraints(decl.type_params) if decl.is_a?(AST::InterfaceDecl) @ctx.interfaces[decl.name] = @ctx.interfaces.fetch(decl.name).with(type_param_constraints: constraints) else @ctx.types.fetch(decl.name).define_type_param_constraints(constraints) end end end end |
#resolve_hash_specialization(callee) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 257 def resolve_hash_specialization(callee) raise_sema_error("hash requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("hash type argument must be a type") unless type_arg.is_a?(AST::TypeRef) target_type = resolve_type_ref(type_arg) binding = resolve_explicit_hash_binding(target_type, context: "hash[#{target_type}]") raise_sema_error("hash[#{target_type}] requires associated function #{target_type}.hash(value: const_ptr[#{target_type}]) -> uint") unless binding HashResolution.new( target_type:, binding:, ) end |
#resolve_imported_module_const_value(import_name, value_name) ⇒ Object
425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 425 def resolve_imported_module_const_value(import_name, value_name) imported_module = @ctx.imports[import_name] return unless imported_module if imported_module.private_value?(value_name) raise_sema_error("#{import_name}.#{value_name} is private to module #{imported_module.name}") end binding = imported_module.values[value_name] return unless binding&.kind == :const binding.const_value end |
#resolve_interface_method_binding(method_decl, type_params: {}, type_param_constraints: {}) ⇒ Object
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 379 def resolve_interface_method_binding(method_decl, type_params: {}, type_param_constraints: {}) raise_sema_error("interface method #{method_decl.name} cannot be async") if method_decl.async params = [] seen = {} method_decl.params.each do |param| raise_sema_error("duplicate parameter #{param.name} in #{method_decl.name}") if seen.key?(param.name) seen[param.name] = true type = resolve_type_ref(param.type, type_params:, type_param_constraints:) validate_parameter_ref_type!(type, function_name: method_decl.name, parameter_name: param.name, external: false) validate_parameter_proc_type!(type, function_name: method_decl.name, parameter_name: param.name, external: false, foreign: false) params << Types::Registry.parameter(param.name, type) end return_type = method_decl.return_type ? resolve_type_ref(method_decl.return_type, type_params:, type_param_constraints:) : @ctx.types.fetch("void") validate_return_ref_type!(return_type, function_name: method_decl.name) validate_return_proc_type!(return_type, function_name: method_decl.name) InterfaceMethodBinding.new( name: method_decl.name, params: params.freeze, return_type: return_type, kind: method_decl.kind, async: method_decl.async, ast: method_decl, ) end |
#resolve_interface_ref(interface_ref) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 130 def resolve_interface_ref(interface_ref) parts = interface_ref.parts interface = if parts.length == 1 @ctx.interfaces[parts.first] elsif parts.length == 2 && @ctx.imports.key?(parts.first) imported_module = @ctx.imports.fetch(parts.first) raw = imported_module.interfaces[parts.last] if imported_module.private_interface?(parts.last) raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}") end raw end raise_sema_error("unknown interface #{interface_ref}", interface_ref) unless interface if interface_ref.type_arguments.any? raise_sema_error("interface #{interface.name} is not generic") unless interface.is_a?(GenericInterfaceBinding) arguments = interface_ref.type_arguments.map { |arg| resolve_type_ref(arg) } interface.instantiate(arguments) else raise_sema_error("generic interface #{interface.name} requires type arguments") if interface.is_a?(GenericInterfaceBinding) interface end end |
#resolve_methods_receiver_target(type_ref) ⇒ Object
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1025 def resolve_methods_receiver_target(type_ref) if type_ref.is_a?(AST::TypeRef) generic_type = resolve_named_generic_type(type_ref.name.parts) if generic_type.is_a?(Types::GenericStructDefinition) || generic_type.is_a?(Types::GenericVariantDefinition) receiver_type_param_names = validate_methods_receiver_type_arguments!(type_ref, generic_type) receiver_type_params = receiver_type_param_names.to_h { |name| [name, Types::TypeVar.new(name)] } receiver_type_param_constraints = generic_type.respond_to?(:type_param_constraints) ? generic_type.type_param_constraints : {} receiver_type = resolve_type_ref(type_ref, type_params: receiver_type_params, type_param_constraints: receiver_type_param_constraints) return [generic_type, receiver_type, receiver_type_param_names, receiver_type_param_constraints] end begin receiver_type = resolve_type_ref(type_ref) unless receiver_type.is_a?(Types::Struct) || receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::Opaque) || receiver_type.is_a?(Types::GenericInstance) || receiver_type.is_a?(Types::Nullable) || receiver_type.is_a?(Types::StringView) || receiver_type.is_a?(Types::Primitive) || receiver_type.is_a?(Types::Variant) || receiver_type.is_a?(Types::VariantInstance) || vector_type?(receiver_type) || matrix_type?(receiver_type) || quaternion_type?(receiver_type) raise_sema_error("extending target #{type_ref} must be a struct, opaque, nullable/generic receiver, variant, or str") end return [receiver_type, receiver_type, [], {}] rescue MilkTea::SemanticError => error receiver_type_param_names = methods_receiver_type_argument_names!(type_ref) raise error if receiver_type_param_names.empty? receiver_type_params = receiver_type_param_names.to_h { |name| [name, Types::TypeVar.new(name)] } receiver_type = resolve_type_ref(type_ref, type_params: receiver_type_params) return [method_dispatch_receiver_type(receiver_type), receiver_type, receiver_type_param_names, {}] end end receiver_type = resolve_type_ref(type_ref) unless receiver_type.is_a?(Types::Struct) || receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::Opaque) || receiver_type.is_a?(Types::GenericInstance) || receiver_type.is_a?(Types::Nullable) || receiver_type.is_a?(Types::StringView) || receiver_type.is_a?(Types::Primitive) || receiver_type.is_a?(Types::Variant) || receiver_type.is_a?(Types::VariantInstance) || vector_type?(receiver_type) || matrix_type?(receiver_type) || quaternion_type?(receiver_type) raise_sema_error("extending target #{type_ref} must be a struct, opaque, nullable/generic receiver, variant, or str") end [receiver_type, receiver_type, [], {}] end |
#resolve_name_in_precheck_scopes(name, scopes) ⇒ Object
409 410 411 412 413 414 415 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 409 def resolve_name_in_precheck_scopes(name, scopes) scopes.reverse_each do |scope| return scope[name] if scope.key?(name) end nil end |
#resolve_named_generic_type(parts) ⇒ Object
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 813 def resolve_named_generic_type(parts) if parts.length == 1 type = @ctx.types[parts.first] return type if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) elsif parts.length >= 2 type = resolve_nested_type_ref(parts) return type if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) if @ctx.imports.key?(parts.first) type = @ctx.imports.fetch(parts.first).types[parts.last] return type if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) end end nil end |
#resolve_named_literal_type_argument(type_ref) ⇒ Object
401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 401 def resolve_named_literal_type_argument(type_ref) value = case type_ref.name.parts.length when 1 resolve_current_module_const_value(type_ref.name.parts.first) when 2 resolve_imported_module_const_value(type_ref.name.parts.first, type_ref.name.parts.last) end return unless value.is_a?(Integer) || value.is_a?(Float) Types::LiteralTypeArg.new(value) end |
#resolve_nested_struct_fields(parent_decl, parent_name: parent_decl.name, type_params:, type_param_constraints:, external_nested_scope: {}) ⇒ Object
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 803 def resolve_nested_struct_fields(parent_decl, parent_name: parent_decl.name, type_params:, type_param_constraints:, external_nested_scope: {}) parent_decl.nested_types.each do |nested| next unless nested.type_params.empty? qualified_name = "#{parent_name}.#{nested.name}" nested_type = @ctx.types[qualified_name] next unless nested_type.is_a?(Types::Struct) nested_type.ast_declaration = nested nested_scope = nested_type.nested_types.merge(external_nested_scope) fields = {} nested.fields.each do |field| raise_sema_error("duplicate field #{qualified_name}.#{field.name}") if fields.key?(field.name) begin field_type = resolve_type_ref(field.type, type_params:, type_param_constraints:, nested_types: nested_scope) validate_stored_ref_type!(field_type, "field #{qualified_name}.#{field.name}") fields[field.name] = field_type rescue SemanticError => e fields[field.name] = @error_type @structural_errors << e if @structural_errors end end nested_type.define_fields(fields) nested_events = {} nested.events.each do |event_decl| raise_sema_error("duplicate event #{qualified_name}.#{event_decl.name}") if nested_events.key?(event_decl.name) begin nested_events[event_decl.name] = resolve_event_decl_type(event_decl, type_params:, type_param_constraints:, owner_type_name: qualified_name, nested_types: nested_scope) rescue SemanticError => e collect_structural_error(e) end end nested_type.define_events(nested_events) resolve_nested_struct_fields(nested, parent_name: qualified_name, type_params:, type_param_constraints:, external_nested_scope: nested_scope) end end |
#resolve_nested_type_bindings(parent_decl, parent_name: parent_decl.name) ⇒ Object
838 839 840 841 842 843 844 845 846 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 838 def resolve_nested_type_bindings(parent_decl, parent_name: parent_decl.name) bindings = {} parent_decl.nested_types.each do |nested| qualified_name = "#{parent_name}.#{nested.name}" nested_type = @ctx.types[qualified_name] bindings[nested.name] = nested_type if nested_type end bindings end |
#resolve_nested_type_ref(parts) ⇒ Object
802 803 804 805 806 807 808 809 810 811 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 802 def resolve_nested_type_ref(parts) current = @ctx.types[parts.first] return nil unless current.is_a?(Types::Struct) || current.is_a?(Types::GenericStructDefinition) parts[1..].each do |part| nested = current.respond_to?(:nested_types) ? current.nested_types[part] : nil return nil unless nested current = nested end current end |
#resolve_non_nullable_type(type_ref, type_params: {}, type_param_constraints: {}, nested_types: nil) ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 212 def resolve_non_nullable_type(type_ref, type_params: {}, type_param_constraints: {}, nested_types: nil) if type_ref.is_a?(AST::FunctionType) params = type_ref.params.map do |param| Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:)) end return Types::Registry.function(nil, params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:)) end if type_ref.is_a?(AST::ProcType) params = type_ref.params.map do |param| Types::Registry.parameter(param.name, resolve_type_ref(param.type, type_params:, type_param_constraints:)) end return Types::Registry.proc(params:, return_type: resolve_type_ref(type_ref.return_type, type_params:, type_param_constraints:)) end if type_ref.is_a?(AST::DynType) interface = resolve_interface_ref(type_ref.interface) raise_sema_error("generic interface #{interface.name} requires type arguments") if interface.is_a?(GenericInterfaceBinding) type_arguments = interface.type_arguments || [] type = Types::Dyn.new(interface, type_arguments) type = Types::Registry.nullable(type) if type_ref.nullable return type end if type_ref.is_a?(AST::TupleType) names = [] element_types = [] type_ref.element_types.each do |et| if et.is_a?(AST::Argument) names << et.name element_types << resolve_type_ref(et.value, type_params:, type_param_constraints:) else names << nil element_types << resolve_type_ref(et, type_params:, type_param_constraints:) end end has_named = names.any? return Types::Registry.tuple(element_types, field_names: has_named ? names : nil) end parts = type_ref.name.parts if type_ref.arguments.any? name = parts.join(".") arguments = type_ref.arguments.map { |argument| resolve_type_argument(argument.value, type_params:, type_param_constraints:) } if name != "ref" && arguments.any? { |argument| contains_ref_type?(argument) && !stored_ref_supported_type?(argument) } raise_sema_error("ref types cannot be nested inside #{name}", type_ref) end if name == "Task" validate_generic_type!(name, arguments) return Types::Registry.task(arguments[0]) end if (generic_type = resolve_named_generic_type(parts)) begin validate_generic_type_param_constraints!(generic_type, arguments, context: "type #{generic_type}", available_type_param_constraints: type_param_constraints) return generic_type.instantiate(arguments) rescue ArgumentError => error raise_sema_error(error.) end end # Handle types with lifetime params only (no type params) if arguments.all? { |a| a.is_a?(Types::LifetimeRef) } type = @ctx.types[name] if type.is_a?(Types::Struct) && type.lifetime_params&.any? lifetime_args = arguments.select { |a| a.is_a?(Types::LifetimeRef) }.map(&:name) if lifetime_args.to_set == type.lifetime_params.to_set return type end end end validate_generic_type!(name, arguments) return Types::Registry.span(arguments.first) if name == "span" return Types::Registry.soa(arguments[0], count: arguments[1].value) if name == "SoA" return Types::Registry.simd(arguments[0], lane_count: arguments[1].value) if name == "simd" arguments = [type_ref.lifetime] + arguments if name == "ref" && type_ref.lifetime return Types::Registry.generic_instance(name, arguments) end if parts.length == 1 && type_ref.lifetime raise_sema_error("lifetime annotations are only valid on ref types, got #{type_ref.name}", type_ref) end if parts.length == 1 return type_params.fetch(parts.first) if type_params.key?(parts.first) if nested_types && (type = nested_types[parts.first]) return type end if parts.first.start_with?("@") raise_sema_error("unknown lifetime #{parts.first}", type_ref) end type = @ctx.types[parts.first] unless type type_names = @ctx.types.keys suggestion = suggest_name(parts.first, type_names) unless suggestion suggestion = import_suggestion_for_type(parts.first) end raise_sema_error("unknown type #{parts.first}", type_ref, suggestion: suggestion ? "did you mean '#{suggestion}'?" : nil) end raise_sema_error("generic type #{parts.first} requires type arguments", type_ref) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) return type end if parts.length >= 2 type = resolve_nested_type_ref(parts) return type if type end if parts.length == 2 && @ctx.imports.key?(parts.first) imported_module = @ctx.imports.fetch(parts.first) type = imported_module.types[parts.last] if imported_module.private_type?(parts.last) raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}", type_ref) end raise_sema_error("unknown type #{type_ref.name}", type_ref) unless type raise_sema_error("generic type #{type_ref.name} requires type arguments", type_ref) if type.is_a?(Types::GenericStructDefinition) || type.is_a?(Types::GenericVariantDefinition) return type end if @compile_time_depth&.positive? && (ct_type = resolve_compile_time_type_ref(type_ref)) return ct_type end raise_sema_error("unknown type #{type_ref.name}", type_ref, suggestion: import_suggestion_for_type(type_ref.name)) end |
#resolve_order_specialization(callee) ⇒ Object
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 289 def resolve_order_specialization(callee) raise_sema_error("order requires exactly one type argument") unless callee.arguments.length == 1 type_arg = callee.arguments.first.value raise_sema_error("order type argument must be a type") unless type_arg.is_a?(AST::TypeRef) target_type = resolve_type_ref(type_arg) binding = resolve_explicit_order_binding(target_type, context: "order[#{target_type}]") 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 OrderResolution.new( target_type:, binding:, ) end |
#resolve_qualified_type_name(parts) ⇒ Object
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 347 def resolve_qualified_type_name(parts) return nil unless parts.length >= 2 if @ctx.imports.key?(parts.first) imported_module = @ctx.imports.fetch(parts.first) type = imported_module.types[parts.last] if imported_module.private_type?(parts.last) raise_sema_error("#{parts.first}.#{parts.last} is private to module #{imported_module.name}") end raise_sema_error("unknown type #{parts.join('.')} for struct destructure") unless type return type end parent_type = @ctx.types[parts.first] if parent_type.respond_to?(:nested_types) && parent_type.nested_types.key?(parts.last) return parent_type.nested_types[parts.last] end raise_sema_error("unknown type #{parts.join('.')} for struct destructure") end |
#resolve_receiver_name_for_call_inference(callee, scopes) ⇒ Object
577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 577 def resolve_receiver_name_for_call_inference(callee, scopes) case callee.receiver when AST::Identifier if callee.receiver.name == "this" binding = lookup_value("this", scopes) return nil unless binding this_type = receiver_type_for_fields(binding.type) this_type&.name else callee.receiver.name end end end |
#resolve_reflection_target_argument(expression, scopes:) ⇒ Object
1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1564 def resolve_reflection_target_argument(expression, scopes:) if (type_expr = resolve_type_expression(expression)) handle = struct_handle_for_type(type_expr) return handle if handle end handle = evaluate_compile_time_const_value(expression, scopes:) return handle if handle.is_a?(Types::FieldHandle) || handle.is_a?(Types::CallableHandle) raise_sema_error("attribute reflection expects a struct type, field handle, or callable handle") end |
#resolve_specialization_type_arguments(expression) ⇒ Object
163 164 165 166 167 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 163 def resolve_specialization_type_arguments(expression) expression.arguments.map do |argument| resolve_type_argument(argument.value, type_params: current_type_params) end end |
#resolve_specialized_callable_binding(expression, scopes:) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 113 def resolve_specialized_callable_binding(expression, scopes:) callable_kind = :function receiver = nil receiver_type = nil binding = case expression.callee when AST::Identifier @ctx.top_level_functions[expression.callee.name] when AST::MemberAccess if expression.callee.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(expression.callee.receiver.name) imported_module = @ctx.imports.fetch(expression.callee.receiver.name) imported_function = imported_module.functions[expression.callee.member] if imported_function.nil? && imported_module.private_function?(expression.callee.member) raise_sema_error("#{expression.callee.receiver.name}.#{expression.callee.member} is private to module #{imported_module.name}") end imported_function elsif (type_expr = resolve_type_expression(expression.callee.receiver)) associated_function = lookup_method(type_expr, expression.callee.member) if associated_function&.type&.receiver_type.nil? receiver_type = type_expr associated_function else if (imported_module = imported_module_with_private_method(type_expr, expression.callee.member)) raise_sema_error("#{type_expr}.#{expression.callee.member} is private to module #{imported_module.name}") end nil end else receiver_type = infer_method_receiver_type(expression.callee.receiver, scopes:, member_name: expression.callee.member) method = lookup_method(receiver_type, expression.callee.member) if method callable_kind = :method receiver = expression.callee.receiver method else if (imported_module = imported_module_with_private_method(receiver_type, expression.callee.member)) raise_sema_error("#{receiver_type}.#{expression.callee.member} is private to module #{imported_module.name}") end nil end end end return nil unless binding type_arguments = resolve_specialization_type_arguments(expression) [callable_kind, instantiate_function_binding_with_receiver(binding, type_arguments, receiver_type:), receiver] end |
#resolve_struct_handle_argument(expression, scopes:) ⇒ Object
1555 1556 1557 1558 1559 1560 1561 1562 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1555 def resolve_struct_handle_argument(expression, scopes:) if (type_expr = resolve_type_expression(expression)) handle = struct_handle_for_type(type_expr) return handle if handle end raise_sema_error("field_of expects a struct type expression") end |
#resolve_type_aliases ⇒ Object
304 305 306 307 308 309 310 311 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 304 def resolve_type_aliases @ctx.ast.declarations.grep(AST::TypeAliasDecl).each do |decl| ensure_available_type_name!(decl.name) @ctx.types[decl.name] = resolve_type_ref(decl.target) rescue SemanticError => e collect_structural_error(e) end end |
#resolve_type_argument(argument, type_params: current_type_params, type_param_constraints: current_type_param_constraints) ⇒ Object
373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 373 def resolve_type_argument(argument, type_params: current_type_params, type_param_constraints: current_type_param_constraints) case argument when AST::TypeRef resolve_type_argument_ref(argument, type_params:, type_param_constraints:) when AST::FunctionType, AST::ProcType resolve_type_ref(argument, type_params:, type_param_constraints:) when AST::IntegerLiteral, AST::FloatLiteral Types::LiteralTypeArg.new(argument.value) else raise_sema_error("unsupported type argument #{argument.class.name}") end end |
#resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:) ⇒ Object
386 387 388 389 390 391 392 393 394 395 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 386 def resolve_type_argument_ref(type_ref, type_params:, type_param_constraints:) return resolve_type_ref(type_ref, type_params:, type_param_constraints:) unless literal_type_argument_name_candidate?(type_ref) resolve_type_ref(type_ref, type_params:, type_param_constraints:) rescue SemanticError => error literal_type_argument = resolve_named_literal_type_argument(type_ref) return literal_type_argument if literal_type_argument raise error end |
#resolve_type_expression(expression) ⇒ Object
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 975 def resolve_type_expression(expression) case expression when AST::Identifier return current_type_params[expression.name] if current_type_params.key?(expression.name) @ctx.types[expression.name] when AST::MemberAccess return nil unless expression.receiver.is_a?(AST::Identifier) if @ctx.imports.key?(expression.receiver.name) imported_module = @ctx.imports.fetch(expression.receiver.name) if imported_module.private_type?(expression.member) raise_sema_error("#{expression.receiver.name}.#{expression.member} is private to module #{imported_module.name}") end return imported_module.types[expression.member] end parent_type = @ctx.types[expression.receiver.name] return parent_type.nested_types[expression.member] if parent_type.respond_to?(:nested_types) && parent_type.nested_types.key?(expression.member) nil when AST::Specialization type_ref = type_ref_from_specialization(expression) return nil unless type_ref resolve_type_ref(type_ref) end end |
#resolve_type_member(type, name) ⇒ Object
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 98 def resolve_type_member(type, name) case type when Types::Enum, Types::Flags type.member(name) when Types::Variant return type if type.arm_names.include?(name) && !type.has_payload?(name) nil end end |
#resolve_type_param_constraints(type_params) ⇒ Object
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 208 def resolve_type_param_constraints(type_params) type_params.each_with_object({}) do |type_param, constraints| if type_param.is_a?(AST::ValueTypeParam) ensure_non_reserved_type_binding_name!( type_param.name, kind_label: "type parameter", line: type_param.line, column: type_param.column, length: type_param.length, ) next end ensure_non_reserved_type_binding_name!( type_param.name, kind_label: "type parameter", line: type_param.line, column: type_param.column, length: type_param.length, ) next if type_param.constraints.empty? resolved_interfaces = [] seen_interfaces = {} type_param.constraints.each do |constraint| case constraint.kind when :interface interface = resolve_interface_ref(constraint.interface_ref) raise_sema_error("duplicate interface constraint #{type_param.name} implements #{interface.name}") if seen_interfaces.key?(interface) seen_interfaces[interface] = true resolved_interfaces << interface else raise_sema_error("unsupported type parameter constraint #{constraint.kind}") end end constraints[type_param.name] = TypeParamConstraintBinding.new( interfaces: resolved_interfaces.freeze, ) end end |
#resolve_type_ref(type_ref, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: nil) ⇒ Object
203 204 205 206 207 208 209 210 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 203 def resolve_type_ref(type_ref, type_params: current_type_params, type_param_constraints: current_type_param_constraints, nested_types: nil) base = resolve_non_nullable_type(type_ref, type_params:, type_param_constraints:, nested_types:) return base if type_ref.is_a?(AST::FunctionType) || type_ref.is_a?(AST::ProcType) || type_ref.is_a?(AST::TupleType) raise_sema_error("ref types are non-null and cannot be nullable", type_ref) if type_ref.nullable && ref_type?(base) type_ref.nullable ? Types::Registry.nullable(base) : base end |
#resolve_variant_arms ⇒ Object
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 624 def resolve_variant_arms .each do |decl| with_error_node(decl) do next unless decl.is_a?(AST::VariantDecl) variant_type = @ctx.types.fetch(decl.name) type_params = if variant_type.is_a?(Types::GenericVariantDefinition) seen = {} variant_type.type_params.each_with_object({}) do |name, params| raise_sema_error("duplicate type parameter #{decl.name}[#{name}]") if seen.key?(name) seen[name] = true params[name] = Types::TypeVar.new(name) end else {} end type_param_constraints = variant_type.is_a?(Types::GenericVariantDefinition) ? variant_type.type_param_constraints : {} seen_arms = [] arms_hash = {} decl.arms.each do |arm| begin raise_sema_error("duplicate arm #{decl.name}.#{arm.name}") if seen_arms.include?(arm.name) unless raw_module? ensure_non_reserved_type_binding_name!( arm.name, kind_label: "arm #{decl.name}", line: arm.line || decl.line, column: arm.column, ) end seen_arms << arm.name field_types = {} seen_fields = [] arm_field_errors = [] arm.fields.each do |field| begin raise_sema_error("duplicate field #{arm.name}.#{field.name}") if seen_fields.include?(field.name) unless raw_module? ensure_non_reserved_type_binding_name!( field.name, kind_label: "field #{decl.name}.#{arm.name}", line: field.line || decl.line, column: field.column, ) end seen_fields << field.name field_type = resolve_type_ref(field.type, type_params:, type_param_constraints:) validate_stored_ref_type!(field_type, "field #{decl.name}.#{arm.name}.#{field.name}") unless proc_storage_supported_type?(field_type) raise_sema_error("field #{decl.name}.#{arm.name}.#{field.name} uses unsupported proc nesting") end field_types[field.name] = field_type rescue SemanticError => e field_types[field.name] = @error_type arm_field_errors << e @structural_errors << e if @structural_errors end end raise arm_field_errors.first unless arm_field_errors.empty? arms_hash[arm.name] = field_types rescue SemanticError => e collect_structural_error(e) end end variant_type.define_arms(arms_hash) end rescue SemanticError => e collect_structural_error(e) end end |
#resolved_attribute_applications_for_target(target) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 6 def resolved_attribute_applications_for_target(target) target_id = case target when Types::StructHandle then target.declaration.object_id when Types::FieldHandle then target.field_declaration.object_id when Types::CallableHandle then target.declaration.object_id end return [] unless target_id applications = @ctx.resolved_attribute_applications[target_id] return applications if applications @ctx.imports.each_value do |imported_module| applications = imported_module.attribute_applications[target_id] return applications if applications end [] end |
#result_let_else_type?(type) ⇒ Boolean
810 811 812 813 814 815 816 817 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 810 def result_let_else_type?(type) return false unless type.is_a?(Types::Variant) success_fields = type.arm("success") failure_fields = type.arm("failure") success_fields && success_fields.length == 1 && success_fields.key?("value") && failure_fields && failure_fields.length == 1 && failure_fields.key?("error") end |
#run_nullability_pre_pass(binding, scopes) ⇒ Object
Runs a strict binding-ID nullability pass before statement checks. Resolution is computed with a lexical pre-check walk so shadowed names are disambiguated without relying on name fallback.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 56 def run_nullability_pre_pass(binding, scopes) return unless binding.ast.respond_to?(:body) @nullability_flow_result = nil resolution = precheck_binding_resolution(binding.ast.body, scopes) graph = ControlFlow::Builder.new( binding_resolution: ControlFlow::BindingResolution.new( identifier_binding_ids: resolution.identifier_binding_ids, declaration_binding_ids: resolution.declaration_binding_ids, mutating_argument_identifier_ids: resolution.mutating_argument_identifier_ids, ), strict_binding_ids: true, ).build(binding.ast.body) @nullability_flow_result = ControlFlow::NullabilityFlow.solve(graph) end |
#safe_reference_source_expression?(expression, scopes:) ⇒ Boolean
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1247 def safe_reference_source_expression?(expression, scopes:) case expression when AST::Identifier true when AST::MemberAccess, AST::IndexAccess safe_reference_source_expression?(expression.receiver, scopes:) when AST::BinaryOp unsafe_context? when AST::Call return false unless expression.arguments.length == 1 && expression.arguments.first.name.nil? if read_call?(expression) argument_type = infer_expression(expression.arguments.first.value, scopes:) ref_type?(argument_type) || pointer_type?(argument_type) else false end else false end end |
#same_attribute_binding?(left, right) ⇒ Boolean
31 32 33 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 31 def same_attribute_binding?(left, right) left.name == right.name && left.module_name == right.module_name end |
#scopes_with_refinements(scopes, refinements) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 20 def scopes_with_refinements(scopes, refinements) return scopes if refinements.nil? || refinements.empty? base_scopes = scopes.last.is_a?(FlowScope) ? scopes[0...-1] : scopes merged_refinements = if scopes.last.is_a?(FlowScope) scopes.last.each_with_object({}) do |(name, binding), result| result[name] = if name.is_a?(String) && binding.respond_to?(:type) binding.type else binding end end else {} end merged_refinements = merge_refinements(merged_refinements, refinements) flow_scope = FlowScope.new merged_refinements.each do |name, refined_type| unless name.is_a?(String) flow_scope[name] = refined_type next end binding = lookup_value(name, base_scopes) next unless binding flow_scope[name] = binding.with_flow_type(refined_type) end return base_scopes if flow_scope.empty? base_scopes + [flow_scope] end |
#set_const_value(name, value) ⇒ Object
239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 239 def set_const_value(name, value) binding = @ctx.top_level_values.fetch(name) @ctx.top_level_values[name] = ValueBinding.new( id: binding.id, name: binding.name, storage_type: binding.storage_type, flow_type: binding.flow_type, mutable: binding.mutable, kind: binding.kind, const_value: value, ) @evaluated_const_values[name] = true end |
#simd_bitwise_result(left_type, right_type) ⇒ Object
240 241 242 243 244 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 240 def simd_bitwise_result(left_type, right_type) return nil unless simd_type?(left_type) && simd_type?(right_type) && left_type == right_type left_type end |
#simd_method_kind(receiver_type, name) ⇒ Object
660 661 662 663 664 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 660 def simd_method_kind(receiver_type, name) return unless simd_type?(receiver_type) SIMD_METHOD_KINDS[name] end |
#simd_mod_result(left_type, right_type) ⇒ Object
246 247 248 249 250 251 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 246 def simd_mod_result(left_type, right_type) return nil unless simd_type?(left_type) && left_type.element_type.integer? return nil unless right_type == left_type.element_type || (simd_type?(right_type) && right_type == left_type) left_type end |
#simd_op_result(operator, left_type, right_type) ⇒ Object
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 224 def simd_op_result(operator, left_type, right_type) if simd_type?(left_type) && simd_type?(right_type) && left_type.element_type == right_type.element_type && left_type.lane_count == right_type.lane_count return left_type if operator == "+" || operator == "-" || operator == "*" || operator == "/" end if simd_type?(left_type) && right_type.numeric? && right_type == left_type.element_type return left_type if operator == "*" || operator == "/" end if left_type.numeric? && simd_type?(right_type) && left_type == right_type.element_type return right_type if operator == "*" end nil end |
#simd_shift_result(left_type, right_type) ⇒ Object
253 254 255 256 257 258 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 253 def simd_shift_result(left_type, right_type) return nil unless simd_type?(left_type) && left_type.element_type.integer? return nil unless right_type.is_a?(Types::Primitive) && right_type.integer? left_type end |
#simd_type?(type) ⇒ Boolean
721 722 723 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 721 def simd_type?(type) type.is_a?(Types::Simd) end |
#simple_foreign_argument_expression?(expression) ⇒ Boolean
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1205 def simple_foreign_argument_expression?(expression) case expression when AST::Identifier, AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral true when AST::MemberAccess simple_foreign_argument_expression?(expression.receiver) else false end end |
#sized_layout_type?(type) ⇒ Boolean
648 649 650 651 652 653 654 655 656 657 658 659 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 648 def sized_layout_type?(type) case type when Types::Primitive, Types::Struct, Types::StructInstance, Types::Union, Types::Enum, Types::Flags, Types::Variant, Types::Span, Types::StringView, Types::Task, Types::Event, Types::Subscription true when Types::Nullable true when Types::GenericInstance pointer_type?(type) || array_type?(type) || str_buffer_type?(type) else false end end |
#snapshot_attribute_applications ⇒ Object
124 125 126 127 128 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 124 def snapshot_attribute_applications @ctx.resolved_attribute_applications.each_with_object({}) do |(target_id, applications), snapshot| snapshot[target_id] = applications end.freeze end |
#snapshot_attributes ⇒ Object
116 117 118 119 120 121 122 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 116 def snapshot_attributes @ctx.attributes.each_with_object({}) do |(name, binding), attributes| next if binding.builtin attributes[name] = binding end.freeze end |
#snapshot_implemented_interfaces ⇒ Object
136 137 138 139 140 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 136 def snapshot_implemented_interfaces @ctx.implemented_interfaces.each_with_object({}) do |(receiver_type, interfaces), snapshot| snapshot[receiver_type] = interfaces.dup.freeze end.freeze end |
#snapshot_methods ⇒ Object
130 131 132 133 134 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 130 def snapshot_methods @ctx.methods.each_with_object({}) do |(receiver_type, bindings), methods| methods[receiver_type] = bindings.dup.freeze end.freeze end |
#soa_type?(type) ⇒ Boolean
717 718 719 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 717 def soa_type?(type) type.is_a?(Types::SoA) end |
#source_column(node) ⇒ Object
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 550 def source_column(node) return nil unless node return node.column if node.column case node when AST::MemberAccess then source_column(node.receiver) when AST::IndexAccess then source_column(node.receiver) || source_column(node.index) when AST::Specialization then source_column(node.callee) when AST::Call then source_column(node.callee) || node.arguments.filter_map { |argument| source_column(argument.value) }.first when AST::Argument then source_column(node.value) when AST::UnaryOp then source_column(node.operand) when AST::BinaryOp then source_column(node.left) || source_column(node.right) when AST::IfExpr then source_column(node.condition) || source_column(node.then_expression) || source_column(node.else_expression) when AST::MatchExpr then source_column(node.expression) || node.arms.filter_map { |arm| source_column(arm.pattern) || source_column(arm.value) }.first when AST::AwaitExpr then source_column(node.expression) when AST::FormatExprPart then source_column(node.expression) when AST::PrefixCast then source_column(node.target_type) when AST::Assignment then source_column(node.target) || source_column(node.value) when AST::ExpressionStmt then source_column(node.expression) when AST::StaticAssert then source_column(node.condition) when AST::IfStmt then node.branches.filter_map { |branch| branch.column || source_column(branch.condition) }.first || node.else_column else nil end end |
#source_length(node) ⇒ Object
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 532 def source_length(node) return nil unless node return node.length if node.respond_to?(:length) && node.length return node.name.to_s.length if node.respond_to?(:name) && node.name case node when AST::Identifier then node.name.to_s.length when AST::MemberAccess then node.member.to_s.length when AST::Assignment then source_length(node.target) || source_length(node.value) when AST::ExpressionStmt then source_length(node.expression) when AST::StaticAssert then source_length(node.condition) when AST::IfStmt node.branches.filter_map { |branch| branch.length || source_length(branch.condition) }.first else nil end end |
#source_line(node) ⇒ Object
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 511 def source_line(node) return nil unless node return node.line if node.line case node when AST::MemberAccess then source_line(node.receiver) when AST::IndexAccess then source_line(node.receiver) || source_line(node.index) when AST::Specialization then source_line(node.callee) when AST::Call then source_line(node.callee) || node.arguments.filter_map { |argument| source_line(argument.value) }.first when AST::Argument then source_line(node.value) when AST::UnaryOp then source_line(node.operand) when AST::BinaryOp then source_line(node.left) || source_line(node.right) when AST::IfExpr then source_line(node.condition) || source_line(node.then_expression) || source_line(node.else_expression) when AST::MatchExpr then source_line(node.expression) || node.arms.filter_map { |arm| source_line(arm.pattern) || source_line(arm.value) }.first when AST::AwaitExpr then source_line(node.expression) when AST::FormatExprPart then source_line(node.expression) when AST::PrefixCast then source_line(node.target_type) else nil end end |
#span_type?(type) ⇒ Boolean
576 577 578 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 576 def span_type?(type) type.is_a?(Types::Span) end |
#specialization_lookup_owner ⇒ Object
123 124 125 126 127 128 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 123 def specialization_lookup_owner return nil if @current_specialization_owner.nil? return nil if @current_specialization_owner.equal?(self) @current_specialization_owner end |
#specialize_function_binding(binding, arguments, scopes:, receiver_type: nil) ⇒ Object
169 170 171 172 173 174 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 169 def specialize_function_binding(binding, arguments, scopes:, receiver_type: nil) return binding if binding.type_params.empty? type_arguments = infer_function_type_arguments(binding, arguments, scopes:, receiver_type:) instantiate_function_binding(binding, type_arguments) end |
#stack_safe_pointer_source?(binding) ⇒ Boolean
1422 1423 1424 1425 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1422 def stack_safe_pointer_source?(binding) return false unless binding.type ref_type?(binding.type) || own_type?(binding.type) || (binding.mutable && binding.kind == :param) end |
#start_local_completion_frame(binding, scopes) ⇒ Object
101 102 103 104 105 106 107 108 109 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 101 def start_local_completion_frame(binding, scopes) frame = { function_name: binding.name, receiver_type: binding.type.receiver_type, snapshots: [], } @active_local_completion_stack << frame record_local_completion_snapshot(binding.ast.line, 0, scopes) end |
#statement_contains_await?(statement) ⇒ Boolean
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 206 def statement_contains_await?(statement) case statement when AST::ErrorBlockStmt (statement.header_expression && expression_contains_await?(statement.header_expression)) || Array(statement.header_iterables).any? { |iterable| expression_contains_await?(iterable) } || statements_contain_await?(statement.body) when AST::LocalDecl (statement.value && expression_contains_await?(statement.value)) || (statement.else_body && statements_contain_await?(statement.else_body)) when AST::Assignment expression_contains_await?(statement.target) || expression_contains_await?(statement.value) when AST::IfStmt statement.branches.any? { |branch| expression_contains_await?(branch.condition) || statements_contain_await?(branch.body) } || (statement.else_body && statements_contain_await?(statement.else_body)) when AST::MatchStmt expression_contains_await?(statement.expression) || statement.arms.any? { |arm| expression_contains_await?(arm.pattern) || statements_contain_await?(arm.body) } when AST::UnsafeStmt statements_contain_await?(statement.body) when AST::StaticAssert expression_contains_await?(statement.condition) || expression_contains_await?(statement.) when AST::ForStmt statement.iterables.any? { |iterable| expression_contains_await?(iterable) } || statements_contain_await?(statement.body) when AST::WhileStmt expression_contains_await?(statement.condition) || statements_contain_await?(statement.body) when AST::ReturnStmt statement.value && expression_contains_await?(statement.value) when AST::DeferStmt (statement.expression && expression_contains_await?(statement.expression)) || (statement.body && statements_contain_await?(statement.body)) when AST::ExpressionStmt expression_contains_await?(statement.expression) else false end end |
#statement_end_line(statement) ⇒ Object
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 184 def statement_end_line(statement) return nil unless statement lines = [statement.line] case statement when AST::ErrorBlockStmt lines.concat(statement_list_lines(statement.body)) when AST::LocalDecl lines << expression_end_line(statement.value) if statement.value lines.concat(statement_list_lines(statement.else_body)) if statement.else_body when AST::IfStmt statement.branches.each do |branch| lines << expression_end_line(branch.condition) lines.concat(statement_list_lines(branch.body)) end lines.concat(statement_list_lines(statement.else_body)) if statement.else_body when AST::UnsafeStmt, AST::ForStmt, AST::WhileStmt lines.concat(statement_list_lines(statement.body)) when AST::MatchStmt statement.arms.each do |arm| if arm.respond_to?(:body) lines.concat(statement_list_lines(arm.body)) else lines << expression_end_line(arm.value) end end when AST::DeferStmt lines.concat(statement_list_lines(statement.body)) if statement.body when AST::Assignment lines << expression_end_line(statement.value) when AST::ReturnStmt lines << expression_end_line(statement.value) when AST::ExpressionStmt lines << expression_end_line(statement.expression) when AST::StaticAssert lines << expression_end_line(statement.condition) end lines.compact.max end |
#statement_list_lines(statements) ⇒ Object
226 227 228 229 230 231 232 233 |
# File 'lib/milk_tea/core/semantic_analyzer/flow_refinement.rb', line 226 def statement_list_lines(statements) return [] unless statements statements.each_with_object([]) do |stmt, lines| end_line = statement_end_line(stmt) lines << end_line if end_line end end |
#statements_contain_await?(statements) ⇒ Boolean
241 242 243 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 241 def statements_contain_await?(statements) statements.any? { |statement| statement_contains_await?(statement) } end |
#static_storage_function_value?(binding) ⇒ Boolean
183 184 185 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 183 def static_storage_function_value?(binding) !binding.external && binding.type_params.empty? end |
#static_storage_member_initializer?(expression, scopes:) ⇒ Boolean
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 125 def static_storage_member_initializer?(expression, scopes:) if (type_expr = resolve_type_expression(expression.receiver)) return true if resolve_type_member(type_expr, expression.member) end return false unless expression.receiver.is_a?(AST::Identifier) return false unless @ctx.imports.key?(expression.receiver.name) imported_module = @ctx.imports.fetch(expression.receiver.name) if imported_module.private_value?(expression.member) || imported_module.private_function?(expression.member) || imported_module.private_type?(expression.member) raise_sema_error("#{expression.receiver.name}.#{expression.member} is private to module #{imported_module.name}") end if (binding = imported_module.values[expression.member]) return true if binding.kind == :const raise_sema_error("module variable initializer cannot reference mutable value #{expression.receiver.name}.#{expression.member}") end function = imported_module.functions[expression.member] function && static_storage_function_value?(function) end |
#stored_ref_supported_type?(type, visited = {}, allow_lifetimes: []) ⇒ Boolean
1135 1136 1137 1138 1139 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1135 def stored_ref_supported_type?(type, visited = {}, allow_lifetimes: []) visitor = StoredRefSupportedVisitor.new(allow_lifetimes:) visitor.visit(type) visitor.result? end |
#str_buffer_capacity(type) ⇒ Object
782 783 784 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 782 def str_buffer_capacity(type) type.arguments.first.value end |
#str_buffer_method_kind(receiver_type, name) ⇒ Object
775 776 777 778 779 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 775 def str_buffer_method_kind(receiver_type, name) return unless str_buffer_type?(receiver_type) STR_BUFFER_METHOD_KINDS[name] end |
#str_buffer_method_name(kind) ⇒ Object
781 782 783 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 781 def str_buffer_method_name(kind) STR_BUFFER_METHOD_NAMES.fetch(kind) end |
#str_buffer_type?(type) ⇒ Boolean
746 747 748 749 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 746 def str_buffer_type?(type) type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 && generic_integer_type_argument?(type.arguments.first) end |
#string_like_type?(type) ⇒ Boolean
918 919 920 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 918 def string_like_type?(type) type == @ctx.types.fetch("str") || type == @ctx.types.fetch("cstr") end |
#string_view_type?(type) ⇒ Boolean
580 581 582 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 580 def string_view_type?(type) type.is_a?(Types::StringView) end |
#struct_declaration_for_type(type) ⇒ Object
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1612 def struct_declaration_for_type(type) return type.ast_declaration if type.respond_to?(:ast_declaration) && type.ast_declaration return nil unless type.respond_to?(:module_name) if type.module_name == @ctx.module_name return @ctx.ast.declarations.find do |decl| decl.is_a?(AST::StructDecl) && decl.name == type.name end end imported_module = imported_module_binding_for_name(type.module_name) return nil unless imported_module declaration = imported_module.type_declarations[type.name] declaration if declaration.is_a?(AST::StructDecl) end |
#struct_handle_for_type(type) ⇒ Object
1602 1603 1604 1605 1606 1607 1608 1609 1610 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1602 def struct_handle_for_type(type) base_type = type.is_a?(Types::StructInstance) ? type.definition : type return nil unless base_type.is_a?(Types::Struct) || base_type.is_a?(Types::GenericStructDefinition) declaration = struct_declaration_for_type(base_type) return nil unless declaration Types::StructHandle.new(base_type, declaration) end |
#struct_instance_type?(type) ⇒ Boolean
843 844 845 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 843 def struct_instance_type?(type) type.is_a?(Types::Struct) || type.is_a?(Types::Variant) end |
#subscription_type?(type) ⇒ Boolean
763 764 765 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 763 def subscription_type?(type) type.is_a?(Types::Subscription) end |
#substitute_type(type, substitutions) ⇒ Object
410 411 412 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 410 def substitute_type(type, substitutions) SubstituteTypeVisitor.new(substitutions).apply(type) end |
#substitute_value_binding(binding, substitutions) ⇒ Object
376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 376 def substitute_value_binding(binding, substitutions) ValueBinding.new( id: binding.id, name: binding.name, storage_type: substitute_type(binding.storage_type, substitutions), flow_type: binding.flow_type ? substitute_type(binding.flow_type, substitutions) : nil, mutable: binding.mutable, kind: binding.kind, const_value: binding.const_value, ) end |
#suggest_name(wrong, candidates, max_distance: 2) ⇒ Object
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 274 def suggest_name(wrong, candidates, max_distance: 2) return nil if wrong.nil? || wrong.to_s.empty? || candidates.nil? || candidates.empty? wrong_str = wrong.to_s closest = nil closest_dist = max_distance + 1 candidates.each do |candidate| next if candidate.nil? cand_str = candidate.to_s next if cand_str.empty? || cand_str == wrong_str dist = levenshtein(wrong_str, cand_str) if dist < closest_dist closest_dist = dist closest = cand_str end end closest_dist <= max_distance ? closest : nil end |
#top_level_event_receiver?(receiver_expression, scopes:) ⇒ Boolean
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 726 def top_level_event_receiver?(receiver_expression, scopes:) case receiver_expression when AST::Identifier binding = lookup_value(receiver_expression.name, scopes) binding&.kind == :event when AST::MemberAccess return false unless receiver_expression.receiver.is_a?(AST::Identifier) && @ctx.imports.key?(receiver_expression.receiver.name) imported_module = @ctx.imports.fetch(receiver_expression.receiver.name) binding = imported_module.values[receiver_expression.member] binding && binding.kind == :event else false end end |
#top_level_function(name) ⇒ Object
421 422 423 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 421 def top_level_function(name) @ctx.top_level_functions[name] end |
#try_record_generic_local_snapshot(stmt, scopes, binding) ⇒ Object
When a generic method body contains a let/var declaration, always record a completion snapshot so hover and completion can discover the local, even when full type inference is impossible. The inferred type is used when available; otherwise the explicit type annotation on the declaration is consulted. As a last resort a fallback primitive type is used so the binding is never silently dropped.
472 473 474 475 476 477 478 479 480 481 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 472 def try_record_generic_local_snapshot(stmt, scopes, binding) type = infer_local_initializer_type(stmt, scopes) || explicit_declaration_type_for(stmt) || fallback_completion_type decl_kind = stmt.kind == :var ? :var : :let vb = value_binding(name: stmt.name, type: type, mutable: stmt.kind == :var, kind: decl_kind) new_scopes = scopes.dup.unshift({ stmt.name => vb }) record_local_completion_snapshot(stmt.line, stmt.column, new_scopes) end |
#type_implements_interface?(type, interface) ⇒ Boolean
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 164 def type_implements_interface?(type, interface) key = interface_implementation_key(type) return true if @ctx.implemented_interfaces.fetch(key, []).include?(interface) @ctx.imports.each_value do |module_binding| return true if module_binding.implemented_interfaces.fetch(key, []).include?(interface) end false end |
#type_satisfies_interface_constraint?(type, interface, available_type_param_constraints: current_type_param_constraints) ⇒ Boolean
175 176 177 178 179 180 181 182 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 175 def type_satisfies_interface_constraint?(type, interface, available_type_param_constraints: current_type_param_constraints) if type.is_a?(Types::TypeVar) constraint = available_type_param_constraints[type.name] return constraint && constraint.interfaces.include?(interface) end type_implements_interface?(type, interface) end |
#typed_null_target_type?(type) ⇒ Boolean
329 330 331 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 329 def typed_null_target_type?(type) type == @ctx.types.fetch("cstr") || pointer_type?(type) || function_pointer_type?(type) end |
#types_compatible?(actual_type, expected_type, expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false) ⇒ Boolean
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 28 def types_compatible?(actual_type, expected_type, expression: nil, scopes: nil, external_numeric: false, external_pointer_null: false, contextual_int_to_float: false) return true if error_type?(actual_type) || error_type?(expected_type) return true if actual_type == expected_type return true if ref_types_compatible?(actual_type, expected_type) return true if null_assignable_to?(actual_type, expected_type) return true if external_pointer_null && external_typed_null_pointer_compatibility?(actual_type, expected_type) return true if expected_type.is_a?(Types::Nullable) && actual_type == expected_type.base return true if mutable_to_const_pointer_compatibility?(actual_type, expected_type) return true if own_to_raw_pointer_compatibility?(actual_type, expected_type) return true if string_literal_cstr_compatibility?(expression, expected_type) return true if exact_compile_time_numeric_compatibility?(actual_type, expression, expected_type, scopes:) return true if integer_to_char_compatibility?(actual_type, expected_type) && (!expression || !scopes || integer_constant_fits_in_char?(expression, scopes)) return true if lossless_integer_compatibility?(actual_type, expected_type) return true if external_numeric && external_numeric_compatibility?(actual_type, expected_type) return true if contextual_int_to_float && contextual_int_to_float_compatibility?(actual_type, expected_type) && (!expression || !scopes || contextual_int_to_float_fits?(expression, expected_type, scopes)) return true if same_external_opaque_handle_pointer_compatibility?(actual_type, expected_type) return true if actual_type.is_a?(Types::Function) && expected_type.is_a?(Types::Function) && !actual_type.receiver_type && !actual_type.variadic && function_type_matches_proc_type?(actual_type, expected_type) return true if quat_vec4_compatible?(actual_type, expected_type) false end |
#unsafe_context? ⇒ Boolean
82 83 84 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 82 def unsafe_context? @unsafe_depth.positive? end |
#unsupported_async_await_context(expression) ⇒ Object
202 203 204 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 202 def unsupported_async_await_context(expression) nil end |
#validate_async_expression_support!(expression, context:) ⇒ Object
195 196 197 198 199 200 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 195 def validate_async_expression_support!(expression, context:) unsupported_context = unsupported_async_await_context(expression) return unless unsupported_context raise_sema_error("await in async functions is not supported inside #{unsupported_context} yet") end |
#validate_async_function_body!(statements) ⇒ Object
125 126 127 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 125 def validate_async_function_body!(statements) statements.each { |statement| validate_async_statement!(statement) } end |
#validate_async_statement!(statement) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 129 def validate_async_statement!(statement) case statement when AST::ErrorBlockStmt if statement.header_expression context = case statement.header_type when :if then "if conditions" when :while then "while conditions" end validate_async_expression_support!(statement.header_expression, context:) if context end if statement.header_type == :for Array(statement.header_iterables).each do |iterable| validate_async_expression_support!(iterable, context: "for iterables") end end statement.body.each { |s| validate_async_statement!(s) } when AST::ErrorStmt nil when AST::LocalDecl validate_async_expression_support!(statement.value, context: "local initializer") if statement.value statement.else_body&.each { |s| validate_async_statement!(s) } when AST::Assignment validate_async_expression_support!(statement.target, context: "assignment target") validate_async_expression_support!(statement.value, context: "assignment") when AST::ExpressionStmt validate_async_expression_support!(statement.expression, context: "expression statement") when AST::ReturnStmt return unless statement.value validate_async_expression_support!(statement.value, context: "return statement") when AST::IfStmt statement.branches.each do |branch| validate_async_expression_support!(branch.condition, context: "if conditions") branch.body.each { |s| validate_async_statement!(s) } end statement.else_body&.each { |s| validate_async_statement!(s) } when AST::WhileStmt validate_async_expression_support!(statement.condition, context: "while conditions") statement.body.each { |s| validate_async_statement!(s) } when AST::ForStmt statement.iterables.each do |iterable| validate_async_expression_support!(iterable, context: "for iterables") end statement.body.each { |s| validate_async_statement!(s) } when AST::MatchStmt validate_async_expression_support!(statement.expression, context: "match discriminants") statement.arms.each { |arm| arm.body.each { |s| validate_async_statement!(s) } } when AST::UnsafeStmt statement.body.each { |s| validate_async_statement!(s) } when AST::DeferStmt validate_async_expression_support!(statement.expression, context: "defer cleanup") if statement.expression statement.body&.each { |s| validate_async_statement!(s) } when AST::WhenStmt statement.branches.each { |branch| branch.body.each { |s| validate_async_statement!(s) } } statement.else_body&.each { |s| validate_async_statement!(s) } when AST::BreakStmt, AST::ContinueStmt, AST::StaticAssert, AST::PassStmt nil else raise_sema_error("async functions currently only support straight-line local declarations, assignments, expression statements, and return statements") end end |
#validate_attribute_target_compatibility!(target, binding) ⇒ Object
1633 1634 1635 1636 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1633 def validate_attribute_target_compatibility!(target, binding) target_kind = attribute_target_kind(target) raise_sema_error("attribute #{qualified_attribute_name(binding)} cannot target #{target_kind}") unless binding.targets.include?(target_kind) end |
#validate_consuming_foreign_expression!(expression, scopes:, root_allowed: false) ⇒ Object
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1020 def validate_consuming_foreign_expression!(expression, scopes:, root_allowed: false) return unless expression return if expression.is_a?(AST::ErrorExpr) if (foreign_call = resolve_foreign_call_expression(expression, scopes:)) && foreign_call_consumes_binding?(foreign_call[:binding]) raise_sema_error("consuming foreign calls must be top-level expression statements") unless root_allowed end case expression when AST::Call, AST::Specialization validate_consuming_foreign_expression!(expression.callee, scopes:, root_allowed: false) expression.arguments.each do |argument| validate_consuming_foreign_expression!(argument.value, scopes:, root_allowed: false) end when AST::UnaryOp validate_consuming_foreign_expression!(expression.operand, scopes:, root_allowed: false) when AST::BinaryOp validate_consuming_foreign_expression!(expression.left, scopes:, root_allowed: false) validate_consuming_foreign_expression!(expression.right, scopes:, root_allowed: false) when AST::IfExpr validate_consuming_foreign_expression!(expression.condition, scopes:, root_allowed: false) validate_consuming_foreign_expression!(expression.then_expression, scopes:, root_allowed: false) validate_consuming_foreign_expression!(expression.else_expression, scopes:, root_allowed: false) when AST::MatchExpr validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false) expression.arms.each do |arm| validate_consuming_foreign_expression!(arm.pattern, scopes:, root_allowed: false) arm_scopes = arm.binding_name ? scopes + [{ arm.binding_name => value_binding(name: arm.binding_name, type: @error_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id)) }] : scopes validate_consuming_foreign_expression!(arm.value, scopes: arm_scopes, root_allowed: false) end when AST::UnsafeExpr validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false) when AST::FormatString expression.parts.each do |part| next unless part.is_a?(AST::FormatExprPart) validate_consuming_foreign_expression!(part.expression, scopes:, root_allowed: false) end when AST::MemberAccess validate_consuming_foreign_expression!(expression.receiver, scopes:, root_allowed: false) when AST::IndexAccess validate_consuming_foreign_expression!(expression.receiver, scopes:, root_allowed: false) validate_consuming_foreign_expression!(expression.index, scopes:, root_allowed: false) when AST::RangeExpr validate_consuming_foreign_expression!(expression.start_expr, scopes:, root_allowed: false) validate_consuming_foreign_expression!(expression.end_expr, scopes:, root_allowed: false) when AST::PrefixCast validate_consuming_foreign_expression!(expression.expression, scopes:, root_allowed: false) end end |
#validate_consuming_foreign_parameter!(type, function_name:, parameter_name:) ⇒ Object
6 7 8 9 10 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 6 def validate_consuming_foreign_parameter!(type, function_name:, parameter_name:) if type.is_a?(Types::Nullable) || !(opaque_type?(type) || pointer_type?(type)) raise_sema_error("consuming parameter #{parameter_name} of #{function_name} must use a non-null opaque or ptr[...] type") end end |
#validate_detach_argument!(expression, scopes:) ⇒ Object
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 716 def validate_detach_argument!(expression, scopes:) return true if expression.is_a?(AST::IntegerLiteral) || expression.is_a?(AST::FloatLiteral) || expression.is_a?(AST::StringLiteral) || expression.is_a?(AST::BooleanLiteral) || expression.is_a?(AST::NullLiteral) || expression.is_a?(AST::CharLiteral) || expression.is_a?(AST::FormatString) if expression.is_a?(AST::Identifier) binding = lookup_value(expression.name, scopes) return true unless binding && %i[let var param].include?(binding.kind) raise_sema_error("detach argument cannot capture local variable #{expression.name}") end true end |
#validate_detach_expression!(expression, scopes:) ⇒ Object
705 706 707 708 709 710 711 712 713 714 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 705 def validate_detach_expression!(expression, scopes:) case expression when AST::Call, AST::Specialization expression.arguments.each do |arg| validate_detach_argument!(arg.value, scopes:) end else raise_sema_error("detach currently only supports global function calls") end end |
#validate_explicit_aggregate_c_name!(decl) ⇒ Object
417 418 419 420 421 422 423 424 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 417 def validate_explicit_aggregate_c_name!(decl) return unless decl.c_name raise_sema_error("explicit C names are only allowed on external structs and unions") unless raw_module? return if !decl.respond_to?(:type_params) || decl.type_params.empty? raise_sema_error("explicit C names are not supported on generic external structs") end |
#validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:) ⇒ Object
69 70 71 72 73 74 75 76 77 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 69 def validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:) return if boundary_type == public_type return if boundary_type == @ctx.types.fetch("cstr") && public_type == @ctx.types.fetch("str") return if foreign_span_boundary_compatible?(public_type, boundary_type) return if foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type) return if foreign_identity_projection_compatible?(public_type, boundary_type) raise_sema_error("foreign parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}") end |
#validate_function_type_param_constraints!(binding, substitutions) ⇒ Object
246 247 248 249 250 251 252 253 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 246 def validate_function_type_param_constraints!(binding, substitutions) binding.type_param_constraints.each do |name, constraints| actual_type = substitutions[name] raise_sema_error("cannot infer type argument #{name} for function #{binding.name}") unless actual_type validate_type_param_constraint_binding!(constraints, actual_type, context: "function #{binding.name}") end end |
#validate_generic_type!(name, arguments) ⇒ Object
835 836 837 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 835 def validate_generic_type!(name, arguments) super(name, arguments) { |msg| raise_sema_error(msg) } end |
#validate_generic_type_param_constraints!(generic_type, arguments, context:, available_type_param_constraints: current_type_param_constraints) ⇒ Object
192 193 194 195 196 197 198 199 200 201 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 192 def validate_generic_type_param_constraints!(generic_type, arguments, context:, available_type_param_constraints: current_type_param_constraints) return if generic_type.type_param_constraints.empty? generic_type.type_params.zip(arguments).each do |name, actual_type| constraints = generic_type.type_param_constraints[name] next unless constraints validate_type_param_constraint_binding!(constraints, actual_type, context:, available_type_param_constraints:) end end |
#validate_hash_operation_argument!(expression, target_type, scopes:, operation:) ⇒ Object
456 457 458 459 460 461 462 463 464 |
# File 'lib/milk_tea/core/semantic_analyzer/calls.rb', line 456 def validate_hash_operation_argument!(expression, target_type, scopes:, operation:) actual_type = infer_expression(expression, scopes:) expected_pointer_type = const_pointer_to(target_type) return if argument_types_compatible?(actual_type, expected_pointer_type, external: false, expression:, scopes:) return if ref_type?(actual_type) && types_compatible?(referenced_type(actual_type), target_type, expression:, scopes:) return if safe_reference_source_expression?(expression, scopes:) && types_compatible?(actual_type, target_type, expression:, scopes:) 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}") end |
#validate_hoistable_foreign_expression!(expression, scopes:, root_hoistable: false) ⇒ Object
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 |
# File 'lib/milk_tea/core/semantic_analyzer/expressions.rb', line 1071 def validate_hoistable_foreign_expression!(expression, scopes:, root_hoistable: false) return unless expression return if expression.is_a?(AST::ErrorExpr) if (foreign_call = resolve_foreign_call_expression(expression, scopes:)) && ( = (foreign_call, scopes:)) raise_sema_error() unless root_hoistable end case expression when AST::Call, AST::Specialization validate_hoistable_foreign_expression!(expression.callee, scopes:, root_hoistable: false) expression.arguments.each do |argument| validate_hoistable_foreign_expression!(argument.value, scopes:, root_hoistable: false) end when AST::UnaryOp validate_hoistable_foreign_expression!(expression.operand, scopes:, root_hoistable: false) when AST::BinaryOp validate_hoistable_foreign_expression!(expression.left, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(expression.right, scopes:, root_hoistable: false) when AST::IfExpr validate_hoistable_foreign_expression!(expression.condition, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(expression.then_expression, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(expression.else_expression, scopes:, root_hoistable: false) when AST::MatchExpr validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false) expression.arms.each do |arm| validate_hoistable_foreign_expression!(arm.pattern, scopes:, root_hoistable: false) arm_scopes = arm.binding_name ? scopes + [{ arm.binding_name => value_binding(name: arm.binding_name, type: @error_type, mutable: false, kind: :local, id: @preassigned_local_binding_ids.fetch(arm.object_id)) }] : scopes validate_hoistable_foreign_expression!(arm.value, scopes: arm_scopes, root_hoistable: false) end when AST::UnsafeExpr validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false) when AST::FormatString expression.parts.each do |part| next unless part.is_a?(AST::FormatExprPart) validate_hoistable_foreign_expression!(part.expression, scopes:, root_hoistable: false) end when AST::MemberAccess validate_hoistable_foreign_expression!(expression.receiver, scopes:, root_hoistable: false) when AST::IndexAccess validate_hoistable_foreign_expression!(expression.receiver, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(expression.index, scopes:, root_hoistable: false) when AST::RangeExpr validate_hoistable_foreign_expression!(expression.start_expr, scopes:, root_hoistable: false) validate_hoistable_foreign_expression!(expression.end_expr, scopes:, root_hoistable: false) when AST::PrefixCast validate_hoistable_foreign_expression!(expression.expression, scopes:, root_hoistable: false) end end |
#validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/milk_tea/core/semantic_analyzer/foreign_functions.rb', line 52 def validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:) unless const_pointer_type?(boundary_type) raise_sema_error("in parameter #{parameter_name} of #{function_name} must lower to const_ptr[...], got #{boundary_type || public_type}") end expected_public_type = pointee_type(boundary_type) return if expected_public_type == public_type return if expected_public_type == @ctx.types.fetch("void") return if foreign_identity_projection_compatible?(public_type, expected_public_type) raise_sema_error("in parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}") end |
#validate_local_proc_type!(type, local_name, initializer:) ⇒ Object
1209 1210 1211 1212 1213 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1209 def validate_local_proc_type!(type, local_name, initializer:) return unless contains_proc_type?(type) raise_sema_error("local #{local_name} uses unsupported proc nesting") unless proc_storage_supported_type?(type) end |
#validate_local_ref_type!(type, local_name) ⇒ Object
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1195 def validate_local_ref_type!(type, local_name) return if ref_type?(type) return if (type.is_a?(Types::Struct) || type.is_a?(Types::StructInstance)) && type.fields.any? && contains_ref_type?(type) return if stored_ref_supported_type?(type) if contains_ref_type?(type) if callable_type?(type) || contains_callable_ref_type?(type) raise_sema_error("local #{local_name} cannot store ref types outside callable parameter positions") end raise_sema_error("local #{local_name} cannot store nested ref types") end end |
#validate_methods_receiver_type_arguments!(type_ref, generic_type) ⇒ Object
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1075 def validate_methods_receiver_type_arguments!(type_ref, generic_type) names = type_ref.arguments.map do |argument| value = argument.value next unless value.is_a?(AST::TypeRef) next unless value.arguments.empty? && !value.nullable && value.name.parts.length == 1 value.name.parts.first end expected_names = generic_type.type_params unless names == expected_names raise_sema_error("extending target #{type_ref} must use the receiver type parameters directly") end expected_names end |
#validate_parameter_proc_type!(type, function_name:, parameter_name:, external:, foreign:) ⇒ Object
1172 1173 1174 1175 1176 1177 1178 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1172 def validate_parameter_proc_type!(type, function_name:, parameter_name:, external:, foreign:) if contains_proc_type?(type) raise_sema_error("external function #{function_name} cannot take proc parameters") if external raise_sema_error("foreign function #{function_name} cannot take proc parameters") if foreign raise_sema_error("parameter #{parameter_name} of #{function_name} uses unsupported proc nesting") unless proc_storage_supported_type?(type) end end |
#validate_parameter_ref_type!(type, function_name:, parameter_name:, external:) ⇒ Object
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1160 def validate_parameter_ref_type!(type, function_name:, parameter_name:, external:) if ref_type?(type) raise_sema_error("external function #{function_name} cannot take ref parameters") if external return end return if callable_param_ref_supported?(type) raise_sema_error("parameter #{parameter_name} of #{function_name} cannot nest ref types") if contains_ref_type?(type) end |
#validate_read_call_arguments!(arguments) ⇒ Object
1295 1296 1297 1298 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1295 def validate_read_call_arguments!(arguments) raise_sema_error("read does not support named arguments") if arguments.any?(&:name) raise_sema_error("read expects 1 argument, got #{arguments.length}") unless arguments.length == 1 end |
#validate_return_proc_type!(type, function_name:) ⇒ Object
1189 1190 1191 1192 1193 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1189 def validate_return_proc_type!(type, function_name:) if contains_proc_type?(type) raise_sema_error("function #{function_name} uses unsupported proc nesting in return type") unless proc_storage_supported_type?(type) end end |
#validate_return_ref_type!(type, function_name:) ⇒ Object
1180 1181 1182 1183 1184 1185 1186 1187 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1180 def validate_return_ref_type!(type, function_name:) if contains_ref_type?(type) if (type.is_a?(Types::Struct) || type.is_a?(Types::StructInstance)) && type.fields.any? raise_sema_error("function #{function_name} cannot return non-owning struct #{type.name} (contains borrowed ref)") end raise_sema_error("function #{function_name} cannot return ref types") end end |
#validate_specialized_function_binding!(function_name, function_type, body_params) ⇒ Object
388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 388 def validate_specialized_function_binding!(function_name, function_type, body_params) function_type.params.each do |param| validate_specialized_function_type!(param.type, function_name:, context: "parameter #{param.name}") validate_specialized_function_type!(param.boundary_type, function_name:, context: "boundary parameter #{param.name}") if param.boundary_type end validate_specialized_function_type!(function_type.return_type, function_name:, context: "return type") validate_specialized_function_type!(function_type.receiver_type, function_name:, context: "receiver type") if function_type.receiver_type body_params.each do |param| validate_specialized_function_type!(param.type, function_name:, context: "body parameter #{param.name}") end end |
#validate_specialized_function_body(binding) ⇒ Object
Validates the body of a specialized (instantiated) function or method binding. The owner checker may be in collecting-errors mode, which would silently swallow body errors into @structural_errors. We temporarily disable collect mode on the owner so the caller receives the SemanticError directly.
269 270 271 272 273 274 275 276 277 278 |
# File 'lib/milk_tea/core/semantic_analyzer/function_binding.rb', line 269 def validate_specialized_function_body(binding) owner = binding.owner prev_collecting = owner.instance_variable_get(:@collecting_errors) owner.instance_variable_set(:@collecting_errors, false) owner.check_function(binding) rescue SemanticError => e raise unless e..include?("cannot assign through immutable") ensure owner.instance_variable_set(:@collecting_errors, prev_collecting) if owner end |
#validate_specialized_function_type!(type, function_name:, context:) ⇒ Object
401 402 403 404 405 406 407 408 |
# File 'lib/milk_tea/core/semantic_analyzer/generics.rb', line 401 def validate_specialized_function_type!(type, function_name:, context:) ValidateSpecializedTypeVisitor.new( function_name:, context:, on_error: ->(msg) { raise_sema_error(msg) }, on_generic_instance: ->(name, args) { validate_generic_type!(name, args) }, ).visit(type) end |
#validate_static_storage_call_initializer!(expression, scopes:) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 148 def validate_static_storage_call_initializer!(expression, scopes:) expression.arguments.each do |argument| validate_static_storage_initializer!(argument.value, scopes:) end callee = expression.callee if callee.is_a?(AST::Identifier) if (type_expr = resolve_type_expression(callee)) return if type_expr.is_a?(Types::Struct) || type_expr.is_a?(Types::StringView) end end if callee.is_a?(AST::MemberAccess) if (type_expr = resolve_type_expression(callee)) return if type_expr.is_a?(Types::Struct) || type_expr.is_a?(Types::StringView) end end if callee.is_a?(AST::Specialization) if callee.callee.is_a?(AST::Identifier) case callee.callee.name when "array", "span", "zero", "reinterpret" return end end if (type_ref = type_ref_from_specialization(callee)) specialized_type = resolve_type_ref(type_ref) return if specialized_type.is_a?(Types::Struct) || result_type?(specialized_type) end end raise_sema_error("module variable initializer must be static-storage-safe") end |
#validate_static_storage_initializer!(expression, scopes:) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/milk_tea/core/semantic_analyzer/top_level.rb', line 69 def validate_static_storage_initializer!(expression, scopes:) case expression when AST::ErrorExpr return when AST::IntegerLiteral, AST::FloatLiteral, AST::StringLiteral, AST::BooleanLiteral, AST::NullLiteral, AST::SizeofExpr, AST::AlignofExpr, AST::OffsetofExpr return when AST::Identifier if (binding = lookup_value(expression.name, scopes)) return if binding.kind == :const raise_sema_error("module variable initializer cannot reference mutable value #{expression.name}") end function = @ctx.top_level_functions[expression.name] return if function && static_storage_function_value?(function) raise_sema_error("module variable initializer must be static-storage-safe") when AST::MemberAccess return if static_storage_member_initializer?(expression, scopes:) raise_sema_error("module variable initializer must be static-storage-safe") when AST::UnaryOp validate_static_storage_initializer!(expression.operand, scopes:) when AST::BinaryOp validate_static_storage_initializer!(expression.left, scopes:) validate_static_storage_initializer!(expression.right, scopes:) when AST::IfExpr validate_static_storage_initializer!(expression.condition, scopes:) validate_static_storage_initializer!(expression.then_expression, scopes:) validate_static_storage_initializer!(expression.else_expression, scopes:) when AST::UnsafeExpr validate_static_storage_initializer!(expression.expression, scopes:) when AST::ExpressionList expression.elements.each { |element| validate_static_storage_initializer!(element, scopes:) } when AST::Specialization if expression.callee.is_a?(AST::Identifier) return if expression.callee.name == "zero" end raise_sema_error("module variable initializer must be static-storage-safe") when AST::Call validate_static_storage_call_initializer!(expression, scopes:) when AST::ProcExpr # Proc expressions are static-storage-safe when their body only # references module-level constants, functions, types, and imports. # The lowering handles capture detection; if a proc truly captures # a local from an enclosing scope (impossible at module level), # the sema validation above already rejects it via the # "cannot reference mutable value" check on the proc body. return else raise_sema_error("module variable initializer must be static-storage-safe") end end |
#validate_stored_ref_type!(type, context, allow_lifetimes: []) ⇒ Object
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1120 def validate_stored_ref_type!(type, context, allow_lifetimes: []) return unless contains_ref_type?(type, allow_lifetimes:) return if stored_ref_supported_type?(type, allow_lifetimes:) if callable_type?(type) || contains_callable_ref_type?(type) raise_sema_error("#{context} cannot store ref types outside callable parameter positions") end if context.start_with?("field ") raise_sema_error("#{context} cannot store ref types; declare a lifetime on the struct and use ref[@lt, T] (e.g. struct MyStruct[@a]: field: ref[@a, SomeType])") end raise_sema_error("#{context} cannot store ref types") end |
#validate_struct_layout!(decl) ⇒ Object
408 409 410 411 412 413 414 415 |
# File 'lib/milk_tea/core/semantic_analyzer/type_declaration.rb', line 408 def validate_struct_layout!(decl) return unless decl.alignment raise_sema_error("align(...) requires a positive alignment") unless decl.alignment.positive? return if power_of_two?(decl.alignment) raise_sema_error("align(...) requires a power-of-two alignment, got #{decl.alignment}") end |
#validate_threaded_for_body!(body) ⇒ Object
1145 1146 1147 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1145 def validate_threaded_for_body!(body) body.each { |stmt| validate_threaded_for_statement!(stmt) } end |
#validate_threaded_for_statement!(stmt) ⇒ Object
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1149 def validate_threaded_for_statement!(stmt) case stmt when AST::BreakStmt raise_sema_error("break is not allowed inside parallel for", line: stmt.line, column: stmt.column) when AST::ContinueStmt raise_sema_error("continue is not allowed inside parallel for", line: stmt.line, column: stmt.column) when AST::ReturnStmt raise_sema_error("return is not allowed inside parallel for", line: stmt.line, column: stmt.column) when AST::DeferStmt raise_sema_error("defer is not allowed inside parallel for", line: stmt.line, column: stmt.column) when AST::AwaitExpr raise_sema_error("await is not allowed inside parallel for", line: stmt.line, column: stmt.column) when AST::IfStmt validate_threaded_for_body!(stmt.then_body) stmt.else_if_clauses&.each { |clause| validate_threaded_for_body!(clause.body) } validate_threaded_for_body!(stmt.else_body) if stmt.else_body when AST::WhileStmt validate_threaded_for_body!(stmt.body) when AST::ForStmt raise_sema_error("nested for loops are not allowed inside parallel for", line: stmt.line, column: stmt.column) if stmt.threaded validate_threaded_for_body!(stmt.body) when AST::MatchStmt stmt.arms&.each { |arm| validate_threaded_for_body!(arm.body) } when AST::UnsafeStmt validate_threaded_for_body!(stmt.body) if stmt.body.is_a?(Array) end end |
#validate_type_param_constraint_binding!(constraints, actual_type, context:, available_type_param_constraints: current_type_param_constraints) ⇒ Object
184 185 186 187 188 189 190 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 184 def validate_type_param_constraint_binding!(constraints, actual_type, context:, available_type_param_constraints: current_type_param_constraints) constraints.interfaces.each do |interface| next if type_satisfies_interface_constraint?(actual_type, interface, available_type_param_constraints:) raise_sema_error("type #{actual_type} does not implement interface #{interface.name} for #{context}") end end |
#value_binding(name:, type:, mutable:, kind:, flow_type: nil, const_value: nil, id: nil) ⇒ Object
1362 1363 1364 1365 1366 1367 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 1362 def value_binding(name:, type:, mutable:, kind:, flow_type: nil, const_value: nil, id: nil) id ||= allocate_binding_id @binding_name_by_id[id] = name @binding_type_by_id[id] = flow_type == type ? type : (flow_type || type) ValueBinding.new(id:, name:, storage_type: type, flow_type: flow_type == type ? nil : flow_type, mutable:, kind:, const_value:) end |
#variant_match_arm_name(pattern, scrutinee_type) ⇒ Object
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 936 def variant_match_arm_name(pattern, scrutinee_type) # Pattern must be `TypeName.arm_name` or `module.TypeName.arm_name` # For struct patterns, the pattern is Call(MemberAccess(...), args) — unwrap the callee callee = case pattern when AST::Call pattern.callee else pattern end return nil unless callee.is_a?(AST::MemberAccess) member = callee.member return nil unless scrutinee_type.arm_names.include?(member) # Verify the receiver resolves to the scrutinee variant type receiver_type = resolve_type_expression(callee.receiver) return member if receiver_type == scrutinee_type if scrutinee_type.is_a?(Types::VariantInstance) && receiver_type.is_a?(Types::GenericVariantDefinition) return member if receiver_type == scrutinee_type.definition end return nil unless scrutinee_type.is_a?(Types::VariantInstance) && receiver_type.is_a?(Types::Variant) return nil unless receiver_type.name == scrutinee_type.name && receiver_type.module_name == scrutinee_type.module_name member end |
#vector_arithmetic_result(operator, left_type, right_type) ⇒ Object
215 216 217 218 219 220 221 222 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 215 def vector_arithmetic_result(operator, left_type, right_type) return vector_op_result(operator, left_type, right_type) if vector_type?(left_type) || vector_type?(right_type) return matrix_op_result(operator, left_type, right_type) if matrix_type?(left_type) || matrix_type?(right_type) return quaternion_op_result(operator, left_type, right_type) if quaternion_type?(left_type) || quaternion_type?(right_type) return simd_op_result(operator, left_type, right_type) if simd_type?(left_type) || simd_type?(right_type) nil end |
#vector_op_result(operator, left_type, right_type) ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 260 def vector_op_result(operator, left_type, right_type) if vector_type?(left_type) && vector_type?(right_type) && left_type.element_type == right_type.element_type return left_type if operator == "+" || operator == "-" return left_type if operator == "*" end if vector_type?(left_type) && right_type.numeric? return left_type if operator == "*" || operator == "/" end if left_type.numeric? && vector_type?(right_type) && operator == "*" return right_type end nil end |
#vector_type?(type) ⇒ Boolean
705 706 707 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 705 def vector_type?(type) type.is_a?(Types::Vector) end |
#walk_assignment_target_reads_for_precheck_resolution(target, operator, scopes, identifier_ids, declaration_ids = nil) ⇒ Object
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 389 def walk_assignment_target_reads_for_precheck_resolution(target, operator, scopes, identifier_ids, declaration_ids = nil) if operator != "=" && target.is_a?(AST::Identifier) if (binding_id = resolve_name_in_precheck_scopes(target.name, scopes)) identifier_ids[target.object_id] = binding_id end end case target when AST::Identifier nil when AST::MemberAccess walk_expression_for_precheck_resolution(target.receiver, scopes, identifier_ids, declaration_ids) when AST::IndexAccess walk_expression_for_precheck_resolution(target.receiver, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(target.index, scopes, identifier_ids, declaration_ids) else walk_expression_for_precheck_resolution(target, scopes, identifier_ids, declaration_ids) end end |
#walk_expression_for_precheck_resolution(expression, scopes, identifier_ids, declaration_ids = nil) ⇒ Object
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 334 def walk_expression_for_precheck_resolution(expression, scopes, identifier_ids, declaration_ids = nil) case expression when nil nil when AST::Identifier if (binding_id = resolve_name_in_precheck_scopes(expression.name, scopes)) identifier_ids[expression.object_id] = binding_id end when AST::MemberAccess walk_expression_for_precheck_resolution(expression.receiver, scopes, identifier_ids, declaration_ids) when AST::IndexAccess walk_expression_for_precheck_resolution(expression.receiver, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(expression.index, scopes, identifier_ids, declaration_ids) when AST::Specialization walk_expression_for_precheck_resolution(expression.callee, scopes, identifier_ids, declaration_ids) when AST::Call walk_expression_for_precheck_resolution(expression.callee, scopes, identifier_ids, declaration_ids) expression.arguments.each { |argument| walk_expression_for_precheck_resolution(argument.value, scopes, identifier_ids, declaration_ids) } when AST::UnaryOp walk_expression_for_precheck_resolution(expression.operand, scopes, identifier_ids, declaration_ids) when AST::BinaryOp walk_expression_for_precheck_resolution(expression.left, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(expression.right, scopes, identifier_ids, declaration_ids) when AST::RangeExpr walk_expression_for_precheck_resolution(expression.start_expr, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(expression.end_expr, scopes, identifier_ids, declaration_ids) when AST::IfExpr walk_expression_for_precheck_resolution(expression.condition, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(expression.then_expression, scopes, identifier_ids, declaration_ids) walk_expression_for_precheck_resolution(expression.else_expression, scopes, identifier_ids, declaration_ids) when AST::MatchExpr walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids) expression.arms.each do |arm| walk_expression_for_precheck_resolution(arm.pattern, scopes, identifier_ids, declaration_ids) arm_scopes = scopes if arm.binding_name binding_id = @preassigned_local_binding_ids.fetch(arm.object_id) arm_scopes = scopes + [{ arm.binding_name => binding_id }] declaration_ids[arm.object_id] = binding_id if declaration_ids end walk_expression_for_precheck_resolution(if arm.respond_to?(:value) then arm.value else arm.body end, arm_scopes, identifier_ids, declaration_ids) end when AST::UnsafeExpr walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids) when AST::AwaitExpr walk_expression_for_precheck_resolution(expression.expression, scopes, identifier_ids, declaration_ids) when AST::FormatString expression.parts.each do |part| next unless part.is_a?(AST::FormatExprPart) walk_expression_for_precheck_resolution(part.expression, scopes, identifier_ids, declaration_ids) end end end |
#walk_statements_for_precheck_resolution(statements, scopes, declaration_ids, identifier_ids) ⇒ Object
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/milk_tea/core/semantic_analyzer/nullability.rb', line 246 def walk_statements_for_precheck_resolution(statements, scopes, declaration_ids, identifier_ids) block_scopes = scopes + [{}] statements.each do |statement| case statement when AST::ErrorBlockStmt if statement.header_type == :for Array(statement.header_iterables).each do |iterable| walk_expression_for_precheck_resolution(iterable, block_scopes, identifier_ids, declaration_ids) end for_scopes = block_scopes + [{}] Array(statement.header_bindings).each do |binding| binding_id = @preassigned_local_binding_ids.fetch(binding.object_id) for_scopes.last[binding.name] = binding_id declaration_ids[binding.object_id] = binding_id end walk_statements_for_precheck_resolution(statement.body || [], for_scopes, declaration_ids, identifier_ids) else walk_expression_for_precheck_resolution(statement.header_expression, block_scopes, identifier_ids, declaration_ids) if statement.header_expression walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids) end when AST::LocalDecl walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids) if statement.value if statement.else_binding && (statement.else_body || statement.recovered_else) else_scopes = block_scopes + [{}] binding_id = @preassigned_local_binding_ids.fetch(statement.else_binding.object_id) else_scopes.last[statement.else_binding.name] = binding_id declaration_ids[statement.else_binding.object_id] = binding_id walk_statements_for_precheck_resolution(statement.else_body || [], else_scopes, declaration_ids, identifier_ids) else walk_statements_for_precheck_resolution(statement.else_body || [], block_scopes, declaration_ids, identifier_ids) end binding_id = @preassigned_local_binding_ids.fetch(statement.object_id) unless let_else_discard_binding_syntax?(statement) block_scopes.last[statement.name] = binding_id declaration_ids[statement.object_id] = binding_id end when AST::Assignment walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids) walk_assignment_target_reads_for_precheck_resolution(statement.target, statement.operator, block_scopes, identifier_ids, declaration_ids) if statement.target.is_a?(AST::Identifier) if (binding_id = resolve_name_in_precheck_scopes(statement.target.name, block_scopes)) identifier_ids[statement.target.object_id] = binding_id end end when AST::IfStmt statement.branches.each do |branch| walk_expression_for_precheck_resolution(branch.condition, block_scopes, identifier_ids, declaration_ids) walk_statements_for_precheck_resolution(branch.body || [], block_scopes, declaration_ids, identifier_ids) end walk_statements_for_precheck_resolution(statement.else_body || [], block_scopes, declaration_ids, identifier_ids) when AST::MatchStmt walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids) statement.arms.each do |arm| arm_scopes = block_scopes + [{}] if arm.binding_name binding_id = @preassigned_local_binding_ids.fetch(arm.object_id) arm_scopes.last[arm.binding_name] = binding_id declaration_ids[arm.object_id] = binding_id end walk_statements_for_precheck_resolution(if arm.respond_to?(:body) then (arm.body || []) else [arm.value].compact end, arm_scopes, declaration_ids, identifier_ids) end when AST::UnsafeStmt, AST::WhileStmt walk_expression_for_precheck_resolution(statement.condition, block_scopes, identifier_ids, declaration_ids) if statement.is_a?(AST::WhileStmt) walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids) when AST::ForStmt statement.iterables.each do |iterable| walk_expression_for_precheck_resolution(iterable, block_scopes, identifier_ids, declaration_ids) end for_scopes = block_scopes + [{}] statement.bindings.each do |binding| binding_id = @preassigned_local_binding_ids.fetch(binding.object_id) for_scopes.last[binding.name] = binding_id declaration_ids[binding.object_id] = binding_id end walk_statements_for_precheck_resolution(statement.body || [], for_scopes, declaration_ids, identifier_ids) when AST::DeferStmt walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids) if statement.expression walk_statements_for_precheck_resolution(statement.body || [], block_scopes, declaration_ids, identifier_ids) if statement.body when AST::ExpressionStmt walk_expression_for_precheck_resolution(statement.expression, block_scopes, identifier_ids, declaration_ids) when AST::ReturnStmt walk_expression_for_precheck_resolution(statement.value, block_scopes, identifier_ids, declaration_ids) if statement.value when AST::StaticAssert walk_expression_for_precheck_resolution(statement.condition, block_scopes, identifier_ids, declaration_ids) end end end |
#when_chosen_body(decl) ⇒ Object
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 1239 def when_chosen_body(decl) discriminant_value = evaluate_compile_time_const_value(decl.discriminant, scopes: []) return nil if discriminant_value.nil? chosen_branch = decl.branches.find do |branch| pattern_value = evaluate_compile_time_const_value(branch.pattern, scopes: []) discriminant_value == pattern_value end chosen_branch&.body || decl.else_body end |
#wider_float_type(left_type, right_type) ⇒ Object
195 196 197 |
# File 'lib/milk_tea/core/semantic_analyzer/type_compatibility.rb', line 195 def wider_float_type(left_type, right_type) left_type.float_width >= right_type.float_width ? left_type : right_type end |
#wildcard_pattern?(expression) ⇒ Boolean
773 774 775 |
# File 'lib/milk_tea/core/semantic_analyzer/statements.rb', line 773 def wildcard_pattern?(expression) expression.is_a?(AST::Identifier) && expression.name == "_" end |
#with_async_function ⇒ Object
41 42 43 44 45 46 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 41 def with_async_function @async_function_depth += 1 yield ensure @async_function_depth -= 1 end |
#with_compile_time ⇒ Object
55 56 57 58 59 60 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 55 def with_compile_time @compile_time_depth += 1 yield ensure @compile_time_depth -= 1 end |
#with_error_node(node) ⇒ Object
492 493 494 495 496 497 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 492 def with_error_node(node) @error_node_stack << node yield ensure @error_node_stack.pop end |
#with_foreign_mapping_context ⇒ Object
34 35 36 37 38 39 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 34 def with_foreign_mapping_context @foreign_mapping_depth += 1 yield ensure @foreign_mapping_depth -= 1 end |
#with_loop ⇒ Object
48 49 50 51 52 53 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 48 def with_loop @loop_depth += 1 yield ensure @loop_depth -= 1 end |
#with_loop_barrier ⇒ Object
74 75 76 77 78 79 80 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 74 def previous_loop_depth = @loop_depth @loop_depth = 0 yield ensure @loop_depth = previous_loop_depth end |
#with_nested_scope(scopes) {|nested_scopes| ... } ⇒ Object
120 121 122 123 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 120 def with_nested_scope(scopes) nested_scopes = scopes + [{}] yield(nested_scopes) end |
#with_return_context(return_type, allow_return:) ⇒ Object
98 99 100 101 102 103 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 98 def with_return_context(return_type, allow_return:) @return_context_stack << { return_type:, allow_return: } yield ensure @return_context_stack.pop end |
#with_scope(bindings) {|[scope]| ... } ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 109 def with_scope(bindings) scope = {} bindings.each do |binding| raise_sema_error("duplicate local #{binding.name}") if scope.key?(binding.name) scope[binding.name] = binding end yield([scope]) end |
#with_type_resolution_scopes(scopes) ⇒ Object
Tracks the innermost value scopes during body checking so that
resolve_type_ref can resolve a compile-time reflection type expression
(e.g. field.type inside an inline for) by evaluating it against the
local bindings in scope. Nil outside body checking.
66 67 68 69 70 71 72 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 66 def with_type_resolution_scopes(scopes) saved = @type_resolution_scopes @type_resolution_scopes = scopes yield ensure @type_resolution_scopes = saved end |
#with_unsafe ⇒ Object
6 7 8 9 10 11 |
# File 'lib/milk_tea/core/semantic_analyzer/analysis_context.rb', line 6 def with_unsafe @unsafe_depth += 1 yield ensure @unsafe_depth -= 1 end |
#zero_initializable_type?(type, operation: "zero") ⇒ Boolean
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 |
# File 'lib/milk_tea/core/semantic_analyzer/name_resolution.rb', line 668 def zero_initializable_type?(type, operation: "zero") return true if type.is_a?(Types::Primitive) && !type.void? return true if type.is_a?(Types::Nullable) return true if type.is_a?(Types::EnumBase) return true if span_type?(type) return true if string_view_type?(type) return true if task_type?(type) return true if event_type?(type) return true if subscription_type?(type) return true if type.is_a?(Types::Struct) return true if type.is_a?(Types::Variant) return true if pointer_type?(type) return true if type.is_a?(Types::Opaque) && !type.external return true if array_type?(type) return true if str_buffer_type?(type) return true if vector_type?(type) return true if matrix_type?(type) return true if quaternion_type?(type) return true if soa_type?(type) return true if simd_type?(type) return true if atomic_type?(type) raise_sema_error("#{operation} does not support type #{type}") end |