浏览代码

Add basic support for strings to core, check, and lower. (#5963)

Add a `Core.String` class to the prelude representing a string view, and
rename the `String` keyword to `str` and make it evaluate to
`Core.String`.

`Core.String` is represented as a pair of a pointer to a character
(actually, to the first character of a string, but we don't have a way
of modeling that yet) and a size (which should be pointer-width, but is
currently always a `u64` as we don't have a `usize` equivalent yet).
`Core.String` values are generated directly by the toolchain for string
literal expressions.

This follows the direction established at the recent summit, but the
design implemented here has not been through the proposal process yet.
Richard Smith 8 月之前
父节点
当前提交
41ed82e033
共有 39 个文件被更改,包括 543 次插入516 次删除
  1. 1 0
      core/prelude/types.carbon
  2. 16 0
      core/prelude/types/string.carbon
  3. 1 0
      toolchain/check/BUILD
  4. 8 6
      toolchain/check/handle_literal.cpp
  5. 126 0
      toolchain/check/literal.cpp
  6. 12 0
      toolchain/check/literal.h
  7. 7 7
      toolchain/check/testdata/array/element_mismatches.carbon
  8. 12 12
      toolchain/check/testdata/for/actual.carbon
  9. 1 65
      toolchain/check/testdata/function/call/fail_not_callable.carbon
  10. 1 237
      toolchain/check/testdata/pointer/fail_address_of_value.carbon
  11. 173 0
      toolchain/check/testdata/primitives/string_literals.carbon
  12. 16 6
      toolchain/check/testdata/primitives/type_literals.carbon
  13. 0 123
      toolchain/check/testdata/var/fail_not_copyable.carbon
  14. 7 7
      toolchain/check/testdata/while/while.carbon
  15. 3 0
      toolchain/diagnostics/diagnostic_kind.def
  16. 2 2
      toolchain/driver/testdata/dump_shared_values.carbon
  17. 4 0
      toolchain/driver/testdata/fail_flush_errors.carbon
  18. 5 5
      toolchain/lex/testdata/basic_syntax.carbon
  19. 4 4
      toolchain/lex/testdata/fail_block_string_second_line.carbon
  20. 2 2
      toolchain/lex/token_kind.def
  21. 7 3
      toolchain/lower/constant.cpp
  22. 2 2
      toolchain/lower/testdata/primitives/false_true.carbon
  23. 2 2
      toolchain/lower/testdata/primitives/int_types.carbon
  24. 2 2
      toolchain/lower/testdata/primitives/numeric_literals.carbon
  25. 79 0
      toolchain/lower/testdata/primitives/string.carbon
  26. 2 2
      toolchain/lower/testdata/primitives/type_values.carbon
  27. 2 2
      toolchain/lower/testdata/primitives/zero.carbon
  28. 2 2
      toolchain/parse/testdata/basics/type_literals.carbon
  29. 2 2
      toolchain/parse/testdata/basics/value_literals.carbon
  30. 2 2
      toolchain/parse/testdata/generics/impl/empty_body.carbon
  31. 5 5
      toolchain/parse/testdata/generics/impl/fail_impl.carbon
  32. 4 4
      toolchain/parse/testdata/let/let.carbon
  33. 2 2
      toolchain/parse/testdata/let/let_tuple.carbon
  34. 2 2
      toolchain/parse/testdata/return/fail_returned_no_var.carbon
  35. 4 4
      toolchain/parse/testdata/return/returned_var.carbon
  36. 2 2
      toolchain/parse/testdata/var/var.carbon
  37. 2 2
      toolchain/parse/testdata/var/var_tuple.carbon
  38. 17 0
      toolchain/testing/testdata/min_prelude/parts/string.carbon
  39. 2 0
      toolchain/testing/testdata/min_prelude/primitives.carbon

+ 1 - 0
core/prelude/types.carbon

@@ -11,4 +11,5 @@ export import library "prelude/types/float_literal";
 export import library "prelude/types/int";
 export import library "prelude/types/int_literal";
 export import library "prelude/types/optional";
+export import library "prelude/types/string";
 export import library "prelude/types/uint";

+ 16 - 0
core/prelude/types/string.carbon

@@ -0,0 +1,16 @@
+// 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
+
+package Core library "prelude/types/string";
+
+import library "prelude/destroy";
+import library "prelude/types/char";
+import library "prelude/types/uint";
+
+class String {
+  // TODO: This should be an array iterator.
+  private var ptr: Char*;
+  // TODO: This should be a word-sized integer.
+  private var size: u64;
+}

+ 1 - 0
toolchain/check/BUILD

@@ -126,6 +126,7 @@ cc_library(
         "//toolchain/base:canonical_value_store",
         "//toolchain/base:index_base",
         "//toolchain/base:kind_switch",
+        "//toolchain/base:value_ids",
         "//toolchain/base:value_store",
         "//toolchain/check:generic_region_stack",
         "//toolchain/check:scope_stack",

+ 8 - 6
toolchain/check/handle_literal.cpp

@@ -6,6 +6,7 @@
 
 #include "toolchain/check/call.h"
 #include "toolchain/check/context.h"
+#include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/inst.h"
 #include "toolchain/check/literal.h"
@@ -66,11 +67,11 @@ auto HandleParseNode(Context& context, Parse::RealLiteralId node_id) -> bool {
 }
 
 auto HandleParseNode(Context& context, Parse::StringLiteralId node_id) -> bool {
-  AddInstAndPush<SemIR::StringLiteral>(
-      context, node_id,
-      {.type_id = GetSingletonType(context, SemIR::StringType::TypeInstId),
-       .string_literal_id = context.tokens().GetStringLiteralValue(
-           context.parse_tree().node_token(node_id))});
+  auto str_literal_id =
+      MakeStringLiteral(context, node_id,
+                        context.tokens().GetStringLiteralValue(
+                            context.parse_tree().node_token(node_id)));
+  context.node_stack().Push(node_id, str_literal_id);
   return true;
 }
 
@@ -128,7 +129,8 @@ auto HandleParseNode(Context& context, Parse::FloatTypeLiteralId node_id)
 
 auto HandleParseNode(Context& context, Parse::StringTypeLiteralId node_id)
     -> bool {
-  context.node_stack().Push(node_id, SemIR::StringType::TypeInstId);
+  auto type_inst_id = MakeStringTypeLiteral(context, node_id);
+  context.node_stack().Push(node_id, type_inst_id);
   return true;
 }
 

+ 126 - 0
toolchain/check/literal.cpp

@@ -9,6 +9,8 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/name_lookup.h"
 #include "toolchain/check/type.h"
+#include "toolchain/check/type_completion.h"
+#include "toolchain/diagnostics/diagnostic.h"
 #include "toolchain/lex/token_info.h"
 #include "toolchain/sem_ir/ids.h"
 
@@ -44,4 +46,128 @@ auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id,
   return PerformCall(context, node_id, fn_inst_id, {width_id});
 }
 
+namespace {
+// The extracted representation of the type `Core.String`.
+struct StringRepr {
+  SemIR::TypeId ptr_field_type_id;
+  SemIR::TypeId size_field_type_id;
+  SemIR::TypeStore::IntTypeInfo size_field_type_info;
+};
+}  // namespace
+
+// Extracts information about the representation of the `Core.String` type
+// necessary for building a string literal.
+static auto GetStringLiteralRepr(Context& context, SemIR::LocId loc_id,
+                                 SemIR::TypeId type_id)
+    -> std::optional<StringRepr> {
+  // The object representation should be a struct type.
+  auto object_repr_id = context.types().GetObjectRepr(type_id);
+  auto struct_repr =
+      context.types().TryGetAs<SemIR::StructType>(object_repr_id);
+  if (!struct_repr) {
+    return std::nullopt;
+  }
+
+  // The struct should have two fields.
+  auto fields = context.struct_type_fields().Get(struct_repr->fields_id);
+  if (fields.size() != 2) {
+    return std::nullopt;
+  }
+
+  // The first field should be a pointer to 8-bit integers.
+  auto ptr_type =
+      context.insts().TryGetAs<SemIR::PointerType>(fields[0].type_inst_id);
+  if (!ptr_type) {
+    return std::nullopt;
+  }
+  auto pointee_type_id =
+      context.types().GetTypeIdForTypeInstId(ptr_type->pointee_id);
+  if (!TryToCompleteType(context, pointee_type_id, loc_id)) {
+    return std::nullopt;
+  }
+  auto elem_type_info = context.types().TryGetIntTypeInfo(pointee_type_id);
+  if (!elem_type_info || context.ints().Get(elem_type_info->bit_width) != 8) {
+    return std::nullopt;
+  }
+
+  // The second field should be an integer type.
+  auto size_field_type_id =
+      context.types().GetTypeIdForTypeInstId(fields[1].type_inst_id);
+  auto size_type_info = context.types().TryGetIntTypeInfo(size_field_type_id);
+  if (!size_type_info) {
+    return std::nullopt;
+  }
+
+  return StringRepr{.ptr_field_type_id = context.types().GetTypeIdForTypeInstId(
+                        fields[0].type_inst_id),
+                    .size_field_type_id = size_field_type_id,
+                    .size_field_type_info = *size_type_info};
+}
+
+auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id,
+                       StringLiteralValueId value_id) -> SemIR::InstId {
+  auto str_type_id = MakeStringType(context, node_id);
+  if (!RequireCompleteType(context, str_type_id, node_id, [&] {
+        CARBON_DIAGNOSTIC(StringLiteralTypeIncomplete, Error,
+                          "type {0} is incomplete", SemIR::TypeId);
+        return context.emitter().Build(node_id, StringLiteralTypeIncomplete,
+                                       str_type_id);
+      })) {
+    return SemIR::ErrorInst::InstId;
+  }
+
+  auto repr = GetStringLiteralRepr(context, node_id, str_type_id);
+  if (!repr) {
+    if (str_type_id != SemIR::ErrorInst::TypeId) {
+      CARBON_DIAGNOSTIC(StringLiteralTypeUnexpected, Error,
+                        "unexpected representation for type {0}",
+                        SemIR::TypeId);
+      context.emitter().Emit(node_id, StringLiteralTypeUnexpected, str_type_id);
+    }
+    return SemIR::ErrorInst::InstId;
+  }
+
+  // The pointer field is a `StringLiteral` object.
+  // TODO: Perhaps `StringLiteral` should instead produce a durable reference,
+  // and we should take its address here?
+  auto ptr_value_id = AddInst<SemIR::StringLiteral>(
+      context, node_id,
+      {.type_id = repr->ptr_field_type_id, .string_literal_id = value_id});
+
+  // The size field is an integer literal.
+  auto size = context.string_literal_values().Get(value_id).size();
+  if (repr->size_field_type_info.bit_width.has_value()) {
+    // Check that the size value fits in the size field.
+    auto width = context.ints()
+                     .Get(repr->size_field_type_info.bit_width)
+                     .getLimitedValue();
+    if (repr->size_field_type_info.is_signed ? !llvm::isIntN(width, size)
+                                             : !llvm::isUIntN(width, size)) {
+      CARBON_DIAGNOSTIC(StringLiteralTooLong, Error,
+                        "string literal is too long");
+      context.emitter().Emit(node_id, StringLiteralTooLong);
+      return SemIR::ErrorInst::InstId;
+    }
+  }
+  auto size_value_id =
+      AddInst<SemIR::IntValue>(context, node_id,
+                               {.type_id = repr->size_field_type_id,
+                                .int_id = context.ints().Add(size)});
+
+  // Build the representation struct.
+  auto elements_id = context.inst_blocks().Add({ptr_value_id, size_value_id});
+  return AddInst<SemIR::StructValue>(
+      context, node_id, {.type_id = str_type_id, .elements_id = elements_id});
+}
+
+auto MakeStringTypeLiteral(Context& context, Parse::NodeId node_id)
+    -> SemIR::InstId {
+  return LookupNameInCore(context, node_id, "String");
+}
+
+auto MakeStringType(Context& context, Parse::NodeId node_id) -> SemIR::TypeId {
+  auto type_inst_id = MakeStringTypeLiteral(context, node_id);
+  return ExprAsType(context, node_id, type_inst_id).type_id;
+}
+
 }  // namespace Carbon::Check

