// 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_TYPE_H_ #define CARBON_TOOLCHAIN_SEM_IR_TYPE_H_ #include "llvm/ADT/STLExtras.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/type_info.h" namespace Carbon::SemIR { #define CARBON_TYPE_QUALIFIERS(X) \ X(Const) \ X(MaybeUnformed) \ X(Partial) CARBON_DEFINE_RAW_ENUM_MASK(TypeQualifiers, uint8_t) { CARBON_TYPE_QUALIFIERS(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; // Represents a set of keyword modifiers, using a separate bit per modifier. class TypeQualifiers : public CARBON_ENUM_MASK_BASE(TypeQualifiers) { public: CARBON_TYPE_QUALIFIERS(CARBON_ENUM_MASK_CONSTANT_DECL) }; #define CARBON_TYPE_QUALIFIERS_WITH_TYPE(X) \ CARBON_ENUM_MASK_CONSTANT_DEFINITION(TypeQualifiers, X) CARBON_TYPE_QUALIFIERS(CARBON_TYPE_QUALIFIERS_WITH_TYPE) #undef CARBON_TYPE_QUALIFIERS_WITH_TYPE // Provides a ValueStore wrapper with an API specific to types. class TypeStore : public Yaml::Printable { public: // Used to return information about an integer type in `GetIntTypeInfo`. struct IntTypeInfo { bool is_signed; IntId bit_width; }; explicit TypeStore(File* file) : file_(file) {} // Returns the ID of the constant used to define the specified type. auto GetConstantId(TypeId type_id) const -> ConstantId { if (!type_id.has_value()) { // TODO: Investigate replacing this with a CHECK or returning `None`. return ConstantId::NotConstant; } return type_id.AsConstantId(); } // Returns the type ID for a constant that is a type value, i.e. it is a value // of type `TypeType`. // // Facet values are of the same typishness as types, but are not themselves // types, so they can not be passed here. They should be converted to a type // through an `as type` conversion, that is, to a value of type `TypeType`. auto GetTypeIdForTypeConstantId(ConstantId constant_id) const -> TypeId; // Returns the type ID for an instruction whose constant value is a type // value, i.e. it is a value of type `TypeType`. // // Instructions whose values are facet values (see `FacetValue`) produce a // value of the same typishness as types, but which are themselves not types, // so they can not be passed here. They should be converted to a type through // an `as type` conversion, such as to a `FacetAccessType` instruction whose // value is of type `TypeType`. auto GetTypeIdForTypeInstId(InstId inst_id) const -> TypeId; auto GetTypeIdForTypeInstId(TypeInstId inst_id) const -> TypeId; // Converts an `InstId` to a `TypeInstId` of the same id value. This process // involves checking that the type of the instruction's value is `TypeType`, // and then this check is encoded in the type system via `TypeInstId`. auto GetAsTypeInstId(InstId inst_id) const -> TypeInstId; // Returns the ID of the instruction used to define the specified type. auto GetInstId(TypeId type_id) const -> TypeInstId; // Returns the instruction used to define the specified type. auto GetAsInst(TypeId type_id) const -> Inst; // Returns the unattached form of the given type. auto GetUnattachedType(TypeId type_id) const -> TypeId; // Converts an ArrayRef of `InstId`s to a range of `TypeInstId`s via // GetAsTypeInstId(). auto GetBlockAsTypeInstIds(llvm::ArrayRef array [[clang::lifetimebound]]) const -> auto { return llvm::map_range(array, [&](InstId type_inst_id) { return GetAsTypeInstId(type_inst_id); }); } // Converts an ArrayRef of `InstId`s to a range of `TypeId`s via // GetTypeIdForTypeInstId(). auto GetBlockAsTypeIds(llvm::ArrayRef array [[clang::lifetimebound]]) const -> auto { return llvm::map_range(array, [&](InstId type_inst_id) { return GetTypeIdForTypeInstId(type_inst_id); }); } // Returns whether the specified kind of instruction was used to define the // type. template auto Is(TypeId type_id) const -> bool { return GetAsInst(type_id).Is(); } // Returns the instruction used to define the specified type, which is known // to be a particular kind of instruction. template auto GetAs(TypeId type_id) const -> InstT { return GetAsInst(type_id).As(); } // Returns the instruction used to define the specified type, if it is of a // particular kind. template auto TryGetAs(TypeId type_id) const -> std::optional { return GetAsInst(type_id).TryAs(); } // Returns whether two type IDs represent the same type. This includes the // case where they might be in different generics and thus might have // different ConstantIds, but are still symbolically equal. auto AreEqualAcrossDeclarations(TypeId a, TypeId b) const -> bool { return GetInstId(a) == GetInstId(b); } // Gets the value representation to use for a type. This returns an // `None` type if the given type is not complete. auto GetValueRepr(TypeId type_id) const -> ValueRepr { if (auto type_info = complete_type_info_.Lookup(type_id)) { return type_info.value().value_repr; } return {.kind = ValueRepr::Unknown}; } // Gets the `CompleteTypeInfo` for a type, with an empty value if the type is // not complete. auto GetCompleteTypeInfo(TypeId type_id) const -> CompleteTypeInfo { if (auto type_info = complete_type_info_.Lookup(type_id)) { return type_info.value(); } return {.value_repr = {.kind = ValueRepr::Unknown}}; } // Sets the `CompleteTypeInfo` associated with a type, marking it as complete. // This can be used with abstract types. auto SetComplete(TypeId type_id, const CompleteTypeInfo& info) -> void { CARBON_CHECK(info.value_repr.kind != ValueRepr::Unknown); auto insert_info = complete_type_info_.Insert(type_id, info); CARBON_CHECK(insert_info.is_inserted(), "Type {0} completed more than once", type_id); complete_types_.push_back(type_id); CARBON_CHECK(IsComplete(type_id)); } // Get the object representation associated with a type. For a non-class type, // this is the type itself. `None` is returned if the object representation // cannot be determined because the type is not complete. auto GetObjectRepr(TypeId type_id) const -> TypeId; // Get the type that the given type adapts, or `None` if the type is not known // to be an adapter, including the case where the type is an incomplete class. auto GetAdaptedType(TypeId type_id) const -> TypeId; // Returns the non-adapter type that is compatible with the specified type. auto GetTransitiveAdaptedType(TypeId type_id) const -> TypeId; // Determines whether the given type is known to be complete. This does not // determine whether the type could be completed, only whether it has been. auto IsComplete(TypeId type_id) const -> bool { return complete_type_info_.Contains(type_id); } // Removes any top-level qualifiers from a type. auto GetUnqualifiedType(TypeId type_id) const -> TypeId { return GetUnqualifiedTypeAndQualifiers(type_id).first; } // Removes any top-level qualifiers from a type and returns the unqualified // type and qualifiers. auto GetUnqualifiedTypeAndQualifiers(TypeId type_id) const -> std::pair; // Returns the non-adapter unqualified type that is compatible with the // specified type. auto GetTransitiveUnqualifiedAdaptedType(TypeId type_id) const -> std::pair; // Determines whether the given type is a signed integer type. This includes // the case where the type is `Core.IntLiteral` or a class type whose object // representation is a signed integer type. auto IsSignedInt(TypeId int_type_id) const -> bool; // Returns integer type information from a type ID that is known to represent // an integer type. Abstracts away the difference between an `IntType` // instruction defined type, a singleton instruction defined type, and a class // adapting such a type. Uses IntId::None for types that have a // non-constant width and for IntLiteral. auto GetIntTypeInfo(TypeId int_type_id) const -> IntTypeInfo; // Similar to `GetIntTypeInfo`, except allows non-`IntType` types to be // handled. auto TryGetIntTypeInfo(TypeId int_type_id) const -> std::optional; // Returns whether `type_id` represents a facet type. auto IsFacetType(TypeId type_id) const -> bool { return type_id == TypeType::TypeId || Is(type_id); } // Returns a list of types that were completed in this file, in the order in // which they were completed. Earlier types in this list cannot contain // instances of later types. auto complete_types() const -> llvm::ArrayRef { return complete_types_; } auto OutputYaml() const -> Yaml::OutputMapping { return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { for (auto type_id : complete_types_) { auto info = GetCompleteTypeInfo(type_id); map.Add(PrintToString(type_id), Yaml::OutputMapping([&](Yaml::OutputMapping::Map map2) { map2.Add("value_repr", Yaml::OutputScalar(info.value_repr)); if (info.abstract_class_id.has_value()) { map2.Add("abstract_class_id", Yaml::OutputScalar(info.abstract_class_id)); } })); } }); } auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(MemUsage::ConcatLabel(label, "complete_type_info_"), complete_type_info_); mem_usage.Collect(MemUsage::ConcatLabel(label, "complete_types_"), complete_types_); } private: File* file_; Map complete_type_info_; llvm::SmallVector complete_types_; }; // Returns the scrutinee type of `type_id`, which must be a `PatternType`. auto ExtractScrutineeType(const File& sem_ir, TypeId type_id) -> TypeId; } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_TYPE_H_