mt-lang 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,155 +1,158 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MilkTea
4
- class SemanticAnalyzer::Checker
5
- private
4
+ class SemanticAnalyzer
5
+ class Checker
6
+ private
6
7
 
7
- def validate_consuming_foreign_parameter!(type, function_name:, parameter_name:)
8
- if type.is_a?(Types::Nullable) || !(opaque_type?(type) || pointer_type?(type))
9
- raise_sema_error("consuming parameter #{parameter_name} of #{function_name} must use a non-null opaque or ptr[...] type")
8
+ def validate_consuming_foreign_parameter!(type, function_name:, parameter_name:)
9
+ if type.is_a?(Types::Nullable) || !(opaque_type?(type) || pointer_type?(type))
10
+ raise_sema_error("consuming parameter #{parameter_name} of #{function_name} must use a non-null opaque or ptr[...] type")
11
+ end
10
12
  end
11
- end
12
-
13
- def foreign_boundary_nullable_pointer_like_base?(base)
14
- return true if base.is_a?(Types::Function) || base.is_a?(Types::Proc)
15
- return true if opaque_type?(base)
16
- return true if base == @ctx.types.fetch("cstr")
17
13
 
18
- pointer_type?(base) || const_pointer_type?(base) || ref_type?(base)
19
- end
20
-
21
- def reject_foreign_nullable_value_type!(type, function_name:, parameter_name:)
22
- return unless type.is_a?(Types::Nullable)
23
- return if foreign_boundary_nullable_pointer_like_base?(type.base)
14
+ def foreign_boundary_nullable_pointer_like_base?(base)
15
+ return true if base.is_a?(Types::Function) || base.is_a?(Types::Proc)
16
+ return true if opaque_type?(base)
17
+ return true if base == @ctx.types.fetch("cstr")
24
18
 
25
- location = parameter_name ? "parameter #{parameter_name}" : "return type"
26
- raise_sema_error("#{location} of foreign/external function #{function_name} cannot use nullable value type #{type}; nullable at an FFI boundary is only supported for pointer-like types (use ptr[...]? or pass an explicit struct)")
27
- end
19
+ pointer_type?(base) || const_pointer_type?(base) || ref_type?(base)
20
+ end
28
21
 
29
- def foreign_cstr_boundary_parameter?(parameter)
30
- parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
31
- end
22
+ def reject_foreign_nullable_value_type!(type, function_name:, parameter_name:)
23
+ return unless type.is_a?(Types::Nullable)
24
+ return if foreign_boundary_nullable_pointer_like_base?(type.base)
32
25
 
33
- def foreign_cstr_argument_compatible?(actual_type, parameter, expression:)
34
- types_compatible?(actual_type, parameter.type, expression:) || actual_type == @ctx.types.fetch("cstr")
35
- end
36
-
37
- def foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints)
38
- return resolve_type_ref(param.boundary_type, type_params:, type_param_constraints:) if param.boundary_type
39
- return const_pointer_to(public_type) if param.mode == :in
40
- return pointer_to(foreign_slot_boundary_value_type(public_type)) if [:out, :inout].include?(param.mode)
26
+ location = parameter_name ? "parameter #{parameter_name}" : "return type"
27
+ 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)")
28
+ end
41
29
 
42
- nil
43
- end
30
+ def foreign_cstr_boundary_parameter?(parameter)
31
+ parameter.boundary_type == @ctx.types.fetch("cstr") && parameter.type == @ctx.types.fetch("str")
32
+ end
44
33
 
45
- def foreign_slot_boundary_value_type(public_type)
46
- if public_type.is_a?(Types::Nullable) && pointer_type?(public_type.base)
47
- return public_type.base
34
+ def foreign_cstr_argument_compatible?(actual_type, parameter, expression:)
35
+ types_compatible?(actual_type, parameter.type, expression:) || actual_type == @ctx.types.fetch("cstr")
48
36
  end
49
37
 
