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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/c_backend/feature_detection.rb +27 -55
- data/lib/milk_tea/core/c_backend/format_helpers.rb +551 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +0 -542
- data/lib/milk_tea/core/c_backend/type_collectors.rb +51 -204
- data/lib/milk_tea/core/c_backend.rb +2 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +1 -1
- data/lib/milk_tea/core/lowering/async/lowering.rb +147 -141
- data/lib/milk_tea/core/lowering/block.rb +759 -725
- data/lib/milk_tea/core/lowering/expressions.rb +94 -0
- data/lib/milk_tea/core/lowering/loops.rb +49 -0
- data/lib/milk_tea/core/lowering/resolve.rb +221 -214
- data/lib/milk_tea/core/lowering/utils.rb +0 -222
- data/lib/milk_tea/core/module_loader.rb +29 -48
- data/lib/milk_tea/core/parser/declarations.rb +2 -2
- data/lib/milk_tea/core/parser/recovery.rb +1 -1
- data/lib/milk_tea/core/parser.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +3 -120
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +116 -113
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +346 -343
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +37 -12
- data/lib/milk_tea/core/types/predicates.rb +501 -499
- data/lib/milk_tea/core/types/visitor.rb +1 -1
- metadata +3 -2
|
@@ -1,155 +1,158 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MilkTea
|
|
4
|
-
class SemanticAnalyzer
|
|
5
|
-
|
|
4
|
+
class SemanticAnalyzer
|
|
5
|
+
class Checker
|
|
6
|
+
private
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
19
|
+
pointer_type?(base) || const_pointer_type?(base) || ref_type?(base)
|
|
20
|
+
end
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
51
|
+
public_type
|
|
52
|
+
end
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
67
|
+
def foreign_mapping_public_alias_name(name)
|
|
68
|
+
"#{name}_public"
|
|
69
|
+
end
|
|
86
70
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
115
|
-
|
|
85
|
+
def foreign_mapping_expression(decl)
|
|
86
|
+
return decl.mapping unless foreign_mapping_auto_call_shorthand?(decl.mapping)
|
|
116
87
|
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|