class Carbon::Concrete::Type

A type. This is, basically, any possible name that could resolve into an actual type. Modules or functions are included in the typing. The Type class includes a parsing module, allowing types to be stored as a string and then re-serialized as a Type.

@note

**This class is frozen upon initialization.**  This means that any
attempt to modify it will result in an error.  In most cases, the
attributes on this class will also be frozen, as well.

Attributes

generics[R]

The generic information for the type. This is derived directly from the last part in the name and the function name (if it exists).

@api public @example

type = Carbon::Type("Result<T, E>")
type.generics
  # => [#<Carbon::Concrete::Type::Generic T>,
  #  #<Carbon::Concrete::Type::Generic E>]

@return [<Type::Generic>]

location[R]

The location of the type. This is used for interfacing with other programs that require a location of some sort.

@api semiprivate @return [Object]

name[R]

The name of the type. This is from the parsed result. This contains the part and function information.

@api private @return [Type::Name]

Public Class Methods

cache() click to toggle source

A cache of types. This maps unparsed strings to their parsed derivatives. The cache is guarenteed to be thread-safe.

@api private @return [{::String => Type}]

# File lib/carbon/concrete/type.rb, line 28
def self.cache
  @cache ||= Concurrent::Map.new
end
from(string) click to toggle source

Returns the corresponding type for the given string value. If a type is not cached in {.cache}, then it computes it using {Parse}. The cache is locked during the computation, such that it won't be modified while the value is being parsed.

@api public @example

type = Carbon::Type("Carbon::Pointer")
type.to_s # => "Carbon::Pointer"
type.function? # => false
second = Carbon::Type("Carbon::Pointer")
type.equal?(second) # => true

@param string [::String] The type to parse. @return [Type]

# File lib/carbon/concrete/type.rb, line 46
def self.from(string)
  return string if string.is_a?(Type)
  cache.compute_if_absent(string) { new(Parse.new(string).value) }
end
new(name, location: nil) click to toggle source

Initialize the type with the given name. After initialization, this freezes the type, preventing modification.

@api public @param name [Type::Name] The name.

# File lib/carbon/concrete/type.rb, line 83
def initialize(name, location: nil)
  @name = name
  @generics = name.last.generics
  @generics += function.generics if function?
  @location = location
  deep_freeze!
end

Public Instance Methods

==(other) click to toggle source

Compares this type to another type. If the other type is this type, it returns true; otherwise, if the other value is a {Type} and the other type's {#to_s} is equal to this one's, it returns true; otherwise, it returns false.

@api public @example Compare with self.

second = type
type.equal?(second) # => true
type == second # => true

@example Compare with a type.

type.equal?(second) # => false
type.to_s # => "Carbon::Pointer<T>"
second.to_s # => "Carbon::Pointer<T>"
type.to_s == second.to_s # => true
type == second # => true

@param other [Type, ::String, ::Object] The other object to compare. @return [::Boolean] True if the other object is similar to this type;

false otherwise.
# File lib/carbon/concrete/type.rb, line 147
def ==(other)
  equal?(other) || (other.is_a?(Type) && other.to_s == to_s)
end
Also aliased as: eql?
accept(visitor, *params) click to toggle source

Accepts the current visitor unto itself.

