mt-lang 0.3.5 → 0.3.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ba5797b3cf3ff504321adf78e281c017a4ca3aa30088f5d472c01fe157f7b4f
4
- data.tar.gz: f1b7c0dd477f0e0868e6b3da0fa7f640ace5ef9d53703a7aa8ab89b8907999c9
3
+ metadata.gz: 3f5f1a4cb47c9822d4aef3caefdd90aca32171fe59775d7906ab9c1485a422cb
4
+ data.tar.gz: 72e1d25c2082827fd40f723ac4f3b11045391dda39b1f8ef339aa5fac5e57611
5
5
  SHA512:
6
- metadata.gz: 9db5f6898f03726a69d907b0a5dec9d19a52ac3141e8cfdda443797476d9fc8f7b0e426812288babdbba3f48d270499e49998224a4509705286992bfd117e810
7
- data.tar.gz: 5de95e68ce290fa2e3f80e5d595e29dba7d19093f91dcc169c6773a027fe80f3866f38a213713709911aeda767ccd28b30eb3b4765b5d58319997aaec587f414
6
+ metadata.gz: b6362b05316203c6b8d6e5c48cbfddad6351540096f9bc90580b8903866bb165e202cc654bd083c4e1de8786acb3f74e86ec85bbb020ee0e5c6874b46ef088fb
7
+ data.tar.gz: 0c7ffb83f660d7d1a30dbbb42713e5e798ff983795590d3e14074346d350f268184de505fc0091c14c975fcc7d141039368e652a2f0540b533575dcf8a097389
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.3.5"
6
+ VERSION = "0.3.7"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -32,7 +32,7 @@ module MilkTea
32
32
 
33
33
  visit = lambda do |aggregate_decl|
34
34
  return if visited[aggregate_decl.linkage_name]
35
- raise CBackendError, "cyclic aggregate dependency involving #{aggregate_decl.linkage_name}" if visiting[aggregate_decl.linkage_name]
35
+ raise CBackendError.new("cyclic aggregate dependency involving #{aggregate_decl.linkage_name}", line: 0, column: 0, path: @path) if visiting[aggregate_decl.linkage_name]
36
36
 
37
37
  visiting[aggregate_decl.linkage_name] = true
38
38
  aggregate_decl_dependencies(aggregate_decl).each do |dependency|
@@ -33,7 +33,12 @@ module MilkTea
33
33
  when IR::NullableSpanIndex
34
34
  "#{nullable_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
35
35
  when IR::Call
36
- raise CBackendError, "array-return call must be materialized before C emission" if array_type?(expression.type)
36
+ if array_type?(expression.type)
37
+ location = [@source_path, @current_line && "line #{@current_line}"].compact.join(" ")
38
+ msg = "array-return call must be materialized before C emission"
39
+ msg += " (#{location})" unless location.empty?
40
+ raise CBackendError.new(msg, line: @current_line, path: @source_path)
41
+ end
37
42
 
38
43
  emit_call_expression(expression)
39
44
  when IR::Unary
@@ -100,7 +105,7 @@ module MilkTea
100
105
  when IR::VariantLiteral
101
106
  emit_variant_literal(expression)
102
107
  else
103
- raise CBackendError, "unsupported IR expression #{expression.class.name}"
108
+ raise CBackendError.new("unsupported IR expression #{expression.class.name}", line: 0, column: 0, path: @path)
104
109
  end
105
110
  end
106
111
 
@@ -158,7 +163,7 @@ module MilkTea
158
163
  when "+", "-" then 9
159
164
  when "*", "/", "%" then 10
160
165
  else
161
- raise CBackendError, "unsupported binary operator #{operator}"
166
+ raise CBackendError.new("unsupported binary operator #{operator}", line: 0, column: 0, path: @path)
162
167
  end
163
168
  end
164
169
 
@@ -403,7 +408,7 @@ module MilkTea
403
408
  end
404
409
  return type.field(field_name) if type.respond_to?(:field)
405
410
 
406
- raise CBackendError, "unsupported aggregate field lookup for #{type}"
411
+ raise CBackendError.new("unsupported aggregate field lookup for #{type}", line: 0, column: 0, path: @path)
407
412
  end
408
413
 
409
414
  def emit_aggregate_field_initializer(type, field)
@@ -23,6 +23,10 @@ module MilkTea
23
23
  indent = INDENT * level
24
24
  aliases = checked_index_aliases_for_statement(statement)
25
25
  alias_lines = emit_checked_index_alias_declarations(aliases, indent)
26
+ if statement.respond_to?(:line) && statement.line
27
+ @current_line = statement.line
28
+ @source_path ||= statement.source_path if statement.respond_to?(:source_path) && statement.source_path
29
+ end
26
30
  line_directive = if @emit_line_directives && statement.respond_to?(:line) && statement.line
27
31
  sp = (statement.respond_to?(:source_path) && statement.source_path) || @source_path
28
32
  sp ? ["#line #{statement.line} #{sp.inspect}"] : []
@@ -174,7 +178,7 @@ module MilkTea
174
178
  lines
175
179
  end
176
180
  else
177
- raise CBackendError, "unsupported IR statement #{statement.class.name}"
181
+ raise CBackendError.new("unsupported IR statement #{statement.class.name}", line: 0, column: 0, path: @path)
178
182
  end
179
183
  end
180
184
 
@@ -497,7 +501,7 @@ module MilkTea
497
501
  when IR::CheckedSpanIndex
498
502
  "#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
499
503
  else
500
- raise CBackendError, "unsupported checked index alias expression #{expression.class.name}"
504
+ raise CBackendError.new("unsupported checked index alias expression #{expression.class.name}", line: 0, column: 0, path: @path)
501
505
  end
502
506
  end
503
507
 
@@ -529,19 +533,19 @@ module MilkTea
529
533
  def emit_for_clause_statement(statement)
530
534
  case statement
531
535
  when IR::LocalDecl
532
- raise CBackendError, "array for-loop init declarations are unsupported" if array_type?(statement.type)
536
+ raise CBackendError.new("array for-loop init declarations are unsupported", line: 0, column: 0, path: @path) if array_type?(statement.type)
533
537
 
534
538
  "#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)}"
535
539
  when IR::Assignment
536
540
  if array_type?(statement.target.type) && statement.operator == "="
537
- raise CBackendError, "array for-loop assignment clauses are unsupported"
541
+ raise CBackendError.new("array for-loop assignment clauses are unsupported", line: 0, column: 0, path: @path)
538
542
  end
539
543
 
540
544
  "#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)}"
541
545
  when IR::ExpressionStmt
542
546
  emit_expression(statement.expression)
543
547
  else
544
- raise CBackendError, "unsupported for-loop clause #{statement.class.name}"
548
+ raise CBackendError.new("unsupported for-loop clause #{statement.class.name}", line: 0, column: 0, path: @path)
545
549
  end
546
550
  end
547
551
 
@@ -171,7 +171,7 @@ module MilkTea
171
171
  when Types::Handle
172
172
  "void*"
173
173
  else
174
- raise CBackendError, "unsupported C type #{type.class.name}"
174
+ raise CBackendError.new("unsupported C type #{type.class.name}", line: 0, column: 0, path: @path)
175
175
  end
176
176
  end
177
177
 
@@ -261,33 +261,33 @@ module MilkTea
261
261
  def generic_c_type(type)
262
262
  case type.name
263
263
  when "ptr"
264
- raise CBackendError, "ptr requires exactly one type argument" unless type.arguments.length == 1
264
+ raise CBackendError.new("ptr requires exactly one type argument", line: 0, column: 0, path: @path) unless type.arguments.length == 1
265
265
 
266
266
  "#{c_type(type.arguments.first)}*"
267
267
  when "const_ptr"
268
- raise CBackendError, "const_ptr requires exactly one type argument" unless type.arguments.length == 1
268
+ raise CBackendError.new("const_ptr requires exactly one type argument", line: 0, column: 0, path: @path) unless type.arguments.length == 1
269
269
 
270
270
  "const #{c_type(type.arguments.first)}*"
271
271
  when "own"
272
- raise CBackendError, "own requires exactly one type argument" unless type.arguments.length == 1
272
+ raise CBackendError.new("own requires exactly one type argument", line: 0, column: 0, path: @path) unless type.arguments.length == 1
273
273
 
274
274
  "#{c_type(type.arguments.first)}*"
275
275
  when "ref"
276
- raise CBackendError, "ref requires at least one type argument" unless [1, 2].include?(type.arguments.length)
276
+ raise CBackendError.new("ref requires at least one type argument", line: 0, column: 0, path: @path) unless [1, 2].include?(type.arguments.length)
277
277
 
