Module: MilkTea::CBackend::Expressions

Included in:
MilkTea::CBackend
Defined in:
lib/milk_tea/core/c_backend/expressions.rb

Instance Method Summary collapse

Instance Method Details

#aggregate_field_type(type, field_name) ⇒ Object

Raises:



399
400
401
402
403
404
405
406
407
408
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 399

def aggregate_field_type(type, field_name)
  return proc_field_types(type).fetch(field_name) if type.is_a?(Types::Proc)
  if type.is_a?(Types::Nullable)
    return Types::Registry.primitive("bool") if field_name == "has_value"
    return type.base if field_name == "value"
  end
  return type.field(field_name) if type.respond_to?(:field)

  raise CBackendError.new("unsupported aggregate field lookup for #{type}", line: 0, column: 0, path: @path)
end

#binary_precedence(operator) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 150

def binary_precedence(operator)
  case operator
  when "or" then 1
  when "and" then 2
  when "|" then 3
  when "^" then 4
  when "&" then 5
  when "==", "!=" then 6
  when "<", "<=", ">", ">=" then 7
  when "<<", ">>" then 8
  when "+", "-" then 9
  when "*", "/", "%" then 10
  else
    raise CBackendError.new("unsupported binary operator #{operator}", line: 0, column: 0, path: @path)
  end
end

#discarded_expression(expression) ⇒ Object



323
324
325
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 323

def discarded_expression(expression)
  "(void)#{wrap_expression(expression)}"
end

#emit_address_of_operand(expression) ⇒ Object



327
328
329
330
331
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 327

def emit_address_of_operand(expression)
  return emit_expression(expression.operand) if expression.is_a?(IR::Unary) && expression.operator == "*"

  "&#{wrap_expression(expression)}"
end

#emit_addressof_field_initializer(field_type, value) ⇒ Object



444
445
446
447
448
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 444

def emit_addressof_field_initializer(field_type, value)
  c_type_name = named_type_c_name(field_type)
  inner = value.expression
  "((#{c_type_name}*)memcpy(malloc(sizeof(#{c_type_name})), &(#{emit_expression(inner)}), sizeof(#{c_type_name})))"
end

#emit_aggregate_field_initializer(type, field) ⇒ Object



410
411
412
413
414
415
416
417
418
419
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 410

def emit_aggregate_field_initializer(type, field)
  field_type = aggregate_field_type(type, field.name)
  if field_type.is_a?(Types::Nullable) && !field.value.type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(field_type.base)
    emit_nullable_some_initializer(field_type, field.value)
  elsif void_storage_field?(field_type)
    emit_void_field_initializer(field.value)
  else
    emit_initializer(field.value)
  end
end

#emit_aggregate_initializer(expression) ⇒ Object



222
223
224
225
226
227
228
229
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 222

def emit_aggregate_initializer(expression)
  return emit_zero_initializer(expression.type) if expression.fields.empty?

  fields = expression.fields.map do |field|
    ".#{sanitize_c_identifier(field.name)} = #{emit_aggregate_field_initializer(expression.type, field)}"
  end.join(", ")
  "{ #{fields} }"
end

#emit_aggregate_literal(expression) ⇒ Object



250
251
252
253
254
255
256
257
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 250

def emit_aggregate_literal(expression)
  return emit_zero_expression(expression.type) if expression.fields.empty?

  fields = expression.fields.map do |field|
    ".#{sanitize_c_identifier(field.name)} = #{emit_aggregate_field_initializer(expression.type, field)}"
  end.join(", ")
  "(#{c_type(expression.type)}){ #{fields} }"
end

#emit_array_call_statement(expression, out_argument, indent) ⇒ Object



284
285
286
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 284

def emit_array_call_statement(expression, out_argument, indent)
  "#{indent}#{emit_call_expression(expression, array_out_argument: out_argument)};"
end

#emit_array_compound_literal(expression) ⇒ Object



361
362
363
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 361

def emit_array_compound_literal(expression)
  "(#{c_declaration(expression.type, '')}) #{emit_array_initializer(expression)}"
end

#emit_array_copy_statement(destination, source, indent) ⇒ Object



