Module: MilkTea::Types::Predicates

Included in:
Intrinsics, 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



487
488
489
490
491
492
493
494
495
# File 'lib/milk_tea/core/types/predicates.rb', line 487

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

#array_element_type(type) ⇒ Object



650
651
652
# File 'lib/milk_tea/core/types/predicates.rb', line 650

def array_element_type(type)
  type.arguments.first
end

#array_length(type) ⇒ Object



646
647
648
# File 'lib/milk_tea/core/types/predicates.rb', line 646

def array_length(type)
  type.arguments[1].value
end

#array_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


634
635
636
# File 'lib/milk_tea/core/types/predicates.rb', line 634

def array_type?(type)
  type.arguments.length == 2 && type.arguments[1].is_a?(Types::LiteralTypeArg) && type.arguments[1].value.is_a?(Integer)
end

#atomic_element_type(type) ⇒ Object



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

def atomic_element_type(type)
  type.arguments.first
end

#atomic_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/milk_tea/core/types/predicates.rb', line 505

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_array_text_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


623
624
625
626
627
628
# File 'lib/milk_tea/core/types/predicates.rb', line 623

def char_array_text_type?(type)
  return false unless array_type?(type)

  element = array_element_type(type)
  element.is_a?(Types::Primitive) && element.name == "char"
end

#char_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#char_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/milk_tea/core/types/predicates.rb', line 153

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

#collection_loop_binding_type(iterable_type, element_type) ⇒ Object



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

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)


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

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

#collection_loop_type(type) ⇒ Object



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

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



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

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

#const_pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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)


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

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)


191
192
193
# File 'lib/milk_tea/core/types/predicates.rb', line 191

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

#dyn_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#exact_integer_constant_value(value) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/milk_tea/core/types/predicates.rb', line 115

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)


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

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

#external_numeric_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


157
158
159
160
161
162
163
164
165
# File 'lib/milk_tea/core/types/predicates.rb', line 157

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



389
390
391
392
393
394
395
396
397
# File 'lib/milk_tea/core/types/predicates.rb', line 389

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
109
110
111
112
113
# 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)
  elsif expected_type.name == "double"
    # Every integer with magnitude <= 2^53 is exactly representable in
    # double precision; larger integer constants would lose precision, so
    # they require an explicit cast instead.
    return value.abs <= (1 << 53) if value.is_a?(Integer)

    true
  else
    false
  end
end

#foreign_boundary_element_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


220
221
222
223
224
225
226
227
228
# File 'lib/milk_tea/core/types/predicates.rb', line 220

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)


328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/milk_tea/core/types/predicates.rb', line 328

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)


349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/milk_tea/core/types/predicates.rb', line 349

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)


290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/milk_tea/core/types/predicates.rb', line 290

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)


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
272
273
274
275
276
# File 'lib/milk_tea/core/types/predicates.rb', line 243

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)


238
239
240
241
# File 'lib/milk_tea/core/types/predicates.rb', line 238

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)


317
318
319
320
321
322
323
324
325
326
# File 'lib/milk_tea/core/types/predicates.rb', line 317

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



313
314
315
# File 'lib/milk_tea/core/types/predicates.rb', line 313

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

#foreign_span_boundary_compatible?(public_type, boundary_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


553
554
555
# File 'lib/milk_tea/core/types/predicates.rb', line 553

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)


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

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)


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

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)


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

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)


167
168
169
170
171
172
173
174
175
176
177
# File 'lib/milk_tea/core/types/predicates.rb', line 167

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)


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

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)


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

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)


195
196
197
198
199
200
201
202
# File 'lib/milk_tea/core/types/predicates.rb', line 195

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)


373
374
375
376
377
378
379
380
381
382
# File 'lib/milk_tea/core/types/predicates.rb', line 373

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)


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

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

#own_to_raw_pointer_compatibility?(actual_type, expected_type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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

#owned_referent_type(type) ⇒ Object



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

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

#pointee_type(type) ⇒ Object



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

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

  type.arguments.first
end

#pointer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#proc_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#quat_vec4_layout_compatible?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


384
385
386
387
# File 'lib/milk_tea/core/types/predicates.rb', line 384

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



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

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)


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

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)


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

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

#referenced_type(type) ⇒ Object



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

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)


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

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)


278
279
280
281
282
283
284
285
286
287
288
# File 'lib/milk_tea/core/types/predicates.rb', line 278

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

#simd_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


642
643
644
# File 'lib/milk_tea/core/types/predicates.rb', line 642

def simd_type?(type)
  type.is_a?(Types::Simd)
end

#soa_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


638
639
640
# File 'lib/milk_tea/core/types/predicates.rb', line 638

def soa_type?(type)
  type.is_a?(Types::SoA)
end

#str_buffer_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


630
631
632
# File 'lib/milk_tea/core/types/predicates.rb', line 630

def str_buffer_type?(type)
  type.arguments.length == 1 && type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
end

#string_builder_ref_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


501
502
503
# File 'lib/milk_tea/core/types/predicates.rb', line 501

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

#string_builder_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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

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



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
617
618
619
620
621
# File 'lib/milk_tea/core/types/predicates.rb', line 557

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)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/milk_tea/core/types/predicates.rb', line 123

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)


369
370
371
# File 'lib/milk_tea/core/types/predicates.rb', line 369

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