mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +01:00
Fix initialization of var parameters. (#7023)
When an initializing expression is used to initialize a var parameter, we need to create the storage earlier in SemIR than the initializing expression. To do so, pass a pending block to initialization containing the var storage. Also stop using `temporary` for this purpose, since we treat temporaries as potentially-constant and immutable, but `var` parameters can be mutated by the callee. We should ideally introduce a new kind of instruction for this purpose but for now we just use `var_storage`.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include "toolchain/check/member_access.h"
|
||||
#include "toolchain/check/operator.h"
|
||||
#include "toolchain/check/pattern_match.h"
|
||||
#include "toolchain/check/pending_block.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
@@ -2048,16 +2049,24 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
return expr_id;
|
||||
}
|
||||
|
||||
auto Initialize(Context& context, SemIR::LocId loc_id, SemIR::InstId storage_id,
|
||||
SemIR::InstId value_id, bool for_return) -> SemIR::InstId {
|
||||
auto InitializeExisting(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId storage_id, SemIR::InstId value_id,
|
||||
bool for_return) -> SemIR::InstId {
|
||||
auto type_id = context.insts().Get(storage_id).type_id();
|
||||
if (for_return &&
|
||||
!SemIR::InitRepr::ForType(context.sem_ir(), type_id).MightBeInPlace()) {
|
||||
// TODO: is it safe to use storage_id when the init repr is dependent?
|
||||
// TODO: Is it safe to use storage_id when the init repr is dependent?
|
||||
storage_id = SemIR::InstId::None;
|
||||
}
|
||||
// TODO: add CHECK that storage_id.index < value_id.index to enforce the
|
||||
// precondition, once existing violations have been cleaned up.
|
||||
|
||||
// TODO: This is only an approximation of a dominance check. Add a general
|
||||
// end-of-phase dominance check and remove the check here and the one in
|
||||
// `MergeReplacing`.
|
||||
CARBON_CHECK(!storage_id.has_value() ||
|
||||
value_id == SemIR::ErrorInst::InstId ||
|
||||
context.insts().GetRawIndex(storage_id) <=
|
||||
context.insts().GetRawIndex(value_id),
|
||||
"Storage might not dominate initializer");
|
||||
PendingBlock target_block(&context);
|
||||
return Convert(context, loc_id, value_id,
|
||||
{.kind = ConversionTarget::Initializing,
|
||||
@@ -2066,6 +2075,36 @@ auto Initialize(Context& context, SemIR::LocId loc_id, SemIR::InstId storage_id,
|
||||
.storage_access_block = &target_block});
|
||||
}
|
||||
|
||||
auto Initialize(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId&& storage_id, PendingBlock&& storage_access_block,
|
||||
SemIR::InstId value_id) -> InitializeResult {
|
||||
CARBON_CHECK(storage_id.has_value());
|
||||
auto type_id = context.insts().Get(storage_id).type_id();
|
||||
auto result_id = Convert(context, loc_id, value_id,
|
||||
{.kind = ConversionTarget::Initializing,
|
||||
.type_id = type_id,
|
||||
.storage_id = storage_id,
|
||||
.storage_access_block = &storage_access_block});
|
||||
|
||||
// Insert the storage block now, in case it wasn't used by the initializer.
|
||||
storage_access_block.InsertHere();
|
||||
if (result_id == SemIR::ErrorInst::InstId) {
|
||||
return {.storage_id = SemIR::ErrorInst::InstId,
|
||||
.init_id = SemIR::ErrorInst::InstId};
|
||||
}
|
||||
|
||||
// Find the storage argument. If the storage block was spliced or written over
|
||||
// an existing storage argument by `Convert`, the resulting expression will
|
||||
// have a storage argument that points to the possibly-rewritten storage
|
||||
// instruction, and we can use that. Otherwise, the storage access block will
|
||||
// have been inserted above, and we can use `storage_id` unchanged.
|
||||
auto storage_arg_id =
|
||||
SemIR::FindStorageArgForInitializer(context.sem_ir(), result_id);
|
||||
return {
|
||||
.storage_id = storage_arg_id.has_value() ? storage_arg_id : storage_id,
|
||||
.init_id = result_id};
|
||||
}
|
||||
|
||||
auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id)
|
||||
-> SemIR::InstId {
|
||||
return Convert(context, SemIR::LocId(expr_id), expr_id,
|
||||
|
||||
+69
-14
@@ -55,7 +55,7 @@ struct ConversionTarget {
|
||||
// be present if the initializing representation might be in-place.
|
||||
Initializing,
|
||||
// Convert to an in-place initializing expression whose storage is
|
||||
// designated by `storage_id` (which must not be `None`)
|
||||
// designated by `storage_id` (which must not be `None`).
|
||||
InPlaceInitializing,
|
||||
Last = InPlaceInitializing
|
||||
};
|
||||
@@ -63,11 +63,14 @@ struct ConversionTarget {
|
||||
Kind kind;
|
||||
// The target type for the conversion.
|
||||
SemIR::TypeId type_id;
|
||||
// The storage being initialized, if any.
|
||||
// The storage being initialized, if any. It must be valid to reference this
|
||||
// instruction after splicing in `storage_access_block` (if specified), so it
|
||||
// must either dominate the initializer or be one of the instructions in
|
||||
// `storage_access_block`.
|
||||
SemIR::InstId storage_id = SemIR::InstId::None;
|
||||
// For an initializer, a block of pending instructions that `storage_id`
|
||||
// depends on, and that can be discarded if `storage_id` is not accessed.
|
||||
// If this is not null or empty, its last element must be storage_id.
|
||||
// depends on. This block will be spliced or merged before any reference to
|
||||
// `storage_id`, and may be discarded if `storage_id` is not accessed.
|
||||
PendingBlock* storage_access_block = nullptr;
|
||||
// Whether failure of conversion is an error and is diagnosed to the user.
|
||||
// When looking for a possible conversion but with graceful fallback,
|
||||
@@ -89,12 +92,17 @@ struct ConversionTarget {
|
||||
auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
ConversionTarget target) -> SemIR::InstId;
|
||||
|
||||
// Converts `value_id` to an initializing expression of the type of
|
||||
// `storage_id`, and returns the possibly-converted initializing expression.
|
||||
// Performs initialization of `storage_id` from the expression `value_id`, which
|
||||
// is converted to an initializing expression of the type of `storage_id` if
|
||||
// necessary, and returns the possibly-converted initializing expression.
|
||||
//
|
||||
// `storage_id` is used as the storage argument of the resulting expression
|
||||
// except as noted below, and when it is used as the storage argument it must
|
||||
// precede `value_id`. The caller is responsible for passing the result to an
|
||||
// inst that is documented as consuming it, such as `Assign`.
|
||||
// except as noted below. As a consequence, `storage_id` must dominate
|
||||
// `value_id` and its subexpressions. This will typically only be the case if
|
||||
// `storage_id` syntactically precedes `value_id`. Otherwise, some action will
|
||||
// need to be taken to reorder the code, such as instead calling `Initialize`
|
||||
// with a pending block containing `storage_id`, or creating a separate
|
||||
// `InstBlock` to hold either the storage or the initializer.
|
||||
//
|
||||
// `for_return` indicates that this conversion is initializing the operand of a
|
||||
// `return` statement. This means that `storage_id` will be the return slot
|
||||
@@ -102,11 +110,58 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
// representation is not in-place, so in that case `storage_id` will be used
|
||||
// solely for its type.
|
||||
//
|
||||
// TODO: Consider making the target type a separate parameter, and making
|
||||
// storage_id optional.
|
||||
auto Initialize(Context& context, SemIR::LocId loc_id, SemIR::InstId storage_id,
|
||||
SemIR::InstId value_id, bool for_return = false)
|
||||
-> SemIR::InstId;
|
||||
// This function does not guarantee to perform an in-place initialization, so
|
||||
// the caller is responsible for passing the returned `InstId` to an inst that
|
||||
// is documented as consuming it, such as `Assign`.
|
||||
auto InitializeExisting(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId storage_id, SemIR::InstId value_id,
|
||||
bool for_return = false) -> SemIR::InstId;
|
||||
|
||||
// Result of Initialize.
|
||||
struct InitializeResult {
|
||||
// The storage location that contains the initialized value. This may be
|
||||
// different from the `storage_id` that was passed to `Initialize` if the
|
||||
// storage block was written over existing instructions rather than being
|
||||
// spliced in.
|
||||
SemIR::InstId storage_id;
|
||||
// The converted initializing expression used to initialize the storage.
|
||||
SemIR::InstId init_id;
|
||||
};
|
||||
|
||||
// Performs initialization of `storage_id` from the expression `value_id`, which
|
||||
// is converted to an initializing expression of the type of `storage_id` if
|
||||
// necessary. `storage_access_block` should be used to supply a pending block
|
||||
// that allocates the storage, and typically contains `storage_id`. The target
|
||||
// of the initialization will be either `storage_id` itself, or an existing
|
||||
// storage argument instruction that is overwritten to hold a copy of
|
||||
// `storage_id` as an optimization for SemIR compactness.
|
||||
//
|
||||
// The storage instruction will only be written over an existing instruction if
|
||||
// it is the sole instruction in the pending block. This is expected to be a
|
||||
// common case. After this happens, the copy of the instruction in the pending
|
||||
// block is expected to be unreachable from the SemIR::File. As a result, the
|
||||
// `storage_id` instruction should not be referenced again after calling this
|
||||
// function, and this function takes it by rvalue reference to remind the caller
|
||||
// of this.
|
||||
//
|
||||
// If the overwrite optimization is not performed, `storage_access_block` will
|
||||
// be inserted before any use of the storage by the initializer, and will be
|
||||
// inserted even if the initializer does not actually use the storage. It must
|
||||
// be valid to reference `storage_id` after splicing in `storage_access_block`,
|
||||
// so `storage_id` must either dominate the initializer (but see the TODO below)
|
||||
// or be one of the instructions in `storage_access_block`. If `storage_id` is
|
||||
// known to always dominate the initializer, `InitializeExisting` should be used
|
||||
// instead.
|
||||
//
|
||||
// TODO: We don't have an implementation of a proper dominance check, so we
|
||||
// fake one up by comparing the order in which the insts were created.
|
||||
//
|
||||
// This function does not guarantee to perform an in-place initialization, so
|
||||
// the caller is responsible for passing the returned `inst_id` to an inst that
|
||||
// is documented as consuming it, such as `Assign`.
|
||||
auto Initialize(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId&& storage_id, PendingBlock&& storage_access_block,
|
||||
SemIR::InstId value_id) -> InitializeResult;
|
||||
|
||||
// Convert the given expression to a value expression of the same type.
|
||||
auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "toolchain/check/pattern_match.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/sem_ir/absolute_node_ref.h"
|
||||
#include "toolchain/sem_ir/expr_info.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
@@ -169,12 +170,19 @@ auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool {
|
||||
.op_name = CoreIdentifier::NewCursor},
|
||||
range_id);
|
||||
auto cursor_type_id = context.insts().Get(cursor_id).type_id();
|
||||
auto cursor_var_id = AddInstWithCleanup<SemIR::VarStorage>(
|
||||
context, node_id,
|
||||
PendingBlock cursor_var_block(&context);
|
||||
auto cursor_var_id = cursor_var_block.AddInstWithCleanup<SemIR::VarStorage>(
|
||||
node_id,
|
||||
{.type_id = cursor_type_id, .pattern_id = SemIR::AbsoluteInstId::None});
|
||||
auto init_id = Initialize(context, node_id, cursor_var_id, cursor_id);
|
||||
AddInst<SemIR::Assign>(context, node_id,
|
||||
{.lhs_id = cursor_var_id, .rhs_id = init_id});
|
||||
// Disable broken lint that suggests a "fix" that doesn't compile.
|
||||
auto init_result = Initialize(context, node_id,
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
std::move(cursor_var_id),
|
||||
std::move(cursor_var_block), cursor_id);
|
||||
AddInst<SemIR::Assign>(
|
||||
context, node_id,
|
||||
{.lhs_id = init_result.storage_id, .rhs_id = init_result.init_id});
|
||||
cursor_var_id = init_result.storage_id;
|
||||
|
||||
// Start emitting the loop header block.
|
||||
auto loop_header_id = StartLoopHeader(context, start_node_id);
|
||||
|
||||
@@ -113,7 +113,7 @@ auto HandleParseNode(Context& context, Parse::InfixOperatorEqualId node_id)
|
||||
}
|
||||
// TODO: Destroy the old value before reinitializing. This will require
|
||||
// building the destruction code before we build the RHS subexpression.
|
||||
rhs_id = Initialize(context, node_id, lhs_id, rhs_id);
|
||||
rhs_id = InitializeExisting(context, node_id, lhs_id, rhs_id);
|
||||
AddInst<SemIR::Assign>(context, node_id,
|
||||
{.lhs_id = lhs_id, .rhs_id = rhs_id});
|
||||
// We model assignment as an expression, so we need to push a value for
|
||||
|
||||
@@ -634,7 +634,6 @@ auto MatchContext::DoPreWork(State state, SemIR::VarPattern var_pattern,
|
||||
auto MatchContext::DoVarPreWorkImpl(State state, SemIR::TypeId pattern_type_id,
|
||||
SemIR::InstId scrutinee_id,
|
||||
WorkItem entry) const -> SemIR::InstId {
|
||||
auto storage_id = SemIR::InstId::None;
|
||||
CARBON_KIND_SWITCH(state) {
|
||||
case CARBON_KIND(CalleeState* _): {
|
||||
// We're emitting pattern-match IR for the callee, but we're still on
|
||||
@@ -646,52 +645,56 @@ auto MatchContext::DoVarPreWorkImpl(State state, SemIR::TypeId pattern_type_id,
|
||||
return scrutinee_id;
|
||||
}
|
||||
case CARBON_KIND(LocalState* _): {
|
||||
// TODO: Find a more efficient way to put these insts in the global_init
|
||||
// block (or drop the distinction between the global_init block and the
|
||||
// file scope?)
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
context_.global_init().Resume();
|
||||
}
|
||||
|
||||
// In a `var`/`let` declaration, the `VarStorage` inst is created before
|
||||
// we start pattern matching.
|
||||
auto lookup_result = context_.var_storage_map().Lookup(entry.pattern_id);
|
||||
CARBON_CHECK(lookup_result);
|
||||
storage_id = lookup_result.value();
|
||||
break;
|
||||
auto storage_id = lookup_result.value();
|
||||
if (scrutinee_id.has_value()) {
|
||||
auto init_id =
|
||||
InitializeExisting(context_, SemIR::LocId(entry.pattern_id),
|
||||
storage_id, scrutinee_id, /*for_return=*/false);
|
||||
// TODO: It's a bit weird to use an `Assign` instruction to model
|
||||
// initialization. Consider adding a different instruction for this
|
||||
// purpose.
|
||||
AddInst<SemIR::Assign>(context_, SemIR::LocId(entry.pattern_id),
|
||||
{.lhs_id = storage_id, .rhs_id = init_id});
|
||||
}
|
||||
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
context_.global_init().Suspend();
|
||||
}
|
||||
return storage_id;
|
||||
}
|
||||
case CARBON_KIND(CallerState* _): {
|
||||
storage_id = AddInst<SemIR::TemporaryStorage>(
|
||||
// TODO: This variable's lifetime should end at the end of the call or the
|
||||
// full-expression. We don't use a temporary here, because temporaries are
|
||||
// treated as being immutable.
|
||||
PendingBlock storage_block(&context_);
|
||||
auto storage_id = storage_block.AddInstWithCleanup<SemIR::VarStorage>(
|
||||
SemIR::LocId(entry.pattern_id),
|
||||
{.type_id = pattern_type_id, .pattern_id = entry.pattern_id});
|
||||
// Disable broken lint that suggests a "fix" that doesn't compile.
|
||||
auto init_result = Initialize(
|
||||
context_, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id = pattern_type_id});
|
||||
CARBON_CHECK(scrutinee_id.has_value());
|
||||
break;
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
std::move(storage_id), std::move(storage_block), scrutinee_id);
|
||||
// TODO: Consider instead creating something like a `Temporary`
|
||||
// instruction that returns a reference so that constant evaluation can
|
||||
// obtain the value of the var parameter.
|
||||
AddInst<SemIR::Assign>(
|
||||
context_, SemIR::LocId(entry.pattern_id),
|
||||
{.lhs_id = init_result.storage_id, .rhs_id = init_result.init_id});
|
||||
return init_result.storage_id;
|
||||
}
|
||||
}
|
||||
// TODO: Find a more efficient way to put these insts in the global_init
|
||||
// block (or drop the distinction between the global_init block and the
|
||||
// file scope?)
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
context_.global_init().Resume();
|
||||
}
|
||||
if (scrutinee_id.has_value()) {
|
||||
auto init_id = Initialize(context_, SemIR::LocId(entry.pattern_id),
|
||||
storage_id, scrutinee_id);
|
||||
// If we created a `TemporaryStorage` to hold the var, create a
|
||||
// corresponding `Temporary` to model that its initialization is complete.
|
||||
// TODO: If the subpattern is a binding, we may want to destroy the
|
||||
// parameter variable in the callee instead of the caller so that we can
|
||||
// support destructive move from it.
|
||||
if (std::holds_alternative<CallerState*>(state)) {
|
||||
storage_id = AddInstWithCleanup<SemIR::Temporary>(
|
||||
context_, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id = context_.insts().Get(storage_id).type_id(),
|
||||
.storage_id = storage_id,
|
||||
.init_id = init_id});
|
||||
} else {
|
||||
// TODO: Consider using different instruction kinds for assignment
|
||||
// versus initialization.
|
||||
AddInst<SemIR::Assign>(context_, SemIR::LocId(entry.pattern_id),
|
||||
{.lhs_id = storage_id, .rhs_id = init_id});
|
||||
}
|
||||
}
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
context_.global_init().Suspend();
|
||||
}
|
||||
return storage_id;
|
||||
}
|
||||
|
||||
auto MatchContext::DoPostWork(State /*state*/,
|
||||
|
||||
@@ -69,22 +69,21 @@ class PendingBlock {
|
||||
|
||||
// Replace the instruction at target_id with the instructions in this block.
|
||||
// The new value for target_id should be value_id. Returns the InstId that
|
||||
// should be used to refer to the result from now on. value_id must precede
|
||||
// target_id, or be the last ID in this block, in order to preserve the
|
||||
// property that SemIR is topologically sorted.
|
||||
// should be used to refer to the result from now on. value_id must dominate
|
||||
// target_id (but see below), or refer to an instruction within this block, in
|
||||
// order to preserve the property that SemIR is topologically sorted.
|
||||
//
|
||||
// TODO: we could also allow value_id to be one of the other insts in this
|
||||
// block, but that would be costlier to enforce.
|
||||
// TODO: We don't have an implementation of a proper dominance check, so we
|
||||
// fake one up by comparing the order in which the insts were created.
|
||||
// Add a general end-of-phase dominance check and remove the one here and in
|
||||
// `InitializeExisting`.
|
||||
auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id)
|
||||
-> SemIR::InstId {
|
||||
CARBON_CHECK(target_id != value_id);
|
||||
|
||||
// TODO: consider adding an end-of-phase check that the SemIR::File is in
|
||||
// SSA form, and dropping this check and the ordering preconditions here and
|
||||
// on Initialize.
|
||||
CARBON_CHECK(value_id.index <= target_id.index ||
|
||||
(!insts_.empty() && insts_.back() == value_id),
|
||||
"Splice would break topological sorting of insts");
|
||||
CARBON_CHECK(context_->insts().GetRawIndex(value_id) <=
|
||||
context_->insts().GetRawIndex(target_id) ||
|
||||
llvm::is_contained(insts_, value_id),
|
||||
"Splice might break dominance condition");
|
||||
SemIR::LocIdAndInst value = context_->insts().GetWithLocId(value_id);
|
||||
|
||||
auto result_id = value_id;
|
||||
|
||||
@@ -191,8 +191,8 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id,
|
||||
out_param_id =
|
||||
call_params[function.call_param_ranges.return_begin().index];
|
||||
CARBON_CHECK(out_param_id.has_value());
|
||||
expr_id = Initialize(context, loc_id, out_param_id, expr_id,
|
||||
/*for_return=*/true);
|
||||
expr_id = InitializeExisting(context, loc_id, out_param_id, expr_id,
|
||||
/*for_return=*/true);
|
||||
if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id)
|
||||
.MightBeInPlace()) {
|
||||
out_param_id = SemIR::InstId::None;
|
||||
|
||||
+6
-7
@@ -286,10 +286,8 @@ fn F(Form:! Core.Form()) ->? Form;
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %.eea: ref %i32 = temporary invalid, %int_0.6a9 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc4_7.2 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.eea, %Destroy.Op.651ba6.2 [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
@@ -317,16 +315,17 @@ fn F(Form:! Core.Form()) ->? Form;
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %.loc4_7.1: ref %i32 = temporary_storage
|
||||
// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5]
|
||||
// CHECK:STDOUT: %bound_method.loc4_7.1: <bound method> = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc4_7.2: <bound method> = bound_method %int_0, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc4_7.2(%int_0) [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc4_7.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc4_7.3: ref %i32 = temporary %.loc4_7.1, %.loc4_7.2 [concrete = constants.%.eea]
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc4_7.3)
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call constants.%Destroy.Op.bound(constants.%.eea)
|
||||
// CHECK:STDOUT: %.loc4: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %x.var: ref %i32 = var @F.%.loc4_7
|
||||
// CHECK:STDOUT: assign %x.var, %.loc4
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%x.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %x.var, constants.%Destroy.Op.651ba6.2
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+5
-5
@@ -110,16 +110,16 @@ fn Test(x: i32) {
|
||||
// CHECK:STDOUT: %I.facet: %I.type = facet_value constants.%i32, (constants.%I.impl_witness) [concrete = constants.%I.facet]
|
||||
// CHECK:STDOUT: %.loc27: %I.type = converted constants.%i32, %I.facet [concrete = constants.%I.facet]
|
||||
// CHECK:STDOUT: %TestOp.specific_fn: <specific function> = specific_function %TestOp.ref, @TestOp(constants.%I.facet) [concrete = constants.%TestOp.specific_fn]
|
||||
// CHECK:STDOUT: %.loc22_25.1: ref %i32 = temporary_storage
|
||||
// CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664]
|
||||
// CHECK:STDOUT: %bound_method.loc27_10.1: <bound method> = bound_method %x.ref, %impl.elem0
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc27_10.2: <bound method> = bound_method %x.ref, %specific_fn
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc27_10.2(%x.ref)
|
||||
// CHECK:STDOUT: %.loc22_25.2: ref %i32 = temporary %.loc22_25.1, %Int.as.Copy.impl.Op.call
|
||||
// CHECK:STDOUT: %TestOp.call: init %empty_tuple.type = call %TestOp.specific_fn(%.loc22_25.2)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.loc22_25.2, constants.%Destroy.Op.651ba6.2
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc22_25.2)
|
||||
// CHECK:STDOUT: %x.var: ref %i32 = var @TestOp.%x.param_patt
|
||||
// CHECK:STDOUT: assign %x.var, %Int.as.Copy.impl.Op.call
|
||||
// CHECK:STDOUT: %TestOp.call: init %empty_tuple.type = call %TestOp.specific_fn(%x.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %x.var, constants.%Destroy.Op.651ba6.2
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+170
-16
@@ -2,7 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
|
||||
// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
|
||||
//
|
||||
@@ -145,6 +145,38 @@ fn G() {
|
||||
let (unused x: (), var unused y: (), unused z: ()) = (F(), F(), F());
|
||||
}
|
||||
|
||||
// --- var_param_from_init.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class X {}
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn TakeVar(var a: X);
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn MakeInit() -> X;
|
||||
|
||||
fn PassInitToVar() {
|
||||
//@dump-sem-ir-begin
|
||||
TakeVar(MakeInit());
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- var_param_from_int_literal.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn TakeVar(var n: i32);
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn Call() {
|
||||
//@dump-sem-ir-begin
|
||||
TakeVar(123);
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- basic.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
@@ -439,7 +471,6 @@ fn G() {
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %G.type: type = fn_type @G [concrete]
|
||||
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %.a4d: ref %empty_tuple.type = temporary invalid, %empty_tuple [concrete]
|
||||
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete]
|
||||
@@ -506,13 +537,15 @@ fn G() {
|
||||
// CHECK:STDOUT: %.loc8_9.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc8_5: %empty_tuple.type = converted %v.ref, %tuple [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc4_13.1: ref %empty_tuple.type = temporary_storage
|
||||
// CHECK:STDOUT: %y.var: ref %empty_tuple.type = var @F.%y.param_patt
|
||||
// CHECK:STDOUT: %.loc8_9.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc4_13.2: init %empty_tuple.type = converted %.loc8_9.1, %.loc8_9.2 [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc4_13.3: ref %empty_tuple.type = temporary %.loc4_13.1, %.loc4_13.2 [concrete = constants.%.a4d]
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc8_5, %.loc4_13.3)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %v.var, constants.%Destroy.Op
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var)
|
||||
// CHECK:STDOUT: %.loc4: init %empty_tuple.type = converted %.loc8_9.1, %.loc8_9.2 [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: assign %y.var, %.loc4
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc8_5, %y.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound.loc4: <bound method> = bound_method %y.var, constants.%Destroy.Op
|
||||
// CHECK:STDOUT: %Destroy.Op.call.loc4: init %empty_tuple.type = call %Destroy.Op.bound.loc4(%y.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound.loc7: <bound method> = bound_method %v.var, constants.%Destroy.Op
|
||||
// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%v.var)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -623,8 +656,8 @@ fn G() {
|
||||
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
|
||||
// CHECK:STDOUT: %.a4d: ref %empty_tuple.type = temporary invalid, %empty_tuple [concrete]
|
||||
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
|
||||
// CHECK:STDOUT: %Self: %Destroy.type = symbolic_binding Self, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.cb2: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Self) [symbolic]
|
||||
@@ -637,7 +670,6 @@ fn G() {
|
||||
// CHECK:STDOUT: %Destroy.facet.5ae: %Destroy.type = facet_value %empty_tuple.type, (%custom_witness.8d7) [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.89a: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.5ae) [concrete]
|
||||
// CHECK:STDOUT: %.518: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.89a, %Destroy.facet.5ae [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.a4d, %Destroy.Op [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
@@ -651,6 +683,8 @@ fn G() {
|
||||
// CHECK:STDOUT: import P//default
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %P.F: %F.type = import_ref P//default, F, loaded [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %y.patt: %pattern_type.cb1 = ref_binding_pattern y [concrete]
|
||||
// CHECK:STDOUT: %y.param_patt: %pattern_type.cb1 = var_param_pattern %y.patt [concrete]
|
||||
// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
|
||||
// CHECK:STDOUT: %Core.import_ref.06b: %Destroy.assoc_type = import_ref Core//prelude/parts/destroy, loc{{\d+_\d+}}, loaded [concrete = constants.%assoc0]
|
||||
// CHECK:STDOUT: %Core.import_ref.8bb: @Destroy.WithSelf.%Destroy.WithSelf.Op.type (%Destroy.WithSelf.Op.type.cb2) = import_ref Core//prelude/parts/destroy, loc{{\d+_\d+}}, loaded [symbolic = @Destroy.WithSelf.%Destroy.WithSelf.Op (constants.%Destroy.WithSelf.Op.9f1)]
|
||||
@@ -675,15 +709,15 @@ fn G() {
|
||||
// CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc7_8.2: %empty_tuple.type = converted %.loc7_8.1, %empty_tuple [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.1: ref %empty_tuple.type = temporary_storage
|
||||
// CHECK:STDOUT: %y.var: ref %empty_tuple.type = var imports.%y.param_patt
|
||||
// CHECK:STDOUT: %.loc7_12.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.2: init %empty_tuple.type = converted %.loc7_12.1, %.loc7_12.2 [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.3: ref %empty_tuple.type = temporary %.1, %.2 [concrete = constants.%.a4d]
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc7_8.2, %.3)
|
||||
// CHECK:STDOUT: %.1: init %empty_tuple.type = converted %.loc7_12.1, %.loc7_12.2 [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: assign %y.var, %.1
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc7_8.2, %y.var)
|
||||
// CHECK:STDOUT: %Op.ref: %Destroy.assoc_type = name_ref Op, imports.%Core.import_ref.06b [concrete = constants.%assoc0]
|
||||
// CHECK:STDOUT: %impl.elem0: %.518 = impl_witness_access constants.%custom_witness.8d7, element0 [concrete = constants.%Destroy.Op]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %.3, %impl.elem0 [concrete = constants.%Destroy.Op.bound]
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %bound_method(%.3) [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %y.var, %impl.elem0
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %bound_method(%y.var)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -950,3 +984,123 @@ fn G() {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %empty_tuple.type) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- var_param_from_init.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %X: type = class_type @X [concrete]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %TakeVar.type: type = fn_type @TakeVar [concrete]
|
||||
// CHECK:STDOUT: %TakeVar: %TakeVar.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %MakeInit.type: type = fn_type @MakeInit [concrete]
|
||||
// CHECK:STDOUT: %MakeInit: %MakeInit.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc7_12.2 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %TakeVar.decl: %TakeVar.type = fn_decl @TakeVar [concrete = constants.%TakeVar] {
|
||||
// CHECK:STDOUT: %a.patt: %pattern_type.05f = ref_binding_pattern a [concrete]
|
||||
// CHECK:STDOUT: %a.param_patt: %pattern_type.05f = var_param_pattern %a.patt [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %a.param: ref %X = ref_param call_param0
|
||||
// CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X]
|
||||
// CHECK:STDOUT: %a: ref %X = ref_binding a, %a.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @TakeVar(%a.param: ref %X);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @PassInitToVar() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %TakeVar.ref: %TakeVar.type = name_ref TakeVar, file.%TakeVar.decl [concrete = constants.%TakeVar]
|
||||
// CHECK:STDOUT: %MakeInit.ref: %MakeInit.type = name_ref MakeInit, file.%MakeInit.decl [concrete = constants.%MakeInit]
|
||||
// CHECK:STDOUT: %a.var: ref %X = var @TakeVar.%a.param_patt
|
||||
// CHECK:STDOUT: %MakeInit.call: init %X to %a.var = call %MakeInit.ref()
|
||||
// CHECK:STDOUT: assign %a.var, %MakeInit.call
|
||||
// CHECK:STDOUT: %TakeVar.call: init %empty_tuple.type = call %TakeVar.ref(%a.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method <unexpected>.inst{{[0-9A-F]+}}.loc7_12, constants.%Destroy.Op.651ba6.2
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(<unexpected>.inst{{[0-9A-F]+}}.loc7_12)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.Op.loc7_12.1(%self.param: ref %empty_struct_type) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.Op.loc7_12.2(%self.param: ref %X) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- var_param_from_int_literal.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete]
|
||||
// CHECK:STDOUT: %TakeVar.type: type = fn_type @TakeVar [concrete]
|
||||
// CHECK:STDOUT: %TakeVar: %TakeVar.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_123.f7f: %i32 = int_value 123 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc5_12.2 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %TakeVar.decl: %TakeVar.type = fn_decl @TakeVar [concrete = constants.%TakeVar] {
|
||||
// CHECK:STDOUT: %n.patt: %pattern_type.7ce = ref_binding_pattern n [concrete]
|
||||
// CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = var_param_pattern %n.patt [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %n.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %n: ref %i32 = ref_binding n, %n.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @TakeVar(%n.param: ref %i32);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Call() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %TakeVar.ref: %TakeVar.type = name_ref TakeVar, file.%TakeVar.decl [concrete = constants.%TakeVar]
|
||||
// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff]
|
||||
// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.1: <bound method> = bound_method %int_123, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.2: <bound method> = bound_method %int_123, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_123) [concrete = constants.%int_123.f7f]
|
||||
// CHECK:STDOUT: %.loc5: init %i32 = converted %int_123, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.f7f]
|
||||
// CHECK:STDOUT: %n.var: ref %i32 = var @TakeVar.%n.param_patt
|
||||
// CHECK:STDOUT: assign %n.var, %.loc5
|
||||
// CHECK:STDOUT: %TakeVar.call: init %empty_tuple.type = call %TakeVar.ref(%n.var)
|
||||
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %n.var, constants.%Destroy.Op.651ba6.2
|
||||
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%n.var)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.Op.loc5_12.1(%self.param: ref %i32.builtin) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.Op.loc5_12.2(%self.param: ref %i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -435,8 +435,8 @@ auto BuildThunkDefinitionForExport(Context& context,
|
||||
context.inst_blocks().Get(thunk_function.call_params_id).back();
|
||||
|
||||
SemIR::LocId loc_id(out_param_id);
|
||||
auto init_id =
|
||||
Initialize(context, loc_id, out_param_id, call_id, /*for_return=*/true);
|
||||
auto init_id = InitializeExisting(context, loc_id, out_param_id, call_id,
|
||||
/*for_return=*/true);
|
||||
AddInst(context, loc_id,
|
||||
SemIR::Assign{
|
||||
.lhs_id = out_param_id,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "toolchain/sem_ir/expr_info.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
#include "toolchain/sem_ir/inst_kind.h"
|
||||
#include "toolchain/sem_ir/type_info.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
|
||||
+5
-6
@@ -116,17 +116,16 @@ fn G() {
|
||||
// CHECK:STDOUT: ; ModuleID = 'var_form_param.carbon'
|
||||
// CHECK:STDOUT: source_filename = "var_form_param.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @int_0.6a9 = internal constant i32 0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @_CF.Main(ptr)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc3_7.1.temp = alloca i32, align 4, !dbg !7
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc3_7.1.temp), !dbg !7
|
||||
// CHECK:STDOUT: call void @_CF.Main(ptr @int_0.6a9), !dbg !8
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr @int_0.6a9), !dbg !7
|
||||
// CHECK:STDOUT: %x.var = alloca i32, align 4, !dbg !7
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %x.var), !dbg !7
|
||||
// CHECK:STDOUT: store i32 0, ptr %x.var, align 4, !dbg !7
|
||||
// CHECK:STDOUT: call void @_CF.Main(ptr %x.var), !dbg !8
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %x.var), !dbg !7
|
||||
// CHECK:STDOUT: ret void, !dbg !9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+10
-5
@@ -137,7 +137,7 @@ fn InitLet() {
|
||||
// CHECK:STDOUT: ; ModuleID = 'var_param.carbon'
|
||||
// CHECK:STDOUT: source_filename = "var_param.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @C.val = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT: @C.val.loc6 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CF.Main(ptr %_) #0 !dbg !4 {
|
||||
@@ -150,11 +150,12 @@ fn InitLet() {
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CCallF.Main() #0 !dbg !11 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc6_6.1.temp = alloca {}, align 8, !dbg !14
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc6_6.1.temp), !dbg !14
|
||||
// CHECK:STDOUT: call void @_CF.Main(ptr @C.val), !dbg !15
|
||||
// CHECK:STDOUT: %_.var = alloca {}, align 8, !dbg !14
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !14
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var, ptr align 1 @C.val.loc6, i64 0, i1 false), !dbg !14
|
||||
// CHECK:STDOUT: call void @_CF.Main(ptr %_.var), !dbg !15
|
||||
// CHECK:STDOUT: call void @_CG.Main(), !dbg !16
|
||||
// CHECK:STDOUT: call void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr @C.val), !dbg !14
|
||||
// CHECK:STDOUT: call void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr %_.var), !dbg !14
|
||||
// CHECK:STDOUT: ret void, !dbg !17
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -167,8 +168,12 @@ fn InitLet() {
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
|
||||
+35
-25
@@ -31,9 +31,8 @@ fn Call() {
|
||||
// CHECK:STDOUT: ; ModuleID = 'var_param.carbon'
|
||||
// CHECK:STDOUT: source_filename = "var_param.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @int_1.5d2 = internal constant i32 1
|
||||
// CHECK:STDOUT: @X.val = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT: @X.val.loc27_18.6 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT: @X.val.loc16 = internal constant {} zeroinitializer
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_COneVar_i32.Main(ptr %_) #0 !dbg !4 {
|
||||
@@ -68,32 +67,38 @@ fn Call() {
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CCall.Main() #0 !dbg !35 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc15_15.1.temp = alloca i32, align 4, !dbg !38
|
||||
// CHECK:STDOUT: %.loc16_13.1.temp = alloca {}, align 8, !dbg !39
|
||||
// CHECK:STDOUT: %.loc18_12.1.temp = alloca i32, align 4, !dbg !40
|
||||
// CHECK:STDOUT: %.loc18_24.1.temp = alloca {}, align 8, !dbg !41
|
||||
// CHECK:STDOUT: %.loc20_15.1.temp = alloca i32, align 4, !dbg !42
|
||||
// CHECK:STDOUT: %_.var.loc15 = alloca i32, align 4, !dbg !38
|
||||
// CHECK:STDOUT: %_.var.loc16 = alloca {}, align 8, !dbg !39
|
||||
// CHECK:STDOUT: %_.var.loc18_12 = alloca i32, align 4, !dbg !40
|
||||
// CHECK:STDOUT: %_.var.loc18_24 = alloca {}, align 8, !dbg !41
|
||||
// CHECK:STDOUT: %_.var.loc20 = alloca i32, align 4, !dbg !42
|
||||
// CHECK:STDOUT: %.loc27_18.2.temp = alloca {}, align 8, !dbg !43
|
||||
// CHECK:STDOUT: %.loc21_23.1.temp = alloca {}, align 8, !dbg !44
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc15_15.1.temp), !dbg !38
|
||||
// CHECK:STDOUT: call void @_COneVar_i32.Main(ptr @int_1.5d2), !dbg !45
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc16_13.1.temp), !dbg !39
|
||||
// CHECK:STDOUT: call void @_COneVar_X.Main(ptr @X.val), !dbg !46
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc18_12.1.temp), !dbg !40
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc18_24.1.temp), !dbg !41
|
||||
// CHECK:STDOUT: call void @_CTwoVars.Main(ptr @int_1.5d2, ptr @X.val), !dbg !47
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc20_15.1.temp), !dbg !42
|
||||
// CHECK:STDOUT: %_.var.loc21 = alloca {}, align 8, !dbg !44
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc15), !dbg !38
|
||||
// CHECK:STDOUT: store i32 1, ptr %_.var.loc15, align 4, !dbg !38
|
||||
// CHECK:STDOUT: call void @_COneVar_i32.Main(ptr %_.var.loc15), !dbg !45
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc16), !dbg !39
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var.loc16, ptr align 1 @X.val.loc16, i64 0, i1 false), !dbg !39
|
||||
// CHECK:STDOUT: call void @_COneVar_X.Main(ptr %_.var.loc16), !dbg !46
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc18_12), !dbg !40
|
||||
// CHECK:STDOUT: store i32 1, ptr %_.var.loc18_12, align 4, !dbg !40
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc18_24), !dbg !41
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var.loc18_24, ptr align 1 @X.val.loc16, i64 0, i1 false), !dbg !41
|
||||
// CHECK:STDOUT: call void @_CTwoVars.Main(ptr %_.var.loc18_12, ptr %_.var.loc18_24), !dbg !47
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc20), !dbg !42
|
||||
// CHECK:STDOUT: store i32 1, ptr %_.var.loc20, align 4, !dbg !42
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc27_18.2.temp), !dbg !43
|
||||
// CHECK:STDOUT: call void @_CVarThenLet.Main(ptr @int_1.5d2, ptr @X.val.loc27_18.6), !dbg !48
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc21_23.1.temp), !dbg !44
|
||||
// CHECK:STDOUT: call void @_CLetThenVar.Main(i32 1, ptr @X.val), !dbg !49
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr @X.val), !dbg !44
|
||||
// CHECK:STDOUT: call void @_CVarThenLet.Main(ptr %_.var.loc20, ptr @X.val.loc16), !dbg !48
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc21), !dbg !44
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var.loc21, ptr align 1 @X.val.loc16, i64 0, i1 false), !dbg !44
|
||||
// CHECK:STDOUT: call void @_CLetThenVar.Main(i32 1, ptr %_.var.loc21), !dbg !49
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr %_.var.loc21), !dbg !44
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr @X.val), !dbg !43
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr @int_1.5d2), !dbg !42
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr @X.val), !dbg !41
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr @int_1.5d2), !dbg !40
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr @X.val), !dbg !39
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr @int_1.5d2), !dbg !38
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %_.var.loc20), !dbg !42
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr %_.var.loc18_24), !dbg !41
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %_.var.loc18_12), !dbg !40
|
||||
// CHECK:STDOUT: call void @"_COp.607f187990247af9:core.Destroy.Core"(ptr %_.var.loc16), !dbg !39
|
||||
// CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %_.var.loc15), !dbg !38
|
||||
// CHECK:STDOUT: ret void, !dbg !50
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -112,13 +117,18 @@ fn Call() {
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; uselistorder directives
|
||||
// CHECK:STDOUT: uselistorder ptr @"_COp.607f187990247af9:core.Destroy.Core", { 3, 2, 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @"_COp.7e389eab4a7e5487:core.Destroy.Core", { 2, 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 6, 5, 4, 3, 2, 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 2, 1, 0 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
|
||||
@@ -55,6 +55,11 @@ auto GetFirstBindingNameFromPatternId(const File& sem_ir, InstId pattern_id)
|
||||
|
||||
// TODO: Look through struct patterns.
|
||||
|
||||
if (auto form_pattern = inst.TryAs<FormParamPattern>()) {
|
||||
// TODO: This introduces a name, but we don't model it as a binding.
|
||||
return EntityNameId::None;
|
||||
}
|
||||
|
||||
auto [name_id, entity_name_id] = GetBoundEntityName(sem_ir, inst);
|
||||
CARBON_CHECK(entity_name_id.has_value(), "Unhandled pattern inst kind {0}",
|
||||
inst);
|
||||
|
||||
Reference in New Issue
Block a user