288
289
290
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 288

def emit_array_copy_statement(destination, source, indent)
  "#{indent}memcpy(#{destination}, #{emit_expression(source)}, sizeof(#{destination}));"
end

#emit_array_initializer(expression) ⇒ Object



354
355
356
357
358
359
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 354

def emit_array_initializer(expression)
  return "{ 0 }" if expression.elements.empty?

  elements = expression.elements.map { |element| emit_initializer(element) }.join(", ")
  "{ #{elements} }"
end

#emit_array_out_argument(destination) ⇒ Object



350
351
352
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 350

def emit_array_out_argument(destination)
  "&#{destination}"
end

#emit_array_return(expression, indent) ⇒ Object



292
293
294
295
296
297
298
299
300
301
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 292

def emit_array_return(expression, indent)
  if expression.is_a?(IR::Call) && array_type?(expression.type)
    return [emit_array_call_statement(expression, ARRAY_OUT_PARAM_NAME, indent), "#{indent}return;"]
  end

  [
    emit_array_copy_statement("*#{ARRAY_OUT_PARAM_NAME}", expression, indent),
    "#{indent}return;",
  ]
end

#emit_binary_expression(expression) ⇒ Object



113
114
115
116
117
118
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 113

def emit_binary_expression(expression)
  parent_precedence = binary_precedence(expression.operator)
  left = emit_binary_operand(expression.left, parent_precedence, side: :left)
  right = emit_binary_operand(expression.right, parent_precedence, side: :right)
  "#{left} #{c_operator(expression.operator)} #{right}"
end

#emit_binary_operand(expression, parent_precedence, side:) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 120

def emit_binary_operand(expression, parent_precedence, side:)
  text = emit_expression(expression)

  case expression
  when IR::Conditional
    "(#{text})"
  when IR::Binary
    child_precedence = binary_precedence(expression.operator)
    if child_precedence < parent_precedence || (side == :right && child_precedence == parent_precedence)
      "(#{text})"
    else
      text
    end
  else
    text
  end
end

#emit_call_callee(expression) ⇒ Object



341
342
343
344
345
346
347
348
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 341

def emit_call_callee(expression)
  case expression
  when IR::Name, IR::Member, IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex, IR::Call
    emit_expression(expression)
  else
    wrap_expression(expression)
  end
end

#emit_call_expression(expression, array_out_argument: nil) ⇒ Object



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

def emit_call_expression(expression, array_out_argument: nil)
  callee = expression.callee.is_a?(String) ? expression.callee : emit_call_callee(expression.callee)
  omit_receiver = omitted_method_receiver_call?(expression)
  arguments = []
  arguments << array_out_argument if array_out_argument
  arguments.concat(expression.arguments.drop(omit_receiver ? 1 : 0).map { |argument| emit_expression(argument) })
  call = "#{callee}(#{arguments.join(', ')})"
  return call unless omit_receiver && expression.arguments.any?
  return call if side_effect_free_expression?(expression.arguments.first)

  "(#{discarded_expression(expression.arguments.first)}, #{call})"
end

#emit_cast_operand(expression) ⇒ Object



333
334
335
336
337
338
339
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 333

def emit_cast_operand(expression)
  if expression.is_a?(IR::Binary) || expression.is_a?(IR::Conditional)
    "(#{emit_expression(expression)})"
  else
    emit_expression(expression)
  end
end

#emit_conditional_condition(expression) ⇒ Object



145
146
147
148
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 145

def emit_conditional_condition(expression)
  text = emit_expression(expression)
  expression.is_a?(IR::Conditional) ? "(#{text})" : text
end

#emit_conditional_expression(expression) ⇒ Object



138
139
140
141
142
143
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 138

def emit_conditional_expression(expression)
  condition = emit_conditional_condition(expression.condition)
  then_expression = emit_expression(expression.then_expression)
  else_expression = emit_expression(expression.else_expression)
  "#{condition} ? #{then_expression} : #{else_expression}"
end

#emit_cyclic_array_initializer(field_type, value) ⇒ Object



450
451
452
453
454
455
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 450

