Repository: carbon-language/carbon-lang
File: toolchain/check/eval.cpp (C++)

Code before:

  Diagnostics::AnnotationScope annotate_diagnostics(
      &context.emitter(), [&](auto& builder) {
        CARBON_DIAGNOSTIC(ResolvingSpecificHere, Note, "in {0} used here",
                          SemIR::SpecificId);
        builder.Note(loc_id, ResolvingSpecificHere, specific_id);
      });

  for (auto [i, inst_id] : llvm::enumerate(eval_block)) {
    auto const_id = TryEvalInstInContext(eval_context, inst_id,
                                         context.insts().Get(inst_id));
    result[i] = context.constant_values().GetInstId(const_id);
    CARBON_CHECK(result[i].has_value(), "Failed to evaluate {0} in eval block",
                 context.insts().Get(inst_id));
  }

  return context.inst_blocks().Add(result);
}

// Information about the function call we are currently executing. Unlike
// evaluation, execution sequentially interprets instructions, and can handle
// control flow and (eventually) side effects and mutable state.
class FunctionExecContext : public EvalContext {
 public:
  // A block argument passed to `BranchWithArg`.
  struct BlockArg {
    SemIR::InstBlockId block_id = SemIR::InstBlockId::None;
    SemIR::ConstantId arg_id = SemIR::ConstantId::None;
  };

  FunctionExecContext(Context* context, SemIR::LocId loc_id,
                      SemIR::SpecificId specific_id,
                      Map<SemIR::InstId, SemIR::ConstantId>* locals,
                      SemIR::InstBlockId args_id)
      : EvalContext(context, loc_id, specific_id,
                    LocalEvalInfo{.locals = locals}),
        args_(context->inst_blocks().Get(args_id)) {}

  // Returns the argument values supplied in the call to the function.
  auto args() const -> llvm::ArrayRef<SemIR::InstId> { return args_; }

  using EvalContext::locals;

  // Branch control flow to the given block. This replaces the innermost block
  // in the block stack, but doesn't affect any enclosing blocks.
  auto BranchTo(SemIR::InstBlockId block_id) -> void {
    blocks_.back() = inst_blocks().Get(block_id);
  }

  // Push a new block to be executed immediately. After the block finishes,
  // control will resume after the current instruction.

Review comment (suggestion):
Could we give this a slightly different name? It was confusing to have both SemIR::BlockArg and then this BlockArg. Maybe this is `BranchWithArgValue`? SemIR::BlockArg says that it "Reads an argument from `BranchWithArg`" so giving it a name showing how it's providing that thing for SemIR::BlockArg to use might be nice.


Code after:

  Diagnostics::AnnotationScope annotate_diagnostics(
      &context.emitter(), [&](auto& builder) {
        CARBON_DIAGNOSTIC(ResolvingSpecificHere, Note, "in {0} used here",
                          SemIR::SpecificId);
        builder.Note(loc_id, ResolvingSpecificHere, specific_id);
      });

  for (auto [i, inst_id] : llvm::enumerate(eval_block)) {
    auto const_id = TryEvalInstInContext(eval_context, inst_id,
                                         context.insts().Get(inst_id));
    result[i] = context.constant_values().GetInstId(const_id);
    CARBON_CHECK(result[i].has_value(), "Failed to evaluate {0} in eval block",
                 context.insts().Get(inst_id));
  }

  return context.inst_blocks().Add(result);
}

// Information about the function call we are currently executing. Unlike
// evaluation, execution sequentially interprets instructions, and can handle
// control flow and (eventually) side effects and mutable state.
class FunctionExecContext : public EvalContext {
 public:
  // A block argument passed to `BranchWithArg`.
  struct BlockArgValue {
    SemIR::InstBlockId block_id = SemIR::InstBlockId::None;
    SemIR::ConstantId arg_id = SemIR::ConstantId::None;
  };

  FunctionExecContext(Context* context, SemIR::LocId loc_id,
                      SemIR::SpecificId specific_id,
                      Map<SemIR::InstId, SemIR::ConstantId>* locals,
                      SemIR::InstBlockId args_id)
      : EvalContext(context, loc_id, specific_id,
                    LocalEvalInfo{.locals = locals}),
        args_(context->inst_blocks().Get(args_id)) {}

  // Returns the argument values supplied in the call to the function.
  auto args() const -> llvm::ArrayRef<SemIR::InstId> { return args_; }

  using EvalContext::locals;

  // Branch control flow to the given block. This replaces the innermost block
  // in the block stack, but doesn't affect any enclosing blocks.
  auto BranchTo(SemIR::InstBlockId block_id) -> void {
    blocks_.back() = inst_blocks().Get(block_id);
  }

  // Push a new block to be executed immediately. After the block finishes,
  // control will resume after the current instruction.