+ 12 - 0
toolchain/check/literal.h

@@ -5,6 +5,7 @@
 #ifndef CARBON_TOOLCHAIN_CHECK_LITERAL_H_
 #define CARBON_TOOLCHAIN_CHECK_LITERAL_H_
 
+#include "toolchain/base/value_ids.h"
 #include "toolchain/check/context.h"
 #include "toolchain/lex/token_info.h"
 #include "toolchain/sem_ir/ids.h"
@@ -29,6 +30,17 @@ auto MakeIntType(Context& context, Parse::NodeId node_id,
 auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id,
                           IntId size_id) -> SemIR::InstId;
 
+// Forms a string literal value instruction for a given string literal.
+auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id,
+                       StringLiteralValueId value_id) -> SemIR::InstId;
+
+// Forms a string literal type expression for a `str` literal.
+auto MakeStringTypeLiteral(Context& context, Parse::NodeId node_id)
+    -> SemIR::InstId;
+
+// Forms a string type.
+auto MakeStringType(Context& context, Parse::NodeId node_id) -> SemIR::TypeId;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_LITERAL_H_

+ 7 - 7
toolchain/check/testdata/array/element_mismatches.carbon

@@ -16,14 +16,14 @@ library "[[@TEST_NAME]]";
 
 class C {}
 
-// CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+7]]:22: error: cannot implicitly convert expression of type `String` to `C` [ConversionFailure]
-// CHECK:STDERR: var a: array(C, 3) = ({}, "Hello", "World");
-// CHECK:STDERR:                      ^~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+4]]:22: note: type `String` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessNote]
-// CHECK:STDERR: var a: array(C, 3) = ({}, "Hello", "World");
-// CHECK:STDERR:                      ^~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+7]]:22: error: cannot implicitly convert expression of type `()` to `C` [ConversionFailure]
+// CHECK:STDERR: var a: array(C, 3) = ({}, (), true);
+// CHECK:STDERR:                      ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+4]]:22: note: type `()` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var a: array(C, 3) = ({}, (), true);
+// CHECK:STDERR:                      ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-var a: array(C, 3) = ({}, "Hello", "World");
+var a: array(C, 3) = ({}, (), true);
 
 // --- fail_var_wrong_type.carbon
 

+ 12 - 12
toolchain/check/testdata/for/actual.carbon