278
278
  "#{c_type(type.arguments.length == 1 ? type.arguments.first : type.arguments[1])}*"
279
279
  when "array"
280
- raise CBackendError, "array requires exactly two type arguments" unless type.arguments.length == 2
280
+ raise CBackendError.new("array requires exactly two type arguments", line: 0, column: 0, path: @path) unless type.arguments.length == 2
281
281
 
282
282
  c_type(type.arguments.first)
283
283
  when "str_buffer"
284
- raise CBackendError, "str_buffer requires exactly one type argument" unless str_buffer_type?(type)
284
+ raise CBackendError.new("str_buffer requires exactly one type argument", line: 0, column: 0, path: @path) unless str_buffer_type?(type)
285
285
 
286
286
  str_buffer_type_name(type)
287
287
  when "atomic"
288
288
  "_Atomic #{c_type(type.arguments.first)}"
289
289
  else
290
- raise CBackendError, "unsupported generic C type #{type.name}"
290
+ raise CBackendError.new("unsupported generic C type #{type.name}", line: 0, column: 0, path: @path)
291
291
  end
292
292
  end
293
293
 
@@ -55,6 +55,8 @@ module MilkTea
55
55
  @checked_index_alias_id = 0
56
56
  @loop_guard_id = 0
57
57
  @emitted_span_type_names = Set.new
58
+ @source_path = program.source_path
59
+ @current_line = nil
58
60
  end
59
61
 
60
62
  def emit
@@ -218,10 +220,16 @@ module MilkTea
218
220
  end
219
221
 
220
222
  constants.each do |constant|
223
+ @current_line = constant.line if constant.line
224
+ @source_path ||= constant.source_path if constant.source_path
221
225
  lines << "#{constant_storage(constant.type)} #{c_declaration(constant.type, constant.linkage_name)} = #{emit_initializer(constant.value)};"
222
226
  end
223
227
  @program.globals.each do |global|
224
228
  next unless emitted_globals.include?(global)
229
+ if global.respond_to?(:line) && global.line
230
+ @current_line = global.line
231
+ @source_path ||= global.source_path if global.respond_to?(:source_path) && global.source_path
232
+ end
225
233
  lines << "#{global_storage(global.type)} #{c_declaration(global.type, global.linkage_name)} = #{emit_initializer(global.value)};"
226
234
  end
227
235
  lines << "" unless constants.empty? && @program.globals.empty?
@@ -114,7 +114,12 @@ module MilkTea
114
114
  else
115
115
  @checker.instance_variable_get(:@ctx).types
116
116
  end
117
- if (type = types[call_expr.callee.name]) && type.is_a?(Types::Struct)
117
+ callee_name = if call_expr.callee.is_a?(AST::Specialization) && call_expr.callee.callee.respond_to?(:name)
118
+ call_expr.callee.callee.name
119
+ elsif call_expr.callee.respond_to?(:name)
120
+ call_expr.callee.name
121
+ end
122
+ if callee_name && (type = types[callee_name]) && type.is_a?(Types::Struct)
118
123
  fields = {}
119
124
  call_expr.arguments.each do |argument|
120
125
  val = evaluate_expression(argument.value, scopes:)
@@ -123,6 +128,19 @@ module MilkTea
123
128
  end
124
129
  next fields
125
130
  end
131
+
132
+ if call_expr.callee.is_a?(AST::Specialization) && @checker.respond_to?(:resolve_type_expression)
133
+ resolved = @checker.send(:resolve_type_expression, call_expr.callee)
134
+ if resolved && @checker.respond_to?(:array_type?) && @checker.send(:array_type?, resolved)
135
+ values = []
136
+ call_expr.arguments.each do |argument|
137
+ val = evaluate_expression(argument.value, scopes:)
138
+ return nil unless val
139
+ values << val
140
+ end
141
+ next values
142
+ end
143
+ end
126
144
 
127
145
  @checker.send(:evaluate_compile_time_const_value, call_expr, scopes:)
128
146
  },
@@ -6,7 +6,9 @@ module MilkTea
6
6
  def initialize(module_name:, includes:, constants:, globals:, opaques:, structs:, unions:, enums:, variants:, static_asserts:, functions:, source_path: nil) = super
7
7
  end
8
8
  Include = Data.define(:header)
9
- Constant = Data.define(:name, :linkage_name, :type, :value)
9
+ Constant = Data.define(:name, :linkage_name, :type, :value, :line, :source_path) do
10
+ def initialize(name:, linkage_name:, type:, value:, line: nil, source_path: nil) = super
11
+ end
10
12
  Global = Data.define(:name, :linkage_name, :type, :value)
