Module: MilkTea::TypePredicates

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



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

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



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

def atomic_element_type(type)
  type.arguments.first
end

#atomic_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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)


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

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

#char_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#collection_loop_binding_type(iterable_type, element_type) ⇒ Object



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

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)


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

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

#collection_loop_type(type) ⇒ Object



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

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



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

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

#const_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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)


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

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)


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

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

#dyn_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#exact_integer_constant_value(value) ⇒ Object



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

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)


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

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

#external_numeric_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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



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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


237
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
# File 'lib/milk_tea/core/types/predicates.rb', line 237

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)


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

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)


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

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



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

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

#foreign_span_boundary_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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



5
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
# File 'lib/milk_tea/core/types/predicates.rb', line 5

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)


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

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)


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

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)


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

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)


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

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)


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

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)


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

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

#own_to_raw_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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

#owned_referent_type(type) ⇒ Object



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

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

#pointee_type(type) ⇒ Object



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

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

  type.arguments.first
end

#pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#proc_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#quat_vec4_layout_compatible?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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)


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

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)


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

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

#referenced_type(type) ⇒ Object



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

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)


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

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)


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

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)


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

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

#string_builder_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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)


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

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

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



551
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
# File 'lib/milk_tea/core/types/predicates.rb', line 551

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)


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

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)


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

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