@@ -222,28 +222,28 @@ fn Read() {
 // CHECK:STDOUT:   %Core.import_ref.1c9: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc12_18, loaded [concrete = constants.%assoc0.724]
 // CHECK:STDOUT:   %Core.import_ref.ed6: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc13_17, loaded [concrete = constants.%assoc1.02e]
 // CHECK:STDOUT:   %Core.import_ref.9e6: type = import_ref Core//prelude/iterate, loc13_17, loaded [concrete = %CursorType]
-// CHECK:STDOUT:   %Core.import_ref.f49: @Optional.%Optional.None.type (%Optional.None.type.ef2) = import_ref Core//prelude/iterate, inst138 [indirect], loaded [symbolic = @Optional.%Optional.None (constants.%Optional.None.fd6)]
-// CHECK:STDOUT:   %Core.import_ref.1a8: @Optional.%Optional.Some.type (%Optional.Some.type.b2c) = import_ref Core//prelude/iterate, inst139 [indirect], loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.d0d)]
-// CHECK:STDOUT:   %Core.import_ref.36a9: @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op.type (%Optional.as.Destroy.impl.Op.type.764) = import_ref Core//prelude/iterate, inst6889 [indirect], loaded [symbolic = @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op (constants.%Optional.as.Destroy.impl.Op.bf8)]
+// CHECK:STDOUT:   %Core.import_ref.f49: @Optional.%Optional.None.type (%Optional.None.type.ef2) = import_ref Core//prelude/iterate, inst139 [indirect], loaded [symbolic = @Optional.%Optional.None (constants.%Optional.None.fd6)]
+// CHECK:STDOUT:   %Core.import_ref.1a8: @Optional.%Optional.Some.type (%Optional.Some.type.b2c) = import_ref Core//prelude/iterate, inst140 [indirect], loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.d0d)]
+// CHECK:STDOUT:   %Core.import_ref.36a9: @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op.type (%Optional.as.Destroy.impl.Op.type.764) = import_ref Core//prelude/iterate, inst6890 [indirect], loaded [symbolic = @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op (constants.%Optional.as.Destroy.impl.Op.bf8)]
 // CHECK:STDOUT:   %Destroy.impl_witness_table.2ff = impl_witness_table (%Core.import_ref.36a9), @Optional.as.Destroy.impl [concrete]
-// CHECK:STDOUT:   %Core.import_ref.cf4: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/iterate, inst482 [indirect], loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)]
+// CHECK:STDOUT:   %Core.import_ref.cf4: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/iterate, inst483 [indirect], loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.2b9 = impl_witness_table (%Core.import_ref.cf4), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
-// CHECK:STDOUT:   %Core.import_ref.741: @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op.type (%Int.as.Destroy.impl.Op.type) = import_ref Core//prelude/iterate, inst450 [indirect], loaded [symbolic = @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op (constants.%Int.as.Destroy.impl.Op)]
+// CHECK:STDOUT:   %Core.import_ref.741: @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op.type (%Int.as.Destroy.impl.Op.type) = import_ref Core//prelude/iterate, inst451 [indirect], loaded [symbolic = @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op (constants.%Int.as.Destroy.impl.Op)]
 // CHECK:STDOUT:   %Destroy.impl_witness_table.1b4 = impl_witness_table (%Core.import_ref.741), @Int.as.Destroy.impl [concrete]
-// CHECK:STDOUT:   %Core.import_ref.19a: @OrderedWith.%OrderedWith.assoc_type (%OrderedWith.assoc_type.03c) = import_ref Core//prelude/iterate, inst862 [indirect], loaded [symbolic = @OrderedWith.%assoc0 (constants.%assoc0.5db)]
-// CHECK:STDOUT:   %Core.import_ref.b2b: @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.2c7) = import_ref Core//prelude/iterate, inst951 [indirect], loaded [symbolic = @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.a5a)]
-// CHECK:STDOUT:   %Core.import_ref.ab6 = import_ref Core//prelude/iterate, inst952 [indirect], unloaded
-// CHECK:STDOUT:   %Core.import_ref.875 = import_ref Core//prelude/iterate, inst953 [indirect], unloaded
-// CHECK:STDOUT:   %Core.import_ref.82b = import_ref Core//prelude/iterate, inst954 [indirect], unloaded
+// CHECK:STDOUT:   %Core.import_ref.19a: @OrderedWith.%OrderedWith.assoc_type (%OrderedWith.assoc_type.03c) = import_ref Core//prelude/iterate, inst863 [indirect], loaded [symbolic = @OrderedWith.%assoc0 (constants.%assoc0.5db)]
+// CHECK:STDOUT:   %Core.import_ref.b2b: @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.2c7) = import_ref Core//prelude/iterate, inst952 [indirect], loaded [symbolic = @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.a5a)]
+// CHECK:STDOUT:   %Core.import_ref.ab6 = import_ref Core//prelude/iterate, inst953 [indirect], unloaded
+// CHECK:STDOUT:   %Core.import_ref.875 = import_ref Core//prelude/iterate, inst954 [indirect], unloaded
+// CHECK:STDOUT:   %Core.import_ref.82b = import_ref Core//prelude/iterate, inst955 [indirect], unloaded
 // CHECK:STDOUT:   %OrderedWith.impl_witness_table.476 = impl_witness_table (%Core.import_ref.b2b, %Core.import_ref.ab6, %Core.import_ref.875, %Core.import_ref.82b), @Int.as.OrderedWith.impl.db3 [concrete]
-// CHECK:STDOUT:   %Core.import_ref.13d: @OrderedWith.%OrderedWith.Less.type (%OrderedWith.Less.type.f19) = import_ref Core//prelude/iterate, inst1926 [indirect], loaded [symbolic = @OrderedWith.%OrderedWith.Less (constants.%OrderedWith.Less.02e)]
+// CHECK:STDOUT:   %Core.import_ref.13d: @OrderedWith.%OrderedWith.Less.type (%OrderedWith.Less.type.f19) = import_ref Core//prelude/iterate, inst1927 [indirect], loaded [symbolic = @OrderedWith.%OrderedWith.Less (constants.%OrderedWith.Less.02e)]
 // CHECK:STDOUT:   %CursorType: type = assoc_const_decl @CursorType [concrete] {}
 // CHECK:STDOUT:   %Core.import_ref.4f9: type = import_ref Core//prelude/iterate, loc12_18, loaded [concrete = %ElementType]
 // CHECK:STDOUT:   %ElementType: type = assoc_const_decl @ElementType [concrete] {}
 // CHECK:STDOUT:   %Core.Optional: %Optional.type = import_ref Core//prelude/types/optional, Optional, loaded [concrete = constants.%Optional.generic]
 // CHECK:STDOUT:   %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
 // CHECK:STDOUT:   %Core.OrderedWith: %OrderedWith.type.270 = import_ref Core//prelude/operators/comparison, OrderedWith, loaded [concrete = constants.%OrderedWith.generic]
-// CHECK:STDOUT:   %Core.import_ref.d49 = import_ref Core//prelude/iterate, inst6644 [indirect], unloaded
+// CHECK:STDOUT:   %Core.import_ref.d49 = import_ref Core//prelude/iterate, inst6645 [indirect], unloaded
 // CHECK:STDOUT:   %Core.Inc: type = import_ref Core//prelude/operators/arithmetic, Inc, loaded [concrete = constants.%Inc.type]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT: }

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

@@ -3,8 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon
-// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
-// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -13,71 +11,9 @@
 // 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 `String` is not callable [CallToNonCallable]
+  // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+4]]:16: error: value of type `Core.String` is not callable [CallToNonCallable]
   // CHECK:STDERR:   var x: i32 = "hello"();
   // CHECK:STDERR:                ^~~~~~~~~
   // CHECK:STDERR:
   var x: i32 = "hello"();
 }
-
-// CHECK:STDOUT: --- fail_not_callable.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %Run.type: type = fn_type @Run [concrete]
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %Run: %Run.type = struct_value () [concrete]
-// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %Int.type: type = generic_class_type @Int [concrete]
-// CHECK:STDOUT:   %Int.generic: %Int.type = struct_value () [concrete]
-// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.7ce: type = pattern_type %i32 [concrete]
-// CHECK:STDOUT:   %str: String = string_literal "hello" [concrete]
-// CHECK:STDOUT:   %Destroy.type: type = facet_type <@Destroy> [concrete]
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.type.b8f: type = fn_type @Int.as.Destroy.impl.Op, @Int.as.Destroy.impl(%int_32) [concrete]
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.715: %Int.as.Destroy.impl.Op.type.b8f = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Destroy.impl.Op.715, @Int.as.Destroy.impl.Op(%int_32) [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
-// CHECK:STDOUT:     .Int = %Core.Int
-// CHECK:STDOUT:     .Destroy = %Core.Destroy
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
-// CHECK:STDOUT:   %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .Run = %Run.decl
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {}
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @Run() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %x.patt: %pattern_type.7ce = binding_pattern x [concrete]
-// CHECK:STDOUT:     %x.var_patt: %pattern_type.7ce = var_pattern %x.patt [concrete]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x.var: ref %i32 = var %x.var_patt
-// CHECK:STDOUT:   %str: String = string_literal "hello" [concrete = constants.%str]
-// CHECK:STDOUT:   assign %x.var, <error>
-// CHECK:STDOUT:   %.loc20: type = splice_block %i32 [concrete = constants.%i32] {
-// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x: ref %i32 = bind_name x, %x.var
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.bound: <bound method> = bound_method %x.var, constants.%Int.as.Destroy.impl.Op.715
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function constants.%Int.as.Destroy.impl.Op.715, @Int.as.Destroy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Destroy.impl.Op.specific_fn]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %x.var, %Int.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %x.var
-// CHECK:STDOUT:   %Int.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr)
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 1 - 237
toolchain/check/testdata/pointer/fail_address_of_value.carbon

@@ -2,9 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
-// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
-// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -103,237 +101,3 @@ fn AddressOfParam(param: i32) {
   // CHECK:STDERR:
   var param_addr: i32* = &param;
 }