11
13
  OpaqueDecl = Data.define(:name, :linkage_name, :forward_declarable, :source_module) do
12
14
  def initialize(name:, linkage_name:, forward_declarable:, source_module: nil) = super
@@ -119,8 +119,8 @@ module MilkTea
119
119
  statement_position: false,
120
120
  )
121
121
  lowered.concat(setup)
122
- raise LoweringError, "foreign call used to initialize #{statement.name} must return a value" if call_type == @ctx.types.fetch("void")
123
- raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
122
+ raise LoweringError.new("foreign call used to initialize #{statement.name} must return a value", line: 0, column: 0, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
123
+ raise LoweringError.new("consuming foreign calls must return void", line: 0, column: 0, path: @ctx.current_analysis_path) unless release_assignments.empty?
124
124
 
125
125
  lowered << IR::Assignment.new(target:, operator: "=", value:)
126
126
  lowered.concat(cleanup_statements)
@@ -307,7 +307,7 @@ module MilkTea
307
307
  when AST::StaticAssert
308
308
  lowered.concat(lower_static_assert(statement))
309
309
  else
310
- raise LoweringError, "unsupported async cf statement #{statement.class.name}"
310
+ raise LoweringError.new("unsupported async cf statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
311
311
  end
312
312
  end
313
313
 
@@ -412,7 +412,7 @@ module MilkTea
412
412
  def lower_async_cf_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, resume_linkage_name:, async_info:, active_defers:)
413
413
  iterable_type = infer_expression_type(statement.iterable, env:)
414
414
  element_type = collection_loop_type(iterable_type)
415
- raise LoweringError, "for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}" unless element_type
415
+ raise LoweringError.new("for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
416
416
 
417
417
  iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
418
418
  continue_label = fresh_c_temp_name(env, "loop_continue")
@@ -468,7 +468,7 @@ module MilkTea
468
468
  iterable = statement.iterables[index]
469
469
  iterable_type = infer_expression_type(iterable, env:)
470
470
  element_type = collection_loop_type(iterable_type)
471
- raise LoweringError, "parallel for loops expect arrays or spans for each iterable, got #{iterable_type}" unless element_type
471
+ raise LoweringError.new("parallel for loops expect arrays or spans for each iterable, got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
472
472
 
473
473
  {
474
474
  binding:,
@@ -744,7 +744,7 @@ module MilkTea
744
744
  when AST::StaticAssert
745
745
  lowered << lower_static_assert(statement, env: local_env)
746
746
  else
747
- raise LoweringError, "unsupported async non-await statement #{statement.class.name}"
747
+ raise LoweringError.new("unsupported async non-await statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
748
748
  end
749
749
  end
750
750
 
@@ -837,7 +837,7 @@ module MilkTea
837
837
  def lower_async_collection_for_stmt(statement, env:, frame_expr:, raw_frame_expr:, async_info:, active_defers: [])
838
838
  iterable_type = infer_expression_type(statement.iterable, env:)
839
839
  element_type = collection_loop_type(iterable_type)
840
- raise LoweringError, "for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}" unless element_type
840
+ raise LoweringError.new("for loop expects start..stop, array[T, N], or span[T], got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
841
841
 
842
842
  iterable_setup, prepared_iterable = prepare_expression_for_inline_lowering(statement.iterable, env:, expected_type: iterable_type)
843
843
  iterable_linkage_name = fresh_c_temp_name(env, "for_items")
@@ -896,7 +896,7 @@ module MilkTea
896
896
  iterable = statement.iterables[index]
897
897
  iterable_type = infer_expression_type(iterable, env:)
898
898
  element_type = collection_loop_type(iterable_type)
899
- raise LoweringError, "parallel for loops expect arrays or spans for each iterable, got #{iterable_type}" unless element_type
899
+ raise LoweringError.new("parallel for loops expect arrays or spans for each iterable, got #{iterable_type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless element_type
900
900
 
901
901
  {
902
902
  binding:,
@@ -998,8 +998,8 @@ module MilkTea
998
998
  statement_position: false,
999
999
  )
1000
1000
  lowered.concat(setup)
1001
- raise LoweringError, "foreign call used in assignment must return a value" if call_type == @ctx.types.fetch("void")
1002
- raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
1001
+ raise LoweringError.new("foreign call used in assignment must return a value", line: 0, column: 0, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
1002
+ raise LoweringError.new("consuming foreign calls must return void", line: 0, column: 0, path: @ctx.current_analysis_path) unless release_assignments.empty?
1003
1003
 
1004
1004
  lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
1005
1005
  lowered.concat(cleanup_statements)
@@ -1135,7 +1135,7 @@ module MilkTea
1135
1135
  expected_type: await_info[:task_type],
1136
1136
  )
1137
1137
  lowered.concat(prepared_setup)
1138
- raise LoweringError, "await does not support foreign task expressions" if foreign_call_info(prepared_task, env)
1138
+ raise LoweringError.new("await does not support foreign task expressions", line: 0, column: 0, path: @ctx.current_analysis_path) if foreign_call_info(prepared_task, env)
1139
1139
 
1140
1140
  task_expr = async_frame_field_expression(frame_expr, await_info[:field_name], await_info[:task_type])
1141
1141
  task_frame_expr = async_task_frame_expression(task_expr, await_info[:task_type])
@@ -1382,7 +1382,7 @@ module MilkTea
1382
1382
  [loop_exit_statement(target, local_defers:, outer_defers:)]
1383
1383
  else
1384
1384
  label = target[:label]
1385
- raise LoweringError, "structured loop exits with cleanup are unsupported" unless label
1385
+ raise LoweringError.new("structured loop exits with cleanup are unsupported", line: 0, column: 0, path: @ctx.current_analysis_path) unless label
1386
1386
 
1387
1387
  cleanup + [IR::GotoStmt.new(label:)]
1388
1388
  end
@@ -166,7 +166,7 @@ module MilkTea
166
166
  when AST::BreakStmt, AST::ContinueStmt, AST::StaticAssert, AST::PassStmt
167
167
  [statement]
168
168
  else
169
- raise LoweringError, "unsupported async statement #{statement.class.name}"
169
+ raise LoweringError.new("unsupported async statement #{statement.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
170
170
  end
171
171
  end
172
172
 
@@ -210,10 +210,10 @@ module MilkTea
210
210
  end
211
211
  [setup, AST::Call.new(callee: target.callee, arguments: normalized_args)]
212
212
  else
213
- raise LoweringError, "unsupported assignment target #{target.class.name}"
213
+ raise LoweringError.new("unsupported assignment target #{target.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
214
214
  end
215
215
  else
216
- raise LoweringError, "unsupported assignment target #{target.class.name}"
216
+ raise LoweringError.new("unsupported assignment target #{target.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
217
217
  end
218
218
  end
219
219
 
@@ -372,7 +372,7 @@ module MilkTea
372
372
  AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
373
373
  when Types::Nullable
374
374
  inner = ast_type_ref_for(type.base)
375
- raise LoweringError, "nullable annotation is only valid for named/generic types" unless inner.is_a?(AST::TypeRef)
375
+ raise LoweringError.new("nullable annotation is only valid for named/generic types", line: 0, column: 0, path: @ctx.current_analysis_path) unless inner.is_a?(AST::TypeRef)
376
376
 
377
377
  AST::TypeRef.new(name: inner.name, arguments: inner.arguments, nullable: true)
378
378
  when Types::GenericInstance
@@ -420,7 +420,7 @@ module MilkTea
420
420
  return_type: ast_type_ref_for(type.return_type),
421
421
  )
422
422
  else
423
- raise LoweringError, "unsupported type for AST normalization #{type.class.name}"
423
+ raise LoweringError.new("unsupported type for AST normalization #{type.class.name}", line: 0, column: 0, path: @ctx.current_analysis_path)
424
424
  end
425
425
  end
426
426
 
@@ -8,7 +8,8 @@ module MilkTea
8
8
  def build_async_main_entrypoint(binding, _constructor_c_name, async_info)
9
9
  task_type = async_info[:task_type]
10
10
  signature = root_main_entrypoint_signature(binding)
11
- raise LoweringError, "async main entrypoint requires a supported signature" unless signature
11
+ raise LoweringError.new("async main entrypoint requires a supported signature",
12
+ line: 0, column: 0, path: @ctx.current_analysis_path) unless signature
12
13
 
13
14
  params, setup_statements, call_arguments, cleanup_statements = build_root_main_entrypoint_bridge(signature)
14
15
  body = []
@@ -229,7 +230,8 @@ module MilkTea
229
230
  cleanup,
230
231
  ]
231
232
  else
232
- raise LoweringError, "unsupported root main entrypoint bridge #{signature[:kind]}"
233
+ raise LoweringError.new("unsupported root main entrypoint bridge #{signature[:kind]}",
234
+ line: 0, column: 0, path: @ctx.current_analysis_path)
233
235
  end
234
236
  end
235
237
 
@@ -55,11 +55,13 @@ module MilkTea
55
55
  when AST::PassStmt
56
56
  nil
57
57
  when AST::BreakStmt
58
- raise LoweringError, "break must be inside a loop" unless loop_flow
58
+ raise LoweringError.new("break must be inside a loop",
59
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
59
60
 
60
61
  lowered.concat(lower_loop_exit(loop_flow[:break_target], local_defers, loop_flow[:break_defers]))
61
62
  when AST::ContinueStmt
62
- raise LoweringError, "continue must be inside a loop" unless loop_flow
63
+ raise LoweringError.new("continue must be inside a loop",
64
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless loop_flow
63
65
 
64
66
  lowered.concat(lower_loop_exit(loop_flow[:continue_target], local_defers, loop_flow[:continue_defers]))
65
67
  when AST::ReturnStmt
@@ -71,7 +73,8 @@ module MilkTea
71
73
  when AST::WhenStmt
72
74
  lower_block_when_stmt(statement, lowered:, local_env:, active_defers:, return_type:, loop_flow:, allow_return:)
73
75
  else
74
- raise LoweringError, "unsupported statement #{statement.class.name}"
76
+ raise LoweringError.new("unsupported statement #{statement.class.name}",
77
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
75
78
  end
76
79
  end
77
80
 
@@ -85,7 +88,8 @@ module MilkTea
85
88
  captures = proc_capture_entries(expression, env)
86
89
  captures.each do |capture|
87
90
  if ref_type?(capture[:type]) || contains_ref_type?(capture[:type])
88
- raise LoweringError, "proc capture #{capture[:name]} cannot use ref types"
91
+ raise LoweringError.new("proc capture #{capture[:name]} cannot use ref types",
92
+ line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
89
93
  end
90
94
  end
91
95
 
@@ -279,7 +283,8 @@ module MilkTea
279
283
  when AST::ConstDecl
280
284
  lower_emitted_const(decl, env:)
281
285
  else
282
- raise LoweringError, "emit is not supported for #{decl.class.name}"
286
+ raise LoweringError.new("emit is not supported for #{decl.class.name}",
287
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
283
288
  end
284
289
  end
285
290
 
@@ -353,7 +358,8 @@ module MilkTea
353
358
  value = lower_static_storage_initializer(decl.value, env:, expected_type: decl.type ? resolve_type_ref(decl.type) : nil)
354
359
  type = decl.type ? resolve_type_ref(decl.type) : infer_expression_type(decl.value, env:)
355
360
  linkage_name = value_c_name(decl.name)
356
- constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:)
361
+ constant = IR::Constant.new(name: decl.name, linkage_name:, type:, value:,
362
+ line: decl.line, source_path: @ctx.current_analysis_path)
357
363
  @artifacts.emitted_declarations << constant
358
364
  []
359
365
  end
@@ -450,7 +456,8 @@ module MilkTea
450
456
  end
451
457
 
452
458
  def lower_block_return_stmt(statement, lowered:, local_defers:, local_env:, active_defers:, return_type:, allow_return:)
453
- raise LoweringError, "return is not allowed inside defer blocks" unless allow_return
459
+ raise LoweringError.new("return is not allowed inside defer blocks",
460
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless allow_return
454
461
 
455
462
  value = nil
456
463
  prepared_setup = []
@@ -477,7 +484,8 @@ module MilkTea
477
484
  contextual_int_to_float: contextual_int_to_float_target?(return_type),
478
485
  ) : nil
479
486
  if prepared_cleanups.any? && cstr_trackable_type?(return_type)
480
- raise LoweringError, "formatted string temporaries cannot be returned as borrowed text; use std.fmt.format(f\"...\") when ownership must escape"
487
+ raise LoweringError.new("formatted string temporaries cannot be returned as borrowed text; use std.fmt.format(f\"...\") when ownership must escape",
488
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path)
481
489
  end
482
490
 
483
491
  prepared_cleanup_list = prepared_cleanups.flat_map(&:itself)
@@ -511,7 +519,7 @@ module MilkTea
511
519
  env: local_env,
512
520
  expected_type: target.type,
513
521
  allow_root_statement_foreign: true,
514
- materialize_array_calls: !array_type?(target.type),
522
+ materialize_array_calls: true,
515
523
  )
516
524
  lowered.concat(prepared_setup)
517
525
  if (foreign_call = foreign_call_info(prepared_value, local_env))
@@ -522,8 +530,10 @@ module MilkTea
522
530
  statement_position: false,
523
531
  )
524
532
  lowered.concat(setup)
525
- raise LoweringError, "foreign call used in assignment must return a value" if call_type == @ctx.types.fetch("void")
526
- raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
533
+ raise LoweringError.new("foreign call used in assignment must return a value",
534
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
535
+ raise LoweringError.new("consuming foreign calls must return void",
536
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
527
537
 
528
538
  lowered << IR::Assignment.new(target:, operator: statement.operator, value:)
529
539
  lowered.concat(cleanup_statements)
@@ -700,7 +710,7 @@ module MilkTea
700
710
  env: local_env,
701
711
  expected_type: storage_type,
702
712
  allow_root_statement_foreign: true,
703
- materialize_array_calls: !array_type?(storage_type),
713
+ materialize_array_calls: true,
704
714
  )
705
715
  lowered.concat(prepared_setup)
706
716
  end
@@ -712,8 +722,10 @@ module MilkTea
712
722
  statement_position: false,
713
723
  )
714
724
  lowered.concat(setup)
715
- raise LoweringError, "foreign call used to initialize #{statement.name} must return a value" if call_type == @ctx.types.fetch("void")
716
- raise LoweringError, "consuming foreign calls must return void" unless release_assignments.empty?
725
+ raise LoweringError.new("foreign call used to initialize #{statement.name} must return a value",
726
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) if call_type == @ctx.types.fetch("void")
727
+ raise LoweringError.new("consuming foreign calls must return void",
728
+ line: statement.line, column: statement.column, path: @ctx.current_analysis_path) unless release_assignments.empty?
717
729
 
718
730
  lowered << IR::LocalDecl.new(name: decl_name, linkage_name:, type: storage_type, value:, line: statement.line, source_path: @ctx.current_analysis_path)
719
731
  lowered.concat(cleanup_statements)
@@ -926,16 +938,25 @@ module MilkTea
926
938
  nested_struct_c_name = nil
927
939
  nested_struct_type = nil
928
940
  if fields.size == 1 && fields.values.first.is_a?(Types::Struct)
929
- nested_struct_type = fields.values.first
930
- nested_struct_fields = nested_struct_type.fields
931
- single_field_name = fields.keys.first
932
- nested_struct_c_name = fresh_c_temp_name(arm_local_env, "match_struct")
933
- struct_expr = IR::Member.new(
934
- receiver: IR::Name.new(name: payload_c_name, type: payload_type, pointer: false),
935
- member: single_field_name,
936
- type: nested_struct_type,
937
- )
938
- arm_body_ir << IR::LocalDecl.new(name: nested_struct_c_name, linkage_name: nested_struct_c_name, type: nested_struct_type, value: struct_expr)
941
+ native_field = fields.keys.first
942
+ has_native_reference = arm.pattern.arguments.any? do |arg|
943
+ next false if !arg.name && arg.value.is_a?(AST::Identifier) && arg.value.name == "_"
944
+
945
+ name = arg.name || (arg.value.is_a?(AST::Identifier) ? arg.value.name : nil)
946
+ name == native_field
947
+ end
948
+ unless has_native_reference
949
+ nested_struct_type = fields.values.first
950
+ nested_struct_fields = nested_struct_type.fields
951
+ single_field_name = fields.keys.first
952
+ nested_struct_c_name = fresh_c_temp_name(arm_local_env, "match_struct")
953
+ struct_expr = IR::Member.new(
954
+ receiver: IR::Name.new(name: payload_c_name, type: payload_type, pointer: false),
955
+ member: single_field_name,
956
+ type: nested_struct_type,
957
+ )
958
+ arm_body_ir << IR::LocalDecl.new(name: nested_struct_c_name, linkage_name: nested_struct_c_name, type: nested_struct_type, value: struct_expr)
959
+ end
939
960
  end
940
961
 
941
962
  if arm.pattern.is_a?(AST::Call) && !arm.pattern.arguments.empty?