Module: MilkTea::Types::Predicates

Included in:
CompatibilityHelpers, StoredRefSupportedVisitor
Defined in:
lib/milk_tea/core/types/predicates.rb

Instance Method Summary collapse

Instance Method Details

#arity_error_message(function_type, name, actual_count, required_count: nil) ⇒ Object



482
483
484
485
486
487
488
489
490
# File 'lib/milk_tea/core/types/predicates.rb', line 482

def arity_error_message(function_type, name, actual_count, required_count: nil)
  if function_type.is_a?(Types::Function) && function_type.variadic
    "function #{name} expects at least #{function_type.params.length} arguments, got #{actual_count}"
  elsif required_count && required_count < function_type.params.length
    "function #{name} expects #{required_count}..#{function_type.params.length} arguments, got #{actual_count}"
  else
    "function #{name} expects #{function_type.params.length} arguments, got #{actual_count}"
  end
end

#atomic_element_type(type) ⇒ Object



430
431
432
# File 'lib/milk_tea/core/types/predicates.rb', line 430

def atomic_element_type(type)
  type.arguments.first
end

#atomic_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


426
427
428
# File 'lib/milk_tea/core/types/predicates.rb', line 426

def atomic_type?(type)
  type.is_a?(Types::GenericInstance) && type.name == "atomic" && type.arguments.length == 1
end

#call_arity_matches?(function_type, actual_count) ⇒ Boolean

Returns:

  • (Boolean)


476
477
478
479
480
# File 'lib/milk_tea/core/types/predicates.rb', line 476

def call_arity_matches?(function_type, actual_count)
  return actual_count >= function_type.params.length if function_type.is_a?(Types::Function) && function_type.variadic

  actual_count == function_type.params.length
end

#callable_param_ref_supported?(type) ⇒ Boolean

Returns:

  • (Boolean)


500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/milk_tea/core/types/predicates.rb', line 500

def callable_param_ref_supported?(type)
  case type
  when Types::Proc
    type.params.all? { |param| ref_type?(param.type) || !contains_ref_type?(param.type) } &&
      !contains_ref_type?(type.return_type)
  when Types::Function
    type.params.all? { |param| ref_type?(param.type) || !contains_ref_type?(param.type) } &&
      !contains_ref_type?(type.return_type) &&
      (type.receiver_type.nil? || !contains_ref_type?(type.receiver_type))
  else
    false
  end
end

#char_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


394
395
396
# File 'lib/milk_tea/core/types/predicates.rb', line 394

def char_pointer_type?(type)
  pointer_type?(type) && type.arguments.first == Types::Registry.primitive("char")
end

#char_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/milk_tea/core/types/predicates.rb', line 148

def char_type?(type)
  type.is_a?(Types::Primitive) && type.name == "char"
end

#collection_loop_binding_type(iterable_type, element_type) ⇒ Object



533
534
535
536
537
538
# File 'lib/milk_tea/core/types/predicates.rb', line 533

def collection_loop_binding_type(iterable_type, element_type)
  return nil unless Types.array_type?(iterable_type) || iterable_type.is_a?(Types::Span)
  return nil unless collection_loop_ref_element_type?(element_type)

  Types::Registry.generic_instance("ref", [element_type])
end

#collection_loop_ref_element_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


540
541
542
# File 'lib/milk_tea/core/types/predicates.rb', line 540

def collection_loop_ref_element_type?(type)
  type.is_a?(Types::Struct)
end

#collection_loop_type(type) ⇒ Object



526
527
528
529
530
531
# File 'lib/milk_tea/core/types/predicates.rb', line 526

def collection_loop_type(type)
  return type.arguments.first if Types.array_type?(type)
  return type.element_type if type.is_a?(Types::Span)

  nil
end

#const_pointer_to(type) ⇒ Object



472
473
474
# File 'lib/milk_tea/core/types/predicates.rb', line 472

def const_pointer_to(type)
  Types::Registry.generic_instance("const_ptr", [type])
end

#const_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


410
411
412
# File 'lib/milk_tea/core/types/predicates.rb', line 410

def const_pointer_type?(type)
  type.is_a?(Types::GenericInstance) && type.name == "const_ptr" && type.arguments.length == 1