-
-// CHECK:STDOUT: --- fail_address_of_value.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %Int.type: type = generic_class_type @Int [concrete]
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %Int.generic: %Int.type = struct_value () [concrete]
-// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.7ce: type = pattern_type %i32 [concrete]
-// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
-// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
-// CHECK:STDOUT:   %struct_type.a.ba9: type = struct_type {.a: %i32} [concrete]
-// CHECK:STDOUT:   %pattern_type.268: type = pattern_type %struct_type.a.ba9 [concrete]
-// CHECK:STDOUT:   %H.type: type = fn_type @H [concrete]
-// CHECK:STDOUT:   %H: %H.type = struct_value () [concrete]
-// CHECK:STDOUT:   %AddressOfLiteral.type: type = fn_type @AddressOfLiteral [concrete]
-// CHECK:STDOUT:   %AddressOfLiteral: %AddressOfLiteral.type = struct_value () [concrete]
-// CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete]
-// CHECK:STDOUT:   %ptr.1d1: type = ptr_type Core.IntLiteral [concrete]
-// CHECK:STDOUT:   %true: bool = bool_literal true [concrete]
-// CHECK:STDOUT:   %ptr.bb2: type = ptr_type bool [concrete]
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
-// CHECK:STDOUT:   %ptr.5f5: type = ptr_type Core.FloatLiteral [concrete]
-// CHECK:STDOUT:   %ptr.a45: type = ptr_type String [concrete]
-// CHECK:STDOUT:   %str: String = string_literal "Hello" [concrete]
-// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
-// CHECK:STDOUT:   %int_2: Core.IntLiteral = int_value 2 [concrete]
-// CHECK:STDOUT:   %tuple.type: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete]
-// CHECK:STDOUT:   %ptr.b50: type = ptr_type %tuple.type [concrete]
-// CHECK:STDOUT:   %int_5: Core.IntLiteral = int_value 5 [concrete]
-// CHECK:STDOUT:   %struct_type.a.a6c: type = struct_type {.a: Core.IntLiteral} [concrete]
-// CHECK:STDOUT:   %ptr.4e0: type = ptr_type %struct_type.a.a6c [concrete]
-// CHECK:STDOUT:   %AddressOfOperator.type: type = fn_type @AddressOfOperator [concrete]
-// CHECK:STDOUT:   %AddressOfOperator: %AddressOfOperator.type = struct_value () [concrete]
-// CHECK:STDOUT:   %false: bool = bool_literal false [concrete]
-// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
-// CHECK:STDOUT:   %Destroy.type: type = facet_type <@Destroy> [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.c80: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%struct_type.a.ba9) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.691: %T.as.Destroy.impl.Op.type.c80 = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.1bb: type = ptr_type %struct_type.a.ba9 [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn.51b: <specific function> = specific_function %T.as.Destroy.impl.Op.691, @T.as.Destroy.impl.Op(%struct_type.a.ba9) [concrete]
-// CHECK:STDOUT:   %AddressOfCall.type: type = fn_type @AddressOfCall [concrete]
-// CHECK:STDOUT:   %AddressOfCall: %AddressOfCall.type = struct_value () [concrete]
-// CHECK:STDOUT:   %AddressOfType.type: type = fn_type @AddressOfType [concrete]
-// CHECK:STDOUT:   %AddressOfType: %AddressOfType.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.db7: type = ptr_type type [concrete]
-// CHECK:STDOUT:   %const: type = const_type %i32 [concrete]
-// CHECK:STDOUT:   %ptr.36b: type = ptr_type %const [concrete]
-// CHECK:STDOUT:   %AddressOfTupleElementValue.type: type = fn_type @AddressOfTupleElementValue [concrete]
-// CHECK:STDOUT:   %AddressOfTupleElementValue: %AddressOfTupleElementValue.type = struct_value () [concrete]
-// CHECK:STDOUT:   %tuple: %tuple.type = tuple_value (%int_1, %int_2) [concrete]
-// CHECK:STDOUT:   %AddressOfParam.type: type = fn_type @AddressOfParam [concrete]
-// CHECK:STDOUT:   %AddressOfParam: %AddressOfParam.type = struct_value () [concrete]
-// CHECK:STDOUT:   %pattern_type.fe8: type = pattern_type %ptr.235 [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.ba2: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%ptr.235) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.649: %T.as.Destroy.impl.Op.type.ba2 = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.5d5: type = ptr_type %ptr.235 [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn.829: <specific function> = specific_function %T.as.Destroy.impl.Op.649, @T.as.Destroy.impl.Op(%ptr.235) [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
-// CHECK:STDOUT:     .Int = %Core.Int
-// CHECK:STDOUT:     .Destroy = %Core.Destroy
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic]
-// CHECK:STDOUT:   %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .G = %G.decl
-// CHECK:STDOUT:     .H = %H.decl
-// CHECK:STDOUT:     .AddressOfLiteral = %AddressOfLiteral.decl
-// CHECK:STDOUT:     .AddressOfOperator = %AddressOfOperator.decl
-// CHECK:STDOUT:     .AddressOfCall = %AddressOfCall.decl
-// CHECK:STDOUT:     .AddressOfType = %AddressOfType.decl
-// CHECK:STDOUT:     .AddressOfTupleElementValue = %AddressOfTupleElementValue.decl
-// CHECK:STDOUT:     .AddressOfParam = %AddressOfParam.decl
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
-// CHECK:STDOUT:     %return.patt: %pattern_type.7ce = return_slot_pattern [concrete]
-// CHECK:STDOUT:     %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt, call_param0 [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param0
-// CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {
-// CHECK:STDOUT:     %return.patt: %pattern_type.268 = return_slot_pattern [concrete]
-// CHECK:STDOUT:     %return.param_patt: %pattern_type.268 = out_param_pattern %return.patt, call_param0 [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.ba9]
-// CHECK:STDOUT:     %return.param: ref %struct_type.a.ba9 = out_param call_param0
-// CHECK:STDOUT:     %return: ref %struct_type.a.ba9 = return_slot %return.param
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %AddressOfLiteral.decl: %AddressOfLiteral.type = fn_decl @AddressOfLiteral [concrete = constants.%AddressOfLiteral] {} {}
-// CHECK:STDOUT:   %AddressOfOperator.decl: %AddressOfOperator.type = fn_decl @AddressOfOperator [concrete = constants.%AddressOfOperator] {} {}
-// CHECK:STDOUT:   %AddressOfCall.decl: %AddressOfCall.type = fn_decl @AddressOfCall [concrete = constants.%AddressOfCall] {} {}
-// CHECK:STDOUT:   %AddressOfType.decl: %AddressOfType.type = fn_decl @AddressOfType [concrete = constants.%AddressOfType] {} {}
-// CHECK:STDOUT:   %AddressOfTupleElementValue.decl: %AddressOfTupleElementValue.type = fn_decl @AddressOfTupleElementValue [concrete = constants.%AddressOfTupleElementValue] {} {}
-// CHECK:STDOUT:   %AddressOfParam.decl: %AddressOfParam.type = fn_decl @AddressOfParam [concrete = constants.%AddressOfParam] {
-// CHECK:STDOUT:     %param.patt: %pattern_type.7ce = binding_pattern param [concrete]
-// CHECK:STDOUT:     %param.param_patt: %pattern_type.7ce = value_param_pattern %param.patt, call_param0 [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %param.param: %i32 = value_param call_param0
-// CHECK:STDOUT:     %.loc99: type = splice_block %i32.loc99 [concrete = constants.%i32] {
-// CHECK:STDOUT:       %int_32.loc99: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:       %i32.loc99: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     }
-// CHECK:STDOUT:     %param: %i32 = bind_name param, %param.param
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @G() -> %i32;
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @H() -> %struct_type.a.ba9;
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfLiteral() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0]
-// CHECK:STDOUT:   %addr.loc24: %ptr.1d1 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %true: bool = bool_literal true [concrete = constants.%true]
-// CHECK:STDOUT:   %addr.loc29: %ptr.bb2 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float]
-// CHECK:STDOUT:   %addr.loc34: %ptr.5f5 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %str: String = string_literal "Hello" [concrete = constants.%str]
-// CHECK:STDOUT:   %addr.loc39: %ptr.a45 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
-// CHECK:STDOUT:   %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2]
-// CHECK:STDOUT:   %.loc44: %tuple.type = tuple_literal (%int_1, %int_2)
-// CHECK:STDOUT:   %addr.loc44: %ptr.b50 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5]
-// CHECK:STDOUT:   %.loc49: %struct_type.a.a6c = struct_literal (%int_5)
-// CHECK:STDOUT:   %addr.loc49: %ptr.4e0 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfOperator() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %true.loc57: bool = bool_literal true [concrete = constants.%true]
-// CHECK:STDOUT:   %false.loc57_10: bool = bool_literal false [concrete = constants.%false]
-// CHECK:STDOUT:   if %true.loc57 br !and.rhs else br !and.result(%false.loc57_10)
-// CHECK:STDOUT:
-// CHECK:STDOUT: !and.rhs:
-// CHECK:STDOUT:   %false.loc57_14: bool = bool_literal false [concrete = constants.%false]
-// CHECK:STDOUT:   br !and.result(%false.loc57_14)
-// CHECK:STDOUT:
-// CHECK:STDOUT: !and.result:
-// CHECK:STDOUT:   %.loc57: bool = block_arg !and.result [concrete = constants.%false]
-// CHECK:STDOUT:   %addr.loc57: %ptr.bb2 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %H.ref: %H.type = name_ref H, file.%H.decl [concrete = constants.%H]
-// CHECK:STDOUT:   %H.call: init %struct_type.a.ba9 = call %H.ref()
-// CHECK:STDOUT:   %.loc62_6.1: ref %struct_type.a.ba9 = temporary_storage
-// CHECK:STDOUT:   %.loc62_6.2: ref %struct_type.a.ba9 = temporary %.loc62_6.1, %H.call
-// CHECK:STDOUT:   %.loc62_7: ref %i32 = struct_access %.loc62_6.2, element0
-// CHECK:STDOUT:   %addr.loc62_3: %ptr.235 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %true.loc67: bool = bool_literal true [concrete = constants.%true]
-// CHECK:STDOUT:   %.loc67: bool = not %true.loc67 [concrete = constants.%false]
-// CHECK:STDOUT:   %addr.loc67: %ptr.bb2 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc62_6.1, constants.%T.as.Destroy.impl.Op.691
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function constants.%T.as.Destroy.impl.Op.691, @T.as.Destroy.impl.Op(constants.%struct_type.a.ba9) [concrete = constants.%T.as.Destroy.impl.Op.specific_fn.51b]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %.loc62_6.1, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc62_6: %ptr.1bb = addr_of %.loc62_6.1
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc62_6)
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfCall() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G]
-// CHECK:STDOUT:   %G.call: init %i32 = call %G.ref()
-// CHECK:STDOUT:   %addr: %ptr.235 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfType() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %int_32.loc83: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:   %i32.loc83: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %addr.loc83: %ptr.db7 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %int_32.loc88: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:   %i32.loc88: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %const: type = const_type %i32.loc88 [concrete = constants.%const]
-// CHECK:STDOUT:   %ptr: type = ptr_type %const [concrete = constants.%ptr.36b]
-// CHECK:STDOUT:   %addr.loc88: %ptr.db7 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfTupleElementValue() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
-// CHECK:STDOUT:   %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2]
-// CHECK:STDOUT:   %.loc96_10.1: %tuple.type = tuple_literal (%int_1, %int_2)
-// CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0]
-// CHECK:STDOUT:   %tuple: %tuple.type = tuple_value (%int_1, %int_2) [concrete = constants.%tuple]
-// CHECK:STDOUT:   %.loc96_10.2: %tuple.type = converted %.loc96_10.1, %tuple [concrete = constants.%tuple]
-// CHECK:STDOUT:   %tuple.elem0: Core.IntLiteral = tuple_access %.loc96_10.2, element0 [concrete = constants.%int_1]
-// CHECK:STDOUT:   %addr: %ptr.1d1 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @AddressOfParam(%param.param: %i32) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %param_addr.patt: %pattern_type.fe8 = binding_pattern param_addr [concrete]
-// CHECK:STDOUT:     %param_addr.var_patt: %pattern_type.fe8 = var_pattern %param_addr.patt [concrete]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %param_addr.var: ref %ptr.235 = var %param_addr.var_patt
-// CHECK:STDOUT:   %param.ref: %i32 = name_ref param, %param
-// CHECK:STDOUT:   %addr.loc104_26: %ptr.235 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   assign %param_addr.var, %addr.loc104_26
-// CHECK:STDOUT:   %.loc104: type = splice_block %ptr [concrete = constants.%ptr.235] {
-// CHECK:STDOUT:     %int_32.loc104: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %i32.loc104: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %ptr: type = ptr_type %i32.loc104 [concrete = constants.%ptr.235]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %param_addr: ref %ptr.235 = bind_name param_addr, %param_addr.var
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %param_addr.var, constants.%T.as.Destroy.impl.Op.649
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function constants.%T.as.Destroy.impl.Op.649, @T.as.Destroy.impl.Op(constants.%ptr.235) [concrete = constants.%T.as.Destroy.impl.Op.specific_fn.829]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %param_addr.var, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc104_3: %ptr.5d5 = addr_of %param_addr.var
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc104_3)
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 173 - 0
toolchain/check/testdata/primitives/string_literals.carbon