@param visitor [#visit] @return [Object]

# File lib/carbon/concrete/type.rb, line 303
def accept(visitor, *params)
  visitor.visit(self, *params)
end
call(name, parameters, generics = []) click to toggle source

Creates a new type using this type as a base. The new type is a function type; this provides information for the function type.

@api semipublic @example

bool = Carbon::Boolean
func = bool.call("<=>", [bool, bool])
func.to_s # => "Carbon::Boolean.<=>(Carbon::Boolean, Carbon::Boolean)"

@param name [::String] The name of the function that is being defined. @param parameters [<Type>] The parameters that the function is

called with.

@return [Type] The new type.

# File lib/carbon/concrete/type.rb, line 207
def call(name, parameters, generics = [])
  func = Type::Function.new(name.to_s, parameters, generics)
  name = Name.new(@name.parts, func)
  Type.new(name)
end
eql?(other)
Alias for: ==
function() click to toggle source

Returns the function information of the type. If this isn't a function type (see {#function?}), then this returns `nil`.

@api public @example Not a function.

type.to_s # => "Carbon::Pointer<T>"
type.function? # => false
type.function # => nil

@example A function.

type.to_s
  # => "Carbon::Pointer<T>.add(Carbon::Pointer<T>, Carbon::Int32)"
type.function? # => true
type.function # => #<Carbon::Concrete::Type::Function ...>

@return [Concrete::Type::Function] The function information, if this is

a function type; otherwise,

@return [nil] returns nil.

# File lib/carbon/concrete/type.rb, line 124
def function
  @name.function
end
function?() click to toggle source

Returns whether or not this type is a function type. It does this by checking to see if the {Type::Name#function} attribute on the {#name} is not nil.

@api public @example Not a function.

type.to_s # => "Carbon::Pointer<T>"
type.function? # => false

@example A function.

type.to_s
  # => "Carbon::Pointer<T>.add(Carbon::Pointer<T>, Carbon::Int32)"
type.function? # => true

@return [::Boolean] True if the type is a function, false otherwise.

# File lib/carbon/concrete/type.rb, line 104
def function?
  !function.nil?
end
hash() click to toggle source

Creates a hash of the request. This is used mostly in the Hash class.

@api private @return [::Numeric] The hash.

# File lib/carbon/concrete/type.rb, line 176
def hash
  to_s.hash
end
inspect() click to toggle source

Pretty inspect.

@api private @return [::String] An inspection string.

# File lib/carbon/concrete/type.rb, line 295
def inspect
  "#<Carbon::Concrete::Type #{self}>".freeze
end
intern() click to toggle source

The interned name of a module. This is used for module lookup. This contains no generic information. For generic functions, all generics are pushed onto the defining module.

@api public @example

type.intern # => "Carbon::Pointer"

@note See {Item::Base#intern} For more information about interned

names.

@see Type::Name#intern @return [::String] The interned name of the type.

# File lib/carbon/concrete/type.rb, line 191
def intern
  @name.intern
end
match?(other, generics = {}) click to toggle source

Checks if the given type “matches” the current type. This matches if any of the following conditions apply:

  1. If the types match exactly, with no generics; otherwise,

  2. If the types match exactly, with generics; otherwise,

  3. If the given type can be made to look like the item type by

introducing generics; otherwise,
  1. They don't match.

The base item provides a good default for most items.

@param type [Concrete::Type] The type to check for matching. @return [Boolean] If the type matches the item.

# File lib/carbon/concrete/type.rb, line 166
def match?(other, generics = {})
  (@generics.none? && to_s == other.to_s && generics) ||
    (!function? && match_generic_module?(other, generics)) ||
    (function? && match_generic_function?(other, generics))
end
sub(generics) click to toggle source

Replaces all of the generics in the {Type} with the mapped ones. This is used when generics are fully resolved.

@note

This also replaces the type itself if the type matches any of the
keys in the generic mapping.

@api semipublic @example Normal replacement.

type # => #<Carbon::Concrete::Type Carbon::Pointer<T>>
mapping # => {"T" => #<Carbon::Concrete::Type Carbon::Int32>}
subbed = type.sub(mapping)
subbed # => #<Carbon::Concrete::Type Carbon::Pointer<Carbon::Int32>>

@example Special replacement.

type # => #<Carbon::Concrete::Type T>
mapping # => {"T" => #<Carbon::Concrete::Type Carbon::Int32>}
subbed = type.sub(mapping)
subbed # => #<Carbon::Concrete::Type Carbon::Int32>

@param generics [{::String => Carbon::Concrete::Type}] The generics to

replace.

@return [Concrete::Type] The new type.

# File lib/carbon/concrete/type.rb, line 233
def sub(generics)
  return sub_function(generics) if function?
  return generics[self] if generics.key?(self)
  return self if generics.none?
  replace_generics(generics)
end
to_module() click to toggle source

Removes all function information from the type. This is the complete opposite of {#call}. This just leaves the module that the function was defined on.

@api semipublic @example

func = Carbon::Boolean.call("test")
func.to_s # => "Carbon::Boolean.test()"
func.to_module.to_s # => "Carbon::Boolean"

@return [Type]

# File lib/carbon/concrete/type.rb, line 271
def to_module
  name = Name.new(@name.parts, nil)
  Type.new(name)
end
to_pointer() click to toggle source

Converts the type into a pointer type. Essentially, it just encapsulates the current type in a pointer.

@example

inner = Carbon::Boolean
inner.to_s # => "Carbon::Boolean"
inner.to_pointer.to_s # => "Carbon::Pointer<Carbon::Boolean>"

@return [Type] The encapsulated type.

# File lib/carbon/concrete/type.rb, line 284
def to_pointer
  ptype = Carbon::Type("Carbon::Pointer<T>")
  tgen = Carbon::Type("T")

  ptype.sub(tgen => self)
end
to_s() click to toggle source

Creates a string representation of the type for comparison or storage. The string representation has a constraint that it must be re-parsable by the {Type::Parse} class.

@api public @example String representation.

type = Carbon::Type("Carbon::Pointer<T>")
type.to_s # => "Carbon::Pointer<T>"

@example Re-parsing string.

form = "Carbon::Pointer<T: Carbon::Sized + Carbon::Numeric>" \
  ".add(Carbon::Pointer<T: Carbon::Sized + Carbon::Numeric>, " \
  "Carbon::Int32)"
type = Carbon::Type(form)
type.to_s == form # => true
second = Carbon::Type(type.to_s)
type == second # => true

@return [::String] The string representation of the type.

# File lib/carbon/concrete/type.rb, line 257
def to_s
  @name.to_s
end

Private Instance Methods

match_generic_function?(other, generics) click to toggle source
# File lib/carbon/concrete/type.rb, line 338
def match_generic_function?(other, generics)
  return false unless match_generic_function_preflight?(other, generics)
  mapping = generics.merge(@generics.map(&:name)
    .zip(other.generics.map(&:name)).to_h)
  params = function.parameters.zip(other.function.parameters)
  # If any of the parameters don't match, then the whole doesn't match.
  # However, if one of the parameters is a generic, then we check to make
  # sure that the same generic placement is used in the other type.
  # For example, `A<T>.+(A<T>, T)` would match `A<B>.+(A<B>, B)`, but not
  # `A<B>.+(A<B>, C)`.
  params.all? do |(ours, theirs)|
    if mapping.key?(ours)
      mapping[ours].match?(theirs, mapping)
    else
      ours.match?(theirs, mapping)
    end
  end && mapping
end
match_generic_function_preflight?(other, _) click to toggle source
# File lib/carbon/concrete/type.rb, line 325
def match_generic_function_preflight?(other, _)
  # If neither have generics, then this can't match them.
  @generics.any? && other.generics.any? &&
    # If the types are modules, then they should have matched earlier.
    function? && other.function? &&
    # The function names should be the same.
    (function.name == other.function.name) &&
    # They should have the same number of generics.
    (@generics.size == other.generics.size) &&
    # They should have the same number of parameters.
    (function.parameters.size == other.function.parameters.size)
end
match_generic_module?(other, generics) click to toggle source
# File lib/carbon/concrete/type.rb, line 318
def match_generic_module?(other, generics)
  return intern == other.intern && {} unless generics.any?
  intern == other.intern &&
    other.generics.all? { |g| generics.values.include?(g.name) } &&
    generics
end
replace_generics(generics) click to toggle source
# File lib/carbon/concrete/type.rb, line 357
def replace_generics(generics)
  part = Part.new(@name.parts.last.value,
    @generics.map { |g| g.sub(generics) })
  name = Name.new([*@name.parts[0..-2], part], @name.function)
  Type.new(name)
end
sub_function(generics) click to toggle source
# File lib/carbon/concrete/type.rb, line 309
def sub_function(generics)
  return self if !@generics.any? &&
                 function.parameters.all? { |p| !p.generics.any? }
  basic = to_module.sub(generics)
  gens = function.generics.map { |p| p.sub(generics) }
  params = function.parameters.map { |p| p.sub(generics) }
  basic.call(function.name, params, gens)
end