end

#contains_ref_type?(type, visited = {}, allow_lifetimes: []) ⇒ Boolean

Returns:

  • (Boolean)


514
515
516
517
518
# File 'lib/milk_tea/core/types/predicates.rb', line 514

def contains_ref_type?(type, visited = {}, allow_lifetimes: [])
  visitor = ContainsRefTypeVisitor.new(allow_lifetimes:)
  visitor.visit(type)
  visitor.found?
end

#contains_type_var?(type) ⇒ Boolean

Returns:

  • (Boolean)


520
521
522
523
524
# File 'lib/milk_tea/core/types/predicates.rb', line 520

def contains_type_var?(type)
  visitor = ContainsTypeVarVisitor.new
  visitor.visit(type)
  visitor.found?
end

#contextual_int_to_float_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


181
182
183
184
# File 'lib/milk_tea/core/types/predicates.rb', line 181

def contextual_int_to_float_compatibility?(actual_type, expected_type)
  actual_type.is_a?(Types::Primitive) && actual_type.integer? &&
    expected_type.is_a?(Types::Primitive) && expected_type.float?
end

#contextual_int_to_float_target?(type) ⇒ Boolean

Returns:

  • (Boolean)


186
187
188
# File 'lib/milk_tea/core/types/predicates.rb', line 186

def contextual_int_to_float_target?(type)
  type.is_a?(Types::Primitive) && type.float?
end

#dyn_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


444
445
446
# File 'lib/milk_tea/core/types/predicates.rb', line 444

def dyn_type?(type)
  type.is_a?(Types::Dyn)
end

#exact_integer_constant_value(value) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/milk_tea/core/types/predicates.rb', line 110

def exact_integer_constant_value(value)
  return value if value.is_a?(Integer)
  return nil unless value.is_a?(Float) && value.finite?

  integer_value = value.to_i
  integer_value.to_f == value ? integer_value : nil
end

#exactly_representable_float32?(value) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/milk_tea/core/types/predicates.rb', line 133

def exactly_representable_float32?(value)
  [value].pack("f").unpack1("f") == value
end

#external_numeric_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
159
160
# File 'lib/milk_tea/core/types/predicates.rb', line 152

def external_numeric_compatibility?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Primitive) && expected_type.is_a?(Types::Primitive)
  return false unless actual_type.numeric? && expected_type.numeric?

  return lossless_external_integer_compatibility?(actual_type, expected_type) if actual_type.integer? && expected_type.integer?
  return expected_type.float_width >= actual_type.float_width if actual_type.float? && expected_type.float?

  false
end

#external_typed_null_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
# File 'lib/milk_tea/core/types/predicates.rb', line 70

def external_typed_null_pointer_compatibility?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Null)
  return false unless actual_type.target_type
  return false if expected_type.is_a?(Types::Nullable)
  return true if expected_type == Types::Registry.primitive("cstr") && char_pointer_type?(actual_type.target_type)
  return false unless pointer_type?(expected_type)

  actual_type.target_type == expected_type
end

#flatten_field_types(type) ⇒ Object



384
385
386
387
388
389
390
391
392
# File 'lib/milk_tea/core/types/predicates.rb', line 384

def flatten_field_types(type)
  if type.is_a?(Types::Primitive)
    [type]
  elsif type.respond_to?(:fields) && type.fields
    type.fields.values.flat_map { |ft| flatten_field_types(ft) }
  else
    [type]
  end
end

#float_constant_fits_type?(value, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/milk_tea/core/types/predicates.rb', line 95

def float_constant_fits_type?(value, expected_type)
  return false unless value.is_a?(Numeric)
  return false unless value.finite?

  if expected_type.name == "float"
    return value.abs <= (1 << 24) if value.is_a?(Integer)

    exactly_representable_float32?(value.to_f)
  else
    return true if expected_type.name == "double"

    false
  end
end

#foreign_boundary_element_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


225
226
227
228
229
230
231
# File 'lib/milk_tea/core/types/predicates.rb', line 225

