mt-lang 0.3.30 → 0.3.32

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.
@@ -236,17 +236,45 @@ module MilkTea
236
236
  lines << ""
237
237
  end
238
238
 
239
+ if uses_variant_equality_helper? || uses_struct_equality_helper?
240
+ lines.concat(emit_aggregate_equality_forward_declarations)
241
+ lines << ""
242
+ end
239
243
  if uses_variant_equality_helper?
240
244
  unless uses_str_equality_helper?
241
- variant_needs_str_eq = emitted_aggregate_variants.any? { |v| v.arms.any? { |a| a.fields.any? { |f| f.type.is_a?(Types::StringView) } } }
242
- if variant_needs_str_eq
243
- lines.concat(emit_string_type) unless uses_string_view?
244
- lines.concat(emit_str_equality_helper)
245
- end
245
+ emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
246
246
  end
247
247
  lines.concat(emit_variant_equality_helpers)
248
248
  lines << ""
249
249
  end
250
+ if uses_struct_equality_helper?
251
+ unless uses_str_equality_helper?
252
+ emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
253
+ end
254
+ lines.concat(emit_struct_equality_helpers)
255
+ lines << ""
256
+ end
257
+ end
258
+
259
+ def emit_aggregate_equality_forward_declarations
260
+ lines = []
261
+ struct_equality_types.each do |type|
262
+ c = named_type_c_name(type)
263
+ lines << "static bool mt_struct_eq_#{c}(struct #{c} left, struct #{c} right);"
264
+ end
265
+ variant_equality_types.each do |type|
266
+ c = named_type_c_name(type)
267
+ lines << "static bool mt_variant_eq_#{c}(struct #{c} left, struct #{c} right);"
268
+ end
269
+ lines
270
+ end
271
+
272
+ def emit_str_equality_support(lines)
273
+ return if @emitted_aggregate_str_equality_support
274
+
275
+ @emitted_aggregate_str_equality_support = true
276
+ lines.concat(emit_string_type) unless uses_string_view?
277
+ lines.concat(emit_str_equality_helper)
250
278
  end
251
279
 
252
280
  def emit_function_forward_declarations(lines)
@@ -31,10 +31,35 @@ module MilkTea
31
31
  return left == right if left.is_a?(String) && right.is_a?(String)
32
32
  return left == right if boolean_value?(left) && boolean_value?(right)
33
33
  return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
34
+ return struct_equality_result(left, right) if left.is_a?(Hash) && right.is_a?(Hash)
35
+ return variant_equality_result(left, right) if left.is_a?(VariantValue) && right.is_a?(VariantValue)
34
36
 
35
37
  nil
36
38
  end
37
39
 
40
+ # Const-time struct values are represented as {field_name => value} hashes;
41
+ # compare them field by field, mirroring the runtime struct == semantics.
42
+ def self.struct_equality_result(left, right)
43
+ return nil unless left.is_a?(Hash) && right.is_a?(Hash)
44
+ return nil unless left.keys.sort == right.keys.sort
45
+
46
+ left.each do |name, value|
47
+ field_result = equality_result(value, right[name])
48
+ return nil if field_result.nil?
49
+ return false if field_result == false
50
+ end
51
+ true
52
+ end
53
+
54
+ # Const-time variant values; fields mirror the runtime arm payload layout.
55
+ VariantValue = Data.define(:arm, :fields)
56
+
57
+ def self.variant_equality_result(left, right)
58
+ return false unless left.arm == right.arm
59
+
60
+ struct_equality_result(left.fields, right.fields)
61
+ end
62
+
38
63
  def self.boolean_value?(value)
39
64
  value == true || value == false
40
65
  end
@@ -324,7 +324,7 @@ module MilkTea
324
324
  newline_start = line_offset + line.length
325
325
  newline_end = has_newline ? (newline_start + 1) : newline_start
326
326
  if @grouping_depth.zero?
