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

Code before:
      auto value_inst = eval_context.insts().Get(
          eval_context.constant_values().GetInstId(value));
      value_inst.SetType(type_id);

      auto from_phase = value.is_symbolic() ? Phase::Symbolic : Phase::Template;
      auto to_phase = eval_context.types().GetConstantId(type_id).is_symbolic()
                          ? Phase::Symbolic
                          : Phase::Template;
      if (to_phase >= from_phase) {
        // If moving from a template constant value to a symbolic type, the new
        // constant value takes on the phase of the new type. We're adding the
        // symbolic bit to the new constant value due to the presence of a
        // symbolic type.
        return MakeConstantResult(eval_context.context(), value_inst, to_phase);
      } else {
        // If moving from a symbolic constant value to a template type, the new
        // constant value has a phase that depends on what is in the value. If
        // there is anything symbolic within the value, then it's symbolic. We
        // can't easily determine that here without evaluating a new constant
        // value. See
        // https://github.com/carbon-language/carbon-lang/pull/4881#discussion_r1939961372
        //
        // TODO: Find a way to avoid recursion for this. Make this function into
        // a while loop? Track the phase of the constant value separately from
        // the phase of the value's type?
        return TryEvalInstInContext(eval_context, SemIR::InstId::None,
                                    value_inst);
      }
    }

    // These semantic wrappers don't change the constant value.
    case CARBON_KIND(SemIR::BindAlias typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::ExportDecl typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::NameRef typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::ValueParamPattern param_pattern): {
      // TODO: Treat this as a non-expression (here and in GetExprCategory)
      // once generic deduction doesn't need patterns to have constant values.
      return eval_context.GetConstantValue(param_pattern.subpattern_id);
    }
    case CARBON_KIND(SemIR::Converted typed_inst): {
      return eval_context.GetConstantValue(typed_inst.result_id);
    }
    case CARBON_KIND(SemIR::InitializeFrom typed_inst): {
      return eval_context.GetConstantValue(typed_inst.src_id);
    }

Review comment (bug):
Can you use `[[clang::musttail]]` here? (I'm not sure if that suppresses the lint check, but if not we can file a clang-tidy bug...)
```suggestion
        [[clang::musttail]] return TryEvalInstInContext(eval_context, SemIR::InstId::None,
```

Code after:
      }

      auto from_phase = Phase::Template;
      auto value_inst_id =
          GetConstantValue(eval_context, inst.source_id, &from_phase);

      auto to_phase = Phase::Template;
      auto type_id = GetConstantValue(eval_context, inst.type_id, &to_phase);

      auto value_inst = eval_context.insts().Get(value_inst_id);
      value_inst.SetType(type_id);

      if (to_phase >= from_phase) {
        // If moving from a template constant value to a symbolic type, the new
        // constant value takes on the phase of the new type. We're adding the
        // symbolic bit to the new constant value due to the presence of a
        // symbolic type.
        return MakeConstantResult(eval_context.context(), value_inst, to_phase);
      } else {
        // If moving from a symbolic constant value to a template type, the new
        // constant value has a phase that depends on what is in the value. If
        // there is anything symbolic within the value, then it's symbolic. We
        // can't easily determine that here without evaluating a new constant
        // value. See
        // https://github.com/carbon-language/carbon-lang/pull/4881#discussion_r1939961372
        [[clang::musttail]] return TryEvalInstInContext(
            eval_context, SemIR::InstId::None, value_inst);
      }
    }

    // These semantic wrappers don't change the constant value.
    case CARBON_KIND(SemIR::BindAlias typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::ExportDecl typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::NameRef typed_inst): {
      return eval_context.GetConstantValue(typed_inst.value_id);
    }
    case CARBON_KIND(SemIR::ValueParamPattern param_pattern): {
      // TODO: Treat this as a non-expression (here and in GetExprCategory)
      // once generic deduction doesn't need patterns to have constant values.
      return eval_context.GetConstantValue(param_pattern.subpattern_id);
    }
    case CARBON_KIND(SemIR::Converted typed_inst): {
      return eval_context.GetConstantValue(typed_inst.result_id);
    }
    case CARBON_KIND(SemIR::InitializeFrom typed_inst): {
      return eval_context.GetConstantValue(typed_inst.src_id);
    }