Skip to content

Commit

Permalink
change tuple index
Browse files Browse the repository at this point in the history
  • Loading branch information
brymer-meneses committed Aug 15, 2024
1 parent 3f7af84 commit 41c5b56
Show file tree
Hide file tree
Showing 39 changed files with 356 additions and 432 deletions.
57 changes: 2 additions & 55 deletions toolchain/check/handle_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,12 @@

namespace Carbon::Check {

auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/)
-> bool {
auto HandleParseNode(Context& /*context*/,
Parse::IndexExprStartId /*node_id*/) -> bool {
// Leave the expression on the stack for IndexExpr.
return true;
}

// Validates that the index (required to be an IntLiteral) is valid within the
// tuple size. Returns the index on success, or nullptr on failure.
static auto ValidateTupleIndex(Context& context, Parse::NodeId node_id,
SemIR::Inst operand_inst,
SemIR::IntLiteral index_inst, int size)
-> const llvm::APInt* {
const auto& index_val = context.ints().Get(index_inst.int_id);
if (index_val.uge(size)) {
CARBON_DIAGNOSTIC(
TupleIndexOutOfBounds, Error,
"Tuple element index `{0}` is past the end of type `{1}`.", TypedInt,
SemIR::TypeId);
context.emitter().Emit(node_id, TupleIndexOutOfBounds,
{.type = index_inst.type_id, .value = index_val},
operand_inst.type_id());
return nullptr;
}
return &index_val;
}

auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
auto index_inst_id = context.node_stack().PopExpr();
auto operand_inst_id = context.node_stack().PopExpr();
Expand Down Expand Up @@ -72,39 +52,6 @@ auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
context.node_stack().Push(node_id, elem_id);
return true;
}
case CARBON_KIND(SemIR::TupleType tuple_type): {
SemIR::TypeId element_type_id = SemIR::TypeId::Error;
auto index_node_id = context.insts().GetLocId(index_inst_id);
index_inst_id = ConvertToValueOfType(
context, index_node_id, index_inst_id,
context.GetBuiltinType(SemIR::BuiltinInstKind::IntType));
auto index_const_id = context.constant_values().Get(index_inst_id);
if (index_const_id == SemIR::ConstantId::Error) {
index_inst_id = SemIR::InstId::BuiltinError;
} else if (!index_const_id.is_template()) {
// TODO: Decide what to do if the index is a symbolic constant.
CARBON_DIAGNOSTIC(TupleIndexNotConstant, Error,
"Tuple index must be a constant.");
context.emitter().Emit(node_id, TupleIndexNotConstant);
index_inst_id = SemIR::InstId::BuiltinError;
} else {
auto index_literal = context.insts().GetAs<SemIR::IntLiteral>(
context.constant_values().GetInstId(index_const_id));
auto type_block = context.type_blocks().Get(tuple_type.elements_id);
if (const auto* index_val =
ValidateTupleIndex(context, node_id, operand_inst,
index_literal, type_block.size())) {
element_type_id = type_block[index_val->getZExtValue()];
} else {
index_inst_id = SemIR::InstId::BuiltinError;
}
}
context.AddInstAndPush<SemIR::TupleIndex>(node_id,
{.type_id = element_type_id,
.tuple_id = operand_inst_id,
.index_id = index_inst_id});
return true;
}
default: {
if (operand_type_id != SemIR::TypeId::Error) {
CARBON_DIAGNOSTIC(TypeNotIndexable, Error,
Expand Down
53 changes: 37 additions & 16 deletions toolchain/check/handle_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@

namespace Carbon::Check {

auto HandleParseNode(Context& context, Parse::MemberAccessExprId node_id)
-> bool {
if (context.node_stack().PeekIs<Parse::NodeKind::ParenExpr>()) {
auto HandleParseNode(Context& context,
Parse::MemberAccessExprId node_id) -> bool {
auto node_kind = context.node_stack().PeekNodeKind();

if (node_kind == Parse::NodeKind::ParenExpr) {
auto member_expr_id = context.node_stack().PopExpr();
auto base_id = context.node_stack().PopExpr();
auto member_id =
PerformCompoundMemberAccess(context, node_id, base_id, member_expr_id);
context.node_stack().Push(node_id, member_id);
} else if (node_kind == Parse::NodeKind::IntLiteral) {
auto index_inst_id = context.node_stack().PopExpr();
auto tuple_inst_id = context.node_stack().PopExpr();

auto tuple_value_inst_id =
PerformTupleIndex(context, node_id, tuple_inst_id, index_inst_id);

context.node_stack().Push(node_id, tuple_value_inst_id);
} else {
SemIR::NameId name_id = context.node_stack().PopName();
auto base_id = context.node_stack().PopExpr();
Expand All @@ -31,8 +41,8 @@ auto HandleParseNode(Context& context, Parse::MemberAccessExprId node_id)
return true;
}

auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id)
-> bool {
auto HandleParseNode(Context& context,
Parse::PointerMemberAccessExprId node_id) -> bool {
auto diagnose_not_pointer = [&context,
&node_id](SemIR::TypeId not_pointer_type_id) {
CARBON_DIAGNOSTIC(ArrowOperatorOfNonPointer, Error,
Expand All @@ -44,14 +54,25 @@ auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id)
builder.Emit();
};

if (context.node_stack().PeekIs<Parse::NodeKind::ParenExpr>()) {
auto node_kind = context.node_stack().PeekNodeKind();

if (node_kind == Parse::NodeKind::ParenExpr) {
auto member_expr_id = context.node_stack().PopExpr();
auto base_id = context.node_stack().PopExpr();
auto deref_base_id = PerformPointerDereference(context, node_id, base_id,
diagnose_not_pointer);
auto member_id = PerformCompoundMemberAccess(context, node_id,
deref_base_id, member_expr_id);
context.node_stack().Push(node_id, member_id);
} else if (node_kind == Parse::NodeKind::IntLiteral) {
auto index_inst_id = context.node_stack().PopExpr();
auto tuple_pointer_inst_id = context.node_stack().PopExpr();
auto tuple_inst_id = PerformPointerDereference(
context, node_id, tuple_pointer_inst_id, diagnose_not_pointer);
auto tuple_value_inst_id =
PerformTupleIndex(context, node_id, tuple_inst_id, index_inst_id);

context.node_stack().Push(node_id, tuple_value_inst_id);
} else {
SemIR::NameId name_id = context.node_stack().PopName();
auto base_id = context.node_stack().PopExpr();
Expand Down Expand Up @@ -101,8 +122,8 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id,
return true;
}

auto HandleParseNode(Context& context, Parse::IdentifierNameId node_id)
-> bool {
auto HandleParseNode(Context& context,
Parse::IdentifierNameId node_id) -> bool {
// The parent is responsible for binding the name.
auto name_id = GetIdentifierAsName(context, node_id);
if (!name_id) {
Expand All @@ -112,8 +133,8 @@ auto HandleParseNode(Context& context, Parse::IdentifierNameId node_id)
return true;
}

auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id)
-> bool {
auto HandleParseNode(Context& context,
Parse::IdentifierNameExprId node_id) -> bool {
auto name_id = GetIdentifierAsName(context, node_id);
if (!name_id) {
return context.TODO(node_id, "Error recovery from keyword name.");
Expand All @@ -126,8 +147,8 @@ auto HandleParseNode(Context& context, Parse::BaseNameId node_id) -> bool {
return true;
}

auto HandleParseNode(Context& context, Parse::SelfTypeNameExprId node_id)
-> bool {
auto HandleParseNode(Context& context,
Parse::SelfTypeNameExprId node_id) -> bool {
return HandleNameAsExpr(context, node_id, SemIR::NameId::SelfType);
}

Expand All @@ -136,13 +157,13 @@ auto HandleParseNode(Context& context, Parse::SelfValueNameId node_id) -> bool {
return true;
}

auto HandleParseNode(Context& context, Parse::SelfValueNameExprId node_id)
-> bool {
auto HandleParseNode(Context& context,
Parse::SelfValueNameExprId node_id) -> bool {
return HandleNameAsExpr(context, node_id, SemIR::NameId::SelfValue);
}

auto HandleParseNode(Context& context, Parse::NameQualifierId /*node_id*/)
-> bool {
auto HandleParseNode(Context& context,
Parse::NameQualifierId /*node_id*/) -> bool {
context.decl_name_stack().ApplyNameQualifier(PopNameComponent(context));
return true;
}
Expand Down
90 changes: 80 additions & 10 deletions toolchain/check/member_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,9 @@ static auto ScopeNeedsImplLookup(Context& context, LookupScope scope) -> bool {
// Given a type and an interface, searches for an impl that describes how that
// type implements that interface, and returns the corresponding witness.
// Returns an invalid InstId if no matching impl is found.
static auto LookupInterfaceWitness(Context& context,
SemIR::ConstantId type_const_id,
SemIR::TypeId interface_type_id)
-> SemIR::InstId {
static auto LookupInterfaceWitness(
Context& context, SemIR::ConstantId type_const_id,
SemIR::TypeId interface_type_id) -> SemIR::InstId {
// TODO: Add a better impl lookup system. At the very least, we should only be
// considering impls that are for the same interface we're querying. We can
// also skip impls that mention any types that aren't part of our impl query.
Expand Down Expand Up @@ -309,9 +308,29 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
}
}

// Validates that the index (required to be an IntLiteral) is valid within the
// tuple size. Returns the index on success, or nullptr on failure.
static auto ValidateTupleIndex(Context& context, Parse::NodeId node_id,
SemIR::Inst operand_inst,
SemIR::IntLiteral index_inst,
int size) -> const llvm::APInt* {
const auto& index_val = context.ints().Get(index_inst.int_id);
if (index_val.uge(size)) {
CARBON_DIAGNOSTIC(
TupleIndexOutOfBounds, Error,
"Tuple element index `{0}` is past the end of type `{1}`.", TypedInt,
SemIR::TypeId);
context.emitter().Emit(node_id, TupleIndexOutOfBounds,
{.type = index_inst.type_id, .value = index_val},
operand_inst.type_id());
return nullptr;
}
return &index_val;
}

auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
SemIR::InstId base_id, SemIR::NameId name_id)
-> SemIR::InstId {
SemIR::InstId base_id,
SemIR::NameId name_id) -> SemIR::InstId {
// If the base is a name scope, such as a class or namespace, perform lookup
// into that scope.
if (auto base_const_id = context.constant_values().Get(base_id);
Expand Down Expand Up @@ -385,10 +404,9 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
return member_id;
}

auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
SemIR::InstId base_id,
SemIR::InstId member_expr_id)
-> SemIR::InstId {
auto PerformCompoundMemberAccess(
Context& context, Parse::NodeId node_id, SemIR::InstId base_id,
SemIR::InstId member_expr_id) -> SemIR::InstId {
// Materialize a temporary for the base expression if necessary.
base_id = ConvertToValueOrRefExpr(context, base_id);
auto base_type_id = context.insts().Get(base_id).type_id();
Expand All @@ -403,6 +421,9 @@ auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
member.type_id())) {
member_id = PerformImplLookup(context, node_id, base_type_const_id,
*assoc_type, member_id);
} else if (context.insts().Is<SemIR::TupleType>(
context.constant_values().GetInstId(base_type_const_id))) {
return PerformTupleIndex(context, node_id, base_id, member_expr_id);
}

// Perform instance binding if we found an instance member.
Expand All @@ -422,4 +443,53 @@ auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
return member_id;
}

auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
SemIR::InstId tuple_inst_id,
SemIR::InstId index_inst_id) -> SemIR::InstId {
tuple_inst_id = ConvertToValueOrRefExpr(context, tuple_inst_id);
auto tuple_inst = context.insts().Get(tuple_inst_id);
auto tuple_type_id = tuple_inst.type_id();

auto tuple_type = context.types().TryGetAs<SemIR::TupleType>(tuple_type_id);
if (!tuple_type) {
CARBON_DIAGNOSTIC(TupleIndexOnANonTupleType, Error,
"Type `{0}` does not support tuple indexing. Only "
"tuples can be indexed that way.",
SemIR::TypeId);
context.emitter().Emit(node_id, TupleIndexOnANonTupleType, tuple_type_id);
return SemIR::InstId::BuiltinError;
}

SemIR::TypeId element_type_id = SemIR::TypeId::Error;
auto index_node_id = context.insts().GetLocId(index_inst_id);
index_inst_id = ConvertToValueOfType(
context, index_node_id, index_inst_id,
context.GetBuiltinType(SemIR::BuiltinInstKind::IntType));
auto index_const_id = context.constant_values().Get(index_inst_id);
if (index_const_id == SemIR::ConstantId::Error) {
index_inst_id = SemIR::InstId::BuiltinError;
} else if (!index_const_id.is_template()) {
// TODO: Decide what to do if the index is a symbolic constant.
CARBON_DIAGNOSTIC(TupleIndexNotConstant, Error,
"Tuple index must be a constant.");
context.emitter().Emit(node_id, TupleIndexNotConstant);
index_inst_id = SemIR::InstId::BuiltinError;
} else {
auto index_literal = context.insts().GetAs<SemIR::IntLiteral>(
context.constant_values().GetInstId(index_const_id));
auto type_block = context.type_blocks().Get(tuple_type->elements_id);
if (const auto* index_val = ValidateTupleIndex(
context, node_id, tuple_inst, index_literal, type_block.size())) {
element_type_id = type_block[index_val->getZExtValue()];
} else {
index_inst_id = SemIR::InstId::BuiltinError;
}
}

return context.AddInst<SemIR::TupleIndex>(node_id,
{.type_id = element_type_id,
.tuple_id = tuple_inst_id,
.index_id = index_inst_id});
}

} // namespace Carbon::Check
10 changes: 8 additions & 2 deletions toolchain/check/member_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace Carbon::Check {
// Creates SemIR to perform a member access with base expression `base_id` and
// member name `name_id`. Returns the result of the access.
auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
SemIR::InstId base_id, SemIR::NameId name_id)
-> SemIR::InstId;
SemIR::InstId base_id,
SemIR::NameId name_id) -> SemIR::InstId;

// Creates SemIR to perform a compound member access with base expression
// `base_id` and member name expression `member_expr_id`. Returns the result of
Expand All @@ -23,6 +23,12 @@ auto PerformCompoundMemberAccess(Context& context, Parse::NodeId node_id,
SemIR::InstId base_id,
SemIR::InstId member_expr_id) -> SemIR::InstId;

// Creates SemIR to perform a tuple index with base expression `tuple_inst_id`
// and index expression `index_inst_id`. Returns the result of the access.
auto PerformTupleIndex(Context& context, Parse::NodeId node_id,
SemIR::InstId tuple_inst_id,
SemIR::InstId index_inst_id) -> SemIR::InstId;

} // namespace Carbon::Check

#endif // CARBON_TOOLCHAIN_CHECK_MEMBER_ACCESS_H_
6 changes: 3 additions & 3 deletions toolchain/check/testdata/eval/aggregate.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var tuple_copy: (i32, i32) = (1, 2) as (i32, i32);

var struct_copy: {.a: i32, .b: i32, .c: i32} = {.c = 3, .b = 2, .a = 1} as {.b: i32, .a: i32, .c: i32};

var tuple_index: [i32; 1] = (0,) as [i32; (5, 7, 1, 9)[2]];
var tuple_index: [i32; 1] = (0,) as [i32; (5, 7, 1, 9).2];

var struct_access: [i32; 1] = (0,) as [i32; {.a = 3, .b = 1}.b];

Expand Down Expand Up @@ -174,10 +174,10 @@ var struct_access: [i32; 1] = (0,) as [i32; {.a = 3, .b = 1}.b];
// CHECK:STDOUT: %.loc15_56: i32 = int_literal 2 [template = constants.%.6]
// CHECK:STDOUT: %tuple: %.20 = tuple_value (%.loc15_44, %.loc15_47, %.loc15_50, %.loc15_53) [template = constants.%tuple.2]
// CHECK:STDOUT: %.loc15_54.2: %.20 = converted %.loc15_54.1, %tuple [template = constants.%tuple.2]
// CHECK:STDOUT: %.loc15_57: i32 = tuple_index %.loc15_54.2, %.loc15_56 [template = constants.%.5]
// CHECK:STDOUT: %.loc15_55: i32 = tuple_index %.loc15_54.2, %.loc15_56 [template = constants.%.5]
// CHECK:STDOUT: %.loc15_38.1: type = value_of_initializer %int.make_type_32.loc15 [template = i32]
// CHECK:STDOUT: %.loc15_38.2: type = converted %int.make_type_32.loc15, %.loc15_38.1 [template = i32]
// CHECK:STDOUT: %.loc15_58: type = array_type %.loc15_57, i32 [template = constants.%.13]
// CHECK:STDOUT: %.loc15_57: type = array_type %.loc15_55, i32 [template = constants.%.13]
// CHECK:STDOUT: %.loc15_5: ref %.13 = splice_block file.%tuple_index.var {}
// CHECK:STDOUT: %.loc15_32.2: i32 = int_literal 0 [template = constants.%.15]
// CHECK:STDOUT: %.loc15_32.3: ref i32 = array_index %.loc15_5, %.loc15_32.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn G() -> (i32, i32) {
}

fn H() -> i32 {
return G()[0];
return G().0;
}

// CHECK:STDOUT: --- in_place_tuple_init.carbon
Expand Down Expand Up @@ -128,8 +128,8 @@ fn H() -> i32 {
// CHECK:STDOUT: %G.call: init %.3 = call %G.ref() to %.loc20_11.1
// CHECK:STDOUT: %.loc20_14: i32 = int_literal 0 [template = constants.%.5]
// CHECK:STDOUT: %.loc20_11.2: ref %.3 = temporary %.loc20_11.1, %G.call
// CHECK:STDOUT: %.loc20_15.1: ref i32 = tuple_index %.loc20_11.2, %.loc20_14
// CHECK:STDOUT: %.loc20_15.2: i32 = bind_value %.loc20_15.1
// CHECK:STDOUT: return %.loc20_15.2
// CHECK:STDOUT: %.loc20_13.1: ref i32 = tuple_index %.loc20_11.2, %.loc20_14
// CHECK:STDOUT: %.loc20_13.2: i32 = bind_value %.loc20_13.1
// CHECK:STDOUT: return %.loc20_13.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
Loading

0 comments on commit 41c5b56

Please sign in to comment.