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

Code before:
          context.clang_decls().Get(template_name_type->decl_id).key.decl));
      return clang::TemplateArgumentLoc(
          context.ast_context(), clang::TemplateArgument(name),
          /*TemplateKWLoc=*/clang::SourceLocation(),
          clang::NestedNameSpecifierLoc(),
          GetCppLocation(context, SemIR::LocId(arg_id)));
    }

    // TODO: Eventually we should also support passing Carbon generics as
    // template template arguments.
    return MakePlaceholderTemplateArg(context, arg_id);
  }

  if (auto* non_type = dyn_cast<clang::NonTypeTemplateParmDecl>(param_decl)) {
    auto param_type = non_type->getType();

    // Handle non-type parameters with a dependent type. For example:
    //
    // C++:    template<typename T, T N> struct S{};
    // Carbon: Cpp.S(i32, 42)
    //
    // When evaluating the second template argument, the generic type of
    // `T` should be substituted with `i32`.
    if (param_type->isInstantiationDependentType()) {
      clang::Sema::InstantiatingTemplate inst(
          context.clang_sema(), clang::SourceLocation(), param_decl, non_type,
          ctai.SugaredConverted, clang::SourceRange());
      if (inst.isInvalid()) {
        return std::nullopt;
      }
      clang::MultiLevelTemplateArgumentList mltal(template_decl,
                                                  ctai.SugaredConverted,
                                                  /*Final=*/true);

      mltal.addOuterRetainedLevels(non_type->getDepth());
      if (!param_type->getAs<clang::PackExpansionType>()) {
        param_type = context.clang_sema().SubstType(param_type, mltal,
                                                    non_type->getLocation(),
                                                    non_type->getDeclName());
      }

      if (!param_type.isNull()) {
        param_type = context.clang_sema().CheckNonTypeTemplateParameterType(
            param_type, non_type->getLocation());
      }
      if (param_type.isNull()) {
        return std::nullopt;
      }
    }

    // Handle integer parameters.

Review comment (suggestion):
In the pack expansion case, we should pass the pack index in from `ConvertArgsToTemplateArgs` and use it here. It would be fine to just add a TODO for that for now, though.

Code after:
  }

  if (auto* non_type = dyn_cast<clang::NonTypeTemplateParmDecl>(param_decl)) {
    auto param_type = non_type->getType();

    // Handle non-type parameters with a dependent type. For example:
    //
    // C++:    template<typename T, T N> struct S{};
    // Carbon: Cpp.S(i32, 42)
    //
    // When evaluating the second template argument, the generic type of
    // `T` should be substituted with `i32`.
    if (param_type->isInstantiationDependentType()) {
      clang::Sema::InstantiatingTemplate inst(
          context.clang_sema(), clang::SourceLocation(), param_decl, non_type,
          *template_args, clang::SourceRange());
      if (inst.isInvalid()) {
        return std::nullopt;
      }
      clang::MultiLevelTemplateArgumentList mltal(template_decl, *template_args,
                                                  /*Final=*/true);

      mltal.addOuterRetainedLevels(non_type->getDepth());
      // TODO: handle pack expansion by passing in the pack index from
      // `ConvertArgsToTemplateArgs`.
      if (!param_type->getAs<clang::PackExpansionType>()) {
        param_type = context.clang_sema().SubstType(param_type, mltal,
                                                    non_type->getLocation(),
                                                    non_type->getDeclName());
      }

      if (!param_type.isNull()) {
        param_type = context.clang_sema().CheckNonTypeTemplateParameterType(
            param_type, non_type->getLocation());
      }
      if (param_type.isNull()) {
        return std::nullopt;
      }
    }

    // Handle integer parameters.
    if (param_type->isIntegerType()) {
      // Get the Carbon type corresponding to the parameter's Clang type.
      const auto type_expr =
          ImportCppType(context, SemIR::LocId(arg_id), param_type);

      // Try to convert the argument to the parameter type.
      const auto converted_inst_id =
          Convert(context, SemIR::LocId(arg_id), arg_id,
                  {
                      .kind = ConversionTarget::Value,