subst.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  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/subst.h"
  5. #include "toolchain/base/kind_switch.h"
  6. #include "toolchain/check/eval.h"
  7. #include "toolchain/check/generic.h"
  8. #include "toolchain/check/inst.h"
  9. #include "toolchain/sem_ir/copy_on_write_block.h"
  10. #include "toolchain/sem_ir/ids.h"
  11. #include "toolchain/sem_ir/inst.h"
  12. #include "toolchain/sem_ir/specific_named_constraint.h"
  13. #include "toolchain/sem_ir/typed_insts.h"
  14. namespace Carbon::Check {
  15. auto SubstInstCallbacks::RebuildType(SemIR::TypeInstId type_inst_id) const
  16. -> SemIR::TypeId {
  17. return context().types().GetTypeIdForTypeInstId(type_inst_id);
  18. }
  19. auto SubstInstCallbacks::RebuildNewInst(SemIR::LocId loc_id,
  20. SemIR::Inst new_inst) const
  21. -> SemIR::InstId {
  22. auto const_id =
  23. EvalOrAddInst(context(), SemIR::LocIdAndInst::RuntimeVerified(
  24. context().sem_ir(), loc_id, new_inst));
  25. CARBON_CHECK(const_id.has_value(),
  26. "Substitution into constant produced non-constant");
  27. CARBON_CHECK(const_id.is_constant(),
  28. "Substitution into constant produced runtime value");
  29. return context().constant_values().GetInstId(const_id);
  30. }
  31. namespace {
  32. // Information about an instruction that we are substituting into.
  33. struct WorklistItem {
  34. // The instruction that we are substituting into.
  35. SemIR::InstId inst_id;
  36. // Whether the operands of this instruction have been added to the worklist.
  37. bool is_expanded : 1;
  38. // Whether the instruction was subst'd and re-added to the worklist.
  39. bool is_repeated : 1;
  40. // The index of the worklist item to process after we finish updating this
  41. // one. For the final child of an instruction, this is the parent. For any
  42. // other child, this is the index of the next child of the parent. For the
  43. // root, this is -1.
  44. int next_index : 31;
  45. };
  46. // A list of instructions that we're currently in the process of substituting
  47. // into. For details of the algorithm used here, see `SubstConstant`.
  48. class Worklist {
  49. public:
  50. explicit Worklist(SemIR::InstId root_id) {
  51. worklist_.push_back({.inst_id = root_id,
  52. .is_expanded = false,
  53. .is_repeated = false,
  54. .next_index = -1});
  55. }
  56. auto operator[](int index) -> WorklistItem& { return worklist_[index]; }
  57. auto size() -> int { return worklist_.size(); }
  58. auto back() -> WorklistItem& { return worklist_.back(); }
  59. auto Push(SemIR::InstId inst_id) -> void {
  60. CARBON_CHECK(inst_id.has_value());
  61. worklist_.push_back({.inst_id = inst_id,
  62. .is_expanded = false,
  63. .is_repeated = false,
  64. .next_index = static_cast<int>(worklist_.size() + 1)});
  65. CARBON_CHECK(worklist_.back().next_index > 0, "Constant too large.");
  66. }
  67. auto Pop() -> SemIR::InstId { return worklist_.pop_back_val().inst_id; }
  68. private:
  69. // Constants can get pretty large, so use a large worklist. This should be
  70. // about 4KiB, which should be small enough to comfortably fit on the stack,
  71. // but large enough that it's unlikely that we'll need a heap allocation.
  72. llvm::SmallVector<WorklistItem, 512> worklist_;
  73. };
  74. } // namespace
  75. // Pushes the specified operand onto the worklist.
  76. static auto PushOperand(Context& context, Worklist& worklist,
  77. SemIR::Inst::ArgAndKind arg) -> void {
  78. auto push_block = [&](SemIR::InstBlockId block_id) {
  79. for (auto inst_id :
  80. context.inst_blocks().Get(SemIR::InstBlockId(block_id))) {
  81. worklist.Push(inst_id);
  82. }
  83. };
  84. auto push_specific = [&](SemIR::SpecificId specific_id) {
  85. if (specific_id.has_value()) {
  86. push_block(context.specifics().Get(specific_id).args_id);
  87. }
  88. };
  89. CARBON_KIND_SWITCH(arg) {
  90. case CARBON_KIND(SemIR::InstId inst_id): {
  91. if (inst_id.has_value()) {
  92. worklist.Push(inst_id);
  93. }
  94. break;
  95. }
  96. case CARBON_KIND(SemIR::TypeInstId inst_id): {
  97. if (inst_id.has_value()) {
  98. worklist.Push(inst_id);
  99. }
  100. break;
  101. }
  102. case CARBON_KIND(SemIR::InstBlockId inst_block_id): {
  103. push_block(inst_block_id);
  104. break;
  105. }
  106. case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): {
  107. for (auto field : context.struct_type_fields().Get(fields_id)) {
  108. worklist.Push(field.type_inst_id);
  109. }
  110. break;
  111. }
  112. case CARBON_KIND(SemIR::SpecificId specific_id): {
  113. push_specific(specific_id);
  114. break;
  115. }
  116. case CARBON_KIND(SemIR::SpecificInterfaceId interface_id): {
  117. auto interface = context.specific_interfaces().Get(interface_id);
  118. push_specific(interface.specific_id);
  119. break;
  120. }
  121. case CARBON_KIND(SemIR::FacetTypeId facet_type_id): {
  122. const auto& facet_type_info = context.facet_types().Get(facet_type_id);
  123. for (auto extends : facet_type_info.extend_constraints) {
  124. push_specific(extends.specific_id);
  125. }
  126. for (auto impls : facet_type_info.self_impls_constraints) {
  127. push_specific(impls.specific_id);
  128. }
  129. for (auto extends : facet_type_info.extend_named_constraints) {
  130. push_specific(extends.specific_id);
  131. }
  132. for (auto impls : facet_type_info.self_impls_named_constraints) {
  133. push_specific(impls.specific_id);
  134. }
  135. for (const auto& type_impls : facet_type_info.type_impls_interfaces) {
  136. worklist.Push(type_impls.self_type);
  137. push_specific(type_impls.specific_interface.specific_id);
  138. }
  139. for (const auto& type_impls :
  140. facet_type_info.type_impls_named_constraints) {
  141. worklist.Push(type_impls.self_type);
  142. push_specific(type_impls.specific_named_constraint.specific_id);
  143. }
  144. for (auto rewrite : facet_type_info.rewrite_constraints) {
  145. worklist.Push(rewrite.lhs_id);
  146. worklist.Push(rewrite.rhs_id);
  147. }
  148. // TODO: Process other requirements as well.
  149. break;
  150. }
  151. default:
  152. break;
  153. }
  154. }
  155. // Converts the operands of this instruction into `InstId`s and pushes them onto
  156. // the worklist.
  157. static auto ExpandOperands(Context& context, Worklist& worklist,
  158. SemIR::InstId inst_id) -> void {
  159. auto inst = context.insts().Get(inst_id);
  160. if (inst.type_id().has_value()) {
  161. worklist.Push(context.types().GetTypeInstId(inst.type_id()));
  162. }
  163. PushOperand(context, worklist, inst.arg0_and_kind());
  164. PushOperand(context, worklist, inst.arg1_and_kind());
  165. }
  166. // Pops the specified operand from the worklist and returns it.
  167. static auto PopOperand(Context& context, Worklist& worklist,
  168. SemIR::Inst::ArgAndKind arg) -> int32_t {
  169. auto pop_block_id = [&](SemIR::InstBlockId old_inst_block_id) {
  170. auto size = context.inst_blocks().Get(old_inst_block_id).size();
  171. SemIR::CopyOnWriteInstBlock new_inst_block(&context.sem_ir(),
  172. old_inst_block_id);
  173. for (auto i : llvm::reverse(llvm::seq(size))) {
  174. new_inst_block.Set(i, worklist.Pop());
  175. }
  176. return new_inst_block.GetCanonical();
  177. };
  178. auto pop_specific = [&](SemIR::SpecificId specific_id) {
  179. if (!specific_id.has_value()) {
  180. return specific_id;
  181. }
  182. auto& specific = context.specifics().Get(specific_id);
  183. auto args_id = pop_block_id(specific.args_id);
  184. return context.specifics().GetOrAdd(specific.generic_id, args_id);
  185. };
  186. CARBON_KIND_SWITCH(arg) {
  187. case CARBON_KIND(SemIR::InstId inst_id): {
  188. if (!inst_id.has_value()) {
  189. return arg.value();
  190. }
  191. return worklist.Pop().index;
  192. }
  193. case CARBON_KIND(SemIR::TypeInstId inst_id): {
  194. if (!inst_id.has_value()) {
  195. return arg.value();
  196. }
  197. return worklist.Pop().index;
  198. }
  199. case CARBON_KIND(SemIR::InstBlockId inst_block_id): {
  200. return pop_block_id(inst_block_id).index;
  201. }
  202. case CARBON_KIND(SemIR::StructTypeFieldsId old_fields_id): {
  203. auto old_fields = context.struct_type_fields().Get(old_fields_id);
  204. SemIR::CopyOnWriteStructTypeFieldsBlock new_fields(&context.sem_ir(),
  205. old_fields_id);
  206. for (auto i : llvm::reverse(llvm::seq(old_fields.size()))) {
  207. new_fields.Set(
  208. i,
  209. {.name_id = old_fields[i].name_id,
  210. .type_inst_id = context.types().GetAsTypeInstId(worklist.Pop())});
  211. }
  212. return new_fields.GetCanonical().index;
  213. }
  214. case CARBON_KIND(SemIR::SpecificId specific_id): {
  215. return pop_specific(specific_id).index;
  216. }
  217. case CARBON_KIND(SemIR::SpecificInterfaceId interface_id): {
  218. auto interface = context.specific_interfaces().Get(interface_id);
  219. auto specific_id = pop_specific(interface.specific_id);
  220. return context.specific_interfaces()
  221. .Add({
  222. .interface_id = interface.interface_id,
  223. .specific_id = specific_id,
  224. })
  225. .index;
  226. }
  227. case CARBON_KIND(SemIR::FacetTypeId facet_type_id): {
  228. const auto& old_facet_type_info =
  229. context.facet_types().Get(facet_type_id);
  230. SemIR::FacetTypeInfo new_facet_type_info = {
  231. .other_requirements = old_facet_type_info.other_requirements};
  232. // Since these were added to a stack, we get them back in reverse order.
  233. new_facet_type_info.rewrite_constraints.resize(
  234. old_facet_type_info.rewrite_constraints.size(),
  235. SemIR::FacetTypeInfo::RewriteConstraint::None);
  236. for (auto& new_constraint :
  237. llvm::reverse(new_facet_type_info.rewrite_constraints)) {
  238. auto rhs_id = worklist.Pop();
  239. auto lhs_id = worklist.Pop();
  240. new_constraint = {.lhs_id = lhs_id, .rhs_id = rhs_id};
  241. }
  242. new_facet_type_info.type_impls_named_constraints.resize(
  243. old_facet_type_info.type_impls_named_constraints.size(),
  244. {SemIR::InstId::None, SemIR::SpecificNamedConstraint::None});
  245. for (auto [old_type_impls, new_type_impls] :
  246. llvm::reverse(llvm::zip_equal(
  247. old_facet_type_info.type_impls_named_constraints,
  248. new_facet_type_info.type_impls_named_constraints))) {
  249. auto specific_id =
  250. pop_specific(old_type_impls.specific_named_constraint.specific_id);
  251. auto self_type = worklist.Pop();
  252. new_type_impls = {
  253. .self_type = self_type,
  254. .specific_named_constraint = {
  255. old_type_impls.specific_named_constraint.named_constraint_id,
  256. specific_id}};
  257. }
  258. new_facet_type_info.type_impls_interfaces.resize(
  259. old_facet_type_info.type_impls_interfaces.size(),
  260. {SemIR::InstId::None, SemIR::SpecificInterface::None});
  261. for (auto [old_type_impls, new_type_impls] : llvm::reverse(
  262. llvm::zip_equal(old_facet_type_info.type_impls_interfaces,
  263. new_facet_type_info.type_impls_interfaces))) {
  264. auto specific_id =
  265. pop_specific(old_type_impls.specific_interface.specific_id);
  266. auto self_type = worklist.Pop();
  267. new_type_impls = {
  268. .self_type = self_type,
  269. .specific_interface = {
  270. old_type_impls.specific_interface.interface_id, specific_id}};
  271. }
  272. new_facet_type_info.self_impls_named_constraints.resize(
  273. old_facet_type_info.self_impls_named_constraints.size(),
  274. SemIR::SpecificNamedConstraint::None);
  275. for (auto [old_constraint, new_constraint] :
  276. llvm::reverse(llvm::zip_equal(
  277. old_facet_type_info.self_impls_named_constraints,
  278. new_facet_type_info.self_impls_named_constraints))) {
  279. new_constraint = {
  280. .named_constraint_id = old_constraint.named_constraint_id,
  281. .specific_id = pop_specific(old_constraint.specific_id)};
  282. }
  283. new_facet_type_info.extend_named_constraints.resize(
  284. old_facet_type_info.extend_named_constraints.size(),
  285. SemIR::SpecificNamedConstraint::None);
  286. for (auto [old_constraint, new_constraint] : llvm::reverse(
  287. llvm::zip_equal(old_facet_type_info.extend_named_constraints,
  288. new_facet_type_info.extend_named_constraints))) {
  289. new_constraint = {
  290. .named_constraint_id = old_constraint.named_constraint_id,
  291. .specific_id = pop_specific(old_constraint.specific_id)};
  292. }
  293. new_facet_type_info.self_impls_constraints.resize(
  294. old_facet_type_info.self_impls_constraints.size(),
  295. SemIR::SpecificInterface::None);
  296. for (auto [old_constraint, new_constraint] : llvm::reverse(
  297. llvm::zip_equal(old_facet_type_info.self_impls_constraints,
  298. new_facet_type_info.self_impls_constraints))) {
  299. new_constraint = {
  300. .interface_id = old_constraint.interface_id,
  301. .specific_id = pop_specific(old_constraint.specific_id)};
  302. }
  303. new_facet_type_info.extend_constraints.resize(
  304. old_facet_type_info.extend_constraints.size(),
  305. SemIR::SpecificInterface::None);
  306. for (auto [old_constraint, new_constraint] : llvm::reverse(
  307. llvm::zip_equal(old_facet_type_info.extend_constraints,
  308. new_facet_type_info.extend_constraints))) {
  309. new_constraint = {
  310. .interface_id = old_constraint.interface_id,
  311. .specific_id = pop_specific(old_constraint.specific_id)};
  312. }
  313. new_facet_type_info.Canonicalize();
  314. return context.facet_types().Add(new_facet_type_info).index;
  315. }
  316. default:
  317. return arg.value();
  318. }
  319. }
  320. // Pops the operands of the specified instruction off the worklist and rebuilds
  321. // the instruction with the updated operands if it has changed.
  322. static auto Rebuild(Context& context, Worklist& worklist, SemIR::InstId inst_id,
  323. SubstInstCallbacks& callbacks) -> SemIR::InstId {
  324. auto inst = context.insts().Get(inst_id);
  325. // Note that we pop in reverse order because we pushed them in forwards order.
  326. int32_t arg1 = PopOperand(context, worklist, inst.arg1_and_kind());
  327. int32_t arg0 = PopOperand(context, worklist, inst.arg0_and_kind());
  328. auto type_id = inst.type_id().has_value()
  329. ? callbacks.RebuildType(
  330. context.types().GetAsTypeInstId(worklist.Pop()))
  331. : SemIR::TypeId::None;
  332. if (type_id == inst.type_id() && arg0 == inst.arg0() && arg1 == inst.arg1()) {
  333. return callbacks.ReuseUnchanged(inst_id);
  334. }
  335. // TODO: Do we need to require this type to be complete?
  336. inst.SetType(type_id);
  337. inst.SetArgs(arg0, arg1);
  338. return callbacks.Rebuild(inst_id, inst);
  339. }
  340. auto SubstInst(Context& context, SemIR::InstId inst_id,
  341. SubstInstCallbacks& callbacks) -> SemIR::InstId {
  342. Worklist worklist(inst_id);
  343. // For each instruction that forms part of the constant, we will visit it
  344. // twice:
  345. //
  346. // - First, we visit it with `is_expanded == false`, we add all of its
  347. // operands onto the worklist, and process them by following this same
  348. // process.
  349. // - Then, once all operands are processed, we visit the instruction with
  350. // `is_expanded == true`, pop the operands back off the worklist, and if any
  351. // of them changed, rebuild this instruction.
  352. //
  353. // The second step is skipped if we can detect in the first step that the
  354. // instruction will not need to be rebuilt.
  355. int index = 0;
  356. while (index != -1) {
  357. auto& item = worklist[index];
  358. if (item.is_expanded) {
  359. // Rebuild this item if necessary. Note that this might pop items from the
  360. // worklist but does not reallocate, so does not invalidate `item`.
  361. auto old_inst_id = std::exchange(
  362. item.inst_id, Rebuild(context, worklist, item.inst_id, callbacks));
  363. if (item.is_repeated && old_inst_id != item.inst_id) {
  364. // SubstOperandsAndRetry was returned for the item, and the instruction
  365. // was rebuilt from new operands, so go through Subst() again. Note that
  366. // we've already called Rebuild so we don't want to leave this item as
  367. // repeated, and call back to ReuseUnchanged for it again later unless
  368. // the next call to Subst() asks for that.
  369. item.is_expanded = false;
  370. item.is_repeated = false;
  371. } else {
  372. index = item.next_index;
  373. continue;
  374. }
  375. }
  376. if (item.is_repeated) {
  377. // SubstAgain was returned for the item, and the result of that Subst() is
  378. // at the back of the worklist, which we pop. Note that popping from the
  379. // worklist does not reallocate, so does not invalidate `item`.
  380. //
  381. // When Subst returns SubstAgain, we must call back to Rebuild or
  382. // ReuseUnchanged for that work item.
  383. item.inst_id = callbacks.ReuseUnchanged(worklist.Pop());
  384. index = item.next_index;
  385. continue;
  386. }
  387. switch (callbacks.Subst(item.inst_id)) {
  388. case SubstInstCallbacks::SubstResult::FullySubstituted:
  389. // If any instruction is an ErrorInst, combining it into another
  390. // instruction will also produce an ErrorInst, so shortcut out here to
  391. // save wasted work.
  392. if (item.inst_id == SemIR::ErrorInst::InstId) {
  393. return SemIR::ErrorInst::InstId;
  394. }
  395. index = item.next_index;
  396. continue;
  397. case SubstInstCallbacks::SubstResult::SubstAgain: {
  398. item.is_repeated = true;
  399. // This modifies `worklist` which invalidates `item`.
  400. worklist.Push(item.inst_id);
  401. worklist.back().next_index = index;
  402. index = worklist.size() - 1;
  403. continue;
  404. }
  405. case SubstInstCallbacks::SubstResult::SubstOperands:
  406. break;
  407. case SubstInstCallbacks::SubstResult::SubstOperandsAndRetry:
  408. item.is_repeated = true;
  409. break;
  410. }
  411. // Extract the operands of this item into the worklist. Note that this
  412. // modifies the worklist, so it's not safe to use `item` after
  413. // `ExpandOperands` returns.
  414. item.is_expanded = true;
  415. int first_operand = worklist.size();
  416. int next_index = item.next_index;
  417. ExpandOperands(context, worklist, item.inst_id);
  418. // If there are any operands, go and update them before rebuilding this
  419. // item.
  420. if (worklist.size() > first_operand) {
  421. worklist.back().next_index = index;
  422. index = first_operand;
  423. } else {
  424. // No need to rebuild this instruction: its operands can't be changed by
  425. // substitution because it has none.
  426. item.inst_id = callbacks.ReuseUnchanged(item.inst_id);
  427. index = next_index;
  428. }
  429. }
  430. CARBON_CHECK(worklist.size() == 1,
  431. "Unexpected data left behind in work list");
  432. return worklist.back().inst_id;
  433. }
  434. auto SubstInst(Context& context, SemIR::TypeInstId inst_id,
  435. SubstInstCallbacks& callbacks) -> SemIR::TypeInstId {
  436. return context.types().GetAsTypeInstId(
  437. SubstInst(context, static_cast<SemIR::InstId>(inst_id), callbacks));
  438. }
  439. namespace {
  440. // Callbacks for performing substitution of a set of Substitutions into a
  441. // symbolic constant.
  442. class SubstConstantCallbacks final : public SubstInstCallbacks {
  443. public:
  444. // `context` must not be null.
  445. SubstConstantCallbacks(Context* context, SemIR::LocId loc_id,
  446. Substitutions substitutions)
  447. : SubstInstCallbacks(context),
  448. loc_id_(loc_id),
  449. substitutions_(substitutions) {}
  450. // Applies the given Substitutions to an instruction, in order to replace
  451. // SymbolicBinding instructions with the value of the binding.
  452. auto Subst(SemIR::InstId& inst_id) -> SubstResult override {
  453. if (context().constant_values().Get(inst_id).is_concrete()) {
  454. // This instruction is a concrete constant, so can't contain any
  455. // bindings that need to be substituted.
  456. return SubstResult::FullySubstituted;
  457. }
  458. auto entity_name_id = SemIR::EntityNameId::None;
  459. if (auto bind =
  460. context().insts().TryGetAs<SemIR::SymbolicBinding>(inst_id)) {
  461. entity_name_id = bind->entity_name_id;
  462. } else if (auto bind =
  463. context().insts().TryGetAs<SemIR::SymbolicBindingPattern>(
  464. inst_id)) {
  465. entity_name_id = bind->entity_name_id;
  466. } else {
  467. return SubstResult::SubstOperands;
  468. }
  469. auto& entity_name = context().entity_names().Get(entity_name_id);
  470. // This is a symbolic binding. Check if we're substituting it.
  471. // TODO: Consider building a hash map for substitutions. We might have a
  472. // lot of them.
  473. for (auto [bind_index, replacement_id] : substitutions_) {
  474. if (entity_name.bind_index() == bind_index) {
  475. // This is the binding we're replacing. Perform substitution.
  476. inst_id = context().constant_values().GetInstId(replacement_id);
  477. return SubstResult::FullySubstituted;
  478. }
  479. }
  480. // If it's not being substituted, we still need to look through it, as we
  481. // may need to substitute into its type (a `FacetType`, with one or more
  482. // `SpecificInterfaces` within).
  483. return SubstResult::SubstOperands;
  484. }
  485. // Rebuilds an instruction by building a new constant.
  486. auto Rebuild(SemIR::InstId /*old_inst_id*/, SemIR::Inst new_inst)
  487. -> SemIR::InstId override {
  488. return RebuildNewInst(loc_id_, new_inst);
  489. }
  490. private:
  491. SemIR::LocId loc_id_;
  492. Substitutions substitutions_;
  493. };
  494. } // namespace
  495. auto SubstConstant(Context& context, SemIR::LocId loc_id,
  496. SemIR::ConstantId const_id, Substitutions substitutions)
  497. -> SemIR::ConstantId {
  498. CARBON_CHECK(const_id.is_constant(), "Substituting into non-constant");
  499. if (substitutions.empty()) {
  500. // Nothing to substitute.
  501. return const_id;
  502. }
  503. if (!const_id.is_symbolic()) {
  504. // A concrete constant can't contain a reference to a symbolic binding.
  505. return const_id;
  506. }
  507. auto callbacks = SubstConstantCallbacks(&context, loc_id, substitutions);
  508. auto subst_inst_id = SubstInst(
  509. context, context.constant_values().GetInstId(const_id), callbacks);
  510. return context.constant_values().Get(subst_inst_id);
  511. }
  512. } // namespace Carbon::Check