def foreign_boundary_element_compatible?(public_type, boundary_type)
  return true if public_type == boundary_type
  return true if public_type == Types::Registry.string_view && boundary_type == Types::Registry.primitive("cstr")
  return true if public_type == Types::Registry.string_view && char_pointer_type?(boundary_type)

  foreign_identity_projection_compatible?(public_type, boundary_type)
end

#foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


215
216
217
218
219
220
221
222
223
# File 'lib/milk_tea/core/types/predicates.rb', line 215

def foreign_char_pointer_buffer_boundary_compatible?(public_type, boundary_type)
  return false unless char_pointer_type?(boundary_type)

  return true if public_type.is_a?(Types::Span) && public_type.element_type == Types::Registry.primitive("char")
  return true if char_array_text_type?(public_type)
  return true if str_buffer_type?(public_type)

  false
end

#foreign_external_layout_compatible?(actual_type, expected_type, seen = {}) ⇒ Boolean

Returns:

  • (Boolean)


323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/milk_tea/core/types/predicates.rb', line 323

def foreign_external_layout_compatible?(actual_type, expected_type, seen = {})
  return false unless actual_type.is_a?(Types::Struct) && expected_type.is_a?(Types::Struct)
  return false if actual_type.is_a?(Types::Union) != expected_type.is_a?(Types::Union)
  return false unless actual_type.external && expected_type.external
  return false unless actual_type.name == expected_type.name
  return false unless actual_type.packed == expected_type.packed
  return false unless actual_type.alignment == expected_type.alignment

  key = [actual_type.object_id, expected_type.object_id]
  return true if seen[key]

  seen[key] = true
  actual_fields = actual_type.fields
  expected_fields = expected_type.fields
  return false unless actual_fields.keys == expected_fields.keys

  actual_fields.all? do |field_name, field_type|
    foreign_external_layout_field_compatible?(field_type, expected_fields.fetch(field_name), seen)
  end
end

#foreign_external_layout_field_compatible?(actual_type, expected_type, seen) ⇒ Boolean

Returns:

  • (Boolean)


344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/milk_tea/core/types/predicates.rb', line 344

def foreign_external_layout_field_compatible?(actual_type, expected_type, seen)
  return true if actual_type == expected_type

  if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable)
    return foreign_external_layout_field_compatible?(actual_type.base, expected_type.base, seen)
  end

  if pointer_type?(actual_type) && pointer_type?(expected_type)
    return foreign_external_layout_field_compatible?(actual_type.arguments.first, expected_type.arguments.first, seen)
  end

  if array_type?(actual_type) && array_type?(expected_type)
    return false unless array_length(actual_type) == array_length(expected_type)

    return foreign_external_layout_field_compatible?(array_element_type(actual_type), array_element_type(expected_type), seen)
  end

  foreign_external_layout_compatible?(actual_type, expected_type, seen)
end

#foreign_function_type_projection_compatible?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/milk_tea/core/types/predicates.rb', line 285

def foreign_function_type_projection_compatible?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Function) && expected_type.is_a?(Types::Function)
  return false unless actual_type.receiver_type == expected_type.receiver_type
  return false unless actual_type.variadic == expected_type.variadic
  return false unless actual_type.params.length == expected_type.params.length
  return false unless foreign_identity_projection_compatible?(actual_type.return_type, expected_type.return_type)

  actual_type.params.zip(expected_type.params).all? do |actual_param, expected_param|
    actual_param.mutable == expected_param.mutable &&
      actual_param.passing_mode == expected_param.passing_mode &&
      actual_param.boundary_type == expected_param.boundary_type &&
      foreign_identity_projection_compatible?(actual_param.type, expected_param.type)
  end
end

#foreign_identity_projection_cast_compatible?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/milk_tea/core/types/predicates.rb', line 238