50
- public_type
51
- end
38
+ def foreign_parameter_boundary_type(param, public_type, type_params:, type_param_constraints: current_type_param_constraints)
39
+ return resolve_type_ref(param.boundary_type, type_params:, type_param_constraints:) if param.boundary_type
40
+ return const_pointer_to(public_type) if param.mode == :in
41
+ return pointer_to(foreign_slot_boundary_value_type(public_type)) if [:out, :inout].include?(param.mode)
52
42
 
53
- def validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:)
54
- unless const_pointer_type?(boundary_type)
55
- raise_sema_error("in parameter #{parameter_name} of #{function_name} must lower to const_ptr[...], got #{boundary_type || public_type}")
43
+ nil
56
44
  end
57
45
 
58
- expected_public_type = pointee_type(boundary_type)
59
- return if expected_public_type == public_type
60
- return if expected_public_type == @ctx.types.fetch("void")
61
- return if foreign_identity_projection_compatible?(public_type, expected_public_type)
62
-
63
- raise_sema_error("in parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
64
- end
46
+ def foreign_slot_boundary_value_type(public_type)
47
+ if public_type.is_a?(Types::Nullable) && pointer_type?(public_type.base)
48
+ return public_type.base
49
+ end
65
50
 
66
- def foreign_mapping_public_alias_name(name)
67
- "#{name}_public"
68
- end
51
+ public_type
52
+ end
69
53
 
70
- def validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:)
71
- return if boundary_type == public_type
72
- return if boundary_type == @ctx.types.fetch("cstr") && public_type == @ctx.types.fetch("str")
73
- return if foreign_span_boundary_compatible?(public_type, boundary_type)
74
- return if foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type)
75
- return if foreign_identity_projection_compatible?(public_type, boundary_type)
54
+ def validate_in_foreign_parameter!(public_type, boundary_type, function_name:, parameter_name:)
55
+ unless const_pointer_type?(boundary_type)
56
+ raise_sema_error("in parameter #{parameter_name} of #{function_name} must lower to const_ptr[...], got #{boundary_type || public_type}")
57
+ end
76
58
 
77
- raise_sema_error("foreign parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
78
- end
59
+ expected_public_type = pointee_type(boundary_type)
60
+ return if expected_public_type == public_type
61
+ return if expected_public_type == @ctx.types.fetch("void")
62
+ return if foreign_identity_projection_compatible?(public_type, expected_public_type)
79
63
 
80
- def foreign_function_binding?(binding)
81
- binding.ast.is_a?(AST::ForeignFunctionDecl)
82
- end
64
+ raise_sema_error("in parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
65
+ end
83
66
 
84
- def foreign_mapping_expression(decl)
85
- return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping)
67
+ def foreign_mapping_public_alias_name(name)
68
+ "#{name}_public"
69
+ end
86
70
 
87
- AST::Call.new(
88
- callee: decl.mapping,
89
- arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) },
90
- )
91
- end
71
+ def validate_foreign_boundary_type!(public_type, boundary_type, function_name:, parameter_name:)
72
+ return if boundary_type == public_type
73
+ return if boundary_type == @ctx.types.fetch("cstr") && public_type == @ctx.types.fetch("str")
74
+ return if foreign_span_boundary_compatible?(public_type, boundary_type)
75
+ return if foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type)
76
+ return if foreign_identity_projection_compatible?(public_type, boundary_type)
92
77
 
93
- def foreign_mapping_auto_call_shorthand?(expression)
94
- case expression
95
- when AST::Identifier
96
- true
97
- when AST::MemberAccess
98
- foreign_mapping_auto_call_shorthand?(expression.receiver)
99
- when AST::Specialization
100
- foreign_mapping_auto_call_shorthand?(expression.callee)
101
- else
102
- false
78
+ raise_sema_error("foreign parameter #{parameter_name} of #{function_name} cannot map #{public_type} as #{boundary_type}")
103
79
  end
104
- end
105
80
 
106
- def foreign_argument_expression(argument)
107
- if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
108
- argument.value.operand
109
- else
110
- argument.value
81
+ def foreign_function_binding?(binding)
82
+ binding.ast.is_a?(AST::ForeignFunctionDecl)
111
83
  end
112
- end
113
84
 
