Skip to content

Commit 37d5046

Browse files
jonmeowjosh11bzygoloid
authored
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 <[email protected]> Co-authored-by: Richard Smith <[email protected]>
1 parent 9c7e0f6 commit 37d5046

File tree

109 files changed

+1515
-1072
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+1515
-1072
lines changed

core/prelude/types.carbon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package Core library "prelude/types";
66

77
export import library "prelude/types/bool";
8+
export import library "prelude/types/char";
89
export import library "prelude/types/int";
910
export import library "prelude/types/int_literal";
1011
export import library "prelude/types/optional";

core/prelude/types/char.carbon

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
package Core library "prelude/types/char";
6+
7+
import library "prelude/destroy";
8+
import library "prelude/operators";
9+
import library "prelude/types/uint";
10+
11+
fn CharLiteral() -> type = "char_literal.make_type";
12+
13+
class Char {
14+
adapt u8;
15+
}
16+
17+
impl CharLiteral() as ImplicitAs(Char) {
18+
fn Convert[self: Self]() -> Char = "char.convert_checked";
19+
}
20+
21+
// TODO: Remove these once ImplicitAs extends As.
22+
impl CharLiteral() as As(Char) {
23+
fn Convert[self: Self]() -> Char = "char.convert_checked";
24+
}

