mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:30:12 +01:00
The parse nodes are still tracked as part of the same value store interface in order to ensure parity, but they're split out from Inst itself in order to reduce the size of Inst -- the expectation is that they don't need to be passed around quite as much. This change doesn't actually reduce the passing very much, although there are hints of it: AddInstAndPush doesn't typically need a separate parse node from the one on the Inst itself, for example. In a couple spots I changed code to rely a little more on the InstId until the ParseNode is needed, but it's very low hanging fruit where done. I think convert could do more to not eagerly fetch the parse node before its use, but more cleanup felt it would be easier to handle separately. I'm currently viewing this as making such cleanup _possible_ rather than executing on it up-front. But also, I want to make sure there's a consensus to head in this direction before pulling the trigger. We speculated that this would result in the parse node being passed around less, and I do think that's the case, although it's a bit fuzzy in the change. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
121 lines
5.3 KiB
C++
121 lines
5.3 KiB
C++
// 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
|
|
|
|
#include "llvm/ADT/APSInt.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/convert.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
auto HandleIndexExprStart(Context& /*context*/,
|
|
Parse::IndexExprStartId /*parse_node*/) -> bool {
|
|
// Leave the expression on the stack for IndexExpr.
|
|
return true;
|
|
}
|
|
|
|
// Validates that the index (required to be an IntLiteral) is valid within
|
|
// the array or tuple size. Returns the index on success, or nullptr on failure.
|
|
static auto ValidateIntLiteralBound(Context& context, Parse::NodeId parse_node,
|
|
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(IndexOutOfBounds, Error,
|
|
"Index `{0}` is past the end of type `{1}`.",
|
|
llvm::APSInt, std::string);
|
|
context.emitter().Emit(
|
|
parse_node, IndexOutOfBounds,
|
|
llvm::APSInt(index_val, /*isUnsigned=*/true),
|
|
context.sem_ir().StringifyType(operand_inst.type_id()));
|
|
return nullptr;
|
|
}
|
|
return &index_val;
|
|
}
|
|
|
|
auto HandleIndexExpr(Context& context, Parse::IndexExprId parse_node) -> bool {
|
|
auto index_inst_id = context.node_stack().PopExpr();
|
|
auto index_inst = context.insts().Get(index_inst_id);
|
|
auto operand_inst_id = context.node_stack().PopExpr();
|
|
operand_inst_id = ConvertToValueOrRefExpr(context, operand_inst_id);
|
|
auto operand_inst = context.insts().Get(operand_inst_id);
|
|
auto operand_type_id = operand_inst.type_id();
|
|
auto operand_type_inst = context.types().GetAsInst(operand_type_id);
|
|
|
|
switch (operand_type_inst.kind()) {
|
|
case SemIR::ArrayType::Kind: {
|
|
auto array_type = operand_type_inst.As<SemIR::ArrayType>();
|
|
auto index_parse_node = context.insts().GetParseNode(index_inst_id);
|
|
// We can check whether integers are in-bounds, although it doesn't affect
|
|
// the IR for an array.
|
|
if (auto index_literal = index_inst.TryAs<SemIR::IntLiteral>();
|
|
index_literal &&
|
|
!ValidateIntLiteralBound(
|
|
context, parse_node, operand_inst, *index_literal,
|
|
context.sem_ir().GetArrayBoundValue(array_type.bound_id))) {
|
|
index_inst_id = SemIR::InstId::BuiltinError;
|
|
}
|
|
auto cast_index_id = ConvertToValueOfType(
|
|
context, index_parse_node, index_inst_id,
|
|
context.GetBuiltinType(SemIR::BuiltinKind::IntType));
|
|
auto array_cat =
|
|
SemIR::GetExprCategory(context.sem_ir(), operand_inst_id);
|
|
if (array_cat == SemIR::ExprCategory::Value) {
|
|
// If the operand is an array value, convert it to an ephemeral
|
|
// reference to an array so we can perform a primitive indexing into it.
|
|
operand_inst_id = context.AddInst(
|
|
{parse_node, SemIR::ValueAsRef{operand_type_id, operand_inst_id}});
|
|
}
|
|
auto elem_id = context.AddInst(
|
|
{parse_node, SemIR::ArrayIndex{array_type.element_type_id,
|
|
operand_inst_id, cast_index_id}});
|
|
if (array_cat != SemIR::ExprCategory::DurableRef) {
|
|
// Indexing a durable reference gives a durable reference expression.
|
|
// Indexing anything else gives a value expression.
|
|
// TODO: This should be replaced by a choice between using `IndexWith`
|
|
// and `IndirectIndexWith`.
|
|
elem_id = ConvertToValueExpr(context, elem_id);
|
|
}
|
|
context.node_stack().Push(parse_node, elem_id);
|
|
return true;
|
|
}
|
|
case SemIR::TupleType::Kind: {
|
|
SemIR::TypeId element_type_id = SemIR::TypeId::Error;
|
|
if (auto index_literal = index_inst.TryAs<SemIR::IntLiteral>()) {
|
|
auto type_block = context.type_blocks().Get(
|
|
operand_type_inst.As<SemIR::TupleType>().elements_id);
|
|
if (const auto* index_val =
|
|
ValidateIntLiteralBound(context, parse_node, operand_inst,
|
|
*index_literal, type_block.size())) {
|
|
element_type_id = type_block[index_val->getZExtValue()];
|
|
} else {
|
|
index_inst_id = SemIR::InstId::BuiltinError;
|
|
}
|
|
} else if (index_inst.type_id() != SemIR::TypeId::Error) {
|
|
CARBON_DIAGNOSTIC(TupleIndexIntLiteral, Error,
|
|
"Tuples indices must be integer literals.");
|
|
context.emitter().Emit(parse_node, TupleIndexIntLiteral);
|
|
index_inst_id = SemIR::InstId::BuiltinError;
|
|
}
|
|
context.AddInstAndPush(
|
|
{parse_node,
|
|
SemIR::TupleIndex{element_type_id, operand_inst_id, index_inst_id}});
|
|
return true;
|
|
}
|
|
default: {
|
|
if (operand_type_id != SemIR::TypeId::Error) {
|
|
CARBON_DIAGNOSTIC(TypeNotIndexable, Error,
|
|
"Type `{0}` does not support indexing.", std::string);
|
|
context.emitter().Emit(parse_node, TypeNotIndexable,
|
|
context.sem_ir().StringifyType(operand_type_id));
|
|
}
|
|
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Check
|