diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index ca240881fbc7..a4dbb92c8555 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -251,6 +251,40 @@ auto Context::is_current_position_reachable() -> bool { SemIR::TerminatorKind::Terminator; } +namespace { +// A handle to a new block that may be modified, with copy-on-write semantics. +// +// The constructor is given the ID of an existing block that provides the +// initial contents of the new block. The new block is lazily allocated; if no +// modifications have been made, the `id()` function will return the original +// block ID. +// +// This is intended to avoid an unnecessary block allocation in the case where +// the new block ends up being exactly the same as the original block. +class CopyOnWriteBlock { + public: + CopyOnWriteBlock(SemIR::File& file, SemIR::NodeBlockId source_id) + : file_(file), source_id_(source_id) {} + + auto id() -> SemIR::NodeBlockId const { return id_; } + + auto Set(int i, SemIR::NodeId value) -> void { + if (file_.GetNodeBlock(id_)[i] == value) { + return; + } + if (id_ == source_id_) { + id_ = file_.AddNodeBlock(file_.GetNodeBlock(source_id_)); + } + file_.GetNodeBlock(id_)[i] = value; + } + + private: + SemIR::File& file_; + SemIR::NodeBlockId source_id_; + SemIR::NodeBlockId id_ = source_id_; +}; +} // namespace + auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, SemIR::NodeId value_id) -> SemIR::NodeId { // Implicitly convert the value to the type of the target. @@ -282,6 +316,79 @@ auto Context::Initialize(Parse::Node parse_node, SemIR::NodeId target_id, case SemIR::ExpressionCategory::Initializing: MarkInitializerFor(expr_id, target_id); return expr_id; + + case SemIR::ExpressionCategory::Mixed: + // Strip off outer stub references. + expr_id = SkipStubReferences(expr_id); + expr = semantics_ir().GetNode(expr_id); + + // TODO: Make non-recursive. + // TODO: This should be done as part of the `ImplicitAs` processing so + // that we can still initialize directly from one tuple element if + // another one needs to be converted. + switch (expr.kind()) { + case SemIR::NodeKind::TupleLiteral: { + auto elements = semantics_ir().GetNodeBlock(expr.GetAsTupleLiteral()); + CopyOnWriteBlock new_block(semantics_ir(), expr.GetAsTupleLiteral()); + bool is_in_place = + SemIR::GetInitializingRepresentation(semantics_ir(), type_id) + .kind == SemIR::InitializingRepresentation::InPlace; + for (auto [i, elem_id] : llvm::enumerate(elements)) { + // TODO: Avoid creating the integer literal. + auto int_id = AddNode(SemIR::Node::IntegerLiteral::Make( + parse_node, CanonicalizeType(SemIR::NodeId::BuiltinIntegerType), + semantics_ir().AddIntegerLiteral(llvm::APInt(32, i)))); + // TODO: We know the type already matches because we already invoked + // `ImplicitAsRequired`, but this will need to change once we stop + // doing that. + auto inner_target_type = semantics_ir().GetNode(elem_id).type_id(); + // TODO: This should be placed into the return slot, and only + // created if needed. + auto inner_target_id = AddNode(SemIR::Node::TupleIndex::Make( + parse_node, inner_target_type, target_id, int_id)); + + auto new_id = + is_in_place ? InitializeAndFinalize(parse_node, inner_target_id, + elem_id) + : Initialize(parse_node, inner_target_id, elem_id); + new_block.Set(i, new_id); + } + return AddNode(SemIR::Node::TupleInit::Make(parse_node, type_id, + expr_id, new_block.id())); + } + + case SemIR::NodeKind::StructLiteral: { + auto elements = + semantics_ir().GetNodeBlock(expr.GetAsStructLiteral()); + CopyOnWriteBlock new_block(semantics_ir(), expr.GetAsStructLiteral()); + bool is_in_place = + SemIR::GetInitializingRepresentation(semantics_ir(), type_id) + .kind == SemIR::InitializingRepresentation::InPlace; + for (auto [i, elem_id] : llvm::enumerate(elements)) { + // TODO: We know the type already matches because we already invoked + // `ImplicitAsRequired`, but this will need to change once we stop + // doing that. + auto inner_target_type = semantics_ir().GetNode(elem_id).type_id(); + // TODO: This should be placed into the return slot, and only + // created if needed. + auto inner_target_id = AddNode(SemIR::Node::StructAccess::Make( + parse_node, inner_target_type, target_id, + SemIR::MemberIndex(i))); + + auto new_id = + is_in_place ? InitializeAndFinalize(parse_node, inner_target_id, + elem_id) + : Initialize(parse_node, inner_target_id, elem_id); + new_block.Set(i, new_id); + } + return AddNode(SemIR::Node::StructInit::Make( + parse_node, type_id, expr_id, new_block.id())); + } + + default: + CARBON_FATAL() << "Unexpected kind for mixed-category expression " + << expr.kind(); + } } } @@ -308,10 +415,16 @@ auto Context::ConvertToValueExpression(SemIR::NodeId expr_id) -> SemIR::NodeId { } switch (SemIR::GetExpressionCategory(semantics_ir(), expr_id)) { - case SemIR::ExpressionCategory::NotExpression: - CARBON_FATAL() << "Converting non-expression node " - << semantics_ir().GetNode(expr_id) - << " to value expression"; + case SemIR::ExpressionCategory::NotExpression: { + // TODO: We currently encounter this for use of namespaces and functions. + // We should provide a better diagnostic for inappropriate use of + // namespace names, and allow use of functions as values. + CARBON_DIAGNOSTIC(UseOfNonExpressionAsValue, Error, + "Expression cannot be used as a value."); + emitter().Emit(semantics_ir().GetNode(expr_id).parse_node(), + UseOfNonExpressionAsValue); + return SemIR::NodeId::BuiltinError; + } case SemIR::ExpressionCategory::Initializing: // Commit to using a temporary for this initializing expression. @@ -330,144 +443,183 @@ auto Context::ConvertToValueExpression(SemIR::NodeId expr_id) -> SemIR::NodeId { case SemIR::ExpressionCategory::Value: return expr_id; + + case SemIR::ExpressionCategory::Mixed: { + // Strip off outer stub references. + expr_id = SkipStubReferences(expr_id); + SemIR::Node expr = semantics_ir().GetNode(expr_id); + + // TODO: Make non-recursive. + switch (expr.kind()) { + case SemIR::NodeKind::TupleLiteral: + case SemIR::NodeKind::StructLiteral: { + bool is_tuple = expr.kind() == SemIR::NodeKind::TupleLiteral; + auto elements_id = + is_tuple ? expr.GetAsTupleLiteral() : expr.GetAsStructLiteral(); + auto elements = semantics_ir().GetNodeBlock(elements_id); + CopyOnWriteBlock new_block(semantics_ir(), elements_id); + for (auto [i, elem_id] : llvm::enumerate(elements)) { + new_block.Set(i, ConvertToValueExpression(elem_id)); + } + return AddNode(is_tuple ? SemIR::Node::TupleValue::Make( + expr.parse_node(), expr.type_id(), + expr_id, new_block.id()) + : SemIR::Node::StructValue::Make( + expr.parse_node(), expr.type_id(), + expr_id, new_block.id())); + } + + default: + CARBON_FATAL() << "Unexpected kind for mixed-category expression " + << expr.kind(); + } + } } } -auto Context::FinalizeTemporary(SemIR::NodeId init_id, bool discarded) +// Convert the given expression to a value or reference expression of the same +// type. +auto Context::ConvertToValueOrReferenceExpression(SemIR::NodeId expr_id, + bool discarded) -> SemIR::NodeId { - // TODO: See if we can refactor this with MarkInitializerFor once recursion - // through struct and tuple values is properly handled. - auto orig_init_id = init_id; - while (true) { - SemIR::Node init = semantics_ir().GetNode(init_id); - CARBON_CHECK(SemIR::GetExpressionCategory(semantics_ir(), init_id) == - SemIR::ExpressionCategory::Initializing) - << "Can only materialize initializing expressions, found " << init; - switch (init.kind()) { - default: - CARBON_FATAL() << "Initialization from unexpected node " << init; + switch (GetExpressionCategory(semantics_ir(), expr_id)) { + case SemIR::ExpressionCategory::Value: + case SemIR::ExpressionCategory::DurableReference: + case SemIR::ExpressionCategory::EphemeralReference: + return expr_id; - case SemIR::NodeKind::StructLiteral: - case SemIR::NodeKind::TupleLiteral: - CARBON_FATAL() << init << " is not modeled as initializing yet"; + case SemIR::ExpressionCategory::Initializing: + return FinalizeTemporary(expr_id, discarded); - case SemIR::NodeKind::StubReference: { - init_id = init.GetAsStubReference(); - continue; - } - - case SemIR::NodeKind::Call: { - auto [refs_id, callee_id] = init.GetAsCall(); - if (semantics_ir().GetFunction(callee_id).return_slot_id.is_valid()) { - // The return slot should have a materialized temporary in it. - auto temporary_id = semantics_ir().GetNodeBlock(refs_id).back(); - CARBON_CHECK(semantics_ir().GetNode(temporary_id).kind() == - SemIR::NodeKind::TemporaryStorage) - << "Return slot for function call does not contain a temporary; " - << "initialized multiple times? Have " - << semantics_ir().GetNode(temporary_id); - return AddNode(SemIR::Node::Temporary::Make( - init.parse_node(), init.type_id(), temporary_id, orig_init_id)); - } - - if (discarded) { - // Don't invent a temporary that we're going to discard. - return SemIR::NodeId::Invalid; - } - - // The function has no return slot, but we want to produce a temporary - // object. Materialize one now. - // TODO: Consider using an invalid ID to mean that we immediately - // materialize and initialize a temporary, rather than two separate - // nodes. - auto temporary_id = AddNode(SemIR::Node::TemporaryStorage::Make( - init.parse_node(), init.type_id())); - return AddNode(SemIR::Node::Temporary::Make( - init.parse_node(), init.type_id(), temporary_id, init_id)); - } - - case SemIR::NodeKind::ArrayInit: { - auto [src_id, refs_id] = init.GetAsArrayInit(); - // The return slot should have a materialized temporary in it. - auto temporary_id = semantics_ir().GetNodeBlock(refs_id).back(); - CARBON_CHECK(semantics_ir().GetNode(temporary_id).kind() == - SemIR::NodeKind::TemporaryStorage) - << "Return slot for array init does not contain a temporary; " - << "initialized multiple times? Have " - << semantics_ir().GetNode(temporary_id); - return AddNode(SemIR::Node::Temporary::Make( - init.parse_node(), init.type_id(), temporary_id, orig_init_id)); - } - } + case SemIR::ExpressionCategory::Mixed: + case SemIR::ExpressionCategory::NotExpression: + return ConvertToValueExpression(expr_id); } } auto Context::MarkInitializerFor(SemIR::NodeId init_id, SemIR::NodeId target_id) -> void { - while (true) { - SemIR::Node init = semantics_ir().GetNode(init_id); - CARBON_CHECK(SemIR::GetExpressionCategory(semantics_ir(), init_id) == - SemIR::ExpressionCategory::Initializing) - << "initialization from non-initializing node " << init; - switch (init.kind()) { - default: - CARBON_FATAL() << "Initialization from unexpected node " << init; + init_id = SkipStubReferences(init_id); + SemIR::Node init = semantics_ir().GetNode(init_id); + CARBON_CHECK(SemIR::GetExpressionCategory(semantics_ir(), init_id) == + SemIR::ExpressionCategory::Initializing) + << "initialization from non-initializing node " << init; - case SemIR::NodeKind::StructLiteral: - case SemIR::NodeKind::TupleLiteral: - CARBON_FATAL() << init << " is not modeled as initializing yet"; + switch (init.kind()) { + default: + CARBON_FATAL() << "Initialization from unexpected node " << init; - case SemIR::NodeKind::StubReference: - init_id = init.GetAsStubReference(); - continue; + case SemIR::NodeKind::StructInit: + case SemIR::NodeKind::TupleInit: + case SemIR::NodeKind::InitializeFrom: + CARBON_FATAL() << init << " should already have a destination"; - case SemIR::NodeKind::Call: { - // If the callee has a return slot, point it at our target. - auto [refs_id, callee_id] = init.GetAsCall(); - if (semantics_ir().GetFunction(callee_id).return_slot_id.is_valid()) { - // Replace the return slot with our given target, and remove the - // tentatively-created temporary. - auto temporary_id = std::exchange( - semantics_ir().GetNodeBlock(refs_id).back(), target_id); - auto temporary = semantics_ir().GetNode(temporary_id); - CARBON_CHECK(temporary.kind() == SemIR::NodeKind::TemporaryStorage) - << "Return slot for function call does not contain a temporary; " - << "initialized multiple times? Have " << temporary; - semantics_ir().ReplaceNode( - temporary_id, SemIR::Node::NoOp::Make(temporary.parse_node())); - } - return; + case SemIR::NodeKind::Call: { + // If the callee has a return slot, point it at our target. + auto [refs_id, callee_id] = init.GetAsCall(); + if (semantics_ir().GetFunction(callee_id).return_slot_id.is_valid()) { + // Replace the return slot with our given target, and remove the + // tentatively-created temporary. + auto temporary_id = std::exchange( + semantics_ir().GetNodeBlock(refs_id).back(), target_id); + auto temporary = semantics_ir().GetNode(temporary_id); + CARBON_CHECK(temporary.kind() == SemIR::NodeKind::TemporaryStorage) + << "Return slot for function call does not contain a temporary; " + << "initialized multiple times? Have " << temporary; + semantics_ir().ReplaceNode( + temporary_id, SemIR::Node::NoOp::Make(temporary.parse_node())); } + return; + } - case SemIR::NodeKind::ArrayInit: { - // Rewrite the return slot as a reference to our target. We can't just - // update the index in `refs_id`, like we do for a Call, because there - // will be other references to the return slot for the individual array - // element initializers. - auto [src_id, refs_id] = init.GetAsArrayInit(); + case SemIR::NodeKind::ArrayInit: { + // Rewrite the return slot as a reference to our target. We can't just + // update the index in `refs_id`, like we do for a Call, because there + // will be other references to the return slot for the individual array + // element initializers. + auto [src_id, refs_id] = init.GetAsArrayInit(); + auto temporary_id = semantics_ir().GetNodeBlock(refs_id).back(); + CARBON_CHECK(semantics_ir().GetNode(temporary_id).kind() == + SemIR::NodeKind::TemporaryStorage) + << "Return slot for array init does not contain a temporary; " + << "initialized multiple times? Have " + << semantics_ir().GetNode(temporary_id); + semantics_ir().ReplaceNode( + temporary_id, + SemIR::Node::StubReference::Make( + init.parse_node(), semantics_ir().GetNode(target_id).type_id(), + target_id)); + return; + } + } +} + +auto Context::FinalizeTemporary(SemIR::NodeId init_id, bool discarded) + -> SemIR::NodeId { + init_id = SkipStubReferences(init_id); + SemIR::Node init = semantics_ir().GetNode(init_id); + CARBON_CHECK(SemIR::GetExpressionCategory(semantics_ir(), init_id) == + SemIR::ExpressionCategory::Initializing) + << "initialization from non-initializing node " << init; + + switch (init.kind()) { + default: + CARBON_FATAL() << "Initialization from unexpected node " << init; + + case SemIR::NodeKind::StructInit: + case SemIR::NodeKind::TupleInit: + case SemIR::NodeKind::InitializeFrom: + CARBON_FATAL() << init << " should already have a destination"; + + case SemIR::NodeKind::Call: { + auto [refs_id, callee_id] = init.GetAsCall(); + if (semantics_ir().GetFunction(callee_id).return_slot_id.is_valid()) { + // The return slot should have a materialized temporary in it. auto temporary_id = semantics_ir().GetNodeBlock(refs_id).back(); CARBON_CHECK(semantics_ir().GetNode(temporary_id).kind() == SemIR::NodeKind::TemporaryStorage) - << "Return slot for array init does not contain a temporary; " + << "Return slot for function call does not contain a temporary; " << "initialized multiple times? Have " << semantics_ir().GetNode(temporary_id); - semantics_ir().ReplaceNode( - temporary_id, - SemIR::Node::StubReference::Make( - init.parse_node(), semantics_ir().GetNode(target_id).type_id(), - target_id)); - return; + return AddNode(SemIR::Node::Temporary::Make( + init.parse_node(), init.type_id(), temporary_id, init_id)); } + + if (discarded) { + // Don't invent a temporary that we're going to discard. + return SemIR::NodeId::Invalid; + } + + // The function has no return slot, but we want to produce a temporary + // object. Materialize one now. + // TODO: Consider using an invalid ID to mean that we immediately + // materialize and initialize a temporary, rather than two separate + // nodes. + auto temporary_id = AddNode(SemIR::Node::TemporaryStorage::Make( + init.parse_node(), init.type_id())); + return AddNode(SemIR::Node::Temporary::Make( + init.parse_node(), init.type_id(), temporary_id, init_id)); + } + + case SemIR::NodeKind::ArrayInit: { + auto [src_id, refs_id] = init.GetAsArrayInit(); + // The return slot should have a materialized temporary in it. + auto temporary_id = semantics_ir().GetNodeBlock(refs_id).back(); + CARBON_CHECK(semantics_ir().GetNode(temporary_id).kind() == + SemIR::NodeKind::TemporaryStorage) + << "Return slot for array init does not contain a temporary; " + << "initialized multiple times? Have " + << semantics_ir().GetNode(temporary_id); + return AddNode(SemIR::Node::Temporary::Make( + init.parse_node(), init.type_id(), temporary_id, init_id)); } } } auto Context::HandleDiscardedExpression(SemIR::NodeId expr_id) -> void { - // If we discard an initializing expression, materialize it first. - if (SemIR::GetExpressionCategory(semantics_ir(), expr_id) == - SemIR::ExpressionCategory::Initializing) { - FinalizeTemporary(expr_id, /*discarded=*/true); - } + // If we discard an initializing expression, convert it to a value or + // reference so that it has something to initialize. + ConvertToValueOrReferenceExpression(expr_id, /*discarded=*/true); // TODO: This will eventually need to do some "do not discard" analysis. (void)expr_id; @@ -553,11 +705,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, // Skip back over StubReferences to find if we're being initialized from a // tuple literal. + value_id = context.SkipStubReferences(value_id); auto value = context.semantics_ir().GetNode(value_id); - while (value.kind() == SemIR::NodeKind::StubReference) { - value_id = value.GetAsStubReference(); - value = context.semantics_ir().GetNode(value_id); - } llvm::ArrayRef literal_elems; if (value.kind() == SemIR::NodeKind::TupleLiteral) { @@ -589,7 +738,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, // directly. Otherwise, materialize a temporary if needed and index into the // result. if (literal_elems.empty()) { - value_id = context.MaterializeIfInitializing(value_id); + value_id = context.ConvertToValueOrReferenceExpression(value_id); } // Arrays are always initialized in-place. Tentatively allocate a temporary @@ -670,15 +819,15 @@ auto Context::ImplicitAs(Parse::Node parse_node, SemIR::NodeId value_id, // A tuple of types converts to type `type`. // TODO: This should apply even for non-literal tuples. if (value.kind() == SemIR::NodeKind::TupleLiteral) { + // The conversion from tuple to `type` is `final`. auto tuple_block_id = value.GetAsTupleLiteral(); llvm::SmallVector type_ids; // If it is empty tuple type, we don't fetch anything. if (tuple_block_id != SemIR::NodeBlockId::Empty) { const auto& tuple_block = semantics_ir_->GetNodeBlock(tuple_block_id); for (auto tuple_node_id : tuple_block) { - // TODO: Eventually ExpressionAsType will insert implicit cast - // instructions. When that happens, this will need to verify the full - // tuple conversion will work before calling it. + // TODO: This call recurses back to this function. Switch to an + // iterative approach. type_ids.push_back( ExpressionAsType(value.parse_node(), tuple_node_id)); } @@ -688,6 +837,8 @@ auto Context::ImplicitAs(Parse::Node parse_node, SemIR::NodeId value_id, return semantics_ir_->GetTypeAllowBuiltinTypes(tuple_type_id); } // When converting `{}` to a type, the result is `{} as type`. + // TODO: This conversion should also be performed for a non-literal value of + // type `{}`. if (value.kind() == SemIR::NodeKind::StructLiteral && value.GetAsStructLiteral() == SemIR::NodeBlockId::Empty) { return semantics_ir_->GetType(value_type_id); diff --git a/toolchain/check/context.h b/toolchain/check/context.h index f4a9c699862b..96ce35789e77 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -105,12 +105,13 @@ class Context { // Returns whether the current position in the current block is reachable. auto is_current_position_reachable() -> bool; - // Converts the given expression to an ephemeral reference to a temporary if - // it is an initializing expression. - auto MaterializeIfInitializing(SemIR::NodeId expr_id) -> SemIR::NodeId { - if (GetExpressionCategory(semantics_ir(), expr_id) == - SemIR::ExpressionCategory::Initializing) { - return FinalizeTemporary(expr_id, /*discarded=*/false); + // Skips past any stub references to find the node that defines the value of + // the given node. + auto SkipStubReferences(SemIR::NodeId expr_id) -> SemIR::NodeId { + SemIR::Node expr = semantics_ir().GetNode(expr_id); + while (expr.kind() == SemIR::NodeKind::StubReference) { + expr_id = expr.GetAsStubReference(); + expr = semantics_ir().GetNode(expr_id); } return expr_id; } @@ -118,6 +119,12 @@ class Context { // Convert the given expression to a value expression of the same type. auto ConvertToValueExpression(SemIR::NodeId expr_id) -> SemIR::NodeId; + // Convert the given expression to a value or reference expression of the same + // type. + auto ConvertToValueOrReferenceExpression(SemIR::NodeId expr_id, + bool discarded = false) + -> SemIR::NodeId; + // Performs initialization of `target_id` from `value_id`. Returns the // possibly-converted initialization expression, which should be assigned to // the target using a suitable node for the kind of initialization. diff --git a/toolchain/check/handle_index.cpp b/toolchain/check/handle_index.cpp index cb08105703e8..9cbe4af8ce4a 100644 --- a/toolchain/check/handle_index.cpp +++ b/toolchain/check/handle_index.cpp @@ -41,7 +41,8 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { auto index_node_id = context.node_stack().PopExpression(); auto index_node = context.semantics_ir().GetNode(index_node_id); auto operand_node_id = context.node_stack().PopExpression(); - operand_node_id = context.MaterializeIfInitializing(operand_node_id); + operand_node_id = + context.ConvertToValueOrReferenceExpression(operand_node_id); auto operand_node = context.semantics_ir().GetNode(operand_node_id); auto operand_type_id = operand_node.type_id(); auto operand_type_node = context.semantics_ir().GetNode( diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index dc6516f2b8d0..9df65cd6b18e 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -14,9 +14,6 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) auto base_id = context.node_stack().PopExpression(); - // Materialize a temporary for the base expression if necessary. - base_id = context.MaterializeIfInitializing(base_id); - auto base = context.semantics_ir().GetNode(base_id); if (base.kind() == SemIR::NodeKind::Namespace) { // For a namespace, just resolve the name. @@ -27,6 +24,9 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) return true; } + // Materialize a temporary for the base expression if necessary. + base_id = context.ConvertToValueOrReferenceExpression(base_id); + auto base_type = context.semantics_ir().GetNode( context.semantics_ir().GetTypeAllowBuiltinTypes(base.type_id())); diff --git a/toolchain/check/handle_paren.cpp b/toolchain/check/handle_paren.cpp index 8ca4d403380e..27bd121ea5e1 100644 --- a/toolchain/check/handle_paren.cpp +++ b/toolchain/check/handle_paren.cpp @@ -28,29 +28,13 @@ auto HandleParenExpressionOrTupleLiteralStart(Context& context, return true; } -static auto HandleTupleLiteralElement(Context& context) -> void { - // Convert the operand to a value. - // TODO: We need to decide how tuple literals interact with expression - // categories. - auto [value_node, value_id] = - context.node_stack().PopExpressionWithParseNode(); - value_id = context.ConvertToValueExpression(value_id); - context.node_stack().Push(value_node, value_id); -} - auto HandleTupleLiteralComma(Context& context, Parse::Node /*parse_node*/) -> bool { - HandleTupleLiteralElement(context); context.ParamOrArgComma(/*for_args=*/true); return true; } auto HandleTupleLiteral(Context& context, Parse::Node parse_node) -> bool { - if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) != - Parse::NodeKind::ParenExpressionOrTupleLiteralStart) { - HandleTupleLiteralElement(context); - } - auto refs_id = context.ParamOrArgEnd( /*for_args=*/true, Parse::NodeKind::ParenExpressionOrTupleLiteralStart); diff --git a/toolchain/check/handle_struct.cpp b/toolchain/check/handle_struct.cpp index 4021e05c1f20..ddad1b61695a 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -45,11 +45,6 @@ auto HandleStructFieldValue(Context& context, Parse::Node parse_node) -> bool { context.node_stack().PopExpressionWithParseNode(); SemIR::StringId name_id = context.node_stack().Pop(); - // Convert the operand to a value. - // TODO: We need to decide how struct literals interact with expression - // categories. - value_node_id = context.ConvertToValueExpression(value_node_id); - // Store the name for the type. context.args_type_info_stack().AddNode(SemIR::Node::StructTypeField::Make( parse_node, name_id, diff --git a/toolchain/check/testdata/array/assign_return_value.carbon b/toolchain/check/testdata/array/assign_return_value.carbon index 7bc344bf529f..b456b0641c45 100644 --- a/toolchain/check/testdata/array/assign_return_value.carbon +++ b/toolchain/check/testdata/array/assign_return_value.carbon @@ -19,8 +19,9 @@ fn Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_28.1: i32 = int_literal 0 // CHECK:STDOUT: %.loc7_28.2: i32 = stub_reference %.loc7_28.1 -// CHECK:STDOUT: %.loc7_30: (i32,) = tuple_literal (%.loc7_28.2) -// CHECK:STDOUT: return %.loc7_30 +// CHECK:STDOUT: %.loc7_30.1: (i32,) = tuple_literal (%.loc7_28.2) +// CHECK:STDOUT: %.loc7_30.2: (i32,) = tuple_value %.loc7_30.1, (%.loc7_28.2) +// CHECK:STDOUT: return %.loc7_30.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { diff --git a/toolchain/check/testdata/array/assign_var.carbon b/toolchain/check/testdata/array/assign_var.carbon index 2bff509752f6..b0e6ec90eb59 100644 --- a/toolchain/check/testdata/array/assign_var.carbon +++ b/toolchain/check/testdata/array/assign_var.carbon @@ -22,7 +22,17 @@ var b: [i32; 3] = a; // CHECK:STDOUT: %.loc7_33.1: i32 = int_literal 3 // CHECK:STDOUT: %.loc7_33.2: i32 = stub_reference %.loc7_33.1 // CHECK:STDOUT: %.loc7_34: (i32, i32, i32) = tuple_literal (%.loc7_27.2, %.loc7_30.2, %.loc7_33.2) -// CHECK:STDOUT: assign %a, %.loc7_34 +// CHECK:STDOUT: %.loc7_35.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_35.2: ref i32 = tuple_index %a, %.loc7_35.1 +// CHECK:STDOUT: %.loc7_35.3: init i32 = initialize_from %.loc7_27.2 to %.loc7_35.2 +// CHECK:STDOUT: %.loc7_35.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_35.5: ref i32 = tuple_index %a, %.loc7_35.4 +// CHECK:STDOUT: %.loc7_35.6: init i32 = initialize_from %.loc7_30.2 to %.loc7_35.5 +// CHECK:STDOUT: %.loc7_35.7: i32 = int_literal 2 +// CHECK:STDOUT: %.loc7_35.8: ref i32 = tuple_index %a, %.loc7_35.7 +// CHECK:STDOUT: %.loc7_35.9: init i32 = initialize_from %.loc7_33.2 to %.loc7_35.8 +// CHECK:STDOUT: %.loc7_35.10: init (i32, i32, i32) = tuple_init %.loc7_34, (%.loc7_35.3, %.loc7_35.6, %.loc7_35.9) +// CHECK:STDOUT: assign %a, %.loc7_35.10 // CHECK:STDOUT: %.loc8_14: i32 = int_literal 3 // CHECK:STDOUT: %.loc8_15: type = array_type %.loc8_14, i32 // CHECK:STDOUT: %b: ref [i32; 3] = var "b" diff --git a/toolchain/check/testdata/array/base.carbon b/toolchain/check/testdata/array/base.carbon index f0201edc9daf..201c4085062c 100644 --- a/toolchain/check/testdata/array/base.carbon +++ b/toolchain/check/testdata/array/base.carbon @@ -60,14 +60,19 @@ var c: [(); 5] = ((), (), (), (), (),); // CHECK:STDOUT: %.loc9_38.3: ref [(); 5] = stub_reference %c // CHECK:STDOUT: %.loc9_38.4: i32 = int_literal 0 // CHECK:STDOUT: %.loc9_38.5: ref () = array_index %.loc9_38.3, %.loc9_38.4 -// CHECK:STDOUT: %.loc9_38.6: i32 = int_literal 1 -// CHECK:STDOUT: %.loc9_38.7: ref () = array_index %.loc9_38.3, %.loc9_38.6 -// CHECK:STDOUT: %.loc9_38.8: i32 = int_literal 2 -// CHECK:STDOUT: %.loc9_38.9: ref () = array_index %.loc9_38.3, %.loc9_38.8 -// CHECK:STDOUT: %.loc9_38.10: i32 = int_literal 3 +// CHECK:STDOUT: %.loc9_38.6: init () = tuple_init %.loc9_20.1, () +// CHECK:STDOUT: %.loc9_38.7: i32 = int_literal 1 +// CHECK:STDOUT: %.loc9_38.8: ref () = array_index %.loc9_38.3, %.loc9_38.7 +// CHECK:STDOUT: %.loc9_38.9: init () = tuple_init %.loc9_24.1, () +// CHECK:STDOUT: %.loc9_38.10: i32 = int_literal 2 // CHECK:STDOUT: %.loc9_38.11: ref () = array_index %.loc9_38.3, %.loc9_38.10 -// CHECK:STDOUT: %.loc9_38.12: i32 = int_literal 4 -// CHECK:STDOUT: %.loc9_38.13: ref () = array_index %.loc9_38.3, %.loc9_38.12 -// CHECK:STDOUT: %.loc9_38.14: init [(); 5] = array_init %.loc9_38.2, (%.loc9_20.2, %.loc9_24.2, %.loc9_28.2, %.loc9_32.2, %.loc9_36.2) to %.loc9_38.3 -// CHECK:STDOUT: assign %c, %.loc9_38.14 +// CHECK:STDOUT: %.loc9_38.12: init () = tuple_init %.loc9_28.1, () +// CHECK:STDOUT: %.loc9_38.13: i32 = int_literal 3 +// CHECK:STDOUT: %.loc9_38.14: ref () = array_index %.loc9_38.3, %.loc9_38.13 +// CHECK:STDOUT: %.loc9_38.15: init () = tuple_init %.loc9_32.1, () +// CHECK:STDOUT: %.loc9_38.16: i32 = int_literal 4 +// CHECK:STDOUT: %.loc9_38.17: ref () = array_index %.loc9_38.3, %.loc9_38.16 +// CHECK:STDOUT: %.loc9_38.18: init () = tuple_init %.loc9_36.1, () +// CHECK:STDOUT: %.loc9_38.19: init [(); 5] = array_init %.loc9_38.2, (%.loc9_38.6, %.loc9_38.9, %.loc9_38.12, %.loc9_38.15, %.loc9_38.18) to %.loc9_38.3 +// CHECK:STDOUT: assign %c, %.loc9_38.19 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon index 042b7caf7bef..1d5242c1f0ae 100644 --- a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon @@ -20,6 +20,8 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 2, +// CHECK:STDOUT: 0, +// CHECK:STDOUT: 1, // CHECK:STDOUT: ] // CHECK:STDOUT: real_literals: [ // CHECK:STDOUT: {mantissa: 34, exponent: -1, is_decimal: 1}, @@ -60,7 +62,14 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type2}, // CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type2}, // CHECK:STDOUT: {kind: TupleLiteral, arg0: block5, type: type3}, -// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: TupleIndex, arg0: node+6, arg1: node+14, type: type0}, +// CHECK:STDOUT: {kind: InitializeFrom, arg0: node+10, arg1: node+15, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, +// CHECK:STDOUT: {kind: TupleIndex, arg0: node+6, arg1: node+17, type: type2}, +// CHECK:STDOUT: {kind: InitializeFrom, arg0: node+12, arg1: node+18, type: type2}, +// CHECK:STDOUT: {kind: TupleInit, arg0: node+13, arg1: block6, type: type3}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+20}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -89,12 +98,23 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: node+12, // CHECK:STDOUT: node+13, // CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: node+21, // CHECK:STDOUT: ], // CHECK:STDOUT: [ // CHECK:STDOUT: node+10, // CHECK:STDOUT: node+12, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+7, // CHECK:STDOUT: ], // CHECK:STDOUT: ] @@ -111,5 +131,12 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: %.loc12_18.1: f64 = real_literal 34e-1 // CHECK:STDOUT: %.loc12_18.2: f64 = stub_reference %.loc12_18.1 // CHECK:STDOUT: %.loc12_21: (i32, f64) = tuple_literal (%.loc12_13.2, %.loc12_18.2) -// CHECK:STDOUT: return %.loc12_21 +// CHECK:STDOUT: %.loc12_22.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc12_22.2: ref i32 = tuple_index %return, %.loc12_22.1 +// CHECK:STDOUT: %.loc12_22.3: init i32 = initialize_from %.loc12_13.2 to %.loc12_22.2 +// CHECK:STDOUT: %.loc12_22.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc12_22.5: ref f64 = tuple_index %return, %.loc12_22.4 +// CHECK:STDOUT: %.loc12_22.6: init f64 = initialize_from %.loc12_18.2 to %.loc12_22.5 +// CHECK:STDOUT: %.loc12_22.7: init (i32, f64) = tuple_init %.loc12_21, (%.loc12_22.3, %.loc12_22.6) +// CHECK:STDOUT: return %.loc12_22.7 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/raw_ir.carbon b/toolchain/check/testdata/basics/raw_ir.carbon index 51f25ea00966..70395576ad8c 100644 --- a/toolchain/check/testdata/basics/raw_ir.carbon +++ b/toolchain/check/testdata/basics/raw_ir.carbon @@ -20,6 +20,8 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 2, +// CHECK:STDOUT: 0, +// CHECK:STDOUT: 1, // CHECK:STDOUT: ] // CHECK:STDOUT: real_literals: [ // CHECK:STDOUT: {mantissa: 34, exponent: -1, is_decimal: 1}, @@ -60,7 +62,14 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type2}, // CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type2}, // CHECK:STDOUT: {kind: TupleLiteral, arg0: block5, type: type3}, -// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: TupleIndex, arg0: node+6, arg1: node+14, type: type0}, +// CHECK:STDOUT: {kind: InitializeFrom, arg0: node+10, arg1: node+15, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, +// CHECK:STDOUT: {kind: TupleIndex, arg0: node+6, arg1: node+17, type: type2}, +// CHECK:STDOUT: {kind: InitializeFrom, arg0: node+12, arg1: node+18, type: type2}, +// CHECK:STDOUT: {kind: TupleInit, arg0: node+13, arg1: block6, type: type3}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+20}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -89,12 +98,23 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: node+12, // CHECK:STDOUT: node+13, // CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: node+21, // CHECK:STDOUT: ], // CHECK:STDOUT: [ // CHECK:STDOUT: node+10, // CHECK:STDOUT: node+12, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+7, // CHECK:STDOUT: ], // CHECK:STDOUT: ] diff --git a/toolchain/check/testdata/basics/textual_ir.carbon b/toolchain/check/testdata/basics/textual_ir.carbon index c44a2649f25d..d0b061f406e5 100644 --- a/toolchain/check/testdata/basics/textual_ir.carbon +++ b/toolchain/check/testdata/basics/textual_ir.carbon @@ -24,5 +24,12 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: %.loc12_18.1: f64 = real_literal 34e-1 // CHECK:STDOUT: %.loc12_18.2: f64 = stub_reference %.loc12_18.1 // CHECK:STDOUT: %.loc12_21: (i32, f64) = tuple_literal (%.loc12_13.2, %.loc12_18.2) -// CHECK:STDOUT: return %.loc12_21 +// CHECK:STDOUT: %.loc12_22.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc12_22.2: ref i32 = tuple_index %return, %.loc12_22.1 +// CHECK:STDOUT: %.loc12_22.3: init i32 = initialize_from %.loc12_13.2 to %.loc12_22.2 +// CHECK:STDOUT: %.loc12_22.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc12_22.5: ref f64 = tuple_index %return, %.loc12_22.4 +// CHECK:STDOUT: %.loc12_22.6: init f64 = initialize_from %.loc12_18.2 to %.loc12_22.5 +// CHECK:STDOUT: %.loc12_22.7: init (i32, f64) = tuple_init %.loc12_21, (%.loc12_22.3, %.loc12_22.6) +// CHECK:STDOUT: return %.loc12_22.7 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/function/call/empty_struct.carbon b/toolchain/check/testdata/function/call/empty_struct.carbon index f002165a9084..952b1e3daee8 100644 --- a/toolchain/check/testdata/function/call/empty_struct.carbon +++ b/toolchain/check/testdata/function/call/empty_struct.carbon @@ -26,6 +26,7 @@ fn Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc12_9.1: {} = struct_literal () // CHECK:STDOUT: %.loc12_9.2: {} = stub_reference %.loc12_9.1 -// CHECK:STDOUT: %.loc12_7: init {} = call @Echo(%.loc12_9.2) +// CHECK:STDOUT: %.loc12_9.3: {} = struct_value %.loc12_9.1, () +// CHECK:STDOUT: %.loc12_7: init {} = call @Echo(%.loc12_9.3) // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/function/call/empty_tuple.carbon b/toolchain/check/testdata/function/call/empty_tuple.carbon index 63430585f45d..30ded7ae14f9 100644 --- a/toolchain/check/testdata/function/call/empty_tuple.carbon +++ b/toolchain/check/testdata/function/call/empty_tuple.carbon @@ -26,6 +26,7 @@ fn Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc12_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc12_9.2: () = stub_reference %.loc12_9.1 -// CHECK:STDOUT: %.loc12_7: init () = call @Echo(%.loc12_9.2) +// CHECK:STDOUT: %.loc12_9.3: () = tuple_value %.loc12_9.1, () +// CHECK:STDOUT: %.loc12_7: init () = call @Echo(%.loc12_9.3) // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_invalid_base.carbon b/toolchain/check/testdata/index/fail_invalid_base.carbon new file mode 100644 index 000000000000..4f50af8037f5 --- /dev/null +++ b/toolchain/check/testdata/index/fail_invalid_base.carbon @@ -0,0 +1,58 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// TODO: The errors here are in bad locations. We should build a SemIR +// representation for a reference to a name so that we can track the location +// properly. + +// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+3]]:12: Expression cannot be used as a value. +// CHECK:STDERR: namespace N; +// CHECK:STDERR: ^ +namespace N; +var a: i32 = N[0]; + +// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+3]]:1: Expression cannot be used as a value. +// CHECK:STDERR: fn F(); +// CHECK:STDERR: ^ +fn F(); +var b: i32 = F[1]; + +// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+3]]:32: `{.a: i32, .b: i32}` does not support indexing. +// CHECK:STDERR: var c: i32 = {.a = 1, .b = 2}[0]; +// CHECK:STDERR: ^ +var c: i32 = {.a = 1, .b = 2}[0]; + +// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+3]]:34: `type` does not support indexing. +// CHECK:STDERR: var d: i32 = {.a: i32, .b: i32}[0]; +// CHECK:STDERR: ^ +var d: i32 = {.a: i32, .b: i32}[0]; + +// CHECK:STDOUT: file "fail_invalid_base.carbon" { +// CHECK:STDOUT: %.loc14 = namespace {} +// CHECK:STDOUT: %a: ref i32 = var "a" +// CHECK:STDOUT: %.loc15: i32 = int_literal 0 +// CHECK:STDOUT: assign %a, +// CHECK:STDOUT: %.loc20 = fn_decl @F +// CHECK:STDOUT: %b: ref i32 = var "b" +// CHECK:STDOUT: %.loc21: i32 = int_literal 1 +// CHECK:STDOUT: assign %b, +// CHECK:STDOUT: %c: ref i32 = var "c" +// CHECK:STDOUT: %.loc26_20: i32 = int_literal 1 +// CHECK:STDOUT: %.loc26_18: i32 = stub_reference %.loc26_20 +// CHECK:STDOUT: %.loc26_28: i32 = int_literal 2 +// CHECK:STDOUT: %.loc26_26: i32 = stub_reference %.loc26_28 +// CHECK:STDOUT: %.loc26_29.1: type = struct_type {.a: i32, .b: i32} +// CHECK:STDOUT: %.loc26_29.2: {.a: i32, .b: i32} = struct_literal (%.loc26_18, %.loc26_26) +// CHECK:STDOUT: %.loc26_31: i32 = int_literal 0 +// CHECK:STDOUT: %.loc26_29.3: {.a: i32, .b: i32} = struct_value %.loc26_29.2, (%.loc26_18, %.loc26_26) +// CHECK:STDOUT: assign %c, +// CHECK:STDOUT: %d: ref i32 = var "d" +// CHECK:STDOUT: %.loc31_31: type = struct_type {.a: i32, .b: i32} +// CHECK:STDOUT: %.loc31_33: i32 = int_literal 0 +// CHECK:STDOUT: assign %d, +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); diff --git a/toolchain/check/testdata/index/fail_non_deterministic_type.carbon b/toolchain/check/testdata/index/fail_non_deterministic_type.carbon index bffe4428f79b..32964d7a22fd 100644 --- a/toolchain/check/testdata/index/fail_non_deterministic_type.carbon +++ b/toolchain/check/testdata/index/fail_non_deterministic_type.carbon @@ -23,7 +23,14 @@ var c: i32 = a[b]; // CHECK:STDOUT: %.loc7_25.1: i32 = int_literal 3 // CHECK:STDOUT: %.loc7_25.2: i32 = stub_reference %.loc7_25.1 // CHECK:STDOUT: %.loc7_26: (i32, i32) = tuple_literal (%.loc7_22.2, %.loc7_25.2) -// CHECK:STDOUT: assign %a, %.loc7_26 +// CHECK:STDOUT: %.loc7_27.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_27.2: ref i32 = tuple_index %a, %.loc7_27.1 +// CHECK:STDOUT: %.loc7_27.3: init i32 = initialize_from %.loc7_22.2 to %.loc7_27.2 +// CHECK:STDOUT: %.loc7_27.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_27.5: ref i32 = tuple_index %a, %.loc7_27.4 +// CHECK:STDOUT: %.loc7_27.6: init i32 = initialize_from %.loc7_25.2 to %.loc7_27.5 +// CHECK:STDOUT: %.loc7_27.7: init (i32, i32) = tuple_init %.loc7_26, (%.loc7_27.3, %.loc7_27.6) +// CHECK:STDOUT: assign %a, %.loc7_27.7 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc8: i32 = int_literal 0 // CHECK:STDOUT: assign %b, %.loc8 diff --git a/toolchain/check/testdata/index/fail_tuple_large_index.carbon b/toolchain/check/testdata/index/fail_tuple_large_index.carbon index fe5281ba1204..588aa0f7ef8f 100644 --- a/toolchain/check/testdata/index/fail_tuple_large_index.carbon +++ b/toolchain/check/testdata/index/fail_tuple_large_index.carbon @@ -20,7 +20,10 @@ var c: i32 = b[0xFFFFFFFFFFFFFFFFF]; // CHECK:STDOUT: %.loc7_18.1: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_18.2: i32 = stub_reference %.loc7_18.1 // CHECK:STDOUT: %.loc7_21: (i32,) = tuple_literal (%.loc7_18.2) -// CHECK:STDOUT: assign %a, %.loc7_21 +// CHECK:STDOUT: %.loc7_22.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_22.2: ref i32 = tuple_index %a, %.loc7_22.1 +// CHECK:STDOUT: %.loc7_22.3: init (i32,) = tuple_init %.loc7_21, (%.loc7_18.2) +// CHECK:STDOUT: assign %a, %.loc7_22.3 // CHECK:STDOUT: %.loc8_9: type = stub_reference i32 // CHECK:STDOUT: %.loc8_13: (type,) = tuple_literal (%.loc8_9) // CHECK:STDOUT: %b: ref (i32,) = var "b" diff --git a/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon b/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon index da3aab695ccc..00e10d68ca93 100644 --- a/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon +++ b/toolchain/check/testdata/index/fail_tuple_non_int_indexing.carbon @@ -22,7 +22,14 @@ var b: i32 = a[2.6]; // CHECK:STDOUT: %.loc7_26.1: i32 = int_literal 6 // CHECK:STDOUT: %.loc7_26.2: i32 = stub_reference %.loc7_26.1 // CHECK:STDOUT: %.loc7_27: (i32, i32) = tuple_literal (%.loc7_22.2, %.loc7_26.2) -// CHECK:STDOUT: assign %a, %.loc7_27 +// CHECK:STDOUT: %.loc7_28.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_28.2: ref i32 = tuple_index %a, %.loc7_28.1 +// CHECK:STDOUT: %.loc7_28.3: init i32 = initialize_from %.loc7_22.2 to %.loc7_28.2 +// CHECK:STDOUT: %.loc7_28.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_28.5: ref i32 = tuple_index %a, %.loc7_28.4 +// CHECK:STDOUT: %.loc7_28.6: init i32 = initialize_from %.loc7_26.2 to %.loc7_28.5 +// CHECK:STDOUT: %.loc7_28.7: init (i32, i32) = tuple_init %.loc7_27, (%.loc7_28.3, %.loc7_28.6) +// CHECK:STDOUT: assign %a, %.loc7_28.7 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc11_16: f64 = real_literal 26e-1 // CHECK:STDOUT: %.loc11_19: ref = tuple_index %a, diff --git a/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon b/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon index c4e510f5cb92..43fdda5ca6f3 100644 --- a/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon +++ b/toolchain/check/testdata/index/fail_tuple_out_of_bound_access.carbon @@ -22,7 +22,14 @@ var b: i32 = a[2]; // CHECK:STDOUT: %.loc7_26.1: i32 = int_literal 6 // CHECK:STDOUT: %.loc7_26.2: i32 = stub_reference %.loc7_26.1 // CHECK:STDOUT: %.loc7_27: (i32, i32) = tuple_literal (%.loc7_22.2, %.loc7_26.2) -// CHECK:STDOUT: assign %a, %.loc7_27 +// CHECK:STDOUT: %.loc7_28.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_28.2: ref i32 = tuple_index %a, %.loc7_28.1 +// CHECK:STDOUT: %.loc7_28.3: init i32 = initialize_from %.loc7_22.2 to %.loc7_28.2 +// CHECK:STDOUT: %.loc7_28.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_28.5: ref i32 = tuple_index %a, %.loc7_28.4 +// CHECK:STDOUT: %.loc7_28.6: init i32 = initialize_from %.loc7_26.2 to %.loc7_28.5 +// CHECK:STDOUT: %.loc7_28.7: init (i32, i32) = tuple_init %.loc7_27, (%.loc7_28.3, %.loc7_28.6) +// CHECK:STDOUT: assign %a, %.loc7_28.7 // CHECK:STDOUT: %b: ref i32 = var "b" // CHECK:STDOUT: %.loc11_16: i32 = int_literal 2 // CHECK:STDOUT: %.loc11_17: ref = tuple_index %a, diff --git a/toolchain/check/testdata/index/tuple_element_access.carbon b/toolchain/check/testdata/index/tuple_element_access.carbon index b55a91276847..f49a7a348220 100644 --- a/toolchain/check/testdata/index/tuple_element_access.carbon +++ b/toolchain/check/testdata/index/tuple_element_access.carbon @@ -17,7 +17,10 @@ var c: i32 = b[0]; // CHECK:STDOUT: %.loc7_18.1: i32 = int_literal 12 // CHECK:STDOUT: %.loc7_18.2: i32 = stub_reference %.loc7_18.1 // CHECK:STDOUT: %.loc7_21: (i32,) = tuple_literal (%.loc7_18.2) -// CHECK:STDOUT: assign %a, %.loc7_21 +// CHECK:STDOUT: %.loc7_22.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_22.2: ref i32 = tuple_index %a, %.loc7_22.1 +// CHECK:STDOUT: %.loc7_22.3: init (i32,) = tuple_init %.loc7_21, (%.loc7_18.2) +// CHECK:STDOUT: assign %a, %.loc7_22.3 // CHECK:STDOUT: %.loc8_9: type = stub_reference i32 // CHECK:STDOUT: %.loc8_13: (type,) = tuple_literal (%.loc8_9) // CHECK:STDOUT: %b: ref (i32,) = var "b" diff --git a/toolchain/check/testdata/index/tuple_return_value_access.carbon b/toolchain/check/testdata/index/tuple_return_value_access.carbon index 8032d354a120..23fbbd71bb65 100644 --- a/toolchain/check/testdata/index/tuple_return_value_access.carbon +++ b/toolchain/check/testdata/index/tuple_return_value_access.carbon @@ -19,8 +19,9 @@ fn Run() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_28.1: i32 = int_literal 0 // CHECK:STDOUT: %.loc7_28.2: i32 = stub_reference %.loc7_28.1 -// CHECK:STDOUT: %.loc7_30: (i32,) = tuple_literal (%.loc7_28.2) -// CHECK:STDOUT: return %.loc7_30 +// CHECK:STDOUT: %.loc7_30.1: (i32,) = tuple_literal (%.loc7_28.2) +// CHECK:STDOUT: %.loc7_30.2: (i32,) = tuple_value %.loc7_30.1, (%.loc7_28.2) +// CHECK:STDOUT: return %.loc7_30.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() -> i32 { diff --git a/toolchain/check/testdata/operators/assignment.carbon b/toolchain/check/testdata/operators/assignment.carbon index ee4f6408ccdb..7b7eed67ceee 100644 --- a/toolchain/check/testdata/operators/assignment.carbon +++ b/toolchain/check/testdata/operators/assignment.carbon @@ -44,7 +44,14 @@ fn Main() { // CHECK:STDOUT: %.loc11_27.1: i32 = int_literal 2 // CHECK:STDOUT: %.loc11_27.2: i32 = stub_reference %.loc11_27.1 // CHECK:STDOUT: %.loc11_28: (i32, i32) = tuple_literal (%.loc11_24.2, %.loc11_27.2) -// CHECK:STDOUT: assign %b, %.loc11_28 +// CHECK:STDOUT: %.loc11_29.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc11_29.2: ref i32 = tuple_index %b, %.loc11_29.1 +// CHECK:STDOUT: %.loc11_29.3: init i32 = initialize_from %.loc11_24.2 to %.loc11_29.2 +// CHECK:STDOUT: %.loc11_29.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc11_29.5: ref i32 = tuple_index %b, %.loc11_29.4 +// CHECK:STDOUT: %.loc11_29.6: init i32 = initialize_from %.loc11_27.2 to %.loc11_29.5 +// CHECK:STDOUT: %.loc11_29.7: init (i32, i32) = tuple_init %.loc11_28, (%.loc11_29.3, %.loc11_29.6) +// CHECK:STDOUT: assign %b, %.loc11_29.7 // CHECK:STDOUT: %.loc12_5: i32 = int_literal 0 // CHECK:STDOUT: %.loc12_6: ref i32 = tuple_index %b, %.loc12_5 // CHECK:STDOUT: %.loc12_10: i32 = int_literal 3 @@ -60,7 +67,12 @@ fn Main() { // CHECK:STDOUT: %.loc15_45: i32 = int_literal 2 // CHECK:STDOUT: %.loc15_43: i32 = stub_reference %.loc15_45 // CHECK:STDOUT: %.loc15_46: {.a: i32, .b: i32} = struct_literal (%.loc15_35, %.loc15_43) -// CHECK:STDOUT: assign %c, %.loc15_46 +// CHECK:STDOUT: %.loc15_47.1: ref i32 = struct_access %c, member0 +// CHECK:STDOUT: %.loc15_47.2: init i32 = initialize_from %.loc15_35 to %.loc15_47.1 +// CHECK:STDOUT: %.loc15_47.3: ref i32 = struct_access %c, member1 +// CHECK:STDOUT: %.loc15_47.4: init i32 = initialize_from %.loc15_43 to %.loc15_47.3 +// CHECK:STDOUT: %.loc15_47.5: init {.a: i32, .b: i32} = struct_init %.loc15_46, (%.loc15_47.2, %.loc15_47.4) +// CHECK:STDOUT: assign %c, %.loc15_47.5 // CHECK:STDOUT: %.loc16_4: ref i32 = struct_access %c, member0 // CHECK:STDOUT: %.loc16_9: i32 = int_literal 3 // CHECK:STDOUT: assign %.loc16_4, %.loc16_9 diff --git a/toolchain/check/testdata/operators/fail_assigment_to_non_assignable.carbon b/toolchain/check/testdata/operators/fail_assigment_to_non_assignable.carbon index 542085c7918b..9ee48a350a02 100644 --- a/toolchain/check/testdata/operators/fail_assigment_to_non_assignable.carbon +++ b/toolchain/check/testdata/operators/fail_assigment_to_non_assignable.carbon @@ -71,21 +71,37 @@ fn Main() { // CHECK:STDOUT: %.loc21_16.1: i32 = int_literal 4 // CHECK:STDOUT: %.loc21_16.2: i32 = stub_reference %.loc21_16.1 // CHECK:STDOUT: %.loc21_17: (i32, i32) = tuple_literal (%.loc21_13.2, %.loc21_16.2) -// CHECK:STDOUT: assign %.loc21_8.2, %.loc21_17 +// CHECK:STDOUT: %.loc21_10.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc21_10.2: i32 = tuple_index %.loc21_8.2, %.loc21_10.1 +// CHECK:STDOUT: %.loc21_10.3: init i32 = initialize_from %.loc21_13.2 to %.loc21_10.2 +// CHECK:STDOUT: %.loc21_10.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc21_10.5: i32 = tuple_index %.loc21_8.2, %.loc21_10.4 +// CHECK:STDOUT: %.loc21_10.6: init i32 = initialize_from %.loc21_16.2 to %.loc21_10.5 +// CHECK:STDOUT: %.loc21_10.7: init (i32, i32) = tuple_init %.loc21_17, (%.loc21_10.3, %.loc21_10.6) +// CHECK:STDOUT: assign %.loc21_8.2, %.loc21_10.7 +// CHECK:STDOUT: %.loc21_8.3: (i32, i32) = tuple_value %.loc21_8.2, (%.loc21_4.2, %.loc21_7.2) // CHECK:STDOUT: %n: ref i32 = var "n" -// CHECK:STDOUT: %.loc22_16: i32 = int_literal 0 -// CHECK:STDOUT: assign %n, %.loc22_16 -// CHECK:STDOUT: %.loc22_7.1: i32 = bind_value %n -// CHECK:STDOUT: %.loc26_4: i32 = stub_reference %.loc22_7.1 -// CHECK:STDOUT: %.loc22_7.2: i32 = bind_value %n -// CHECK:STDOUT: %.loc26_7: i32 = stub_reference %.loc22_7.2 -// CHECK:STDOUT: %.loc26_8: (i32, i32) = tuple_literal (%.loc26_4, %.loc26_7) +// CHECK:STDOUT: %.loc22: i32 = int_literal 0 +// CHECK:STDOUT: assign %n, %.loc22 +// CHECK:STDOUT: %.loc26_4.1: ref i32 = stub_reference %n +// CHECK:STDOUT: %.loc26_7.1: ref i32 = stub_reference %n +// CHECK:STDOUT: %.loc26_8.1: (i32, i32) = tuple_literal (%.loc26_4.1, %.loc26_7.1) // CHECK:STDOUT: %.loc26_13.1: i32 = int_literal 1 // CHECK:STDOUT: %.loc26_13.2: i32 = stub_reference %.loc26_13.1 // CHECK:STDOUT: %.loc26_16.1: i32 = int_literal 2 // CHECK:STDOUT: %.loc26_16.2: i32 = stub_reference %.loc26_16.1 // CHECK:STDOUT: %.loc26_17: (i32, i32) = tuple_literal (%.loc26_13.2, %.loc26_16.2) -// CHECK:STDOUT: assign %.loc26_8, %.loc26_17 +// CHECK:STDOUT: %.loc26_10.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc26_10.2: i32 = tuple_index %.loc26_8.1, %.loc26_10.1 +// CHECK:STDOUT: %.loc26_10.3: init i32 = initialize_from %.loc26_13.2 to %.loc26_10.2 +// CHECK:STDOUT: %.loc26_10.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc26_10.5: i32 = tuple_index %.loc26_8.1, %.loc26_10.4 +// CHECK:STDOUT: %.loc26_10.6: init i32 = initialize_from %.loc26_16.2 to %.loc26_10.5 +// CHECK:STDOUT: %.loc26_10.7: init (i32, i32) = tuple_init %.loc26_17, (%.loc26_10.3, %.loc26_10.6) +// CHECK:STDOUT: assign %.loc26_8.1, %.loc26_10.7 +// CHECK:STDOUT: %.loc26_4.2: i32 = bind_value %.loc26_4.1 +// CHECK:STDOUT: %.loc26_7.2: i32 = bind_value %.loc26_7.1 +// CHECK:STDOUT: %.loc26_8.2: (i32, i32) = tuple_value %.loc26_8.1, (%.loc26_4.2, %.loc26_7.2) // CHECK:STDOUT: %.loc30: type = ptr_type i32 // CHECK:STDOUT: assign i32, %.loc30 // CHECK:STDOUT: %.loc34_9: i32 = int_literal 1 @@ -99,7 +115,13 @@ fn Main() { // CHECK:STDOUT: %.loc34_36: i32 = int_literal 4 // CHECK:STDOUT: %.loc34_34: i32 = stub_reference %.loc34_36 // CHECK:STDOUT: %.loc34_37: {.x: i32, .y: i32} = struct_literal (%.loc34_26, %.loc34_34) -// CHECK:STDOUT: assign %.loc34_18.2, %.loc34_37 +// CHECK:STDOUT: %.loc34_20.1: i32 = struct_access %.loc34_18.2, member0 +// CHECK:STDOUT: %.loc34_20.2: init i32 = initialize_from %.loc34_26 to %.loc34_20.1 +// CHECK:STDOUT: %.loc34_20.3: i32 = struct_access %.loc34_18.2, member1 +// CHECK:STDOUT: %.loc34_20.4: init i32 = initialize_from %.loc34_34 to %.loc34_20.3 +// CHECK:STDOUT: %.loc34_20.5: init {.x: i32, .y: i32} = struct_init %.loc34_37, (%.loc34_20.2, %.loc34_20.4) +// CHECK:STDOUT: assign %.loc34_18.2, %.loc34_20.5 +// CHECK:STDOUT: %.loc34_18.3: {.x: i32, .y: i32} = struct_value %.loc34_18.2, (%.loc34_7, %.loc34_15) // CHECK:STDOUT: %.loc38_7: bool = bool_literal true // CHECK:STDOUT: if %.loc38_7 br !if.expr.then.loc38 else br !if.expr.else.loc38 // CHECK:STDOUT: diff --git a/toolchain/check/testdata/pointer/address_of_lvalue.carbon b/toolchain/check/testdata/pointer/address_of_lvalue.carbon index 89609a903c57..a139fef27ace 100644 --- a/toolchain/check/testdata/pointer/address_of_lvalue.carbon +++ b/toolchain/check/testdata/pointer/address_of_lvalue.carbon @@ -29,7 +29,12 @@ fn F() { // CHECK:STDOUT: %.loc8_45: i32 = int_literal 2 // CHECK:STDOUT: %.loc8_43: i32 = stub_reference %.loc8_45 // CHECK:STDOUT: %.loc8_46: {.a: i32, .b: i32} = struct_literal (%.loc8_35, %.loc8_43) -// CHECK:STDOUT: assign %s, %.loc8_46 +// CHECK:STDOUT: %.loc8_47.1: ref i32 = struct_access %s, member0 +// CHECK:STDOUT: %.loc8_47.2: init i32 = initialize_from %.loc8_35 to %.loc8_47.1 +// CHECK:STDOUT: %.loc8_47.3: ref i32 = struct_access %s, member1 +// CHECK:STDOUT: %.loc8_47.4: init i32 = initialize_from %.loc8_43 to %.loc8_47.3 +// CHECK:STDOUT: %.loc8_47.5: init {.a: i32, .b: i32} = struct_init %.loc8_46, (%.loc8_47.2, %.loc8_47.4) +// CHECK:STDOUT: assign %s, %.loc8_47.5 // CHECK:STDOUT: %.loc10_27: type = struct_type {.a: i32, .b: i32} // CHECK:STDOUT: %.loc10_28: type = ptr_type {.a: i32, .b: i32} // CHECK:STDOUT: %p: ref {.a: i32, .b: i32}* = var "p" @@ -56,7 +61,14 @@ fn F() { // CHECK:STDOUT: %.loc14_27.1: i32 = int_literal 2 // CHECK:STDOUT: %.loc14_27.2: i32 = stub_reference %.loc14_27.1 // CHECK:STDOUT: %.loc14_28: (i32, i32) = tuple_literal (%.loc14_24.2, %.loc14_27.2) -// CHECK:STDOUT: assign %t, %.loc14_28 +// CHECK:STDOUT: %.loc14_29.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc14_29.2: ref i32 = tuple_index %t, %.loc14_29.1 +// CHECK:STDOUT: %.loc14_29.3: init i32 = initialize_from %.loc14_24.2 to %.loc14_29.2 +// CHECK:STDOUT: %.loc14_29.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc14_29.5: ref i32 = tuple_index %t, %.loc14_29.4 +// CHECK:STDOUT: %.loc14_29.6: init i32 = initialize_from %.loc14_27.2 to %.loc14_29.5 +// CHECK:STDOUT: %.loc14_29.7: init (i32, i32) = tuple_init %.loc14_28, (%.loc14_29.3, %.loc14_29.6) +// CHECK:STDOUT: assign %t, %.loc14_29.7 // CHECK:STDOUT: %.loc15_14: type = ptr_type i32 // CHECK:STDOUT: %t0: ref i32* = var "t0" // CHECK:STDOUT: %.loc15_21: i32 = int_literal 0 diff --git a/toolchain/check/testdata/pointer/fail_address_of_value.carbon b/toolchain/check/testdata/pointer/fail_address_of_value.carbon index 44f401d13003..e26b08906b32 100644 --- a/toolchain/check/testdata/pointer/fail_address_of_value.carbon +++ b/toolchain/check/testdata/pointer/fail_address_of_value.carbon @@ -167,9 +167,10 @@ fn AddressOfParameter(param: i32) { // CHECK:STDOUT: %.loc75_6.2: i32 = stub_reference %.loc75_6.1 // CHECK:STDOUT: %.loc75_9.1: i32 = int_literal 2 // CHECK:STDOUT: %.loc75_9.2: i32 = stub_reference %.loc75_9.1 -// CHECK:STDOUT: %.loc75_10: (i32, i32) = tuple_literal (%.loc75_6.2, %.loc75_9.2) +// CHECK:STDOUT: %.loc75_10.1: (i32, i32) = tuple_literal (%.loc75_6.2, %.loc75_9.2) // CHECK:STDOUT: %.loc75_12: i32 = int_literal 0 -// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10, %.loc75_12 +// CHECK:STDOUT: %.loc75_10.2: (i32, i32) = tuple_value %.loc75_10.1, (%.loc75_6.2, %.loc75_9.2) +// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10.2, %.loc75_12 // CHECK:STDOUT: %.loc75_3: i32* = address_of %.loc75_13 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/fail_dereference_not_pointer.carbon b/toolchain/check/testdata/pointer/fail_dereference_not_pointer.carbon index 1f501f90ea70..1c03cc541aa6 100644 --- a/toolchain/check/testdata/pointer/fail_dereference_not_pointer.carbon +++ b/toolchain/check/testdata/pointer/fail_dereference_not_pointer.carbon @@ -28,9 +28,11 @@ fn Deref(n: i32) { // CHECK:STDOUT: %.loc11: ref = dereference %n // CHECK:STDOUT: %.loc15_5.1: type = tuple_type () // CHECK:STDOUT: %.loc15_5.2: () = tuple_literal () -// CHECK:STDOUT: %.loc15_3: ref = dereference %.loc15_5.2 +// CHECK:STDOUT: %.loc15_5.3: () = tuple_value %.loc15_5.2, () +// CHECK:STDOUT: %.loc15_3: ref = dereference %.loc15_5.3 // CHECK:STDOUT: %.loc19_5.1: type = struct_type {} // CHECK:STDOUT: %.loc19_5.2: {} = struct_literal () -// CHECK:STDOUT: %.loc19_3: ref = dereference %.loc19_5.2 +// CHECK:STDOUT: %.loc19_5.3: {} = struct_value %.loc19_5.2, () +// CHECK:STDOUT: %.loc19_3: ref = dereference %.loc19_5.3 // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/return/struct.carbon b/toolchain/check/testdata/return/struct.carbon index e9d3ad73ead2..25243bc9a2ab 100644 --- a/toolchain/check/testdata/return/struct.carbon +++ b/toolchain/check/testdata/return/struct.carbon @@ -16,6 +16,7 @@ fn Main() -> {.a: i32} { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc8_16: i32 = int_literal 3 // CHECK:STDOUT: %.loc8_14: i32 = stub_reference %.loc8_16 -// CHECK:STDOUT: %.loc8_17: {.a: i32} = struct_literal (%.loc8_14) -// CHECK:STDOUT: return %.loc8_17 +// CHECK:STDOUT: %.loc8_17.1: {.a: i32} = struct_literal (%.loc8_14) +// CHECK:STDOUT: %.loc8_17.2: {.a: i32} = struct_value %.loc8_17.1, (%.loc8_14) +// CHECK:STDOUT: return %.loc8_17.2 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/return/tuple.carbon b/toolchain/check/testdata/return/tuple.carbon index a39e0c172cbb..41583c00aa6a 100644 --- a/toolchain/check/testdata/return/tuple.carbon +++ b/toolchain/check/testdata/return/tuple.carbon @@ -20,5 +20,12 @@ fn Main() -> (i32, i32) { // CHECK:STDOUT: %.loc9_15.1: i32 = int_literal 35 // CHECK:STDOUT: %.loc9_15.2: i32 = stub_reference %.loc9_15.1 // CHECK:STDOUT: %.loc9_17: (i32, i32) = tuple_literal (%.loc9_11.2, %.loc9_15.2) -// CHECK:STDOUT: return %.loc9_17 +// CHECK:STDOUT: %.loc9_18.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc9_18.2: ref i32 = tuple_index %return, %.loc9_18.1 +// CHECK:STDOUT: %.loc9_18.3: init i32 = initialize_from %.loc9_11.2 to %.loc9_18.2 +// CHECK:STDOUT: %.loc9_18.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc9_18.5: ref i32 = tuple_index %return, %.loc9_18.4 +// CHECK:STDOUT: %.loc9_18.6: init i32 = initialize_from %.loc9_15.2 to %.loc9_18.5 +// CHECK:STDOUT: %.loc9_18.7: init (i32, i32) = tuple_init %.loc9_17, (%.loc9_18.3, %.loc9_18.6) +// CHECK:STDOUT: return %.loc9_18.7 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/empty.carbon b/toolchain/check/testdata/struct/empty.carbon index 767ab48430ac..4caa0d17c966 100644 --- a/toolchain/check/testdata/struct/empty.carbon +++ b/toolchain/check/testdata/struct/empty.carbon @@ -12,7 +12,8 @@ var y: {} = x; // CHECK:STDOUT: %.loc7_9.2: {} = struct_literal () // CHECK:STDOUT: %x: ref {} = var "x" // CHECK:STDOUT: %.loc7_14: {} = struct_literal () -// CHECK:STDOUT: assign %x, %.loc7_14 +// CHECK:STDOUT: %.loc7_15: init {} = struct_init %.loc7_14, () +// CHECK:STDOUT: assign %x, %.loc7_15 // CHECK:STDOUT: %.loc8: {} = struct_literal () // CHECK:STDOUT: %y: ref {} = var "y" // CHECK:STDOUT: %.loc7_5: {} = bind_value %x diff --git a/toolchain/check/testdata/struct/fail_member_access_type.carbon b/toolchain/check/testdata/struct/fail_member_access_type.carbon index 27c3e9263207..288e3204d130 100644 --- a/toolchain/check/testdata/struct/fail_member_access_type.carbon +++ b/toolchain/check/testdata/struct/fail_member_access_type.carbon @@ -16,7 +16,9 @@ var y: i32 = x.b; // CHECK:STDOUT: %.loc7_26: f64 = real_literal 40e-1 // CHECK:STDOUT: %.loc7_24: f64 = stub_reference %.loc7_26 // CHECK:STDOUT: %.loc7_29: {.a: f64} = struct_literal (%.loc7_24) -// CHECK:STDOUT: assign %x, %.loc7_29 +// CHECK:STDOUT: %.loc7_30.1: ref f64 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_30.2: init {.a: f64} = struct_init %.loc7_29, (%.loc7_24) +// CHECK:STDOUT: assign %x, %.loc7_30.2 // CHECK:STDOUT: %y: ref i32 = var "y" // CHECK:STDOUT: assign %y, // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/fail_non_member_access.carbon b/toolchain/check/testdata/struct/fail_non_member_access.carbon index 4b06622b9777..6775e8ef01cf 100644 --- a/toolchain/check/testdata/struct/fail_non_member_access.carbon +++ b/toolchain/check/testdata/struct/fail_non_member_access.carbon @@ -16,7 +16,9 @@ var y: i32 = x.b; // CHECK:STDOUT: %.loc7_26: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_24: i32 = stub_reference %.loc7_26 // CHECK:STDOUT: %.loc7_27: {.a: i32} = struct_literal (%.loc7_24) -// CHECK:STDOUT: assign %x, %.loc7_27 +// CHECK:STDOUT: %.loc7_28.1: ref i32 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_28.2: init {.a: i32} = struct_init %.loc7_27, (%.loc7_24) +// CHECK:STDOUT: assign %x, %.loc7_28.2 // CHECK:STDOUT: %y: ref i32 = var "y" // CHECK:STDOUT: assign %y, // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/member_access.carbon b/toolchain/check/testdata/struct/member_access.carbon index 18304a7b56d7..8ac94f05d011 100644 --- a/toolchain/check/testdata/struct/member_access.carbon +++ b/toolchain/check/testdata/struct/member_access.carbon @@ -16,7 +16,12 @@ var z: i32 = y; // CHECK:STDOUT: %.loc7_45: i32 = int_literal 1 // CHECK:STDOUT: %.loc7_43: i32 = stub_reference %.loc7_45 // CHECK:STDOUT: %.loc7_46: {.a: f64, .b: i32} = struct_literal (%.loc7_33, %.loc7_43) -// CHECK:STDOUT: assign %x, %.loc7_46 +// CHECK:STDOUT: %.loc7_47.1: ref f64 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_47.2: init f64 = initialize_from %.loc7_33 to %.loc7_47.1 +// CHECK:STDOUT: %.loc7_47.3: ref i32 = struct_access %x, member1 +// CHECK:STDOUT: %.loc7_47.4: init i32 = initialize_from %.loc7_43 to %.loc7_47.3 +// CHECK:STDOUT: %.loc7_47.5: init {.a: f64, .b: i32} = struct_init %.loc7_46, (%.loc7_47.2, %.loc7_47.4) +// CHECK:STDOUT: assign %x, %.loc7_47.5 // CHECK:STDOUT: %y: ref i32 = var "y" // CHECK:STDOUT: %.loc8_15.1: ref i32 = struct_access %x, member1 // CHECK:STDOUT: %.loc8_15.2: i32 = bind_value %.loc8_15.1 diff --git a/toolchain/check/testdata/struct/one_entry.carbon b/toolchain/check/testdata/struct/one_entry.carbon index 6c0a57f3636c..fdac1ce3283b 100644 --- a/toolchain/check/testdata/struct/one_entry.carbon +++ b/toolchain/check/testdata/struct/one_entry.carbon @@ -13,7 +13,9 @@ var y: {.a: i32} = x; // CHECK:STDOUT: %.loc7_26: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_24: i32 = stub_reference %.loc7_26 // CHECK:STDOUT: %.loc7_27: {.a: i32} = struct_literal (%.loc7_24) -// CHECK:STDOUT: assign %x, %.loc7_27 +// CHECK:STDOUT: %.loc7_28.1: ref i32 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_28.2: init {.a: i32} = struct_init %.loc7_27, (%.loc7_24) +// CHECK:STDOUT: assign %x, %.loc7_28.2 // CHECK:STDOUT: %.loc8: type = struct_type {.a: i32} // CHECK:STDOUT: %y: ref {.a: i32} = var "y" // CHECK:STDOUT: %.loc7_5: {.a: i32} = bind_value %x diff --git a/toolchain/check/testdata/struct/tuple_as_element.carbon b/toolchain/check/testdata/struct/tuple_as_element.carbon index b1a256745838..8995965a375e 100644 --- a/toolchain/check/testdata/struct/tuple_as_element.carbon +++ b/toolchain/check/testdata/struct/tuple_as_element.carbon @@ -21,7 +21,15 @@ var y: {.a: i32, .b: (i32,)} = x; // CHECK:STDOUT: %.loc7_49: (i32,) = tuple_literal (%.loc7_47.2) // CHECK:STDOUT: %.loc7_44: (i32,) = stub_reference %.loc7_49 // CHECK:STDOUT: %.loc7_50: {.a: i32, .b: (i32,)} = struct_literal (%.loc7_36, %.loc7_44) -// CHECK:STDOUT: assign %x, %.loc7_50 +// CHECK:STDOUT: %.loc7_51.1: ref i32 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_51.2: init i32 = initialize_from %.loc7_36 to %.loc7_51.1 +// CHECK:STDOUT: %.loc7_51.3: ref (i32,) = struct_access %x, member1 +// CHECK:STDOUT: %.loc7_51.4: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_51.5: ref i32 = tuple_index %.loc7_51.3, %.loc7_51.4 +// CHECK:STDOUT: %.loc7_51.6: init (i32,) = tuple_init %.loc7_49, (%.loc7_47.2) +// CHECK:STDOUT: %.loc7_51.7: init (i32,) = initialize_from %.loc7_51.6 to %.loc7_51.3 +// CHECK:STDOUT: %.loc7_51.8: init {.a: i32, .b: (i32,)} = struct_init %.loc7_50, (%.loc7_51.2, %.loc7_51.7) +// CHECK:STDOUT: assign %x, %.loc7_51.8 // CHECK:STDOUT: %.loc8_23: type = stub_reference i32 // CHECK:STDOUT: %.loc8_27: (type,) = tuple_literal (%.loc8_23) // CHECK:STDOUT: %.loc8_28: type = struct_type {.a: i32, .b: (i32,)} diff --git a/toolchain/check/testdata/struct/two_entries.carbon b/toolchain/check/testdata/struct/two_entries.carbon index bccf29256a8f..b761a251256c 100644 --- a/toolchain/check/testdata/struct/two_entries.carbon +++ b/toolchain/check/testdata/struct/two_entries.carbon @@ -15,7 +15,12 @@ var y: {.a: i32, .b: i32} = x; // CHECK:STDOUT: %.loc7_43: i32 = int_literal 2 // CHECK:STDOUT: %.loc7_41: i32 = stub_reference %.loc7_43 // CHECK:STDOUT: %.loc7_44: {.a: i32, .b: i32} = struct_literal (%.loc7_33, %.loc7_41) -// CHECK:STDOUT: assign %x, %.loc7_44 +// CHECK:STDOUT: %.loc7_45.1: ref i32 = struct_access %x, member0 +// CHECK:STDOUT: %.loc7_45.2: init i32 = initialize_from %.loc7_33 to %.loc7_45.1 +// CHECK:STDOUT: %.loc7_45.3: ref i32 = struct_access %x, member1 +// CHECK:STDOUT: %.loc7_45.4: init i32 = initialize_from %.loc7_41 to %.loc7_45.3 +// CHECK:STDOUT: %.loc7_45.5: init {.a: i32, .b: i32} = struct_init %.loc7_44, (%.loc7_45.2, %.loc7_45.4) +// CHECK:STDOUT: assign %x, %.loc7_45.5 // CHECK:STDOUT: %.loc8: type = struct_type {.a: i32, .b: i32} // CHECK:STDOUT: %y: ref {.a: i32, .b: i32} = var "y" // CHECK:STDOUT: %.loc7_5: {.a: i32, .b: i32} = bind_value %x diff --git a/toolchain/check/testdata/tuples/empty.carbon b/toolchain/check/testdata/tuples/empty.carbon index 26d80538c25e..9f74854a15f7 100644 --- a/toolchain/check/testdata/tuples/empty.carbon +++ b/toolchain/check/testdata/tuples/empty.carbon @@ -12,7 +12,8 @@ var y: () = x; // CHECK:STDOUT: %.loc7_9.2: () = tuple_literal () // CHECK:STDOUT: %x: ref () = var "x" // CHECK:STDOUT: %.loc7_14: () = tuple_literal () -// CHECK:STDOUT: assign %x, %.loc7_14 +// CHECK:STDOUT: %.loc7_15: init () = tuple_init %.loc7_14, () +// CHECK:STDOUT: assign %x, %.loc7_15 // CHECK:STDOUT: %.loc8: () = tuple_literal () // CHECK:STDOUT: %y: ref () = var "y" // CHECK:STDOUT: %.loc7_5: () = bind_value %x diff --git a/toolchain/check/testdata/tuples/nested_tuple.carbon b/toolchain/check/testdata/tuples/nested_tuple.carbon index 3cb7b78ccfa2..eac7812f337c 100644 --- a/toolchain/check/testdata/tuples/nested_tuple.carbon +++ b/toolchain/check/testdata/tuples/nested_tuple.carbon @@ -27,5 +27,18 @@ var x: ((i32, i32), i32) = ((12, 76), 6); // CHECK:STDOUT: %.loc7_39.1: i32 = int_literal 6 // CHECK:STDOUT: %.loc7_39.2: i32 = stub_reference %.loc7_39.1 // CHECK:STDOUT: %.loc7_40: ((i32, i32), i32) = tuple_literal (%.loc7_36.2, %.loc7_39.2) -// CHECK:STDOUT: assign %x, %.loc7_40 +// CHECK:STDOUT: %.loc7_41.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_41.2: ref (i32, i32) = tuple_index %x, %.loc7_41.1 +// CHECK:STDOUT: %.loc7_41.3: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_41.4: ref i32 = tuple_index %.loc7_41.2, %.loc7_41.3 +// CHECK:STDOUT: %.loc7_41.5: init i32 = initialize_from %.loc7_30.2 to %.loc7_41.4 +// CHECK:STDOUT: %.loc7_41.6: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_41.7: ref i32 = tuple_index %.loc7_41.2, %.loc7_41.6 +// CHECK:STDOUT: %.loc7_41.8: init i32 = initialize_from %.loc7_34.2 to %.loc7_41.7 +// CHECK:STDOUT: %.loc7_41.9: init (i32, i32) = tuple_init %.loc7_36.1, (%.loc7_41.5, %.loc7_41.8) +// CHECK:STDOUT: %.loc7_41.10: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_41.11: ref i32 = tuple_index %x, %.loc7_41.10 +// CHECK:STDOUT: %.loc7_41.12: init i32 = initialize_from %.loc7_39.2 to %.loc7_41.11 +// CHECK:STDOUT: %.loc7_41.13: init ((i32, i32), i32) = tuple_init %.loc7_40, (%.loc7_41.9, %.loc7_41.12) +// CHECK:STDOUT: assign %x, %.loc7_41.13 // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/tuples/one_element.carbon b/toolchain/check/testdata/tuples/one_element.carbon index b1a9f92dd086..30605ed73f15 100644 --- a/toolchain/check/testdata/tuples/one_element.carbon +++ b/toolchain/check/testdata/tuples/one_element.carbon @@ -16,7 +16,10 @@ var y: (i32,) = x; // CHECK:STDOUT: %.loc7_18.1: i32 = int_literal 4 // CHECK:STDOUT: %.loc7_18.2: i32 = stub_reference %.loc7_18.1 // CHECK:STDOUT: %.loc7_20: (i32,) = tuple_literal (%.loc7_18.2) -// CHECK:STDOUT: assign %x, %.loc7_20 +// CHECK:STDOUT: %.loc7_21.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_21.2: ref i32 = tuple_index %x, %.loc7_21.1 +// CHECK:STDOUT: %.loc7_21.3: init (i32,) = tuple_init %.loc7_20, (%.loc7_18.2) +// CHECK:STDOUT: assign %x, %.loc7_21.3 // CHECK:STDOUT: %.loc8_9: type = stub_reference i32 // CHECK:STDOUT: %.loc8_13: (type,) = tuple_literal (%.loc8_9) // CHECK:STDOUT: %y: ref (i32,) = var "y" diff --git a/toolchain/check/testdata/tuples/two_elements.carbon b/toolchain/check/testdata/tuples/two_elements.carbon index 3165be3c09bc..92c2c3e4d778 100644 --- a/toolchain/check/testdata/tuples/two_elements.carbon +++ b/toolchain/check/testdata/tuples/two_elements.carbon @@ -19,7 +19,14 @@ var y: (i32, i32) = x; // CHECK:STDOUT: %.loc7_25.1: i32 = int_literal 102 // CHECK:STDOUT: %.loc7_25.2: i32 = stub_reference %.loc7_25.1 // CHECK:STDOUT: %.loc7_28: (i32, i32) = tuple_literal (%.loc7_22.2, %.loc7_25.2) -// CHECK:STDOUT: assign %x, %.loc7_28 +// CHECK:STDOUT: %.loc7_29.1: i32 = int_literal 0 +// CHECK:STDOUT: %.loc7_29.2: ref i32 = tuple_index %x, %.loc7_29.1 +// CHECK:STDOUT: %.loc7_29.3: init i32 = initialize_from %.loc7_22.2 to %.loc7_29.2 +// CHECK:STDOUT: %.loc7_29.4: i32 = int_literal 1 +// CHECK:STDOUT: %.loc7_29.5: ref i32 = tuple_index %x, %.loc7_29.4 +// CHECK:STDOUT: %.loc7_29.6: init i32 = initialize_from %.loc7_25.2 to %.loc7_29.5 +// CHECK:STDOUT: %.loc7_29.7: init (i32, i32) = tuple_init %.loc7_28, (%.loc7_29.3, %.loc7_29.6) +// CHECK:STDOUT: assign %x, %.loc7_29.7 // CHECK:STDOUT: %.loc8_9: type = stub_reference i32 // CHECK:STDOUT: %.loc8_14: type = stub_reference i32 // CHECK:STDOUT: %.loc8_17: (type, type) = tuple_literal (%.loc8_9, %.loc8_14) diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 29b5ca65e743..270b5c9142ee 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -134,6 +134,7 @@ CARBON_DIAGNOSTIC_KIND(QualifiedDeclarationInNonScope) CARBON_DIAGNOSTIC_KIND(QualifiedDeclarationNonScopeEntity) CARBON_DIAGNOSTIC_KIND(QualifiedExpressionUnsupported) CARBON_DIAGNOSTIC_KIND(QualifiedExpressionNameNotFound) +CARBON_DIAGNOSTIC_KIND(UseOfNonExpressionAsValue) // ============================================================================ // Other diagnostics diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 414aae531446..f3c496b45d2e 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -301,6 +301,99 @@ auto HandleStructAccess(FunctionContext& context, SemIR::NodeId node_id, context.SetLocal(node_id, gep); } +auto HandleStructLiteral(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + // A StructLiteral should always be converted to a StructInit or StructValue + // if its value is needed. + context.SetLocal(node_id, + llvm::PoisonValue::get(context.GetType(node.type_id()))); +} + +// Emits the value representation for a struct or tuple whose elements are the +// contents of `refs_id`. +auto EmitStructOrTupleValueRepresentation(FunctionContext& context, + SemIR::TypeId type_id, + SemIR::NodeBlockId refs_id, + llvm::Twine name) -> llvm::Value* { + auto* llvm_type = context.GetType(type_id); + + switch (SemIR::GetValueRepresentation(context.semantics_ir(), type_id).kind) { + case SemIR::ValueRepresentation::None: + // TODO: Add a helper to get a "no value representation" value. + return llvm::PoisonValue::get(llvm_type); + + case SemIR::ValueRepresentation::Copy: { + auto refs = context.semantics_ir().GetNodeBlock(refs_id); + CARBON_CHECK(refs.size() == 1) + << "Unexpected size for aggregate with by-copy value representation"; + // TODO: Remove the LLVM StructType wrapper in this case, so we don't + // need this `insert_value` wrapping. + return context.builder().CreateInsertValue( + llvm::PoisonValue::get(llvm_type), context.GetLocal(refs[0]), {0}); + } + + case SemIR::ValueRepresentation::Pointer: { + // Write the object representation to a local alloca so we can produce a + // pointer to it as the value representation. + auto* alloca = context.builder().CreateAlloca( + llvm_type, /*ArraySize=*/nullptr, name); + for (auto [i, ref] : + llvm::enumerate(context.semantics_ir().GetNodeBlock(refs_id))) { + auto* gep = context.builder().CreateStructGEP(llvm_type, alloca, i); + // TODO: We are loading a value representation here and storing an + // object representation! + context.builder().CreateStore(context.GetLocal(ref), gep); + } + return alloca; + } + + case SemIR::ValueRepresentation::Custom: + CARBON_FATAL() + << "Aggregate should never have custom value representation"; + } +} + +auto HandleStructInit(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + auto* llvm_type = context.GetType(node.type_id()); + + switch (SemIR::GetInitializingRepresentation(context.semantics_ir(), + node.type_id()) + .kind) { + case SemIR::InitializingRepresentation::None: + case SemIR::InitializingRepresentation::InPlace: + // TODO: Add a helper to poison a value slot. + context.SetLocal(node_id, llvm::PoisonValue::get(llvm_type)); + break; + + case SemIR::InitializingRepresentation::ByCopy: { + auto [struct_literal_id, refs_id] = node.GetAsStructInit(); + context.SetLocal(node_id, + EmitStructOrTupleValueRepresentation( + context, node.type_id(), refs_id, "struct.init")); + break; + } + } +} + +auto HandleStructValue(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + auto [struct_literal_id, refs_id] = node.GetAsStructValue(); + context.SetLocal(node_id, EmitStructOrTupleValueRepresentation( + context, node.type_id(), refs_id, "struct")); +} + +auto HandleStructTypeField(FunctionContext& /*context*/, + SemIR::NodeId /*node_id*/, SemIR::Node /*node*/) + -> void { + // No action to take. +} + +auto HandleStubReference(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + context.SetLocal(node_id, context.GetLocal(node.GetAsStubReference())); +} + auto HandleTupleIndex(FunctionContext& context, SemIR::NodeId node_id, SemIR::Node node) -> void { auto [tuple_node_id, index_node_id] = node.GetAsTupleIndex(); @@ -317,49 +410,40 @@ auto HandleTupleIndex(FunctionContext& context, SemIR::NodeId node_id, auto HandleTupleLiteral(FunctionContext& context, SemIR::NodeId node_id, SemIR::Node node) -> void { + // A TupleLiteral should always be converted to a TupleInit or TupleValue if + // its value is needed. + context.SetLocal(node_id, + llvm::PoisonValue::get(context.GetType(node.type_id()))); +} + +auto HandleTupleInit(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { auto* llvm_type = context.GetType(node.type_id()); - auto* alloca = - context.builder().CreateAlloca(llvm_type, /*ArraySize=*/nullptr, "tuple"); - context.SetLocal(node_id, alloca); - auto refs = context.semantics_ir().GetNodeBlock(node.GetAsTupleLiteral()); - for (auto [i, ref] : llvm::enumerate(refs)) { - auto* gep = context.builder().CreateStructGEP(llvm_type, alloca, i); - context.builder().CreateStore(context.GetLocal(ref), gep); + + switch (SemIR::GetInitializingRepresentation(context.semantics_ir(), + node.type_id()) + .kind) { + case SemIR::InitializingRepresentation::None: + case SemIR::InitializingRepresentation::InPlace: + // TODO: Add a helper to poison a value slot. + context.SetLocal(node_id, llvm::PoisonValue::get(llvm_type)); + break; + + case SemIR::InitializingRepresentation::ByCopy: { + auto [struct_literal_id, refs_id] = node.GetAsTupleInit(); + context.SetLocal( + node_id, EmitStructOrTupleValueRepresentation(context, node.type_id(), + refs_id, "tuple.init")); + break; + } } } -auto HandleStructTypeField(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node /*node*/) - -> void { - // No action to take. -} - -auto HandleStructLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto* llvm_type = context.GetType(node.type_id()); - auto* alloca = context.builder().CreateAlloca( - llvm_type, /*ArraySize=*/nullptr, "struct"); - context.SetLocal(node_id, alloca); - - auto refs = context.semantics_ir().GetNodeBlock(node.GetAsStructLiteral()); - // Get type information for member names. - auto type_refs = context.semantics_ir().GetNodeBlock( - context.semantics_ir() - .GetNode(context.semantics_ir().GetType(node.type_id())) - .GetAsStructType()); - for (auto [i, ref, type_ref] : llvm::enumerate(refs, type_refs)) { - auto [field_name_id, field_type_id] = - context.semantics_ir().GetNode(type_ref).GetAsStructTypeField(); - auto member_name = context.semantics_ir().GetString(field_name_id); - auto* gep = - context.builder().CreateStructGEP(llvm_type, alloca, i, member_name); - context.builder().CreateStore(context.GetLocal(ref), gep); - } -} - -auto HandleStubReference(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal(node_id, context.GetLocal(node.GetAsStubReference())); +auto HandleTupleValue(FunctionContext& context, SemIR::NodeId node_id, + SemIR::Node node) -> void { + auto [struct_literal_id, refs_id] = node.GetAsTupleValue(); + context.SetLocal(node_id, EmitStructOrTupleValueRepresentation( + context, node.type_id(), refs_id, "tuple")); } auto HandleUnaryOperatorNot(FunctionContext& context, SemIR::NodeId node_id, diff --git a/toolchain/lower/testdata/array/assign_return_value.carbon b/toolchain/lower/testdata/array/assign_return_value.carbon index 9f32b1ee450b..dc3ab0be2e67 100644 --- a/toolchain/lower/testdata/array/assign_return_value.carbon +++ b/toolchain/lower/testdata/array/assign_return_value.carbon @@ -14,11 +14,10 @@ fn Run() { // CHECK:STDOUT: source_filename = "assign_return_value.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) { -// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 24, ptr %2, align 4 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 +// CHECK:STDOUT: store i32 12, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 +// CHECK:STDOUT: store i32 24, ptr %tuple.index1, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/array/base.carbon b/toolchain/lower/testdata/array/base.carbon index c0fcbca893b8..5e599604d13b 100644 --- a/toolchain/lower/testdata/array/base.carbon +++ b/toolchain/lower/testdata/array/base.carbon @@ -15,75 +15,40 @@ fn Run() { // CHECK:STDOUT: ; ModuleID = 'base.carbon' // CHECK:STDOUT: source_filename = "base.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define void @Run() { // CHECK:STDOUT: %a = alloca [1 x i32], align 4 -// CHECK:STDOUT: %tuple = alloca { i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [1 x i32], ptr %a, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %array.index, align 4 // CHECK:STDOUT: %b = alloca [2 x double], align 8 -// CHECK:STDOUT: %tuple1 = alloca { double, double }, align 8 -// CHECK:STDOUT: %2 = getelementptr inbounds { double, double }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store double 0x4026333333333334, ptr %2, align 8 -// CHECK:STDOUT: %3 = getelementptr inbounds { double, double }, ptr %tuple1, i32 0, i32 1 -// CHECK:STDOUT: store double 2.200000e+00, ptr %3, align 8 -// CHECK:STDOUT: %array.index2 = getelementptr inbounds [2 x double], ptr %b, i32 0, i32 0 -// CHECK:STDOUT: store double 0x4026333333333334, ptr %array.index2, align 8 -// CHECK:STDOUT: %array.index3 = getelementptr inbounds [2 x double], ptr %b, i32 0, i32 1 -// CHECK:STDOUT: store double 2.200000e+00, ptr %array.index3, align 8 -// CHECK:STDOUT: %tuple4 = alloca {}, align 8 +// CHECK:STDOUT: %array.index1 = getelementptr inbounds [2 x double], ptr %b, i32 0, i32 0 +// CHECK:STDOUT: store double 0x4026333333333334, ptr %array.index1, align 8 +// CHECK:STDOUT: %array.index2 = getelementptr inbounds [2 x double], ptr %b, i32 0, i32 1 +// CHECK:STDOUT: store double 2.200000e+00, ptr %array.index2, align 8 // CHECK:STDOUT: %c = alloca [5 x {}], align 8 -// CHECK:STDOUT: %tuple5 = alloca {}, align 8 -// CHECK:STDOUT: %tuple6 = alloca {}, align 8 -// CHECK:STDOUT: %tuple7 = alloca {}, align 8 -// CHECK:STDOUT: %tuple8 = alloca {}, align 8 -// CHECK:STDOUT: %tuple9 = alloca {}, align 8 -// CHECK:STDOUT: %tuple10 = alloca { {}, {}, {}, {}, {} }, align 8 -// CHECK:STDOUT: %4 = getelementptr inbounds { {}, {}, {}, {}, {} }, ptr %tuple10, i32 0, i32 0 -// CHECK:STDOUT: store ptr %tuple5, ptr %4, align 8 -// CHECK:STDOUT: %5 = getelementptr inbounds { {}, {}, {}, {}, {} }, ptr %tuple10, i32 0, i32 1 -// CHECK:STDOUT: store ptr %tuple6, ptr %5, align 8 -// CHECK:STDOUT: %6 = getelementptr inbounds { {}, {}, {}, {}, {} }, ptr %tuple10, i32 0, i32 2 -// CHECK:STDOUT: store ptr %tuple7, ptr %6, align 8 -// CHECK:STDOUT: %7 = getelementptr inbounds { {}, {}, {}, {}, {} }, ptr %tuple10, i32 0, i32 3 -// CHECK:STDOUT: store ptr %tuple8, ptr %7, align 8 -// CHECK:STDOUT: %8 = getelementptr inbounds { {}, {}, {}, {}, {} }, ptr %tuple10, i32 0, i32 4 -// CHECK:STDOUT: store ptr %tuple9, ptr %8, align 8 -// CHECK:STDOUT: %array.index11 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 0 -// CHECK:STDOUT: %array.index12 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 1 -// CHECK:STDOUT: %array.index13 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 2 -// CHECK:STDOUT: %array.index14 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 3 -// CHECK:STDOUT: %array.index15 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 4 -// CHECK:STDOUT: %tuple16 = alloca { %type, %type, %type }, align 8 -// CHECK:STDOUT: %9 = getelementptr inbounds { %type, %type, %type }, ptr %tuple16, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %9, align 1 -// CHECK:STDOUT: %10 = getelementptr inbounds { %type, %type, %type }, ptr %tuple16, i32 0, i32 1 -// CHECK:STDOUT: store %type zeroinitializer, ptr %10, align 1 -// CHECK:STDOUT: %11 = getelementptr inbounds { %type, %type, %type }, ptr %tuple16, i32 0, i32 2 -// CHECK:STDOUT: store %type zeroinitializer, ptr %11, align 1 +// CHECK:STDOUT: %array.index3 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 0 +// CHECK:STDOUT: %array.index4 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 1 +// CHECK:STDOUT: %array.index5 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 2 +// CHECK:STDOUT: %array.index6 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 3 +// CHECK:STDOUT: %array.index7 = getelementptr inbounds [5 x {}], ptr %c, i32 0, i32 4 // CHECK:STDOUT: %d = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %tuple17 = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %12 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple17, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %12, align 4 -// CHECK:STDOUT: %13 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple17, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %13, align 4 -// CHECK:STDOUT: %14 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple17, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %14, align 4 -// CHECK:STDOUT: %e = alloca [3 x i32], align 4 -// CHECK:STDOUT: %array.index18 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 0 // CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 0 -// CHECK:STDOUT: %15 = load i32, ptr %tuple.index, align 4 -// CHECK:STDOUT: store i32 %15, ptr %array.index18, align 4 -// CHECK:STDOUT: %array.index19 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 1 -// CHECK:STDOUT: %tuple.index20 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 -// CHECK:STDOUT: %16 = load i32, ptr %tuple.index20, align 4 -// CHECK:STDOUT: store i32 %16, ptr %array.index19, align 4 -// CHECK:STDOUT: %array.index21 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 2 -// CHECK:STDOUT: %tuple.index22 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 -// CHECK:STDOUT: %17 = load i32, ptr %tuple.index22, align 4 -// CHECK:STDOUT: store i32 %17, ptr %array.index21, align 4 +// CHECK:STDOUT: store i32 1, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index8 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 +// CHECK:STDOUT: store i32 2, ptr %tuple.index8, align 4 +// CHECK:STDOUT: %tuple.index9 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 +// CHECK:STDOUT: store i32 3, ptr %tuple.index9, align 4 +// CHECK:STDOUT: %e = alloca [3 x i32], align 4 +// CHECK:STDOUT: %array.index10 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 0 +// CHECK:STDOUT: %tuple.index11 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.index11, align 4 +// CHECK:STDOUT: store i32 %1, ptr %array.index10, align 4 +// CHECK:STDOUT: %array.index12 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 1 +// CHECK:STDOUT: %tuple.index13 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 1 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.index13, align 4 +// CHECK:STDOUT: store i32 %2, ptr %array.index12, align 4 +// CHECK:STDOUT: %array.index14 = getelementptr inbounds [3 x i32], ptr %e, i32 0, i32 2 +// CHECK:STDOUT: %tuple.index15 = getelementptr inbounds { i32, i32, i32 }, ptr %d, i32 0, i32 2 +// CHECK:STDOUT: %3 = load i32, ptr %tuple.index15, align 4 +// CHECK:STDOUT: store i32 %3, ptr %array.index14, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/array/function_param.carbon b/toolchain/lower/testdata/array/function_param.carbon index 4f116f3add03..584e38fc6e23 100644 --- a/toolchain/lower/testdata/array/function_param.carbon +++ b/toolchain/lower/testdata/array/function_param.carbon @@ -22,13 +22,6 @@ fn G() -> i32 { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define i32 @G() { -// CHECK:STDOUT: %tuple = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %2, align 4 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 2 -// CHECK:STDOUT: store i32 3, ptr %3, align 4 // CHECK:STDOUT: %temp = alloca [3 x i32], align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [3 x i32], ptr %temp, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %array.index, align 4 @@ -39,6 +32,6 @@ fn G() -> i32 { // CHECK:STDOUT: %F = call i32 @F(ptr %temp, i32 1) // CHECK:STDOUT: %temp3 = alloca i32, align 4 // CHECK:STDOUT: store i32 %F, ptr %temp3, align 4 -// CHECK:STDOUT: %4 = load i32, ptr %temp3, align 4 -// CHECK:STDOUT: ret i32 %4 +// CHECK:STDOUT: %1 = load i32, ptr %temp3, align 4 +// CHECK:STDOUT: ret i32 %1 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/basics/numeric_literals.carbon b/toolchain/lower/testdata/basics/numeric_literals.carbon index cec19e63c715..973bbbafe008 100644 --- a/toolchain/lower/testdata/basics/numeric_literals.carbon +++ b/toolchain/lower/testdata/basics/numeric_literals.carbon @@ -28,15 +28,6 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: %ints = alloca [4 x i32], align 4 -// CHECK:STDOUT: %tuple = alloca { i32, i32, i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32, i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 8, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32, i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 9, ptr %2, align 4 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32, i32, i32 }, ptr %tuple, i32 0, i32 2 -// CHECK:STDOUT: store i32 8, ptr %3, align 4 -// CHECK:STDOUT: %4 = getelementptr inbounds { i32, i32, i32, i32 }, ptr %tuple, i32 0, i32 3 -// CHECK:STDOUT: store i32 8, ptr %4, align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [4 x i32], ptr %ints, i32 0, i32 0 // CHECK:STDOUT: store i32 8, ptr %array.index, align 4 // CHECK:STDOUT: %array.index1 = getelementptr inbounds [4 x i32], ptr %ints, i32 0, i32 1 @@ -46,30 +37,17 @@ fn F() { // CHECK:STDOUT: %array.index3 = getelementptr inbounds [4 x i32], ptr %ints, i32 0, i32 3 // CHECK:STDOUT: store i32 8, ptr %array.index3, align 4 // CHECK:STDOUT: %floats = alloca [6 x double], align 8 -// CHECK:STDOUT: %tuple4 = alloca { double, double, double, double, double, double }, align 8 -// CHECK:STDOUT: %5 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 0 -// CHECK:STDOUT: store double 9.000000e-01, ptr %5, align 8 -// CHECK:STDOUT: %6 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 1 -// CHECK:STDOUT: store double 8.000000e+00, ptr %6, align 8 -// CHECK:STDOUT: %7 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 2 -// CHECK:STDOUT: store double 8.000000e+01, ptr %7, align 8 -// CHECK:STDOUT: %8 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 3 -// CHECK:STDOUT: store double 1.000000e+07, ptr %8, align 8 -// CHECK:STDOUT: %9 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 4 -// CHECK:STDOUT: store double 1.000000e+08, ptr %9, align 8 -// CHECK:STDOUT: %10 = getelementptr inbounds { double, double, double, double, double, double }, ptr %tuple4, i32 0, i32 5 -// CHECK:STDOUT: store double 1.000000e-08, ptr %10, align 8 -// CHECK:STDOUT: %array.index5 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 0 -// CHECK:STDOUT: store double 9.000000e-01, ptr %array.index5, align 8 -// CHECK:STDOUT: %array.index6 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 1 -// CHECK:STDOUT: store double 8.000000e+00, ptr %array.index6, align 8 -// CHECK:STDOUT: %array.index7 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 2 -// CHECK:STDOUT: store double 8.000000e+01, ptr %array.index7, align 8 -// CHECK:STDOUT: %array.index8 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 3 -// CHECK:STDOUT: store double 1.000000e+07, ptr %array.index8, align 8 -// CHECK:STDOUT: %array.index9 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 4 -// CHECK:STDOUT: store double 1.000000e+08, ptr %array.index9, align 8 -// CHECK:STDOUT: %array.index10 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 5 -// CHECK:STDOUT: store double 1.000000e-08, ptr %array.index10, align 8 +// CHECK:STDOUT: %array.index4 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 0 +// CHECK:STDOUT: store double 9.000000e-01, ptr %array.index4, align 8 +// CHECK:STDOUT: %array.index5 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 1 +// CHECK:STDOUT: store double 8.000000e+00, ptr %array.index5, align 8 +// CHECK:STDOUT: %array.index6 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 2 +// CHECK:STDOUT: store double 8.000000e+01, ptr %array.index6, align 8 +// CHECK:STDOUT: %array.index7 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 3 +// CHECK:STDOUT: store double 1.000000e+07, ptr %array.index7, align 8 +// CHECK:STDOUT: %array.index8 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 4 +// CHECK:STDOUT: store double 1.000000e+08, ptr %array.index8, align 8 +// CHECK:STDOUT: %array.index9 = getelementptr inbounds [6 x double], ptr %floats, i32 0, i32 5 +// CHECK:STDOUT: store double 1.000000e-08, ptr %array.index9, align 8 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/function/call/empty_struct.carbon b/toolchain/lower/testdata/function/call/empty_struct.carbon index 935e58c31589..530485949273 100644 --- a/toolchain/lower/testdata/function/call/empty_struct.carbon +++ b/toolchain/lower/testdata/function/call/empty_struct.carbon @@ -16,14 +16,11 @@ fn Main() { // CHECK:STDOUT: source_filename = "empty_struct.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define void @Echo() { -// CHECK:STDOUT: %struct = alloca {}, align 8 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %struct = alloca {}, align 8 // CHECK:STDOUT: %b = alloca {}, align 8 -// CHECK:STDOUT: %struct1 = alloca {}, align 8 // CHECK:STDOUT: call void @Echo() // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/function/call/empty_tuple.carbon b/toolchain/lower/testdata/function/call/empty_tuple.carbon index 6315f5277cc3..7a19452fb5b5 100644 --- a/toolchain/lower/testdata/function/call/empty_tuple.carbon +++ b/toolchain/lower/testdata/function/call/empty_tuple.carbon @@ -20,9 +20,7 @@ fn Main() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 // CHECK:STDOUT: %b = alloca {}, align 8 -// CHECK:STDOUT: %tuple1 = alloca {}, align 8 // CHECK:STDOUT: call void @Echo() // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon b/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon index 8badb9a07049..b43157f37631 100644 --- a/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon +++ b/toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon @@ -25,7 +25,6 @@ fn Main() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 // CHECK:STDOUT: %x = alloca {}, align 8 // CHECK:STDOUT: call void @Foo() // CHECK:STDOUT: %temp = alloca {}, align 8 diff --git a/toolchain/lower/testdata/function/call/return_implicit.carbon b/toolchain/lower/testdata/function/call/return_implicit.carbon index a82f5c0e081f..2211361ae138 100644 --- a/toolchain/lower/testdata/function/call/return_implicit.carbon +++ b/toolchain/lower/testdata/function/call/return_implicit.carbon @@ -20,7 +20,6 @@ fn Main() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 // CHECK:STDOUT: %b = alloca {}, align 8 // CHECK:STDOUT: call void @MakeImplicitEmptyTuple() // CHECK:STDOUT: ret void diff --git a/toolchain/lower/testdata/function/call/struct_param.carbon b/toolchain/lower/testdata/function/call/struct_param.carbon index 1a2dc9d8a7c7..0089335e4ae9 100644 --- a/toolchain/lower/testdata/function/call/struct_param.carbon +++ b/toolchain/lower/testdata/function/call/struct_param.carbon @@ -18,16 +18,14 @@ fn Main() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %struct = alloca {}, align 8 -// CHECK:STDOUT: %struct1 = alloca { i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %struct1, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %a, align 4 -// CHECK:STDOUT: %struct2 = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %a3 = getelementptr inbounds { i32, i32 }, ptr %struct2, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %a3, align 4 -// CHECK:STDOUT: %b = getelementptr inbounds { i32, i32 }, ptr %struct2, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %b, align 4 -// CHECK:STDOUT: %1 = load { i32 }, ptr %struct1, align 4 -// CHECK:STDOUT: call void @F({ i32 } %1, ptr %struct2) +// CHECK:STDOUT: %struct = alloca { i32, i32 }, align 8 +// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 0 +// CHECK:STDOUT: store i32 2, ptr %1, align 4 +// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 1 +// CHECK:STDOUT: store i32 3, ptr %2, align 4 +// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr %struct) // CHECK:STDOUT: ret void // CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1 } diff --git a/toolchain/lower/testdata/function/call/tuple_param.carbon b/toolchain/lower/testdata/function/call/tuple_param.carbon index 7f8660d46250..c4880b2747cb 100644 --- a/toolchain/lower/testdata/function/call/tuple_param.carbon +++ b/toolchain/lower/testdata/function/call/tuple_param.carbon @@ -18,16 +18,14 @@ fn Main() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 -// CHECK:STDOUT: %tuple2 = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple2, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %2, align 4 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32 }, ptr %tuple2, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %3, align 4 -// CHECK:STDOUT: %4 = load { i32 }, ptr %tuple1, align 4 -// CHECK:STDOUT: call void @F({ i32 } %4, ptr %tuple2) +// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 +// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 +// CHECK:STDOUT: store i32 2, ptr %1, align 4 +// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 +// CHECK:STDOUT: store i32 3, ptr %2, align 4 +// CHECK:STDOUT: call void @F({ i32 } { i32 1 }, ptr %tuple) // CHECK:STDOUT: ret void // CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1 } diff --git a/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon b/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon index 8189ae6759ae..0659fedfdc62 100644 --- a/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon +++ b/toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon @@ -19,28 +19,27 @@ fn Main() { // CHECK:STDOUT: %tuple.index = extractvalue { i32 } %b, 0 // CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %c, i32 0, i32 0 // CHECK:STDOUT: %tuple.index2 = getelementptr inbounds { i32, i32 }, ptr %c, i32 0, i32 1 -// CHECK:STDOUT: %tuple = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 %tuple.index, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store ptr %tuple.index1, ptr %2, align 8 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple, i32 0, i32 2 -// CHECK:STDOUT: store ptr %tuple.index2, ptr %3, align 8 +// CHECK:STDOUT: %tuple.index3 = getelementptr inbounds { i32, i32, i32 }, ptr %return, i32 0, i32 0 +// CHECK:STDOUT: store i32 %tuple.index, ptr %tuple.index3, align 4 +// CHECK:STDOUT: %tuple.index4 = getelementptr inbounds { i32, i32, i32 }, ptr %return, i32 0, i32 1 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.index1, align 4 +// CHECK:STDOUT: store i32 %1, ptr %tuple.index4, align 4 +// CHECK:STDOUT: %tuple.index5 = getelementptr inbounds { i32, i32, i32 }, ptr %return, i32 0, i32 2 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.index2, align 4 +// CHECK:STDOUT: store i32 %2, ptr %tuple.index5, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 -// CHECK:STDOUT: %tuple2 = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple2, i32 0, i32 0 -// CHECK:STDOUT: store i32 2, ptr %2, align 4 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32 }, ptr %tuple2, i32 0, i32 1 -// CHECK:STDOUT: store i32 3, ptr %3, align 4 // CHECK:STDOUT: %temp = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %4 = load { i32 }, ptr %tuple1, align 4 -// CHECK:STDOUT: call void @F(ptr %temp, { i32 } %4, ptr %tuple2) +// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 +// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 +// CHECK:STDOUT: store i32 2, ptr %1, align 4 +// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 +// CHECK:STDOUT: store i32 3, ptr %2, align 4 +// CHECK:STDOUT: call void @F(ptr %temp, { i32 } { i32 1 }, ptr %tuple) // CHECK:STDOUT: ret void // CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 1, { 2, 0, 1, 3, 4, 5 } diff --git a/toolchain/lower/testdata/index/array_element_access.carbon b/toolchain/lower/testdata/index/array_element_access.carbon index 81ae68247481..bc52c026d5ba 100644 --- a/toolchain/lower/testdata/index/array_element_access.carbon +++ b/toolchain/lower/testdata/index/array_element_access.carbon @@ -18,20 +18,14 @@ fn Run() { // CHECK:STDOUT: source_filename = "array_element_access.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define void @A(ptr sret({ i32, i32 }) %return) { -// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %2, align 4 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 +// CHECK:STDOUT: store i32 1, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 +// CHECK:STDOUT: store i32 2, ptr %tuple.index1, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: define void @B(ptr sret([2 x i32]) %return) { -// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %2, align 4 // CHECK:STDOUT: %array.index = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %array.index, align 4 // CHECK:STDOUT: %array.index1 = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 1 diff --git a/toolchain/lower/testdata/index/tuple_element_access.carbon b/toolchain/lower/testdata/index/tuple_element_access.carbon index 77a62d20fba1..d012b2e22ce2 100644 --- a/toolchain/lower/testdata/index/tuple_element_access.carbon +++ b/toolchain/lower/testdata/index/tuple_element_access.carbon @@ -14,31 +14,21 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'tuple_element_access.carbon' // CHECK:STDOUT: source_filename = "tuple_element_access.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %tuple = alloca { %type, %type, %type }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { %type, %type, %type }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %1, align 1 -// CHECK:STDOUT: %2 = getelementptr inbounds { %type, %type, %type }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store %type zeroinitializer, ptr %2, align 1 -// CHECK:STDOUT: %3 = getelementptr inbounds { %type, %type, %type }, ptr %tuple, i32 0, i32 2 -// CHECK:STDOUT: store %type zeroinitializer, ptr %3, align 1 // CHECK:STDOUT: %a = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32, i32, i32 }, align 8 -// CHECK:STDOUT: %4 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 0, ptr %4, align 4 -// CHECK:STDOUT: %5 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple1, i32 0, i32 1 -// CHECK:STDOUT: store i32 1, ptr %5, align 4 -// CHECK:STDOUT: %6 = getelementptr inbounds { i32, i32, i32 }, ptr %tuple1, i32 0, i32 2 -// CHECK:STDOUT: store i32 2, ptr %6, align 4 -// CHECK:STDOUT: %b = alloca i32, align 4 // CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 0 -// CHECK:STDOUT: %7 = load i32, ptr %tuple.index, align 4 -// CHECK:STDOUT: store i32 %7, ptr %b, align 4 -// CHECK:STDOUT: %c = alloca i32, align 4 +// CHECK:STDOUT: store i32 0, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 1 +// CHECK:STDOUT: store i32 1, ptr %tuple.index1, align 4 // CHECK:STDOUT: %tuple.index2 = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 2 -// CHECK:STDOUT: %8 = load i32, ptr %tuple.index2, align 4 -// CHECK:STDOUT: store i32 %8, ptr %c, align 4 +// CHECK:STDOUT: store i32 2, ptr %tuple.index2, align 4 +// CHECK:STDOUT: %b = alloca i32, align 4 +// CHECK:STDOUT: %tuple.index3 = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %tuple.index3, align 4 +// CHECK:STDOUT: store i32 %1, ptr %b, align 4 +// CHECK:STDOUT: %c = alloca i32, align 4 +// CHECK:STDOUT: %tuple.index4 = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 2 +// CHECK:STDOUT: %2 = load i32, ptr %tuple.index4, align 4 +// CHECK:STDOUT: store i32 %2, ptr %c, align 4 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/index/tuple_return_value_access.carbon b/toolchain/lower/testdata/index/tuple_return_value_access.carbon index ac713a1898ee..53dc6739fee0 100644 --- a/toolchain/lower/testdata/index/tuple_return_value_access.carbon +++ b/toolchain/lower/testdata/index/tuple_return_value_access.carbon @@ -14,11 +14,10 @@ fn Run() { // CHECK:STDOUT: source_filename = "tuple_return_value_access.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) { -// CHECK:STDOUT: %tuple = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %1, align 4 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32, i32 }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store i32 24, ptr %2, align 4 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0 +// CHECK:STDOUT: store i32 12, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1 +// CHECK:STDOUT: store i32 24, ptr %tuple.index1, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/operators/assignment.carbon b/toolchain/lower/testdata/operators/assignment.carbon index 6c0a7cb398fc..b8a0f34ea962 100644 --- a/toolchain/lower/testdata/operators/assignment.carbon +++ b/toolchain/lower/testdata/operators/assignment.carbon @@ -14,22 +14,14 @@ fn Main() { // CHECK:STDOUT: ; ModuleID = 'assignment.carbon' // CHECK:STDOUT: source_filename = "assignment.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define void @Main() { // CHECK:STDOUT: %a = alloca i32, align 4 // CHECK:STDOUT: store i32 12, ptr %a, align 4 // CHECK:STDOUT: store i32 9, ptr %a, align 4 -// CHECK:STDOUT: %tuple = alloca { %type, %type }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { %type, %type }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %1, align 1 -// CHECK:STDOUT: %2 = getelementptr inbounds { %type, %type }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store %type zeroinitializer, ptr %2, align 1 // CHECK:STDOUT: %b = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %3, align 4 -// CHECK:STDOUT: %4 = getelementptr inbounds { i32, i32 }, ptr %tuple1, i32 0, i32 1 -// CHECK:STDOUT: store i32 2, ptr %4, align 4 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %b, i32 0, i32 0 +// CHECK:STDOUT: store i32 1, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %b, i32 0, i32 1 +// CHECK:STDOUT: store i32 2, ptr %tuple.index1, align 4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/pointer/address_of_field.carbon b/toolchain/lower/testdata/pointer/address_of_field.carbon index 5c6eefb5957f..bce0c86a58ff 100644 --- a/toolchain/lower/testdata/pointer/address_of_field.carbon +++ b/toolchain/lower/testdata/pointer/address_of_field.carbon @@ -19,10 +19,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: %s = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %struct = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 0 +// CHECK:STDOUT: %a = getelementptr inbounds { i32, i32 }, ptr %s, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %a, align 4 -// CHECK:STDOUT: %b = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 1 +// CHECK:STDOUT: %b = getelementptr inbounds { i32, i32 }, ptr %s, i32 0, i32 1 // CHECK:STDOUT: store i32 2, ptr %b, align 4 // CHECK:STDOUT: %b1 = getelementptr inbounds { i32, i32 }, ptr %s, i32 0, i32 1 // CHECK:STDOUT: %1 = load ptr, ptr %b1, align 8 diff --git a/toolchain/lower/testdata/struct/empty.carbon b/toolchain/lower/testdata/struct/empty.carbon index d03c5949ca4d..49a834f65b0a 100644 --- a/toolchain/lower/testdata/struct/empty.carbon +++ b/toolchain/lower/testdata/struct/empty.carbon @@ -14,10 +14,7 @@ fn Run() -> i32 { // CHECK:STDOUT: source_filename = "empty.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %struct = alloca {}, align 8 // CHECK:STDOUT: %x = alloca {}, align 8 -// CHECK:STDOUT: %struct1 = alloca {}, align 8 -// CHECK:STDOUT: %struct2 = alloca {}, align 8 // CHECK:STDOUT: %y = alloca {}, align 8 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/member_access.carbon b/toolchain/lower/testdata/struct/member_access.carbon index c21f6c7e728d..7bc855c6e488 100644 --- a/toolchain/lower/testdata/struct/member_access.carbon +++ b/toolchain/lower/testdata/struct/member_access.carbon @@ -16,10 +16,9 @@ fn Run() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { // CHECK:STDOUT: %x = alloca { double, i32 }, align 8 -// CHECK:STDOUT: %struct = alloca { double, i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { double, i32 }, ptr %struct, i32 0, i32 0 +// CHECK:STDOUT: %a = getelementptr inbounds { double, i32 }, ptr %x, i32 0, i32 0 // CHECK:STDOUT: store double 0.000000e+00, ptr %a, align 8 -// CHECK:STDOUT: %b = getelementptr inbounds { double, i32 }, ptr %struct, i32 0, i32 1 +// CHECK:STDOUT: %b = getelementptr inbounds { double, i32 }, ptr %x, i32 0, i32 1 // CHECK:STDOUT: store i32 1, ptr %b, align 4 // CHECK:STDOUT: %y = alloca i32, align 4 // CHECK:STDOUT: %b1 = getelementptr inbounds { double, i32 }, ptr %x, i32 0, i32 1 diff --git a/toolchain/lower/testdata/struct/nested_struct.carbon b/toolchain/lower/testdata/struct/nested_struct.carbon index 2d1cc73229e8..b90af4d6a692 100644 --- a/toolchain/lower/testdata/struct/nested_struct.carbon +++ b/toolchain/lower/testdata/struct/nested_struct.carbon @@ -12,11 +12,6 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'nested_struct.carbon' // CHECK:STDOUT: source_filename = "nested_struct.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %struct = alloca { %type }, align 8 -// CHECK:STDOUT: %b = getelementptr inbounds { %type }, ptr %struct, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %b, align 1 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/one_entry.carbon b/toolchain/lower/testdata/struct/one_entry.carbon index 2c0679af396b..963c797805c2 100644 --- a/toolchain/lower/testdata/struct/one_entry.carbon +++ b/toolchain/lower/testdata/struct/one_entry.carbon @@ -15,13 +15,10 @@ fn Run() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { // CHECK:STDOUT: %x = alloca { i32 }, align 8 -// CHECK:STDOUT: %struct = alloca { i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %struct, i32 0, i32 0 -// CHECK:STDOUT: store i32 4, ptr %a, align 4 -// CHECK:STDOUT: %1 = load { i32 }, ptr %struct, align 4 -// CHECK:STDOUT: store { i32 } %1, ptr %x, align 4 +// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0 +// CHECK:STDOUT: store { i32 } { i32 4 }, ptr %x, align 4 // CHECK:STDOUT: %y = alloca { i32 }, align 8 -// CHECK:STDOUT: %2 = load { i32 }, ptr %x, align 4 -// CHECK:STDOUT: store { i32 } %2, ptr %y, align 4 +// CHECK:STDOUT: %1 = load { i32 }, ptr %x, align 4 +// CHECK:STDOUT: store { i32 } %1, ptr %y, align 4 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/two_entries.carbon b/toolchain/lower/testdata/struct/two_entries.carbon index 0dacc43f5799..bee9d133ecb0 100644 --- a/toolchain/lower/testdata/struct/two_entries.carbon +++ b/toolchain/lower/testdata/struct/two_entries.carbon @@ -15,10 +15,9 @@ fn Run() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { // CHECK:STDOUT: %x = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %struct = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %a = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 0 +// CHECK:STDOUT: %a = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 0 // CHECK:STDOUT: store i32 1, ptr %a, align 4 -// CHECK:STDOUT: %b = getelementptr inbounds { i32, i32 }, ptr %struct, i32 0, i32 1 +// CHECK:STDOUT: %b = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 1 // CHECK:STDOUT: store i32 2, ptr %b, align 4 // CHECK:STDOUT: %y = alloca { i32, i32 }, align 8 // CHECK:STDOUT: ret i32 0 diff --git a/toolchain/lower/testdata/tuple/empty.carbon b/toolchain/lower/testdata/tuple/empty.carbon index 3ec094587d74..b645586b98d7 100644 --- a/toolchain/lower/testdata/tuple/empty.carbon +++ b/toolchain/lower/testdata/tuple/empty.carbon @@ -14,10 +14,7 @@ fn Run() -> i32 { // CHECK:STDOUT: source_filename = "empty.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %tuple = alloca {}, align 8 // CHECK:STDOUT: %x = alloca {}, align 8 -// CHECK:STDOUT: %tuple1 = alloca {}, align 8 -// CHECK:STDOUT: %tuple2 = alloca {}, align 8 // CHECK:STDOUT: %y = alloca {}, align 8 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/tuple/nested_tuple.carbon b/toolchain/lower/testdata/tuple/nested_tuple.carbon index a19d257c819d..1e35308923aa 100644 --- a/toolchain/lower/testdata/tuple/nested_tuple.carbon +++ b/toolchain/lower/testdata/tuple/nested_tuple.carbon @@ -12,14 +12,6 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'nested_tuple.carbon' // CHECK:STDOUT: source_filename = "nested_tuple.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %tuple = alloca { %type }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { %type }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %1, align 1 -// CHECK:STDOUT: %tuple1 = alloca { { %type } }, align 8 -// CHECK:STDOUT: %2 = getelementptr inbounds { { %type } }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store ptr %tuple, ptr %2, align 8 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/tuple/one_entry.carbon b/toolchain/lower/testdata/tuple/one_entry.carbon index 039e21658b1d..d1b3dd06ff7e 100644 --- a/toolchain/lower/testdata/tuple/one_entry.carbon +++ b/toolchain/lower/testdata/tuple/one_entry.carbon @@ -13,23 +13,12 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'one_entry.carbon' // CHECK:STDOUT: source_filename = "one_entry.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %tuple = alloca { %type }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { %type }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %1, align 1 // CHECK:STDOUT: %x = alloca { i32 }, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32 }, align 8 -// CHECK:STDOUT: %2 = getelementptr inbounds { i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 1, ptr %2, align 4 -// CHECK:STDOUT: %3 = load { i32 }, ptr %tuple1, align 4 -// CHECK:STDOUT: store { i32 } %3, ptr %x, align 4 -// CHECK:STDOUT: %tuple2 = alloca { %type }, align 8 -// CHECK:STDOUT: %4 = getelementptr inbounds { %type }, ptr %tuple2, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %4, align 1 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0 +// CHECK:STDOUT: store { i32 } { i32 1 }, ptr %x, align 4 // CHECK:STDOUT: %y = alloca { i32 }, align 8 -// CHECK:STDOUT: %5 = load { i32 }, ptr %x, align 4 -// CHECK:STDOUT: store { i32 } %5, ptr %y, align 4 +// CHECK:STDOUT: %1 = load { i32 }, ptr %x, align 4 +// CHECK:STDOUT: store { i32 } %1, ptr %y, align 4 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/tuple/two_entries.carbon b/toolchain/lower/testdata/tuple/two_entries.carbon index 35b8c547a960..79993b999b54 100644 --- a/toolchain/lower/testdata/tuple/two_entries.carbon +++ b/toolchain/lower/testdata/tuple/two_entries.carbon @@ -13,25 +13,12 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon' // CHECK:STDOUT: source_filename = "two_entries.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: %type = type {} -// CHECK:STDOUT: // CHECK:STDOUT: define i32 @Run() { -// CHECK:STDOUT: %tuple = alloca { %type, %type }, align 8 -// CHECK:STDOUT: %1 = getelementptr inbounds { %type, %type }, ptr %tuple, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %1, align 1 -// CHECK:STDOUT: %2 = getelementptr inbounds { %type, %type }, ptr %tuple, i32 0, i32 1 -// CHECK:STDOUT: store %type zeroinitializer, ptr %2, align 1 // CHECK:STDOUT: %x = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %tuple1 = alloca { i32, i32 }, align 8 -// CHECK:STDOUT: %3 = getelementptr inbounds { i32, i32 }, ptr %tuple1, i32 0, i32 0 -// CHECK:STDOUT: store i32 12, ptr %3, align 4 -// CHECK:STDOUT: %4 = getelementptr inbounds { i32, i32 }, ptr %tuple1, i32 0, i32 1 -// CHECK:STDOUT: store i32 7, ptr %4, align 4 -// CHECK:STDOUT: %tuple2 = alloca { %type, %type }, align 8 -// CHECK:STDOUT: %5 = getelementptr inbounds { %type, %type }, ptr %tuple2, i32 0, i32 0 -// CHECK:STDOUT: store %type zeroinitializer, ptr %5, align 1 -// CHECK:STDOUT: %6 = getelementptr inbounds { %type, %type }, ptr %tuple2, i32 0, i32 1 -// CHECK:STDOUT: store %type zeroinitializer, ptr %6, align 1 +// CHECK:STDOUT: %tuple.index = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 0 +// CHECK:STDOUT: store i32 12, ptr %tuple.index, align 4 +// CHECK:STDOUT: %tuple.index1 = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 1 +// CHECK:STDOUT: store i32 7, ptr %tuple.index1, align 4 // CHECK:STDOUT: %y = alloca { i32, i32 }, align 8 // CHECK:STDOUT: ret i32 0 // CHECK:STDOUT: } diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index c297edcfdc5f..67417cdd0376 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -220,11 +220,15 @@ static auto GetTypePrecedence(NodeKind kind) -> int { case NodeKind::StructAccess: case NodeKind::StructTypeField: case NodeKind::StructLiteral: + case NodeKind::StructInit: + case NodeKind::StructValue: case NodeKind::StubReference: case NodeKind::Temporary: case NodeKind::TemporaryStorage: case NodeKind::TupleIndex: case NodeKind::TupleLiteral: + case NodeKind::TupleInit: + case NodeKind::TupleValue: case NodeKind::UnaryOperatorNot: case NodeKind::VarStorage: CARBON_FATAL() << "GetTypePrecedence for non-type node kind " << kind; @@ -382,11 +386,15 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const case NodeKind::StringLiteral: case NodeKind::StructAccess: case NodeKind::StructLiteral: + case NodeKind::StructInit: + case NodeKind::StructValue: case NodeKind::StubReference: case NodeKind::Temporary: case NodeKind::TemporaryStorage: case NodeKind::TupleIndex: case NodeKind::TupleLiteral: + case NodeKind::TupleInit: + case NodeKind::TupleValue: case NodeKind::UnaryOperatorNot: case NodeKind::VarStorage: // We don't need to handle stringification for nodes that don't show up @@ -454,7 +462,9 @@ auto GetExpressionCategory(const File& file, NodeId node_id) case NodeKind::PointerType: case NodeKind::RealLiteral: case NodeKind::StringLiteral: + case NodeKind::StructValue: case NodeKind::StructType: + case NodeKind::TupleValue: case NodeKind::TupleType: case NodeKind::UnaryOperatorNot: return ExpressionCategory::Value; @@ -484,15 +494,13 @@ auto GetExpressionCategory(const File& file, NodeId node_id) case NodeKind::StructLiteral: case NodeKind::TupleLiteral: - // TODO: Eventually these will depend on the context in which the value - // is used, and could be either Value or Initializing. We may want - // different node kinds for a struct/tuple initializer versus a - // struct/tuple value construction. - return ExpressionCategory::Value; + return ExpressionCategory::Mixed; case NodeKind::ArrayInit: case NodeKind::Call: case NodeKind::InitializeFrom: + case NodeKind::StructInit: + case NodeKind::TupleInit: return ExpressionCategory::Initializing; case NodeKind::Dereference: @@ -542,10 +550,14 @@ auto GetValueRepresentation(const File& file, TypeId type_id) case NodeKind::StructAccess: case NodeKind::StructTypeField: case NodeKind::StructLiteral: + case NodeKind::StructInit: + case NodeKind::StructValue: case NodeKind::Temporary: case NodeKind::TemporaryStorage: case NodeKind::TupleIndex: case NodeKind::TupleLiteral: + case NodeKind::TupleInit: + case NodeKind::TupleValue: case NodeKind::UnaryOperatorNot: case NodeKind::VarStorage: CARBON_FATAL() << "Type refers to non-type node " << node; diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 78a80283f317..a0cba077826a 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -385,6 +385,11 @@ enum class ExpressionCategory : int8_t { // This node represents an initializing expression, that describes how to // initialize an object. Initializing, + // This node represents a syntactic combination of expressions that are + // permitted to have different expression categories. This is used for tuple + // and struct literals, where the subexpressions for different elements can + // have different categories. + Mixed, }; // Returns the expression category for a node. diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index cd0f3ebb094c..cf6b3064a6c3 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -519,6 +519,7 @@ class Formatter { switch (GetExpressionCategory(semantics_ir_, node_id)) { case ExpressionCategory::NotExpression: case ExpressionCategory::Value: + case ExpressionCategory::Mixed: break; case ExpressionCategory::DurableReference: case ExpressionCategory::EphemeralReference: diff --git a/toolchain/sem_ir/node.h b/toolchain/sem_ir/node.h index b4e13070eb69..0b35da7c1285 100644 --- a/toolchain/sem_ir/node.h +++ b/toolchain/sem_ir/node.h @@ -407,6 +407,9 @@ class Node : public Printable { using StructAccess = Factory; + using StructInit = Factory; + using StructLiteral = Factory; @@ -416,6 +419,9 @@ class Node : public Printable { FactoryNoType; + using StructValue = Factory; + using StubReference = Factory; using Temporary = @@ -426,10 +432,16 @@ class Node : public Printable { using TupleIndex = Factory; + using TupleInit = Factory; + using TupleLiteral = Factory; using TupleType = Factory; + using TupleValue = Factory; + using UnaryOperatorNot = Factory; diff --git a/toolchain/sem_ir/node_kind.def b/toolchain/sem_ir/node_kind.def index 614264f57a78..4fc51e3a5f26 100644 --- a/toolchain/sem_ir/node_kind.def +++ b/toolchain/sem_ir/node_kind.def @@ -75,26 +75,31 @@ CARBON_SEMANTICS_NODE_KIND_IMPL(Parameter, "parameter", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(PointerType, "ptr_type", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(RealLiteral, "real_literal", Typed, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(Return, "return", None, Terminator) CARBON_SEMANTICS_NODE_KIND_IMPL(ReturnExpression, "return", None, Terminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(Return, "return", None, Terminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StringLiteral, "string_literal", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StructAccess, "struct_access", Typed, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(StructType, "struct_type", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(StructInit, "struct_init", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(StructLiteral, "struct_literal", Typed, + NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StructTypeField, "struct_type_field", None, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(StructLiteral, "struct_literal", Typed, +CARBON_SEMANTICS_NODE_KIND_IMPL(StructType, "struct_type", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(StructValue, "struct_value", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(StubReference, "stub_reference", Typed, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(Temporary, "temporary", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(TemporaryStorage, "temporary_storage", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(Temporary, "temporary", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(TupleIndex, "tuple_index", Typed, NotTerminator) -CARBON_SEMANTICS_NODE_KIND_IMPL(TupleType, "tuple_type", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(TupleInit, "tuple_init", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(TupleLiteral, "tuple_literal", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(TupleType, "tuple_type", Typed, NotTerminator) +CARBON_SEMANTICS_NODE_KIND_IMPL(TupleValue, "tuple_value", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(UnaryOperatorNot, "not", Typed, NotTerminator) CARBON_SEMANTICS_NODE_KIND_IMPL(VarStorage, "var", Typed, NotTerminator)