114
- def foreign_argument_legacy_passing_mode(argument)
115
- return nil unless argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
85
+ def foreign_mapping_expression(decl)
86
+ return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping)
116
87
 
117
- argument.value.operator
118
- end
88
+ AST::Call.new(
89
+ callee: decl.mapping,
90
+ arguments: decl.params.map { |param| AST::Argument.new(name: nil, value: AST::Identifier.new(name: param.name)) },
91
+ )
92
+ end
119
93
 
120
- def foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type)
121
- case parameter.passing_mode
122
- when :plain
123
- infer_expression(argument.value, scopes:, expected_type:)
124
- when :consuming
125
- foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
126
- parameter.type
127
- when :in, :out, :inout
128
- if (legacy_passing_mode = foreign_argument_legacy_passing_mode(argument))
129
- raise_sema_error("argument #{parameter.name} to #{function_name} must not use #{legacy_passing_mode}; directional passing is declared on #{function_name}")
94
+ def foreign_mapping_auto_call_shorthand?(expression)
95
+ case expression
96
+ when AST::Identifier
97
+ true
98
+ when AST::MemberAccess
99
+ foreign_mapping_auto_call_shorthand?(expression.receiver)
100
+ when AST::Specialization
101
+ foreign_mapping_auto_call_shorthand?(expression.callee)
102
+ else
103
+ false
130
104
  end
105
+ end
131
106
 
132
- if parameter.passing_mode == :in
133
- infer_expression(argument.value, scopes:, expected_type: expected_type)
107
+ def foreign_argument_expression(argument)
108
+ if argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
109
+ argument.value.operand
134
110
  else
135
- infer_lvalue(argument.value, scopes:)
111
+ argument.value
136
112
  end
137
- else
138
- raise_sema_error("unsupported foreign passing mode #{parameter.passing_mode}")
139
113
  end
140
- end
141
114
 
142
- def foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
143
- unless argument.value.is_a?(AST::Identifier)
144
- raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
115
+ def foreign_argument_legacy_passing_mode(argument)
116
+ return nil unless argument.value.is_a?(AST::UnaryOp) && ["out", "in", "inout"].include?(argument.value.operator)
117
+
118
+ argument.value.operator
145
119
  end
146
120
 
147
- binding = lookup_value(argument.value.name, scopes)
148
- unless binding && %i[let var param].include?(binding.kind) && binding.storage_type.is_a?(Types::Nullable) && binding.storage_type.base == parameter.type
149
- raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
121
+ def foreign_argument_actual_type(parameter, argument, scopes:, function_name:, expected_type: parameter.type)
122
+ case parameter.passing_mode
123
+ when :plain
124
+ infer_expression(argument.value, scopes:, expected_type:)
125
+ when :consuming
126
+ foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
127
+ parameter.type
128
+ when :in, :out, :inout
129
+ if (legacy_passing_mode = foreign_argument_legacy_passing_mode(argument))
130
+ raise_sema_error("argument #{parameter.name} to #{function_name} must not use #{legacy_passing_mode}; directional passing is declared on #{function_name}")
131
+ end
132
+
133
+ if parameter.passing_mode == :in
134
+ infer_expression(argument.value, scopes:, expected_type: expected_type)
135
+ else
136
+ infer_lvalue(argument.value, scopes:)
137
+ end
138
+ else
139
+ raise_sema_error("unsupported foreign passing mode #{parameter.passing_mode}")
140
+ end
150
141
  end
151
142
 
152
- binding
143
+ def foreign_consuming_argument_binding(parameter, argument, scopes:, function_name:)
144
+ unless argument.value.is_a?(AST::Identifier)
145
+ raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
146
+ end
147
+
148
+ binding = lookup_value(argument.value.name, scopes)
149
+ unless binding && %i[let var param].include?(binding.kind) && binding.storage_type.is_a?(Types::Nullable) && binding.storage_type.base == parameter.type
150
+ raise_sema_error("consuming argument #{parameter.name} to #{function_name} must be a bare nullable local or parameter binding")
151
+ end
152
+
153
+ binding
154
+ end
153
155
  end
156
+
154
157
  end
155
158
  end