def foreign_identity_projection_cast_compatible?(actual_type, expected_type)
  return true if actual_type == expected_type
  return true if mutable_to_const_pointer_compatibility?(actual_type, expected_type)
  return true if same_external_opaque_c_name?(actual_type, expected_type)
  return true if foreign_function_type_projection_compatible?(actual_type, expected_type)
  return true if native_foreign_layout_compatible?(actual_type, expected_type)
  return true if native_foreign_layout_compatible?(expected_type, actual_type)
  return true if quat_vec4_layout_compatible?(actual_type, expected_type)

  if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable)
    return foreign_identity_projection_cast_compatible?(actual_type.base, expected_type.base)
  end

  return foreign_identity_projection_cast_compatible?(actual_type, expected_type.base) if expected_type.is_a?(Types::Nullable)
  return false if actual_type.is_a?(Types::Nullable)

  return true if same_external_opaque_handle_pointer_compatibility?(actual_type, expected_type)

  if pointer_type?(actual_type) && pointer_type?(expected_type)
    return false if const_pointer_type?(actual_type) && mutable_pointer_type?(expected_type)
    return true if void_pointer_type?(actual_type) || void_pointer_type?(expected_type)

    return true if foreign_identity_projection_cast_compatible?(actual_type.arguments.first, expected_type.arguments.first)

    return foreign_external_layout_compatible?(actual_type.arguments.first, expected_type.arguments.first)
  end

  return true if void_pointer_type?(actual_type) && opaque_type?(expected_type)
  return true if opaque_type?(actual_type) && void_pointer_type?(expected_type)
  return true if char_pointer_type?(actual_type) && expected_type == Types::Registry.primitive("cstr")
  return true if actual_type == Types::Registry.primitive("cstr") && char_pointer_type?(expected_type)

  false
end

#foreign_identity_projection_compatible?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
236
# File 'lib/milk_tea/core/types/predicates.rb', line 233

def foreign_identity_projection_compatible?(actual_type, expected_type)
  foreign_identity_projection_cast_compatible?(actual_type, expected_type) ||
    foreign_identity_projection_reinterpret_compatible?(actual_type, expected_type)
end

#foreign_identity_projection_reinterpret_compatible?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


312
313
314
315
316
317
318
319
320
321
# File 'lib/milk_tea/core/types/predicates.rb', line 312

def foreign_identity_projection_reinterpret_compatible?(actual_type, expected_type)
  if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable)
    return foreign_identity_projection_reinterpret_compatible?(actual_type.base, expected_type.base)
  end

  return foreign_identity_projection_reinterpret_compatible?(actual_type, expected_type.base) if expected_type.is_a?(Types::Nullable)
  return false if actual_type.is_a?(Types::Nullable)

  foreign_external_layout_compatible?(actual_type, expected_type)
end

#foreign_opaque_c_name(type) ⇒ Object



308
309
310
# File 'lib/milk_tea/core/types/predicates.rb', line 308

def foreign_opaque_c_name(type)
  type.linkage_name || type.name
end

#foreign_span_boundary_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


209
210
211
212
213
# File 'lib/milk_tea/core/types/predicates.rb', line 209

def foreign_span_boundary_compatible?(public_type, boundary_type)
  return false unless public_type.is_a?(Types::Span) && boundary_type.is_a?(Types::Span)

  foreign_boundary_element_compatible?(public_type.element_type, boundary_type.element_type)
end

#function_type_matches_proc_type?(function_type, proc_type) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/milk_tea/core/types/predicates.rb', line 49

def function_type_matches_proc_type?(function_type, proc_type)
  return false if function_type.receiver_type || function_type.variadic
  return false unless function_type.params.length == proc_type.params.length
  return false unless function_type.return_type == proc_type.return_type

  function_type.params.zip(proc_type.params).all? do |function_param, proc_param|
    next false unless function_param.mutable == proc_param.mutable

    function_param.type == proc_param.type ||
      same_external_opaque_handle_pointer_compatibility?(function_param.type, proc_param.type)
  end
end

#generic_integer_type_argument?(argument) ⇒ Boolean

Returns:

  • (Boolean)


548
549
550
# File 'lib/milk_tea/core/types/predicates.rb', line 548

def generic_integer_type_argument?(argument)
  integer_type_argument?(argument) || argument.is_a?(Types::TypeVar)
end

#integer_constant_fits_type?(value, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
91
92
93
# File 'lib/milk_tea/core/types/predicates.rb', line 88

def integer_constant_fits_type?(value, expected_type)
  integer_value = exact_integer_constant_value(value)
  return false if integer_value.nil?

  value_fits_integer_type?(integer_value, expected_type)
end