@@ -0,0 +1,173 @@
+// 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/uint.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/primitives/string_literals.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/primitives/string_literals.carbon
+
+// --- fail_no_string.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  // CHECK:STDERR: fail_no_string.carbon:[[@LINE+4]]:3: error: name `Core.String` implicitly referenced here, but not found [CoreNameNotFound]
+  // CHECK:STDERR:   "hello";
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  "hello";
+}
+
+// --- unusual_string.carbon
+
+package Core library "[[@TEST_NAME]]";
+
+import library "prelude/parts/uint";
+
+class String {
+  var begin: u8*;
+  var end: u8*;
+}
+
+// --- fail_use_unusual_string.carbon
+
+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: let x: str = "hello";
+// CHECK:STDERR:              ^~~~~~~
+// CHECK:STDERR:
+let x: str = "hello";
+
+// --- incomplete_string.carbon
+
+package Core library "[[@TEST_NAME]]";
+
+class String;
+
+// --- fail_use_incomplete_string.carbon
+
+library "[[@TEST_NAME]]";
+
+import Core library "incomplete_string";
+
+fn F() {
+  // CHECK:STDERR: fail_use_incomplete_string.carbon:[[@LINE+7]]:3: error: type `Core.String` is incomplete [StringLiteralTypeIncomplete]
+  // CHECK:STDERR:   "hello";
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: incomplete_string.carbon:4:1: note: class was forward declared here [ClassForwardDeclaredHere]
+  // CHECK:STDERR: class String;
+  // CHECK:STDERR: ^~~~~~~~~~~~~
+  // CHECK:STDERR:
+  "hello";
+}
+
+// --- small_string.carbon
+
+package Core library "[[@TEST_NAME]]";
+
+import library "prelude/parts/uint";
+
+class String {
+  var ptr: u8*;
+  // An intentionally small `size` field to test the behavior when a string
+  // literal is large enough that its size can't be represented.
+  var size: u8;
+}
+
+// --- use_small_string.carbon
+
+library "[[@TEST_NAME]]";
+
+import Core library "small_string";
+
+//@dump-sem-ir-begin
+let x: str = "hello";
+//@dump-sem-ir-end
+
+// --- fail_overfill_small_string.carbon
+
+library "[[@TEST_NAME]]";
+
+import Core library "small_string";
+
+//@dump-sem-ir-begin
+// CHECK:STDERR: fail_overfill_small_string.carbon:[[@LINE+4]]:14: error: string literal is too long [StringLiteralTooLong]
+// CHECK:STDERR: let x: str = '''
+// CHECK:STDERR:              ^~~
+// CHECK:STDERR:
+let x: str = '''
+A string so long its size does not fit in `u8`. This results in an error,
+because we can't form a `Core.String` value that describes this string.
+
+This string contains 256 characters. Among them are:
+
+* 6 `g`s,
+* 21 `s`s,
+* 9 newlines,
+* and only one `p`.
+''';
+//@dump-sem-ir-end
+
+// CHECK:STDOUT: --- use_small_string.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %String: 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:   %int_5: %u8 = int_value 5 [concrete]
+// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_5) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// 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: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str]
+// 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:   <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:   %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: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// 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: }
+// 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:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 16 - 6
toolchain/check/testdata/primitives/type_literals.carbon

@@ -45,7 +45,7 @@ var test_f128: f128;
 library "[[@TEST_NAME]]";
 
 //@dump-sem-ir-begin
-let test_str: String = "Test";
+let test_str: str = "Test";
 //@dump-sem-ir-end
 
 // --- type.carbon
@@ -393,8 +393,16 @@ var x: type = 42;
 // CHECK:STDOUT: --- string.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %pattern_type: type = pattern_type String [concrete]