toolchain/check/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ cc_library(
131131
"//toolchain/check:scope_stack",
132132
"//toolchain/diagnostics:diagnostic_emitter",
133133
"//toolchain/diagnostics:format_providers",
134+
"//toolchain/lex:token_info",
134135
"//toolchain/lex:token_kind",
135136
"//toolchain/lex:tokenized_buffer",
136137
"//toolchain/parse:node_kind",
@@ -296,6 +297,7 @@ cc_library(
296297
hdrs = ["diagnostic_emitter.h"],
297298
deps = [
298299
":context",
300+
"//common:ostream",
299301
"//common:raw_string_ostream",
300302
"//toolchain/diagnostics:diagnostic_emitter",
301303
"//toolchain/lex:token_index",

toolchain/check/eval.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <optional>
1010
#include <utility>
1111

12+
#include "llvm/Support/ConvertUTF.h"
1213
#include "toolchain/base/canonical_value_store.h"
1314
#include "toolchain/base/kind_switch.h"
1415
#include "toolchain/check/action.h"
@@ -951,6 +952,28 @@ static auto PerformArrayIndex(EvalContext& eval_context, SemIR::ArrayIndex inst)
951952
return eval_context.GetConstantValue(elements[index_val.getZExtValue()]);
952953
}
953954

955+
// Performs a conversion between character types, diagnosing if the value
956+
// doesn't fit in the destination type.
957+
static auto PerformCheckedCharConvert(Context& context, SemIR::LocId loc_id,
958+
SemIR::InstId arg_id,
959+
SemIR::TypeId dest_type_id)
960+
-> SemIR::ConstantId {
961+
auto arg = context.insts().GetAs<SemIR::CharLiteralValue>(arg_id);
962+
963+
// Values over 0x80 require multiple code units in UTF-8.
964+
if (arg.value.index >= 0x80) {
965+
CARBON_DIAGNOSTIC(CharTooLargeForType, Error,
966+
"character value {0} too large for type {1}",
967+
SemIR::CharId, SemIR::TypeId);
968+
context.emitter().Emit(loc_id, CharTooLargeForType, arg.value,
969+
dest_type_id);
970+
return SemIR::ErrorInst::ConstantId;
971+
}
972+
973+
llvm::APInt int_val(8, arg.value.index, /*isSigned=*/false);
974+
return MakeIntResult(context, dest_type_id, /*is_signed=*/false, int_val);
975+
}
976+
954977
// Forms a constant int type as an evaluation result. Requires that width_id is
955978
// constant.
956979
static auto MakeIntTypeResult(Context& context, SemIR::LocId loc_id,
@@ -1590,6 +1613,10 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
15901613
return MakeFacetTypeResult(eval_context.context(), combined_info, phase);
15911614
}
15921615

1616+
case SemIR::BuiltinFunctionKind::CharLiteralMakeType: {
1617+
return context.constant_values().Get(SemIR::CharLiteralType::TypeInstId);
1618+
}
1619+
15931620
case SemIR::BuiltinFunctionKind::IntLiteralMakeType: {
15941621
return context.constant_values().Get(SemIR::IntLiteralType::TypeInstId);
15951622
}
@@ -1619,6 +1646,15 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
16191646
return context.constant_values().Get(SemIR::BoolType::TypeInstId);
16201647
}
16211648

1649+
// Character conversions.
1650+
case SemIR::BuiltinFunctionKind::CharConvertChecked: {
1651+
if (phase != Phase::Concrete) {
1652+
return MakeConstantResult(context, call, phase);
1653+
}
1654+
return PerformCheckedCharConvert(context, loc_id, arg_ids[0],
1655+
call.type_id);
1656+
}
1657+
16221658
// Integer conversions.
16231659
case SemIR::BuiltinFunctionKind::IntConvert: {
16241660
if (phase != Phase::Concrete) {

toolchain/check/handle_literal.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ auto HandleParseNode(Context& context, Parse::BoolLiteralTrueId node_id)
3535
return true;
3636
}
3737

38+
auto HandleParseNode(Context& context, Parse::CharLiteralId node_id) -> bool {
39+
auto value = context.tokens().GetCharLiteralValue(
40+
context.parse_tree().node_token(node_id));
41+
auto inst_id = AddInst<SemIR::CharLiteralValue>(
42+
context, node_id,
43+
{.type_id = GetSingletonType(context, SemIR::CharLiteralType::TypeInstId),
44+
.value = SemIR::CharId(value.value)});
45+
context.node_stack().Push(node_id, inst_id);
46+
return true;
47+
}
48+
3849
auto HandleParseNode(Context& context, Parse::IntLiteralId node_id) -> bool {
3950
auto int_literal_id = MakeIntLiteral(
4051
context, node_id,

toolchain/check/literal.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "toolchain/check/convert.h"
1010
#include "toolchain/check/name_lookup.h"
1111
#include "toolchain/check/type.h"
12+
#include "toolchain/lex/token_info.h"
1213
#include "toolchain/sem_ir/ids.h"
1314

1415
namespace Carbon::Check {

toolchain/check/literal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define CARBON_TOOLCHAIN_CHECK_LITERAL_H_
77

88
#include "toolchain/check/context.h"
9+
#include "toolchain/lex/token_info.h"
910
#include "toolchain/sem_ir/ids.h"
1011

1112
namespace Carbon::Check {

toolchain/check/testdata/alias/export_name.carbon

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ var d: D* = &c;
111111
// CHECK:STDOUT: %Main.C = import_ref Main//base, C, unloaded
112112
// CHECK:STDOUT: %Main.D: type = import_ref Main//base, D, loaded [concrete = constants.%C]
113113
// CHECK:STDOUT: %Main.import_ref.8f2: <witness> = import_ref Main//base, loc4_10, loaded [concrete = constants.%complete_type]
114-
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//base, inst16 [no loc], unloaded
114+
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//base, inst17 [no loc], unloaded
115115
// CHECK:STDOUT: }
116116
// CHECK:STDOUT:
117117
// CHECK:STDOUT: file {
@@ -142,7 +142,7 @@ var d: D* = &c;
142142
// CHECK:STDOUT: %Main.C: type = import_ref Main//base, C, loaded [concrete = constants.%C]
143143
// CHECK:STDOUT: %Main.D = import_ref Main//base, D, unloaded
144144
// CHECK:STDOUT: %Main.import_ref.8f2: <witness> = import_ref Main//base, loc4_10, loaded [concrete = constants.%complete_type]
145-
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//base, inst16 [no loc], unloaded
145+
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//base, inst17 [no loc], unloaded
146146
// CHECK:STDOUT: }
147147
// CHECK:STDOUT:
148148
// CHECK:STDOUT: file {
@@ -173,8 +173,8 @@ var d: D* = &c;
173173
// CHECK:STDOUT:
174174
// CHECK:STDOUT: imports {
175175
// CHECK:STDOUT: %Main.D: type = import_ref Main//export, D, loaded [concrete = constants.%C]
176-
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//export, inst22 [indirect], loaded [concrete = constants.%complete_type]
177-
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//export, inst23 [indirect], unloaded
176+
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//export, inst23 [indirect], loaded [concrete = constants.%complete_type]
177+
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//export, inst24 [indirect], unloaded
178178
// CHECK:STDOUT: }
179179
// CHECK:STDOUT:
180180
// CHECK:STDOUT: file {
@@ -257,8 +257,8 @@ var d: D* = &c;
257257
// CHECK:STDOUT: imports {
258258
// CHECK:STDOUT: %Main.D: type = import_ref Main//export, D, loaded [concrete = constants.%C]
259259
// CHECK:STDOUT: %Main.C: type = import_ref Main//export_orig, C, loaded [concrete = constants.%C]
260-
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//export_orig, inst22 [indirect], loaded [concrete = constants.%complete_type]
261-
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//export_orig, inst23 [indirect], unloaded
260+
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//export_orig, inst23 [indirect], loaded [concrete = constants.%complete_type]
261+
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//export_orig, inst24 [indirect], unloaded
262262
// CHECK:STDOUT: }
263263
// CHECK:STDOUT:
264264
// CHECK:STDOUT: file {

toolchain/check/testdata/alias/import.carbon

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ var c: () = a_alias_alias;
120120
// CHECK:STDOUT: %Main.c_alias: type = import_ref Main//class1, c_alias, loaded [concrete = constants.%C]
121121
// CHECK:STDOUT: %Main.a = import_ref Main//class1, a, unloaded
122122
// CHECK:STDOUT: %Main.import_ref.8f2: <witness> = import_ref Main//class1, loc4_10, loaded [concrete = constants.%complete_type]
123-
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//class1, inst16 [no loc], unloaded
123+
// CHECK:STDOUT: %Main.import_ref.2c4 = import_ref Main//class1, inst17 [no loc], unloaded
124124
// CHECK:STDOUT: }
125125
// CHECK:STDOUT:
126126
// CHECK:STDOUT: file {
@@ -166,8 +166,8 @@ var c: () = a_alias_alias;
166166
// CHECK:STDOUT: imports {
167167
// CHECK:STDOUT: %Main.c_alias_alias: type = import_ref Main//class2, c_alias_alias, loaded [concrete = constants.%C]
168168
// CHECK:STDOUT: %Main.b = import_ref Main//class2, b, unloaded
169-
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//class2, inst23 [indirect], loaded [concrete = constants.%complete_type]
170-
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//class2, inst24 [indirect], unloaded
169+
// CHECK:STDOUT: %Main.import_ref.8db: <witness> = import_ref Main//class2, inst24 [indirect], loaded [concrete = constants.%complete_type]
170+
// CHECK:STDOUT: %Main.import_ref.6a9 = import_ref Main//class2, inst25 [indirect], unloaded
171171
// CHECK:STDOUT: }
172172
// CHECK:STDOUT:
173173
// CHECK:STDOUT: file {

toolchain/check/testdata/alias/import_access.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ var inst: Test.A = {};
9797
// CHECK:STDOUT: %Test.C = import_ref Test//def, C, unloaded
9898
// CHECK:STDOUT: %Test.A: type = import_ref Test//def, A, loaded [concrete = constants.%C]
9999
// CHECK:STDOUT: %Test.import_ref.8f2: <witness> = import_ref Test//def, loc4_10, loaded [concrete = constants.%complete_type]
100-
// CHECK:STDOUT: %Test.import_ref.2c4 = import_ref Test//def, inst16 [no loc], unloaded
100+
// CHECK:STDOUT: %Test.import_ref.2c4 = import_ref Test//def, inst17 [no loc], unloaded
101101
// CHECK:STDOUT: }
102102
// CHECK:STDOUT:
103103
// CHECK:STDOUT: file {

0 commit comments

Comments
 (0)