#integer_like_char_source_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


141
142
143
144
145
146
# File 'lib/milk_tea/core/types/predicates.rb', line 141

def integer_like_char_source_type?(type)
  return true if type.is_a?(Types::Primitive) && type.integer?
  return true if type.is_a?(Types::EnumBase) && type.backing_type.is_a?(Types::Primitive) && type.backing_type.integer?

  false
end

#integer_to_char_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/milk_tea/core/types/predicates.rb', line 137

def integer_to_char_compatibility?(actual_type, expected_type)
  char_type?(expected_type) && integer_like_char_source_type?(actual_type)
end

#integer_type_argument?(argument) ⇒ Boolean

Returns:

  • (Boolean)


544
545
546
# File 'lib/milk_tea/core/types/predicates.rb', line 544

def integer_type_argument?(argument)
  argument.is_a?(Types::LiteralTypeArg) && argument.value.is_a?(Integer)
end

#lossless_external_integer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
165
166
167
168
169
170
171
172
# File 'lib/milk_tea/core/types/predicates.rb', line 162

def lossless_external_integer_compatibility?(actual_type, expected_type)
  return false unless actual_type.fixed_width_integer? && expected_type.fixed_width_integer?

  if actual_type.signed_integer? == expected_type.signed_integer?
    return expected_type.integer_width >= actual_type.integer_width
  end

  return false if actual_type.signed_integer?

  expected_type.signed_integer? && expected_type.integer_width > actual_type.integer_width
end

#lossless_integer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


174
175
176
177
178
179
# File 'lib/milk_tea/core/types/predicates.rb', line 174

def lossless_integer_compatibility?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Primitive) && expected_type.is_a?(Types::Primitive)
  return false unless actual_type.integer? && expected_type.integer?

  lossless_external_integer_compatibility?(actual_type, expected_type)
end

#method_dispatch_receiver_type(receiver_type) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/milk_tea/core/types/predicates.rb', line 6

def method_dispatch_receiver_type(receiver_type)
  return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)

  if receiver_type.is_a?(Types::Nullable)
    dispatch_base_type = method_dispatch_receiver_type(receiver_type.base)
    return receiver_type if dispatch_base_type == receiver_type.base

    return Types::Registry.nullable(dispatch_base_type)
  end

  case receiver_type
  when Types::Span
    normalized = Types::Registry.span(Types::TypeVar.new("__receiver_arg0"))
    return normalized == receiver_type ? receiver_type : normalized
  when Types::SoA
    normalized = Types::Registry.soa(Types::TypeVar.new("__receiver_arg0"), count: 0)
    return normalized == receiver_type ? receiver_type : normalized
  when Types::Simd
    normalized = Types::Registry.simd(Types::TypeVar.new("__receiver_arg0"), lane_count: 0)
    return normalized == receiver_type ? receiver_type : normalized
  when Types::Task
    normalized = Types::Registry.task(Types::TypeVar.new("__receiver_arg0"))
    return normalized == receiver_type ? receiver_type : normalized
  when Types::Dyn
    normalized = Types::Registry.dyn(Types::TypeVar.new("__receiver_arg0"))
    return normalized == receiver_type ? receiver_type : normalized
  end

  return receiver_type unless receiver_type.is_a?(Types::GenericInstance)

  dispatch_receiver_type = Types::Registry.generic_instance(
    receiver_type.name,
    receiver_type.arguments.each_with_index.map do |_argument, index|
      Types::TypeVar.new("__receiver_arg#{index}")
    end,
  )
  dispatch_receiver_type == receiver_type ? receiver_type : dispatch_receiver_type
end

#mutable_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


406
407
408
# File 'lib/milk_tea/core/types/predicates.rb', line 406

def mutable_pointer_type?(type)
  type.is_a?(Types::GenericInstance) && type.name == "ptr" && type.arguments.length == 1
end

#mutable_to_const_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


190
191
192
193
194
195
196
197
# File 'lib/milk_tea/core/types/predicates.rb', line 190

