@@ -1505,30 +1505,46 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
15051505 ? function->getNumParams ()
15061506 : (proto ? proto->getNumParams () : num_args);
15071507
1508+ // Pre-compute per-parameter metadata once to avoid recomputing in two loops.
1509+ struct ParamInfo {
1510+ clang::Expr *arg;
1511+ std::string name;
1512+ clang::QualType type;
1513+ bool has_default_value;
1514+ };
1515+ const unsigned num_params_to_bind =
1516+ std::min<unsigned >(num_named_params, num_args);
1517+ std::vector<ParamInfo> param_infos;
1518+ param_infos.reserve (num_params_to_bind);
1519+ for (unsigned i = 0 ; i < num_params_to_bind; ++i) {
1520+ ParamInfo pi;
1521+ pi.arg = expr->getArg (i + arg_begin);
1522+ pi.name = function ? function->getParamDecl (i)->getNameAsString ()
1523+ : (" arg" + std::to_string (i));
1524+ pi.type = function ? function->getParamDecl (i)->getType ()
1525+ : proto->getParamType (i);
1526+ pi.has_default_value =
1527+ function && function->getParamDecl (i)->hasDefaultArg ();
1528+ param_infos.push_back (std::move (pi));
1529+ }
1530+
15081531 // Track which args are materialized temps bound to reference params
15091532 std::vector<std::string> temp_refs (num_args);
15101533
1511- for (unsigned i = 0 ; i < num_named_params && i < num_args; ++i) {
1512- auto *arg = expr->getArg (i + arg_begin);
1513- std::string param_name = function
1514- ? function->getParamDecl (i)->getNameAsString ()
1515- : (" arg" + std::to_string (i));
1516- clang::QualType param_type = function ? function->getParamDecl (i)->getType ()
1517- : proto->getParamType (i);
1518-
1534+ for (unsigned i = 0 ; i < num_params_to_bind; ++i) {
1535+ auto &pi = param_infos[i];
15191536 bool is_materialize_to_ref =
1520- clang::isa<clang::MaterializeTemporaryExpr>(arg) &&
1521- param_type ->isLValueReferenceType ();
1537+ clang::isa<clang::MaterializeTemporaryExpr>(pi. arg ) &&
1538+ pi. type ->isLValueReferenceType ();
15221539
15231540 if (is_materialize_to_ref) {
15241541 auto [binding, ref] =
1525- MaterializeTemp (std::format (" _{}" , param_name ), param_type, arg);
1542+ MaterializeTemp (std::format (" _{}" , pi. name ), pi. type , pi. arg );
15261543 StrCat (binding);
15271544 temp_refs[i] = std::move (ref);
1528- } else if (!clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
1529- StrCat (" let" , std::format (" _{}: {}" , param_name, ToString (param_type)),
1530- " =" );
1531- convert_param_ty (param_type, arg);
1545+ } else if (!clang::isa<clang::MaterializeTemporaryExpr>(pi.arg )) {
1546+ StrCat (" let" , std::format (" _{}: {}" , pi.name , ToString (pi.type )), " =" );
1547+ convert_param_ty (pi.type , pi.arg );
15321548 StrCat (" ;" );
15331549 }
15341550 }
@@ -1541,28 +1557,19 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
15411557 }
15421558 {
15431559 PushParen call_args (*this );
1544- for (unsigned i = 0 ; i < num_named_params && i < num_args; ++i) {
1545- auto *arg = expr->getArg (i + arg_begin);
1546- std::string param_name =
1547- function ? function->getParamDecl (i)->getNameAsString ()
1548- : (" arg" + std::to_string (i));
1549- clang::QualType param_type = function
1550- ? function->getParamDecl (i)->getType ()
1551- : proto->getParamType (i);
1552- bool is_parm_with_default_value =
1553- function && function->getParamDecl (i)->hasDefaultArg ();
1554-
1555- if (is_parm_with_default_value) {
1560+ for (unsigned i = 0 ; i < num_params_to_bind; ++i) {
1561+ auto &pi = param_infos[i];
1562+ if (pi.has_default_value ) {
15561563 StrCat (" Some(" );
15571564 }
15581565 if (!temp_refs[i].empty ()) {
15591566 StrCat (temp_refs[i]);
1560- } else if (clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
1561- convert_param_ty (param_type, arg);
1567+ } else if (clang::isa<clang::MaterializeTemporaryExpr>(pi. arg )) {
1568+ convert_param_ty (pi. type , pi. arg );
15621569 } else {
1563- StrCat (std::format (" _{}" , param_name ));
1570+ StrCat (std::format (" _{}" , pi. name ));
15641571 }
1565- if (is_parm_with_default_value ) {
1572+ if (pi. has_default_value ) {
15661573 StrCat (" )" );
15671574 }
15681575 StrCat (token::kComma );
@@ -3560,6 +3567,11 @@ std::string Converter::ConvertIRFragment(
35603567 } else if (auto *g = std::get_if<GenericFragment>(&frag)) {
35613568 result += Mapper::InstantiateTemplate (GetCalleeOrExpr (expr), g->name );
35623569 } else if (auto *ph = std::get_if<PlaceholderFragment>(&frag)) {
3570+ // Placeholder args are single-digit only ("a0".."a9"); enforced here
3571+ // to match the existing matchTemplate design constraint. A static_assert
3572+ // is not possible because ph->arg is runtime data from parsed rules.
3573+ assert (ph->arg .size () == 2 && std::isdigit (static_cast <unsigned char >(ph->arg [1 ])) &&
3574+ " placeholder arg must be a single-digit index" );
35633575 auto arg_idx = static_cast <int >(ph->arg [1 ] - ' 0' ); // "a0" -> 0
35643576 assert (arg_idx < static_cast <int >(all_args.size ()));
35653577 auto *arg = all_args[arg_idx];
0 commit comments