def emit_cyclic_array_initializer(field_type, value)
  elem_c_name = c_type(array_element_type(field_type))
  elem_count = array_length(field_type)
  elements = value.is_a?(IR::ArrayLiteral) ? value.elements.map { |e| emit_initializer(e) }.join(", ") : ""
  "((#{elem_c_name}*)memcpy(malloc(#{elem_count} * sizeof(#{elem_c_name})), &(#{elem_c_name}[#{elem_count}]){ #{elements} }, #{elem_count} * sizeof(#{elem_c_name})))"
end

#emit_cyclic_struct_initializer(field_type, value) ⇒ Object



457
458
459
460
461
462
463
464
465
466
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 457

def emit_cyclic_struct_initializer(field_type, value)
  field_c_name = named_type_c_name(field_type)
  init = emit_initializer(value)
  source_expr = if init.start_with?("{")
    "&(#{c_type(field_type)})#{init}"
  else
    "&(#{init})"
  end
  "((#{field_c_name}*)memcpy(malloc(sizeof(#{field_c_name})), #{source_expr}, sizeof(#{field_c_name})))"
end

#emit_expression(expression) ⇒ 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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 6

def emit_expression(expression)
  case expression
  when IR::Name
    expression.name
  when IR::Member
    operator = pointer_member_receiver?(expression.receiver) ? "->" : "."
    member = "#{wrap_member_receiver(expression.receiver)}#{operator}#{sanitize_c_identifier(expression.member)}"
    expression.type.is_a?(Types::Primitive) && expression.type.void? ? "((void)(#{member}))" : member
  when IR::Index
    "#{wrap_index_receiver(expression.receiver)}[#{emit_expression(expression.index)}]"
  when IR::CheckedIndex
    if (alias_name = checked_index_alias(expression))
      "(*#{alias_name})"
    else
      "(*#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)}))"
    end
  when IR::CheckedSpanIndex
    if (alias_name = checked_index_alias(expression))
      "(*#{alias_name})"
    else
      "(*#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)}))"
    end
  when IR::NullableIndex
    "#{nullable_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)})"
  when IR::NullableSpanIndex
    "#{nullable_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
  when IR::Call
    if array_type?(expression.type)
      location = [@source_path, @current_line && "line #{@current_line}"].compact.join(" ")
      msg = "array-return call must be materialized before C emission"
      msg += " (#{location})" unless location.empty?
      raise CBackendError.new(msg, line: @current_line, path: @source_path)
    end

    emit_call_expression(expression)
  when IR::Unary
    if expression.operator == "not"
      "!#{wrap_expression(expression.operand)}"
    else
      "#{expression.operator}#{wrap_expression(expression.operand)}"
    end
  when IR::Binary
    return emit_str_equality_expression(expression) if str_equality_expression?(expression)
    return emit_variant_equality_expression(expression) if variant_equality_expression?(expression)
    return emit_nullable_null_comparison(expression) if nullable_null_comparison?(expression)

    emit_binary_expression(expression)
  when IR::Conditional
    emit_conditional_expression(expression)
  when IR::ReinterpretExpr
    if no_op_reinterpret?(expression.target_type, expression.source_type)
      emit_expression(expression.expression)
    else
      "#{reinterpret_helper_name(expression.target_type, expression.source_type)}(#{emit_expression(expression.expression)})"
    end
  when IR::SizeofExpr
    "sizeof(#{layout_type_expression(expression.target_type)})"
  when IR::AlignofExpr
    "_Alignof(#{layout_type_expression(expression.target_type)})"
  when IR::OffsetofExpr
    "offsetof(#{layout_type_expression(expression.target_type)}, #{sanitize_c_identifier(expression.field)})"
  when IR::IntegerLiteral
    expression.value.to_s
  when IR::FloatLiteral
    emit_float_literal(expression)
  when IR::StringLiteral
    expression.type.is_a?(Types::StringView) ? emit_str_literal(expression) : expression.value.inspect
  when IR::BooleanLiteral
    expression.value ? "true" : "false"
  when IR::NullLiteral
    nullable_value_type?(expression.type) ? emit_zero_expression(expression.type) : "NULL"
  when IR::ZeroInit
    emit_zero_expression(expression.type)
  when IR::AddressOf
    case expression.expression
    when IR::CheckedIndex
      alias_name = checked_index_alias(expression.expression)
      alias_name || "#{checked_array_index_helper_name(expression.expression.receiver_type)}(#{emit_address_of_operand(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
    when IR::CheckedSpanIndex
      alias_name = checked_index_alias(expression.expression)
      alias_name || "#{checked_span_index_helper_name(expression.expression.receiver_type)}(#{emit_expression(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
    else
      emit_address_of_operand(expression.expression)
    end
  when IR::Cast
    if no_op_cast?(expression)
      emit_expression(expression.expression)
    else
      "(#{c_type(expression.target_type)}) #{emit_cast_operand(expression.expression)}"
    end
  when IR::AggregateLiteral
    emit_aggregate_literal(expression)
  when IR::ArrayLiteral
    emit_array_compound_literal(expression)
  when IR::SimdLaneWith
    emit_simd_lane_with(expression)
  when IR::VariantLiteral
    emit_variant_literal(expression)
  else
    raise CBackendError.new("unsupported IR expression #{expression.class.name}", line: 0, column: 0, path: @path)
  end