def mutable_to_const_pointer_compatibility?(actual_type, expected_type)
  return mutable_to_const_pointer_compatibility?(actual_type.base, expected_type.base) if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable)
  return mutable_to_const_pointer_compatibility?(actual_type, expected_type.base) if expected_type.is_a?(Types::Nullable)
  return false if actual_type.is_a?(Types::Nullable)
  return false unless (mutable_pointer_type?(actual_type) || own_type?(actual_type)) && const_pointer_type?(expected_type)

  pointee_type(actual_type) == pointee_type(expected_type)
end

#native_foreign_layout_compatible?(native_type, foreign_type) ⇒ Boolean

Returns:

  • (Boolean)


368
369
370
371
372
373
374
375
376
377
# File 'lib/milk_tea/core/types/predicates.rb', line 368

def native_foreign_layout_compatible?(native_type, foreign_type)
  return false unless (native_type.is_a?(Types::Vector) || native_type.is_a?(Types::Matrix) || native_type.is_a?(Types::Quaternion))
  return false unless foreign_type.is_a?(Types::Struct) && foreign_type.external

  native_flat = flatten_field_types(native_type)
  foreign_flat = flatten_field_types(foreign_type)
  return false unless native_flat.size == foreign_flat.size

  native_flat.zip(foreign_flat).all? { |nf, ff| nf == ff }
end

#null_assignable_to?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
# File 'lib/milk_tea/core/types/predicates.rb', line 62

def null_assignable_to?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Null)
  return false unless expected_type.is_a?(Types::Nullable)
  return true unless actual_type.target_type

  actual_type.target_type == expected_type.base
end

#numeric_constant_fits_type?(value, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
86
# File 'lib/milk_tea/core/types/predicates.rb', line 80

def numeric_constant_fits_type?(value, expected_type)
  if expected_type.integer?
    integer_constant_fits_type?(value, expected_type)
  else
    float_constant_fits_type?(value, expected_type)
  end
end

#opaque_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


398
399
400
# File 'lib/milk_tea/core/types/predicates.rb', line 398

def opaque_type?(type)
  type.is_a?(Types::Opaque)
end

#own_to_raw_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


199
200
201
202
203
204
205
206
207
# File 'lib/milk_tea/core/types/predicates.rb', line 199

def own_to_raw_pointer_compatibility?(actual_type, expected_type)
  return own_to_raw_pointer_compatibility?(actual_type.base, expected_type.base) if actual_type.is_a?(Types::Nullable) && expected_type.is_a?(Types::Nullable)
  return own_to_raw_pointer_compatibility?(actual_type, expected_type.base) if expected_type.is_a?(Types::Nullable)
  return false if actual_type.is_a?(Types::Nullable)
  return false unless own_type?(actual_type)
  return false unless mutable_pointer_type?(expected_type) || const_pointer_type?(expected_type)

  pointee_type(actual_type) == pointee_type(expected_type)
end

#own_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


414
415
416
# File 'lib/milk_tea/core/types/predicates.rb', line 414

def own_type?(type)
  type.is_a?(Types::GenericInstance) && type.name == "own" && type.arguments.length == 1
end

#owned_referent_type(type) ⇒ Object



418
419
420
# File 'lib/milk_tea/core/types/predicates.rb', line 418

def owned_referent_type(type)
  type.arguments.first if own_type?(type)
end

#pointee_type(type) ⇒ Object



460
461
462
463
464
# File 'lib/milk_tea/core/types/predicates.rb', line 460

def pointee_type(type)
  return unless pointer_type?(type)

  type.arguments.first
end

#pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


402
403
404
# File 'lib/milk_tea/core/types/predicates.rb', line 402

def pointer_type?(type)
  mutable_pointer_type?(type) || const_pointer_type?(type) || own_type?(type)
end

#proc_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


456
457
458
# File 'lib/milk_tea/core/types/predicates.rb', line 456

def proc_type?(type)
  type.is_a?(Types::Proc)
end

#quat_vec4_layout_compatible?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


379
380
381
382
# File 'lib/milk_tea/core/types/predicates.rb', line 379

def quat_vec4_layout_compatible?(a, b)
  (a.is_a?(Types::Quaternion) && b.is_a?(Types::Vector) && b.width == 4 && b.element_type.name == "float") ||
    (b.is_a?(Types::Quaternion) && a.is_a?(Types::Vector) && a.width == 4 && a.element_type.name == "float")
