Эх сурвалжийг харах

Add support for `char` keyword per #5903. (#6078)

Make inst namer and stringify print `Core.Char` and `Core.String` as
`char` and `str` respectively. Plus a few cleanups.
Richard Smith 7 сар өмнө
parent
commit
bac828d244

+ 7 - 0
toolchain/check/handle_literal.cpp

@@ -83,6 +83,13 @@ auto HandleParseNode(Context& context, Parse::BoolTypeLiteralId node_id)
   return true;
 }
 
+auto HandleParseNode(Context& context, Parse::CharTypeLiteralId node_id)
+    -> bool {
+  auto type_inst_id = MakeCharTypeLiteral(context, node_id);
+  context.node_stack().Push(node_id, type_inst_id);
+  return true;
+}
+
 // Shared implementation for handling `iN` and `uN` literals.
 static auto HandleIntOrUnsignedIntTypeLiteral(Context& context,
                                               Parse::NodeId node_id,

+ 1 - 1
toolchain/check/testdata/function/call/fail_not_callable.carbon

@@ -11,7 +11,7 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_not_callable.carbon
 
 fn Run() {
-  // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+4]]:16: error: value of type `Core.String` is not callable [CallToNonCallable]
+  // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+4]]:16: error: value of type `str` is not callable [CallToNonCallable]
   // CHECK:STDERR:   var x: i32 = "hello"();
   // CHECK:STDERR:                ^~~~~~~~~
   // CHECK:STDERR:

+ 23 - 23
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -1006,12 +1006,12 @@ fn F() {
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %.d16: Core.CharLiteral = char_value U+0058 [concrete]
-// CHECK:STDOUT:   %Char: type = class_type @Char [concrete]
-// CHECK:STDOUT:   %ptr.fb0: type = ptr_type %Char [concrete]
+// CHECK:STDOUT:   %char: type = class_type @Char [concrete]
+// CHECK:STDOUT:   %ptr.fb0: type = ptr_type %char [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ImplicitAs.type.3be: type = facet_type <@ImplicitAs, @ImplicitAs(%Char)> [concrete]
-// CHECK:STDOUT:   %ImplicitAs.Convert.type.f57: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%Char) [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.3be: type = facet_type <@ImplicitAs, @ImplicitAs(%char)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.f57: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%char) [concrete]
 // CHECK:STDOUT:   %Copy.type: type = facet_type <@Copy> [concrete]
 // CHECK:STDOUT:   %Copy.Op.type: type = fn_type @Copy.Op [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness.892: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.1f2 [concrete]
@@ -1020,15 +1020,15 @@ fn F() {
 // CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.type: type = fn_type @Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete]
 // CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = struct_value () [concrete]
 // CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %.d16, %Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete]
-// CHECK:STDOUT:   %int_88: %Char = int_value 88 [concrete]
+// CHECK:STDOUT:   %int_88: %char = int_value 88 [concrete]
 // CHECK:STDOUT:   %Copy.impl_witness.9ba: <witness> = impl_witness imports.%Copy.impl_witness_table.0e0 [concrete]
-// CHECK:STDOUT:   %Copy.facet.3d0: %Copy.type = facet_value %Char, (%Copy.impl_witness.9ba) [concrete]
+// CHECK:STDOUT:   %Copy.facet.3d0: %Copy.type = facet_value %char, (%Copy.impl_witness.9ba) [concrete]
 // CHECK:STDOUT:   %.1ed: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.3d0 [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.type: type = fn_type @Char.as.Copy.impl.Op [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op: %Char.as.Copy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.bound: <bound method> = bound_method %int_88, %Char.as.Copy.impl.Op [concrete]
-// CHECK:STDOUT:   %Char.as.Destroy.impl.Op.type: type = fn_type @Char.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %Char.as.Destroy.impl.Op: %Char.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.type: type = fn_type @char.as.Copy.impl.Op [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op: %char.as.Copy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.bound: <bound method> = bound_method %int_88, %char.as.Copy.impl.Op [concrete]
+// CHECK:STDOUT:   %char.as.Destroy.impl.Op.type: type = fn_type @char.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %char.as.Destroy.impl.Op: %char.as.Destroy.impl.Op.type = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -1044,8 +1044,8 @@ fn F() {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import_ref.543: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = import_ref Core//prelude/types/char, loc23_36, loaded [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1f2 = impl_witness_table (%Core.import_ref.543), @Core.CharLiteral.as.ImplicitAs.impl [concrete]
-// CHECK:STDOUT:   %Core.import_ref.247: %Char.as.Copy.impl.Op.type = import_ref Core//prelude/types/char, loc19_31, loaded [concrete = constants.%Char.as.Copy.impl.Op]
-// CHECK:STDOUT:   %Copy.impl_witness_table.0e0 = impl_witness_table (%Core.import_ref.247), @Char.as.Copy.impl [concrete]
+// CHECK:STDOUT:   %Core.import_ref.247: %char.as.Copy.impl.Op.type = import_ref Core//prelude/types/char, loc19_31, loaded [concrete = constants.%char.as.Copy.impl.Op]
+// CHECK:STDOUT:   %Copy.impl_witness_table.0e0 = impl_witness_table (%Core.import_ref.247), @char.as.Copy.impl [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
@@ -1055,19 +1055,19 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_11.1: Core.CharLiteral = char_value U+0058 [concrete = constants.%.d16]
 // CHECK:STDOUT:   %impl.elem0.loc8_11.1: %.8d6 = impl_witness_access constants.%ImplicitAs.impl_witness.892, element0 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert]
 // CHECK:STDOUT:   %bound_method.loc8_11.1: <bound method> = bound_method %.loc8_11.1, %impl.elem0.loc8_11.1 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert.bound]
-// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.call: init %Char = call %bound_method.loc8_11.1(%.loc8_11.1) [concrete = constants.%int_88]
-// CHECK:STDOUT:   %.loc8_11.2: %Char = value_of_initializer %Core.CharLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_88]
-// CHECK:STDOUT:   %.loc8_11.3: %Char = converted %.loc8_11.1, %.loc8_11.2 [concrete = constants.%int_88]
-// CHECK:STDOUT:   %.loc8_11.4: ref %Char = temporary_storage
-// CHECK:STDOUT:   %impl.elem0.loc8_11.2: %.1ed = impl_witness_access constants.%Copy.impl_witness.9ba, element0 [concrete = constants.%Char.as.Copy.impl.Op]
-// CHECK:STDOUT:   %bound_method.loc8_11.2: <bound method> = bound_method %.loc8_11.3, %impl.elem0.loc8_11.2 [concrete = constants.%Char.as.Copy.impl.Op.bound]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.call: init %Char = call %bound_method.loc8_11.2(%.loc8_11.3) [concrete = constants.%int_88]
-// CHECK:STDOUT:   %.loc8_11.5: ref %Char = temporary %.loc8_11.4, %Char.as.Copy.impl.Op.call
+// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.call: init %char = call %bound_method.loc8_11.1(%.loc8_11.1) [concrete = constants.%int_88]
+// CHECK:STDOUT:   %.loc8_11.2: %char = value_of_initializer %Core.CharLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_88]
+// CHECK:STDOUT:   %.loc8_11.3: %char = converted %.loc8_11.1, %.loc8_11.2 [concrete = constants.%int_88]
+// CHECK:STDOUT:   %.loc8_11.4: ref %char = temporary_storage
+// CHECK:STDOUT:   %impl.elem0.loc8_11.2: %.1ed = impl_witness_access constants.%Copy.impl_witness.9ba, element0 [concrete = constants.%char.as.Copy.impl.Op]
+// CHECK:STDOUT:   %bound_method.loc8_11.2: <bound method> = bound_method %.loc8_11.3, %impl.elem0.loc8_11.2 [concrete = constants.%char.as.Copy.impl.Op.bound]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.call: init %char = call %bound_method.loc8_11.2(%.loc8_11.3) [concrete = constants.%int_88]
+// CHECK:STDOUT:   %.loc8_11.5: ref %char = temporary %.loc8_11.4, %char.as.Copy.impl.Op.call
 // CHECK:STDOUT:   %addr.loc8_14: %ptr.fb0 = addr_of %.loc8_11.5
 // CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_14)
-// CHECK:STDOUT:   %Char.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.5, constants.%Char.as.Destroy.impl.Op
+// CHECK:STDOUT:   %char.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_11.5, constants.%char.as.Destroy.impl.Op
 // CHECK:STDOUT:   %addr.loc8_11: %ptr.fb0 = addr_of %.loc8_11.5
-// CHECK:STDOUT:   %Char.as.Destroy.impl.Op.call: init %empty_tuple.type = call %Char.as.Destroy.impl.Op.bound(%addr.loc8_11)
+// CHECK:STDOUT:   %char.as.Destroy.impl.Op.call: init %empty_tuple.type = call %char.as.Destroy.impl.Op.bound(%addr.loc8_11)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/interop/cpp/function/operators.carbon

@@ -327,7 +327,7 @@ import Cpp library "plus_with_string_view_conversion.h";
 fn F() {
   let s1: str = "hello";
   let s2: str = "world";
-  // CHECK:STDERR: fail_todo_import_plus_with_string_view_conversion.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.AddWith(Core.String)` in type `Core.String` that does not implement that interface [MissingImplInMemberAccess]
+  // CHECK:STDERR: fail_todo_import_plus_with_string_view_conversion.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.AddWith(str)` in type `str` that does not implement that interface [MissingImplInMemberAccess]
   // CHECK:STDERR:   let c: Cpp.C = s1 + s2;
   // CHECK:STDERR:                  ^~~~~~~
   // CHECK:STDERR:

+ 13 - 13
toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon

@@ -46,7 +46,7 @@ import Cpp library "string_view.h";
 
 //@dump-sem-ir-begin
 fn F() {
-  // CHECK:STDERR: fail_todo_5891_import_multiple.carbon:[[@LINE+7]]:3: error: call argument of type `Core.String` is not supported [CppCallArgTypeNotSupported]
+  // CHECK:STDERR: fail_todo_5891_import_multiple.carbon:[[@LINE+7]]:3: error: call argument of type `str` is not supported [CppCallArgTypeNotSupported]
   // CHECK:STDERR:   Cpp.Consume("hello");
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_todo_5891_import_multiple.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
@@ -69,7 +69,7 @@ fn G() -> str {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
-// CHECK:STDOUT:   %String: type = class_type @String [concrete]
+// CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %u64: type = class_type @UInt, @UInt(%int_64) [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
@@ -77,15 +77,15 @@ fn G() -> str {
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
 // CHECK:STDOUT:   %.fd2: type = cpp_overload_set_type @Produce [concrete]
 // CHECK:STDOUT:   %empty_struct.c28: %.fd2 = struct_value () [concrete]
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete]
+// CHECK:STDOUT:   %str.3b1: %ptr.3e8 = string_literal "hello" [concrete]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_5) [concrete]
-// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %String [concrete]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.3b1, %int_5) [concrete]
+// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %str.ee0 [concrete]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
 // CHECK:STDOUT:   %.a47: type = cpp_overload_set_type @Produce__carbon_thunk [concrete]
 // CHECK:STDOUT:   %empty_struct.ab9: %.a47 = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.85f: type = ptr_type %String [concrete]
+// CHECK:STDOUT:   %ptr.85f: type = ptr_type %str.ee0 [concrete]
 // CHECK:STDOUT:   %Produce__carbon_thunk.type: type = fn_type @Produce__carbon_thunk [concrete]
 // CHECK:STDOUT:   %Produce__carbon_thunk: %Produce__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT: }
@@ -111,8 +111,8 @@ fn G() -> str {
 // CHECK:STDOUT:     %return.patt: %pattern_type.461 = return_slot_pattern [concrete]
 // CHECK:STDOUT:     %return.param_patt: %pattern_type.461 = out_param_pattern %return.patt, call_param0 [concrete]
 // CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %return.param: ref %String = out_param call_param0
-// CHECK:STDOUT:     %return: ref %String = return_slot %return.param
+// CHECK:STDOUT:     %return.param: ref %str.ee0 = out_param call_param0
+// CHECK:STDOUT:     %return: ref %str.ee0 = return_slot %return.param
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -120,20 +120,20 @@ fn G() -> str {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Consume.ref: %.fd2 = name_ref Consume, imports.%.f17 [concrete = constants.%empty_struct.c28]
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str]
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str.3b1]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete = constants.%int_5]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_5) [concrete = constants.%String.val]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str, %int_5) [concrete = constants.%String.val]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G() -> %return.param: %String {
+// CHECK:STDOUT: fn @G() -> %return.param: %str.ee0 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Produce.ref: %.a47 = name_ref Produce, imports.%.5d1 [concrete = constants.%empty_struct.ab9]
-// CHECK:STDOUT:   %.loc20: ref %String = splice_block %return {}
+// CHECK:STDOUT:   %.loc20: ref %str.ee0 = splice_block %return {}
 // CHECK:STDOUT:   %addr: %ptr.85f = addr_of %.loc20
 // CHECK:STDOUT:   %Produce__carbon_thunk.call: init %empty_tuple.type = call imports.%Produce__carbon_thunk.decl(%addr)
-// CHECK:STDOUT:   %.loc21: init %String = in_place_init %Produce__carbon_thunk.call, %.loc20
+// CHECK:STDOUT:   %.loc21: init %str.ee0 = in_place_init %Produce__carbon_thunk.call, %.loc20
 // CHECK:STDOUT:   return %.loc21 to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 17 - 17
toolchain/check/testdata/primitives/import_symbolic.carbon

@@ -245,10 +245,10 @@ fn F() -> u32 {
 // CHECK:STDOUT: --- import_char.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %Char: type = class_type @Char [concrete]
+// CHECK:STDOUT:   %char: type = class_type @Char [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
-// CHECK:STDOUT:   %int_97: %Char = int_value 97 [concrete]
+// CHECK:STDOUT:   %int_97: %char = int_value 97 [concrete]
 // CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness imports.%I.impl_witness_table [concrete]
 // CHECK:STDOUT:   %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete]
 // CHECK:STDOUT:   %I.assoc_type: type = assoc_entity_type @I [concrete]
@@ -256,26 +256,26 @@ fn F() -> u32 {
 // CHECK:STDOUT:   %Copy.type: type = facet_type <@Copy> [concrete]
 // CHECK:STDOUT:   %Copy.Op.type: type = fn_type @Copy.Op [concrete]
 // CHECK:STDOUT:   %Copy.impl_witness.9ba: <witness> = impl_witness imports.%Copy.impl_witness_table.0e0 [concrete]
-// CHECK:STDOUT:   %Copy.facet.3d0: %Copy.type = facet_value %Char, (%Copy.impl_witness.9ba) [concrete]
+// CHECK:STDOUT:   %Copy.facet.3d0: %Copy.type = facet_value %char, (%Copy.impl_witness.9ba) [concrete]
 // CHECK:STDOUT:   %.1ed: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.3d0 [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.type: type = fn_type @Char.as.Copy.impl.Op [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op: %Char.as.Copy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.bound: <bound method> = bound_method %int_97, %Char.as.Copy.impl.Op [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.type: type = fn_type @char.as.Copy.impl.Op [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op: %char.as.Copy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.bound: <bound method> = bound_method %int_97, %char.as.Copy.impl.Op [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Main.I: type = import_ref Main//char, I, loaded [concrete = constants.%I.type]
 // CHECK:STDOUT:   %Main.C: type = import_ref Main//char, C, loaded [concrete = constants.%C]
 // CHECK:STDOUT:   %Main.import_ref.da1: %I.assoc_type = import_ref Main//char, loc5_10, loaded [concrete = constants.%assoc0.941]
-// CHECK:STDOUT:   %Main.import_ref.d0c: %Char = import_ref Main//char, loc10_30, loaded [concrete = constants.%int_97]
+// CHECK:STDOUT:   %Main.import_ref.d0c: %char = import_ref Main//char, loc10_30, loaded [concrete = constants.%int_97]
 // CHECK:STDOUT:   %I.impl_witness_table = impl_witness_table (%Main.import_ref.d0c), @C.as.I.impl [concrete]
-// CHECK:STDOUT:   %Main.import_ref.8e8: %Char = import_ref Main//char, loc5_10, loaded [concrete = %val]
-// CHECK:STDOUT:   %val: %Char = assoc_const_decl @val [concrete] {}
-// CHECK:STDOUT:   %Core.import_ref.247: %Char.as.Copy.impl.Op.type = import_ref Core//prelude/types/char, loc19_31, loaded [concrete = constants.%Char.as.Copy.impl.Op]
-// CHECK:STDOUT:   %Copy.impl_witness_table.0e0 = impl_witness_table (%Core.import_ref.247), @Char.as.Copy.impl [concrete]
+// CHECK:STDOUT:   %Main.import_ref.8e8: %char = import_ref Main//char, loc5_10, loaded [concrete = %val]
+// CHECK:STDOUT:   %val: %char = assoc_const_decl @val [concrete] {}
+// CHECK:STDOUT:   %Core.import_ref.247: %char.as.Copy.impl.Op.type = import_ref Core//prelude/types/char, loc19_31, loaded [concrete = constants.%char.as.Copy.impl.Op]
+// CHECK:STDOUT:   %Copy.impl_witness_table.0e0 = impl_witness_table (%Core.import_ref.247), @char.as.Copy.impl [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F() -> %Char {
+// CHECK:STDOUT: fn @F() -> %char {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C]
 // CHECK:STDOUT:   %I.ref: type = name_ref I, imports.%Main.I [concrete = constants.%I.type]
@@ -284,11 +284,11 @@ fn F() -> u32 {
 // CHECK:STDOUT:   %val.ref: %I.assoc_type = name_ref val, imports.%Main.import_ref.da1 [concrete = constants.%assoc0.941]
 // CHECK:STDOUT:   %as_type: type = facet_access_type %.loc7_13 [concrete = constants.%C]
 // CHECK:STDOUT:   %.loc7_18: type = converted %.loc7_13, %as_type [concrete = constants.%C]
-// CHECK:STDOUT:   %impl.elem0.loc7_18.1: %Char = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%int_97]
-// CHECK:STDOUT:   %impl.elem0.loc7_18.2: %.1ed = impl_witness_access constants.%Copy.impl_witness.9ba, element0 [concrete = constants.%Char.as.Copy.impl.Op]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %impl.elem0.loc7_18.1, %impl.elem0.loc7_18.2 [concrete = constants.%Char.as.Copy.impl.Op.bound]
-// CHECK:STDOUT:   %Char.as.Copy.impl.Op.call: init %Char = call %bound_method(%impl.elem0.loc7_18.1) [concrete = constants.%int_97]
-// CHECK:STDOUT:   return %Char.as.Copy.impl.Op.call to %return
+// CHECK:STDOUT:   %impl.elem0.loc7_18.1: %char = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%int_97]
+// CHECK:STDOUT:   %impl.elem0.loc7_18.2: %.1ed = impl_witness_access constants.%Copy.impl_witness.9ba, element0 [concrete = constants.%char.as.Copy.impl.Op]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %impl.elem0.loc7_18.1, %impl.elem0.loc7_18.2 [concrete = constants.%char.as.Copy.impl.Op.bound]
+// CHECK:STDOUT:   %char.as.Copy.impl.Op.call: init %char = call %bound_method(%impl.elem0.loc7_18.1) [concrete = constants.%int_97]
+// CHECK:STDOUT:   return %char.as.Copy.impl.Op.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- import_char_literal.carbon

+ 13 - 13
toolchain/check/testdata/primitives/string_literals.carbon

@@ -37,7 +37,7 @@ class String {
 
 import Core library "unusual_string";
 
-// CHECK:STDERR: fail_use_unusual_string.carbon:[[@LINE+4]]:14: error: unexpected representation for type `Core.String` [StringLiteralTypeUnexpected]
+// CHECK:STDERR: fail_use_unusual_string.carbon:[[@LINE+4]]:14: error: unexpected representation for type `str` [StringLiteralTypeUnexpected]
 // CHECK:STDERR: let x: str = "hello";
 // CHECK:STDERR:              ^~~~~~~
 // CHECK:STDERR:
@@ -116,14 +116,14 @@ This string contains 256 characters. Among them are:
 // CHECK:STDOUT: --- use_small_string.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %String: type = class_type @String [concrete]
+// CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
-// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %String [concrete]
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete]
+// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %str.ee0 [concrete]
+// CHECK:STDOUT:   %str.3b1: %ptr.3e8 = string_literal "hello" [concrete]
 // CHECK:STDOUT:   %int_5: %u8 = int_value 5 [concrete]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_5) [concrete]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.3b1, %int_5) [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -133,26 +133,26 @@ This string contains 256 characters. Among them are:
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.461 = binding_pattern x [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x: %String = bind_name x, @__global_init.%String.val
+// CHECK:STDOUT:   %x: %str.ee0 = bind_name x, @__global_init.%String.val
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str]
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str.3b1]
 // CHECK:STDOUT:   %int_5: %u8 = int_value 5 [concrete = constants.%int_5]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_5) [concrete = constants.%String.val]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str, %int_5) [concrete = constants.%String.val]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_overfill_small_string.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %String: type = class_type @String [concrete]
+// CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
-// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %String [concrete]
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "A string so long its size does not fit in `u8`. This results in an error,\nbecause we can't form a `Core.String` value that describes this string.\n\nThis string contains 256 characters. Among them are:\n\n* 6 `g`s,\n* 21 `s`s,\n* 9 newlines,\n* and only one `p`.\n" [concrete]
+// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %str.ee0 [concrete]
+// CHECK:STDOUT:   %str.294: %ptr.3e8 = string_literal "A string so long its size does not fit in `u8`. This results in an error,\nbecause we can't form a `Core.String` value that describes this string.\n\nThis string contains 256 characters. Among them are:\n\n* 6 `g`s,\n* 21 `s`s,\n* 9 newlines,\n* and only one `p`.\n" [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -162,12 +162,12 @@ This string contains 256 characters. Among them are:
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %x.patt: %pattern_type.461 = binding_pattern x [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x: %String = bind_name x, <error> [concrete = <error>]
+// CHECK:STDOUT:   %x: %str.ee0 = bind_name x, <error> [concrete = <error>]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "A string so long its size does not fit in `u8`. This results in an error,\nbecause we can't form a `Core.String` value that describes this string.\n\nThis string contains 256 characters. Among them are:\n\n* 6 `g`s,\n* 21 `s`s,\n* 9 newlines,\n* and only one `p`.\n" [concrete = constants.%str]
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "A string so long its size does not fit in `u8`. This results in an error,\nbecause we can't form a `Core.String` value that describes this string.\n\nThis string contains 256 characters. Among them are:\n\n* 6 `g`s,\n* 21 `s`s,\n* 9 newlines,\n* and only one `p`.\n" [concrete = constants.%str.294]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 115 - 7
toolchain/check/testdata/primitives/type_literals.carbon

@@ -40,6 +40,14 @@ var test_f64: f64;
 var test_f128: f128;
 //@dump-sem-ir-end
 
+// --- char.carbon
+
+library "[[@TEST_NAME]]";
+
+//@dump-sem-ir-begin
+let test_char: char = 'c';
+//@dump-sem-ir-end
+
 // --- string.carbon
 
 library "[[@TEST_NAME]]";
@@ -201,6 +209,66 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:
 var x: type = 42;
 
+// --- fail_stringify_type_literals.carbon
+
+library "[[@TEST_NAME]]";
+
+// Ensure that type literals get stringified properly.
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `i32` [ConversionFailure]
+// CHECK:STDERR: var test_i32: i32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_i32: i32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_i32: i32 = ();
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `f32` [ConversionFailure]
+// CHECK:STDERR: var test_f32: f32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(f32)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_f32: f32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_f32: f32 = ();
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `u32` [ConversionFailure]
+// CHECK:STDERR: var test_u32: u32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(u32)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_u32: u32 = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_u32: u32 = ();
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `bool` [ConversionFailure]
+// CHECK:STDERR: var test_bool: bool = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_bool: bool = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_bool: bool = ();
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `char` [ConversionFailure]
+// CHECK:STDERR: var test_char: char = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(char)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_char: char = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_char: char = ();
+
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `str` [ConversionFailure]
+// CHECK:STDERR: var test_str: str = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_stringify_type_literals.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(str)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var test_str: str = ();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+var test_str: str = ();
+
 // CHECK:STDOUT: --- iN.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -390,19 +458,59 @@ var x: type = 42;
 // CHECK:STDOUT:   %test_f128: ref %f128.b8c = bind_name test_f128, %test_f128.var [concrete = %test_f128.var]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- char.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %char: type = class_type @Char [concrete]
+// CHECK:STDOUT:   %pattern_type.b09: type = pattern_type %char [concrete]
+// CHECK:STDOUT:   %.b9b: Core.CharLiteral = char_value U+0063 [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.3be: type = facet_type <@ImplicitAs, @ImplicitAs(%char)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.f57: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%char) [concrete]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.3be = facet_value Core.CharLiteral, (%ImplicitAs.impl_witness) [concrete]
+// CHECK:STDOUT:   %.8d6: type = fn_type_with_self_type %ImplicitAs.Convert.type.f57, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.type: type = fn_type @Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %.b9b, %Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %int_99: %char = int_value 99 [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core.import_ref.543: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = import_ref Core//prelude/parts/char, loc13_36, loaded [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.543), @Core.CharLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %test_char.patt: %pattern_type.b09 = binding_pattern test_char [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl.elem0: %.8d6 = impl_witness_access constants.%ImplicitAs.impl_witness, element0 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method @__global_init.%.loc5, %impl.elem0 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %Core.CharLiteral.as.ImplicitAs.impl.Convert.call: init %char = call %bound_method(@__global_init.%.loc5) [concrete = constants.%int_99]
+// CHECK:STDOUT:   %.loc5_23.1: %char = value_of_initializer %Core.CharLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_99]
+// CHECK:STDOUT:   %.loc5_23.2: %char = converted @__global_init.%.loc5, %.loc5_23.1 [concrete = constants.%int_99]
+// CHECK:STDOUT:   %test_char: %char = bind_name test_char, %.loc5_23.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc5: Core.CharLiteral = char_value U+0063 [concrete = constants.%.b9b]
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- string.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %String: type = class_type @String [concrete]
+// CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %u64: type = class_type @UInt, @UInt(%int_64) [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
-// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %String [concrete]
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "Test" [concrete]
+// CHECK:STDOUT:   %pattern_type.461: type = pattern_type %str.ee0 [concrete]
+// CHECK:STDOUT:   %str.c1c: %ptr.3e8 = string_literal "Test" [concrete]
 // CHECK:STDOUT:   %int_4: %u64 = int_value 4 [concrete]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_4) [concrete]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.c1c, %int_4) [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -412,14 +520,14 @@ var x: type = 42;
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %test_str.patt: %pattern_type.461 = binding_pattern test_str [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %test_str: %String = bind_name test_str, @__global_init.%String.val
+// CHECK:STDOUT:   %test_str: %str.ee0 = bind_name test_str, @__global_init.%String.val
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "Test" [concrete = constants.%str]
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "Test" [concrete = constants.%str.c1c]
 // CHECK:STDOUT:   %int_4: %u64 = int_value 4 [concrete = constants.%int_4]
-// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_4) [concrete = constants.%String.val]
+// CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str, %int_4) [concrete = constants.%String.val]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 13 - 13
toolchain/lex/testdata/basic_syntax.carbon

@@ -12,21 +12,21 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lex/testdata/basic_syntax.carbon
 // CHECK:STDOUT: - filename: basic_syntax.carbon
 // CHECK:STDOUT:   tokens:
-// CHECK:STDOUT:   - { index:  0, kind:         "FileStart", line: {{ *\d+}}, column:   1, indent: 1, spelling: "" }
+// CHECK:STDOUT:   - { index:  0, kind:       "FileStart", line: {{ *\d+}}, column:   1, indent: 1, spelling: "" }
 
 fn run(str program) {
-// CHECK:STDOUT:   - { index:  1, kind:                "Fn", line: {{ *}}[[@LINE-1]], column:   1, indent: 1, spelling: "fn", has_leading_space: true }
-// CHECK:STDOUT:   - { index:  2, kind:        "Identifier", line: {{ *}}[[@LINE-2]], column:   4, indent: 1, spelling: "run", identifier: 0, has_leading_space: true }
-// CHECK:STDOUT:   - { index:  3, kind:         "OpenParen", line: {{ *}}[[@LINE-3]], column:   7, indent: 1, spelling: "(", closing_token: 6 }
-// CHECK:STDOUT:   - { index:  4, kind: "StringTypeLiteral", line: {{ *}}[[@LINE-4]], column:   8, indent: 1, spelling: "str" }
-// CHECK:STDOUT:   - { index:  5, kind:        "Identifier", line: {{ *}}[[@LINE-5]], column:  12, indent: 1, spelling: "program", identifier: 1, has_leading_space: true }
-// CHECK:STDOUT:   - { index:  6, kind:        "CloseParen", line: {{ *}}[[@LINE-6]], column:  19, indent: 1, spelling: ")", opening_token: 3 }
-// CHECK:STDOUT:   - { index:  7, kind:    "OpenCurlyBrace", line: {{ *}}[[@LINE-7]], column:  21, indent: 1, spelling: "{", closing_token: 11, has_leading_space: true }
+// CHECK:STDOUT:   - { index:  1, kind:              "Fn", line: {{ *}}[[@LINE-1]], column:   1, indent: 1, spelling: "fn", has_leading_space: true }
+// CHECK:STDOUT:   - { index:  2, kind:      "Identifier", line: {{ *}}[[@LINE-2]], column:   4, indent: 1, spelling: "run", identifier: 0, has_leading_space: true }
+// CHECK:STDOUT:   - { index:  3, kind:       "OpenParen", line: {{ *}}[[@LINE-3]], column:   7, indent: 1, spelling: "(", closing_token: 6 }
+// CHECK:STDOUT:   - { index:  4, kind:             "Str", line: {{ *}}[[@LINE-4]], column:   8, indent: 1, spelling: "str" }
+// CHECK:STDOUT:   - { index:  5, kind:      "Identifier", line: {{ *}}[[@LINE-5]], column:  12, indent: 1, spelling: "program", identifier: 1, has_leading_space: true }
+// CHECK:STDOUT:   - { index:  6, kind:      "CloseParen", line: {{ *}}[[@LINE-6]], column:  19, indent: 1, spelling: ")", opening_token: 3 }
+// CHECK:STDOUT:   - { index:  7, kind:  "OpenCurlyBrace", line: {{ *}}[[@LINE-7]], column:  21, indent: 1, spelling: "{", closing_token: 11, has_leading_space: true }
   return True;
-  // CHECK:STDOUT:   - { index:  8, kind:            "Return", line: {{ *}}[[@LINE-1]], column:   3, indent: 3, spelling: "return", has_leading_space: true }
-  // CHECK:STDOUT:   - { index:  9, kind:        "Identifier", line: {{ *}}[[@LINE-2]], column:  10, indent: 3, spelling: "True", identifier: 2, has_leading_space: true }
-  // CHECK:STDOUT:   - { index: 10, kind:              "Semi", line: {{ *}}[[@LINE-3]], column:  14, indent: 3, spelling: ";" }
+  // CHECK:STDOUT:   - { index:  8, kind:          "Return", line: {{ *}}[[@LINE-1]], column:   3, indent: 3, spelling: "return", has_leading_space: true }
+  // CHECK:STDOUT:   - { index:  9, kind:      "Identifier", line: {{ *}}[[@LINE-2]], column:  10, indent: 3, spelling: "True", identifier: 2, has_leading_space: true }
+  // CHECK:STDOUT:   - { index: 10, kind:            "Semi", line: {{ *}}[[@LINE-3]], column:  14, indent: 3, spelling: ";" }
 }
-// CHECK:STDOUT:   - { index: 11, kind:   "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column:   1, indent: 1, spelling: "}", opening_token: 7, has_leading_space: true }
+// CHECK:STDOUT:   - { index: 11, kind: "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column:   1, indent: 1, spelling: "}", opening_token: 7, has_leading_space: true }
 
-// CHECK:STDOUT:   - { index: 12, kind:           "FileEnd", line: {{ *}}[[@LINE+0]], column: {{ *\d+}}, indent: 1, spelling: "", has_leading_space: true }
+// CHECK:STDOUT:   - { index: 12, kind:         "FileEnd", line: {{ *}}[[@LINE+0]], column: {{ *\d+}}, indent: 1, spelling: "", has_leading_space: true }

+ 6 - 6
toolchain/lex/testdata/fail_block_string_second_line.carbon

@@ -19,9 +19,9 @@ var s: str = '''
 // CHECK:STDERR:
 // CHECK:STDOUT: - filename: fail_block_string_second_line.carbon
 // CHECK:STDOUT:   tokens:
-// CHECK:STDOUT:   - { index: 1, kind:               "Var", line: {{ *}}[[@LINE-17]], column:   1, indent: 1, spelling: "var", has_leading_space: true }
-// CHECK:STDOUT:   - { index: 2, kind:        "Identifier", line: {{ *}}[[@LINE-18]], column:   5, indent: 1, spelling: "s", identifier: 0, has_leading_space: true }
-// CHECK:STDOUT:   - { index: 3, kind:             "Colon", line: {{ *}}[[@LINE-19]], column:   6, indent: 1, spelling: ":" }
-// CHECK:STDOUT:   - { index: 4, kind: "StringTypeLiteral", line: {{ *}}[[@LINE-20]], column:   8, indent: 1, spelling: "str", has_leading_space: true }
-// CHECK:STDOUT:   - { index: 5, kind:             "Equal", line: {{ *}}[[@LINE-21]], column:  12, indent: 1, spelling: "=", has_leading_space: true }
-// CHECK:STDOUT:   - { index: 6, kind:     "StringLiteral", line: {{ *}}[[@LINE-22]], column:  14, indent: 1, spelling: "'''\n  error here: '''", value: "error here: ", has_leading_space: true }
+// CHECK:STDOUT:   - { index: 1, kind:           "Var", line: {{ *}}[[@LINE-17]], column:   1, indent: 1, spelling: "var", has_leading_space: true }
+// CHECK:STDOUT:   - { index: 2, kind:    "Identifier", line: {{ *}}[[@LINE-18]], column:   5, indent: 1, spelling: "s", identifier: 0, has_leading_space: true }
+// CHECK:STDOUT:   - { index: 3, kind:         "Colon", line: {{ *}}[[@LINE-19]], column:   6, indent: 1, spelling: ":" }
+// CHECK:STDOUT:   - { index: 4, kind:           "Str", line: {{ *}}[[@LINE-20]], column:   8, indent: 1, spelling: "str", has_leading_space: true }
+// CHECK:STDOUT:   - { index: 5, kind:         "Equal", line: {{ *}}[[@LINE-21]], column:  12, indent: 1, spelling: "=", has_leading_space: true }
+// CHECK:STDOUT:   - { index: 6, kind: "StringLiteral", line: {{ *}}[[@LINE-22]], column:  14, indent: 1, spelling: "'''\n  error here: '''", value: "error here: ", has_leading_space: true }

+ 2 - 1
toolchain/lex/token_kind.def

@@ -176,6 +176,7 @@ CARBON_KEYWORD_TOKEN(Auto,                "auto")
 CARBON_KEYWORD_TOKEN(Bool,                "bool")
 CARBON_KEYWORD_TOKEN(Break,               "break")
 CARBON_KEYWORD_TOKEN(Case,                "case")
+CARBON_KEYWORD_TOKEN(Char,                "char")
 CARBON_KEYWORD_TOKEN(Const,               "const")
 CARBON_KEYWORD_TOKEN(Continue,            "continue")
 CARBON_KEYWORD_TOKEN(Core,                "Core")
@@ -209,7 +210,7 @@ CARBON_KEYWORD_TOKEN(Returned,            "returned")
 CARBON_KEYWORD_TOKEN(SelfTypeIdentifier,  "Self")
 CARBON_KEYWORD_TOKEN(SelfValueIdentifier, "self")
 // TODO: Although we provide a `str` type literal, it's not standardized.
-CARBON_KEYWORD_TOKEN(StringTypeLiteral,   "str")
+CARBON_KEYWORD_TOKEN(Str,                 "str")
 CARBON_KEYWORD_TOKEN(Template,            "template")
 CARBON_KEYWORD_TOKEN(Then,                "then")
 CARBON_KEYWORD_TOKEN(True,                "true")

+ 6 - 1
toolchain/parse/handle_expr.cpp

@@ -114,6 +114,11 @@ auto HandleExprInPostfix(Context& context) -> void {
       context.PushState(state);
       break;
     }
+    case Lex::TokenKind::Char: {
+      context.AddLeafNode(NodeKind::CharTypeLiteral, context.Consume());
+      context.PushState(state);
+      break;
+    }
     case Lex::TokenKind::IntTypeLiteral: {
       context.AddLeafNode(NodeKind::IntTypeLiteral, context.Consume());
       context.PushState(state);
@@ -129,7 +134,7 @@ auto HandleExprInPostfix(Context& context) -> void {
       context.PushState(state);
       break;
     }
-    case Lex::TokenKind::StringTypeLiteral: {
+    case Lex::TokenKind::Str: {
       context.AddLeafNode(NodeKind::StringTypeLiteral, context.Consume());
       context.PushState(state);
       break;

+ 5 - 4
toolchain/parse/node_kind.def

@@ -251,14 +251,15 @@ CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(CharLiteral, CharLiteral)
 CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(RealLiteral, RealLiteral)
 CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(StringLiteral, StringLiteral)
 
+CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(AutoTypeLiteral, Auto)
 CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(BoolTypeLiteral, Bool)
+CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(CharTypeLiteral, Char)
+CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(FloatTypeLiteral, FloatTypeLiteral)
 CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(IntTypeLiteral, IntTypeLiteral)
+CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(StringTypeLiteral, Str)
+CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(TypeTypeLiteral, Type)
 CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(UnsignedIntTypeLiteral,
                                      UnsignedIntTypeLiteral)
-CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(FloatTypeLiteral, FloatTypeLiteral)
-CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(StringTypeLiteral, StringTypeLiteral)
-CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(TypeTypeLiteral, Type)
-CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(AutoTypeLiteral, Auto)
 
 CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Amp)
 CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Caret)

+ 9 - 0
toolchain/parse/testdata/basics/type_literals.carbon

@@ -10,6 +10,7 @@
 
 var test_i32: i32 = 0;
 var test_f64: f64 = 0.1;
+var test_char: char = 'c';
 var test_str: str = "Test";
 
 // CHECK:STDOUT: - filename: type_literals.carbon
@@ -32,6 +33,14 @@ var test_str: str = "Test";
 // CHECK:STDOUT:       {kind: 'RealLiteral', text: '0.1'},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'VariableIntroducer', text: 'var'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'test_char'},
+// CHECK:STDOUT:           {kind: 'CharTypeLiteral', text: 'char'},
+// CHECK:STDOUT:         {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'VariablePattern', text: 'var', subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
+// CHECK:STDOUT:       {kind: 'CharLiteral', text: ''c''},
+// CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'test_str'},
 // CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'VarBindingPattern', text: ':', subtree_size: 3},

+ 1 - 1
toolchain/sem_ir/inst_namer.cpp

@@ -774,7 +774,7 @@ auto InstNamer::NamingContext::NameInst() -> void {
       return;
     }
     case CARBON_KIND(ClassType inst): {
-      if (auto literal_info = NumericTypeLiteralInfo::ForType(sem_ir(), inst);
+      if (auto literal_info = TypeLiteralInfo::ForType(sem_ir(), inst);
           literal_info.is_valid()) {
         AddInstName(literal_info.GetLiteralAsString(sem_ir()));
       } else {

+ 7 - 0
toolchain/sem_ir/name_scope.h

@@ -355,6 +355,13 @@ class NameScopeStore {
   // Returns whether the provided scope ID is for the Core package.
   auto IsCorePackage(NameScopeId scope_id) const -> bool;
 
+  // Returns whether the provided scope ID is valid and is directly contained
+  // within the Core package.
+  auto IsInCorePackage(NameScopeId scope_id) const -> bool {
+    return scope_id.has_value() &&
+           IsCorePackage(Get(scope_id).parent_scope_id());
+  }
+
   auto OutputYaml() const -> Yaml::OutputMapping {
     return values_.OutputYaml();
   }

+ 2 - 2
toolchain/sem_ir/stringify.cpp

@@ -288,7 +288,7 @@ class Stringifier {
 
   auto StringifyInst(InstId /*inst_id*/, ClassType inst) -> void {
     const auto& class_info = sem_ir_->classes().Get(inst.class_id);
-    if (auto literal_info = NumericTypeLiteralInfo::ForType(*sem_ir_, inst);
+    if (auto literal_info = TypeLiteralInfo::ForType(*sem_ir_, inst);
         literal_info.is_valid()) {
       literal_info.PrintLiteral(*sem_ir_, *out_);
       return;
@@ -751,7 +751,7 @@ auto StringifySpecific(const File& sem_ir, SpecificId specific_id)
       // Print `Core.Int(N)` as `iN`.
       // TODO: This duplicates work done in StringifyInst for ClassType.
       const auto& class_info = sem_ir.classes().Get(class_decl.class_id);
-      if (auto literal_info = NumericTypeLiteralInfo::ForType(
+      if (auto literal_info = TypeLiteralInfo::ForType(
               sem_ir, ClassType{.type_id = TypeType::TypeId,
                                 .class_id = class_decl.class_id,
                                 .specific_id = specific_id});

+ 55 - 3
toolchain/sem_ir/type_info.cpp

@@ -85,9 +85,7 @@ auto NumericTypeLiteralInfo::ForType(const File& file, ClassType class_type)
 
   // The class must be declared in the `Core` package.
   const auto& class_info = file.classes().Get(class_type.class_id);
-  if (!class_info.scope_id.has_value() ||
-      !file.name_scopes().IsCorePackage(
-          file.name_scopes().Get(class_info.scope_id).parent_scope_id())) {
+  if (!file.name_scopes().IsInCorePackage(class_info.scope_id)) {
     return NumericTypeLiteralInfo::Invalid;
   }
 
@@ -135,4 +133,58 @@ auto NumericTypeLiteralInfo::GetLiteralAsString(const File& file) const
   return out.TakeStr();
 }
 
+auto TypeLiteralInfo::ForType(const File& file, ClassType class_type)
+    -> TypeLiteralInfo {
+  if (class_type.specific_id.has_value()) {
+    auto numeric = NumericTypeLiteralInfo::ForType(file, class_type);
+    if (numeric.is_valid()) {
+      return {.kind = Numeric, .numeric = numeric};
+    }
+    return {.kind = None};
+  }
+
+  // The class must be declared in the `Core` package.
+  const auto& class_info = file.classes().Get(class_type.class_id);
+  if (!file.name_scopes().IsInCorePackage(class_info.scope_id)) {
+    return {.kind = None};
+  }
+
+  // The class's name must be the name corresponding to a type literal.
+  auto name_ident = file.names().GetAsStringIfIdentifier(class_info.name_id);
+  if (!name_ident) {
+    return {.kind = None};
+  }
+
+  Kind kind = llvm::StringSwitch<Kind>(*name_ident)
+                  .Case("Char", Char)
+                  .Case("String", Str)
+                  .Default(None);
+  return {.kind = kind};
+}
+
+auto TypeLiteralInfo::PrintLiteral(const File& file,
+                                   llvm::raw_ostream& out) const -> void {
+  CARBON_CHECK(is_valid());
+  switch (kind) {
+    case None:
+      CARBON_FATAL("Printing invalid type literal");
+    case Numeric:
+      numeric.PrintLiteral(file, out);
+      break;
+    case Char:
+      out << "char";
+      break;
+    case Str:
+      out << "str";
+      break;
+  }
+}
+
+auto TypeLiteralInfo::GetLiteralAsString(const File& file) const
+    -> std::string {
+  RawStringOstream out;
+  PrintLiteral(file, out);
+  return out.TakeStr();
+}
+
 }  // namespace Carbon::SemIR

+ 32 - 0
toolchain/sem_ir/type_info.h

@@ -227,6 +227,38 @@ struct NumericTypeLiteralInfo {
   IntId bit_width_id;
 };
 
+// Information about a literal that corresponds to a type.
+struct TypeLiteralInfo {
+  enum Kind : char {
+    None,
+    // A numeric type literal such as `i8`; see `numeric` field for details.
+    Numeric,
+    // `char` / `Core.Char`.
+    Char,
+    // `str` / `Core.String`.
+    // TODO: Rename `Core.String` to `Core.Str`.
+    Str,
+  };
+
+  // Returns the type literal that would evaluate to this class type, if any.
+  static auto ForType(const File& file, ClassType class_type)
+      -> TypeLiteralInfo;
+
+  // Prints the type literal that corresponds to this type.
+  auto PrintLiteral(const File& file, llvm::raw_ostream& out) const -> void;
+
+  // Gets a string containing the literal.
+  auto GetLiteralAsString(const File& file) const -> std::string;
+
+  // Returns whether this is a valid type literal.
+  auto is_valid() const -> bool { return kind != None; }
+
+  // The kind of the literal.
+  Kind kind;
+  // If this is a numeric literal, additional information about the literal.
+  NumericTypeLiteralInfo numeric = NumericTypeLiteralInfo::Invalid;
+};
+
 inline constexpr NumericTypeLiteralInfo NumericTypeLiteralInfo::Invalid = {
     .kind = None, .bit_width_id = IntId::None};
 

+ 21 - 0
toolchain/testing/testdata/min_prelude/parts/char.carbon

@@ -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/parts/uint.carbon
+
+// --- min_prelude/parts/char.carbon
+
+package Core library "prelude/parts/char";
+
+import library "prelude/parts/uint";
+
+fn CharLiteral() -> type = "char_literal.make_type";
+
+class Char {
+  adapt u8;
+}
+
+impl CharLiteral() as ImplicitAs(Char) {
+  fn Convert[self: Self]() -> Char = "char.convert_checked";
+}

+ 2 - 0
toolchain/testing/testdata/min_prelude/primitives.carbon

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/bool.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/char.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/copy.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/float.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/int.carbon
@@ -16,6 +17,7 @@
 package Core library "prelude";
 
 export import library "prelude/parts/bool";
+export import library "prelude/parts/char";
 export import library "prelude/parts/copy";
 export import library "prelude/parts/float";
 export import library "prelude/parts/int";