end

#emit_float_literal(expression) ⇒ Object



484
485
486
487
488
489
490
491
492
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 484

def emit_float_literal(expression)
  value = expression.value
  literal = if value.finite? && value == value.to_i
    format("%.1f", value)
  else
    value.to_s
  end
  expression.type.name == "float" ? "#{literal}f" : literal
end

#emit_initializer(expression) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 205

def emit_initializer(expression)
  case expression
  when IR::ArrayLiteral
    emit_array_initializer(expression)
  when IR::AggregateLiteral
    emit_aggregate_initializer(expression)
  when IR::VariantLiteral
    emit_variant_initializer(expression)
  when IR::StringLiteral
    expression.type.is_a?(Types::StringView) ? emit_str_initializer(expression) : emit_expression(expression)
  when IR::ZeroInit
    emit_zero_initializer(expression.type)
  else
    emit_expression(expression)
  end
end

#emit_nullable_null_comparison(expression) ⇒ Object



199
200
201
202
203
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 199

def emit_nullable_null_comparison(expression)
  operand = expression.left.is_a?(IR::NullLiteral) ? expression.right : expression.left
  access = "#{wrap_member_receiver(operand)}.has_value"
  expression.operator == "==" ? "!#{access}" : access
end

#emit_nullable_some_initializer(nullable_type, value) ⇒ Object



421
422
423
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 421

def emit_nullable_some_initializer(nullable_type, value)
  "(#{c_type(nullable_type)}){ .has_value = true, .value = #{emit_initializer(value)} }"
end

#emit_simd_lane_with(expression) ⇒ Object



365
366
367
368
369
370
371
372
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 365

def emit_simd_lane_with(expression)
  simd_type = expression.type
  elem_c = primitive_c_type(simd_type.element_type.name)
  src = wrap_expression(expression.src)
  idx = emit_expression(expression.index)
  val = wrap_expression(expression.value)
  "({ #{c_type(simd_type)} _mt_with = #{src}; ((#{elem_c}*)&_mt_with)[#{idx}] = #{val}; _mt_with; })"
end

#emit_str_equality_expression(expression) ⇒ Object



167
168
169
170
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 167

def emit_str_equality_expression(expression)
  call = "mt_str_equal(#{emit_expression(expression.left)}, #{emit_expression(expression.right)})"
  expression.operator == "!=" ? "!#{call}" : call
end

#emit_str_initializer(expression) ⇒ Object



242
243
244
245
246
247
248
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 242

def emit_str_initializer(expression)
  if @str_literal_map && @str_literal_map[expression.value]
    @str_literal_map[expression.value]
  else
    "{ .data = #{expression.value.inspect}, .len = #{expression.value.bytesize} }"
  end
end

#emit_str_literal(expression) ⇒ Object



476
477
478
479
480
481
482
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 476

def emit_str_literal(expression)
  if @str_literal_map && @str_literal_map[expression.value]
    @str_literal_map[expression.value]
  else
    "(mt_str){ .data = #{expression.value.inspect}, .len = #{expression.value.bytesize} }"
  end
end

#emit_variant_equality_expression(expression) ⇒ Object



177
178
179
180
181
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 177

def emit_variant_equality_expression(expression)
  helper_name = variant_equality_helper_name(expression.left.type)
  call = "#{helper_name}(#{emit_expression(expression.left)}, #{emit_expression(expression.right)})"
  expression.operator == "!=" ? "!#{call}" : call
end

#emit_variant_field_initializer(type, arm_name, field) ⇒ Object



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 425

def emit_variant_field_initializer(type, arm_name, field)
  field_type = type.arm(arm_name).fetch(field.name)
  outer_c = named_type_c_name(type)

  if field_type.is_a?(Types::Nullable) && !field.value.type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(field_type.base)
    emit_nullable_some_initializer(field_type, field.value)
  elsif field.value.is_a?(IR::AddressOf) && !field_type.is_a?(Types::Nullable)
    emit_addressof_field_initializer(field_type, field.value)
  elsif aggregate_field_creates_cycle?(field_type, outer_c) && array_type?(field_type)
    emit_cyclic_array_initializer(field_type, field.value)
  elsif aggregate_field_creates_cycle?(field_type, outer_c) && !field_type.is_a?(Types::GenericInstance)
    emit_cyclic_struct_initializer(field_type, field.value)
  elsif void_storage_field?(field_type)
    emit_void_field_initializer(field.value)
  else
    emit_initializer(field.value)
  end
end

#emit_variant_initializer(expression) ⇒ Object



231
232
233
234
235
236
237
238
239
240
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 231

def emit_variant_initializer(expression)
  outer_c = named_type_c_name(expression.type)
  kind_constant = "#{outer_c}_kind_#{expression.arm_name}"
  if expression.fields.empty?
    "{ .kind = #{kind_constant} }"
  else
    payload_fields = expression.fields.map { |field| ".#{sanitize_c_identifier(field.name)} = #{emit_variant_field_initializer(expression.type, expression.arm_name, field)}" }.join(", ")
    "{ .kind = #{kind_constant}, .data.#{sanitize_c_identifier(expression.arm_name)} = { #{payload_fields} } }"
  end
end

#emit_variant_literal(expression) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 259

def emit_variant_literal(expression)
  outer_c = named_type_c_name(expression.type)
  kind_constant = "#{outer_c}_kind_#{expression.arm_name}"
  if expression.fields.empty?
    "(#{outer_c}){ .kind = #{kind_constant} }"
  else
    arm_c = "#{outer_c}_#{expression.arm_name}"
    payload_fields = expression.fields.map { |field| ".#{sanitize_c_identifier(field.name)} = #{emit_variant_field_initializer(expression.type, expression.arm_name, field)}" }.join(", ")
    "(#{outer_c}){ .kind = #{kind_constant}, .data.#{sanitize_c_identifier(expression.arm_name)} = (struct #{arm_c}){ #{payload_fields} } }"
  end
end

#emit_void_field_initializer(expression) ⇒ Object



468
469
470
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 468

def emit_void_field_initializer(expression)
  "(#{emit_expression(expression)}, 0)"
end

#emit_zero_expression(type) ⇒ Object



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

def emit_zero_expression(type)
  return "(#{c_type(type)}) #{emit_zero_initializer(type)}" if type.is_a?(Types::StringView)
  return "NULL" if type.is_a?(Types::Nullable) && c_backend_pointer_like_type?(type.base)
  return "(#{c_type(type)}){ 0 }" if type.is_a?(Types::Nullable)
  return emit_zero_initializer(type) if type.is_a?(Types::Primitive)
  return emit_zero_initializer(type) if type.is_a?(Types::EnumBase)

  "(#{c_declaration(type, '')}) #{emit_zero_initializer(type)}"
end

#emit_zero_initializer(type) ⇒ Object



374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 374