end

#ref_lifetime(type) ⇒ Object



438
439
440
441
442
# File 'lib/milk_tea/core/types/predicates.rb', line 438

def ref_lifetime(type)
  return unless type.is_a?(Types::GenericInstance) && type.name == "ref" && type.arguments.length == 2

  type.arguments.first
end

#ref_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


422
423
424
# File 'lib/milk_tea/core/types/predicates.rb', line 422

def ref_type?(type)
  type.is_a?(Types::GenericInstance) && type.name == "ref" && [1, 2].include?(type.arguments.length)
end

#ref_type_without_lifetime?(type) ⇒ Boolean

Returns:

  • (Boolean)


434
435
436
# File 'lib/milk_tea/core/types/predicates.rb', line 434

def ref_type_without_lifetime?(type)
  type.is_a?(Types::GenericInstance) && type.name == "ref" && type.arguments.length == 1
end

#referenced_type(type) ⇒ Object



466
467
468
469
470
# File 'lib/milk_tea/core/types/predicates.rb', line 466

def referenced_type(type)
  return unless ref_type?(type)

  type.arguments.length == 1 ? type.arguments.first : type.arguments[1]
end

#same_external_opaque_c_name?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


300
301
302
303
304
305
306
# File 'lib/milk_tea/core/types/predicates.rb', line 300

def same_external_opaque_c_name?(actual_type, expected_type)
  return false unless actual_type.is_a?(Types::Opaque) && expected_type.is_a?(Types::Opaque)
  return false unless actual_type.external || actual_type.linkage_name
  return false unless expected_type.external || expected_type.linkage_name

  foreign_opaque_c_name(actual_type) == foreign_opaque_c_name(expected_type)
end

#same_external_opaque_handle_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


273
274
275
276
277
278
279
280
281
282
283
# File 'lib/milk_tea/core/types/predicates.rb', line 273

def same_external_opaque_handle_pointer_compatibility?(actual_type, expected_type)
  if actual_type.is_a?(Types::Opaque) && pointer_type?(expected_type)
    return same_external_opaque_c_name?(actual_type, expected_type.arguments.first)
  end

  if pointer_type?(actual_type) && expected_type.is_a?(Types::Opaque)
    return same_external_opaque_c_name?(actual_type.arguments.first, expected_type)
  end

  false
end

#string_builder_ref_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


496
497
498
# File 'lib/milk_tea/core/types/predicates.rb', line 496

def string_builder_ref_type?(type)
  ref_type?(type) && string_builder_type?(referenced_type(type))
end

#string_builder_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


492
493
494
# File 'lib/milk_tea/core/types/predicates.rb', line 492

def string_builder_type?(type)
  type.respond_to?(:name) && type.respond_to?(:module_name) && type.name == "String" && type.module_name == "std.string"
end

#struct_with_target_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


452
453
454
# File 'lib/milk_tea/core/types/predicates.rb', line 452

def struct_with_target_type?(type)
  type.is_a?(Types::Struct) || type.is_a?(Types::Vector) || type.is_a?(Types::Matrix) || type.is_a?(Types::Quaternion)
end

#task_root_proc_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/milk_tea/core/types/predicates.rb', line 45

def task_root_proc_type?(type)
  proc_type?(type) && type.params.empty? && type.return_type.is_a?(Types::Task)
end

#task_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


448
449
450
# File 'lib/milk_tea/core/types/predicates.rb', line 448

def task_type?(type)
  type.is_a?(Types::Task)
end

#validate_generic_type!(name, arguments, &error) ⇒ Object



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
# File 'lib/milk_tea/core/types/predicates.rb', line 552

