Class: Stone::Type::Union

Inherits:
Stone::Type show all
Defined in:
lib/stone/type.rb

Overview

Union type - represents a value that can be one of several types

Constant Summary

Constants inherited from Stone::Type

PRIMITIVE_ALIGNMENTS, PRIMITIVE_SIZES

Instance Attribute Summary collapse

Attributes inherited from Stone::Type

#fields, #llvm_type, #max, #min, #name, #param_types, #property_types, #return_type

Instance Method Summary collapse

Methods inherited from Stone::Type

#as_String, function, #inspect, #payload_llvm_type, #pointer_type?, primitive, record, #to_s, union

Constructor Details

#initialize(alternatives:) ⇒ Union

Returns a new instance of Union.



173
174
175
176
177
178
# File 'lib/stone/type.rb', line 173

def initialize(alternatives:)
  @alternatives = flatten_and_dedupe(alternatives)
  fail ::ArgumentError, "Union type requires at least one alternative" if @alternatives.empty?

  super(name: generate_name, llvm_type: create_variable_sized_llvm_type)
end

Instance Attribute Details

#alternativesObject (readonly)

Returns the value of attribute alternatives.



171
172
173
# File 'lib/stone/type.rb', line 171

def alternatives
  @alternatives
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



291
292
293
294
295
# File 'lib/stone/type.rb', line 291

def ==(other)
  return false unless other.is_a?(Union)

  Set.new(@alternatives) == Set.new(other.alternatives)
end

#alignmentObject



184
185
186
187
# File 'lib/stone/type.rb', line 184

def alignment
  # Union alignment is max of pointer alignment and payload alignment
  [8, payload_alignment].max
end

#common_llvm_result_typeObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/stone/type.rb', line 197

def common_llvm_result_type
  # Determine best common type for phi merge without int2ptr/ptr2int conversions.
  # - If ALL non-null alternatives are pointers, use pointer
  # - If ALL non-null alternatives are integers, use i64
  # - For MIXED types, return nil (caller should not extract, return union as-is)
  non_null_alts = @alternatives.reject { |alt| alt.name == "Null" }
  return LLVM::Int64.type if non_null_alts.empty?

  all_pointers = non_null_alts.all?(&:pointer_type?)
  all_integers = non_null_alts.all? { |alt| %w[Int Bool].include?(alt.name) }

  return LLVM::Type.pointer if all_pointers
  return LLVM::Int64.type if all_integers

  nil # Mixed types - don't extract
end

#compatible_with?(other) ⇒ Boolean

Returns:

  • (Boolean)


279
280
281
# File 'lib/stone/type.rb', line 279

def compatible_with?(other)
  other.union? ? covers_all_alternatives?(other) : includes_compatible_type?(other)
end

#function?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/stone/type.rb', line 244

def function?
  false
end

#hashObject



298
299
300
# File 'lib/stone/type.rb', line 298

def hash
  Set.new(@alternatives).hash
end

#homogeneous?Boolean

Returns:

  • (Boolean)


214
215
216
217
# File 'lib/stone/type.rb', line 214

def homogeneous?
  # Returns true if all non-null alternatives have compatible LLVM types
  !common_llvm_result_type.nil?
end

#needs_runtime_type_tag?Boolean

Returns true if the union contains types that cannot be distinguished by value alone. For example, Bool | Int - both are integers, and Bool(1) looks like Int(1). Such unions require returning the runtime type tag to Ruby for proper interpretation.

Returns:

  • (Boolean)


222
223
224
225
# File 'lib/stone/type.rb', line 222

def needs_runtime_type_tag?
  non_null_names = @alternatives.reject { |alt| alt.name == "Null" }.map(&:name)
  non_null_names.include?("Bool") && non_null_names.include?("Int")
end

#non_null_typeObject



272
273
274
275
276
277
# File 'lib/stone/type.rb', line 272

def non_null_type
  remaining = @alternatives.reject { |t| null_type?(t) }
  return remaining.first if remaining.length == 1

  Stone::Type.union(alternatives: remaining)
end

#nullable?Boolean

Returns:

  • (Boolean)


248
249
250
# File 'lib/stone/type.rb', line 248

def nullable?
  @alternatives.any? { |t| null_type?(t) }
end

#payload_alignmentObject



193
194
195
# File 'lib/stone/type.rb', line 193

def payload_alignment
  @alternatives.map(&:alignment).max || 1
end

#payload_sizeObject



189
190
191
# File 'lib/stone/type.rb', line 189

def payload_size
  @alternatives.map(&:size_bytes).max || 0
end

#primitive?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/stone/type.rb', line 236

def primitive?
  false
end

#property_return_type(property_name) ⇒ Object

Look through non-null alternatives for a property. Returns the property type if ALL non-null alternatives have it with the same type. This enables chained access like o.value.x where value is Point | Null.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/stone/type.rb', line 255

def property_return_type(property_name)
  non_null_alts = @alternatives.reject { |alt| null_type?(alt) }
  return nil if non_null_alts.empty?

  # Get property type from each non-null alternative
  prop_types = non_null_alts.map { |alt| alt.property_return_type(property_name) }

  # All alternatives must have this property
  return nil if prop_types.any?(&:nil?)

  # For now, require all alternatives to return the same type
  # (Future: could return a union of the return types)
  return nil unless prop_types.uniq.length == 1

  prop_types.first
end

#record?Boolean

Returns:

  • (Boolean)


240
241
242
# File 'lib/stone/type.rb', line 240

def record?
  false
end

#size_bytesObject



180
181
182
# File 'lib/stone/type.rb', line 180

def size_bytes
  8 + payload_size  # tag pointer (8 bytes) + payload
end

#union?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/stone/type.rb', line 232

def union?
  true
end