Interop support for initialization via std::initializer_list. (#6672)

Add a new builtin function `cpp.std.initializer_list.make` that takes an
array and returns a `std::initializer_list`, initialized to refer to
that array. When C++ initialization wants to perform a
`std::initializer_list`-from-array construction, synthesize a
declaration of a matching builtin function and use that to perform the
initialization.

Ideally we would specify this conversion as an impl of `ImplicitAs` in
the prelude instead of hardcoding it in the interop layer, but
unfortunately that's not currently possible, for various reasons -- we
can't make the conversion form-generic, we can't deduce the array length
from the initializer, and we can't deduce against the arguments of
imported C++ class templates yet -- so for now synthesizing a builtin
function on demand is the best we can do.

Assisted-by: Gemini 3 Pro via Antigravity
This commit is contained in:
Richard Smith
2026-01-30 22:24:18 +00:00
committed by GitHub
parent 20a5c43e95
commit c0b24047dd
27 changed files with 2137 additions and 525 deletions
+2 -58
View File
@@ -71,37 +71,8 @@ auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool {
// Propagate the type expression.
auto form_expr = ExprAsReturnForm(context, type_node_id, type_inst_id);
context.PushReturnForm(form_expr);
llvm::SmallVector<SemIR::InstId, 1> return_patterns;
auto form_inst = context.insts().Get(form_expr.form_inst_id);
CARBON_KIND_SWITCH(form_inst) {
case SemIR::RefForm::Kind: {
break;
}
case CARBON_KIND(SemIR::InitForm init_form): {
auto pattern_type_id = GetPatternType(context, form_expr.type_id);
auto return_slot_pattern_id = AddPatternInst<SemIR::ReturnSlotPattern>(
context, node_id,
{.type_id = pattern_type_id,
.type_inst_id = form_expr.type_component_id});
return_patterns.push_back(AddPatternInst(
context,
SemIR::LocIdAndInst::UncheckedLoc(
type_node_id,
SemIR::OutParamPattern{.type_id = pattern_type_id,
.subpattern_id = return_slot_pattern_id,
.index = init_form.index})));
break;
}
case SemIR::ErrorInst::Kind: {
break;
}
default:
CARBON_FATAL("unexpected inst kind: {0}", form_inst);
}
context.node_stack().Push(
node_id, context.inst_blocks().AddCanonical(return_patterns));
auto return_patterns_id = AddReturnPatterns(context, node_id, form_expr);
context.node_stack().Push(node_id, return_patterns_id);
return true;
}
@@ -631,33 +602,6 @@ static auto LookupBuiltinFunctionKind(Context& context,
return kind;
}
// Returns whether `function` is a valid declaration of `builtin_kind`.
static auto IsValidBuiltinDeclaration(Context& context,
const SemIR::Function& function,
SemIR::BuiltinFunctionKind builtin_kind)
-> bool {
if (!function.call_params_id.has_value()) {
// For now, we have no builtins that support positional parameters.
return false;
}
// Find the list of call parameters other than the implicit return slots.
auto call_params = context.inst_blocks()
.Get(function.call_params_id)
.drop_back(context.inst_blocks()
.GetOrEmpty(function.return_patterns_id)
.size());
// Get the return type. This is `()` if none was specified.
auto return_type_id = function.GetDeclaredReturnType(context.sem_ir());
if (!return_type_id.has_value()) {
return_type_id = GetTupleType(context, {});
}
return builtin_kind.IsValidType(context.sem_ir(), call_params,
return_type_id);
}
auto HandleParseNode(Context& context,
Parse::BuiltinFunctionDefinitionId /*node_id*/) -> bool {
auto name_id =