# encoding: utf-8 # frozen_string_literal: true module Carbon module Concrete module Item # A "base" for other items. This just has some common methods that all # items need to share to be compatible with the index. This module is # the bases of the build system in the {Concrete} module. module Base # Returns the interned name of the item. This contains no generic # information. Interned names are mostly used to match generic # information laden types with the items that define them; for example, # it is meant to be used to match the type # `Carbon::Pointer` to the module `Carbon::Pointer`. # The common parts of both is the module name that the type uses - or, # rather, everything but the generic information. Interned names # discard the information that is unneeded for matching the two. # # This methodology has the downside that a module with the same name # as the base of a generic module cannot exist; however, allowing such # would ultimately be confusing. # # @api public # @example For a module. # item.name # => "Carbon::Pointer" # item.intern # => "Carbon::Pointer" # @example For a function. # item.name # # => "Carbon::Pointer.+(Carbon::Pointer, Carbon::Int32)" # item.intern # # => "Carbon::Pointer.+(Carbon::Pointer, Carbon::Int32)" # @return [::String] The interned name. attr_reader :intern # Returns the full name of the item. This can include generic # information. # # @example # item.name # => "Carbon::Pointer" # @return [::String] attr_reader :name # Returns the generic information associated with this item. This is # used internally for generic substitution later on. # # @api semipublic # @example # item.generics # => [#] # @return [] attr_reader :generics # The dependencies that this item is based on. These are requests # because we _request_ the item from the index as a dependency. # Requests contain the module name that it builds and the generics # it requires. # # @api semipublic # @example # item.dependencies # => Set[#] # @return [Set] attr_reader :dependencies # Creates a hash from a given {Concrete::Type} that can be used to # intialize an instance of this object. This is mostly used for # {Index#define} and shouldn't be used anywhere else. # # @api private # @param type [Concrete::Type] The type. # @return [{::Symbol => ::Object}] def self.from(type) { module: type } end # Compares this item to another object. If the other object _is_ this # item, then it returns true; otherwise, if the other object is an # item, and the other object's {#intern} is equal to this object's # {#intern}, then it returns true; otherwise, it returns false. # # @api public # @example # first.intern # => "Carbon::Pointer" # second.intern # => "Carbon::Pointer" # first == first # => true # first == second # => true # second == second # => true # second == first # => true # @param other [Base, ::String, ::Object] The object to compare. # @return [::Boolean] The result of comparison. def ==(other) equal?(other) || (other.is_a?(Base) && intern == other.intern) end alias_method :eql?, :== # Creates a hash of the item. This is used mostly in the Hash # class. # # @api private # @return [Numeric] def hash intern.hash end # rubocop:disable Lint/UnusedMethodArgument # Performs compilation for the item. This converts the item into an # LLVM-based value or type, to be used for compiling. If # unimplemented, it raises a `NotImplementedError`. # # @api private # @param build [Concrete::Build] The build information for the item. # @param generics [{Concrete::Type => Concrete::Type}] The generics # to apply for the item. # @return [void] # @raise [NotImplementedError] If it is not implemented. def call(build, generics) fail NotImplementedError, "Could not build #{self.class}" end # Performs modification to the item. This is done after _all_ items # are loaded into the index, so that an item can load outside # information if need be. # # @param index [Concrete::Index] The index. # @return [Concrete::Item::Base] A modified version of the item, or # self. def define(index) self end # Modifies the dependencies of this item so that they conform to the # given request. This should resolve all of our dependencies so that # they no longer hold any sort of generic class. # # @api private # @param request [Request] The request. # @yield [dep] The corrected dependencies. # @yieldparam dep [Request] The corrected dependency. # @return [::Enumerable] An enumerator of the corrected dependencies, # if no block was given. # @return [void] If a block was given. def corrected_dependencies(request, &block) return to_enum(:corrected_dependencies, request) unless block_given? return dependencies.each(&block) if generics.empty? forced_corrected_dependencies(request, &block) end private def forced_corrected_dependencies(request, &block) # Array -> Array<(Type::Generic, Numeric)> -> # Array<(String, Type::Generic)> -> {String => Type::Generic} mapping = generics .each_with_index .map { |gen, i| [gen.name.intern, request.generics[i]] } .to_h dependencies.map { |dep| dep.sub(mapping) }.each(&block) end end end end end