def emit_zero_initializer(type)
  return "{ 0 }" if type.is_a?(Types::StringView)
  return "{ 0 }" if array_type?(type)
  return "NULL" if type.is_a?(Types::Nullable) && c_backend_pointer_like_type?(type.base)
  return "{ 0 }" if type.is_a?(Types::Nullable)
  return "NULL" if raw_pointer_type?(type) || ref_type?(type)
  return "NULL" if type.is_a?(Types::Opaque) && !type.external
  return "false" if type.is_a?(Types::Primitive) && type.boolean?
  return "0.0" if type.is_a?(Types::Primitive) && type.float?
  return "0" if type.is_a?(Types::Primitive) && !type.void?
  return "(#{c_type(type)}) 0" if type.is_a?(Types::EnumBase)

  "{ 0 }"
end

#emitted_function_params(function) ⇒ Object



303
304
305
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 303

def emitted_function_params(function)
  omitted_method_receiver_function?(function) ? function.params.drop(1) : function.params
end

#layout_type_expression(type) ⇒ Object



503
504
505
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 503

def layout_type_expression(type)
  c_declaration(type, "")
end

#nullable_null_comparison?(expression) ⇒ Boolean

Returns:

  • (Boolean)


192
193
194
195
196
197
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 192

def nullable_null_comparison?(expression)
  return false unless EQUALITY_OPERATORS.include?(expression.operator)

  (nullable_value_type?(expression.left.type) && expression.right.is_a?(IR::NullLiteral)) ||
    (nullable_value_type?(expression.right.type) && expression.left.is_a?(IR::NullLiteral))
end

#nullable_value_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 188

def nullable_value_type?(type)
  type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(type.base)
end

#omitted_method_receiver_call?(expression) ⇒ Boolean

Returns:

  • (Boolean)


307
308
309
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 307

def omitted_method_receiver_call?(expression)
  expression.callee.is_a?(String) && omitted_method_receiver_function_names.key?(expression.callee)
end

#omitted_method_receiver_function?(function) ⇒ Boolean

Returns:

  • (Boolean)


317
318
319
320
321
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 317

def omitted_method_receiver_function?(function)
  function.method_receiver_param &&
    function.params.first &&
    name_reference_count_in_statements(function.body, function.params.first.linkage_name).zero?
end

#omitted_method_receiver_function_namesObject



311
312
313
314
315
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 311

def omitted_method_receiver_function_names
  @omitted_method_receiver_function_names ||= emitted_functions.each_with_object({}) do |function, omitted|
    omitted[function.linkage_name] = true if omitted_method_receiver_function?(function)
  end
end

#str_equality_expression?(expression) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 109

def str_equality_expression?(expression)
  EQUALITY_OPERATORS.include?(expression.operator) && expression.left.type.is_a?(Types::StringView) && expression.right.type.is_a?(Types::StringView)
end

#variant_equality_expression?(expression) ⇒ Boolean

Returns:

  • (Boolean)


172
173
174
175
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 172

def variant_equality_expression?(expression)
  EQUALITY_OPERATORS.include?(expression.operator) &&
    (expression.left.type.is_a?(Types::Variant) || expression.left.type.is_a?(Types::VariantArmPayload))
end

#variant_equality_helper_name(type) ⇒ Object



183
184
185
186
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 183

def variant_equality_helper_name(type)
  variant = type.is_a?(Types::VariantArmPayload) ? type.variant_type : type
  "mt_variant_eq_#{named_type_c_name(variant)}"
end

#void_storage_field?(type) ⇒ Boolean

Returns:

  • (Boolean)


472
473
474
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 472

def void_storage_field?(type)
  type.is_a?(Types::Primitive) && type.void?
end

#wrap_expression(expression) ⇒ Object



494
495
496
497
498
499
500
501
# File 'lib/milk_tea/core/c_backend/expressions.rb', line 494

def wrap_expression(expression)
  case expression
  when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::Member, IR::Index, IR::Call, IR::AggregateLiteral, IR::ArrayLiteral, IR::ReinterpretExpr, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr
    emit_expression(expression)
  else
    "(#{emit_expression(expression)})"
  end
end