// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_ #define CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_ #include "common/set.h" #include "toolchain/base/value_store.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::SemIR { // Information for a generic entity, such as a generic class, a generic // interface, or generic function. // // Note that this includes both checked generics and template generics. struct Generic : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{decl: " << decl_id << ", bindings: " << bindings_id << ", self_specific_id: " << self_specific_id << ", decl_block_id: " << decl_block_id << ", definition_block_id: " << definition_block_id << "}"; } // Returns the eval block for the specified region of the generic. This is a // block of instructions that should be evaluated to compute the values and // instructions needed by that region of the generic. auto GetEvalBlock(GenericInstIndex::Region region) const -> InstBlockId { return region == GenericInstIndex::Region::Declaration ? decl_block_id : definition_block_id; } // The following members always have values, and do not change throughout the // lifetime of the generic. // The first declaration of the generic entity. InstId decl_id; // A block containing the IDs of compile time bindings in this generic scope. // The index in this block will match the `bind_index` in the name binding // instruction's `EntityName`. InstBlockId bindings_id; // The self specific of this generic, which is a specific where every generic // parameter's argument is that same parameter. For example, the self specific // of `Vector(T:! type)` is `Vector(T)`. SpecificId self_specific_id; // The following members are set at the end of the corresponding region of the // generic. // The eval block for the declaration region of the generic. InstBlockId decl_block_id = InstBlockId::None; // The eval block for the definition region of the generic. InstBlockId definition_block_id = InstBlockId::None; }; // Provides storage for generics. class GenericStore : public ValueStore> { public: using ValueStore::ValueStore; // Get the self specific for a generic, or `None` if the `id` is `None`. auto GetSelfSpecific(GenericId id) const -> SpecificId { return id.has_value() ? Get(id).self_specific_id : SpecificId::None; } }; // A specific, which is the combination of a generic and specified generic // arguments. For each construct that depends on a compile-time parameter in the // generic entity, this contains the corresponding specific value. This includes // values for the compile-time parameters themselves. struct Specific : Printable { auto Print(llvm::raw_ostream& out) const -> void { auto print_block = [&](llvm::StringLiteral region, InstBlockId id, bool has_error) { out << ", " << region << "_block_id: " << id << ", " << region << "_has_error: " << has_error; }; out << "{generic: " << generic_id << ", args: " << args_id; print_block("decl", decl_block_id, decl_block_has_error); print_block("definition", definition_block_id, definition_block_has_error); out << "}"; } // Returns true if this specific has never been resolved. Such specifics are // used to track non-canonical argument values, for example in a non-canonical // `ClassType` that describes how the arguments to the class were written. auto IsUnresolved() const -> bool { return !decl_block_id.has_value(); } // Returns the value block for this region of the specific. This is a block // containing values and instructions produced by evaluating the corresponding // eval block of the generic within the context of this specific. These are // the constant values and types and the instantiated template-dependent // instructions that are used in this region of the specific. Each inst in // the value block corresponds to the inst in the corresponding eval block // with the same index. auto GetValueBlock(GenericInstIndex::Region region) const -> InstBlockId { return region == GenericInstIndex::Region::Declaration ? decl_block_id : definition_block_id; } // Returns whether either block has an error. auto HasError() const -> bool { return decl_block_has_error || definition_block_has_error; } // The generic that this is a specific version of. GenericId generic_id; // Argument values, corresponding to the bindings in `Generic::bindings_id`. InstBlockId args_id; // The following members are set when the corresponding region of the specific // is resolved. // The value block for the declaration region of the specific. InstBlockId decl_block_id = InstBlockId::None; // The value block for the definition region of the specific. InstBlockId definition_block_id = InstBlockId::None; // Whether the corresponding block contains an error. These are stored // directly on Specific so that they pack together. bool decl_block_has_error = false; bool definition_block_has_error = false; }; // Provides storage for deduplicated specifics, which represent generics plus // their associated generic argument list. class SpecificStore : public Yaml::Printable { public: using IdType = SpecificId; using ValueStore = ValueStore>; explicit SpecificStore(CheckIRId check_ir_id) : specifics_(check_ir_id) {} // Adds a new specific, or gets the existing specific for a specified generic // and argument list. Returns the ID of the specific. The argument IDs must be // for instructions in the constant block, and must be a canonical instruction // block ID. auto GetOrAdd(GenericId generic_id, InstBlockId args_id) -> SpecificId; // Gets the specific with the given ID. auto Get(SpecificId specific_id) const -> const Specific& { return specifics_.Get(specific_id); } // Gets the specific with the given ID. auto Get(SpecificId specific_id) -> Specific& { return specifics_.Get(specific_id); } // Gets the arguments of the specified specific, or `Empty` if `None` is // passed. auto GetArgsOrEmpty(SpecificId specific_id) const -> InstBlockId { return specific_id.has_value() ? Get(specific_id).args_id : InstBlockId::Empty; } // These are to support printable structures, and are not guaranteed. auto OutputYaml() const -> Yaml::OutputMapping { return specifics_.OutputYaml(); } // Collects memory usage of members. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void; auto values() const [[clang::lifetimebound]] -> ValueStore::Range { return specifics_.values(); } auto size() const -> size_t { return specifics_.size(); } auto enumerate() const [[clang::lifetimebound]] -> auto { return specifics_.enumerate(); } auto GetIdTag() const { return specifics_.GetIdTag(); } private: // Context for hashing keys. class KeyContext; ValueStore specifics_; Carbon::Set lookup_table_; }; // Gets the substituted constant value of a potentially generic instruction // within a specific. Note that this does not perform substitution, and will // return `None` if the substituted constant value is not yet known. auto GetConstantValueInSpecific(const File& sem_ir, SpecificId specific_id, InstId inst_id) -> ConstantId; // Gets the substituted constant value of a potentially generic instruction // within a specific, where the generic instruction and the specific may be in // different files. Returns the file in which the constant value was found and // the constant ID in that file. auto GetConstantValueInSpecific(const File& specific_ir, SpecificId specific_id, const File& inst_ir, InstId inst_id) -> std::pair; // Gets the substituted type of an instruction within a specific. Note that this // does not perform substitution, and will return `None` if the substituted type // is not yet known. auto GetTypeOfInstInSpecific(const File& sem_ir, SpecificId specific_id, InstId inst_id) -> TypeId; // Gets the substituted type of a potentially generic instruction within a // specific, where the generic instruction and the specific may be in different // files. Returns the file in which the type was found and the type ID in that // file. auto GetTypeOfInstInSpecific(const File& specific_ir, SpecificId specific_id, const File& inst_ir, InstId inst_id) -> std::pair; } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_