eval.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. namespace Carbon::Check {
  7. // `GetConstantValue` checks to see whether the provided ID describes a value
  8. // with constant phase, and if so, returns the corresponding constant value.
  9. // Overloads are provided for different kinds of ID.
  10. // If the given instruction is constant, returns its constant value.
  11. static auto GetConstantValue(Context& context, SemIR::InstId inst_id)
  12. -> SemIR::InstId {
  13. return context.constant_values().Get(inst_id);
  14. }
  15. // If the given instruction block contains only constants, returns a
  16. // corresponding block of those values.
  17. static auto GetConstantValue(Context& context, SemIR::InstBlockId inst_block_id)
  18. -> SemIR::InstBlockId {
  19. auto insts = context.inst_blocks().Get(inst_block_id);
  20. llvm::SmallVector<SemIR::InstId> const_insts;
  21. for (auto inst_id : insts) {
  22. auto const_inst_id = GetConstantValue(context, inst_id);
  23. if (!const_inst_id.is_valid()) {
  24. return SemIR::InstBlockId::Invalid;
  25. }
  26. // Once we leave the small buffer, we know the first few elements are all
  27. // constant, so it's likely that the entire block is constant. Resize to the
  28. // target size given that we're going to allocate memory now anyway.
  29. if (const_insts.size() == const_insts.capacity()) {
  30. const_insts.reserve(insts.size());
  31. }
  32. const_insts.push_back(const_inst_id);
  33. }
  34. // TODO: If the new block is identical to the original block, return the
  35. // original ID.
  36. return context.inst_blocks().Add(const_insts);
  37. }
  38. // Replaces the specified field of the given typed instruction with its constant
  39. // value, if it has constant phase. Returns true on success, false if the value
  40. // has runtime phase.
  41. template <typename InstT, typename FieldIdT>
  42. static auto ReplaceFieldWithConstantValue(Context& context, InstT* inst,
  43. FieldIdT InstT::*field) -> bool {
  44. auto unwrapped = GetConstantValue(context, inst->*field);
  45. if (!unwrapped.is_valid()) {
  46. return false;
  47. }
  48. inst->*field = unwrapped;
  49. return true;
  50. }
  51. // If the specified fields of the given typed instruction have constant values,
  52. // replaces the fields with their constant values and builds a corresponding
  53. // constant value. Otherwise returns `SemIR::InstId::Invalid`.
  54. template <typename InstT, typename... EachFieldIdT>
  55. static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst,
  56. EachFieldIdT InstT::*... each_field_id)
  57. -> SemIR::InstId {
  58. // Build a constant instruction by replacing each non-constant operand with
  59. // its constant value.
  60. auto typed_inst = inst.As<InstT>();
  61. if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id) &&
  62. ...)) {
  63. return context.AddConstantInst(typed_inst);
  64. }
  65. return SemIR::InstId::Invalid;
  66. }
  67. auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
  68. -> SemIR::InstId {
  69. // TODO: Ensure we have test coverage for each of these cases that can result
  70. // in a constant, once those situations are all reachable.
  71. // clang warns on unhandled enum values; clang-tidy is incorrect here.
  72. // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
  73. switch (inst.kind()) {
  74. // These cases are constants if their operands are.
  75. case SemIR::AddrOf::Kind:
  76. return RebuildIfFieldsAreConstant(context, inst,
  77. &SemIR::AddrOf::lvalue_id);
  78. case SemIR::ArrayType::Kind:
  79. return RebuildIfFieldsAreConstant(context, inst,
  80. &SemIR::ArrayType::bound_id);
  81. case SemIR::BoundMethod::Kind:
  82. return RebuildIfFieldsAreConstant(context, inst,
  83. &SemIR::BoundMethod::object_id,
  84. &SemIR::BoundMethod::function_id);
  85. case SemIR::StructValue::Kind:
  86. return RebuildIfFieldsAreConstant(context, inst,
  87. &SemIR::StructValue::elements_id);
  88. case SemIR::Temporary::Kind:
  89. return RebuildIfFieldsAreConstant(context, inst,
  90. &SemIR::Temporary::init_id);
  91. case SemIR::TupleValue::Kind:
  92. return RebuildIfFieldsAreConstant(context, inst,
  93. &SemIR::TupleValue::elements_id);
  94. // These cases are constants already.
  95. case SemIR::Builtin::Kind:
  96. case SemIR::ClassType::Kind:
  97. case SemIR::ConstType::Kind:
  98. case SemIR::PointerType::Kind:
  99. case SemIR::StructType::Kind:
  100. case SemIR::TupleType::Kind:
  101. case SemIR::UnboundElementType::Kind:
  102. return inst_id;
  103. case SemIR::BaseDecl::Kind:
  104. case SemIR::FieldDecl::Kind:
  105. case SemIR::FunctionDecl::Kind:
  106. // TODO: Consider adding a corresponding `Value` inst.
  107. return inst_id;
  108. case SemIR::BoolLiteral::Kind:
  109. case SemIR::IntLiteral::Kind:
  110. case SemIR::RealLiteral::Kind:
  111. case SemIR::StringLiteral::Kind:
  112. // Promote literals to the constant block.
  113. return context.AddConstantInst(inst);
  114. // TODO: These need special handling.
  115. case SemIR::ArrayIndex::Kind:
  116. case SemIR::ArrayInit::Kind:
  117. case SemIR::BindValue::Kind:
  118. case SemIR::Call::Kind:
  119. case SemIR::ClassElementAccess::Kind:
  120. case SemIR::ClassInit::Kind:
  121. case SemIR::CrossRef::Kind:
  122. case SemIR::Deref::Kind:
  123. case SemIR::InitializeFrom::Kind:
  124. case SemIR::SpliceBlock::Kind:
  125. case SemIR::StructAccess::Kind:
  126. case SemIR::StructInit::Kind:
  127. case SemIR::TemporaryStorage::Kind:
  128. case SemIR::TupleAccess::Kind:
  129. case SemIR::TupleIndex::Kind:
  130. case SemIR::TupleInit::Kind:
  131. case SemIR::ValueAsRef::Kind:
  132. case SemIR::ValueOfInitializer::Kind:
  133. break;
  134. case SemIR::BindName::Kind:
  135. case SemIR::BindSymbolicName::Kind:
  136. // TODO: Should we really be looking through runtime and symbolic `let`
  137. // bindings?
  138. return GetConstantValue(context, inst.As<SemIR::AnyBindName>().value_id);
  139. case SemIR::NameRef::Kind:
  140. return GetConstantValue(context, inst.As<SemIR::NameRef>().value_id);
  141. case SemIR::Converted::Kind:
  142. return GetConstantValue(context, inst.As<SemIR::Converted>().result_id);
  143. case SemIR::UnaryOperatorNot::Kind: {
  144. auto const_id = GetConstantValue(
  145. context, inst.As<SemIR::UnaryOperatorNot>().operand_id);
  146. if (!const_id.is_valid()) {
  147. break;
  148. }
  149. auto value = context.insts().TryGetAs<SemIR::BoolLiteral>(const_id);
  150. if (!value) {
  151. // TODO: Can we CHECK this instead?
  152. break;
  153. }
  154. value->value =
  155. (value->value == SemIR::BoolValue::False ? SemIR::BoolValue::True
  156. : SemIR::BoolValue::False);
  157. return context.AddConstantInst(*value);
  158. }
  159. // These cases are either not expressions or not constant.
  160. case SemIR::AddrPattern::Kind:
  161. case SemIR::Assign::Kind:
  162. case SemIR::BlockArg::Kind:
  163. case SemIR::Branch::Kind:
  164. case SemIR::BranchIf::Kind:
  165. case SemIR::BranchWithArg::Kind:
  166. case SemIR::ClassDecl::Kind:
  167. case SemIR::Import::Kind:
  168. case SemIR::InterfaceDecl::Kind:
  169. case SemIR::LazyImportRef::Kind:
  170. case SemIR::Namespace::Kind:
  171. case SemIR::Param::Kind:
  172. case SemIR::ReturnExpr::Kind:
  173. case SemIR::Return::Kind:
  174. case SemIR::StructLiteral::Kind:
  175. case SemIR::StructTypeField::Kind:
  176. case SemIR::TupleLiteral::Kind:
  177. case SemIR::VarStorage::Kind:
  178. break;
  179. }
  180. return SemIR::InstId::Invalid;
  181. }
  182. } // namespace Carbon::Check