327
- if Token::LINE_CONTINUATION_OPERATORS.include?(@tokens.last&.type)
327
+ if Token::LINE_CONTINUATION_OPERATORS.include?(@tokens.last&.type) && !trailing_is_member_access?
328
328
  @continuation_pending = true
329
329
  else
330
330
  @tokens << build_token(:newline, "\n", nil, line_number, line.length + 1, start_offset: newline_start, end_offset: newline_end)
@@ -345,6 +345,10 @@ module MilkTea
345
345
  1
346
346
  end
347
347
 
348
+ def trailing_is_member_access?
349
+ @tokens.last&.type == :is && @tokens[-2]&.type == :dot
350
+ end
351
+
348
352
  def emit_newline(line, line_number, line_offset, has_newline)
349
353
  newline_start = line_offset + line.bytesize
350
354
  newline_end = has_newline ? (newline_start + 1) : newline_start
@@ -78,6 +78,14 @@ module MilkTea
78
78
  IR::AggregateField.new(name:, value: lower_const_value_literal(field_type, field_value))
79
79
  end
80
80
  IR::AggregateLiteral.new(type:, fields:)
81
+ when CompileTime::VariantValue
82
+ arm_field_types = type.respond_to?(:arm) ? (type.arm(const_value.arm) || {}) : {}
83
+ fields = const_value.fields.map do |field_name, field_value|
84
+ field_type = arm_field_types[field_name]
85
+ raise LoweringError.new("constant variant arm #{const_value.arm} field #{field_name} not found in #{type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless field_type
86
+ IR::AggregateField.new(name: field_name, value: lower_const_value_literal(field_type, field_value))
87
+ end
88
+ IR::VariantLiteral.new(type:, arm_name: const_value.arm, fields:)
81
89
  else
82
90
  raise LoweringError.new("unsupported const value type #{const_value.class}", line: 0, column: 0, path: @ctx.current_analysis_path)
83
91
  end
@@ -454,7 +454,7 @@ module MilkTea
454
454
  match(:async)
455
455
  match(:editable) if check(:editable)
456
456
  match(:static) if check(:static)
457
- result = check(:function)
457
+ result = check(:function) && !check_next(:colon)
458
458
  @current = saved
459
459
  result
460
460
  end
@@ -468,11 +468,13 @@ module MilkTea
468
468
 
469
469
  visibility, visibility_token = parse_visibility
470
470
 
471
- if match(:event)
471
+ if check(:event) && !check_next(:colon)
472
+ advance
472
473
  return [:event, parse_event_decl(visibility:, attributes: field_attributes)]
473
474
  end
474
475
 
475
- if match(:struct)
476
+ if check(:struct) && !check_next(:colon)
477
+ advance
476
478
  return [:nested_type, parse_struct_decl(visibility:, attributes: field_attributes, inline_methods: false)]
477
479
  end
478
480
 
@@ -741,7 +743,10 @@ module MilkTea
741
743
  end
742
744
 
743
745
  def parse_visibility
744
- return [:public, previous] if match(:public)
746
+ if check(:public) && !check_next(:colon)
747
+ advance
748
+ return [:public, previous]
749
+ end
745
750
 
746
751
  [:private, nil]
747
752
  end
@@ -270,10 +270,19 @@ module MilkTea
270
270
 
271
271
  if expected_type.is_a?(Types::Primitive) && expected_type.integer? &&
272
272
  value_fits_integer_type?(expression.value, expected_type)
273
- expected_type
274
- else
275
- @ctx.types.fetch("int")
273
+ return expected_type
276
274
  end
275
+
276
+ # A bare literal (no suffix, no fitting expected type) picks the
277
+ # narrowest fixed-width type that holds its value: int -> long -> ulong.
278
+ # Falling through to int unconditionally emitted overflowing C
279
+ # (e.g. `int32_t x = 2147483648;`).
280
+ ["int", "long", "ulong"].each do |name|
281
+ candidate = @ctx.types.fetch(name)
282
+ return candidate if value_fits_integer_type?(expression.value, candidate)
283
+ end
284
+
285
+ raise_sema_error("integer literal #{expression.value} does not fit in any integer type", expression)
277
286
  end
278
287
 
279
288
  INTEGER_SUFFIX_TYPES = {
@@ -300,11 +309,23 @@ module MilkTea
300
309
  @ctx.types.fetch("double")
301
310
  elsif expected_type.is_a?(Types::Primitive) && expected_type.float?
302
311
  expected_type
303
- else
312
+ elsif float_literal_fits_in_float?(expression.value)
304
313
  @ctx.types.fetch("float")
314
+ else
315
+ # A bare float literal beyond float32 range would silently overflow
316
+ # to infinity in C (`float x = 1e40`); promote to double instead.
317
+ @ctx.types.fetch("double")
305
318
  end
306
319
  end
307
320
 
321
+ FLOAT32_MAX_MAGNITUDE = 3.4028234663852886e+38
322
+
323
+ def float_literal_fits_in_float?(value)
324
+ return false unless value.is_a?(Numeric) && value.finite?
325
+
326
+ value.abs <= FLOAT32_MAX_MAGNITUDE
327
+ end
328
+
308
329
  def infer_identifier(expression, scopes:, expected_type: nil)
309
330
  binding = lookup_value(expression.name, scopes)
310
331
  if binding
@@ -662,8 +683,16 @@ module MilkTea
662
683
  when "==", "!="
663
684
  unless c_natively_equality_comparable_type?(left_type) && c_natively_equality_comparable_type?(right_type)
664
685
  bad_type = c_natively_equality_comparable_type?(right_type) ? left_type : right_type
665
- if struct_instance_type?(bad_type)
666
- raise_sema_error("operator #{expression.operator} is not supported for struct type #{bad_type}; use equal[#{bad_type}](...) instead")
686
+ if bad_type.is_a?(Types::Union)
687
+ raise_sema_error("operator #{expression.operator} is not supported for union type #{bad_type}; use equal[#{bad_type}](...) instead")
688
+ elsif bad_type.is_a?(Types::Variant)
689
+ field = first_non_equality_comparable_variant_field(bad_type)
690
+ field_hint = field ? " (arm '#{field[0]}' field '#{field[1]}' of type #{field[2]} is not equality-comparable)" : ""
691
+ raise_sema_error("operator #{expression.operator} is not supported for variant type #{bad_type}#{field_hint}; use equal[#{bad_type}](...) instead")
692
+ elsif struct_instance_type?(bad_type) && !bad_type.is_a?(Types::Variant)
693
+ field = first_non_equality_comparable_field(bad_type)
694
+ field_hint = field ? " (field '#{field.first}' of type #{field.last} is not equality-comparable)" : ""
695
+ raise_sema_error("operator #{expression.operator} is not supported for struct type #{bad_type}#{field_hint}; use equal[#{bad_type}](...) instead")
667
696
  else
668
697
  raise_sema_error("operator #{expression.operator} is not supported for type #{bad_type}")
669
698
  end
@@ -902,23 +902,65 @@ module MilkTea
902
902
  type.is_a?(Types::Struct) || type.is_a?(Types::Variant)
903
903
  end
904
904
 
905
- def c_natively_equality_comparable_type?(type)
905
+ def c_natively_equality_comparable_type?(type, visiting = nil)
906
906
  return true if type.is_a?(Types::Primitive)
907
907
  return true if type.is_a?(Types::EnumBase)
908
908
  return true if type.is_a?(Types::Opaque)
909
- return true if type.is_a?(Types::Nullable)
909
+ return true if type.is_a?(Types::Nullable) && equality_comparable_field_type?(type.base, visiting)
910
910
  return true if type.is_a?(Types::Null)
911
911
  return true if type.is_a?(Types::Function)
912
912
  return true if type.is_a?(Types::Error)
913
913
  return true if type.is_a?(Types::StringView)
914
914
  return true if pointer_type?(type)
915
915
  return true if ref_type?(type)
916
- return true if type.is_a?(Types::Variant)
916
+ return true if type.is_a?(Types::Variant) && equality_comparable_variant_type?(type, visiting)
917
917
  return true if type.is_a?(Types::VariantArmPayload)
918
+ return true if equality_comparable_struct_type?(type, visiting)
919
+
920
+ false
921
+ end
922
+
923
+ # A struct value is equality-comparable with ==/!= when every field is
924
+ # itself equality-comparable. Untagged unions are excluded because their
925
+ # active-field comparison is ambiguous.
926
+ def equality_comparable_struct_type?(type, visiting = nil)
927
+ type.is_a?(Types::Struct) && !type.is_a?(Types::Union) &&
928
+ type.fields.all? { |_name, field_type| equality_comparable_field_type?(field_type, visiting) }
929
+ end
930
+
931
+ def equality_comparable_field_type?(type, visiting = nil)
932
+ return true if c_natively_equality_comparable_type?(type, visiting)
933
+ return true if array_type?(type) && equality_comparable_field_type?(array_element_type(type), visiting)
918
934
 
919
935
  false
920
936
  end
921
937
 
938
+ # A variant is equality-comparable when every arm payload field is itself
939
+ # equality-comparable. A variant that reaches itself through value fields
940
+ # (a recursive/cyclic variant) is comparable too: the C backend embeds
941
+ # cyclic fields as pointers, and those fields compare by pointer identity.
942
+ def equality_comparable_variant_type?(type, visiting = nil)
943
+ return true if visiting&.key?(type)
944
+
945
+ visiting = (visiting || {}).merge(type => true)
946
+ type.arm_names.all? do |arm_name|
947
+ (type.arm(arm_name) || {}).all? { |_field_name, field_type| equality_comparable_field_type?(field_type, visiting) }
948
+ end
949
+ end
950
+
951
+ def first_non_equality_comparable_field(struct_type)
952
+ struct_type.fields.find { |_name, field_type| !equality_comparable_field_type?(field_type) }
953
+ end
954
+
955
+ def first_non_equality_comparable_variant_field(variant_type)
956
+ variant_type.arm_names.each do |arm_name|
957
+ (variant_type.arm(arm_name) || {}).each do |field_name, field_type|
958
+ return [arm_name, field_name, field_type] unless equality_comparable_field_type?(field_type)
959
+ end
960
+ end
961
+ nil
962
+ end
963
+
922
964
  def collection_loop_type(type)
923
965
  super
924
966
  end
@@ -301,7 +301,12 @@ module MilkTea
301
301
  end
302
302
 
303
303
  if (receiver_type = resolve_type_expression(member_access_expression.receiver))
304
- next resolve_enum_member_const_value(receiver_type, member_access_expression.member)
304
+ if receiver_type.is_a?(Types::EnumBase)
305
+ next resolve_enum_member_const_value(receiver_type, member_access_expression.member)
306
+ end
307
+ if receiver_type.is_a?(Types::Variant)
308
+ next CompileTime::VariantValue.new(arm: member_access_expression.member, fields: {})
309
+ end
305
310
  end
306
311
 
307
312
  next unless member_access_expression.receiver.is_a?(AST::Identifier)
@@ -321,6 +326,31 @@ module MilkTea
321
326
 
322
327
  def evaluate_compile_time_call(expression, scopes: nil)
323
328
  case expression.callee
329
+ when AST::MemberAccess
330
+ if (receiver_type = resolve_type_expression(expression.callee.receiver)) && receiver_type.is_a?(Types::Variant)
331
+ arm_name = expression.callee.member
332
+ fields = {}
333
+ expression.arguments.each do |argument|
334
+ val = CompileTime.evaluate(argument.value, resolve_identifier: lambda { |id|
335
+ if scopes
336
+ binding = lookup_value(id.name, scopes)
337
+ return binding.const_value unless binding&.const_value.nil?
338
+ end
339
+ resolve_current_module_const_value(id.name)
340
+ }, resolve_member_access: lambda { |ma|
341
+ if (member_receiver_type = resolve_type_expression(ma.receiver))
342
+ next resolve_enum_member_const_value(member_receiver_type, ma.member) if member_receiver_type.is_a?(Types::EnumBase)
343
+ next CompileTime::VariantValue.new(arm: ma.member, fields: {}) if member_receiver_type.is_a?(Types::Variant)
344
+ end
345
+ nil
346
+ }, resolve_call: lambda { |inner_call|
347
+ evaluate_compile_time_call(inner_call, scopes:)
348
+ })
349
+ return nil unless val
350
+ fields[argument.name] = val
351
+ end
352
+ return CompileTime::VariantValue.new(arm: arm_name, fields: fields)
353
+ end
324
354
  when AST::Identifier
325
355
  if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct)
326
356
  fields = {}
data/std/c/box2d.mt CHANGED
@@ -424,7 +424,7 @@ enum b2BodyType: int
424
424
  b2_bodyTypeCount = 3
425
425
 
426
426
  struct b2BodyDef:
427
- type_: b2BodyType
427
+ type: b2BodyType
428
428
  position: b2Vec2
429
429
  rotation: b2Rot
430
430
  linearVelocity: b2Vec2
data/std/c/cgltf.mt CHANGED
@@ -44,7 +44,7 @@ struct cgltf_file_options:
44
44
  user_data: ptr[void]
45
45
 
46
46
  struct cgltf_options:
47
- type_: cgltf_file_type
47
+ type: cgltf_file_type
48
48
  json_token_count: ptr_uint
49
49
  memory: cgltf_memory_options
50
50
  file: cgltf_file_options
@@ -186,7 +186,7 @@ struct cgltf_buffer_view:
186
186
  offset: ptr_uint
187
187
  size: ptr_uint
188
188
  stride: ptr_uint
189
- type_: cgltf_buffer_view_type
189
+ type: cgltf_buffer_view_type
190
190
  data: ptr[void]
191
191
  has_meshopt_compression: int
192
192
  meshopt_compression: cgltf_meshopt_compression
@@ -206,7 +206,7 @@ struct cgltf_accessor:
206
206
  name: ptr[char]
207
207
  component_type: cgltf_component_type
208
208
  normalized: int
209
- type_: cgltf_type
209
+ type: cgltf_type
210
210
  offset: ptr_uint
211
211
  count: ptr_uint
212
212
  stride: ptr_uint
@@ -223,7 +223,7 @@ struct cgltf_accessor:
223
223
 
224
224
  struct cgltf_attribute:
225
225
  name: ptr[char]
226
- type_: cgltf_attribute_type
226
+ type: cgltf_attribute_type
227
227
  index: int
228
228
  data: ptr[cgltf_accessor]
229
229
 
@@ -398,7 +398,7 @@ struct cgltf_material:
398
398
  extensions: ptr[cgltf_extension]
399
399
 
400
400
  struct cgltf_material_mapping:
401
- variant_: ptr_uint
401
+ variant: ptr_uint
402
402
  material: ptr[cgltf_material]
403
403
  extras: cgltf_extras
404
404
 
@@ -416,7 +416,7 @@ struct cgltf_mesh_gpu_instancing:
416
416
  attributes_count: ptr_uint
417
417
 
418
418
  struct cgltf_primitive:
419
- type_: cgltf_primitive_type
419
+ type: cgltf_primitive_type
420
420
  indices: ptr[cgltf_accessor]
421
421
  material: ptr[cgltf_material]
422
422
  attributes: ptr[cgltf_attribute]
@@ -471,7 +471,7 @@ struct cgltf_camera_orthographic:
471
471
 
472
472
  struct cgltf_camera:
473
473
  name: ptr[char]
474
- type_: cgltf_camera_type
474
+ type: cgltf_camera_type
475
475
  data: cgltf_camera_data
476
476
  extras: cgltf_extras
477
477
  extensions_count: ptr_uint
@@ -481,7 +481,7 @@ struct cgltf_light:
481
481
  name: ptr[char]
482
482
  color: array[cgltf_float, 3]
483
483
  intensity: float
484
- type_: cgltf_light_type
484
+ type: cgltf_light_type
485
485
  range: float
486
486
  spot_inner_cone_angle: float
487
487
  spot_outer_cone_angle: float
data/std/c/cjson.mt CHANGED
@@ -8,7 +8,7 @@ struct cJSON:
8
8
  next: ptr[cJSON]
9
9
  prev: ptr[cJSON]
10
10
  child: ptr[cJSON]
11
- type_: int
11
+ type: int
12
12
  valuestring: ptr[char]
13
13
  valueint: int
14
14
  valuedouble: double
data/std/c/curl.mt CHANGED
@@ -29,7 +29,7 @@ struct curl_header = c"struct curl_header":
29
29
 
30
30
  struct curl_ws_frame = c"struct curl_ws_frame":
31
31
  age: int
32
- flags_: int
32
+ flags: int
33
33
  offset: ptr_int
34
34
  bytesleft: ptr_int
35
35
  len: ptr_uint
@@ -69,7 +69,7 @@ struct curl_httppost = c"struct curl_httppost":
69
69
  contenttype: ptr[char]
70
70
  contentheader: ptr[curl_slist]
71
71
  more: ptr[curl_httppost]
72
- flags_: ptr_int
72
+ flags: ptr_int
73
73
  showfilename: ptr[char]
74
74
  userp: ptr[void]
75
75
  contentlen: ptr_int
@@ -100,7 +100,7 @@ struct curl_fileinfo = c"struct curl_fileinfo":
100
100
  size: ptr_int
101
101
  hardlinks: ptr_int
102
102
  strings: curl_fileinfo_strings
103
- flags_: uint
103
+ flags: uint
104
104
  b_data: ptr[char]
105
105
  b_size: ptr_uint
106
106
  b_used: ptr_uint
@@ -1069,8 +1069,8 @@ external function curl_url_strerror(error: int) -> cstr
1069
1069
  struct curl_easyoption = c"struct curl_easyoption":
1070
1070
  name: cstr
1071
1071
  id: CURLoption
1072
- type_: int
1073
- flags_: uint
1072
+ type: int
1073
+ flags: uint
1074
1074
 
1075
1075
  external function curl_easy_option_by_name(name: cstr) -> const_ptr[curl_easyoption]
1076
1076
  external function curl_easy_option_by_id(id: CURLoption) -> const_ptr[curl_easyoption]
data/std/c/enet.mt CHANGED
@@ -213,7 +213,7 @@ type ENetPacketFreeCallback = fn(arg0: ptr[ENetPacket]) -> void
213
213
 
214
214
  struct ENetPacket:
215
215
  referenceCount: ptr_uint
216
- flags_: uint
216
+ flags: uint
217
217
  data: ptr[enet_uint8]
218
218
  dataLength: ptr_uint
219
219
  freeCallback: fn(arg0: ptr[ENetPacket]) -> void
@@ -327,7 +327,7 @@ struct ENetPeer:
327
327
  outgoingSendReliableCommands: ENetList
328
328
  outgoingCommands: ENetList
329
329
  dispatchedCommands: ENetList
330
- flags_: ushort
330
+ flags: ushort
331
331
  reserved: ushort
332
332
  incomingUnsequencedGroup: ushort
333
333
  outgoingUnsequencedGroup: ushort
@@ -389,7 +389,7 @@ enum ENetEventType: int
389
389
  ENET_EVENT_TYPE_RECEIVE = 3
390
390
 
391
391
  struct ENetEvent:
392
- type_: ENetEventType
392
+ type: ENetEventType
393
393
  peer: ptr[ENetPeer]
394
394
  channelID: ubyte
395
395
  data: uint