def validate_generic_type!(name, arguments, &error)
  case name
  when "ptr"
    error.call("ptr requires exactly one type argument") unless arguments.length == 1
    error.call("ptr type argument must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
  when "const_ptr"
    error.call("const_ptr requires exactly one type argument") unless arguments.length == 1
    error.call("const_ptr type argument must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
    error.call("const_ptr cannot target ref types") if contains_ref_type?(arguments.first)
  when "own"
    error.call("own requires exactly one type argument") unless arguments.length == 1
    error.call("own type argument must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
  when "ref"
    unless [1, 2].include?(arguments.length)
      error.call("ref requires exactly one type argument")
    end
    type_arg = arguments.length == 1 ? arguments.first : arguments[1]
    error.call("ref type argument must be a type") if type_arg.is_a?(Types::LiteralTypeArg)
    error.call("ref cannot target void") if type_arg.is_a?(Types::Primitive) && type_arg.void?
    if contains_ref_type?(type_arg)
      unless (type_arg.is_a?(Types::Struct) || type_arg.is_a?(Types::StructInstance)) && type_arg.lifetime_params&.any?
        error.call("ref cannot target another ref type")
      end
    end
  when "span"
    error.call("span requires exactly one type argument") unless arguments.length == 1
    error.call("span element type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
  when "array"
    error.call("array requires exactly two type arguments") unless arguments.length == 2
    error.call("array element type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
    error.call("array length must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments[1])
    error.call("array length must be positive") if integer_type_argument?(arguments[1]) && !arguments[1].value.positive?
  when "SoA"
    error.call("SoA requires exactly two type arguments") unless arguments.length == 2
    error.call("SoA element type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
    error.call("SoA element type must be a struct with fields") unless arguments.first.respond_to?(:fields) && arguments.first.fields.any?
    error.call("SoA length must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments[1])
    error.call("SoA length must be positive") if integer_type_argument?(arguments[1]) && !arguments[1].value.positive?
  when "simd"
    error.call("simd requires exactly two type arguments") unless arguments.length == 2
    error.call("simd element type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
    error.call("simd element type must be a numeric primitive") unless arguments.first.is_a?(Types::Primitive) && arguments.first.numeric?
    error.call("simd lane count must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments[1])
    error.call("simd lane count must be positive") if integer_type_argument?(arguments[1]) && !arguments[1].value.positive?
    if integer_type_argument?(arguments[1])
      width_bytes = arguments.first.name == "double" || arguments.first.name == "long" || arguments.first.name == "ulong" ? arguments[1].value * 8 : arguments.first.name == "int" || arguments.first.name == "uint" || arguments.first.name == "float" ? arguments[1].value * 4 : arguments.first.name == "short" || arguments.first.name == "ushort" ? arguments[1].value * 2 : arguments[1].value
      valid = [16, 32].include?(width_bytes)
      error.call("simd width must be 128 or 256 bits, got #{width_bytes * 8}") unless valid
    end
  when "str_buffer"
    error.call("str_buffer requires exactly one type argument") unless arguments.length == 1
    error.call("str_buffer capacity must be an integer literal, named const, or type parameter") unless generic_integer_type_argument?(arguments.first)
    error.call("str_buffer capacity must be positive") if integer_type_argument?(arguments.first) && !arguments.first.value.positive?
  when "Task"
    error.call("Task requires exactly one type argument") unless arguments.length == 1
    error.call("Task result type must be a type") if arguments.first.is_a?(Types::LiteralTypeArg)
  when "atomic"
    error.call("atomic requires exactly one type argument") unless arguments.length == 1
    arg = arguments.first
    error.call("atomic type argument must be a type") if arg.is_a?(Types::LiteralTypeArg)
    error.call("atomic type argument must be a primitive integer or bool") unless arg.is_a?(Types::Primitive) && (arg.integer? || arg.boolean?)
  else
    error.call("unknown generic type #{name}")
  end
end

#value_fits_integer_type?(value, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/milk_tea/core/types/predicates.rb', line 118

def value_fits_integer_type?(value, expected_type)
  return false unless expected_type.is_a?(Types::Primitive) && expected_type.integer?

  width = expected_type.integer_width
  return false if width.nil?

  min_value, max_value = if expected_type.signed_integer?
                          [-(1 << (width - 1)), (1 << (width - 1)) - 1]
                        else
                          [0, (1 << width) - 1]
                        end

  value >= min_value && value <= max_value
end

#void_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


364
365
366
# File 'lib/milk_tea/core/types/predicates.rb', line 364

def void_pointer_type?(type)
  pointer_type?(type) && type.arguments.first == Types::Registry.primitive("void")
end