eval.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/eval.h"
  5. #include "toolchain/sem_ir/typed_insts.h"
  6. #include "toolchain/sem_ir/value_stores.h"
  7. namespace Carbon::Check {
  8. // `GetConstantValue` checks to see whether the provided ID describes a value
  9. // with constant phase, and if so, returns the corresponding constant value.
  10. // Overloads are provided for different kinds of ID.
  11. // If the given instruction is constant, returns its constant value.
  12. static auto GetConstantValue(Context& context, SemIR::InstId inst_id,
  13. bool* is_symbolic) -> SemIR::InstId {
  14. auto const_id = context.constant_values().Get(inst_id);
  15. *is_symbolic |= const_id.is_symbolic();
  16. return const_id.inst_id();
  17. }
  18. // If the given instruction block contains only constants, returns a
  19. // corresponding block of those values.
  20. static auto GetConstantValue(Context& context, SemIR::InstBlockId inst_block_id,
  21. bool* is_symbolic) -> SemIR::InstBlockId {
  22. auto insts = context.inst_blocks().Get(inst_block_id);
  23. llvm::SmallVector<SemIR::InstId> const_insts;
  24. for (auto inst_id : insts) {
  25. auto const_inst_id = GetConstantValue(context, inst_id, is_symbolic);
  26. if (!const_inst_id.is_valid()) {
  27. return SemIR::InstBlockId::Invalid;
  28. }
  29. // Once we leave the small buffer, we know the first few elements are all
  30. // constant, so it's likely that the entire block is constant. Resize to the
  31. // target size given that we're going to allocate memory now anyway.
  32. if (const_insts.size() == const_insts.capacity()) {
  33. const_insts.reserve(insts.size());
  34. }
  35. const_insts.push_back(const_inst_id);
  36. }
  37. // TODO: If the new block is identical to the original block, return the
  38. // original ID.
  39. return context.inst_blocks().Add(const_insts);
  40. }
  41. // Replaces the specified field of the given typed instruction with its constant
  42. // value, if it has constant phase. Returns true on success, false if the value
  43. // has runtime phase.
  44. template <typename InstT, typename FieldIdT>
  45. static auto ReplaceFieldWithConstantValue(Context& context, InstT* inst,
  46. FieldIdT InstT::*field,
  47. bool* is_symbolic) -> bool {
  48. auto unwrapped = GetConstantValue(context, inst->*field, is_symbolic);
  49. if (!unwrapped.is_valid()) {
  50. return false;
  51. }
  52. inst->*field = unwrapped;
  53. return true;
  54. }
  55. // If the specified fields of the given typed instruction have constant values,
  56. // replaces the fields with their constant values and builds a corresponding
  57. // constant value. Otherwise returns `SemIR::InstId::Invalid`.
  58. template <typename InstT, typename... EachFieldIdT>
  59. static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst,
  60. EachFieldIdT InstT::*... each_field_id)
  61. -> SemIR::ConstantId {
  62. // Build a constant instruction by replacing each non-constant operand with
  63. // its constant value.
  64. auto typed_inst = inst.As<InstT>();
  65. bool is_symbolic = false;
  66. if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id,
  67. &is_symbolic) &&
  68. ...)) {
  69. return context.AddConstant(typed_inst, is_symbolic);
  70. }
  71. return SemIR::ConstantId::NotConstant;
  72. }
  73. auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
  74. -> SemIR::ConstantId {
  75. // TODO: Ensure we have test coverage for each of these cases that can result
  76. // in a constant, once those situations are all reachable.
  77. // clang warns on unhandled enum values; clang-tidy is incorrect here.
  78. // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
  79. switch (inst.kind()) {
  80. // These cases are constants if their operands are.
  81. case SemIR::AddrOf::Kind:
  82. return RebuildIfFieldsAreConstant(context, inst,
  83. &SemIR::AddrOf::lvalue_id);
  84. case SemIR::ArrayType::Kind:
  85. return RebuildIfFieldsAreConstant(context, inst,
  86. &SemIR::ArrayType::bound_id);
  87. case SemIR::BoundMethod::Kind:
  88. return RebuildIfFieldsAreConstant(context, inst,
  89. &SemIR::BoundMethod::object_id,
  90. &SemIR::BoundMethod::function_id);
  91. case SemIR::StructType::Kind:
  92. return RebuildIfFieldsAreConstant(context, inst,
  93. &SemIR::StructType::fields_id);
  94. case SemIR::StructValue::Kind:
  95. return RebuildIfFieldsAreConstant(context, inst,
  96. &SemIR::StructValue::elements_id);
  97. case SemIR::Temporary::Kind:
  98. return RebuildIfFieldsAreConstant(context, inst,
  99. &SemIR::Temporary::init_id);
  100. case SemIR::TupleValue::Kind:
  101. return RebuildIfFieldsAreConstant(context, inst,
  102. &SemIR::TupleValue::elements_id);
  103. // These cases are always constants.
  104. case SemIR::Builtin::Kind:
  105. case SemIR::ClassType::Kind:
  106. case SemIR::ConstType::Kind:
  107. case SemIR::PointerType::Kind:
  108. case SemIR::StructTypeField::Kind:
  109. case SemIR::TupleType::Kind:
  110. case SemIR::UnboundElementType::Kind:
  111. // TODO: Propagate symbolic / template nature from operands.
  112. return context.AddConstant(inst, /*is_symbolic=*/false);
  113. // These cases are treated as being the unique canonical definition of the
  114. // corresponding constant value.
  115. // TODO: This doesn't properly handle redeclarations. Consider adding a
  116. // corresponding `Value` inst for each of these cases.
  117. case SemIR::BaseDecl::Kind:
  118. case SemIR::FieldDecl::Kind:
  119. case SemIR::FunctionDecl::Kind:
  120. case SemIR::Namespace::Kind:
  121. return SemIR::ConstantId::ForTemplateConstant(inst_id);
  122. case SemIR::BoolLiteral::Kind:
  123. case SemIR::IntLiteral::Kind:
  124. case SemIR::RealLiteral::Kind:
  125. case SemIR::StringLiteral::Kind:
  126. // Promote literals to the constant block.
  127. // TODO: Convert literals into a canonical form. Currently we can form two
  128. // different `i32` constants with the same value if they are represented
  129. // by `APInt`s with different bit widths.
  130. return context.AddConstant(inst, /*is_symbolic=*/false);
  131. // TODO: These need special handling.
  132. case SemIR::ArrayIndex::Kind:
  133. case SemIR::ArrayInit::Kind:
  134. case SemIR::BindValue::Kind:
  135. case SemIR::Call::Kind:
  136. case SemIR::ClassElementAccess::Kind:
  137. case SemIR::ClassInit::Kind:
  138. case SemIR::CrossRef::Kind:
  139. case SemIR::Deref::Kind:
  140. case SemIR::InitializeFrom::Kind:
  141. case SemIR::SpliceBlock::Kind:
  142. case SemIR::StructAccess::Kind:
  143. case SemIR::StructInit::Kind:
  144. case SemIR::TemporaryStorage::Kind:
  145. case SemIR::TupleAccess::Kind:
  146. case SemIR::TupleIndex::Kind:
  147. case SemIR::TupleInit::Kind:
  148. case SemIR::ValueAsRef::Kind:
  149. case SemIR::ValueOfInitializer::Kind:
  150. break;
  151. case SemIR::BindSymbolicName::Kind:
  152. // TODO: Consider forming a constant value here using a de Bruijn index or
  153. // similar, so that corresponding symbolic parameters in redeclarations
  154. // are treated as the same value.
  155. return SemIR::ConstantId::ForSymbolicConstant(inst_id);
  156. case SemIR::BindName::Kind:
  157. // TODO: We need to look through `BindName`s for member accesses naming
  158. // fields, where the member name is a `BindName`. Should we really be
  159. // creating a `BindName` in that case?
  160. return context.constant_values().Get(inst.As<SemIR::BindName>().value_id);
  161. case SemIR::NameRef::Kind:
  162. return context.constant_values().Get(inst.As<SemIR::NameRef>().value_id);
  163. case SemIR::Converted::Kind:
  164. return context.constant_values().Get(
  165. inst.As<SemIR::Converted>().result_id);
  166. case SemIR::UnaryOperatorNot::Kind: {
  167. auto const_id = context.constant_values().Get(
  168. inst.As<SemIR::UnaryOperatorNot>().operand_id);
  169. if (!const_id.is_template()) {
  170. break;
  171. }
  172. // A template constant of bool type is always a bool literal.
  173. auto value =
  174. context.insts().GetAs<SemIR::BoolLiteral>(const_id.inst_id());
  175. value.value =
  176. (value.value == SemIR::BoolValue::False ? SemIR::BoolValue::True
  177. : SemIR::BoolValue::False);
  178. return context.AddConstant(value, /*is_symbolic=*/false);
  179. }
  180. // These cases are either not expressions or not constant.
  181. case SemIR::AddrPattern::Kind:
  182. case SemIR::Assign::Kind:
  183. case SemIR::BlockArg::Kind:
  184. case SemIR::Branch::Kind:
  185. case SemIR::BranchIf::Kind:
  186. case SemIR::BranchWithArg::Kind:
  187. case SemIR::ClassDecl::Kind:
  188. case SemIR::Import::Kind:
  189. case SemIR::InterfaceDecl::Kind:
  190. case SemIR::LazyImportRef::Kind:
  191. case SemIR::Param::Kind:
  192. case SemIR::ReturnExpr::Kind:
  193. case SemIR::Return::Kind:
  194. case SemIR::StructLiteral::Kind:
  195. case SemIR::TupleLiteral::Kind:
  196. case SemIR::VarStorage::Kind:
  197. break;
  198. }
  199. return SemIR::ConstantId::NotConstant;
  200. }
  201. } // namespace Carbon::Check