-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Added string indexing #6329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Added string indexing #6329
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1687,6 +1687,39 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context, | |||||
| return context.constant_values().Get(arg_ids[0]); | ||||||
| } | ||||||
|
|
||||||
| case SemIR::BuiltinFunctionKind::StringAt: { | ||||||
| Phase phase = Phase::Concrete; | ||||||
| auto str_id = GetConstantValue(eval_context, arg_ids[0], &phase); | ||||||
| auto index_id = GetConstantValue(eval_context, arg_ids[1], &phase); | ||||||
|
|
||||||
| if (phase != Phase::Concrete) { | ||||||
| return MakeNonConstantResult(phase); | ||||||
| } | ||||||
|
|
||||||
| auto str_struct = eval_context.insts().GetAs<SemIR::StructValue>(str_id); | ||||||
| auto elements = eval_context.inst_blocks().Get(str_struct.elements_id); | ||||||
| CARBON_CHECK(elements.size() == 2, "String struct should have 2 fields."); | ||||||
|
|
||||||
| auto ptr_const_id = eval_context.constant_values().Get(elements[0]); | ||||||
| auto string_literal = eval_context.insts().GetAs<SemIR::StringLiteral>( | ||||||
| eval_context.constant_values().GetInstId(ptr_const_id)); | ||||||
|
Comment on lines
+1704
to
+1705
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably will need to change when we have ways to construct a compile-time string from another compile-time string, but I think it's okay for now. |
||||||
|
|
||||||
| auto string_value = eval_context.sem_ir().string_literal_values().Get( | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Avoid copying the string |
||||||
| string_literal.string_literal_id); | ||||||
|
|
||||||
| auto index_inst = eval_context.insts().GetAs<SemIR::IntValue>(index_id); | ||||||
| const auto& index_val = eval_context.ints().Get(index_inst.int_id); | ||||||
|
|
||||||
| auto char_value = | ||||||
| static_cast<uint8_t>(string_value[index_val.getZExtValue()]); | ||||||
|
|
||||||
| auto int_id = eval_context.ints().Add( | ||||||
| llvm::APSInt(llvm::APInt(32, char_value), /*isUnsigned=*/false)); | ||||||
| return MakeConstantResult( | ||||||
| eval_context.context(), | ||||||
| SemIR::IntValue{.type_id = call.type_id, .int_id = int_id}, phase); | ||||||
| } | ||||||
|
|
||||||
| case SemIR::BuiltinFunctionKind::PrintChar: | ||||||
| case SemIR::BuiltinFunctionKind::PrintInt: | ||||||
| case SemIR::BuiltinFunctionKind::ReadChar: | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,68 @@ auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/) | |||||||||
| return true; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Performs bounds checking for string indexing when the index is a constant. | ||||||||||
| static auto CheckStringIndexBounds(Context& context, | ||||||||||
| SemIR::InstId operand_inst_id, | ||||||||||
| SemIR::InstId index_inst_id, | ||||||||||
| const llvm::APInt& index_int) -> void { | ||||||||||
| if (index_int.isNegative()) { | ||||||||||
| CARBON_DIAGNOSTIC(ArrayIndexNegative, Error, "index `{0}` is negative.", | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| TypedInt); | ||||||||||
| context.emitter().Emit( | ||||||||||
| SemIR::LocId(index_inst_id), ArrayIndexNegative, | ||||||||||
| {.type = context.insts().Get(index_inst_id).type_id(), | ||||||||||
| .value = index_int}); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| auto operand_const_id = context.constant_values().Get(operand_inst_id); | ||||||||||
| if (!operand_const_id.is_constant()) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| auto operand_const_inst_id = | ||||||||||
| context.constant_values().GetInstId(operand_const_id); | ||||||||||
| auto str_struct = | ||||||||||
| context.insts().TryGetAs<SemIR::StructValue>(operand_const_inst_id); | ||||||||||
| if (!str_struct) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| auto elements = context.inst_blocks().Get(str_struct->elements_id); | ||||||||||
| CARBON_CHECK(elements.size() == 2, "String struct should have 2 fields."); | ||||||||||
|
|
||||||||||
| auto ptr_const_id = context.constant_values().Get(elements[0]); | ||||||||||
| auto ptr_inst_id = context.constant_values().GetInstId(ptr_const_id); | ||||||||||
| auto string_literal = | ||||||||||
| context.insts().TryGetAs<SemIR::StringLiteral>(ptr_inst_id); | ||||||||||
| if (!string_literal) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| auto string_value = context.sem_ir().string_literal_values().Get( | ||||||||||
| string_literal->string_literal_id); | ||||||||||
| if (index_int.getActiveBits() > 64 || | ||||||||||
| index_int.getZExtValue() >= string_value.size()) { | ||||||||||
| CARBON_DIAGNOSTIC(StringAtIndexOutOfBounds, Error, | ||||||||||
| "string index `{0}` is past the end of the string.", | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about something that says the bound in it?
Suggested change
|
||||||||||
| TypedInt); | ||||||||||
| context.emitter().Emit( | ||||||||||
| SemIR::LocId(index_inst_id), StringAtIndexOutOfBounds, | ||||||||||
| {.type = context.insts().Get(index_inst_id).type_id(), | ||||||||||
| .value = index_int}); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Checks if the given ClassType is the String class. | ||||||||||
| static auto IsStringType(Context& context, SemIR::ClassType class_type) | ||||||||||
| -> bool { | ||||||||||
| auto& class_info = context.classes().Get(class_type.class_id); | ||||||||||
| auto identifier_id = class_info.name_id.AsIdentifierId(); | ||||||||||
| return identifier_id.has_value() && | ||||||||||
| context.identifiers().Get(identifier_id) == "String"; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Performs an index with base expression `operand_inst_id` and | ||||||||||
| // `operand_type_id` for types that are not an array. This checks if | ||||||||||
| // the base expression implements the `IndexWith` interface; if so, uses the | ||||||||||
|
|
@@ -84,6 +146,27 @@ auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool { | |||||||||
| return true; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| case CARBON_KIND(SemIR::ClassType class_type): { | ||||||||||
| if (IsStringType(context, class_type)) { | ||||||||||
| auto index_const_id = context.constant_values().Get(index_inst_id); | ||||||||||
| if (index_const_id.is_constant()) { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given these bounds checks are only done for constant values, I am wondering why they are happening here instead of inside eval, more closely matching
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bounds checks happen here because string indexing goes through the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fear I was not clear. I am not wondering why it's using the ArrayIndex implementation. I am wondering if the checking can be done in the implementation of StringAt in eval: https://github.com/carbon-language/carbon-lang/pull/6329/files/153ff8269fd4a57553db0ec1a000d98ebb3279a2#diff-89653437c0b9dd4925413361e1a840632f5f4dd03dc63fee83ce2d04a1586bf1R1716 we have all the information we need here to check and return an Error instead of duplicating a lot of the code here.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I attempted to move the bounds checking to eval, but the For example, the SemIR dump from this test case This call does not have the the I suppose the issue lies in Since the string variable
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eval is constant evaluation. Each instruction has a constant value that is either constant (an instruction representing the constant result) or that is "runtime" (aka NotConstant). See If all the operands of (inputs to) the instruction are constant, then the resulting value can also be constant. That's when we execute StringAt in eval, and produce a constant value as a result. And since we have constant inputs, we can do bounds checks there. If any of the operands of the instruction are runtime, as in the example you wrote here, then we can not evaluate to a constant value during compile (in eval). Instead, we the call instruction goes through to lower, and we generate runtime code for it. Does that make sense?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the following test case The semir shows: The NameRef Even trying a more literal case, where the string itself is concrete Generates this semir The call itself still is not being evaluated as constant. Is there something preventing interface method calls from being constant evaluated?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In your first example: This is a runtime value. a
Here's another example of an interface with builtin methods: carbon-lang/core/prelude/types/int.carbon Lines 52 to 55 in 315b0ac
Here is us calling that to generate a compile-time constant value: https://carbon.godbolt.org/z/jnfTcc4ee (the semir gets truncated)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might help: The
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Heres what I found, note output from lldb The test attempts to evaluate
Is there a way to make the constant evaluator "see through" the (Apologies for all the questions on this PR I really appreciate your patience!)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, right. Note the different way that EqWith is written: fn At[self: Self](subscript: T) -> Char {
return StringAt(self, subscript);
} fn Equal[self: Self](other: UInt(M)) -> bool = "int.eq";Instead of writing a function that calls a builtin internally, we should make the At function into the builtin. Sorry I overlooked that this whole time, I didn't forsee that issue. |
||||||||||
| auto index_const_inst_id = | ||||||||||
| context.constant_values().GetInstId(index_const_id); | ||||||||||
| if (auto index_val = context.insts().TryGetAs<SemIR::IntValue>( | ||||||||||
| index_const_inst_id)) { | ||||||||||
| const auto& index_int = context.ints().Get(index_val->int_id); | ||||||||||
| CheckStringIndexBounds(context, operand_inst_id, index_inst_id, | ||||||||||
| index_int); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| auto elem_id = | ||||||||||
| PerformIndexWith(context, node_id, operand_inst_id, index_inst_id); | ||||||||||
| context.node_stack().Push(node_id, elem_id); | ||||||||||
| return true; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| default: { | ||||||||||
| auto elem_id = | ||||||||||
| PerformIndexWith(context, node_id, operand_inst_id, index_inst_id); | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||
| // 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| // AUTOUPDATE | ||||||||||
| // TIP: To test this file alone, run: | ||||||||||
| // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/operators/overloaded/string_indexing.carbon | ||||||||||
| // TIP: To dump output, run: | ||||||||||
| // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/overloaded/string_indexing.carbon | ||||||||||
|
|
||||||||||
|
|
||||||||||
| // --- test_string_indexing.carbon | ||||||||||
|
|
||||||||||
|
|
||||||||||
|
Comment on lines
+13
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| import Core library "io"; | ||||||||||
| import Core library "range"; | ||||||||||
|
|
||||||||||
| fn PrintStr(msg: str) { | ||||||||||
| for (i: i32 in Core.Range(msg.Size() as i32)) { | ||||||||||
| Core.PrintChar(msg[i]); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to print things to test indexing? I think we could just make a string, and then do an index into it and wrap that in |
||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| fn Run() { | ||||||||||
| PrintStr("Hello World!\n"); | ||||||||||
| } | ||||||||||
|
Comment on lines
+24
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no Run in tests |
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||
| // 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| // AUTOUPDATE | ||||||||||
| // TIP: To test this file alone, run: | ||||||||||
| // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_negative.carbon | ||||||||||
| // TIP: To dump output, run: | ||||||||||
| // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_negative.carbon | ||||||||||
|
|
||||||||||
|
|
||||||||||
| // --- fail_negative_index.carbon | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider moving this test into the string_indexing.carbon file, so we only have to compile the prelude once? |
||||||||||
|
|
||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| fn TestNegativeIndex() { | ||||||||||
| let test_str: str = "Test"; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Everything is a test in tests :) I would just go with a simple name |
||||||||||
| // CHECK:STDERR: fail_negative_index.carbon:[[@LINE+4]]:31: error: index `-1` is negative. [ArrayIndexNegative] | ||||||||||
| // CHECK:STDERR: let c: Core.Char = test_str[-1]; | ||||||||||
| // CHECK:STDERR: ^~ | ||||||||||
| // CHECK:STDERR: | ||||||||||
| let c: Core.Char = test_str[-1]; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||
| // 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| // AUTOUPDATE | ||||||||||
| // TIP: To test this file alone, run: | ||||||||||
| // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_out_of_bounds.carbon | ||||||||||
| // TIP: To dump output, run: | ||||||||||
| // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_out_of_bounds.carbon | ||||||||||
|
|
||||||||||
| // --- fail_out_of_bounds.carbon | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this into the string_indexing.carbon file? |
||||||||||
|
|
||||||||||
| // CHECK:STDERR: fail_out_of_bounds.carbon:[[@LINE+4]]:27: error: string index `4` is past the end of the string. [StringAtIndexOutOfBounds] | ||||||||||
| // CHECK:STDERR: var c: Core.Char = "Test"[4]; | ||||||||||
| // CHECK:STDERR: ^ | ||||||||||
| // CHECK:STDERR: | ||||||||||
| var c: Core.Char = "Test"[4]; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In these 3 tests we have 3 pretty different formulations of the test structure, it'd be nice to just have one simple structure that we repeat. I think just Would be simple and clear? |
||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||
| // 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon | ||||||
| // AUTOUPDATE | ||||||
| // TIP: To test this file alone, run: | ||||||
| // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_wrong_type.carbon | ||||||
| // TIP: To dump output, run: | ||||||
| // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/overloaded/string_indexing_wrong_type.carbon | ||||||
|
|
||||||
| // --- fail_wrong_type.carbon | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move into string_indexing.carbon? |
||||||
|
|
||||||
| fn TestWrongType() { | ||||||
| var x: i32 = 42; | ||||||
| // CHECK:STDERR: fail_wrong_type.carbon:[[@LINE+4]]:22: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `i32` that does not implement that interface [MissingImplInMemberAccess] | ||||||
| // CHECK:STDERR: var c: Core.Char = x[0]; | ||||||
| // CHECK:STDERR: ^~~~ | ||||||
| // CHECK:STDERR: | ||||||
| var c: Core.Char = x[0]; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -177,6 +177,22 @@ struct AnyType { | |||||||
| return true; | ||||||||
| } | ||||||||
| }; | ||||||||
| // Constraint that checks if a type is Core.String. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| struct CoreStringType { | ||||||||
| static auto Check(const File& sem_ir, ValidateState& /*state*/, | ||||||||
| TypeId type_id) -> bool { | ||||||||
| auto type_inst_id = sem_ir.types().GetInstId(type_id); | ||||||||
| auto class_type = sem_ir.insts().TryGetAs<ClassType>(type_inst_id); | ||||||||
| if (!class_type) { | ||||||||
| // Not a string. | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| return false; | ||||||||
| } | ||||||||
|
|
||||||||
| const auto& class_info = sem_ir.classes().Get(class_type->class_id); | ||||||||
|
|
||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| return sem_ir.names().GetFormatted(class_info.name_id).str() == "String"; | ||||||||
| } | ||||||||
| }; | ||||||||
|
|
||||||||
| // Constraint that requires the type to be the type type. | ||||||||
| using Type = BuiltinType<TypeType::TypeInstId>; | ||||||||
|
|
@@ -322,6 +338,11 @@ constexpr BuiltinInfo PrintInt = { | |||||||
| constexpr BuiltinInfo ReadChar = {"read.char", | ||||||||
| ValidateSignature<auto()->AnySizedInt>}; | ||||||||
|
|
||||||||
| // Gets a character from a string at the given index. | ||||||||
| constexpr BuiltinInfo StringAt = { | ||||||||
| "string.at", | ||||||||
| ValidateSignature<auto(CoreStringType, AnySizedInt)->AnySizedInt>}; | ||||||||
|
|
||||||||
| // Returns the `Core.CharLiteral` type. | ||||||||
| constexpr BuiltinInfo CharLiteralMakeType = {"char_literal.make_type", | ||||||||
| ValidateSignature<auto()->Type>}; | ||||||||
|
|
||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some unresolved comments still, like this one