@@ -1689,6 +1689,76 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
16891689
16901690 case SemIR::BuiltinFunctionKind::PrintChar:
16911691 case SemIR::BuiltinFunctionKind::PrintInt:
1692+ case SemIR::BuiltinFunctionKind::StringAt:{
1693+ // We need both the string and index to be constant
1694+ Phase phase = Phase::Concrete;
1695+ auto str_id = GetConstantValue (eval_context, arg_ids[0 ], &phase);
1696+ auto index_id = GetConstantValue (eval_context, arg_ids[1 ], &phase);
1697+ // If either isnt constant, we can't evaluate it at compile time
1698+ if (!str_id.has_value () || !index_id.has_value ()) {
1699+ return MakeNonConstantResult (phase);
1700+ }
1701+
1702+ auto str_struct = eval_context.insts ().TryGetAs <SemIR::StructValue>(str_id);
1703+ if (!str_struct) {
1704+ return MakeNonConstantResult (phase);
1705+ }
1706+
1707+ auto elements = eval_context.inst_blocks ().Get (str_struct->elements_id );
1708+ if (elements.size () != 2 ) {
1709+ return MakeNonConstantResult (phase);
1710+ }
1711+
1712+ auto ptr_const_id = eval_context.constant_values ().Get (elements[0 ]);
1713+ auto string_literal = eval_context.insts ().TryGetAs <SemIR::StringLiteral>(
1714+ eval_context.constant_values ().GetInstId (ptr_const_id));
1715+ if (!string_literal){
1716+ return MakeNonConstantResult (phase);
1717+ }
1718+
1719+ auto string_value = eval_context.sem_ir ().string_literal_values ().Get (
1720+ string_literal->string_literal_id );
1721+ // Get index value
1722+ auto index_inst = eval_context.insts ().TryGetAs <SemIR::IntValue>(index_id);
1723+ if (!index_inst) {
1724+ CARBON_CHECK (phase != Phase::Concrete, " Concrete constant integer should be a literal" );
1725+ return MakeNonConstantResult (phase);
1726+ }
1727+
1728+ const auto & index_val = eval_context.ints ().Get (index_inst->int_id );
1729+
1730+ // Check bounds
1731+ if (index_val.isNegative ()){
1732+ CARBON_DIAGNOSTIC (ArrayIndexNegative, Error, " index `{0}` is negative" ,
1733+ TypedInt);
1734+ eval_context.emitter ().Emit (
1735+ eval_context.GetDiagnosticLoc (index_id), ArrayIndexNegative,
1736+ {.type = eval_context.insts ().Get (index_id).type_id (),
1737+ .value = index_val});
1738+ return SemIR::ErrorInst::ConstantId;
1739+ }
1740+
1741+ // Check for out of bounds
1742+ if (index_val.getActiveBits () > 64 ||
1743+ index_val.getZExtValue () >= string_value.size ()) {
1744+ CARBON_DIAGNOSTIC (ArrayIndexOutOfBounds, Error,
1745+ " string index `{0}` is past the end of the string" ,
1746+ TypedInt);
1747+ eval_context.emitter ().Emit (
1748+ eval_context.GetDiagnosticLoc (index_id), ArrayIndexOutOfBounds,
1749+ {.type = eval_context.insts ().Get (index_id).type_id (),
1750+ .value = index_val});
1751+ return SemIR::ErrorInst::ConstantId;
1752+ }
1753+
1754+ auto char_value = static_cast <uint8_t >(string_value[index_val.getZExtValue ()]);
1755+
1756+ auto int_id = eval_context.ints ().Add (llvm::APSInt (llvm::APInt (32 , char_value), /* isUnsigned=*/ false ));
1757+ return MakeConstantResult (eval_context.context (),
1758+ SemIR::IntValue{.type_id = call.type_id , .int_id = int_id},
1759+ phase);
1760+
1761+ }
16921762 case SemIR::BuiltinFunctionKind::ReadChar:
16931763 case SemIR::BuiltinFunctionKind::FloatAddAssign:
16941764 case SemIR::BuiltinFunctionKind::FloatSubAssign:
0 commit comments