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
|
@@ -39,50 +39,6 @@ module MilkTea
|
|
|
39
39
|
callee.is_a?(AST::MemberAccess) ? callee.member : nil
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def async_variant_match_arm_binding(arm, scrutinee_expr, scrutinee_type, env:, frame_expr: nil, local_fields: nil)
|
|
43
|
-
arm_env = duplicate_env(env)
|
|
44
|
-
binding_decl = nil
|
|
45
|
-
|
|
46
|
-
if arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
|
|
47
|
-
arm_name = variant_match_arm_name_from_pattern(arm.pattern)
|
|
48
|
-
if arm_name && scrutinee_type.has_payload?(arm_name)
|
|
49
|
-
fields = scrutinee_type.arm(arm_name)
|
|
50
|
-
payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
|
|
51
|
-
|
|
52
|
-
field_key = async_match_binding_field_key(arm)
|
|
53
|
-
field_info = local_fields&.fetch(field_key, nil)
|
|
54
|
-
if field_info && frame_expr
|
|
55
|
-
target = async_frame_field_expression(frame_expr, field_info[:field_name], field_info[:storage_type])
|
|
56
|
-
binding_c = async_frame_field_c_name(field_info[:field_name])
|
|
57
|
-
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
|
|
58
|
-
data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
|
|
59
|
-
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
60
|
-
binding_decl = IR::Assignment.new(target:, operator: "=", value: arm_expr)
|
|
61
|
-
else
|
|
62
|
-
data_expr = IR::Member.new(receiver: scrutinee_expr, member: "data", type: nil)
|
|
63
|
-
arm_expr = IR::Member.new(receiver: data_expr, member: arm_name, type: payload_type)
|
|
64
|
-
binding_c = c_local_name(arm.binding_name)
|
|
65
|
-
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: binding_c, mutable: false, pointer: false)
|
|
66
|
-
binding_decl = IR::LocalDecl.new(name: arm.binding_name, linkage_name: binding_c, type: payload_type, value: arm_expr)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
[arm_env, binding_decl]
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
|
|
75
|
-
return unless scrutinee_type.is_a?(Types::Variant)
|
|
76
|
-
return unless arm.binding_name && !wildcard_arm_pattern?(arm.pattern)
|
|
77
|
-
|
|
78
|
-
arm_name = variant_match_arm_name_from_pattern(arm.pattern)
|
|
79
|
-
return unless arm_name && scrutinee_type.has_payload?(arm_name)
|
|
80
|
-
|
|
81
|
-
fields = scrutinee_type.arm(arm_name)
|
|
82
|
-
payload_type = Types::VariantArmPayload.new(scrutinee_type, arm_name, fields)
|
|
83
|
-
arm_env[:scopes].last[arm.binding_name] = local_binding(type: payload_type, linkage_name: c_local_name(arm.binding_name), mutable: true, pointer: false)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
42
|
def array_type?(type)
|
|
87
43
|
type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
|
|
88
44
|
type.arguments[1].is_a?(Types::LiteralTypeArg)
|
|
@@ -213,56 +169,6 @@ module MilkTea
|
|
|
213
169
|
super
|
|
214
170
|
end
|
|
215
171
|
|
|
216
|
-
def iterator_loop_info(type, env:)
|
|
217
|
-
iter_name = "__mt_for_iterable__"
|
|
218
|
-
iterator_name = "__mt_for_iterator__"
|
|
219
|
-
probe_env = duplicate_env(env)
|
|
220
|
-
current_actual_scope(probe_env[:scopes])[iter_name] = local_binding(type:, linkage_name: iter_name, mutable: false, pointer: false)
|
|
221
|
-
|
|
222
|
-
iter_call = AST::Call.new(
|
|
223
|
-
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iter_name), member: "iter"),
|
|
224
|
-
arguments: [],
|
|
225
|
-
)
|
|
226
|
-
iterator_type = infer_expression_type(iter_call, env: probe_env)
|
|
227
|
-
|
|
228
|
-
current_actual_scope(probe_env[:scopes])[iterator_name] = local_binding(type: iterator_type, linkage_name: iterator_name, mutable: true, pointer: false)
|
|
229
|
-
next_call = AST::Call.new(
|
|
230
|
-
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "next"),
|
|
231
|
-
arguments: [],
|
|
232
|
-
)
|
|
233
|
-
item_storage_type = infer_expression_type(next_call, env: probe_env)
|
|
234
|
-
if item_storage_type.is_a?(Types::Nullable) && nullable_iterator_item_type?(item_storage_type.base)
|
|
235
|
-
return {
|
|
236
|
-
kind: :nullable_item,
|
|
237
|
-
iterator_type:,
|
|
238
|
-
item_storage_type:,
|
|
239
|
-
item_type: item_storage_type.base,
|
|
240
|
-
}
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
if item_storage_type == @ctx.types.fetch("bool")
|
|
244
|
-
current_call = AST::Call.new(
|
|
245
|
-
callee: AST::MemberAccess.new(receiver: AST::Identifier.new(name: iterator_name), member: "current"),
|
|
246
|
-
arguments: [],
|
|
247
|
-
)
|
|
248
|
-
current_type = infer_expression_type(current_call, env: probe_env)
|
|
249
|
-
return {
|
|
250
|
-
kind: :current_item,
|
|
251
|
-
iterator_type:,
|
|
252
|
-
item_storage_type: current_type,
|
|
253
|
-
item_type: current_type,
|
|
254
|
-
}
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
nil
|
|
258
|
-
rescue LoweringError
|
|
259
|
-
nil
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def nullable_iterator_item_type?(type)
|
|
263
|
-
type == @ctx.types.fetch("cstr") || pointer_type?(type)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
172
|
def collection_loop_item_value(iterable_ref, iterable_type, index_ref, element_type)
|
|
267
173
|
if array_type?(iterable_type)
|
|
268
174
|
IR::Index.new(receiver: iterable_ref, index: index_ref, type: element_type)
|
|
@@ -841,18 +747,6 @@ module MilkTea
|
|
|
841
747
|
end
|
|
842
748
|
end
|
|
843
749
|
|
|
844
|
-
def lower_async_loop_exit(target, local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
|
|
845
|
-
cleanup = lower_async_cleanup_entries(local_defers, outer_defers, frame_expr:, raw_frame_expr:, async_info:)
|
|
846
|
-
if cleanup.empty?
|
|
847
|
-
[loop_exit_statement(target, local_defers:, outer_defers:)]
|
|
848
|
-
else
|
|
849
|
-
label = target[:label]
|
|
850
|
-
raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
|
|
851
|
-
|
|
852
|
-
cleanup + [IR::GotoStmt.new(label:)]
|
|
853
|
-
end
|
|
854
|
-
end
|
|
855
|
-
|
|
856
750
|
def contains_label_target?(statements, label)
|
|
857
751
|
statements.any? do |statement|
|
|
858
752
|
case statement
|
|
@@ -928,28 +822,6 @@ module MilkTea
|
|
|
928
822
|
!let_else_discard_binding_syntax?(statement)
|
|
929
823
|
end
|
|
930
824
|
|
|
931
|
-
def async_local_decl_field_key(statement)
|
|
932
|
-
return "__discard_#{statement.line}" if statement.name == "_"
|
|
933
|
-
|
|
934
|
-
statement.name
|
|
935
|
-
end
|
|
936
|
-
|
|
937
|
-
def async_local_decl_field_name(statement)
|
|
938
|
-
return "local_discard_#{statement.line}" if statement.name == "_"
|
|
939
|
-
|
|
940
|
-
"local_#{statement.name}"
|
|
941
|
-
end
|
|
942
|
-
|
|
943
|
-
def async_match_binding_field_key(arm)
|
|
944
|
-
@async_binding_counter ||= 0
|
|
945
|
-
@async_binding_counter += 1
|
|
946
|
-
"match_binding_#{@async_binding_counter}"
|
|
947
|
-
end
|
|
948
|
-
|
|
949
|
-
def async_match_binding_field_name(arm)
|
|
950
|
-
"local_match_binding_#{@async_binding_counter}"
|
|
951
|
-
end
|
|
952
|
-
|
|
953
825
|
def let_else_storage_c_name(statement, env)
|
|
954
826
|
return fresh_c_temp_name(env, "let_else_discard") if let_else_discard_binding_syntax?(statement)
|
|
955
827
|
|
|
@@ -1133,100 +1005,6 @@ module MilkTea
|
|
|
1133
1005
|
[storage_type, success_type, return_type, nil]
|
|
1134
1006
|
end
|
|
1135
1007
|
|
|
1136
|
-
def prepare_result_propagation_for_inline_lowering(expression, env:, allow_void_success: false)
|
|
1137
|
-
storage_type, success_type, return_type, error_type = infer_result_propagation_types(expression, env:, allow_void_success:)
|
|
1138
|
-
is_option = option_let_else_type?(storage_type)
|
|
1139
|
-
|
|
1140
|
-
env[:prepared_expression_cleanups] ||= []
|
|
1141
|
-
cleanup_start = env[:prepared_expression_cleanups].length
|
|
1142
|
-
operand_setup, operand = prepare_expression_for_inline_lowering(expression.operand, env:, expected_type: storage_type)
|
|
1143
|
-
operand_cleanups = env[:prepared_expression_cleanups].drop(cleanup_start)
|
|
1144
|
-
|
|
1145
|
-
result_name = fresh_c_temp_name(env, "propagate")
|
|
1146
|
-
result_ref = IR::Name.new(name: result_name, type: storage_type, pointer: false)
|
|
1147
|
-
return_context = env.fetch(:return_context)
|
|
1148
|
-
failure_return = if storage_type == return_type
|
|
1149
|
-
result_ref
|
|
1150
|
-
elsif is_option
|
|
1151
|
-
IR::VariantLiteral.new(
|
|
1152
|
-
type: return_type,
|
|
1153
|
-
arm_name: "none",
|
|
1154
|
-
fields: [],
|
|
1155
|
-
)
|
|
1156
|
-
else
|
|
1157
|
-
IR::VariantLiteral.new(
|
|
1158
|
-
type: return_type,
|
|
1159
|
-
arm_name: "failure",
|
|
1160
|
-
fields: [
|
|
1161
|
-
IR::AggregateField.new(
|
|
1162
|
-
name: "error",
|
|
1163
|
-
value: variant_binding_projection_expression(result_ref, storage_type, "failure", "error", error_type),
|
|
1164
|
-
),
|
|
1165
|
-
],
|
|
1166
|
-
)
|
|
1167
|
-
end
|
|
1168
|
-
failure_cleanup = operand_cleanups.flat_map(&:itself)
|
|
1169
|
-
failure_terminator = if return_context[:async_info]
|
|
1170
|
-
failure_cleanup +
|
|
1171
|
-
lower_async_cleanup_entries(
|
|
1172
|
-
return_context[:local_defers],
|
|
1173
|
-
return_context[:active_defers],
|
|
1174
|
-
frame_expr: return_context.fetch(:frame_expr),
|
|
1175
|
-
raw_frame_expr: return_context.fetch(:raw_frame_expr),
|
|
1176
|
-
async_info: return_context.fetch(:async_info),
|
|
1177
|
-
) +
|
|
1178
|
-
async_complete_statements(
|
|
1179
|
-
frame_expr: return_context.fetch(:frame_expr),
|
|
1180
|
-
raw_frame_expr: return_context.fetch(:raw_frame_expr),
|
|
1181
|
-
async_info: return_context.fetch(:async_info),
|
|
1182
|
-
value: failure_return,
|
|
1183
|
-
)
|
|
1184
|
-
else
|
|
1185
|
-
failure_cleanup +
|
|
1186
|
-
cleanup_statements(return_context[:local_defers], return_context[:active_defers]) +
|
|
1187
|
-
[IR::ReturnStmt.new(value: failure_return, source_path: @ctx.current_analysis_path)]
|
|
1188
|
-
end
|
|
1189
|
-
|
|
1190
|
-
if success_type == @ctx.types.fetch("void")
|
|
1191
|
-
return [
|
|
1192
|
-
operand_setup + [
|
|
1193
|
-
IR::LocalDecl.new(
|
|
1194
|
-
name: result_name,
|
|
1195
|
-
linkage_name: result_name,
|
|
1196
|
-
type: storage_type,
|
|
1197
|
-
value: lower_contextual_expression(operand, env:, expected_type: storage_type),
|
|
1198
|
-
),
|
|
1199
|
-
IR::IfStmt.new(
|
|
1200
|
-
condition: let_else_failure_condition(result_ref, storage_type),
|
|
1201
|
-
then_body: failure_terminator,
|
|
1202
|
-
else_body: nil,
|
|
1203
|
-
),
|
|
1204
|
-
],
|
|
1205
|
-
nil,
|
|
1206
|
-
]
|
|
1207
|
-
end
|
|
1208
|
-
|
|
1209
|
-
projection = is_option ? :option_some_value : :result_success_value
|
|
1210
|
-
register_prepared_temp!(env, result_name, success_type, storage_type:, projection:)
|
|
1211
|
-
|
|
1212
|
-
[
|
|
1213
|
-
operand_setup + [
|
|
1214
|
-
IR::LocalDecl.new(
|
|
1215
|
-
name: result_name,
|
|
1216
|
-
linkage_name: result_name,
|
|
1217
|
-
type: storage_type,
|
|
1218
|
-
value: lower_contextual_expression(operand, env:, expected_type: storage_type),
|
|
1219
|
-
),
|
|
1220
|
-
IR::IfStmt.new(
|
|
1221
|
-
condition: let_else_failure_condition(result_ref, storage_type),
|
|
1222
|
-
then_body: failure_terminator,
|
|
1223
|
-
else_body: nil,
|
|
1224
|
-
),
|
|
1225
|
-
],
|
|
1226
|
-
AST::Identifier.new(name: result_name),
|
|
1227
|
-
]
|
|
1228
|
-
end
|
|
1229
|
-
|
|
1230
1008
|
def c_type_name(type)
|
|
1231
1009
|
if type.is_a?(Types::Nullable)
|
|
1232
1010
|
return "nullable_#{c_type_name(type.base)}"
|
|
@@ -334,55 +334,40 @@ module MilkTea
|
|
|
334
334
|
private_class_method :raise_platform_conflict!
|
|
335
335
|
|
|
336
336
|
def check_path(path)
|
|
337
|
-
resolved_path =
|
|
338
|
-
|
|
339
|
-
shared_cache_mtime_checked = false
|
|
340
|
-
|
|
341
|
-
return @analysis_cache[resolved_path] if @analysis_cache.key?(resolved_path)
|
|
342
|
-
|
|
343
|
-
if use_shared_cache?
|
|
344
|
-
entry = @shared_cache[resolved_path]
|
|
345
|
-
if entry
|
|
346
|
-
shared_cache_mtime_checked = true
|
|
347
|
-
shared_cache_mtime = File.mtime(resolved_path).to_f rescue nil
|
|
348
|
-
if shared_cache_mtime && entry[:mtime] == shared_cache_mtime
|
|
349
|
-
@analysis_cache[resolved_path] = entry[:analysis]
|
|
350
|
-
return entry[:analysis]
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
end
|
|
337
|
+
resolved_path, ast, cached = check_module_cache(path)
|
|
338
|
+
return cached if cached
|
|
354
339
|
|
|
355
|
-
if @checking_paths.include?(resolved_path)
|
|
356
|
-
raise ModuleLoadError.new("cyclic import detected", path: resolved_path)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
@checking_paths << resolved_path
|
|
360
|
-
ast = load_file(resolved_path)
|
|
361
340
|
imported_modules = imported_modules_for_ast(ast, importer_path: resolved_path)
|
|
362
|
-
|
|
363
341
|
global_index = build_global_import_index(ast)
|
|
364
342
|
analysis = SemanticAnalyzer.check(ast, imported_modules:, path: resolved_path, global_import_index: global_index)
|
|
365
343
|
@analysis_cache[resolved_path] = analysis
|
|
344
|
+
update_shared_cache(resolved_path, analysis)
|
|
345
|
+
analysis
|
|
346
|
+
ensure
|
|
347
|
+
@checking_paths.pop if @checking_paths.last == resolved_path
|
|
348
|
+
end
|
|
366
349
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
350
|
+
def check_path_collecting_errors(path)
|
|
351
|
+
resolved_path, ast, cached = check_module_cache(path, extra_cache: @collecting_analysis_cache)
|
|
352
|
+
return cached if cached
|
|
353
|
+
|
|
354
|
+
imported_modules = imported_modules_for_ast_collecting_errors(ast, importer_path: resolved_path).modules
|
|
355
|
+
result = SemanticAnalyzer.check_collecting_errors(ast, imported_modules:, path: resolved_path)
|
|
356
|
+
analysis = result[:analysis]
|
|
357
|
+
raise(result[:errors].first || ModuleLoadError.new("module analysis unavailable", path: resolved_path)) unless analysis
|
|
375
358
|
|
|
359
|
+
@collecting_path_errors[resolved_path] = result[:errors]
|
|
360
|
+
@collecting_analysis_cache[resolved_path] = analysis
|
|
376
361
|
analysis
|
|
377
362
|
ensure
|
|
378
363
|
@checking_paths.pop if @checking_paths.last == resolved_path
|
|
379
364
|
end
|
|
380
365
|
|
|
381
|
-
def
|
|
366
|
+
def check_module_cache(path, extra_cache: nil)
|
|
382
367
|
resolved_path = self.class.resolve_source_path(path, platform: @platform, error_class: ModuleLoadError)
|
|
383
368
|
|
|
384
|
-
return @analysis_cache[resolved_path] if @analysis_cache.key?(resolved_path)
|
|
385
|
-
return
|
|
369
|
+
return [resolved_path, nil, @analysis_cache[resolved_path]] if @analysis_cache.key?(resolved_path)
|
|
370
|
+
return [resolved_path, nil, extra_cache[resolved_path]] if extra_cache&.key?(resolved_path)
|
|
386
371
|
|
|
387
372
|
if use_shared_cache?
|
|
388
373
|
entry = @shared_cache[resolved_path]
|
|
@@ -390,27 +375,23 @@ module MilkTea
|
|
|
390
375
|
mtime = File.mtime(resolved_path).to_f rescue nil
|
|
391
376
|
if mtime && entry[:mtime] == mtime
|
|
392
377
|
@analysis_cache[resolved_path] = entry[:analysis]
|
|
393
|
-
return entry[:analysis]
|
|
378
|
+
return [resolved_path, nil, entry[:analysis]]
|
|
394
379
|
end
|
|
395
380
|
end
|
|
396
381
|
end
|
|
397
382
|
|
|
398
|
-
if @checking_paths.include?(resolved_path)
|
|
399
|
-
raise ModuleLoadError.new("cyclic import detected", path: resolved_path)
|
|
400
|
-
end
|
|
383
|
+
raise ModuleLoadError.new("cyclic import detected", path: resolved_path) if @checking_paths.include?(resolved_path)
|
|
401
384
|
|
|
402
385
|
@checking_paths << resolved_path
|
|
403
386
|
ast = load_file(resolved_path)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
analysis = result[:analysis]
|
|
407
|
-
raise(result[:errors].first || ModuleLoadError.new("module analysis unavailable", path: resolved_path)) unless analysis
|
|
387
|
+
[resolved_path, ast, nil]
|
|
388
|
+
end
|
|
408
389
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
@
|
|
390
|
+
def update_shared_cache(resolved_path, analysis)
|
|
391
|
+
return unless use_shared_cache?
|
|
392
|
+
|
|
393
|
+
mtime = File.mtime(resolved_path).to_f rescue nil
|
|
394
|
+
@shared_cache[resolved_path] = { mtime:, analysis: } if mtime
|
|
414
395
|
end
|
|
415
396
|
|
|
416
397
|
def parse_file(path)
|
|
@@ -24,7 +24,7 @@ module MilkTea
|
|
|
24
24
|
attributes = parse_attribute_applications
|
|
25
25
|
visibility, visibility_token = parse_visibility
|
|
26
26
|
|
|
27
|
-
if
|
|
27
|
+
if builtin_attribute_identifier?(peek)
|
|
28
28
|
raise error(peek, "layout modifiers must use attributes like @[packed] or @[align(...)]")
|
|
29
29
|
elsif match(:attribute)
|
|
30
30
|
reject_attributes!(attributes, "attribute")
|
|
@@ -185,7 +185,7 @@ module MilkTea
|
|
|
185
185
|
if match(:public)
|
|
186
186
|
reject_attributes!(attributes)
|
|
187
187
|
raise error(previous, "public is not allowed in external files")
|
|
188
|
-
elsif
|
|
188
|
+
elsif builtin_attribute_identifier?(peek)
|
|
189
189
|
raise error(peek, "layout modifiers must use attributes like @[packed] or @[align(...)]")
|
|
190
190
|
elsif match(:attribute)
|
|
191
191
|
reject_attributes!(attributes)
|
|
@@ -124,7 +124,7 @@ module MilkTea
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def top_level_recovery_start?(token)
|
|
127
|
-
token.column.to_i <= 1 && (TOP_LEVEL_RECOVERY_START_TYPES.include?(token.type) ||
|
|
127
|
+
token.column.to_i <= 1 && (TOP_LEVEL_RECOVERY_START_TYPES.include?(token.type) || builtin_attribute_identifier?(token))
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
end
|
data/lib/milk_tea/core/parser.rb
CHANGED
|
@@ -290,7 +290,7 @@ module MilkTea
|
|
|
290
290
|
@tokens[@current + 1]&.type == :identifier
|
|
291
291
|
end
|
|
292
292
|
|
|
293
|
-
def
|
|
293
|
+
def builtin_attribute_identifier?(token)
|
|
294
294
|
token&.type == :identifier && BUILTIN_ATTRIBUTE_NAME_LEXEMES.include?(token.lexeme)
|
|
295
295
|
end
|
|
296
296
|
|
|
@@ -783,143 +783,26 @@ module MilkTea
|
|
|
783
783
|
end
|
|
784
784
|
|
|
785
785
|
def infer_integer_match_expression(expression, scrutinee_type, scopes:, expected_type:)
|
|
786
|
-
has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
787
|
-
raise_sema_error("match on integer type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
|
|
788
|
-
|
|
789
|
-
covered_values = {}
|
|
790
|
-
wildcard_seen = false
|
|
791
786
|
arm_entries = []
|
|
792
|
-
|
|
793
|
-
expression.arms.each do |arm|
|
|
794
|
-
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
795
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
796
|
-
next
|
|
797
|
-
end
|
|
798
|
-
|
|
799
|
-
if wildcard_pattern?(arm.pattern)
|
|
800
|
-
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
801
|
-
|
|
802
|
-
wildcard_seen = true
|
|
803
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
804
|
-
next
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
if arm.pattern.is_a?(AST::RangeExpr)
|
|
808
|
-
start_val = arm.pattern.start_expr
|
|
809
|
-
end_val = arm.pattern.end_expr
|
|
810
|
-
unless (start_val.is_a?(AST::IntegerLiteral) || start_val.is_a?(AST::CharLiteral)) &&
|
|
811
|
-
(end_val.is_a?(AST::IntegerLiteral) || end_val.is_a?(AST::CharLiteral))
|
|
812
|
-
raise_sema_error("range match arm bounds must be integer or char literals, got #{start_val.class.name}..#{end_val.class.name}")
|
|
813
|
-
end
|
|
814
|
-
|
|
815
|
-
raise_sema_error("range match arm start #{start_val.value} must be <= end #{end_val.value}") if start_val.value > end_val.value
|
|
816
|
-
|
|
817
|
-
range_key = [start_val.value, end_val.value]
|
|
818
|
-
raise_sema_error("duplicate match arm range #{range_key[0]}..#{range_key[1]}") if covered_values.key?(range_key)
|
|
819
|
-
|
|
820
|
-
covered_values[range_key] = true
|
|
821
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
822
|
-
next
|
|
823
|
-
end
|
|
824
|
-
|
|
825
|
-
unless arm.pattern.is_a?(AST::IntegerLiteral) || arm.pattern.is_a?(AST::CharLiteral)
|
|
826
|
-
raise_sema_error("match arm for integer scrutinee must be an integer literal, char literal, range, or _, got #{arm.pattern.class.name}")
|
|
827
|
-
end
|
|
828
|
-
|
|
829
|
-
value = arm.pattern.value
|
|
830
|
-
raise_sema_error("duplicate match arm value #{value}") if covered_values.key?(value)
|
|
831
|
-
|
|
832
|
-
covered_values[value] = true
|
|
787
|
+
each_integer_match_arm(expression, scrutinee_type, scopes:) do |arm|
|
|
833
788
|
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
834
789
|
end
|
|
835
|
-
|
|
836
790
|
match_expression_common_type(arm_entries, expected_type)
|
|
837
791
|
end
|
|
838
792
|
|
|
839
793
|
def infer_string_match_expression(expression, scrutinee_type, scopes:, expected_type:)
|
|
840
|
-
has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
841
|
-
raise_sema_error("match on str requires a wildcard arm (_:)") unless has_wildcard
|
|
842
|
-
|
|
843
|
-
covered_values = {}
|
|
844
|
-
wildcard_seen = false
|
|
845
794
|
arm_entries = []
|
|
846
|
-
|
|
847
|
-
expression.arms.each do |arm|
|
|
848
|
-
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
849
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
850
|
-
next
|
|
851
|
-
end
|
|
852
|
-
|
|
853
|
-
if wildcard_pattern?(arm.pattern)
|
|
854
|
-
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
855
|
-
wildcard_seen = true
|
|
856
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
857
|
-
next
|
|
858
|
-
end
|
|
859
|
-
|
|
860
|
-
unless arm.pattern.is_a?(AST::StringLiteral)
|
|
861
|
-
raise_sema_error("match arm for str scrutinee must be a string literal or _, got #{arm.pattern.class.name}")
|
|
862
|
-
end
|
|
863
|
-
|
|
864
|
-
value = arm.pattern.value
|
|
865
|
-
raise_sema_error("duplicate match arm value \"#{value}\"") if covered_values.key?(value)
|
|
866
|
-
|
|
867
|
-
covered_values[value] = true
|
|
795
|
+
each_string_match_arm(expression, scrutinee_type, scopes:) do |arm|
|
|
868
796
|
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
869
797
|
end
|
|
870
|
-
|
|
871
798
|
match_expression_common_type(arm_entries, expected_type)
|
|
872
799
|
end
|
|
873
800
|
|
|
874
801
|
def infer_tuple_match_expression(expression, scrutinee_type, scopes:, expected_type:)
|
|
875
|
-
has_wildcard = expression.arms.any? { |arm| wildcard_pattern?(arm.pattern) }
|
|
876
|
-
raise_sema_error("match on tuple type #{scrutinee_type} requires a wildcard arm (_:)") unless has_wildcard
|
|
877
|
-
|
|
878
|
-
arity = scrutinee_type.element_types.length
|
|
879
|
-
covered_values = {}
|
|
880
|
-
wildcard_seen = false
|
|
881
802
|
arm_entries = []
|
|
882
|
-
|
|
883
|
-
expression.arms.each do |arm|
|
|
884
|
-
if arm.pattern.is_a?(AST::ErrorExpr)
|
|
885
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
886
|
-
next
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
if wildcard_pattern?(arm.pattern)
|
|
890
|
-
raise_sema_error("duplicate wildcard arm in match") if wildcard_seen
|
|
891
|
-
wildcard_seen = true
|
|
892
|
-
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
893
|
-
next
|
|
894
|
-
end
|
|
895
|
-
|
|
896
|
-
unless arm.pattern.is_a?(AST::ExpressionList)
|
|
897
|
-
raise_sema_error("match arm for tuple scrutinee must be a tuple literal or _, got #{arm.pattern.class.name}")
|
|
898
|
-
end
|
|
899
|
-
|
|
900
|
-
elements = arm.pattern.elements
|
|
901
|
-
raise_sema_error("tuple match arm has #{elements.length} elements but scrutinee has #{arity}") unless elements.length == arity
|
|
902
|
-
|
|
903
|
-
values = elements.map do |elem|
|
|
904
|
-
if elem.is_a?(AST::Identifier) && elem.name == "_"
|
|
905
|
-
:wildcard
|
|
906
|
-
elsif elem.is_a?(AST::IntegerLiteral) || elem.is_a?(AST::CharLiteral) ||
|
|
907
|
-
elem.is_a?(AST::StringLiteral) || elem.is_a?(AST::BooleanLiteral)
|
|
908
|
-
elem.value
|
|
909
|
-
else
|
|
910
|
-
raise_sema_error("tuple match arm element must be a literal or _, got #{elem.class.name}")
|
|
911
|
-
end
|
|
912
|
-
end
|
|
913
|
-
|
|
914
|
-
all_literal = values.all? { |v| v != :wildcard }
|
|
915
|
-
if all_literal
|
|
916
|
-
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)
|
|
917
|
-
covered_values[values] = true
|
|
918
|
-
end
|
|
919
|
-
|
|
803
|
+
each_tuple_match_arm(expression, scrutinee_type, scopes:) do |arm|
|
|
920
804
|
arm_entries << [infer_match_expression_arm_value(arm, scopes:, expected_type:), arm.value]
|
|
921
805
|
end
|
|
922
|
-
|
|
923
806
|
match_expression_common_type(arm_entries, expected_type)
|
|
924
807
|
end
|
|
925
808
|
|