-// CHECK:STDOUT:   %str: String = string_literal "Test" [concrete]
+// CHECK:STDOUT:   %String: 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:   %int_4: %u64 = int_value 4 [concrete]
+// CHECK:STDOUT:   %String.val: %String = struct_value (%str, %int_4) [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -402,14 +410,16 @@ var x: type = 42;
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %test_str.patt: %pattern_type = binding_pattern test_str [concrete]
+// 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.%str
+// CHECK:STDOUT:   %test_str: %String = bind_name test_str, @__global_init.%String.val
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %str: String = string_literal "Test" [concrete = constants.%str]
+// CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "Test" [concrete = constants.%str]
+// 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:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 123
toolchain/check/testdata/var/fail_not_copyable.carbon

@@ -3,8 +3,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon
-// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
-// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -16,14 +14,6 @@ class X {
 }
 
 fn F(x: X) {
-  // TODO: Strings should eventually be copyable, once we decide how to
-  // represent them.
-  // CHECK:STDERR: fail_not_copyable.carbon:[[@LINE+4]]:19: error: cannot copy value of type `String` [CopyOfUncopyableType]
-  // CHECK:STDERR:   var s: String = "hello";
-  // CHECK:STDERR:                   ^~~~~~~
-  // CHECK:STDERR:
-  var s: String = "hello";
-
   // TODO: Decide on rules for when classes are copyable.
   // CHECK:STDERR: fail_not_copyable.carbon:[[@LINE+4]]:14: error: cannot copy value of type `X` [CopyOfUncopyableType]
   // CHECK:STDERR:   var y: X = x;
@@ -31,116 +21,3 @@ fn F(x: X) {
   // CHECK:STDERR:
   var y: X = x;
 }
-
-// CHECK:STDOUT: --- fail_not_copyable.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %X: type = class_type @X [concrete]
-// CHECK:STDOUT:   %Destroy.type: type = facet_type <@Destroy> [concrete]
-// CHECK:STDOUT:   %pattern_type.f6d: type = pattern_type auto [concrete]
-// CHECK:STDOUT:   %Destroy.impl_witness.807: <witness> = impl_witness @X.%Destroy.impl_witness_table [concrete]
-// CHECK:STDOUT:   %ptr.d17: type = ptr_type %X [concrete]
-// CHECK:STDOUT:   %pattern_type.1c6: type = pattern_type %ptr.d17 [concrete]
-// CHECK:STDOUT:   %X.as.Destroy.impl.Op.type: type = fn_type @X.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %X.as.Destroy.impl.Op: %X.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
-// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
-// CHECK:STDOUT:   %pattern_type.019: type = pattern_type %X [concrete]
-// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
-// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.a45: type = ptr_type String [concrete]
-// CHECK:STDOUT:   %pattern_type.b05: type = pattern_type String [concrete]
-// CHECK:STDOUT:   %str: String = string_literal "hello" [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.type.051: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(String) [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.5ef: %T.as.Destroy.impl.Op.type.051 = struct_value () [concrete]
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function %T.as.Destroy.impl.Op.5ef, @T.as.Destroy.impl.Op(String) [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
-// CHECK:STDOUT:     .Destroy = %Core.Destroy
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .X = %X.decl
-// CHECK:STDOUT:     .F = %F.decl
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %X.decl: type = class_decl @X [concrete = constants.%X] {} {}
-// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
-// CHECK:STDOUT:     %x.patt: %pattern_type.019 = binding_pattern x [concrete]
-// CHECK:STDOUT:     %x.param_patt: %pattern_type.019 = value_param_pattern %x.patt, call_param0 [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %x.param: %X = value_param call_param0
-// CHECK:STDOUT:     %X.ref.loc18: type = name_ref X, file.%X.decl [concrete = constants.%X]
-// CHECK:STDOUT:     %x: %X = bind_name x, %x.param
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: impl @X.as.Destroy.impl: @X.%Self.ref as constants.%Destroy.type {
-// CHECK:STDOUT:   %X.as.Destroy.impl.Op.decl: %X.as.Destroy.impl.Op.type = fn_decl @X.as.Destroy.impl.Op [concrete = constants.%X.as.Destroy.impl.Op] {
-// CHECK:STDOUT:     %self.patt: %pattern_type.1c6 = binding_pattern self [concrete]
-// CHECK:STDOUT:     %self.param_patt: %pattern_type.1c6 = value_param_pattern %self.patt, call_param0 [concrete]
-// CHECK:STDOUT:     %.loc15: %pattern_type.f6d = addr_pattern %self.param_patt [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %self.param: %ptr.d17 = value_param call_param0
-// CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X]
-// CHECK:STDOUT:     %self: %ptr.d17 = bind_name self, %self.param
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Op = %X.as.Destroy.impl.Op.decl
-// CHECK:STDOUT:   witness = @X.%Destroy.impl_witness
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: class @X {
-// CHECK:STDOUT:   %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X]
-// CHECK:STDOUT:   impl_decl @X.as.Destroy.impl [concrete] {} {}
-// CHECK:STDOUT:   %Destroy.impl_witness_table = impl_witness_table (@X.as.Destroy.impl.%X.as.Destroy.impl.Op.decl), @X.as.Destroy.impl [concrete]
-// CHECK:STDOUT:   %Destroy.impl_witness: <witness> = impl_witness %Destroy.impl_witness_table [concrete = constants.%Destroy.impl_witness.807]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   complete_type_witness = %complete_type
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%X
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @X.as.Destroy.impl.Op(%self.param: %ptr.d17) = "no_op";
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @F(%x.param: %X) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %s.patt: %pattern_type.b05 = binding_pattern s [concrete]
-// CHECK:STDOUT:     %s.var_patt: %pattern_type.b05 = var_pattern %s.patt [concrete]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %s.var: ref String = var %s.var_patt
-// CHECK:STDOUT:   %str: String = string_literal "hello" [concrete = constants.%str]
-// CHECK:STDOUT:   assign %s.var, <error>
-// CHECK:STDOUT:   %s: ref String = bind_name s, %s.var
-// CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %y.patt: %pattern_type.019 = binding_pattern y [concrete]
-// CHECK:STDOUT:     %y.var_patt: %pattern_type.019 = var_pattern %y.patt [concrete]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %y.var: ref %X = var %y.var_patt
-// CHECK:STDOUT:   %x.ref: %X = name_ref x, %x
-// CHECK:STDOUT:   assign %y.var, <error>
-// CHECK:STDOUT:   %X.ref.loc32: type = name_ref X, file.%X.decl [concrete = constants.%X]
-// CHECK:STDOUT:   %y: ref %X = bind_name y, %y.var
-// CHECK:STDOUT:   %X.as.Destroy.impl.Op.bound: <bound method> = bound_method %y.var, constants.%X.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc32: %ptr.d17 = addr_of %y.var
-// CHECK:STDOUT:   %X.as.Destroy.impl.Op.call: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound(%addr.loc32)
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.bound: <bound method> = bound_method %s.var, constants.%T.as.Destroy.impl.Op.5ef
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function constants.%T.as.Destroy.impl.Op.5ef, @T.as.Destroy.impl.Op(String) [concrete = constants.%T.as.Destroy.impl.Op.specific_fn]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %s.var, %T.as.Destroy.impl.Op.specific_fn
-// CHECK:STDOUT:   %addr.loc25: %ptr.a45 = addr_of %s.var
-// CHECK:STDOUT:   %T.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc25)
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 7 - 7
toolchain/check/testdata/while/while.carbon

@@ -80,14 +80,14 @@ fn While() {
 library "[[@TEST_NAME]]";
 
 fn While() {
-  // CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+7]]:9: error: cannot implicitly convert expression of type `String` to `bool` [ConversionFailure]
-  // CHECK:STDERR:   while ("Hello") {}
-  // CHECK:STDERR:         ^~~~~~~~~
-  // CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+4]]:9: note: type `String` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
-  // CHECK:STDERR:   while ("Hello") {}
-  // CHECK:STDERR:         ^~~~~~~~~
+  // CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+7]]:9: error: cannot implicitly convert expression of type `{}` to `bool` [ConversionFailure]
+  // CHECK:STDERR:   while ({}) {}
+  // CHECK:STDERR:         ^~~~
+  // CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+4]]:9: note: type `{}` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   while ({}) {}
+  // CHECK:STDERR:         ^~~~
   // CHECK:STDERR:
-  while ("Hello") {}
+  while ({}) {}
 }
 
 // --- fail_bad_break_continue.carbon

+ 3 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -436,6 +436,9 @@ CARBON_DIAGNOSTIC_KIND(NonConstantCallToCompTimeOnlyFunction)
 CARBON_DIAGNOSTIC_KIND(CompTimeOnlyFunctionHere)
 CARBON_DIAGNOSTIC_KIND(TypeNotIndexable)
 CARBON_DIAGNOSTIC_KIND(SelfOutsideImplicitParamList)
+CARBON_DIAGNOSTIC_KIND(StringLiteralTooLong)
+CARBON_DIAGNOSTIC_KIND(StringLiteralTypeIncomplete)
+CARBON_DIAGNOSTIC_KIND(StringLiteralTypeUnexpected)
 CARBON_DIAGNOSTIC_KIND(StructInitElementCountMismatch)
 CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInLiteral)
 CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInConversion)

+ 2 - 2
toolchain/driver/testdata/dump_shared_values.carbon

@@ -16,8 +16,8 @@ var int2: i32 = 8;
 var real1: f64 = 1.0;
 var real2: f64 = 0.8e8;
 var real3: f64 = 0.8e9;
-var str1: String = "abc";
-var str2: String = "ab'\"c";
+var str1: str = "abc";
+var str2: str = "ab'\"c";
 
 // CHECK:STDOUT: ---
 // CHECK:STDOUT: filename:        dump_shared_values.carbon

+ 4 - 0
toolchain/driver/testdata/fail_flush_errors.carbon

@@ -25,6 +25,10 @@ fn F() {
   // Add the name into the string table from the tokenized buffer's string
   // literal storage. Use a hex escape to ensure that the tokenized buffer
   // allocates separate storage for the result.
+  // CHECK:STDERR: fail_flush_errors.carbon:[[@LINE+4]]:3: error: `Core.String` implicitly referenced here, but package `Core` not found [CoreNotFound]
+  // CHECK:STDERR:   "undec\x6Cared2";
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
   "undec\x6Cared2";
   // CHECK:STDERR: fail_flush_errors.carbon:[[@LINE+4]]:3: error: name `undeclared2` not found [NameNotFound]
   // CHECK:STDERR:   undeclared2;

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

@@ -14,14 +14,14 @@
 // CHECK:STDOUT:   tokens:
 // CHECK:STDOUT:   - { index:  0, kind:         "FileStart", line: {{ *\d+}}, column:   1, indent: 1, spelling: "" }
 
-fn run(String program) {
+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: "String" }
-// CHECK:STDOUT:   - { index:  5, kind:        "Identifier", line: {{ *}}[[@LINE-5]], column:  15, indent: 1, spelling: "program", identifier: 1, has_leading_space: true }
-// CHECK:STDOUT:   - { index:  6, kind:        "CloseParen", line: {{ *}}[[@LINE-6]], column:  22, indent: 1, spelling: ")", opening_token: 3 }
-// CHECK:STDOUT:   - { index:  7, kind:    "OpenCurlyBrace", line: {{ *}}[[@LINE-7]], column:  24, indent: 1, spelling: "{", closing_token: 11, has_leading_space: true }
+// 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 }
   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 }

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

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-var s: String = '''
+var s: str = '''
   error here: '''
 
 // Put all autoupdates below because otherwise is puts a CHECK in the middle of
@@ -22,6 +22,6 @@ var s: String = '''
 // 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: "String", has_leading_space: true }
-// CHECK:STDOUT:   - { index: 5, kind:             "Equal", line: {{ *}}[[@LINE-21]], column:  15, indent: 1, spelling: "=", has_leading_space: true }
-// CHECK:STDOUT:   - { index: 6, kind:     "StringLiteral", line: {{ *}}[[@LINE-22]], column:  17, indent: 1, spelling: "'''\n  error here: '''", value: "error here: ", has_leading_space: true }
+// 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 }

+ 2 - 2
toolchain/lex/token_kind.def

@@ -207,8 +207,8 @@ CARBON_KEYWORD_TOKEN(Return,              "return")
 CARBON_KEYWORD_TOKEN(Returned,            "returned")
 CARBON_KEYWORD_TOKEN(SelfTypeIdentifier,  "Self")
 CARBON_KEYWORD_TOKEN(SelfValueIdentifier, "self")
-// TODO: Although we provide a String type literal, it's not standardized.
-CARBON_KEYWORD_TOKEN(StringTypeLiteral,   "String")
+// TODO: Although we provide a `str` type literal, it's not standardized.
+CARBON_KEYWORD_TOKEN(StringTypeLiteral,   "str")
 CARBON_KEYWORD_TOKEN(Template,            "template")
 CARBON_KEYWORD_TOKEN(Then,                "then")
 CARBON_KEYWORD_TOKEN(True,                "true")

+ 7 - 3
toolchain/lower/constant.cpp

@@ -6,6 +6,7 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Value.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/lower/file_context.h"
@@ -268,9 +269,12 @@ static auto EmitAsConstant(ConstantContext& context,
   return context.GetUnusedConstant(inst.type_id);
 }
 
-static auto EmitAsConstant(ConstantContext& /*context*/,
-                           SemIR::StringLiteral inst) -> llvm::Constant* {
-  CARBON_FATAL("TODO: Add support: {0}", inst);
+static auto EmitAsConstant(ConstantContext& context, SemIR::StringLiteral inst)
+    -> llvm::Constant* {
+  return llvm::IRBuilder<>(context.llvm_context())
+      .CreateGlobalString(
+          context.sem_ir().string_literal_values().Get(inst.string_literal_id),
+          /*name=*/"", /*address_space=*/0, &context.llvm_module());
 }
 
 static auto EmitAsConstant(ConstantContext& context, SemIR::VarStorage inst)

+ 2 - 2
toolchain/lower/testdata/basics/false_true.carbon → toolchain/lower/testdata/primitives/false_true.carbon

@@ -6,9 +6,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/basics/false_true.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/false_true.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/basics/false_true.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/false_true.carbon
 
 fn F() -> bool {
   return false;

+ 2 - 2
toolchain/lower/testdata/basics/int_types.carbon → toolchain/lower/testdata/primitives/int_types.carbon

@@ -6,9 +6,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/basics/int_types.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/int_types.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/basics/int_types.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/int_types.carbon
 
 fn F_i8(a: i8) -> i8 { return a; }
 fn F_u16(a: u16) -> u16 { return a; }

+ 2 - 2
toolchain/lower/testdata/basics/numeric_literals.carbon → toolchain/lower/testdata/primitives/numeric_literals.carbon

@@ -6,9 +6,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/basics/numeric_literals.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/numeric_literals.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/basics/numeric_literals.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/numeric_literals.carbon
 
 fn F() {
   // 8 and 9 trigger special behavior in APInt when mishandling signed versus

+ 79 - 0
toolchain/lower/testdata/primitives/string.carbon

@@ -0,0 +1,79 @@
+// 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
+//
+// This is an integration test for the Core.String in the prelude, so use the
+// real prelude.
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon
+// EXTRA-ARGS: --target=x86_64-linux-gnu
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/string.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/string.carbon
+
+fn F(s: str);
+
+fn G() {
+  F("Hello");
+}
+
+fn H() {
+  // This can reuse the same string literal object.
+  let v: str = "Hello";
+  F(v);
+}
+
+fn I() {
+  // This can't.
+  let v: str = "World";
+  F(v);
+}
+
+// CHECK:STDOUT: ; ModuleID = 'string.carbon'
+// CHECK:STDOUT: source_filename = "string.carbon"
+// CHECK:STDOUT:
+// CHECK:STDOUT: @0 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
+// CHECK:STDOUT: @1 = private unnamed_addr constant [6 x i8] c"World\00", align 1
+// CHECK:STDOUT: @String.val.13d.String.val = internal constant { ptr, i64 } { ptr @0, i64 5 }
+// CHECK:STDOUT: @String.val.afc.String.val = internal constant { ptr, i64 } { ptr @1, i64 5 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_CF.Main(ptr)
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CG.Main() !dbg !4 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_CF.Main(ptr @String.val.13d.String.val), !dbg !7
+// CHECK:STDOUT:   ret void, !dbg !8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CH.Main() !dbg !9 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_CF.Main(ptr @String.val.13d.String.val), !dbg !10
+// CHECK:STDOUT:   ret void, !dbg !11
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CI.Main() !dbg !12 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_CF.Main(ptr @String.val.afc.String.val), !dbg !13
+// CHECK:STDOUT:   ret void, !dbg !14
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !3 = !DIFile(filename: "string.carbon", directory: "")
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
+// CHECK:STDOUT: !6 = !{}
+// CHECK:STDOUT: !7 = !DILocation(line: 19, column: 3, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 18, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "H", linkageName: "_CH.Main", scope: null, file: !3, line: 22, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !10 = !DILocation(line: 25, column: 3, scope: !9)
+// CHECK:STDOUT: !11 = !DILocation(line: 22, column: 1, scope: !9)
+// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "I", linkageName: "_CI.Main", scope: null, file: !3, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !13 = !DILocation(line: 31, column: 3, scope: !12)
+// CHECK:STDOUT: !14 = !DILocation(line: 28, column: 1, scope: !12)

+ 2 - 2
toolchain/lower/testdata/basics/type_values.carbon → toolchain/lower/testdata/primitives/type_values.carbon

@@ -6,9 +6,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/basics/type_values.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/type_values.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/basics/type_values.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/type_values.carbon
 
 // TODO: It would be nice to treat these functions as returning `void` instead
 // of `{}`.

+ 2 - 2
toolchain/lower/testdata/basics/zero.carbon → toolchain/lower/testdata/primitives/zero.carbon

@@ -6,9 +6,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/basics/zero.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/primitives/zero.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/basics/zero.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/primitives/zero.carbon
 
 fn Main() -> i32 {
   return 0;

+ 2 - 2
toolchain/parse/testdata/basics/type_literals.carbon

@@ -10,7 +10,7 @@
 
 var test_i32: i32 = 0;
 var test_f64: f64 = 0.1;
-var test_str: String = "Test";
+var test_str: str = "Test";
 
 // CHECK:STDOUT: - filename: type_literals.carbon
 // CHECK:STDOUT:   parse_tree: [
@@ -33,7 +33,7 @@ var test_str: String = "Test";
 // 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: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariablePattern', text: 'var', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/basics/value_literals.carbon

@@ -43,7 +43,7 @@ var c: Core.Char = 'c';
 
 // --- string_literal.carbon
 
-var test_str: String = "Test";
+var test_str: str = "Test";
 
 // CHECK:STDOUT: - filename: int_literals.carbon
 // CHECK:STDOUT:   parse_tree: [
@@ -148,7 +148,7 @@ var test_str: String = "Test";
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'test_str'},
-// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariablePattern', text: 'var', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/generics/impl/empty_body.carbon

@@ -8,14 +8,14 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/impl/empty_body.carbon
 
-impl String as Interface {
+impl str as Interface {
 }
 
 // CHECK:STDOUT: - filename: empty_body.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
-// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},

+ 5 - 5
toolchain/parse/testdata/generics/impl/fail_impl.carbon

@@ -70,11 +70,11 @@ impl forall [invalid] i8;
 // CHECK:STDERR:
 impl forall f16 as Quux;
 
-// CHECK:STDERR: fail_impl.carbon:[[@LINE+4]]:30: error: expected `as` in `impl` declaration [ImplExpectedAs]
-// CHECK:STDERR: impl forall [T:! type] String;
-// CHECK:STDERR:                              ^
+// CHECK:STDERR: fail_impl.carbon:[[@LINE+4]]:27: error: expected `as` in `impl` declaration [ImplExpectedAs]
+// CHECK:STDERR: impl forall [T:! type] str;
+// CHECK:STDERR:                           ^
 // CHECK:STDERR:
-impl forall [T:! type] String;
+impl forall [T:! type] str;
 
 // CHECK:STDERR: fail_impl.carbon:[[@LINE+4]]:26: error: expected `as` in `impl` declaration [ImplExpectedAs]
 // CHECK:STDERR: impl forall [T:! type] T missing_as;
@@ -157,7 +157,7 @@ impl
 // CHECK:STDOUT:           {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
-// CHECK:STDOUT:       {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:       {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 10},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:       {kind: 'Forall', text: 'forall'},

+ 4 - 4
toolchain/parse/testdata/let/let.carbon

@@ -11,8 +11,8 @@
 let v: i32 = 0;
 let _: i32 = 1;
 fn F() {
-  let s: String = "hello";
-  let _: i32 = "goodbye";
+  let s: str = "hello";
+  let _: str = "goodbye";
 }
 
 // CHECK:STDOUT: - filename: let.carbon
@@ -39,14 +39,14 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'LetIntroducer', text: 'let'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 's'},
-// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'LetInitializer', text: '='},
 // CHECK:STDOUT:         {kind: 'StringLiteral', text: '"hello"'},
 // CHECK:STDOUT:       {kind: 'LetDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'LetIntroducer', text: 'let'},
 // CHECK:STDOUT:           {kind: 'UnderscoreName', text: '_'},
-// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'LetInitializer', text: '='},
 // CHECK:STDOUT:         {kind: 'StringLiteral', text: '"goodbye"'},

+ 2 - 2
toolchain/parse/testdata/let/let_tuple.carbon

@@ -9,7 +9,7 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/let/let_tuple.carbon
 
 fn F() {
-  let (s: String, i: i32) = ("hello", 0);
+  let (s: str, i: i32) = ("hello", 0);
 }
 
 // CHECK:STDOUT: - filename: let_tuple.carbon
@@ -23,7 +23,7 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'LetIntroducer', text: 'let'},
 // CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 's'},
-// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'i'},

+ 2 - 2
toolchain/parse/testdata/return/fail_returned_no_var.carbon

@@ -8,7 +8,7 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/return/fail_returned_no_var.carbon
 
-fn F() -> String {
+fn F() -> str {
   // CHECK:STDERR: fail_returned_no_var.carbon:[[@LINE+4]]:12: error: expected `var` after `returned` [ExpectedVarAfterReturned]
   // CHECK:STDERR:   returned fn G() -> i32;
   // CHECK:STDERR:            ^~
@@ -23,7 +23,7 @@ fn F() -> String {
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
 // CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'EmptyDecl', text: ';', has_error: yes},

+ 4 - 4
toolchain/parse/testdata/return/returned_var.carbon

@@ -8,8 +8,8 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/return/returned_var.carbon
 
-fn F() -> String {
-  returned var s: String = "hello";
+fn F() -> str {
+  returned var s: str = "hello";
   return var;
 }
 
@@ -20,13 +20,13 @@ fn F() -> String {
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
 // CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:         {kind: 'ReturnedModifier', text: 'returned'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 's'},
-// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:           {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'VariablePattern', text: 'var', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/var/var.carbon

@@ -11,7 +11,7 @@
 var v: i32 = 0;
 var w: i32;
 fn F() {
-  var s: String = "hello";
+  var s: str = "hello";
 }
 
 // CHECK:STDOUT: - filename: var.carbon
@@ -38,7 +38,7 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 's'},
-// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:             {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:           {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'VariablePattern', text: 'var', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/var/var_tuple.carbon

@@ -9,7 +9,7 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/var/var_tuple.carbon
 
 fn F() {
-  var (s: String, i: 32) = ("hello", 0);
+  var (s: str, i: 32) = ("hello", 0);
 }
 
 // CHECK:STDOUT: - filename: var_tuple.carbon
@@ -23,7 +23,7 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 's'},
-// CHECK:STDOUT:               {kind: 'StringTypeLiteral', text: 'String'},
+// CHECK:STDOUT:               {kind: 'StringTypeLiteral', text: 'str'},
 // CHECK:STDOUT:             {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'i'},

+ 17 - 0
toolchain/testing/testdata/min_prelude/parts/string.carbon

@@ -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/parts/uint.carbon
+
+// --- min_prelude/parts/string.carbon
+
+// A minimal prelude for testing using `Int` or `i32`; required for arrays.
+package Core library "prelude/parts/string";
+
+import library "prelude/parts/uint";
+
+class String {
+  private var ptr: u8*;
+  private var size: u64;
+}

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

@@ -5,6 +5,7 @@
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/bool.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/float.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/int.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/string.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/uint.carbon
 // EXTRA-ARGS: --custom-core --exclude-dump-file-prefix=min_prelude/
 
@@ -16,4 +17,5 @@ package Core library "prelude";
 export import library "prelude/parts/bool";
 export import library "prelude/parts/float";
 export import library "prelude/parts/int";
+export import library "prelude/parts/string";
 export import library "prelude/parts/uint";