Support parse/check/lower for char (#5901)

toolchain/check/testdata/builtins/char/basics.carbon and
toolchain/lower/testdata/builtins/char.carbon are probably the most
interesting tests here. The parse tests is required because this adds a
new node kind, and we need coverage of it; but the attached info is
minor. There's a fair amount of test churn here because I'm adding the
Core.Char and Core.CharLiteral types as new singletons.

My intent here is that `CharId` is always a unicode code point, even
when the type is a `Char` and thus must be a single UTF-8 code unit
(single byte). This mainly means the stored value of a `CharValue` can
be printed internally without knowing the type.

---------

Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Jon Ross-Perkins
2025-08-06 20:39:01 +00:00
committed by GitHub
co-authored by josh11b Richard Smith
parent 9c7e0f6bd5
commit 37d5046ceb
109 changed files with 1520 additions and 1077 deletions
+31 -1
View File
@@ -95,6 +95,26 @@ struct NoReturn {
// Constraint that a type is `bool`.
using Bool = BuiltinType<BoolType::TypeInstId>;
// Constraint that a type is `Core.CharLiteral`.
using CharLiteral = BuiltinType<CharLiteralType::TypeInstId>;
// Constraint that a type is `u8` or an adapted type, including `Core.Char`.
struct CharCompatible {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
auto int_info = sem_ir.types().TryGetIntTypeInfo(type_id);
if (!int_info) {
// Not an integer.
return false;
}
if (!int_info->bit_width.has_value() || int_info->is_signed) {
// Must be unsigned.
return false;
}
return sem_ir.ints().Get(int_info->bit_width) == 8;
}
};
// Constraint that requires the type to be a sized integer type.
struct AnySizedInt {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
@@ -248,6 +268,10 @@ constexpr BuiltinInfo PrintInt = {
constexpr BuiltinInfo ReadChar = {"read.char",
ValidateSignature<auto()->AnySizedInt>};
// Returns the `Core.CharLiteral` type.
constexpr BuiltinInfo CharLiteralMakeType = {"char_literal.make_type",
ValidateSignature<auto()->Type>};
// Returns the `Core.IntLiteral` type.
constexpr BuiltinInfo IntLiteralMakeType = {"int_literal.make_type",
ValidateSignature<auto()->Type>};
@@ -269,6 +293,11 @@ constexpr BuiltinInfo FloatMakeType = {"float.make_type",
constexpr BuiltinInfo BoolMakeType = {"bool.make_type",
ValidateSignature<auto()->Type>};
// Converts between char types, with a diagnostic if the value doesn't fit.
constexpr BuiltinInfo CharConvertChecked = {
"char.convert_checked",
ValidateSignature<auto(CharLiteral)->CharCompatible>};
// Converts between integer types, truncating if necessary.
constexpr BuiltinInfo IntConvert = {"int.convert",
ValidateSignature<auto(AnyInt)->AnyInt>};
@@ -578,8 +607,9 @@ auto BuiltinFunctionKind::IsCompTimeOnly(const File& sem_ir,
llvm::ArrayRef<InstId> arg_ids,
TypeId return_type_id) const -> bool {
switch (*this) {
case CharConvertChecked:
case IntConvertChecked:
// Checked integer conversions are compile-time only.
// Checked conversions are compile-time only.
return true;
case IntConvert: