Przeglądaj źródła

Remove explorer/ and installers/ directories (#5290)

References were removed by #5287, #5291, and #5292; this is just
deleting the directories themselves.

Note this does not change anything else (e.g. removing things in
MODULE.bazel that were only used by explorer). I'm trying to make a pure
delete that's easy to review.
Jon Ross-Perkins 1 rok temu
rodzic
commit
ea130f1e73
100 zmienionych plików z 0 dodań i 30513 usunięć
  1. 0 109
      explorer/BUILD
  2. 0 340
      explorer/README.md
  3. 0 0
      explorer/__init__.py
  4. 0 202
      explorer/ast/BUILD
  5. 0 46
      explorer/ast/README.md
  6. 0 110
      explorer/ast/address.h
  7. 0 36
      explorer/ast/ast.h
  8. 0 161
      explorer/ast/ast_kinds.h
  9. 0 11
      explorer/ast/ast_node.cpp
  10. 0 91
      explorer/ast/ast_node.h
  11. 0 19
      explorer/ast/ast_rtti.cpp
  12. 0 100
      explorer/ast/ast_rtti.h
  13. 0 132
      explorer/ast/ast_test_matchers.h
  14. 0 214
      explorer/ast/ast_test_matchers_internal.cpp
  15. 0 237
      explorer/ast/ast_test_matchers_internal.h
  16. 0 162
      explorer/ast/ast_test_matchers_test.cpp
  17. 0 88
      explorer/ast/bindings.cpp
  18. 0 92
      explorer/ast/bindings.h
  19. 0 104
      explorer/ast/clone_context.cpp
  20. 0 167
      explorer/ast/clone_context.h
  21. 0 529
      explorer/ast/declaration.cpp
  22. 0 1058
      explorer/ast/declaration.h
  23. 0 74
      explorer/ast/element.cpp
  24. 0 192
      explorer/ast/element.h
  25. 0 142
      explorer/ast/element_path.h
  26. 0 96
      explorer/ast/element_test.cpp
  27. 0 429
      explorer/ast/expression.cpp
  28. 0 1260
      explorer/ast/expression.h
  29. 0 27
      explorer/ast/expression_category.h
  30. 0 138
      explorer/ast/expression_test.cpp
  31. 0 18
      explorer/ast/impl_binding.cpp
  32. 0 105
      explorer/ast/impl_binding.h
  33. 0 25
      explorer/ast/library_name.h
  34. 0 51
      explorer/ast/paren_contents.h
  35. 0 197
      explorer/ast/pattern.cpp
  36. 0 484
      explorer/ast/pattern.h
  37. 0 132
      explorer/ast/pattern_test.cpp
  38. 0 106
      explorer/ast/return_term.h
  39. 0 217
      explorer/ast/statement.cpp
  40. 0 587
      explorer/ast/statement.h
  41. 0 173
      explorer/ast/static_scope.cpp
  42. 0 127
      explorer/ast/static_scope.h
  43. 0 1370
      explorer/ast/value.cpp
  44. 0 1744
      explorer/ast/value.h
  45. 0 59
      explorer/ast/value_kinds.def
  46. 0 158
      explorer/ast/value_node.h
  47. 0 312
      explorer/ast/value_transform.h
  48. 0 164
      explorer/base/BUILD
  49. 0 280
      explorer/base/arena.h
  50. 0 96
      explorer/base/arena_test.cpp
  51. 0 54
      explorer/base/decompose.h
  52. 0 39
      explorer/base/decompose_test.cpp
  53. 0 26
      explorer/base/error_builders.h
  54. 0 26
      explorer/base/error_builders_test.cpp
  55. 0 22
      explorer/base/nonnull.h
  56. 0 36
      explorer/base/print_as_id.h
  57. 0 37
      explorer/base/set_file_context_raii_test.cpp
  58. 0 36
      explorer/base/set_program_phase_raii_test.cpp
  59. 0 73
      explorer/base/source_location.h
  60. 0 226
      explorer/base/trace_stream.h
  61. 0 715
      explorer/data/prelude.carbon
  62. 0 133
      explorer/file_test.cpp
  63. 0 368
      explorer/interpreter/BUILD
  64. 0 114
      explorer/interpreter/README.md
  65. 0 212
      explorer/interpreter/action.cpp
  66. 0 465
      explorer/interpreter/action.h
  67. 0 299
      explorer/interpreter/action_stack.cpp
  68. 0 163
      explorer/interpreter/action_stack.h
  69. 0 49
      explorer/interpreter/builtins.cpp
  70. 0 68
      explorer/interpreter/builtins.def
  71. 0 62
      explorer/interpreter/builtins.h
  72. 0 104
      explorer/interpreter/dictionary.h
  73. 0 109
      explorer/interpreter/exec_program.cpp
  74. 0 30
      explorer/interpreter/exec_program.h
  75. 0 209
      explorer/interpreter/heap.cpp
  76. 0 104
      explorer/interpreter/heap.h
  77. 0 61
      explorer/interpreter/heap_allocation_interface.h
  78. 0 445
      explorer/interpreter/impl_scope.cpp
  79. 0 206
      explorer/interpreter/impl_scope.h
  80. 0 2637
      explorer/interpreter/interpreter.cpp
  81. 0 31
      explorer/interpreter/interpreter.h
  82. 0 176
      explorer/interpreter/matching_impl_set.cpp
  83. 0 136
      explorer/interpreter/matching_impl_set.h
  84. 0 313
      explorer/interpreter/pattern_analysis.cpp
  85. 0 162
      explorer/interpreter/pattern_analysis.h
  86. 0 251
      explorer/interpreter/pattern_match.cpp
  87. 0 37
      explorer/interpreter/pattern_match.h
  88. 0 247
      explorer/interpreter/resolve_control_flow.cpp
  89. 0 23
      explorer/interpreter/resolve_control_flow.h
  90. 0 984
      explorer/interpreter/resolve_names.cpp
  91. 0 22
      explorer/interpreter/resolve_names.h
  92. 0 471
      explorer/interpreter/resolve_unformed.cpp
  93. 0 75
      explorer/interpreter/resolve_unformed.h
  94. 0 75
      explorer/interpreter/stack.h
  95. 0 49
      explorer/interpreter/stack_space.cpp
  96. 0 48
      explorer/interpreter/stack_space.h
  97. 0 6662
      explorer/interpreter/type_checker.cpp
  98. 0 624
      explorer/interpreter/type_checker.h
  99. 0 143
      explorer/interpreter/type_structure.cpp
  100. 0 85
      explorer/interpreter/type_structure.h

+ 0 - 109
explorer/BUILD

@@ -1,109 +0,0 @@
-# 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
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
-load("//bazel/cc_toolchains:defs.bzl", "cc_env")
-load("//testing/file_test:rules.bzl", "file_test")
-
-package(default_visibility = [
-    "//bazel/check_deps:__pkg__",
-    "//explorer:__subpackages__",
-    "//installers:__subpackages__",
-])
-
-filegroup(
-    name = "standard_libraries",
-    srcs = ["data/prelude.carbon"],
-)
-
-cc_library(
-    name = "main",
-    srcs = ["main.cpp"],
-    hdrs = ["main.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:error",
-        "//common:ostream",
-        "//explorer/base:trace_stream",
-        "//explorer/parse_and_execute",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_binary(
-    name = "explorer",
-    srcs = ["main_bin.cpp"],
-    env = cc_env(),
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":main",
-        "//common:bazel_working_dir",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_binary(
-    name = "file_test",
-    testonly = 1,
-    srcs = ["file_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":main",
-        "//common:check",
-        "//common:raw_string_ostream",
-        "//testing/base:file_helpers",
-        "//testing/file_test:file_test_base",
-        "//testing/file_test:manifest",
-        "@abseil-cpp//absl/flags:flag",
-        "@abseil-cpp//absl/strings",
-        "@re2",
-    ],
-)
-
-file_test(
-    name = "file_test.notrace",
-    size = "small",
-    prebuilt_binary = ":file_test",
-    shard_count = 20,
-    tests = glob(["testdata/**/*.carbon"]),
-)
-
-file_test(
-    name = "file_test.trace",
-    size = "small",
-    args = ["--trace"],
-    prebuilt_binary = ":file_test",
-    shard_count = 30,
-    tests = glob(
-        ["testdata/**/*.carbon"],
-        exclude = [
-            # `limits` tests check for various limit conditions (such as an
-            # infinite loop). The tests collectively don't test tracing
-            # because it creates substantial additional overhead.
-            "testdata/limits/**",
-            # `trace` tests do tracing by default.
-            "testdata/trace/**",
-            # Expensive tests to trace.
-            "testdata/assoc_const/rewrite_large_type.carbon",
-            "testdata/linked_list/typed_linked_list.carbon",
-        ],
-    ),
-)
-
-filegroup(
-    name = "tree_sitter_testdata",
-    srcs = glob(
-        ["testdata/**/*.carbon"],
-        exclude = [
-            "testdata/**/fail_*",
-        ],
-    ),
-    visibility = ["//utils/tree_sitter:__pkg__"],
-)

+ 0 - 340
explorer/README.md

@@ -1,340 +0,0 @@
-# Explorer
-
-<!--
-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
--->
-
-<!--
-{% raw %}
-Hides `{{` from jekyll's liquid parsing. Note endraw at the bottom.
--->
-
-`explorer` is an implementation of Carbon whose primary purpose is to act as a
-clear specification of the language. As an extension of that goal, it can also
-be used as a platform for prototyping and validating changes to the language.
-Consequently, it prioritizes straightforward, readable code over performance,
-diagnostic quality, and other conventional implementation priorities. In other
-words, its intended audience is people working on the design of Carbon, and it
-is not intended for real-world Carbon programming on any scale. See the
-[`toolchain`](/toolchain/) directory for a separate implementation that's
-focused on the needs of Carbon users.
-
-## Overview
-
-`explorer` represents Carbon code using an abstract syntax tree (AST), which is
-defined in the [`ast`](ast/) directory. The [`syntax`](syntax/) directory
-contains lexer and parser, which define how the AST is generated from Carbon
-code. The [`interpreter`](interpreter/) directory contains the remainder of the
-implementation.
-
-`explorer` is an interpreter rather than a compiler, although it attempts to
-separate compile time from run time, since that separation is an important
-constraint on Carbon's design.
-
-## Programming conventions
-
-The class hierarchies in `explorer` are built to support
-[LLVM-style RTTI](https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html), and
-define a `kind` accessor that returns an enum identifying the concrete type.
-`explorer` typically relies less on virtual dispatch, and more on using `kind`
-as the key of a `switch` and then down-casting in the individual cases. As a
-result, adding a new derived class to a hierarchy requires updating existing
-code to handle it. It is generally better to avoid defining `default` cases for
-RTTI switches, so that the compiler can help ensure the code is updated when a
-new type is added.
-
-`explorer` never uses plain pointer types directly. Instead, we use the
-[`Nonnull<T*>`](base/nonnull.h) alias for pointers that are not nullable, or
-`std::optional<Nonnull<T*>>` for pointers that are nullable.
-
-Many of the most commonly-used objects in `explorer` have lifetimes that are
-tied to the lifespan of the entire Carbon program. We manage the lifetimes of
-those objects by allocating them through an [`Arena`](base/arena.h) object,
-which can allocate objects of arbitrary types, and retains ownership of them. As
-of this writing, all of `explorer` uses a single `Arena` object, we may
-introduce multiple `Arena`s for different lifetime groups in the future.
-
-For simplicity, `explorer` generally treats all errors as fatal. Errors caused
-by bugs in the user-provided Carbon code should be reported with the error
-builders in [`error_builders.h`](base/error_builders.h). Errors caused by bugs
-in `explorer` itself should be reported with
-[`CHECK` or `FATAL`](../common/check.h).
-
-### `Decompose` functions
-
-Many of explorer's data structures provide a `Decompose` method, which allows
-simple data types to be generically decomposed into their fields. The
-`Decompose` function for a type takes a function and calls it with the fields of
-that type. For example:
-
-```
-class MyType {
- public:
-  MyType(Type1 arg1, Type2 arg2) : arg1_(arg1), arg2_(arg2) {}
-
-  template <typename F>
-  auto Decompose(F f) const { return f(arg1_, arg2_); }
-
- private:
-  Type1 arg1_;
-  Type2 arg2_;
-};
-```
-
-Where possible, a value equivalent to the original value should be created by
-passing the given arguments to the constructor of the type. For example,
-`my_value.Decompose([](auto ...args) { return MyType(args...); })` should
-recreate the original value.
-
-## Example Programs (Regression Tests)
-
-The [`testdata/`](testdata/) subdirectory includes some example programs with
-expected output.
-
-These tests make use of [GoogleTest](https://github.com/google/googletest) with
-Bazel's `cc_test` rules. Tests have boilerplate at the top:
-
-```carbon
-// 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
-//
-// AUTOUPDATE
-// CHECK:STDOUT: result: 7
-
-package ExplorerTest api;
-
-fn Main() -> i32 {
-  return (1 + 2) + 4;
-}
-```
-
-To explain this boilerplate:
-
--   The standard copyright is expected.
--   The `AUTOUPDATE` line indicates that `CHECK` lines matching the output will
-    be automatically inserted immediately below by the
-    `./autoupdate_testdata.sh` script.
--   The `CHECK` lines indicate expected output.
-    -   Where a `CHECK` line contains text like `{{.*}}`, the double curly
-        braces indicate a contained regular expression.
--   The `package` is required in all test files, per normal Carbon syntax rules.
-
-### Useful commands
-
--   `./autoupdate_testdata.sh` -- Updates expected output.
-    -   This can be combined with `git diff` to see changes in output.
--   `bazel test ... --test_output=errors` -- Runs tests and prints any errors.
--   `bazel test //explorer:file_test.subset --test_arg=explorer/testdata/DIR/FILE.carbon`
-    -- Runs a specific test.
--   `bazel run testdata/DIR/FILE.carbon.run` -- Runs explorer on the file.
--   `bazel run testdata/DIR/FILE.carbon.verbose` -- Runs explorer on the file
-    with tracing enabled.
-
-## Explorer's Trace Output
-
-Explorer's Trace Output refers to a detailed record of program phases and their
-internal processes a program goes through when executed using the `explorer`. It
-also records things like changes in memory and action stack that describes the
-state of the program.
-
-Tracing can be turned on using the `--trace_file=...` option. Explorer tests can
-be run with tracing enabled by using the `<testname>.verbose` test target.
-
-By default, `explorer` prints the state of the program and each step that is
-performed during execution for the file containing the main function when
-tracing is enabled. Tracing for different phases and file contexts can be
-selected using filtering that is explained below.
-
-Printing directly to the standard output using the `--trace_file` option is
-supported by passing `-` in place of a filepath (`--trace_file=-`).
-
-### Filtering of the trace
-
-Trace output can be filtered based on either program phase or file context.
-
-Trace output can be filtered by selecting program phases and file contexts for
-which tracing should be enabled. The `-trace_phase=...` option is used to select
-program phases, while the `-trace_file_context=...` option is used to select
-file contexts.
-
-The following options can be passed as a comma-separated list to the
-`-trace_phase=...` option to select program phases:
-
--   `source_program`: Includes trace output for the source program phase.
--   `name_resolution`: Includes trace output for the name resolution phase.
--   `control_flow_resolution`: Includes trace output for the control flow
-    resolution phase.
--   `type_checking`: Includes trace output for the type checking phase.
--   `unformed_variables_resolution`: Includes trace output for the unformed
-    variables resolution phase.
--   `declarations`: Includes trace output for printing declarations.
--   `execution`: Includes trace output for program execution.
--   `timing`: Includes timing logs indicating the time taken by each phase.
--   `all`: Includes trace output for all phases.
--   By default, tracing is only enabled for the `execution` phase.
-
-The following options can be passed as a comma-separated list to the
-`-trace_file_context=...` option to select file contexts:
-
--   `main`: Includes trace output for the file containing the main function.
--   `prelude`: Includes trace output for the prelude.
--   `import`: Includes trace output for imports.
--   `include`: Includes trace output for all.
--   By default, tracing is only enabled for the `main` file context.
-
-**Note (for developers):** Two
-[RAII](https://en.cppreference.com/w/cpp/language/raii) classes
-`SetProgramPhase` and `SetFileContext` are provided for setting program phase
-and file context dynamically in the code.
-
-### State of the Program
-
-The state of the program is represented by the memory and the stack. The memory
-is a mapping of addresses to values, and the stack is a list of actions.
-
-The state of the program is constantly changing as the program executes. The
-memory is updated as objects are allocated and deallocated, and the stack is
-updated as actions are performed. The state of the program can be used to track
-the progress of the program and to debug the program.
-
-#### Memory
-
-The memory is a mapping of addresses to values. The memory is used to represent
-both heap-allocated objects and also mutable parts of the procedure call stack.
-
-1. **Memory Allocation** is printed as
-
-```
-++# memory-alloc: #<allocation_index> `value` uninitialized?
-```
-
-2. **Read Memory** is printed as
-
-```
-<-- memory-read: #<allocation_index> `value`
-```
-
-3. **Write Memory** is printed as
-
-```
---> memory-write: #<allocation_index> `value`
-```
-
-4. **Memory Deallocation** is printed as
-
-```
---# memory-dealloc: #<allocation_index> `value`
-```
-
-`allocation_index` is used for locating an object within the heap. `value`
-represents the object inside heap that is accessed using `allocation_index`
-
-#### Stack (Action Stack)
-
-The stack is list of actions, push and pop changes in the stack are printed in
-the following format
-
-```
->[] stack-push: <action> (<source location>)
-<[] stack-pop:  <action> (<source location>)
-```
-
-`action` is printed in the following format
-
-```
-ActionKind pos: <pos_count> `<syntax>` results: [<collected_results>]  scope: [<scope>]
-```
-
-1. `ActionKind`: The `kind` of an action. Examples: ExpressionAction,
-   DeclarationAction, etc.
-2. `pos_count`: The position of execution (an integer) for this action. Each
-   action can take multiple steps to complete.
-3. `syntax`: The syntax for the part of the program to be executed, such as an
-   expression or statement.
-4. `collected_results`: The results from subexpressions of this part.
-5. `scope`: The variables whose lifetimes are associated with this part of the
-   program.
-
-The stack always begins with a function call to `Main`.
-
-In the special case of a function call, when the function call finishes, the
-result value appears at the end of the `results`.
-
-### Step of Execution
-
-Each step of execution is printed in the following format:
-
-    ->> step ActionKind pos: position syntax (<file-location>) --->
-
--   The `syntax` is the part of the program being executed.
--   The `ActionKind` is the kind of action for which the step is executed.
--   The `position` says how far along `explorer` is in executing this action.
--   The `file-location` gives the filename and line number for the `syntax`.
-
-Each step of execution can push new actions on the stack, pop actions, increment
-the position number of an action, and add result values to an action.
-
-### Trace Conventions (For Developers)
-
-#### Syntax and Code Formatting
-
-When including syntax or code within trace messages, it should be wrapped
-appropriately to maintain clarity and differentiation between code elements and
-regular text in the trace output.
-
--   For single-line code or syntax, use single backticks.
--   For multiline code blocks, use triple backticks (\`\`\`) to enclose the
-    code.
-
-**Examples:**
-
-````
-For single line code:
-`let x: i32 = 0;`
-
-For multi line code:
-```
-fn Main() -> i32 {
-    return 0;
-}
-```
-````
-
-#### Line Prefixes
-
-Each line of trace output starts with a prefix that indicates the nature of the
-information being presented. These prefixes are added using specific formatting
-methods in the `TraceStream` class.
-
-**Example usage:**
-
-```
-trace_stream->PrefixMethod() << ... ;
-```
-
-#### Formatting Utility Methods
-
-The `TraceStream` class also have utility methods for adding formatted headings
-and subheadings to the trace output. These methods help structure the trace
-information and provide visual separation for different sections.
-
-`Heading(...)` method prints the heading in following format:
-
-```
-* * * * * * * * * *  Heading * * * * * * * * * *
-------------------------------------------------
-```
-
-`SubHeading(...)` method prints the heading in the following format:
-
-```
-- - - - -  Sub Heading - - - - -
---------------------------------
-```
-
-<!--
-{% endraw %}
--->

+ 0 - 0
explorer/__init__.py


+ 0 - 202
explorer/ast/BUILD

@@ -1,202 +0,0 @@
-# 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
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-
-package(default_visibility = ["//explorer:__subpackages__"])
-
-cc_library(
-    name = "ast",
-    srcs = [
-        "ast_node.cpp",
-        "ast_rtti.cpp",
-        "bindings.cpp",
-        "clone_context.cpp",
-        "declaration.cpp",
-        "element.cpp",
-        "expression.cpp",
-        "impl_binding.cpp",
-        "pattern.cpp",
-        "statement.cpp",
-        "value.cpp",
-    ],
-    hdrs = [
-        "address.h",
-        "ast.h",
-        "ast_kinds.h",
-        "ast_node.h",
-        "ast_rtti.h",
-        "bindings.h",
-        "clone_context.h",
-        "declaration.h",
-        "element.h",
-        "element_path.h",
-        "expression.h",
-        "impl_binding.h",
-        "pattern.h",
-        "return_term.h",
-        "statement.h",
-        "value.h",
-        "value_node.h",
-        "value_transform.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    textual_hdrs = [
-        "value_kinds.def",
-    ],
-    deps = [
-        ":expression_category",
-        ":library_name",
-        ":paren_contents",
-        "//common:check",
-        "//common:enum_base",
-        "//common:error",
-        "//common:indirect_value",
-        "//common:ostream",
-        "//explorer/base:arena",
-        "//explorer/base:decompose",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:source_location",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "ast_test_matchers",
-    testonly = 1,
-    srcs = [
-        "ast_test_matchers_internal.cpp",
-        "ast_test_matchers_internal.h",
-    ],
-    hdrs = ["ast_test_matchers.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        "@googletest//:gtest",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "ast_test_matchers_test",
-    size = "small",
-    srcs = ["ast_test_matchers_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        ":ast_test_matchers",
-        "//explorer/base:arena",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "expression_test",
-    size = "small",
-    srcs = ["expression_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        ":paren_contents",
-        "//explorer/base:arena",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "library_name",
-    hdrs = ["library_name.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-)
-
-cc_library(
-    name = "paren_contents",
-    hdrs = ["paren_contents.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//explorer/base:error_builders",
-        "//explorer/base:source_location",
-    ],
-)
-
-cc_test(
-    name = "element_test",
-    size = "small",
-    srcs = ["element_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        ":paren_contents",
-        "//explorer/base:arena",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "pattern_test",
-    size = "small",
-    srcs = ["pattern_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        ":paren_contents",
-        "//explorer/base:arena",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "static_scope",
-    srcs = ["static_scope.cpp"],
-    hdrs = ["static_scope.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":ast",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:source_location",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "expression_category",
-    hdrs = ["expression_category.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = ["@llvm-project//llvm:Support"],
-)

+ 0 - 46
explorer/ast/README.md

@@ -1,46 +0,0 @@
-# Explorer AST
-
-<!--
-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
--->
-
-The code in this directory defines the AST that represents Carbon code in the
-rest of `explorer`.
-
-The AST is not quite immutable, because some node properties are set during some
-phase of static analysis, rather than during parsing. However, AST mutations are
-_monotonic_: once set, a node property cannot be changed. Furthermore, if a
-property is set after parsing, its documentation specifies what phase is
-responsible for setting it. Certain properties have `has_foo()` members for
-querying whether they are set, but those are for internal use within the phase
-that sets them. As a result, you can think of the AST as if it were immutable,
-but with certain parts that you can't yet observe, depending on what phase of
-compilation you're in.
-
-All node types in the AST are derived from [`AstNode`](ast_node.h), and use
-[LLVM-style RTTI](https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html) to support
-safe down-casting and similar operations. Each abstract class `Foo` in the
-hierarchy has a `kind` method which returns a enum `FooKind` that identifies the
-concrete type of the object, and a `FooKind` value can be safely `static_cast`ed
-to `BarKind` if that value represents a type that's derived from both `Foo` and
-`Bar`.
-
-These `FooKind` enums are generated from a description of the class hierarchy
-that is provided by [X macros](https://en.wikipedia.org/wiki/X_Macro) defined in
-[`ast_kinds.h`](ast_kinds.h) that specify the classes derived from each AST base
-class. Those macros must be kept up to date as the class hierarchy changes.
-
-The AST class hierarchy is structured in a fairly unsurprising way, with
-abstract classes such as `Statement` and `Expression`, and concrete classes
-representing individual syntactic constructs, such as `If` for if-statements.
-
-Sometimes it is useful to work with a subset of node types that "cuts across"
-the primary class hierarchy. Rather than deal with the pitfalls of multiple
-inheritance, we handle these cases using a form of type erasure: we specify a
-notional interface that those types conform to, and then define a "view" class
-that behaves like a pointer to an instance of that interface. Types declare that
-they model an interface `Foo` by defining a public static member named
-`ImplementsCarbonFoo`. See [ValueNodeView](value_node.h) for an example of this
-pattern.

+ 0 - 110
explorer/ast/address.h

@@ -1,110 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_ADDRESS_H_
-#define CARBON_EXPLORER_AST_ADDRESS_H_
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/element_path.h"
-
-namespace Carbon {
-
-// An AllocationId identifies an _allocation_ produced by a Heap. An allocation
-// is analogous to the C++ notion of a complete object: the `Value` in an
-// allocation is not a sub-part of any other `Value`.
-class AllocationId : public Printable<AllocationId> {
- public:
-  AllocationId(const AllocationId&) = default;
-  auto operator=(const AllocationId&) -> AllocationId& = default;
-
-  inline friend auto operator==(AllocationId lhs, AllocationId rhs) -> bool {
-    return lhs.index_ == rhs.index_;
-  }
-  inline friend auto hash_value(AllocationId id) {
-    return llvm::hash_combine(id.index_);
-  }
-
-  // Prints a human-readable representation of *this to `out`.
-  //
-  // Currently that representation consists of an integer index.
-  void Print(llvm::raw_ostream& out) const {
-    out << "Allocation(" << index_ << ")";
-  }
-
- private:
-  // The representation of AllocationId describes how to locate an object within
-  // a Heap, so its implementation details are tied to the implementation
-  // details of Heap.
-  friend class Heap;
-
-  explicit AllocationId(size_t index) : index_(index) {}
-
-  size_t index_;
-};
-
-// An Address represents a memory address in the Carbon virtual machine.
-// Addresses are used to access values stored in a Heap. Unlike an
-// AllocationId, an Address can refer to a sub-Value of some larger Value.
-class Address : public Printable<Address> {
- public:
-  // Constructs an `Address` that refers to the value stored in `allocation`.
-  explicit Address(AllocationId allocation) : allocation_(allocation) {}
-
-  Address(const Address&) = default;
-  Address(Address&&) = default;
-  auto operator=(const Address&) -> Address& = default;
-  auto operator=(Address&&) -> Address& = default;
-
-  // Prints a human-readable representation of `a` to `out`.
-  //
-  // Currently, that representation consists of an AllocationId followed by an
-  // optional ElementPath specifying a particular field within that allocation.
-  void Print(llvm::raw_ostream& out) const {
-    out << allocation_ << element_path_;
-  }
-
-  // If *this represents the address of an object with a field named
-  // `field_name`, this method returns the address of that field.
-  auto ElementAddress(Nonnull<const Element*> element) const -> Address {
-    Address result = *this;
-    result.element_path_.Append(element);
-    return result;
-  }
-
-  // Drop all trailing BaseElements from the element path, returning the
-  // downcasted address.
-  auto DowncastedAddress() const -> Address {
-    Address address = *this;
-    address.element_path_.RemoveTrailingBaseElements();
-    return address;
-  }
-
-  inline friend auto operator==(const Address& lhs, const Address& rhs)
-      -> bool {
-    return lhs.allocation_ == rhs.allocation_ &&
-           lhs.element_path_ == rhs.element_path_;
-  }
-  inline friend auto hash_value(const Address& a) -> llvm::hash_code {
-    return llvm::hash_combine(a.allocation_, a.element_path_);
-  }
-
- private:
-  // The representation of Address describes how to locate an object within
-  // the Heap, so its implementation details are tied to the implementation
-  // details of the Heap.
-  friend class Heap;
-  friend class RuntimeScope;
-
-  AllocationId allocation_;
-  ElementPath element_path_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_ADDRESS_H_

+ 0 - 36
explorer/ast/ast.h

@@ -1,36 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_AST_H_
-#define CARBON_EXPLORER_AST_AST_H_
-
-#include <vector>
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/library_name.h"
-#include "explorer/base/nonnull.h"
-
-namespace Carbon {
-
-// A Carbon file's AST.
-struct AST {
-  // The package directive's library.
-  LibraryName package;
-  // The package directive's API or impl state.
-  bool is_api;
-  // Import directives.
-  std::vector<LibraryName> imports;
-  // The file's ordered declarations.
-  std::vector<Nonnull<Declaration*>> declarations;
-  // Synthesized call to `Main`. Injected after parsing.
-  std::optional<Nonnull<CallExpression*>> main_call;
-  // The number of declarations which came from the prelude.
-  // TODO: This should be removed once the prelude AST can be separated from
-  // per-file ASTs.
-  int num_prelude_declarations = 0;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_AST_H_

+ 0 - 161
explorer/ast/ast_kinds.h

@@ -1,161 +0,0 @@
-// 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 file defines a number of X-macros that can be used to walk the AST
-// class hierarchy. The main entry points are:
-//
-// -   `CARBON_AST_FOR_EACH_FINAL_CLASS(ACTION)`
-//     Invokes `ACTION` on each leaf class in the AST class hierarchy.
-// -   `CARBON_AST_FOR_EACH_ABSTRACT_CLASS(ACTION)`
-//     Invokes `ACTION` on each non-leaf class in the AST class hierarchy.
-// -   `CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(BASE, ACTION)`
-//     Invokes `ACTION` on each leaf class in the AST class hierarchy that
-//     derives from `BASE`.
-// -   `CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(BASE, ACTION)`
-//     Invokes `ACTION` on each non-leaf class in the AST class hierarchy that
-//     derives from `BASE`.
-//
-// These macros are implemented in terms of `CARBON_Foo_KINDS` X-macros. Each of
-// these macros takes two macro names as arguments:
-//
-// -   `CARBON_Foo_KINDS(ABSTRACT, FINAL)`
-//     Invokes `ABSTRACT(Class)` for each abstract class that inherits from
-//     `Foo`.
-//     Invokes `FINAL(Class)` for each final class that inherits from `Foo`.
-//
-// A fake root class, `AST_RTTI`, is also provided, so that
-// `CARBON_AST_RTTI_KINDS` can be used to walk all AST classes.
-#ifndef CARBON_EXPLORER_AST_AST_KINDS_H_
-#define CARBON_EXPLORER_AST_AST_KINDS_H_
-
-#define CARBON_RTTI_NOOP_ACTION(X)
-
-#define CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(ROOT, ACTION) \
-  CARBON_##ROOT##_KINDS(ACTION, CARBON_RTTI_NOOP_ACTION)
-#define CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(ROOT, ACTION) \
-  CARBON_##ROOT##_KINDS(CARBON_RTTI_NOOP_ACTION, ACTION)
-#define CARBON_AST_FOR_EACH_ABSTRACT_CLASS(ACTION) \
-  CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(AST_RTTI, ACTION)
-#define CARBON_AST_FOR_EACH_FINAL_CLASS(ACTION) \
-  CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(AST_RTTI, ACTION)
-
-// Class hierarchy description follows, with one _KINDS macro for each abstract
-// base class.
-
-#define CARBON_AST_RTTI_KINDS(ABSTRACT, FINAL)            \
-  CARBON_AstNode_KINDS(ABSTRACT, FINAL) ABSTRACT(AstNode) \
-  CARBON_Element_KINDS(ABSTRACT, FINAL) ABSTRACT(Element)
-
-#define CARBON_AstNode_KINDS(ABSTRACT, FINAL)                     \
-  CARBON_Pattern_KINDS(ABSTRACT, FINAL) ABSTRACT(Pattern)         \
-  CARBON_Declaration_KINDS(ABSTRACT, FINAL) ABSTRACT(Declaration) \
-  FINAL(ImplBinding)                                              \
-  FINAL(AlternativeSignature)                                     \
-  CARBON_Statement_KINDS(ABSTRACT, FINAL) ABSTRACT(Statement)     \
-  CARBON_Expression_KINDS(ABSTRACT, FINAL) ABSTRACT(Expression)   \
-  CARBON_WhereClause_KINDS(ABSTRACT, FINAL) ABSTRACT(WhereClause)
-
-#define CARBON_Pattern_KINDS(ABSTRACT, FINAL) \
-  FINAL(AutoPattern)                          \
-  FINAL(VarPattern)                           \
-  FINAL(AddrPattern)                          \
-  FINAL(BindingPattern)                       \
-  FINAL(GenericBinding)                       \
-  FINAL(TuplePattern)                         \
-  FINAL(AlternativePattern)                   \
-  FINAL(ExpressionPattern)
-
-#define CARBON_Declaration_KINDS(ABSTRACT, FINAL)         \
-  FINAL(NamespaceDeclaration)                             \
-  CARBON_CallableDeclaration_KINDS(ABSTRACT, FINAL)       \
-      ABSTRACT(CallableDeclaration)                       \
-  FINAL(SelfDeclaration)                                  \
-  FINAL(ClassDeclaration)                                 \
-  FINAL(MixinDeclaration)                                 \
-  FINAL(MixDeclaration)                                   \
-  FINAL(ChoiceDeclaration)                                \
-  FINAL(VariableDeclaration)                              \
-  CARBON_ConstraintTypeDeclaration_KINDS(ABSTRACT, FINAL) \
-      ABSTRACT(ConstraintTypeDeclaration)                 \
-  FINAL(InterfaceExtendDeclaration)                       \
-  FINAL(InterfaceRequireDeclaration)                      \
-  FINAL(AssociatedConstantDeclaration)                    \
-  FINAL(ImplDeclaration)                                  \
-  FINAL(MatchFirstDeclaration)                            \
-  FINAL(AliasDeclaration)                                 \
-  FINAL(ExtendBaseDeclaration)
-
-#define CARBON_CallableDeclaration_KINDS(ABSTRACT, FINAL) \
-  FINAL(FunctionDeclaration)                              \
-  FINAL(DestructorDeclaration)
-
-#define CARBON_ConstraintTypeDeclaration_KINDS(ABSTRACT, FINAL) \
-  FINAL(InterfaceDeclaration)                                   \
-  FINAL(ConstraintDeclaration)
-
-#define CARBON_Statement_KINDS(ABSTRACT, FINAL)         \
-  FINAL(ExpressionStatement)                            \
-  FINAL(Assign)                                         \
-  FINAL(IncrementDecrement)                             \
-  FINAL(VariableDefinition)                             \
-  FINAL(If)                                             \
-  CARBON_Return_KINDS(ABSTRACT, FINAL) ABSTRACT(Return) \
-  FINAL(Block)                                          \
-  FINAL(While)                                          \
-  FINAL(Break)                                          \
-  FINAL(Continue)                                       \
-  FINAL(Match)                                          \
-  FINAL(For)
-
-#define CARBON_Return_KINDS(ABSTRACT, FINAL) \
-  FINAL(ReturnVar)                           \
-  FINAL(ReturnExpression)
-
-#define CARBON_Expression_KINDS(ABSTRACT, FINAL)       \
-  FINAL(BoolTypeLiteral)                               \
-  FINAL(BoolLiteral)                                   \
-  FINAL(CallExpression)                                \
-  CARBON_ConstantValueLiteral_KINDS(ABSTRACT, FINAL)   \
-      ABSTRACT(ConstantValueLiteral)                   \
-  CARBON_MemberAccessExpression_KINDS(ABSTRACT, FINAL) \
-      ABSTRACT(MemberAccessExpression)                 \
-  FINAL(IndexExpression)                               \
-  FINAL(IntTypeLiteral)                                \
-  FINAL(IntLiteral)                                    \
-  FINAL(OperatorExpression)                            \
-  FINAL(StringLiteral)                                 \
-  FINAL(StringTypeLiteral)                             \
-  FINAL(TupleLiteral)                                  \
-  FINAL(StructLiteral)                                 \
-  FINAL(TypeTypeLiteral)                               \
-  FINAL(IdentifierExpression)                          \
-  FINAL(DotSelfExpression)                             \
-  FINAL(IntrinsicExpression)                           \
-  FINAL(IfExpression)                                  \
-  FINAL(WhereExpression)                               \
-  FINAL(BuiltinConvertExpression)                      \
-  FINAL(UnimplementedExpression)
-
-#define CARBON_ConstantValueLiteral_KINDS(ABSTRACT, FINAL) \
-  FINAL(FunctionTypeLiteral)                               \
-  FINAL(StructTypeLiteral)                                 \
-  FINAL(ArrayTypeLiteral)                                  \
-  FINAL(ValueLiteral)
-
-#define CARBON_MemberAccessExpression_KINDS(ABSTRACT, FINAL) \
-  FINAL(SimpleMemberAccessExpression)                        \
-  FINAL(CompoundMemberAccessExpression)                      \
-  FINAL(BaseAccessExpression)
-
-#define CARBON_WhereClause_KINDS(ABSTRACT, FINAL) \
-  FINAL(ImplsWhereClause)                         \
-  FINAL(EqualsWhereClause)                        \
-  FINAL(RewriteWhereClause)
-
-#define CARBON_Element_KINDS(ABSTRACT, FINAL) \
-  FINAL(NamedElement)                         \
-  FINAL(PositionalElement)                    \
-  FINAL(BaseElement)
-
-#endif  // CARBON_EXPLORER_AST_AST_KINDS_H_

+ 0 - 11
explorer/ast/ast_node.cpp

@@ -1,11 +0,0 @@
-// 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 "explorer/ast/ast_node.h"
-
-namespace Carbon {
-
-AstNode::~AstNode() = default;
-
-}  // namespace Carbon

+ 0 - 91
explorer/ast/ast_node.h

@@ -1,91 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_AST_NODE_H_
-#define CARBON_EXPLORER_AST_AST_NODE_H_
-
-#include "explorer/ast/ast_rtti.h"
-#include "explorer/base/source_location.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-class CloneContext;
-
-// Base class for all nodes in the AST.
-//
-// Every class derived from this class must be listed in ast_kinds.h. As a
-// result, every abstract class `Foo` will have a `FooKind` enumerated type,
-// whose enumerators correspond to the subclasses of `Foo`.
-//
-// AstNode and its derived classes support LLVM-style RTTI, including
-// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
-// class derived from Declaration must provide a `classof` operation, with
-// the following form, where `Foo` is the name of the derived class:
-//
-// static auto classof(const AstNode* node) -> bool {
-//   return InheritsFromFoo(node->kind());
-// }
-//
-// Furthermore, if the class is abstract, it must provide a `kind()` operation,
-// with the following form:
-//
-// auto kind() const -> FooKind { return static_cast<FooKind>(root_kind()); }
-//
-// The definitions of `InheritsFromFoo` and `FooKind` are generated from
-// ast_rtti.h, and are implicitly provided by this header.
-//
-// Every AST node is expected to provide a cloning constructor:
-//
-// explicit MyAstNode(CloneContext& context, const MyAstNode& other);
-//
-// The cloning constructor should behave like a copy constructor, but pointers
-// to other AST nodes should be passed through context.Clone to clone the
-// referenced object.
-//
-// TODO: To support generic traversal, add children() method, and ensure that
-//   all AstNodes are reachable from a root AstNode.
-class AstNode : public Printable<AstNode> {
- public:
-  AstNode(AstNode&&) = delete;
-  auto operator=(AstNode&&) -> AstNode& = delete;
-  virtual ~AstNode() = 0;
-
-  // Print the AST rooted at the node.
-  virtual void Print(llvm::raw_ostream& out) const = 0;
-  // Print identifying information about the node, such as its name.
-  virtual void PrintID(llvm::raw_ostream& out) const = 0;
-
-  // Returns an enumerator specifying the concrete type of this node.
-  //
-  // Abstract subclasses of AstNode will provide their own `kind()` method
-  // which hides this one, and provides a narrower return type.
-  auto kind() const -> AstNodeKind { return kind_; }
-
-  // The location of the code described by this node.
-  auto source_loc() const -> SourceLocation { return source_loc_; }
-
- protected:
-  // Constructs an AstNode representing code at the given location. `kind`
-  // must be the enumerator that exactly matches the concrete type being
-  // constructed.
-  explicit AstNode(AstNodeKind kind, SourceLocation source_loc)
-      : kind_(kind), source_loc_(source_loc) {}
-
-  // Clone this AstNode.
-  explicit AstNode(CloneContext& /*context*/, const AstNode& other)
-      : kind_(other.kind_), source_loc_(other.source_loc_) {}
-
-  // Equivalent to kind(), but will not be hidden by `kind()` methods of
-  // derived classes.
-  auto root_kind() const -> AstNodeKind { return kind_; }
-
- private:
-  AstNodeKind kind_;
-  SourceLocation source_loc_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_AST_NODE_H_

+ 0 - 19
explorer/ast/ast_rtti.cpp

@@ -1,19 +0,0 @@
-// 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 "explorer/ast/ast_rtti.h"
-
-namespace Carbon {
-
-// Define kind names for the base enumeration type.
-CARBON_DEFINE_ENUM_CLASS_NAMES(AstRttiNodeKind) = {
-    CARBON_AST_FOR_EACH_FINAL_CLASS(CARBON_ENUM_CLASS_NAME_STRING)};
-
-// For other kind enumerations, reuse the same table.
-#define DEFINE_NAME_FUNCTION(C)                                        \
-  CARBON_ENUM_NAME_FUNCTION(C##Kind) {                                 \
-    return AstRttiNodeKind(static_cast<const C##Kind&>(*this)).name(); \
-  }
-
-}  // namespace Carbon

+ 0 - 100
explorer/ast/ast_rtti.h

@@ -1,100 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_AST_RTTI_H_
-#define CARBON_EXPLORER_AST_AST_RTTI_H_
-
-#include "common/enum_base.h"
-#include "explorer/ast/ast_kinds.h"
-
-namespace Carbon {
-
-// Assign numbers to all AST types.
-CARBON_DEFINE_RAW_ENUM_CLASS(AstRttiNodeKind, int) {
-#define DEFINE_ENUMERATOR(E) E,
-  CARBON_AST_FOR_EACH_FINAL_CLASS(DEFINE_ENUMERATOR)
-#undef DEFINE_ENUMERATOR
-};
-
-// An enumerated type defining the kinds of AST nodes.
-class AstRttiNodeKind : public CARBON_ENUM_BASE(AstRttiNodeKind) {
- public:
-  using IsCarbonAstRttiNodeKind = void;
-
-  AstRttiNodeKind() = default;
-
-  // All our other RTTI node kinds implicitly convert to this one.
-  // TODO: We could support conversion to the base class's kind for all node
-  // kinds if we find a reason to do so.
-  template <typename Kind, typename = typename Kind::IsCarbonAstRttiNodeKind>
-  /*implicit*/ AstRttiNodeKind(Kind kind)
-      : AstRttiNodeKind(FromInt(kind.AsInt())) {}
-
-  // Expose the integer value of this node. This is used to set the values of
-  // other enumerations to match, and to implement range checks.
-  using EnumBase::AsInt;
-
-  CARBON_AST_FOR_EACH_FINAL_CLASS(CARBON_ENUM_CONSTANT_DECL)
-};
-
-// Define the constant members for AstRttiNodeKind.
-#define CONSTANT_DEFINITION(E) \
-  CARBON_ENUM_CONSTANT_DEFINITION(AstRttiNodeKind, E)
-CARBON_AST_FOR_EACH_FINAL_CLASS(CONSTANT_DEFINITION)
-#undef CONSTANT_DEFINITION
-
-// Define Kind enumerations for all base classes.
-#define DEFINE_KIND_ENUM(C)                                                 \
-  CARBON_DEFINE_RAW_ENUM_CLASS_NO_NAMES(C##Kind, int) {                     \
-    CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(C, DEFINE_ENUMERATOR)             \
-  };                                                                        \
-  template <typename Derived>                                               \
-  class C##KindTemplate                                                     \
-      : public CARBON_ENUM_BASE_CRTP(C##Kind, Derived, AstRttiNodeKind) {   \
-   private:                                                                 \
-    using Base = CARBON_ENUM_BASE_CRTP(C##Kind, Derived, AstRttiNodeKind);  \
-    friend class AstRttiNodeKind;                                           \
-                                                                            \
-   public:                                                                  \
-    using IsCarbonAstRttiNodeKind = void;                                   \
-                                                                            \
-    C##KindTemplate() = default;                                            \
-                                                                            \
-    /* This type can be explicitly converted from the generic node kind. */ \
-    explicit C##KindTemplate(AstRttiNodeKind base_kind)                     \
-        : Base(Base::FromInt(base_kind.AsInt())) {}                         \
-                                                                            \
-    CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(                                  \
-        C, CARBON_INLINE_ENUM_CONSTANT_DEFINITION)                          \
-  };                                                                        \
-  class C##Kind : public C##KindTemplate<C##Kind> {                         \
-    using C##KindTemplate<C##Kind>::C##KindTemplate;                        \
-  };
-#define DEFINE_ENUMERATOR(E) E = AstRttiNodeKind::E.AsInt(),
-CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_KIND_ENUM)
-#undef DEFINE_KIND_ENUM
-#undef DEFINE_ENUMERATOR
-
-// Define InheritsFrom functions for each abstract class.
-#define DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT(C)             \
-  inline bool InheritsFrom##C(Carbon::AstRttiNodeKind kind) { \
-    return CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(             \
-        C, INHERITS_FROM_CLASS_TEST) false;                   \
-  }
-#define INHERITS_FROM_CLASS_TEST(C) kind == Carbon::AstRttiNodeKind::C ||
-CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT)
-#undef DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT
-#undef INHERITS_FROM_CLASS_TEST
-
-// Define trivial InheritsFrom functions for each final class.
-#define DEFINE_INHERITS_FROM_FUNCTION_FINAL(C)                \
-  inline bool InheritsFrom##C(Carbon::AstRttiNodeKind kind) { \
-    return kind == Carbon::AstRttiNodeKind::C;                \
-  }
-CARBON_AST_FOR_EACH_FINAL_CLASS(DEFINE_INHERITS_FROM_FUNCTION_FINAL)
-#undef DEFINE_INHERITS_FROM_FUNCTION_FINAL
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_AST_RTTI_H_

+ 0 - 132
explorer/ast/ast_test_matchers.h

@@ -1,132 +0,0 @@
-// 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
-
-// Googlemock matchers for the AST. Unless otherwise specified, all the
-// functions in this file return matchers that can be applied to any
-// AstNode or AstNode*.
-//
-// TODO: Provide matchers for all node Kinds, and establish more uniform
-// conventions for them.
-
-#ifndef CARBON_EXPLORER_AST_AST_TEST_MATCHERS_H_
-#define CARBON_EXPLORER_AST_AST_TEST_MATCHERS_H_
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <ostream>
-
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/ast_test_matchers_internal.h"
-#include "explorer/ast/expression.h"
-
-namespace Carbon {
-
-// Matches a Block node whose .statements() match `matcher`.
-inline auto BlockContentsAre(
-    ::testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher) {
-  return TestingInternal::BlockContentsMatcher(std::move(matcher));
-}
-
-// Matches a literal with the given value.
-// TODO: add overload for string literals
-inline auto MatchesLiteral(int value) {
-  return TestingInternal::MatchesIntLiteralMatcher(value);
-}
-
-// The following functions all match a OperatorExpression with two
-// operands that match `lhs` and `rhs` (respectively). The name of the function
-// indicates what value of `.op()` they match.
-inline auto MatchesMul(::testing::Matcher<AstNode> lhs,
-                       ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::Mul, std::move(lhs), std::move(rhs));
-}
-
-inline auto MatchesAdd(::testing::Matcher<AstNode> lhs,
-                       ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::Add, std::move(lhs), std::move(rhs));
-}
-
-inline auto MatchesAnd(::testing::Matcher<AstNode> lhs,
-                       ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::And, std::move(lhs), std::move(rhs));
-}
-
-inline auto MatchesEq(::testing::Matcher<AstNode> lhs,
-                      ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::Eq, std::move(lhs), std::move(rhs));
-}
-
-inline auto MatchesOr(::testing::Matcher<AstNode> lhs,
-                      ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::Or, std::move(lhs), std::move(rhs));
-}
-
-inline auto MatchesSub(::testing::Matcher<AstNode> lhs,
-                       ::testing::Matcher<AstNode> rhs) {
-  return TestingInternal::BinaryOperatorExpressionMatcher(
-      Operator::Sub, std::move(lhs), std::move(rhs));
-}
-
-// Matches a return statement with no operand.
-inline auto MatchesEmptyReturn() {
-  return TestingInternal::MatchesReturnMatcher();
-}
-
-// Matches a return statement with an explicit operand that matches `matcher`.
-inline auto MatchesReturn(::testing::Matcher<AstNode> matcher) {
-  return TestingInternal::MatchesReturnMatcher(matcher);
-}
-
-// Matches a FunctionDeclaration. By default the returned object matches any
-// FunctionDeclaration, but it has methods for restricting the match, which can
-// be chained fluent-style:
-//
-// EXPECT_THAT(node, MatchesFunctionDeclaration()
-//     .WithName("Foo")
-//     .WithBody(BlockContentsAre(...)));
-//
-// The available methods are:
-//
-// // *this only matches if the declared name matches name_matcher.
-// WithName(::testing::Matcher<std::string> name_matcher)
-//
-// // *this only matches if the declaration has a body that matches
-// // body_matcher.
-// WithBody(::testing::Matcher<AstNode> body_matcher)
-//
-// TODO: Add method for matching only if the declaration has no body.
-// TODO: Add methods for matching parameters, deduced parameters,
-//   and return term.
-inline auto MatchesFunctionDeclaration() {
-  return TestingInternal::MatchesFunctionDeclarationMatcher();
-}
-
-// Matches an UnimplementedExpression with the given label, whose children
-// match `children_matcher`.
-inline auto MatchesUnimplementedExpression(
-    std::string label,
-    ::testing::Matcher<llvm::ArrayRef<Nonnull<const AstNode*>>>
-        children_matcher) {
-  return TestingInternal::MatchesUnimplementedExpressionMatcher(
-      std::move(label), std::move(children_matcher));
-}
-
-// Matches an `AST` whose declarations match the given matcher. Unlike other
-// matchers in this file, this matcher does not match pointers.
-inline auto ASTDeclarations(
-    ::testing::Matcher<std::vector<Nonnull<Declaration*>>>
-        declarations_matcher) {
-  return TestingInternal::ASTDeclarationsMatcher(
-      std::move(declarations_matcher));
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_AST_TEST_MATCHERS_H_

+ 0 - 214
explorer/ast/ast_test_matchers_internal.cpp

@@ -1,214 +0,0 @@
-// 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 "explorer/ast/ast_test_matchers_internal.h"
-
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace Carbon::TestingInternal {
-
-AstNodeMatcherBase::~AstNodeMatcherBase() = default;
-
-auto BlockContentsMatcher::MatchAndExplainImpl(
-    Nonnull<const AstNode*> node, ::testing::MatchResultListener* out) const
-    -> bool {
-  const auto* block = llvm::dyn_cast<Block>(node);
-  if (block == nullptr) {
-    *out << "is not a Block";
-    return false;
-  }
-  *out << "is a Block whose statements collection ";
-  return matcher_.MatchAndExplain(block->statements(), out);
-}
-
-auto MatchesIntLiteralMatcher::MatchAndExplainImpl(
-    const AstNode* node, ::testing::MatchResultListener* listener) const
-    -> bool {
-  const auto* literal = llvm::dyn_cast<IntLiteral>(node);
-  if (literal == nullptr) {
-    *listener << "is not an IntLiteral";
-    return false;
-  }
-  bool matched = literal->value() == value_;
-  *listener << "is " << (matched ? "" : "not ") << "a literal " << value_;
-  return matched;
-}
-
-auto BinaryOperatorExpressionMatcher::MatchAndExplainImpl(
-    Nonnull<const AstNode*> node, ::testing::MatchResultListener* out) const
-    -> bool {
-  const auto* op = llvm::dyn_cast<OperatorExpression>(node);
-  if (op == nullptr) {
-    *out << "which is not a OperatorExpression";
-    return false;
-  }
-  if (op->arguments().size() != 2) {
-    *out << "which does not have two operands";
-    return false;
-  }
-  if (op->op() != op_) {
-    *out << "whose operator is not " << OperatorToString(op_);
-    return false;
-  }
-  *out << "which is a " << OperatorToString(op_)
-       << " expression whose left operand ";
-  bool matched = lhs_.MatchAndExplain(*op->arguments()[0], out);
-  *out << " and right operand ";
-  if (!rhs_.MatchAndExplain(*op->arguments()[1], out)) {
-    matched = false;
-  }
-  return matched;
-}
-
-void BinaryOperatorExpressionMatcher::DescribeToImpl(std::ostream* out,
-                                                     bool negated) const {
-  *out << "is " << (negated ? "not " : "") << "a " << OperatorToString(op_)
-       << " expression whose ";
-  *out << "left operand ";
-  lhs_.DescribeTo(out);
-  *out << " and right operand ";
-  rhs_.DescribeTo(out);
-}
-
-auto MatchesReturnMatcher::MatchAndExplainImpl(
-    const AstNode* node, ::testing::MatchResultListener* listener) const
-    -> bool {
-  const auto* ret = llvm::dyn_cast<ReturnExpression>(node);
-  if (ret == nullptr) {
-    *listener << "which is not a return statement";
-    return false;
-  }
-  *listener << "which is a return statement ";
-  if (ret->is_omitted_expression()) {
-    *listener << "with no operand";
-    return !matcher_.has_value();
-  } else if (matcher_.has_value()) {
-    *listener << "whose operand ";
-    return matcher_->MatchAndExplain(ret->expression(), listener);
-  } else {
-    *listener << "that has an operand";
-    return false;
-  }
-}
-
-void MatchesReturnMatcher::DescribeToImpl(std::ostream* out,
-                                          bool negated) const {
-  *out << "is " << (negated ? "not " : "") << "a return statement ";
-  if (matcher_.has_value()) {
-    *out << "whose operand ";
-    matcher_->DescribeTo(out);
-  } else {
-    *out << "with no operand";
-  }
-}
-
-namespace {
-// llvm::raw_ostream implementation backed by a MatchResultListener, so
-// we can use tools like llvm::ListSeparator.
-class RawListenerOstream : public llvm::raw_ostream {
- public:
-  explicit RawListenerOstream(Nonnull<::testing::MatchResultListener*> listener)
-      : out_(listener->stream()), fake_pos_(0) {}
-
-  void write_impl(const char* ptr, size_t size) override {
-    if (out_ == nullptr) {
-      fake_pos_ += size;
-    } else {
-      out_->write(ptr, size);
-    }
-  }
-
-  auto current_pos() const -> uint64_t override {
-    if (out_ == nullptr) {
-      return fake_pos_;
-    } else {
-      return out_->tellp();
-    }
-  }
-
-  ~RawListenerOstream() override { flush(); }
-
- private:
-  std::ostream* out_;
-  // fake_pos_ tracks the notional output position when out_ is null.
-  uint64_t fake_pos_;
-};
-}  // namespace
-
-auto MatchesFunctionDeclarationMatcher::MatchAndExplainImpl(
-    const AstNode* node, ::testing::MatchResultListener* listener) const
-    -> bool {
-  RawListenerOstream out(listener);
-  const auto* decl = llvm::dyn_cast<FunctionDeclaration>(node);
-  if (decl == nullptr) {
-    out << "which is not a function declaration";
-    return false;
-  }
-  out << "which is a function declaration ";
-  llvm::ListSeparator sep(", and");
-  if (name_matcher_.has_value()) {
-    out << sep << "whose name ";
-    if (!name_matcher_->MatchAndExplain(std::string(decl->name().inner_name()),
-                                        listener)) {
-      // We short-circuit here because if the name doesn't match, that's
-      // probably the only information the user cares about.
-      return false;
-    }
-  }
-  bool matched = true;
-  if (body_matcher_.has_value()) {
-    out << sep;
-    if (!decl->body().has_value()) {
-      out << "that doesn't have a body";
-      matched = false;
-    } else {
-      out << "whose body ";
-      if (!body_matcher_->MatchAndExplain(**decl->body(), listener)) {
-        matched = false;
-      }
-    }
-  }
-  return matched;
-}
-
-void MatchesFunctionDeclarationMatcher::DescribeToImpl(std::ostream* out,
-                                                       bool negated) const {
-  llvm::raw_os_ostream raw_out(*out);
-  raw_out << "is " << (negated ? "not " : "") << "a function declaration ";
-  llvm::ListSeparator sep(", and");
-  if (name_matcher_.has_value()) {
-    raw_out << sep << "whose name ";
-    name_matcher_->DescribeTo(out);
-  }
-  if (body_matcher_.has_value()) {
-    raw_out << sep << "whose body ";
-    body_matcher_->DescribeTo(out);
-  }
-}
-
-auto MatchesUnimplementedExpressionMatcher::MatchAndExplainImpl(
-    const AstNode* node, ::testing::MatchResultListener* listener) const
-    -> bool {
-  const auto* unimplemented = llvm::dyn_cast<UnimplementedExpression>(node);
-  if (unimplemented == nullptr) {
-    *listener << "is not an UnimplementedExpression";
-    return false;
-  }
-  if (unimplemented->label() != label_) {
-    *listener << "is not labeled " << label_;
-    return false;
-  }
-  *listener << "is an unimplemented " << label_ << " node whose children ";
-  return children_matcher_.MatchAndExplain(unimplemented->children(), listener);
-}
-
-void MatchesUnimplementedExpressionMatcher::DescribeToImpl(std::ostream* out,
-                                                           bool negated) const {
-  *out << "is " << (negated ? "not " : "") << "an unimplemented " << label_
-       << " node whose children ";
-  children_matcher_.DescribeTo(out);
-}
-
-}  // namespace Carbon::TestingInternal

+ 0 - 237
explorer/ast/ast_test_matchers_internal.h

@@ -1,237 +0,0 @@
-// 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
-
-// Implementation details of the functions in ast_test_matchers.h.
-
-#ifndef CARBON_EXPLORER_AST_AST_TEST_MATCHERS_INTERNAL_H_
-#define CARBON_EXPLORER_AST_AST_TEST_MATCHERS_INTERNAL_H_
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <ostream>
-
-#include "explorer/ast/ast.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/statement.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon::TestingInternal {
-
-// Abstract GoogleMock matcher which matches AstNodes, and is agnostic to
-// whether they are passed by pointer or reference. Derived classes specify what
-// kinds of AstNodes they match by overriding DescribeToImpl and
-// MatchAndExplainImpl.
-class AstNodeMatcherBase {
- public:
-  // NOLINTNEXTLINE(readability-identifier-naming)
-  using is_gtest_matcher = void;
-
-  virtual ~AstNodeMatcherBase();
-
-  void DescribeTo(std::ostream* out) const {
-    DescribeToImpl(out, /*negated=*/false);
-  }
-
-  void DescribeNegationTo(std::ostream* out) const {
-    DescribeToImpl(out, /*negated=*/true);
-  }
-
-  auto MatchAndExplain(const AstNode& node,
-                       ::testing::MatchResultListener* out) const -> bool {
-    return MatchAndExplainImpl(&node, out);
-  }
-
-  auto MatchAndExplain(Nonnull<const AstNode*> node,
-                       ::testing::MatchResultListener* out) const -> bool {
-    return MatchAndExplainImpl(node, out);
-  }
-
- private:
-  // The implementation of this method must satisfy the contract of
-  // `DescribeTo(out)` (as specified by GoogleMock) if `negated` is false,
-  // or the contract of `DescribeNegationTo(out)` if `negated` is true.
-  virtual void DescribeToImpl(std::ostream* out, bool negated) const = 0;
-
-  // The implementation of this method must satisfy the contract of
-  // `MatchAndExplain(node, out)`, as specified by GoogleMock.
-  virtual auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
-                                   ::testing::MatchResultListener* out) const
-      -> bool = 0;
-};
-
-// Matches a Block based on its contents.
-class BlockContentsMatcher : public AstNodeMatcherBase {
- public:
-  // Constructs a matcher which matches a Block node whose .statements() matches
-  // `matcher`.
-  explicit BlockContentsMatcher(
-      ::testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher)
-      : matcher_(std::move(matcher)) {}
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override {
-    *out << "is " << (negated ? "not " : "")
-         << "a Block whose statements collection ";
-    matcher_.DescribeTo(out);
-  }
-
-  auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
-                           ::testing::MatchResultListener* out) const
-      -> bool override;
-
-  testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher_;
-};
-
-// Matches an IntLiteral.
-class MatchesIntLiteralMatcher : public AstNodeMatcherBase {
- public:
-  // Constructs a matcher which matches an IntLiteral whose value() is `value`.
-  explicit MatchesIntLiteralMatcher(int value) : value_(value) {}
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override {
-    *out << "is " << (negated ? "not " : "") << "a literal " << value_;
-  }
-
-  auto MatchAndExplainImpl(const AstNode* node,
-                           ::testing::MatchResultListener* listener) const
-      -> bool override;
-
-  int value_;
-};
-
-// Matches a OperatorExpression that has two operands.
-class BinaryOperatorExpressionMatcher : public AstNodeMatcherBase {
- public:
-  // Constructs a matcher which matches a OperatorExpression whose
-  // operator is `op`, and which has two operands that match `lhs` and `rhs`
-  // respectively.
-  explicit BinaryOperatorExpressionMatcher(Operator op,
-                                           ::testing::Matcher<AstNode> lhs,
-                                           ::testing::Matcher<AstNode> rhs)
-      : op_(op), lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override;
-
-  auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
-                           ::testing::MatchResultListener* out) const
-      -> bool override;
-
-  Operator op_;
-  ::testing::Matcher<AstNode> lhs_;
-  ::testing::Matcher<AstNode> rhs_;
-};
-
-// Matches a Return node.
-class MatchesReturnMatcher : public AstNodeMatcherBase {
- public:
-  // Constructs a matcher which matches a Return statement that has no operand.
-  explicit MatchesReturnMatcher() = default;
-
-  // Constructs a matcher which matches a Return statement that has an explicit
-  // operand that matches `matcher`.
-  explicit MatchesReturnMatcher(::testing::Matcher<AstNode> matcher)
-      : matcher_(std::move(matcher)) {}
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override;
-
-  auto MatchAndExplainImpl(const AstNode* node,
-                           ::testing::MatchResultListener* listener) const
-      -> bool override;
-
-  std::optional<::testing::Matcher<AstNode>> matcher_;
-};
-
-// Matches a FunctionDeclaration. See documentation for
-// MatchesFunctionDeclaration in ast_test_matchers.h.
-class MatchesFunctionDeclarationMatcher : public AstNodeMatcherBase {
- public:
-  MatchesFunctionDeclarationMatcher() = default;
-
-  auto WithName(::testing::Matcher<std::string> name_matcher)
-      -> MatchesFunctionDeclarationMatcher& {
-    name_matcher_ = std::move(name_matcher);
-    return *this;
-  }
-
-  auto WithBody(::testing::Matcher<AstNode> body_matcher)
-      -> MatchesFunctionDeclarationMatcher& {
-    body_matcher_ = std::move(body_matcher);
-    return *this;
-  }
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override;
-  auto MatchAndExplainImpl(const AstNode* node,
-                           ::testing::MatchResultListener* listener) const
-      -> bool override;
-
-  std::optional<::testing::Matcher<std::string>> name_matcher_;
-  std::optional<::testing::Matcher<AstNode>> body_matcher_;
-};
-
-// Matches an UnimplementedExpression.
-class MatchesUnimplementedExpressionMatcher : public AstNodeMatcherBase {
- public:
-  // Constructs a matcher which matches an UnimplementedExpression that has the
-  // given label, and whose children match children_matcher.
-  MatchesUnimplementedExpressionMatcher(
-      std::string label,
-      ::testing::Matcher<llvm::ArrayRef<Nonnull<const AstNode*>>>
-          children_matcher)
-      : label_(std::move(label)),
-        children_matcher_(std::move(children_matcher)) {}
-
- private:
-  void DescribeToImpl(std::ostream* out, bool negated) const override;
-
-  auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
-                           ::testing::MatchResultListener* listener) const
-      -> bool override;
-
-  std::string label_;
-  ::testing::Matcher<llvm::ArrayRef<Nonnull<const AstNode*>>> children_matcher_;
-};
-
-// Matches an `AST`.
-class ASTDeclarationsMatcher {
- public:
-  // NOLINTNEXTLINE(readability-identifier-naming)
-  using is_gtest_matcher = void;
-
-  // Constructs a matcher which matches an `AST` whose `declarations` member
-  // matches `declarations_matcher`
-  explicit ASTDeclarationsMatcher(
-      ::testing::Matcher<std::vector<Nonnull<Declaration*>>>
-          declarations_matcher)
-      : declarations_matcher_(std::move(declarations_matcher)) {}
-
-  void DescribeTo(std::ostream* out) const {
-    *out << "AST declarations ";
-    declarations_matcher_.DescribeTo(out);
-  }
-
-  void DescribeNegationTo(std::ostream* out) const {
-    *out << "AST declarations ";
-    declarations_matcher_.DescribeNegationTo(out);
-  }
-
-  auto MatchAndExplain(const AST& ast,
-                       ::testing::MatchResultListener* listener) const -> bool {
-    *listener << "whose declarations ";
-    return declarations_matcher_.MatchAndExplain(ast.declarations, listener);
-  }
-
- private:
-  ::testing::Matcher<std::vector<Nonnull<Declaration*>>> declarations_matcher_;
-};
-
-}  // namespace Carbon::TestingInternal
-
-#endif  // CARBON_EXPLORER_AST_AST_TEST_MATCHERS_INTERNAL_H_

+ 0 - 162
explorer/ast/ast_test_matchers_test.cpp

@@ -1,162 +0,0 @@
-// 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 "explorer/ast/ast_test_matchers.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/statement.h"
-#include "explorer/base/arena.h"
-
-namespace Carbon {
-namespace {
-
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::IsEmpty;
-using ::testing::Not;
-
-static constexpr SourceLocation DummyLoc("dummy", 0, FileKind::Main);
-
-TEST(ASTTestMatchers, BlockContentsAreTest) {
-  Block empty_block(DummyLoc, {});
-  EXPECT_THAT(empty_block, BlockContentsAre(IsEmpty()));
-  EXPECT_THAT(&empty_block, BlockContentsAre(IsEmpty()));
-
-  Break break_node(DummyLoc);
-  EXPECT_THAT(break_node, Not(BlockContentsAre(_)));
-
-  Block break_block(DummyLoc, {&break_node});
-  EXPECT_THAT(break_block, Not(BlockContentsAre(IsEmpty())));
-}
-
-TEST(ASTTestMatchers, MatchesLiteralTest) {
-  IntLiteral literal(DummyLoc, 42);
-  EXPECT_THAT(literal, MatchesLiteral(42));
-  EXPECT_THAT(&literal, MatchesLiteral(42));
-  EXPECT_THAT(literal, Not(MatchesLiteral(43)));
-  EXPECT_THAT(StringLiteral(DummyLoc, "foo"), Not(MatchesLiteral(42)));
-}
-
-TEST(ASTTestMatchers, MatchesMulTest) {
-  IntLiteral two(DummyLoc, 2);
-  IntLiteral three(DummyLoc, 3);
-  OperatorExpression mul(DummyLoc, Operator::Mul, {&two, &three});
-  EXPECT_THAT(mul, MatchesMul(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(&mul, MatchesMul(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(mul, MatchesMul(_, _));
-  EXPECT_THAT(mul, Not(MatchesMul(MatchesLiteral(2), MatchesLiteral(2))));
-  EXPECT_THAT(StringLiteral(DummyLoc, "foo"), Not(MatchesMul(_, _)));
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::Deref, {&two}),
-              Not(MatchesMul(_, _)));
-
-  OperatorExpression nested(DummyLoc, Operator::Mul, {&two, &mul});
-  EXPECT_THAT(nested,
-              MatchesMul(MatchesLiteral(2),
-                         MatchesMul(MatchesLiteral(2), MatchesLiteral(3))));
-}
-
-TEST(ASTTestMatchers, MatchesBinaryOpTest) {
-  IntLiteral two(DummyLoc, 2);
-  IntLiteral three(DummyLoc, 3);
-
-  // Testing of MatchesMul provides most of the coverage for these matchers,
-  // since they are thin wrappers around a common implementation. We only test
-  // the others enough to detect copy-paste errors in the wrappers.
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::Add, {&two, &three}),
-              MatchesAdd(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::And, {&two, &three}),
-              MatchesAnd(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::Eq, {&two, &three}),
-              MatchesEq(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::Or, {&two, &three}),
-              MatchesOr(MatchesLiteral(2), MatchesLiteral(3)));
-  EXPECT_THAT(OperatorExpression(DummyLoc, Operator::Sub, {&two, &three}),
-              MatchesSub(MatchesLiteral(2), MatchesLiteral(3)));
-}
-
-TEST(ASTTestMatchers, MatchesReturnTest) {
-  TupleLiteral unit(DummyLoc);
-  ReturnExpression empty_return(DummyLoc, &unit,
-                                /*is_omitted_expression=*/true);
-  EXPECT_THAT(empty_return, MatchesEmptyReturn());
-  EXPECT_THAT(&empty_return, MatchesEmptyReturn());
-  EXPECT_THAT(empty_return, Not(MatchesReturn(_)));
-
-  IntLiteral int_val(DummyLoc, 42);
-  ReturnExpression explicit_return(DummyLoc, &int_val,
-                                   /*is_omitted_expression=*/false);
-  EXPECT_THAT(explicit_return, MatchesReturn(MatchesLiteral(42)));
-  EXPECT_THAT(explicit_return, Not(MatchesEmptyReturn()));
-
-  EXPECT_THAT(int_val, Not(MatchesEmptyReturn()));
-  EXPECT_THAT(int_val, Not(MatchesReturn(_)));
-}
-
-TEST(ASTTestMatchers, MatchesFunctionDeclarationTest) {
-  TuplePattern params(DummyLoc, {});
-  Block body(DummyLoc, {});
-  FunctionDeclaration decl(DummyLoc, DeclaredName(DummyLoc, "Foo"), {},
-                           std::nullopt, &params, ReturnTerm::Omitted(DummyLoc),
-                           &body, VirtualOverride::None);
-
-  EXPECT_THAT(decl, MatchesFunctionDeclaration());
-  EXPECT_THAT(&decl, MatchesFunctionDeclaration());
-  EXPECT_THAT(decl, MatchesFunctionDeclaration().WithName("Foo"));
-  EXPECT_THAT(decl, MatchesFunctionDeclaration().WithBody(_));
-  EXPECT_THAT(decl, MatchesFunctionDeclaration().WithName("Foo").WithBody(_));
-  EXPECT_THAT(decl, MatchesFunctionDeclaration().WithBody(_).WithName("Foo"));
-  EXPECT_THAT(decl, Not(MatchesFunctionDeclaration().WithName("Bar")));
-  EXPECT_THAT(decl,
-              Not(MatchesFunctionDeclaration().WithBody(MatchesLiteral(0))));
-
-  FunctionDeclaration forward_decl(
-      DummyLoc, DeclaredName(DummyLoc, "Foo"), {}, std::nullopt, &params,
-      ReturnTerm::Omitted(DummyLoc), std::nullopt, VirtualOverride::None);
-  EXPECT_THAT(forward_decl, MatchesFunctionDeclaration().WithName("Foo"));
-  EXPECT_THAT(forward_decl, Not(MatchesFunctionDeclaration().WithBody(_)));
-
-  EXPECT_THAT(body, Not(MatchesFunctionDeclaration()));
-}
-
-TEST(ASTTestMatchers, MatchesUnimplementedExpressionTest) {
-  IntLiteral two(DummyLoc, 2);
-  IntLiteral three(DummyLoc, 3);
-  UnimplementedExpression unimplemented(DummyLoc, "DummyLabel", &two, &three);
-
-  EXPECT_THAT(unimplemented, MatchesUnimplementedExpression(
-                                 "DummyLabel", ElementsAre(MatchesLiteral(2),
-                                                           MatchesLiteral(3))));
-  EXPECT_THAT(
-      &unimplemented,
-      MatchesUnimplementedExpression(
-          "DummyLabel", ElementsAre(MatchesLiteral(2), MatchesLiteral(3))));
-  EXPECT_THAT(
-      unimplemented,
-      Not(MatchesUnimplementedExpression(
-          "WrongLabel", ElementsAre(MatchesLiteral(2), MatchesLiteral(3)))));
-  EXPECT_THAT(unimplemented,
-              Not(MatchesUnimplementedExpression("DummyLabel", IsEmpty())));
-  EXPECT_THAT(two,
-              Not(MatchesUnimplementedExpression("DummyLabel", IsEmpty())));
-}
-
-TEST(ASTTestMatchers, ASTDeclarationsTest) {
-  TuplePattern params(DummyLoc, {});
-  Block body(DummyLoc, {});
-  FunctionDeclaration decl(DummyLoc, DeclaredName(DummyLoc, "Foo"), {},
-                           std::nullopt, &params, ReturnTerm::Omitted(DummyLoc),
-                           &body, VirtualOverride::None);
-  AST ast = {.declarations = {&decl}};
-
-  EXPECT_THAT(ast, ASTDeclarations(ElementsAre(MatchesFunctionDeclaration())));
-  EXPECT_THAT(ast, Not(ASTDeclarations(IsEmpty())));
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 88
explorer/ast/bindings.cpp

@@ -1,88 +0,0 @@
-// 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 "explorer/ast/bindings.h"
-
-#include "common/error.h"
-#include "explorer/ast/impl_binding.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/value.h"
-#include "llvm/ADT/StringExtras.h"
-
-namespace Carbon {
-
-Bindings::Bindings(CloneContext& context, const Bindings& other) {
-  for (auto [binding, value] : other.args_) {
-    args_.insert({context.Remap(binding), context.Clone(value)});
-  }
-  for (auto [binding, value] : other.witnesses_) {
-    witnesses_.insert({context.Remap(binding), context.Clone(value)});
-  }
-}
-
-void Bindings::Add(Nonnull<const GenericBinding*> binding,
-                   Nonnull<const Value*> value,
-                   std::optional<Nonnull<const Value*>> witness) {
-  bool added_value = args_.insert({binding, value}).second;
-  CARBON_CHECK(added_value, "Add of already-existing binding");
-
-  if (witness) {
-    // TODO: Eventually we should check that we have a witness if and only if
-    // the binding has an impl binding.
-    auto impl_binding = binding->impl_binding();
-    CARBON_CHECK(impl_binding, "Given witness but have no impl binding");
-    bool added_witness = witnesses_.insert({*impl_binding, *witness}).second;
-    CARBON_CHECK(added_witness, "Add of already-existing binding");
-  }
-}
-
-void Bindings::Print(llvm::raw_ostream& out) const {
-  std::vector<std::pair<Nonnull<const GenericBinding*>, Nonnull<const Value*>>>
-      args(args_.begin(), args_.end());
-
-  std::vector<std::pair<Nonnull<const ImplBinding*>, Nonnull<const Value*>>>
-      witnesses(witnesses_.begin(), witnesses_.end());
-
-  std::stable_sort(args.begin(), args.end(), [](const auto& a, const auto& b) {
-    return a.first->index() < b.first->index();
-  });
-
-  std::stable_sort(
-      witnesses.begin(), witnesses.end(), [](const auto& a, const auto& b) {
-        return a.first->type_var()->index() < b.first->type_var()->index();
-      });
-
-  llvm::ListSeparator sep;
-  out << " >  bindings args: [";
-  for (const auto& [binding, value] : args) {
-    out << sep << "`" << *binding << "`: `" << *value << "`";
-  }
-  out << "]\n >  bindings witnesses: [";
-  for (const auto& [binding, value] : witnesses) {
-    out << sep << "`" << *binding << "`: `" << *value << "`";
-  }
-  out << "]";
-}
-
-auto Bindings::None() -> Nonnull<const Bindings*> {
-  static Nonnull<const Bindings*> bindings = new Bindings;
-  return bindings;
-}
-
-auto Bindings::SymbolicIdentity(
-    Nonnull<Arena*> arena,
-    llvm::ArrayRef<Nonnull<const GenericBinding*>> bindings)
-    -> Nonnull<const Bindings*> {
-  auto* result = arena->New<Bindings>();
-  for (const auto* binding : bindings) {
-    std::optional<Nonnull<const Value*>> witness;
-    if (binding->impl_binding()) {
-      witness = *binding->impl_binding().value()->symbolic_identity();
-    }
-    result->Add(binding, *binding->symbolic_identity(), witness);
-  }
-  return result;
-}
-
-}  // namespace Carbon

+ 0 - 92
explorer/ast/bindings.h

@@ -1,92 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_BINDINGS_H_
-#define CARBON_EXPLORER_AST_BINDINGS_H_
-
-#include <map>
-#include <utility>
-
-#include "common/ostream.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/base/nonnull.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringExtras.h"
-
-namespace Carbon {
-
-class Arena;
-class ImplBinding;
-class GenericBinding;
-class Value;
-
-using BindingMap =
-    std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
-using ImplWitnessMap =
-    std::map<Nonnull<const ImplBinding*>, Nonnull<const Value*>>;
-
-// A set of evaluated bindings in some context, such as a function or class.
-//
-// These are shared by a context and all unparameterized entities within that
-// context. For example, a class and the name of a method within that class
-// will have the same set of bindings.
-class Bindings : public Printable<Bindings> {
- public:
-  // Gets an empty set of bindings.
-  static auto None() -> Nonnull<const Bindings*>;
-
-  // Makes a set of symbolic identity bindings for the given collection of
-  // generic bindings and their impl bindings.
-  static auto SymbolicIdentity(
-      Nonnull<Arena*> arena,
-      llvm::ArrayRef<Nonnull<const GenericBinding*>> bindings)
-      -> Nonnull<const Bindings*>;
-
-  // Create an empty set of bindings.
-  Bindings() = default;
-
-  // Create an instantiated set of bindings for use during evaluation,
-  // containing both arguments and witnesses.
-  explicit Bindings(BindingMap args, ImplWitnessMap witnesses)
-      : args_(std::move(args)), witnesses_(std::move(witnesses)) {}
-
-  enum NoWitnessesTag { NoWitnesses };
-
-  // Create a set of bindings for use during type-checking, containing only the
-  // arguments but not the corresponding witnesses.
-  explicit Bindings(BindingMap args, NoWitnessesTag /*unused*/)
-      : args_(std::move(args)) {}
-
-  explicit Bindings(CloneContext& context, const Bindings& other);
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(args_, witnesses_);
-  }
-
-  void Print(llvm::raw_ostream& out) const;
-
-  // Add a value, and perhaps a witness, for a generic binding.
-  void Add(Nonnull<const GenericBinding*> binding, Nonnull<const Value*> value,
-           std::optional<Nonnull<const Value*>> witness);
-
-  // Argument values corresponding to generic bindings.
-  auto args() const -> const BindingMap& { return args_; }
-
-  // Witnesses corresponding to impl bindings.
-  auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
-
-  // Determine whether this is an empty set of bindings.
-  [[nodiscard]] auto empty() const -> bool {
-    return args_.empty() && witnesses_.empty();
-  }
-
- private:
-  BindingMap args_;
-  ImplWitnessMap witnesses_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_BINDINGS_H_

+ 0 - 104
explorer/ast/clone_context.cpp

@@ -1,104 +0,0 @@
-// 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 "explorer/ast/clone_context.h"
-
-#include "explorer/ast/ast_kinds.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/value_transform.h"
-
-namespace Carbon {
-
-auto CloneContext::CloneBase(Nonnull<const AstNode*> node)
-    -> Nonnull<AstNode*> {
-  auto [it, added] = nodes_.insert({node, nullptr});
-  CARBON_CHECK(
-      added, "node was {0}: {1}",
-      it->second ? "cloned multiple times" : "remapped before it was cloned",
-      *node);
-
-  // TODO: Generate a Visit member on AstNode and use it here to avoid these
-  // macros.
-  switch (node->kind()) {
-#define IGNORE(C)
-#define CLONE_CASE(C)                                         \
-  case AstNodeKind::C:                                        \
-    arena_->New<C>(Arena::WriteAddressTo{&it->second}, *this, \
-                   static_cast<const C&>(*node));             \
-    break;
-    CARBON_AstNode_KINDS(IGNORE, CLONE_CASE)
-#undef CLONE_CASE
-#undef IGNORE
-  }
-
-  // Cloning may have invalidated our iterator; redo lookup.
-  auto* result = nodes_[node];
-  CARBON_CHECK(result, "CloneImpl didn't set the result pointer");
-  return result;
-}
-
-class CloneContext::CloneValueTransform
-    : public ValueTransform<CloneValueTransform, NoOpUnwrapper> {
- public:
-  CloneValueTransform(Nonnull<CloneContext*> context, Nonnull<Arena*> arena)
-      : ValueTransform(arena), context_(context) {}
-
-  using ValueTransform::operator();
-
-  // Transforming a pointer to an AstNode should remap the node. Values do not
-  // own the nodes they point to, apart from the exceptions handled below.
-  template <typename NodeT>
-  auto operator()(Nonnull<const NodeT*> node, int /*unused*/ = 0)
-      -> std::enable_if_t<std::is_base_of_v<AstNode, NodeT>,
-                          Nonnull<const NodeT*>> {
-    return context_->Remap(node);
-  }
-
-  // Transforming a value node view should clone it. The value node view does
-  // not itself own the node it points to, so this is a shallow clone.
-  auto operator()(ValueNodeView value_node) -> ValueNodeView {
-    return context_->Clone(value_node);
-  }
-
-  // A FunctionType may or may not own its bindings.
-  auto operator()(Nonnull<const FunctionType*> fn_type)
-      -> Nonnull<const FunctionType*> {
-    for (const auto* binding : fn_type->deduced_bindings()) {
-      context_->MaybeCloneBase(binding);
-    }
-    for (auto [index, binding] : fn_type->generic_parameters()) {
-      context_->MaybeCloneBase(binding);
-    }
-    return ValueTransform::operator()(fn_type);
-  }
-
-  // A ConstraintType owns its self binding, so we need to clone it.
-  auto operator()(Nonnull<const ConstraintType*> constraint)
-      -> Nonnull<const Value*> {
-    context_->Clone(constraint->self_binding());
-    return ValueTransform::operator()(constraint);
-  }
-
- private:
-  Nonnull<CloneContext*> context_;
-};
-
-auto CloneContext::CloneBase(Nonnull<const Value*> value) -> Nonnull<Value*> {
-  return const_cast<Value*>(CloneValueTransform(this, arena_).Transform(value));
-}
-
-auto CloneContext::CloneBase(Nonnull<const Element*> elem)
-    -> Nonnull<Element*> {
-  return const_cast<Element*>(
-      CloneValueTransform(this, arena_).Transform(elem));
-}
-
-void CloneContext::MaybeCloneBase(Nonnull<const AstNode*> node) {
-  auto it = nodes_.find(node);
-  if (it == nodes_.end()) {
-    Clone(node);
-  }
-}
-
-}  // namespace Carbon

+ 0 - 167
explorer/ast/clone_context.h

@@ -1,167 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_CLONE_CONTEXT_H_
-#define CARBON_EXPLORER_AST_CLONE_CONTEXT_H_
-
-#include <optional>
-#include <type_traits>
-#include <vector>
-
-#include "common/check.h"
-#include "explorer/ast/ast_rtti.h"
-#include "explorer/base/arena.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-class AstNode;
-class Element;
-class Value;
-
-// A context for performing a deep copy of some fragment of the AST.
-//
-// This class carries the state necessary to make the copy, including ensuring
-// that each node is cloned only once and mapping from old nodes to new ones.
-class CloneContext {
- public:
-  explicit CloneContext(Nonnull<Arena*> arena) : arena_(arena) {}
-
-  CloneContext(const CloneContext&) = delete;
-  auto operator=(const CloneContext&) -> const CloneContext& = delete;
-
-  // Clone an AST element.
-  template <typename T>
-  auto Clone(Nonnull<const T*> node) -> Nonnull<T*> {
-    if constexpr (std::is_convertible_v<const T*, const AstNode*>) {
-      const AstNode* base_node = node;
-      // Note, we can't use `llvm::cast<T>` here because we might not have
-      // finished cloning `base_node` yet and its kind might not be set. This
-      // happens when there is a pointer cycle in the AST.
-      return static_cast<T*>(CloneBase(base_node));
-    } else if constexpr (std::is_convertible_v<const T*, const Value*>) {
-      const Value* base_value = node;
-      return static_cast<T*>(CloneBase(base_value));
-    } else {
-      static_assert(std::is_convertible_v<const T*, const Element*>,
-                    "unknown pointer type to clone");
-      const Element* base_elem = node;
-      return static_cast<T*>(CloneBase(base_elem));
-    }
-  }
-
-  // Clone anything with a clone constructor, that is, a constructor of the
-  // form:
-  //
-  //   explicit MyType(CloneContext&, const MyType&)
-  //
-  // Clone constructors should call Clone on their owned elements to form a new
-  // value. Pointers returned by Clone should not be inspected by the clone
-  // constructor, as the pointee is not necessarily fully initialized until the
-  // overall cloning process completes.
-  //
-  // Clone constructors should call Remap on values that they do not own, such
-  // as for the declaration named by an IdentifierExpression.
-  template <typename T>
-  auto Clone(const T& other)
-      -> std::enable_if_t<std::is_constructible_v<T, CloneContext&, const T&>,
-                          T> {
-    return T(*this, other);
-  }
-
-  template <typename T>
-  auto Clone(std::optional<T> node) -> std::optional<T> {
-    if (node) {
-      return Clone(*node);
-    }
-    return std::nullopt;
-  }
-
-  template <typename T>
-  auto Clone(const std::vector<T>& nodes) -> std::vector<T> {
-    std::vector<T> result;
-    result.reserve(nodes.size());
-    for (const auto& node : nodes) {
-      result.push_back(Clone(node));
-    }
-    return result;
-  }
-
-  // Find the new or existing node corresponding to the given node. This should
-  // be used when a cloned node has a non-owning reference to another node,
-  // that might refer to something being cloned or might refer to the original
-  // object. The returned node might not be fully constructed and should not be
-  // inspected.
-  template <typename T>
-  auto Remap(Nonnull<T*> node) -> Nonnull<T*> {
-    // Note, we can't use `llvm::cast<T>` here because we might not have
-    // finished cloning `base_node` yet and its kind might not be set. This
-    // happens when there is a pointer cycle in the AST.
-    T* cloned = static_cast<T*>(nodes_[node]);
-    return cloned ? cloned : node;
-  }
-
-  // It's safe to remap a `const` object by remapping the non-const version and
-  // adding back the `const`.
-  template <typename T>
-  auto Remap(Nonnull<const T*> node) -> Nonnull<const T*> {
-    return Remap(const_cast<T*>(node));
-  }
-
-  template <typename T>
-  auto Remap(std::optional<T> node) -> std::optional<T> {
-    if (node) {
-      return Remap(*node);
-    }
-    return std::nullopt;
-  }
-
-  template <typename T>
-  auto Remap(const std::vector<T>& nodes) -> std::vector<T> {
-    std::vector<T> result;
-    result.reserve(nodes.size());
-    for (const auto& node : nodes) {
-      result.push_back(Remap(node));
-    }
-    return result;
-  }
-
-  template <typename T>
-  auto GetExistingClone(Nonnull<const T*> node) -> Nonnull<T*> {
-    AstNode* cloned = nodes_.lookup(node);
-    CARBON_CHECK(cloned, "expected node to be cloned");
-    return llvm::cast<T>(cloned);
-  }
-
- private:
-  // A value transform that remaps or clones AST elements referred to by the
-  // value being transformed.
-  class CloneValueTransform;
-
-  // Clone the given node, and remember the mapping from the original to the
-  // new node for remapping.
-  auto CloneBase(Nonnull<const AstNode*> node) -> Nonnull<AstNode*>;
-
-  // Clone the given value, replacing references to cloned local declarations
-  // with references to the copies.
-  auto CloneBase(Nonnull<const Value*> value) -> Nonnull<Value*>;
-
-  // Clone the given element reference.
-  auto CloneBase(Nonnull<const Element*> elem) -> Nonnull<Element*>;
-
-  // Clone the given node if it's not already been cloned. This should be used
-  // very sparingly, in cases where ownership is unclear.
-  void MaybeCloneBase(Nonnull<const AstNode*> node);
-
-  // Arena to allocate new nodes within.
-  Nonnull<Arena*> arena_;
-
-  // Mapping from old nodes to new nodes.
-  llvm::DenseMap<const AstNode*, AstNode*> nodes_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_CLONE_CONTEXT_H_

+ 0 - 529
explorer/ast/declaration.cpp

@@ -1,529 +0,0 @@
-// 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 "explorer/ast/declaration.h"
-
-#include "explorer/ast/value.h"
-#include "explorer/base/print_as_id.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-
-Declaration::~Declaration() = default;
-
-void Declaration::Print(llvm::raw_ostream& out) const { PrintIndent(0, out); }
-void Declaration::PrintIndent(int indent_num_spaces,
-                              llvm::raw_ostream& out) const {
-  if (kind() != DeclarationKind::FunctionDeclaration &&
-      kind() != DeclarationKind::DestructorDeclaration) {
-    out.indent(indent_num_spaces);
-  }
-
-  switch (kind()) {
-    case DeclarationKind::NamespaceDeclaration:
-      out << PrintAsID(*this) << ";";
-      break;
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      const auto& iface_decl = cast<ConstraintTypeDeclaration>(*this);
-      out << PrintAsID(*this);
-      out << " {\n";
-      for (Nonnull<Declaration*> m : iface_decl.members()) {
-        out.indent(indent_num_spaces + 2) << *m << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      const auto& impl_decl = cast<ImplDeclaration>(*this);
-      out << PrintAsID(impl_decl) << " {\n";
-      for (Nonnull<Declaration*> m : impl_decl.members()) {
-        m->PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration: {
-      const auto& match_first_decl = cast<MatchFirstDeclaration>(*this);
-      out << PrintAsID(match_first_decl) << " {\n";
-      for (Nonnull<const ImplDeclaration*> m :
-           match_first_decl.impl_declarations()) {
-        m->PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case DeclarationKind::FunctionDeclaration:
-      cast<FunctionDeclaration>(*this).PrintIndent(indent_num_spaces, out);
-      break;
-    case DeclarationKind::DestructorDeclaration:
-      cast<DestructorDeclaration>(*this).PrintIndent(indent_num_spaces, out);
-      break;
-    case DeclarationKind::ClassDeclaration: {
-      const auto& class_decl = cast<ClassDeclaration>(*this);
-      out << PrintAsID(class_decl);
-      if (class_decl.type_params().has_value()) {
-        out << **class_decl.type_params();
-      }
-      out << " {\n";
-      for (Nonnull<Declaration*> m : class_decl.members()) {
-        m->PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      const auto& mixin_decl = cast<MixinDeclaration>(*this);
-      out << PrintAsID(mixin_decl) << "{\n";
-      for (Nonnull<Declaration*> m : mixin_decl.members()) {
-        m->PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case DeclarationKind::MixDeclaration: {
-      const auto& mix_decl = cast<MixDeclaration>(*this);
-      out << PrintAsID(mix_decl);
-      out << mix_decl.mixin() << ";";
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration: {
-      const auto& choice = cast<ChoiceDeclaration>(*this);
-      out << PrintAsID(choice) << " {\n";
-      for (Nonnull<const AlternativeSignature*> alt : choice.alternatives()) {
-        out.indent(indent_num_spaces + 2) << *alt << ";\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-
-    case DeclarationKind::VariableDeclaration: {
-      const auto& var = cast<VariableDeclaration>(*this);
-      out << PrintAsID(var);
-      if (var.has_initializer()) {
-        out << " = " << var.initializer();
-      }
-      out << ";";
-      break;
-    }
-
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      out << PrintAsID(*this) << ";";
-      break;
-    }
-
-    case DeclarationKind::SelfDeclaration: {
-      out << "Self";
-      break;
-    }
-
-    case DeclarationKind::AliasDeclaration: {
-      const auto& alias = cast<AliasDeclaration>(*this);
-      out << PrintAsID(alias) << " = " << alias.target() << ";";
-      break;
-    }
-
-    case DeclarationKind::ExtendBaseDeclaration: {
-      out << PrintAsID(*this) << ";";
-      break;
-    }
-  }
-}
-
-void Declaration::PrintID(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case DeclarationKind::NamespaceDeclaration:
-      out << "namespace " << cast<NamespaceDeclaration>(*this).name();
-      break;
-    case DeclarationKind::InterfaceDeclaration: {
-      const auto& iface_decl = cast<InterfaceDeclaration>(*this);
-      out << "interface " << iface_decl.name();
-      break;
-    }
-    case DeclarationKind::ConstraintDeclaration: {
-      const auto& constraint_decl = cast<ConstraintDeclaration>(*this);
-      out << "constraint " << constraint_decl.name();
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      const auto& impl_decl = cast<ImplDeclaration>(*this);
-      switch (impl_decl.kind()) {
-        case ImplKind::InternalImpl:
-          out << "extend ";
-          break;
-        case ImplKind::ExternalImpl:
-          break;
-      }
-      out << "impl ";
-      if (!impl_decl.deduced_parameters().empty()) {
-        out << "forall [";
-        llvm::ListSeparator sep;
-        for (const auto* param : impl_decl.deduced_parameters()) {
-          out << sep << *param;
-        }
-        out << "] ";
-      }
-      if (impl_decl.kind() != ImplKind::InternalImpl) {
-        out << *impl_decl.impl_type() << " ";
-      }
-      out << "as " << impl_decl.interface();
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration:
-      out << "match_first";
-      break;
-    case DeclarationKind::FunctionDeclaration:
-      out << "fn " << cast<FunctionDeclaration>(*this).name();
-      break;
-    case DeclarationKind::DestructorDeclaration:
-      out << *GetName(*this);
-      break;
-    case DeclarationKind::ClassDeclaration: {
-      const auto& class_decl = cast<ClassDeclaration>(*this);
-      out << "class " << class_decl.name();
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      const auto& mixin_decl = cast<MixinDeclaration>(*this);
-      out << "__mixin " << mixin_decl.name();
-      if (mixin_decl.self()->type().kind() != ExpressionKind::TypeTypeLiteral) {
-        out << " for " << mixin_decl.self()->type();
-      }
-      break;
-    }
-    case DeclarationKind::MixDeclaration: {
-      out << "__mix ";
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration: {
-      const auto& choice = cast<ChoiceDeclaration>(*this);
-      out << "choice " << choice.name();
-      break;
-    }
-
-    case DeclarationKind::VariableDeclaration: {
-      const auto& var = cast<VariableDeclaration>(*this);
-      out << "var " << var.binding();
-      break;
-    }
-
-    case DeclarationKind::InterfaceExtendDeclaration: {
-      const auto& extend = cast<InterfaceExtendDeclaration>(*this);
-      out << "extend " << *extend.base();
-      break;
-    }
-
-    case DeclarationKind::InterfaceRequireDeclaration: {
-      const auto& impl = cast<InterfaceRequireDeclaration>(*this);
-      out << "require " << *impl.impl_type() << " impls " << *impl.constraint();
-      break;
-    }
-
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      const auto& let = cast<AssociatedConstantDeclaration>(*this);
-      out << "let " << let.binding();
-      break;
-    }
-
-    case DeclarationKind::SelfDeclaration: {
-      out << "Self";
-      break;
-    }
-
-    case DeclarationKind::AliasDeclaration: {
-      const auto& alias = cast<AliasDeclaration>(*this);
-      out << "alias " << alias.name();
-      break;
-    }
-
-    case DeclarationKind::ExtendBaseDeclaration: {
-      const auto& extend = cast<ExtendBaseDeclaration>(*this);
-      out << "extend base: " << *extend.base_class();
-      break;
-    }
-  }
-}
-
-void DeclaredName::Print(llvm::raw_ostream& out) const {
-  for (const auto& [loc, name] : qualifiers()) {
-    out << name << ".";
-  }
-  out << inner_name();
-}
-
-auto GetName(const Declaration& declaration)
-    -> std::optional<std::string_view> {
-  switch (declaration.kind()) {
-    case DeclarationKind::NamespaceDeclaration:
-      return cast<NamespaceDeclaration>(declaration).name().inner_name();
-    case DeclarationKind::FunctionDeclaration:
-      return cast<FunctionDeclaration>(declaration).name().inner_name();
-    case DeclarationKind::DestructorDeclaration:
-      return "destructor";
-    case DeclarationKind::ClassDeclaration:
-      return cast<ClassDeclaration>(declaration).name().inner_name();
-    case DeclarationKind::MixinDeclaration: {
-      return cast<MixinDeclaration>(declaration).name().inner_name();
-    }
-    case DeclarationKind::MixDeclaration: {
-      return std::nullopt;
-    }
-    case DeclarationKind::ChoiceDeclaration:
-      return cast<ChoiceDeclaration>(declaration).name().inner_name();
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration:
-      return cast<ConstraintTypeDeclaration>(declaration).name().inner_name();
-    case DeclarationKind::VariableDeclaration:
-      return cast<VariableDeclaration>(declaration).binding().name();
-    case DeclarationKind::AssociatedConstantDeclaration:
-      return cast<AssociatedConstantDeclaration>(declaration).binding().name();
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::ImplDeclaration:
-    case DeclarationKind::MatchFirstDeclaration:
-      return std::nullopt;
-    case DeclarationKind::SelfDeclaration:
-      return SelfDeclaration::name();
-    case DeclarationKind::AliasDeclaration: {
-      return cast<AliasDeclaration>(declaration).name().inner_name();
-    }
-    case DeclarationKind::ExtendBaseDeclaration: {
-      return "extend base";
-    }
-  }
-}
-
-void ReturnTerm::Print(llvm::raw_ostream& out) const {
-  switch (kind_) {
-    case ReturnKind::Omitted:
-      return;
-    case ReturnKind::Auto:
-      out << "-> auto";
-      return;
-    case ReturnKind::Expression:
-      CARBON_CHECK(type_expression_.has_value());
-      out << "-> " << **type_expression_;
-      return;
-  }
-}
-
-namespace {
-
-// The deduced parameters of a function declaration.
-struct DeducedParameters {
-  // The `self` parameter, if any.
-  std::optional<Nonnull<Pattern*>> self_pattern;
-
-  // All other deduced parameters.
-  std::vector<Nonnull<GenericBinding*>> resolved_params;
-};
-
-// Split the `self` pattern (if any) out of `deduced_params`.
-auto SplitDeducedParameters(
-    SourceLocation source_loc,
-    const std::vector<Nonnull<AstNode*>>& deduced_params)
-    -> ErrorOr<DeducedParameters> {
-  DeducedParameters result;
-  for (Nonnull<AstNode*> param : deduced_params) {
-    switch (param->kind()) {
-      case AstNodeKind::GenericBinding:
-        result.resolved_params.push_back(&cast<GenericBinding>(*param));
-        break;
-      case AstNodeKind::BindingPattern: {
-        Nonnull<BindingPattern*> binding = &cast<BindingPattern>(*param);
-        if (binding->name() != "self") {
-          return ProgramError(source_loc)
-                 << "illegal binding pattern in implicit parameter list";
-        }
-        if (result.self_pattern.has_value()) {
-          return ProgramError(source_loc)
-                 << "parameter list cannot contain more than one `self` "
-                    "parameter";
-        }
-        result.self_pattern = binding;
-        break;
-      }
-      case AstNodeKind::AddrPattern: {
-        Nonnull<AddrPattern*> addr_pattern = &cast<AddrPattern>(*param);
-        Nonnull<BindingPattern*> binding =
-            &cast<BindingPattern>(addr_pattern->binding());
-        if (binding->name() != "self") {
-          return ProgramError(source_loc)
-                 << "illegal binding pattern in implicit parameter list";
-        }
-        if (result.self_pattern.has_value()) {
-          return ProgramError(source_loc)
-                 << "parameter list cannot contain more than one `self` "
-                    "parameter";
-        }
-        result.self_pattern = addr_pattern;
-        break;
-      }
-      default:
-        return ProgramError(source_loc)
-               << "illegal AST node in implicit parameter list";
-    }
-  }
-  return result;
-}
-}  // namespace
-
-auto DestructorDeclaration::CreateDestructor(
-    Nonnull<Arena*> arena, SourceLocation source_loc,
-    std::vector<Nonnull<AstNode*>> deduced_params,
-    Nonnull<TuplePattern*> param_pattern, ReturnTerm return_term,
-    std::optional<Nonnull<Block*>> body, VirtualOverride virt_override)
-    -> ErrorOr<Nonnull<DestructorDeclaration*>> {
-  DeducedParameters split_params;
-  CARBON_ASSIGN_OR_RETURN(split_params,
-                          SplitDeducedParameters(source_loc, deduced_params));
-  return arena->New<DestructorDeclaration>(
-      source_loc, std::move(split_params.resolved_params),
-      split_params.self_pattern, param_pattern, return_term, body,
-      virt_override);
-}
-
-auto FunctionDeclaration::Create(Nonnull<Arena*> arena,
-                                 SourceLocation source_loc, DeclaredName name,
-                                 std::vector<Nonnull<AstNode*>> deduced_params,
-                                 Nonnull<TuplePattern*> param_pattern,
-                                 ReturnTerm return_term,
-                                 std::optional<Nonnull<Block*>> body,
-                                 VirtualOverride virt_override)
-    -> ErrorOr<Nonnull<FunctionDeclaration*>> {
-  DeducedParameters split_params;
-  CARBON_ASSIGN_OR_RETURN(split_params,
-                          SplitDeducedParameters(source_loc, deduced_params));
-  return arena->New<FunctionDeclaration>(
-      source_loc, std::move(name), std::move(split_params.resolved_params),
-      split_params.self_pattern, param_pattern, return_term, body,
-      virt_override);
-}
-
-void CallableDeclaration::PrintIndent(int indent_num_spaces,
-                                      llvm::raw_ostream& out) const {
-  auto name = GetName(*this);
-  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *this);
-  out.indent(indent_num_spaces) << "fn " << *name << " ";
-  if (!deduced_parameters_.empty() || self_pattern_) {
-    out << "[";
-    llvm::ListSeparator sep;
-    for (Nonnull<const GenericBinding*> deduced : deduced_parameters_) {
-      out << sep << *deduced;
-    }
-    if (self_pattern_) {
-      out << sep << **self_pattern_;
-    }
-    out << "]";
-  }
-  out << *param_pattern_;
-  if (!return_term_.is_omitted()) {
-    out << " " << return_term_;
-  }
-  if (body_) {
-    out << "\n";
-    (*body_)->PrintIndent(indent_num_spaces, out);
-  } else {
-    out << ";";
-  }
-}
-
-ClassDeclaration::ClassDeclaration(CloneContext& context,
-                                   const ClassDeclaration& other)
-    : Declaration(context, other),
-      name_(other.name_),
-      extensibility_(other.extensibility_),
-      self_decl_(context.Clone(other.self_decl_)),
-      type_params_(context.Clone(other.type_params_)),
-      members_(context.Clone(other.members_)),
-      base_type_(context.Clone(other.base_type_)) {}
-
-ExtendBaseDeclaration::ExtendBaseDeclaration(CloneContext& context,
-                                             const ExtendBaseDeclaration& other)
-    : Declaration(context, other),
-      base_class_(context.Clone(other.base_class_)) {}
-
-ConstraintTypeDeclaration::ConstraintTypeDeclaration(
-    CloneContext& context, const ConstraintTypeDeclaration& other)
-    : Declaration(context, other),
-      name_(other.name_),
-      params_(context.Clone(other.params_)),
-      self_type_(context.Clone(other.self_type_)),
-      self_(context.Clone(other.self_)),
-      members_(context.Clone(other.members_)),
-      constraint_type_(context.Clone(other.constraint_type_)) {}
-
-auto ImplDeclaration::Create(Nonnull<Arena*> arena, SourceLocation source_loc,
-                             ImplKind kind, Nonnull<Expression*> impl_type,
-                             Nonnull<Expression*> interface,
-                             std::vector<Nonnull<AstNode*>> deduced_params,
-                             std::vector<Nonnull<Declaration*>> members)
-    -> ErrorOr<Nonnull<ImplDeclaration*>> {
-  std::vector<Nonnull<GenericBinding*>> resolved_params;
-  for (Nonnull<AstNode*> param : deduced_params) {
-    switch (param->kind()) {
-      case AstNodeKind::GenericBinding:
-        resolved_params.push_back(&cast<GenericBinding>(*param));
-        break;
-      default:
-        return ProgramError(source_loc)
-               << "illegal AST node in implicit parameter list of impl";
-    }
-  }
-  Nonnull<SelfDeclaration*> self_decl =
-      arena->New<SelfDeclaration>(impl_type->source_loc());
-  return arena->New<ImplDeclaration>(source_loc, kind, impl_type, self_decl,
-                                     interface, resolved_params, members);
-}
-
-ImplDeclaration::ImplDeclaration(CloneContext& context,
-                                 const ImplDeclaration& other)
-    : Declaration(context, other),
-      kind_(other.kind_),
-      deduced_parameters_(context.Clone(other.deduced_parameters_)),
-      impl_type_(context.Clone(other.impl_type_)),
-      self_decl_(context.Clone(other.self_decl_)),
-      interface_(context.Clone(other.interface_)),
-      constraint_type_(context.Clone(other.constraint_type_)),
-      members_(context.Clone(other.members_)),
-      impl_bindings_(context.Remap(other.impl_bindings_)),
-      match_first_(context.Remap(other.match_first_)) {}
-
-void AlternativeSignature::Print(llvm::raw_ostream& out) const {
-  out << "alt " << name();
-  if (auto params = parameters()) {
-    out << **params;
-  }
-}
-
-void AlternativeSignature::PrintID(llvm::raw_ostream& out) const {
-  out << name();
-}
-
-auto ChoiceDeclaration::FindAlternative(std::string_view name) const
-    -> std::optional<const AlternativeSignature*> {
-  for (const auto* alt : alternatives()) {
-    if (alt->name() == name) {
-      return alt;
-    }
-  }
-  return std::nullopt;
-}
-
-MixDeclaration::MixDeclaration(CloneContext& context,
-                               const MixDeclaration& other)
-    : Declaration(context, other),
-      mixin_(context.Clone(other.mixin_)),
-      mixin_value_(context.Clone(other.mixin_value_)) {}
-
-}  // namespace Carbon

+ 0 - 1058
explorer/ast/declaration.h

@@ -1,1058 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_DECLARATION_H_
-#define CARBON_EXPLORER_AST_DECLARATION_H_
-
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/impl_binding.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/return_term.h"
-#include "explorer/ast/statement.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class MixinPseudoType;
-class ConstraintType;
-class NominalClassType;
-class MatchFirstDeclaration;
-
-// Abstract base class of all AST nodes representing declarations.
-//
-// Declaration and its derived classes support LLVM-style RTTI, including
-// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
-// class derived from Declaration must provide a `classof` operation, and
-// every concrete derived class must have a corresponding enumerator
-// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
-// details.
-class Declaration : public AstNode {
- public:
-  ~Declaration() override = 0;
-
-  Declaration(const Declaration&) = delete;
-  auto operator=(const Declaration&) -> Declaration& = delete;
-
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  virtual void PrintIndent(int indent_num_spaces, llvm::raw_ostream& out) const;
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromDeclaration(node->kind());
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> DeclarationKind {
-    return static_cast<DeclarationKind>(root_kind());
-  }
-
-  // The static type of the declared entity. Cannot be called before
-  // typechecking.
-  auto static_type() const -> const Value& { return **static_type_; }
-
-  // Sets the static type of the declared entity. Can only be called once,
-  // during typechecking.
-  void set_static_type(Nonnull<const Value*> type) {
-    CARBON_CHECK(!static_type_.has_value());
-    static_type_ = type;
-  }
-
-  // Returns whether the static type has been set. Should only be called
-  // during typechecking: before typechecking it's guaranteed to be false,
-  // and after typechecking it's guaranteed to be true.
-  auto has_static_type() const -> bool { return static_type_.has_value(); }
-
-  // Sets the value returned by constant_value(). Can only be called once,
-  // during typechecking.
-  void set_constant_value(Nonnull<const Value*> value) {
-    CARBON_CHECK(!constant_value_.has_value());
-    constant_value_ = value;
-  }
-
-  // See value_node.h for API.
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_;
-  }
-
-  // See value_node.h for API.
-  auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_;
-  }
-
-  // Returns whether this node has been declared.
-  auto is_declared() const -> bool { return is_declared_; }
-
-  // Set that this node is declared. Should only be called once, by the
-  // type-checker, once the node is ready to be named and used.
-  void set_is_declared() {
-    CARBON_CHECK(!is_declared_, "should not be declared twice");
-    is_declared_ = true;
-  }
-
-  // Returns whether this node has been fully type-checked.
-  auto is_type_checked() const -> bool { return is_type_checked_; }
-
-  // Set that this node is type-checked. Should only be called once, by the
-  // type-checker, once full type-checking is complete.
-  void set_is_type_checked() {
-    CARBON_CHECK(!is_type_checked_, "should not be type-checked twice");
-    is_type_checked_ = true;
-  }
-
- protected:
-  // Constructs a Declaration representing syntax at the given line number.
-  // `kind` must be the enumerator corresponding to the most-derived type being
-  // constructed.
-  explicit Declaration(AstNodeKind kind, SourceLocation source_loc)
-      : AstNode(kind, source_loc) {}
-
-  explicit Declaration(CloneContext& context, const Declaration& other)
-      : AstNode(context, other),
-        static_type_(context.Clone(other.static_type_)),
-        constant_value_(context.Clone(other.constant_value_)),
-        is_declared_(other.is_declared_),
-        is_type_checked_(other.is_type_checked_) {}
-
- private:
-  std::optional<Nonnull<const Value*>> static_type_;
-  std::optional<Nonnull<const Value*>> constant_value_;
-  bool is_declared_ = false;
-  bool is_type_checked_ = false;
-};
-
-// Determine whether two declarations declare the same entity.
-inline auto DeclaresSameEntity(const Declaration& first,
-                               const Declaration& second) -> bool {
-  return &first == &second;
-}
-
-// A name being declared in a named declaration.
-class DeclaredName : public Printable<DeclaredName> {
- public:
-  struct NameComponent {
-    SourceLocation source_loc;
-    std::string name;
-  };
-
-  explicit DeclaredName(SourceLocation loc, std::string name)
-      : components_{{loc, std::move(name)}} {}
-
-  void Print(llvm::raw_ostream& out) const;
-
-  void Append(SourceLocation loc, std::string name) {
-    components_.push_back({loc, std::move(name)});
-  }
-
-  // Returns the location of the first name component.
-  auto source_loc() const -> SourceLocation {
-    return components_.front().source_loc;
-  }
-
-  // Returns whether this is a qualified name, as opposed to a simple
-  // single-identifier name.
-  auto is_qualified() const { return components_.size() > 1; }
-
-  // Returns a range containing the components of the name other than the final
-  // component.
-  auto qualifiers() const -> llvm::ArrayRef<NameComponent> {
-    return llvm::ArrayRef(components_).drop_back();
-  }
-
-  // Returns the innermost name, which is the unqualified name of the entity
-  // being declared. For example in `fn Namespace.Func();`, returns `"Func"`.
-  auto inner_name() const -> std::string_view {
-    return components_.back().name;
-  }
-
- private:
-  std::vector<NameComponent> components_;
-};
-
-// A declaration of a namespace.
-class NamespaceDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  explicit NamespaceDeclaration(SourceLocation source_loc, DeclaredName name)
-      : Declaration(AstNodeKind::NamespaceDeclaration, source_loc),
-        name_(std::move(name)) {}
-
-  explicit NamespaceDeclaration(CloneContext& context,
-                                const NamespaceDeclaration& other)
-      : Declaration(context, other), name_(other.name_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromNamespaceDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
- private:
-  DeclaredName name_;
-};
-
-// A function's virtual override keyword.
-enum class VirtualOverride { None, Abstract, Virtual, Impl };
-
-class CallableDeclaration : public Declaration {
- public:
-  CallableDeclaration(AstNodeKind kind, SourceLocation loc,
-                      std::vector<Nonnull<GenericBinding*>> deduced_params,
-                      std::optional<Nonnull<Pattern*>> self_pattern,
-                      Nonnull<TuplePattern*> param_pattern,
-                      ReturnTerm return_term,
-                      std::optional<Nonnull<Block*>> body,
-                      VirtualOverride virt_override)
-      : Declaration(kind, loc),
-        deduced_parameters_(std::move(deduced_params)),
-        self_pattern_(self_pattern),
-        param_pattern_(param_pattern),
-        return_term_(return_term),
-        body_(body),
-        virt_override_(virt_override) {}
-
-  explicit CallableDeclaration(CloneContext& context,
-                               const CallableDeclaration& other)
-      : Declaration(context, other),
-        deduced_parameters_(context.Clone(other.deduced_parameters_)),
-        self_pattern_(context.Clone(other.self_pattern_)),
-        param_pattern_(context.Clone(other.param_pattern_)),
-        return_term_(context.Clone(other.return_term_)),
-        body_(context.Clone(other.body_)),
-        virt_override_(other.virt_override_) {}
-
-  void PrintIndent(int indent_num_spaces,
-                   llvm::raw_ostream& out) const override;
-  auto deduced_parameters() const
-      -> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
-    return deduced_parameters_;
-  }
-  auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
-    return deduced_parameters_;
-  }
-  auto self_pattern() const -> const Pattern& { return **self_pattern_; }
-  auto self_pattern() -> Pattern& { return **self_pattern_; }
-  auto param_pattern() const -> const TuplePattern& { return *param_pattern_; }
-  auto param_pattern() -> TuplePattern& { return *param_pattern_; }
-  auto return_term() const -> const ReturnTerm& { return return_term_; }
-  auto return_term() -> ReturnTerm& { return return_term_; }
-  auto body() const -> std::optional<Nonnull<const Block*>> { return body_; }
-  auto body() -> std::optional<Nonnull<Block*>> { return body_; }
-  auto virt_override() const -> VirtualOverride { return virt_override_; }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  auto is_method() const -> bool { return self_pattern_.has_value(); }
-
- private:
-  std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
-  std::optional<Nonnull<Pattern*>> self_pattern_;
-  Nonnull<TuplePattern*> param_pattern_;
-  ReturnTerm return_term_;
-  std::optional<Nonnull<Block*>> body_;
-  VirtualOverride virt_override_;
-};
-
-class FunctionDeclaration : public CallableDeclaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
-                     DeclaredName name,
-                     std::vector<Nonnull<AstNode*>> deduced_params,
-                     Nonnull<TuplePattern*> param_pattern,
-                     ReturnTerm return_term,
-                     std::optional<Nonnull<Block*>> body,
-                     VirtualOverride virt_override)
-      -> ErrorOr<Nonnull<FunctionDeclaration*>>;
-
-  // Use `Create()` instead. This is public only so Arena::New() can call it.
-  FunctionDeclaration(SourceLocation source_loc, DeclaredName name,
-                      std::vector<Nonnull<GenericBinding*>> deduced_params,
-                      std::optional<Nonnull<Pattern*>> self_pattern,
-                      Nonnull<TuplePattern*> param_pattern,
-                      ReturnTerm return_term,
-                      std::optional<Nonnull<Block*>> body,
-                      VirtualOverride virt_override)
-      : CallableDeclaration(AstNodeKind::FunctionDeclaration, source_loc,
-                            std::move(deduced_params), self_pattern,
-                            param_pattern, return_term, body, virt_override),
-        name_(std::move(name)) {}
-
-  explicit FunctionDeclaration(CloneContext& context,
-                               const FunctionDeclaration& other)
-      : CallableDeclaration(context, other), name_(other.name_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromFunctionDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-
- private:
-  DeclaredName name_;
-};
-
-class DestructorDeclaration : public CallableDeclaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  static auto CreateDestructor(Nonnull<Arena*> arena, SourceLocation source_loc,
-                               std::vector<Nonnull<AstNode*>> deduced_params,
-                               Nonnull<TuplePattern*> param_pattern,
-                               ReturnTerm return_term,
-                               std::optional<Nonnull<Block*>> body,
-                               VirtualOverride virt_override)
-      -> ErrorOr<Nonnull<DestructorDeclaration*>>;
-
-  // Use `Create()` instead. This is public only so Arena::New() can call it.
-  DestructorDeclaration(SourceLocation source_loc,
-                        std::vector<Nonnull<GenericBinding*>> deduced_params,
-                        std::optional<Nonnull<Pattern*>> self_pattern,
-                        Nonnull<TuplePattern*> param_pattern,
-                        ReturnTerm return_term,
-                        std::optional<Nonnull<Block*>> body,
-                        VirtualOverride virt_override)
-      : CallableDeclaration(AstNodeKind::DestructorDeclaration, source_loc,
-                            std::move(deduced_params), self_pattern,
-                            param_pattern, return_term, body, virt_override) {}
-
-  explicit DestructorDeclaration(CloneContext& context,
-                                 const DestructorDeclaration& other)
-      : CallableDeclaration(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromDestructorDeclaration(node->kind());
-  }
-};
-
-class SelfDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  explicit SelfDeclaration(SourceLocation source_loc)
-      : Declaration(AstNodeKind::SelfDeclaration, source_loc) {}
-
-  explicit SelfDeclaration(CloneContext& context, const SelfDeclaration& other)
-      : Declaration(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromSelfDeclaration(node->kind());
-  }
-
-  static auto name() -> std::string_view { return "Self"; }
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-};
-
-enum class ClassExtensibility { None, Base, Abstract };
-
-class ClassDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  ClassDeclaration(SourceLocation source_loc, DeclaredName name,
-                   Nonnull<SelfDeclaration*> self_decl,
-                   ClassExtensibility extensibility,
-                   std::optional<Nonnull<TuplePattern*>> type_params,
-                   std::vector<Nonnull<Declaration*>> members)
-      : Declaration(AstNodeKind::ClassDeclaration, source_loc),
-        name_(std::move(name)),
-        extensibility_(extensibility),
-        self_decl_(self_decl),
-        type_params_(type_params),
-        members_(std::move(members)) {}
-
-  explicit ClassDeclaration(CloneContext& context,
-                            const ClassDeclaration& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromClassDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-  auto extensibility() const -> ClassExtensibility { return extensibility_; }
-  auto type_params() const -> std::optional<Nonnull<const TuplePattern*>> {
-    return type_params_;
-  }
-  auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
-    return type_params_;
-  }
-  auto self() const -> Nonnull<const SelfDeclaration*> { return self_decl_; }
-  auto self() -> Nonnull<SelfDeclaration*> { return self_decl_; }
-
-  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
-    return members_;
-  }
-  auto destructor() const
-      -> std::optional<Nonnull<const DestructorDeclaration*>> {
-    for (const auto& x : members_) {
-      if (x->kind() == DeclarationKind::DestructorDeclaration) {
-        return llvm::cast<DestructorDeclaration>(x);
-      }
-    }
-    return std::nullopt;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  // Returns the original base type, before instantiation & substitutions
-  // Use `NominalClassType::base()` to get the instantiated type.
-  auto base_type() const -> std::optional<Nonnull<const NominalClassType*>> {
-    return base_type_;
-  }
-  void set_base_type(
-      std::optional<Nonnull<const NominalClassType*>> base_type) {
-    base_type_ = base_type;
-  }
-
- private:
-  DeclaredName name_;
-  ClassExtensibility extensibility_;
-  Nonnull<SelfDeclaration*> self_decl_;
-  std::optional<Nonnull<TuplePattern*>> type_params_;
-  std::vector<Nonnull<Declaration*>> members_;
-  std::optional<Nonnull<const NominalClassType*>> base_type_;
-};
-
-// EXPERIMENTAL MIXIN FEATURE
-class MixinDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  MixinDeclaration(SourceLocation source_loc, DeclaredName name,
-                   std::optional<Nonnull<TuplePattern*>> params,
-                   Nonnull<GenericBinding*> self,
-                   std::vector<Nonnull<Declaration*>> members)
-      : Declaration(AstNodeKind::MixinDeclaration, source_loc),
-        name_(std::move(name)),
-        params_(params),
-        self_(self),
-        members_(std::move(members)) {}
-
-  explicit MixinDeclaration(CloneContext& context,
-                            const MixinDeclaration& other)
-      : Declaration(context, other),
-        name_(other.name_),
-        params_(context.Clone(other.params_)),
-        self_(context.Clone(other.self_)),
-        members_(context.Clone(other.members_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMixinDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-  auto params() const -> std::optional<Nonnull<const TuplePattern*>> {
-    return params_;
-  }
-  auto params() -> std::optional<Nonnull<TuplePattern*>> { return params_; }
-  auto self() const -> Nonnull<const GenericBinding*> { return self_; }
-  auto self() -> Nonnull<GenericBinding*> { return self_; }
-  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
-    return members_;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
- private:
-  DeclaredName name_;
-  std::optional<Nonnull<TuplePattern*>> params_;
-  Nonnull<GenericBinding*> self_;
-  std::vector<Nonnull<Declaration*>> members_;
-};
-
-// EXPERIMENTAL MIXIN FEATURE
-class MixDeclaration : public Declaration {
- public:
-  MixDeclaration(SourceLocation source_loc,
-                 std::optional<Nonnull<Expression*>> mixin_type)
-      : Declaration(AstNodeKind::MixDeclaration, source_loc),
-        mixin_(mixin_type) {}
-
-  explicit MixDeclaration(CloneContext& context, const MixDeclaration& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMixDeclaration(node->kind());
-  }
-
-  auto mixin() const -> const Expression& { return **mixin_; }
-  auto mixin() -> Expression& { return **mixin_; }
-
-  auto mixin_value() const -> const MixinPseudoType& { return **mixin_value_; }
-  void set_mixin_value(Nonnull<const MixinPseudoType*> mixin_value) {
-    mixin_value_ = mixin_value;
-  }
-
- private:
-  std::optional<Nonnull<Expression*>> mixin_;
-  std::optional<Nonnull<const MixinPseudoType*>> mixin_value_;
-};
-
-class ExtendBaseDeclaration : public Declaration {
- public:
-  ExtendBaseDeclaration(SourceLocation source_loc,
-                        Nonnull<Expression*> base_class)
-      : Declaration(AstNodeKind::ExtendBaseDeclaration, source_loc),
-        base_class_(base_class) {}
-
-  explicit ExtendBaseDeclaration(CloneContext& context,
-                                 const ExtendBaseDeclaration& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromExtendBaseDeclaration(node->kind());
-  }
-
-  auto base_class() const -> Nonnull<const Expression*> { return base_class_; }
-  auto base_class() -> Nonnull<Expression*> { return base_class_; }
-
- private:
-  Nonnull<Expression*> base_class_;
-};
-
-class AlternativeSignature : public AstNode {
- public:
-  AlternativeSignature(SourceLocation source_loc, std::string name,
-                       std::optional<Nonnull<TupleLiteral*>> parameters)
-      : AstNode(AstNodeKind::AlternativeSignature, source_loc),
-        name_(std::move(name)),
-        parameters_(parameters) {}
-
-  explicit AlternativeSignature(CloneContext& context,
-                                const AlternativeSignature& other)
-      : AstNode(context, other),
-        name_(other.name_),
-        parameters_(context.Clone(other.parameters_)),
-        parameters_static_type_(context.Clone(other.parameters_static_type_)) {}
-
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAlternativeSignature(node->kind());
-  }
-
-  auto name() const -> const std::string& { return name_; }
-  auto parameters() const -> std::optional<Nonnull<const TupleLiteral*>> {
-    return parameters_;
-  }
-  auto parameters() -> std::optional<Nonnull<TupleLiteral*>> {
-    return parameters_;
-  }
-
-  // The static type described by the parameters expression, if any. Cannot be
-  // called before type checking. This will be nullopt after type checking if
-  // this alternative does not have a parameter list.
-  auto parameters_static_type() const -> std::optional<Nonnull<const Value*>> {
-    return parameters_static_type_;
-  }
-
-  // Sets the static type of the declared entity. Can only be called once,
-  // during typechecking.
-  void set_parameters_static_type(Nonnull<const Value*> type) {
-    CARBON_CHECK(!parameters_static_type_.has_value());
-    parameters_static_type_ = type;
-  }
-
- private:
-  std::string name_;
-  std::optional<Nonnull<TupleLiteral*>> parameters_;
-  std::optional<Nonnull<const Value*>> parameters_static_type_;
-};
-
-class ChoiceDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  ChoiceDeclaration(SourceLocation source_loc, DeclaredName name,
-                    std::optional<Nonnull<TuplePattern*>> type_params,
-                    std::vector<Nonnull<AlternativeSignature*>> alternatives)
-      : Declaration(AstNodeKind::ChoiceDeclaration, source_loc),
-        name_(std::move(name)),
-        type_params_(type_params),
-        alternatives_(std::move(alternatives)) {}
-
-  explicit ChoiceDeclaration(CloneContext& context,
-                             const ChoiceDeclaration& other)
-      : Declaration(context, other),
-        name_(other.name_),
-        type_params_(context.Clone(other.type_params_)),
-        alternatives_(context.Clone(other.alternatives_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromChoiceDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-
-  auto type_params() const -> std::optional<Nonnull<const TuplePattern*>> {
-    return type_params_;
-  }
-  auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
-    return type_params_;
-  }
-
-  auto alternatives() const
-      -> llvm::ArrayRef<Nonnull<const AlternativeSignature*>> {
-    return alternatives_;
-  }
-  auto alternatives() -> llvm::ArrayRef<Nonnull<AlternativeSignature*>> {
-    return alternatives_;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  auto FindAlternative(std::string_view name) const
-      -> std::optional<const AlternativeSignature*>;
-
- private:
-  DeclaredName name_;
-  std::optional<Nonnull<TuplePattern*>> type_params_;
-  std::vector<Nonnull<AlternativeSignature*>> alternatives_;
-};
-
-// Global variable definition implements the Declaration concept.
-class VariableDeclaration : public Declaration {
- public:
-  VariableDeclaration(SourceLocation source_loc,
-                      Nonnull<BindingPattern*> binding,
-                      std::optional<Nonnull<Expression*>> initializer,
-                      ExpressionCategory expression_category)
-      : Declaration(AstNodeKind::VariableDeclaration, source_loc),
-        binding_(binding),
-        initializer_(initializer),
-        expression_category_(expression_category) {}
-
-  explicit VariableDeclaration(CloneContext& context,
-                               const VariableDeclaration& other)
-      : Declaration(context, other),
-        binding_(context.Clone(other.binding_)),
-        initializer_(context.Clone(other.initializer_)),
-        expression_category_(other.expression_category_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromVariableDeclaration(node->kind());
-  }
-
-  auto binding() const -> const BindingPattern& { return *binding_; }
-  auto binding() -> BindingPattern& { return *binding_; }
-  auto initializer() const -> const Expression& { return **initializer_; }
-  auto initializer() -> Expression& { return **initializer_; }
-  auto expression_category() const -> ExpressionCategory {
-    return expression_category_;
-  }
-
-  auto has_initializer() const -> bool { return initializer_.has_value(); }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_initializer(Nonnull<Expression*> initializer) {
-    CARBON_CHECK(has_initializer(), "should not add a new initializer");
-    initializer_ = initializer;
-  }
-
- private:
-  Nonnull<BindingPattern*> binding_;
-  std::optional<Nonnull<Expression*>> initializer_;
-  ExpressionCategory expression_category_;
-};
-
-// Base class for constraint and interface declarations. Interfaces and named
-// constraints behave the same in most respects, but only interfaces can
-// introduce new associated functions and constants.
-class ConstraintTypeDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  ConstraintTypeDeclaration(AstNodeKind kind, Nonnull<Arena*> arena,
-                            SourceLocation source_loc, DeclaredName name,
-                            std::optional<Nonnull<TuplePattern*>> params,
-                            std::vector<Nonnull<Declaration*>> members)
-      : Declaration(kind, source_loc),
-        name_(std::move(name)),
-        params_(params),
-        self_type_(arena->New<SelfDeclaration>(source_loc)),
-        members_(std::move(members)) {
-    // `interface X` has `Self:! X`.
-    auto* self_type_ref = arena->New<IdentifierExpression>(
-        source_loc, std::string(name_.inner_name()));
-    self_type_ref->set_value_node(self_type_);
-    self_ = arena->New<GenericBinding>(source_loc, "Self", self_type_ref,
-                                       GenericBinding::BindingKind::Checked);
-  }
-
-  explicit ConstraintTypeDeclaration(CloneContext& context,
-                                     const ConstraintTypeDeclaration& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromConstraintTypeDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-  auto params() const -> std::optional<Nonnull<const TuplePattern*>> {
-    return params_;
-  }
-  auto params() -> std::optional<Nonnull<TuplePattern*>> { return params_; }
-  // Get the type of `Self`, which is a reference to the interface itself, with
-  // parameters mapped to their values. For example, in `interface X(T:!
-  // type)`, the self type is `X(T)`.
-  auto self_type() const -> Nonnull<const SelfDeclaration*> {
-    return self_type_;
-  }
-  auto self_type() -> Nonnull<SelfDeclaration*> { return self_type_; }
-  auto self() const -> Nonnull<const GenericBinding*> { return self_; }
-  auto self() -> Nonnull<GenericBinding*> { return self_; }
-  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
-    return members_;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  // Get the constraint type corresponding to this interface, or nullopt if
-  // this interface is incomplete.
-  auto constraint_type() const
-      -> std::optional<Nonnull<const ConstraintType*>> {
-    return constraint_type_;
-  }
-
-  // Set the constraint type corresponding to this interface. Can only be set
-  // once, by type-checking.
-  void set_constraint_type(Nonnull<const ConstraintType*> constraint_type) {
-    CARBON_CHECK(!constraint_type_);
-    constraint_type_ = constraint_type;
-  }
-
- private:
-  DeclaredName name_;
-  std::optional<Nonnull<TuplePattern*>> params_;
-  Nonnull<SelfDeclaration*> self_type_;
-  Nonnull<GenericBinding*> self_;
-  std::vector<Nonnull<Declaration*>> members_;
-  std::optional<Nonnull<const ConstraintType*>> constraint_type_;
-};
-
-// A `interface` declaration.
-class InterfaceDeclaration : public ConstraintTypeDeclaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  InterfaceDeclaration(Nonnull<Arena*> arena, SourceLocation source_loc,
-                       DeclaredName name,
-                       std::optional<Nonnull<TuplePattern*>> params,
-                       std::vector<Nonnull<Declaration*>> members)
-      : ConstraintTypeDeclaration(AstNodeKind::InterfaceDeclaration, arena,
-                                  source_loc, std::move(name), params,
-                                  std::move(members)) {}
-
-  explicit InterfaceDeclaration(CloneContext& context,
-                                const InterfaceDeclaration& other)
-      : ConstraintTypeDeclaration(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromInterfaceDeclaration(node->kind());
-  }
-};
-
-// A `constraint` declaration, such as `constraint X { impl as Y; }`.
-class ConstraintDeclaration : public ConstraintTypeDeclaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  ConstraintDeclaration(Nonnull<Arena*> arena, SourceLocation source_loc,
-                        DeclaredName name,
-                        std::optional<Nonnull<TuplePattern*>> params,
-                        std::vector<Nonnull<Declaration*>> members)
-      : ConstraintTypeDeclaration(AstNodeKind::ConstraintDeclaration, arena,
-                                  source_loc, std::move(name), params,
-                                  std::move(members)) {}
-
-  explicit ConstraintDeclaration(CloneContext& context,
-                                 const ConstraintDeclaration& other)
-      : ConstraintTypeDeclaration(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromConstraintDeclaration(node->kind());
-  }
-};
-
-// An `extends` declaration in an interface.
-class InterfaceExtendDeclaration : public Declaration {
- public:
-  InterfaceExtendDeclaration(SourceLocation source_loc,
-                             Nonnull<Expression*> base)
-      : Declaration(AstNodeKind::InterfaceExtendDeclaration, source_loc),
-        base_(base) {}
-
-  explicit InterfaceExtendDeclaration(CloneContext& context,
-                                      const InterfaceExtendDeclaration& other)
-      : Declaration(context, other), base_(context.Clone(other.base_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromInterfaceExtendDeclaration(node->kind());
-  }
-
-  auto base() const -> const Expression* { return base_; }
-  auto base() -> Expression* { return base_; }
-
- private:
-  Nonnull<Expression*> base_;
-};
-
-// A `require ... impls` declaration in an interface.
-class InterfaceRequireDeclaration : public Declaration {
- public:
-  InterfaceRequireDeclaration(SourceLocation source_loc,
-                              Nonnull<Expression*> impl_type,
-                              Nonnull<Expression*> constraint)
-      : Declaration(AstNodeKind::InterfaceRequireDeclaration, source_loc),
-        impl_type_(impl_type),
-        constraint_(constraint) {}
-
-  explicit InterfaceRequireDeclaration(CloneContext& context,
-                                       const InterfaceRequireDeclaration& other)
-      : Declaration(context, other),
-        impl_type_(context.Clone(other.impl_type_)),
-        constraint_(context.Clone(other.constraint_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromInterfaceRequireDeclaration(node->kind());
-  }
-
-  auto impl_type() const -> const Expression* { return impl_type_; }
-  auto impl_type() -> Expression* { return impl_type_; }
-
-  auto constraint() const -> const Expression* { return constraint_; }
-  auto constraint() -> Expression* { return constraint_; }
-
- private:
-  Nonnull<Expression*> impl_type_;
-  Nonnull<Expression*> constraint_;
-};
-
-class AssociatedConstantDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  AssociatedConstantDeclaration(SourceLocation source_loc,
-                                Nonnull<GenericBinding*> binding)
-      : Declaration(AstNodeKind::AssociatedConstantDeclaration, source_loc),
-        binding_(binding) {}
-
-  explicit AssociatedConstantDeclaration(
-      CloneContext& context, const AssociatedConstantDeclaration& other)
-      : Declaration(context, other), binding_(context.Clone(other.binding_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAssociatedConstantDeclaration(node->kind());
-  }
-
-  auto binding() const -> const GenericBinding& { return *binding_; }
-  auto binding() -> GenericBinding& { return *binding_; }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
- private:
-  Nonnull<GenericBinding*> binding_;
-};
-
-enum class ImplKind { InternalImpl, ExternalImpl };
-
-class ImplDeclaration : public Declaration {
- public:
-  static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
-                     ImplKind kind, Nonnull<Expression*> impl_type,
-                     Nonnull<Expression*> interface,
-                     std::vector<Nonnull<AstNode*>> deduced_params,
-                     std::vector<Nonnull<Declaration*>> members)
-      -> ErrorOr<Nonnull<ImplDeclaration*>>;
-
-  // Use `Create` instead.
-  ImplDeclaration(SourceLocation source_loc, ImplKind kind,
-                  Nonnull<Expression*> impl_type,
-                  Nonnull<SelfDeclaration*> self_decl,
-                  Nonnull<Expression*> interface,
-                  std::vector<Nonnull<GenericBinding*>> deduced_params,
-                  std::vector<Nonnull<Declaration*>> members)
-      : Declaration(AstNodeKind::ImplDeclaration, source_loc),
-        kind_(kind),
-        deduced_parameters_(std::move(deduced_params)),
-        impl_type_(impl_type),
-        self_decl_(self_decl),
-        interface_(interface),
-        members_(std::move(members)) {}
-
-  explicit ImplDeclaration(CloneContext& context, const ImplDeclaration& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromImplDeclaration(node->kind());
-  }
-  // Return whether this is an external or internal impl.
-  auto kind() const -> ImplKind { return kind_; }
-  // Return the type that is doing the implementing.
-  auto impl_type() const -> Nonnull<Expression*> { return impl_type_; }
-  // Return the interface that is being implemented.
-  auto interface() const -> const Expression& { return *interface_; }
-  auto interface() -> Expression& { return *interface_; }
-  void set_constraint_type(Nonnull<const ConstraintType*> constraint_type) {
-    constraint_type_ = constraint_type;
-  }
-  auto constraint_type() const -> Nonnull<const ConstraintType*> {
-    return *constraint_type_;
-  }
-  // Returns the deduced parameters specified on the impl declaration. This
-  // does not include any generic parameters from enclosing scopes.
-  auto deduced_parameters() const
-      -> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
-    return deduced_parameters_;
-  }
-  auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
-    return deduced_parameters_;
-  }
-  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
-    return members_;
-  }
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-  void set_impl_bindings(llvm::ArrayRef<Nonnull<const ImplBinding*>> imps) {
-    impl_bindings_ = imps;
-  }
-  auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
-    return impl_bindings_;
-  }
-  auto self() const -> Nonnull<const SelfDeclaration*> { return self_decl_; }
-  auto self() -> Nonnull<SelfDeclaration*> { return self_decl_; }
-
-  // Set the enclosing match_first declaration. Should only be called once,
-  // during type-checking.
-  void set_match_first(Nonnull<const MatchFirstDeclaration*> match_first) {
-    match_first_ = match_first;
-  }
-  // Get the enclosing match_first declaration, if any exists.
-  auto match_first() const
-      -> std::optional<Nonnull<const MatchFirstDeclaration*>> {
-    return match_first_;
-  }
-
- private:
-  ImplKind kind_;
-  std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
-  Nonnull<Expression*> impl_type_;
-  Nonnull<SelfDeclaration*> self_decl_;
-  Nonnull<Expression*> interface_;
-  std::optional<Nonnull<const ConstraintType*>> constraint_type_;
-  std::vector<Nonnull<Declaration*>> members_;
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
-  std::optional<Nonnull<const MatchFirstDeclaration*>> match_first_;
-};
-
-class MatchFirstDeclaration : public Declaration {
- public:
-  MatchFirstDeclaration(
-      SourceLocation source_loc,
-      std::vector<Nonnull<ImplDeclaration*>> impl_declarations)
-      : Declaration(AstNodeKind::MatchFirstDeclaration, source_loc),
-        impl_declarations_(std::move(impl_declarations)) {}
-
-  explicit MatchFirstDeclaration(CloneContext& context,
-                                 const MatchFirstDeclaration& other)
-      : Declaration(context, other),
-        impl_declarations_(context.Clone(other.impl_declarations_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMatchFirstDeclaration(node->kind());
-  }
-
-  auto impl_declarations() const
-      -> llvm::ArrayRef<Nonnull<const ImplDeclaration*>> {
-    return impl_declarations_;
-  }
-  auto impl_declarations() -> llvm::ArrayRef<Nonnull<ImplDeclaration*>> {
-    return impl_declarations_;
-  }
-
- private:
-  std::vector<Nonnull<ImplDeclaration*>> impl_declarations_;
-};
-
-class AliasDeclaration : public Declaration {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  explicit AliasDeclaration(SourceLocation source_loc, DeclaredName name,
-                            Nonnull<Expression*> target)
-      : Declaration(AstNodeKind::AliasDeclaration, source_loc),
-        name_(std::move(name)),
-        target_(target) {}
-
-  explicit AliasDeclaration(CloneContext& context,
-                            const AliasDeclaration& other)
-      : Declaration(context, other),
-        name_(other.name_),
-        target_(context.Clone(other.target_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAliasDeclaration(node->kind());
-  }
-
-  auto name() const -> const DeclaredName& { return name_; }
-  auto target() const -> const Expression& { return *target_; }
-  auto target() -> Expression& { return *target_; }
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-  // Sets the resolved declaration of alias target. Should only be called once,
-  // during name resolution.
-  void set_resolved_declaration(Nonnull<const Declaration*> decl) {
-    CARBON_CHECK(!resolved_declaration_.has_value());
-    resolved_declaration_ = decl;
-  }
-  // Get the resolved declaration of alias target, if any exists.
-  auto resolved_declaration() const
-      -> std::optional<Nonnull<const Declaration*>> {
-    return resolved_declaration_;
-  }
-
- private:
-  DeclaredName name_;
-  Nonnull<Expression*> target_;
-  std::optional<Nonnull<const Declaration*>> resolved_declaration_;
-};
-
-// Return the unqualified name of a declaration, if it has one.
-auto GetName(const Declaration&) -> std::optional<std::string_view>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_DECLARATION_H_

+ 0 - 74
explorer/ast/element.cpp

@@ -1,74 +0,0 @@
-// 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 "explorer/ast/element.h"
-
-#include "common/check.h"
-#include "explorer/ast/declaration.h"
-
-namespace Carbon {
-NamedElement::NamedElement(Nonnull<const Declaration*> declaration)
-    : Element(ElementKind::NamedElement), element_(declaration) {}
-
-NamedElement::NamedElement(Nonnull<const NamedValue*> struct_member)
-    : Element(ElementKind::NamedElement), element_(struct_member) {}
-
-auto NamedElement::IsNamed(std::string_view name) const -> bool {
-  return this->name() == name;
-}
-
-auto NamedElement::name() const -> std::string_view {
-  if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
-    return GetName(*decl).value();
-  } else {
-    const auto* named_value = cast<const NamedValue*>(element_);
-    return named_value->name;
-  }
-}
-
-auto NamedElement::type() const -> const Value& {
-  if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
-    return decl->static_type();
-  } else {
-    const auto* named_value = cast<const NamedValue*>(element_);
-    return *named_value->value;
-  }
-}
-
-auto NamedElement::declaration() const
-    -> std::optional<Nonnull<const Declaration*>> {
-  if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
-    return decl;
-  }
-  return std::nullopt;
-}
-
-auto NamedElement::struct_member() const
-    -> std::optional<Nonnull<const NamedValue*>> {
-  if (const auto* member = element_.dyn_cast<const NamedValue*>()) {
-    return member;
-  }
-  return std::nullopt;
-}
-
-void NamedElement::Print(llvm::raw_ostream& out) const { out << name(); }
-
-// Prints the Element
-void PositionalElement::Print(llvm::raw_ostream& out) const {
-  out << "element #" << index_;
-}
-
-// Return whether the element's name matches `name`.
-auto PositionalElement::IsNamed(std::string_view /*name*/) const -> bool {
-  return false;
-}
-
-void BaseElement::Print(llvm::raw_ostream& out) const { out << "base class"; }
-
-// Return whether the element's name matches `name`.
-auto BaseElement::IsNamed(std::string_view /*name*/) const -> bool {
-  return false;
-}
-
-}  // namespace Carbon

+ 0 - 192
explorer/ast/element.h

@@ -1,192 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_ELEMENT_H_
-#define CARBON_EXPLORER_AST_ELEMENT_H_
-
-#include <optional>
-#include <string>
-#include <string_view>
-
-#include "common/ostream.h"
-#include "explorer/ast/ast_rtti.h"
-#include "explorer/base/decompose.h"
-#include "explorer/base/nonnull.h"
-#include "llvm/ADT/PointerUnion.h"
-
-namespace Carbon {
-
-class Declaration;
-class Value;
-
-// A NamedValue represents a value with a name, such as a single struct field.
-struct NamedValue : HashFromDecompose<NamedValue> {
-  NamedValue(std::string name, Nonnull<const Value*> value)
-      : name(std::move(name)), value(value) {}
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(name, value);
-  }
-
-  // The field name.
-  std::string name;
-
-  // The field's value.
-  Nonnull<const Value*> value;
-};
-
-// A generic member of a type. This is can be a named, positional or other type
-// of member.
-//
-// Arena's canonicalization support is enabled for Element and all derived
-// types. As a result, all Elements must be immutable, and all their constructor
-// arguments must be copyable, equality-comparable, and hashable. See
-// Arena's documentation for details.
-class Element : public Printable<Element> {
- protected:
-  explicit Element(ElementKind kind) : kind_(kind) {}
-
- public:
-  using EnableCanonicalizedAllocation = void;
-  virtual ~Element() = default;
-
-  // Call `f` on this value, cast to its most-derived type. `R` specifies the
-  // expected return type of `f`.
-  template <typename R, typename F>
-  auto Visit(F f) const -> R;
-
-  // Prints the Member
-  virtual void Print(llvm::raw_ostream& out) const = 0;
-
-  // Return whether the member's name matches `name`.
-  virtual auto IsNamed(std::string_view name) const -> bool = 0;
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> ElementKind { return kind_; }
-
-  // The declared type of the member, which might include type variables.
-  virtual auto type() const -> const Value& = 0;
-
- private:
-  const ElementKind kind_;
-};
-
-// A named element of a type.
-//
-// This is either a declared member of a class, interface, or similar, or a
-// member of a struct with no declaration.
-class NamedElement : public Element {
- public:
-  explicit NamedElement(Nonnull<const Declaration*> declaration);
-  explicit NamedElement(Nonnull<const NamedValue*> struct_member);
-
-  template <typename F>
-  auto Decompose(F f) const {
-    if (auto decl = declaration()) {
-      return f(*decl);
-    } else {
-      return f(*struct_member());
-    }
-  }
-
-  // Prints the element's name
-  void Print(llvm::raw_ostream& out) const override;
-
-  auto IsNamed(std::string_view name) const -> bool override;
-
-  static auto classof(const Element* member) -> bool {
-    return InheritsFromNamedElement(member->kind());
-  }
-
-  auto type() const -> const Value& override;
-  // The name of the member.
-  auto name() const -> std::string_view;
-  // A declaration of the member, if any exists.
-  auto declaration() const -> std::optional<Nonnull<const Declaration*>>;
-  // A name and type pair, if this is a struct member.
-  auto struct_member() const -> std::optional<Nonnull<const NamedValue*>>;
-
- private:
-  const llvm::PointerUnion<Nonnull<const Declaration*>,
-                           Nonnull<const NamedValue*>>
-      element_;
-};
-
-// A positional element of a type.
-//
-// This is a positional tuple element, or other index-based value.
-class PositionalElement : public Element {
- public:
-  explicit PositionalElement(int index, Nonnull<const Value*> type)
-      : Element(ElementKind::PositionalElement), index_(index), type_(type) {}
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(index_, type_);
-  }
-
-  // Prints the element
-  void Print(llvm::raw_ostream& out) const override;
-
-  // Return whether the member's name matches `name`.
-  auto IsNamed(std::string_view name) const -> bool override;
-
-  static auto classof(const Element* member) -> bool {
-    return InheritsFromPositionalElement(member->kind());
-  }
-
-  auto index() const -> int { return index_; }
-  auto type() const -> const Value& override { return *type_; }
-
- private:
-  const int index_;
-  const Nonnull<const Value*> type_;
-};
-
-// A base class object.
-//
-// This is the base class object of a class value.
-class BaseElement : public Element {
- public:
-  explicit BaseElement(Nonnull<const Value*> type)
-      : Element(ElementKind::BaseElement), type_(type) {}
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(type_);
-  }
-
-  // Prints the Member
-  void Print(llvm::raw_ostream& out) const override;
-
-  // Return whether the member's name matches `name`.
-  auto IsNamed(std::string_view name) const -> bool override;
-
-  static auto classof(const Element* member) -> bool {
-    return InheritsFromBaseElement(member->kind());
-  }
-
-  auto type() const -> const Value& override { return *type_; }
-
- private:
-  const Nonnull<const Value*> type_;
-};
-
-template <typename R, typename F>
-auto Element::Visit(F f) const -> R {
-  switch (kind()) {
-    case ElementKind::NamedElement:
-      return f(static_cast<const NamedElement*>(this));
-    case ElementKind::PositionalElement:
-      return f(static_cast<const PositionalElement*>(this));
-    case ElementKind::BaseElement:
-      return f(static_cast<const BaseElement*>(this));
-  }
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_ELEMENT_H_

+ 0 - 142
explorer/ast/element_path.h

@@ -1,142 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_ELEMENT_PATH_H_
-#define CARBON_EXPLORER_AST_ELEMENT_PATH_H_
-
-#include <algorithm>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/element.h"
-#include "explorer/ast/value_node.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class InterfaceType;
-class Witness;
-
-// Given some initial Value, a ElementPath identifies a sub-Value within it,
-// in much the same way that a file path identifies a file within some
-// directory. FieldPaths are relative rather than absolute: the initial
-// Value is specified by the context in which the ElementPath is used, not
-// by the ElementPath itself.
-//
-// A ElementPath consists of a series of steps, which specify how to
-// incrementally navigate from a Value to one of its fields. Currently
-// there is only one kind of step, a string specifying a child field by name,
-// but that may change as Carbon develops. Note that an empty ElementPath
-// refers to the initial Value itself.
-class ElementPath : public Printable<ElementPath> {
- public:
-  // Constructs an empty ElementPath.
-  ElementPath() = default;
-
-  // A single component of the ElementPath, which is typically the name
-  // of a field. However, inside a generic, when there is a field
-  // access on something of a generic type, e.g., `T`, then we also
-  // need `witness`, a pointer to the witness table containing that field.
-  class Component : public Printable<Component> {
-   public:
-    explicit Component(Nonnull<const Element*> element) : element_(element) {}
-    Component(Nonnull<const Element*> element,
-              std::optional<Nonnull<const InterfaceType*>> interface,
-              std::optional<Nonnull<const Witness*>> witness)
-        : element_(element), interface_(interface), witness_(witness) {}
-
-    inline friend auto operator==(const Component& lhs, const Component& rhs)
-        -> bool {
-      return lhs.element_ == rhs.element_ && lhs.interface_ == rhs.interface_ &&
-             lhs.witness_ == rhs.witness_;
-    }
-    inline friend auto hash_value(const Component& component)
-        -> llvm::hash_code {
-      return llvm::hash_combine(component.element_, component.interface_,
-                                component.witness_);
-    }
-
-    auto element() const -> Nonnull<const Element*> { return element_; }
-
-    auto IsNamed(std::string_view name) const -> bool {
-      return element_->IsNamed(name);
-    }
-
-    auto interface() const -> std::optional<Nonnull<const InterfaceType*>> {
-      return interface_;
-    }
-
-    auto witness() const -> std::optional<Nonnull<const Witness*>> {
-      return witness_;
-    }
-
-    void Print(llvm::raw_ostream& out) const { return element_->Print(out); }
-
-   private:
-    Nonnull<const Element*> element_;
-    std::optional<Nonnull<const InterfaceType*>> interface_;
-    std::optional<Nonnull<const Witness*>> witness_;
-  };
-
-  // Constructs a ElementPath consisting of a single step.
-  explicit ElementPath(Nonnull<const Element*> element)
-      : components_({Component(element)}) {}
-  explicit ElementPath(const Component& f) : components_({f}) {}
-
-  ElementPath(const ElementPath&) = default;
-  ElementPath(ElementPath&&) = default;
-  auto operator=(const ElementPath&) -> ElementPath& = default;
-  auto operator=(ElementPath&&) -> ElementPath& = default;
-
-  inline friend auto operator==(const ElementPath& lhs, const ElementPath& rhs)
-      -> bool {
-    return lhs.components_ == rhs.components_;
-  }
-  inline friend auto hash_value(const ElementPath& path) -> llvm::hash_code {
-    return llvm::hash_combine_range(path.components_.begin(),
-                                    path.components_.end());
-  }
-
-  // Returns whether *this is empty.
-  auto IsEmpty() const -> bool { return components_.empty(); }
-
-  // Appends `element` to the end of *this.
-  auto Append(Nonnull<const Element*> element) -> void {
-    components_.push_back(Component(element));
-  }
-
-  // Removes all trailing `BaseElement`s, errors if there are no base elements.
-  auto RemoveTrailingBaseElements() -> void {
-    CARBON_CHECK(!components_.empty() && components_.back().element()->kind() ==
-                                             ElementKind::BaseElement,
-                 "No base elements to remove.");
-    const auto r_it = std::find_if(
-        components_.rbegin(), components_.rend(), [](const Component& c) {
-          return c.element()->kind() != ElementKind::BaseElement;
-        });
-    components_.erase(r_it.base(), components_.end());
-  }
-
-  void Print(llvm::raw_ostream& out) const {
-    for (const Component& component : components_) {
-      out << "." << component;
-    }
-  }
-
- private:
-  // The representation of ElementPath describes how to locate a Value within
-  // another Value, so its implementation details are tied to the implementation
-  // details of Value.
-  friend class Value;
-  friend class Heap;
-  std::vector<Component> components_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_ELEMENT_PATH_H_

+ 0 - 96
explorer/ast/element_test.cpp

@@ -1,96 +0,0 @@
-// 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 "explorer/ast/element.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <optional>
-
-#include "explorer/ast/bindings.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-namespace {
-
-static auto FakeSourceLoc(int line_num) -> SourceLocation {
-  return SourceLocation("<test>", line_num, FileKind::Main);
-}
-
-class ElementTest : public ::testing::Test {
- protected:
-  Arena arena;
-};
-
-TEST_F(ElementTest, NamedElementType) {
-  const auto src_loc = FakeSourceLoc(1);
-  VariableDeclaration decl{
-      src_loc,
-      arena.New<BindingPattern>(src_loc, "valuename",
-                                arena.New<AutoPattern>(src_loc),
-                                ExpressionCategory::Reference),
-      std::nullopt, ExpressionCategory::Reference};
-  const auto* static_type = arena.New<IntValue>(1);
-  decl.set_static_type(static_type);
-  NamedElement element_decl(&decl);
-
-  EXPECT_EQ(&element_decl.type(), static_type);
-
-  NamedElement named_val(
-      arena.New<NamedValue>(NamedValue{"valuename", static_type}));
-  EXPECT_EQ(&named_val.type(), static_type);
-}
-
-TEST_F(ElementTest, NamedElementDeclaration) {
-  const auto src_loc = FakeSourceLoc(1);
-  VariableDeclaration decl{
-      src_loc,
-      arena.New<BindingPattern>(src_loc, "valuename",
-                                arena.New<AutoPattern>(src_loc),
-                                ExpressionCategory::Reference),
-      std::nullopt, ExpressionCategory::Reference};
-  const auto* static_type = arena.New<IntValue>(1);
-  NamedElement element_decl(&decl);
-
-  EXPECT_TRUE(element_decl.declaration());
-
-  NamedElement named_val(
-      arena.New<NamedValue>(NamedValue{"valuename", static_type}));
-  EXPECT_FALSE(named_val.declaration());
-}
-
-TEST_F(ElementTest, NamedElementIsNamed) {
-  const auto src_loc = FakeSourceLoc(1);
-  VariableDeclaration decl{
-      src_loc,
-      arena.New<BindingPattern>(src_loc, "valuename",
-                                arena.New<AutoPattern>(src_loc),
-                                ExpressionCategory::Reference),
-      std::nullopt, ExpressionCategory::Reference};
-  NamedElement member_decl(&decl);
-  EXPECT_TRUE(member_decl.IsNamed("valuename"));
-  EXPECT_FALSE(member_decl.IsNamed("anything"));
-
-  NamedElement named_val(
-      arena.New<NamedValue>(NamedValue{"valuename", arena.New<IntValue>(1)}));
-  EXPECT_TRUE(named_val.IsNamed("valuename"));
-  EXPECT_FALSE(named_val.IsNamed("anything"));
-}
-
-TEST_F(ElementTest, PositionalElementIsNamed) {
-  PositionalElement element(1, arena.New<IntValue>(1));
-  EXPECT_FALSE(element.IsNamed("anything"));
-}
-
-TEST_F(ElementTest, BaseElementIsNamed) {
-  BaseElement element(arena.New<IntValue>(1));
-  EXPECT_FALSE(element.IsNamed("anything"));
-}
-}  // namespace
-}  // namespace Carbon

+ 0 - 429
explorer/ast/expression.cpp

@@ -1,429 +0,0 @@
-// 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 "explorer/ast/expression.h"
-
-#include <map>
-#include <optional>
-
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/error_builders.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace Carbon {
-
-using llvm::cast;
-using llvm::isa;
-
-auto IntrinsicExpression::FindIntrinsic(std::string_view name,
-                                        SourceLocation source_loc)
-    -> ErrorOr<Intrinsic> {
-  // TODO: Remove Print special casing once we have variadics or overloads.
-  if (name == "Print") {
-    return Intrinsic::Print;
-  }
-  static const auto& intrinsic_map = *new std::map<std::string_view, Intrinsic>(
-      {{"print", Intrinsic::Print},
-       {"new", Intrinsic::Alloc},
-       {"delete", Intrinsic::Dealloc},
-       {"print_allocs", Intrinsic::PrintAllocs},
-       {"rand", Intrinsic::Rand},
-       {"implicit_as", Intrinsic::ImplicitAs},
-       {"implicit_as_convert", Intrinsic::ImplicitAsConvert},
-       {"int_eq", Intrinsic::IntEq},
-       {"int_compare", Intrinsic::IntCompare},
-       {"int_bit_complement", Intrinsic::IntBitComplement},
-       {"int_bit_and", Intrinsic::IntBitAnd},
-       {"int_bit_or", Intrinsic::IntBitOr},
-       {"int_bit_xor", Intrinsic::IntBitXor},
-       {"int_left_shift", Intrinsic::IntLeftShift},
-       {"int_right_shift", Intrinsic::IntRightShift},
-       {"str_eq", Intrinsic::StrEq},
-       {"str_compare", Intrinsic::StrCompare},
-       {"assert", Intrinsic::Assert}});
-  name.remove_prefix(std::strlen("__intrinsic_"));
-  auto it = intrinsic_map.find(name);
-  if (it == intrinsic_map.end()) {
-    return ProgramError(source_loc) << "Unknown intrinsic '" << name << "'";
-  }
-  return it->second;
-}
-
-auto IntrinsicExpression::name() const -> std::string_view {
-  switch (intrinsic()) {
-    case IntrinsicExpression::Intrinsic::Print:
-      // TODO: Remove Print special casing once we have variadics or overloads.
-      return "Print";
-    case IntrinsicExpression::Intrinsic::Alloc:
-      return "__intrinsic_new";
-    case IntrinsicExpression::Intrinsic::Dealloc:
-      return "__intrinsic_delete";
-    case IntrinsicExpression::Intrinsic::PrintAllocs:
-      return "__intrinsic_print_allocs";
-    case IntrinsicExpression::Intrinsic::Rand:
-      return "__intrinsic_rand";
-    case IntrinsicExpression::Intrinsic::ImplicitAs:
-      return "__intrinsic_implicit_as";
-    case IntrinsicExpression::Intrinsic::ImplicitAsConvert:
-      return "__intrinsic_implicit_as_convert";
-    case IntrinsicExpression::Intrinsic::IntEq:
-      return "__intrinsic_int_eq";
-    case IntrinsicExpression::Intrinsic::IntCompare:
-      return "__intrinsic_int_compare";
-    case IntrinsicExpression::Intrinsic::IntBitComplement:
-      return "__intrinsic_int_bit_complement";
-    case IntrinsicExpression::Intrinsic::IntBitAnd:
-      return "__intrinsic_int_bit_and";
-    case IntrinsicExpression::Intrinsic::IntBitOr:
-      return "__intrinsic_int_bit_or";
-    case IntrinsicExpression::Intrinsic::IntBitXor:
-      return "__intrinsic_int_bit_xor";
-    case IntrinsicExpression::Intrinsic::IntLeftShift:
-      return "__intrinsic_int_left_shift";
-    case IntrinsicExpression::Intrinsic::IntRightShift:
-      return "__intrinsic_int_right_shift";
-    case IntrinsicExpression::Intrinsic::StrEq:
-      return "__intrinsic_str_eq";
-    case IntrinsicExpression::Intrinsic::StrCompare:
-      return "__intrinsic_str_compare";
-    case IntrinsicExpression::Intrinsic::Assert:
-      return "__intrinsic_assert";
-  }
-}
-
-auto ExpressionFromParenContents(
-    Nonnull<Arena*> arena, SourceLocation source_loc,
-    const ParenContents<Expression>& paren_contents) -> Nonnull<Expression*> {
-  std::optional<Nonnull<Expression*>> single_term = paren_contents.SingleTerm();
-  if (single_term.has_value()) {
-    return *single_term;
-  } else {
-    return TupleExpressionFromParenContents(arena, source_loc, paren_contents);
-  }
-}
-
-auto TupleExpressionFromParenContents(
-    Nonnull<Arena*> arena, SourceLocation source_loc,
-    const ParenContents<Expression>& paren_contents) -> Nonnull<TupleLiteral*> {
-  return arena->New<TupleLiteral>(source_loc, paren_contents.elements);
-}
-
-Expression::~Expression() = default;
-
-auto OperatorToString(Operator op) -> std::string_view {
-  switch (op) {
-    case Operator::Add:
-      return "+";
-    case Operator::As:
-      return "as";
-    case Operator::AddressOf:
-    case Operator::BitwiseAnd:
-      return "&";
-    case Operator::BitwiseOr:
-      return "|";
-    case Operator::BitwiseXor:
-    case Operator::Complement:
-      return "^";
-    case Operator::BitShiftLeft:
-      return "<<";
-    case Operator::BitShiftRight:
-      return ">>";
-    case Operator::Div:
-      return "/";
-    case Operator::Neg:
-    case Operator::Sub:
-      return "-";
-    case Operator::Mul:
-    case Operator::Deref:
-    case Operator::Ptr:
-      return "*";
-    case Operator::Not:
-      return "not";
-    case Operator::NotEq:
-      return "!=";
-    case Operator::And:
-      return "and";
-    case Operator::Or:
-      return "or";
-    case Operator::Eq:
-      return "==";
-    case Operator::Mod:
-      return "%";
-    case Operator::Less:
-      return "<";
-    case Operator::LessEq:
-      return "<=";
-    case Operator::Greater:
-      return ">";
-    case Operator::GreaterEq:
-      return ">=";
-  }
-}
-
-static void PrintFields(llvm::raw_ostream& out,
-                        const std::vector<FieldInitializer>& fields,
-                        std::string_view separator) {
-  llvm::ListSeparator sep;
-  for (const auto& field : fields) {
-    out << sep << "." << field.name() << separator << field.expression();
-  }
-}
-
-void Expression::Print(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case ExpressionKind::IndexExpression: {
-      const auto& index = cast<IndexExpression>(*this);
-      out << index.object() << "[" << index.offset() << "]";
-      break;
-    }
-    case ExpressionKind::SimpleMemberAccessExpression: {
-      const auto& access = cast<SimpleMemberAccessExpression>(*this);
-      out << access.object() << "." << access.member_name();
-      break;
-    }
-    case ExpressionKind::CompoundMemberAccessExpression: {
-      const auto& access = cast<CompoundMemberAccessExpression>(*this);
-      out << access.object() << ".(" << access.path() << ")";
-      break;
-    }
-    case ExpressionKind::BaseAccessExpression: {
-      const auto& access = cast<BaseAccessExpression>(*this);
-      out << access.object() << ".base";
-      break;
-    }
-    case ExpressionKind::TupleLiteral: {
-      out << "(";
-      llvm::ListSeparator sep;
-      for (Nonnull<const Expression*> field :
-           cast<TupleLiteral>(*this).fields()) {
-        out << sep << *field;
-      }
-      out << ")";
-      break;
-    }
-    case ExpressionKind::StructLiteral:
-      out << "{";
-      PrintFields(out, cast<StructLiteral>(*this).fields(), " = ");
-      out << "}";
-      break;
-    case ExpressionKind::StructTypeLiteral:
-      out << "{";
-      PrintFields(out, cast<StructTypeLiteral>(*this).fields(), ": ");
-      out << "}";
-      break;
-    case ExpressionKind::OperatorExpression: {
-      out << "(";
-      const auto& op = cast<OperatorExpression>(*this);
-      switch (op.arguments().size()) {
-        case 0:
-          out << OperatorToString(op.op());
-          break;
-        case 1:
-          out << OperatorToString(op.op()) << " " << *op.arguments()[0];
-          break;
-        case 2:
-          out << *op.arguments()[0] << " " << OperatorToString(op.op()) << " "
-              << *op.arguments()[1];
-          break;
-        default:
-          CARBON_FATAL("Unexpected argument count: {0}", op.arguments().size());
-      }
-      out << ")";
-      break;
-    }
-    case ExpressionKind::CallExpression: {
-      const auto& call = cast<CallExpression>(*this);
-      out << call.function();
-      if (isa<TupleLiteral>(call.argument())) {
-        out << call.argument();
-      } else {
-        out << "(" << call.argument() << ")";
-      }
-      break;
-    }
-    case ExpressionKind::FunctionTypeLiteral: {
-      const auto& fn = cast<FunctionTypeLiteral>(*this);
-      out << "fn " << fn.parameter() << " -> " << fn.return_type();
-      break;
-    }
-    case ExpressionKind::IntrinsicExpression: {
-      const auto& iexp = cast<IntrinsicExpression>(*this);
-      out << iexp.name() << iexp.args();
-      break;
-    }
-    case ExpressionKind::IfExpression: {
-      const auto& if_expr = cast<IfExpression>(*this);
-      out << "if " << if_expr.condition() << " then "
-          << if_expr.then_expression() << " else " << if_expr.else_expression();
-      break;
-    }
-    case ExpressionKind::WhereExpression: {
-      const auto& where = cast<WhereExpression>(*this);
-      out << where.self_binding().type() << " where ";
-      llvm::ListSeparator sep(" and ");
-      for (const WhereClause* clause : where.clauses()) {
-        out << sep << *clause;
-      }
-      break;
-    }
-    case ExpressionKind::BuiltinConvertExpression: {
-      // These don't represent source syntax, so just print the original
-      // expression.
-      out << *cast<BuiltinConvertExpression>(this)->source_expression();
-      break;
-    }
-    case ExpressionKind::UnimplementedExpression: {
-      const auto& unimplemented = cast<UnimplementedExpression>(*this);
-      out << "UnimplementedExpression<" << unimplemented.label() << ">(";
-      llvm::ListSeparator sep;
-      for (Nonnull<const AstNode*> child : unimplemented.children()) {
-        out << sep << *child;
-      }
-      out << ")";
-      break;
-    }
-    case ExpressionKind::ArrayTypeLiteral: {
-      const auto& array_literal = cast<ArrayTypeLiteral>(*this);
-      out << "[" << array_literal.element_type_expression() << ";";
-      if (array_literal.has_size_expression()) {
-        out << " " << array_literal.size_expression();
-      }
-      out << "]";
-      break;
-    }
-    case ExpressionKind::IdentifierExpression:
-    case ExpressionKind::DotSelfExpression:
-    case ExpressionKind::IntLiteral:
-    case ExpressionKind::BoolLiteral:
-    case ExpressionKind::BoolTypeLiteral:
-    case ExpressionKind::IntTypeLiteral:
-    case ExpressionKind::StringLiteral:
-    case ExpressionKind::StringTypeLiteral:
-    case ExpressionKind::TypeTypeLiteral:
-    case ExpressionKind::ValueLiteral:
-      PrintID(out);
-      break;
-  }
-}
-
-void Expression::PrintID(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case ExpressionKind::IdentifierExpression:
-      out << cast<IdentifierExpression>(*this).name();
-      break;
-    case ExpressionKind::DotSelfExpression:
-      out << ".Self";
-      break;
-    case ExpressionKind::IntLiteral:
-      out << cast<IntLiteral>(*this).value();
-      break;
-    case ExpressionKind::BoolLiteral:
-      out << (cast<BoolLiteral>(*this).value() ? "true" : "false");
-      break;
-    case ExpressionKind::BoolTypeLiteral:
-      out << "bool";
-      break;
-    case ExpressionKind::IntTypeLiteral:
-      out << "i32";
-      break;
-    case ExpressionKind::StringLiteral:
-      out << "\"";
-      out.write_escaped(cast<StringLiteral>(*this).value());
-      out << "\"";
-      break;
-    case ExpressionKind::StringTypeLiteral:
-      out << "String";
-      break;
-    case ExpressionKind::TypeTypeLiteral:
-      out << "type";
-      break;
-    case ExpressionKind::ValueLiteral:
-      out << cast<ConstantValueLiteral>(*this).constant_value();
-      break;
-    case ExpressionKind::ArrayTypeLiteral:
-    case ExpressionKind::FunctionTypeLiteral:
-    case ExpressionKind::StructLiteral:
-    case ExpressionKind::IndexExpression:
-    case ExpressionKind::SimpleMemberAccessExpression:
-    case ExpressionKind::CompoundMemberAccessExpression:
-    case ExpressionKind::BaseAccessExpression:
-    case ExpressionKind::IfExpression:
-    case ExpressionKind::WhereExpression:
-    case ExpressionKind::BuiltinConvertExpression:
-    case ExpressionKind::TupleLiteral:
-    case ExpressionKind::StructTypeLiteral:
-    case ExpressionKind::CallExpression:
-    case ExpressionKind::OperatorExpression:
-    case ExpressionKind::IntrinsicExpression:
-    case ExpressionKind::UnimplementedExpression:
-      out << "...";
-      break;
-  }
-}
-
-DotSelfExpression::DotSelfExpression(CloneContext& context,
-                                     const DotSelfExpression& other)
-    : Expression(context, other),
-      name_(other.name_),
-      self_binding_(context.Remap(other.self_binding_)) {}
-
-MemberAccessExpression::MemberAccessExpression(
-    CloneContext& context, const MemberAccessExpression& other)
-    : Expression(context, other),
-      object_(context.Clone(other.object_)),
-      is_type_access_(other.is_type_access_),
-      is_addr_me_method_(other.is_addr_me_method_),
-      impl_(context.Clone(other.impl_)),
-      constant_value_(context.Clone(other.constant_value_)) {}
-
-SimpleMemberAccessExpression::SimpleMemberAccessExpression(
-    CloneContext& context, const SimpleMemberAccessExpression& other)
-    : RewritableMixin(context, other),
-      member_name_(other.member_name_),
-      member_(context.Clone(other.member_)),
-      found_in_interface_(context.Clone(other.found_in_interface_)),
-      value_node_(context.Clone(other.value_node_)) {}
-
-CompoundMemberAccessExpression::CompoundMemberAccessExpression(
-    CloneContext& context, const CompoundMemberAccessExpression& other)
-    : MemberAccessExpression(context, other),
-      path_(context.Clone(other.path_)),
-      member_(context.Clone(other.member_)) {}
-
-WhereClause::~WhereClause() = default;
-
-void WhereClause::Print(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case WhereClauseKind::ImplsWhereClause: {
-      const auto& clause = cast<ImplsWhereClause>(*this);
-      out << clause.type() << " impls " << clause.constraint();
-      break;
-    }
-    case WhereClauseKind::EqualsWhereClause: {
-      const auto& clause = cast<EqualsWhereClause>(*this);
-      out << clause.lhs() << " == " << clause.rhs();
-      break;
-    }
-    case WhereClauseKind::RewriteWhereClause: {
-      const auto& clause = cast<RewriteWhereClause>(*this);
-      out << "." << clause.member_name() << " = " << clause.replacement();
-      break;
-    }
-  }
-}
-
-void WhereClause::PrintID(llvm::raw_ostream& out) const { out << "..."; }
-
-WhereExpression::WhereExpression(CloneContext& context,
-                                 const WhereExpression& other)
-    : RewritableMixin(context, other),
-      self_binding_(context.Clone(other.self_binding_)),
-      clauses_(context.Clone(other.clauses_)),
-      enclosing_dot_self_(context.Remap(other.enclosing_dot_self_)) {}
-
-}  // namespace Carbon

+ 0 - 1260
explorer/ast/expression.h

@@ -1,1260 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_EXPRESSION_H_
-#define CARBON_EXPLORER_AST_EXPRESSION_H_
-
-#include <map>
-#include <optional>
-#include <string>
-#include <utility>
-#include <variant>
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/bindings.h"
-#include "explorer/ast/element.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/paren_contents.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class Value;
-class Witness;
-class MemberName;
-class VariableType;
-class InterfaceType;
-class ImplBinding;
-class GenericBinding;
-
-class Expression : public AstNode {
- public:
-  ~Expression() override = 0;
-
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromExpression(node->kind());
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> ExpressionKind {
-    return static_cast<ExpressionKind>(root_kind());
-  }
-
-  // The static type of this expression. Cannot be called before typechecking.
-  auto static_type() const -> const Value& {
-    CARBON_CHECK(static_type_.has_value());
-    return **static_type_;
-  }
-
-  // Sets the static type of this expression. Can only be called once, during
-  // typechecking.
-  void set_static_type(Nonnull<const Value*> type) {
-    CARBON_CHECK(!static_type_.has_value());
-    static_type_ = type;
-  }
-
-  // The value category of this expression. Cannot be called before
-  // typechecking.
-  auto expression_category() const -> ExpressionCategory {
-    return *expression_category_;
-  }
-
-  // Sets the value category of this expression. Can be called multiple times,
-  // but the argument must have the same value each time.
-  void set_expression_category(ExpressionCategory expression_category) {
-    CARBON_CHECK(!expression_category_.has_value() ||
-                 expression_category == *expression_category_);
-    expression_category_ = expression_category;
-  }
-
-  // Determines whether the expression has already been type-checked. Should
-  // only be used by type-checking.
-  auto is_type_checked() const -> bool {
-    return static_type_.has_value() && expression_category_.has_value();
-  }
-
- protected:
-  // Constructs an Expression representing syntax at the given line number.
-  // `kind` must be the enumerator corresponding to the most-derived type being
-  // constructed.
-  Expression(AstNodeKind kind, SourceLocation source_loc)
-      : AstNode(kind, source_loc) {}
-
-  explicit Expression(CloneContext& context, const Expression& other)
-      : AstNode(context, other) {}
-
- private:
-  std::optional<Nonnull<const Value*>> static_type_;
-  std::optional<ExpressionCategory> expression_category_;
-};
-
-// A mixin for expressions that can be rewritten to a different expression by
-// type-checking.
-template <typename Base>
-class RewritableMixin : public Base {
- public:
-  using Base::Base;
-
-  explicit RewritableMixin(CloneContext& context, const RewritableMixin& other)
-      : Base(context, other),
-        rewritten_form_(context.Clone(other.rewritten_form_)) {}
-
-  // Set the rewritten form of this expression. Can only be called during type
-  // checking.
-  auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
-    rewritten_form_ = rewritten_form;
-    this->set_static_type(&rewritten_form->static_type());
-    this->set_expression_category(rewritten_form->expression_category());
-  }
-
-  // Get the rewritten form of this expression. A rewritten form is used when
-  // the expression is rewritten as a function call on an interface. A
-  // rewritten form is not used when providing built-in operator semantics.
-  auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
-    return rewritten_form_;
-  }
-
- private:
-  std::optional<Nonnull<const Expression*>> rewritten_form_;
-};
-
-// A FieldInitializer represents the initialization of a single struct field.
-class FieldInitializer {
- public:
-  FieldInitializer(std::string name, Nonnull<Expression*> expression)
-      : name_(std::move(name)), expression_(expression) {}
-
-  explicit FieldInitializer(CloneContext& context,
-                            const FieldInitializer& other)
-      : name_(other.name_), expression_(context.Clone(other.expression_)) {}
-
-  auto name() const -> const std::string& { return name_; }
-
-  auto expression() const -> const Expression& { return *expression_; }
-  auto expression() -> Expression& { return *expression_; }
-
- private:
-  // The field name. Cannot be empty.
-  std::string name_;
-
-  // The expression that initializes the field.
-  Nonnull<Expression*> expression_;
-};
-
-enum class Operator {
-  Add,
-  AddressOf,
-  And,
-  As,
-  BitwiseAnd,
-  BitwiseOr,
-  BitwiseXor,
-  BitShiftLeft,
-  BitShiftRight,
-  Complement,
-  Deref,
-  Div,
-  Eq,
-  Less,
-  LessEq,
-  Greater,
-  GreaterEq,
-  Mul,
-  Mod,
-  Neg,
-  Not,
-  NotEq,
-  Or,
-  Sub,
-  Ptr,
-};
-
-// Returns the lexical representation of `op`, such as "+" for `Add`.
-auto OperatorToString(Operator op) -> std::string_view;
-
-class IdentifierExpression : public Expression {
- public:
-  explicit IdentifierExpression(SourceLocation source_loc, std::string name)
-      : Expression(AstNodeKind::IdentifierExpression, source_loc),
-        name_(std::move(name)) {}
-
-  explicit IdentifierExpression(CloneContext& context,
-                                const IdentifierExpression& other)
-      : Expression(context, other),
-        name_(other.name_),
-        value_node_(context.Clone(other.value_node_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIdentifierExpression(node->kind());
-  }
-
-  auto name() const -> const std::string& { return name_; }
-
-  // Returns the ValueNodeView this identifier refers to. Cannot be called
-  // before name resolution.
-  auto value_node() const -> const ValueNodeView& { return *value_node_; }
-
-  // Sets the value returned by value_node. Can be called only during name
-  // resolution.
-  void set_value_node(ValueNodeView value_node) {
-    CARBON_CHECK(!value_node_.has_value() || value_node_ == value_node);
-    value_node_ = std::move(value_node);
-  }
-
- private:
-  std::string name_;
-  std::optional<ValueNodeView> value_node_;
-};
-
-// A `.Self` expression within either a `:!` binding or a standalone `where`
-// expression.
-//
-// In a `:!` binding, the type of `.Self` is always `type`. For example, in
-// `A:! AddableWith(.Self)`, the expression `.Self` refers to the same type as
-// `A`, but with type `type`.
-//
-// In a `where` binding, the type of `.Self` is the constraint preceding the
-// `where` keyword. For example, in `Foo where .Result impls Bar(.Self)`, the
-// type of `.Self` is `Foo`.
-class DotSelfExpression : public Expression {
- public:
-  explicit DotSelfExpression(SourceLocation source_loc)
-      : Expression(AstNodeKind::DotSelfExpression, source_loc) {}
-
-  explicit DotSelfExpression(CloneContext& context,
-                             const DotSelfExpression& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromDotSelfExpression(node->kind());
-  }
-
-  // The self binding. Cannot be called before name resolution.
-  auto self_binding() const -> const GenericBinding& { return **self_binding_; }
-  auto self_binding() -> GenericBinding& { return **self_binding_; }
-
-  // Sets the self binding. Called only during name resolution.
-  void set_self_binding(Nonnull<GenericBinding*> self_binding) {
-    CARBON_CHECK(!self_binding_.has_value() || self_binding_ == self_binding);
-    self_binding_ = self_binding;
-  }
-
- private:
-  std::string name_;
-  std::optional<Nonnull<GenericBinding*>> self_binding_;
-};
-
-class MemberAccessExpression : public Expression {
- public:
-  explicit MemberAccessExpression(AstNodeKind kind, SourceLocation source_loc,
-                                  Nonnull<Expression*> object)
-      : Expression(kind, source_loc), object_(object) {}
-
-  explicit MemberAccessExpression(CloneContext& context,
-                                  const MemberAccessExpression& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMemberAccessExpression(node->kind());
-  }
-
-  auto object() const -> const Expression& { return *object_; }
-  auto object() -> Expression& { return *object_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_object(Nonnull<Expression*> object) { object_ = object; }
-
-  // Returns true if this is an access of a member of the type of the object,
-  // rather than an access of a member of the object itself. In this case, the
-  // value of the object expression is ignored, and the type is accessed
-  // instead.
-  //
-  // For example, given `x: Class`, `x.StaticFunction` is a type access
-  // equivalent to `T.StaticFunction`, and given `T:! Interface` and `y: T`,
-  // `y.AssociatedConstant` is a type access equivalent to
-  // `T.AssociatedConstant`.
-  auto is_type_access() const -> bool { return is_type_access_; }
-
-  // Can only be called once, during typechecking.
-  void set_is_type_access(bool type_access) { is_type_access_ = type_access; }
-
-  // Returns true if the member is a method that has a "self" declaration in an
-  // AddrPattern.
-  auto is_addr_me_method() const -> bool { return is_addr_me_method_; }
-
-  // Can only be called once, during typechecking.
-  void set_is_addr_me_method() { is_addr_me_method_ = true; }
-
-  // If `object` has a generic type, returns the witness value, which might be
-  // either concrete or symbolic. Otherwise, returns `std::nullopt`. Should not
-  // be called before typechecking.
-  auto impl() const -> std::optional<Nonnull<const Witness*>> { return impl_; }
-
-  // Can only be called once, during typechecking.
-  void set_impl(Nonnull<const Witness*> impl) {
-    CARBON_CHECK(!impl_.has_value());
-    impl_ = impl;
-  }
-
-  // Returns the constant value of this expression, if one has been set. This
-  // value will be used instead of accessing a member. Even if this is present,
-  // the operand of the member access expression must still be evaluated, in
-  // case it has side effects.
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_;
-  }
-
-  // Sets the value returned by constant_value(). Can only be called once,
-  // during typechecking.
-  void set_constant_value(Nonnull<const Value*> value) {
-    CARBON_CHECK(!constant_value_.has_value());
-    constant_value_ = value;
-  }
-
- private:
-  Nonnull<Expression*> object_;
-  bool is_type_access_ = false;
-  bool is_addr_me_method_ = false;
-  std::optional<Nonnull<const Witness*>> impl_;
-  std::optional<Nonnull<const Value*>> constant_value_;
-};
-
-class SimpleMemberAccessExpression
-    : public RewritableMixin<MemberAccessExpression> {
- public:
-  explicit SimpleMemberAccessExpression(SourceLocation source_loc,
-                                        Nonnull<Expression*> object,
-                                        std::string member_name)
-      : RewritableMixin(AstNodeKind::SimpleMemberAccessExpression, source_loc,
-                        object),
-        member_name_(std::move(member_name)) {}
-
-  explicit SimpleMemberAccessExpression(
-      CloneContext& context, const SimpleMemberAccessExpression& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromSimpleMemberAccessExpression(node->kind());
-  }
-
-  auto member_name() const -> const std::string& { return member_name_; }
-
-  // Returns the `NamedElement` that the member name resolved to.
-  // Should not be called before typechecking.
-  auto member() const -> const NamedElement& {
-    CARBON_CHECK(member_.has_value());
-    return *member_.value();
-  }
-
-  // Can only be called once, during typechecking.
-  void set_member(Nonnull<const NamedElement*> member) { member_ = member; }
-
-  // If `object` is a constrained type parameter and `member` was found in an
-  // interface, returns that interface. Should not be called before
-  // typechecking.
-  auto found_in_interface() const
-      -> std::optional<Nonnull<const InterfaceType*>> {
-    return found_in_interface_;
-  }
-
-  // Can only be called once, during typechecking.
-  void set_found_in_interface(Nonnull<const InterfaceType*> interface) {
-    CARBON_CHECK(!found_in_interface_.has_value());
-    found_in_interface_ = interface;
-  }
-
-  // Returns the ValueNodeView this identifier refers to, if this was
-  // determined by name resolution. Cannot be called before name resolution.
-  auto value_node() const -> std::optional<ValueNodeView> {
-    return value_node_;
-  }
-
-  // Sets the value returned by value_node. Can be called only during name
-  // resolution.
-  void set_value_node(ValueNodeView value_node) {
-    CARBON_CHECK(!value_node_.has_value() || value_node_ == value_node);
-    value_node_ = std::move(value_node);
-  }
-
- private:
-  std::string member_name_;
-  std::optional<Nonnull<const NamedElement*>> member_;
-  std::optional<Nonnull<const InterfaceType*>> found_in_interface_;
-  std::optional<ValueNodeView> value_node_;
-};
-
-// A compound member access expression of the form `object.(path)`.
-//
-// `path` is required to have `TypeOfMemberName` type, and describes the member
-// being accessed, which is one of:
-//
-// -   An instance member of a type: `object.(Type.member)`.
-// -   A non-instance member of an interface: `Type.(Interface.member)` or
-//     `object.(Interface.member)`.
-// -   An instance member of an interface: `object.(Interface.member)` or
-//     `object.(Type.(Interface.member))`.
-//
-// Note that the `path` is evaluated during type-checking, not at runtime, so
-// the corresponding `member` is determined statically.
-class CompoundMemberAccessExpression : public MemberAccessExpression {
- public:
-  explicit CompoundMemberAccessExpression(SourceLocation source_loc,
-                                          Nonnull<Expression*> object,
-                                          Nonnull<Expression*> path)
-      : MemberAccessExpression(AstNodeKind::CompoundMemberAccessExpression,
-                               source_loc, object),
-        path_(path) {}
-
-  explicit CompoundMemberAccessExpression(
-      CloneContext& context, const CompoundMemberAccessExpression& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromCompoundMemberAccessExpression(node->kind());
-  }
-
-  auto path() const -> const Expression& { return *path_; }
-  auto path() -> Expression& { return *path_; }
-
-  // Returns the `MemberName` value that evaluation of the path produced.
-  // Should not be called before typechecking.
-  auto member() const -> const MemberName& {
-    CARBON_CHECK(member_.has_value());
-    return **member_;
-  }
-
-  // Can only be called once, during typechecking.
-  void set_member(Nonnull<const MemberName*> member) {
-    CARBON_CHECK(!member_.has_value());
-    member_ = member;
-  }
-
- private:
-  Nonnull<Expression*> path_;
-  std::optional<Nonnull<const MemberName*>> member_;
-};
-
-class IndexExpression : public Expression {
- public:
-  explicit IndexExpression(SourceLocation source_loc,
-                           Nonnull<Expression*> object,
-                           Nonnull<Expression*> offset)
-      : Expression(AstNodeKind::IndexExpression, source_loc),
-        object_(object),
-        offset_(offset) {}
-
-  explicit IndexExpression(CloneContext& context, const IndexExpression& other)
-      : Expression(context, other),
-        object_(context.Clone(other.object_)),
-        offset_(context.Clone(other.offset_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIndexExpression(node->kind());
-  }
-
-  auto object() const -> const Expression& { return *object_; }
-  auto object() -> Expression& { return *object_; }
-  auto offset() const -> const Expression& { return *offset_; }
-  auto offset() -> Expression& { return *offset_; }
-
- private:
-  Nonnull<Expression*> object_;
-  Nonnull<Expression*> offset_;
-};
-
-class BaseAccessExpression : public MemberAccessExpression {
- public:
-  explicit BaseAccessExpression(SourceLocation source_loc,
-                                Nonnull<Expression*> object,
-                                Nonnull<const BaseElement*> base)
-      : MemberAccessExpression(AstNodeKind::BaseAccessExpression, source_loc,
-                               object),
-        base_(base) {
-    set_static_type(&base->type());
-    set_expression_category(ExpressionCategory::Value);
-  }
-
-  explicit BaseAccessExpression(CloneContext& context,
-                                const BaseAccessExpression& other)
-      : MemberAccessExpression(context, other),
-        base_(context.Clone(other.base_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBaseAccessExpression(node->kind());
-  }
-
-  auto element() const -> const BaseElement& { return *base_; }
-
- private:
-  const Nonnull<const BaseElement*> base_;
-};
-
-class IntLiteral : public Expression {
- public:
-  explicit IntLiteral(SourceLocation source_loc, int value)
-      : Expression(AstNodeKind::IntLiteral, source_loc), value_(value) {}
-
-  explicit IntLiteral(CloneContext& context, const IntLiteral& other)
-      : Expression(context, other), value_(other.value_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIntLiteral(node->kind());
-  }
-
-  auto value() const -> int { return value_; }
-
- private:
-  int value_;
-};
-
-class BoolLiteral : public Expression {
- public:
-  explicit BoolLiteral(SourceLocation source_loc, bool value)
-      : Expression(AstNodeKind::BoolLiteral, source_loc), value_(value) {}
-
-  explicit BoolLiteral(CloneContext& context, const BoolLiteral& other)
-      : Expression(context, other), value_(other.value_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBoolLiteral(node->kind());
-  }
-
-  auto value() const -> bool { return value_; }
-
- private:
-  bool value_;
-};
-
-class StringLiteral : public Expression {
- public:
-  explicit StringLiteral(SourceLocation source_loc, std::string value)
-      : Expression(AstNodeKind::StringLiteral, source_loc),
-        value_(std::move(value)) {}
-
-  explicit StringLiteral(CloneContext& context, const StringLiteral& other)
-      : Expression(context, other), value_(other.value_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromStringLiteral(node->kind());
-  }
-
-  auto value() const -> const std::string& { return value_; }
-
- private:
-  std::string value_;
-};
-
-class StringTypeLiteral : public Expression {
- public:
-  explicit StringTypeLiteral(SourceLocation source_loc)
-      : Expression(AstNodeKind::StringTypeLiteral, source_loc) {}
-
-  explicit StringTypeLiteral(CloneContext& context,
-                             const StringTypeLiteral& other)
-      : Expression(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromStringTypeLiteral(node->kind());
-  }
-};
-
-class TupleLiteral : public Expression {
- public:
-  explicit TupleLiteral(SourceLocation source_loc)
-      : TupleLiteral(source_loc, {}) {}
-
-  explicit TupleLiteral(SourceLocation source_loc,
-                        std::vector<Nonnull<Expression*>> fields)
-      : Expression(AstNodeKind::TupleLiteral, source_loc),
-        fields_(std::move(fields)) {}
-
-  explicit TupleLiteral(CloneContext& context, const TupleLiteral& other)
-      : Expression(context, other), fields_(context.Clone(other.fields_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromTupleLiteral(node->kind());
-  }
-
-  auto fields() const -> llvm::ArrayRef<Nonnull<const Expression*>> {
-    return fields_;
-  }
-  auto fields() -> llvm::ArrayRef<Nonnull<Expression*>> { return fields_; }
-
- private:
-  std::vector<Nonnull<Expression*>> fields_;
-};
-
-// A literal value of a struct type.
-class StructLiteral : public Expression {
- public:
-  explicit StructLiteral(SourceLocation loc) : StructLiteral(loc, {}) {}
-
-  explicit StructLiteral(SourceLocation loc,
-                         std::vector<FieldInitializer> fields)
-      : Expression(AstNodeKind::StructLiteral, loc),
-        fields_(std::move(fields)) {}
-
-  explicit StructLiteral(CloneContext& context, const StructLiteral& other)
-      : Expression(context, other), fields_(context.Clone(other.fields_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromStructLiteral(node->kind());
-  }
-
-  auto fields() const -> llvm::ArrayRef<FieldInitializer> { return fields_; }
-  auto fields() -> llvm::MutableArrayRef<FieldInitializer> { return fields_; }
-
- private:
-  std::vector<FieldInitializer> fields_;
-};
-
-// A base class for literals with a constant value determined by type-checking.
-class ConstantValueLiteral : public Expression {
- public:
-  explicit ConstantValueLiteral(
-      AstNodeKind kind, SourceLocation source_loc,
-      std::optional<Nonnull<const Value*>> constant_value = std::nullopt)
-      : Expression(kind, source_loc), constant_value_(constant_value) {}
-
-  explicit ConstantValueLiteral(CloneContext& context,
-                                const ConstantValueLiteral& other)
-      : Expression(context, other),
-        constant_value_(context.Clone(other.constant_value_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromConstantValueLiteral(node->kind());
-  }
-
-  // Returns the constant value of this expression.
-  auto constant_value() const -> const Value& {
-    CARBON_CHECK(constant_value_);
-    return **constant_value_;
-  }
-
-  // Sets the value returned by constant_value(). Can only be called once,
-  // during typechecking.
-  void set_constant_value(Nonnull<const Value*> value) {
-    CARBON_CHECK(!constant_value_.has_value());
-    constant_value_ = value;
-  }
-
- private:
-  std::optional<Nonnull<const Value*>> constant_value_;
-};
-
-// A literal representing a struct type.
-//
-// Note that a struct type literal can't be empty because `{}` is a struct
-// value. However, that value implicitly converts to a type.
-class StructTypeLiteral : public ConstantValueLiteral {
- public:
-  explicit StructTypeLiteral(SourceLocation loc,
-                             std::vector<FieldInitializer> fields)
-      : ConstantValueLiteral(AstNodeKind::StructTypeLiteral, loc),
-        fields_(std::move(fields)) {
-    CARBON_CHECK(
-        !fields_.empty(),
-        "`{}` is represented as a StructLiteral, not a StructTypeLiteral.");
-  }
-
-  explicit StructTypeLiteral(CloneContext& context,
-                             const StructTypeLiteral& other)
-      : ConstantValueLiteral(context, other),
-        fields_(context.Clone(other.fields_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromStructTypeLiteral(node->kind());
-  }
-
-  auto fields() const -> llvm::ArrayRef<FieldInitializer> { return fields_; }
-  auto fields() -> llvm::MutableArrayRef<FieldInitializer> { return fields_; }
-
- private:
-  std::vector<FieldInitializer> fields_;
-};
-
-class OperatorExpression : public RewritableMixin<Expression> {
- public:
-  explicit OperatorExpression(SourceLocation source_loc, Operator op,
-                              std::vector<Nonnull<Expression*>> arguments)
-      : RewritableMixin(AstNodeKind::OperatorExpression, source_loc),
-        op_(op),
-        arguments_(std::move(arguments)) {}
-
-  explicit OperatorExpression(CloneContext& context,
-                              const OperatorExpression& other)
-      : RewritableMixin(context, other),
-        op_(other.op_),
-        arguments_(context.Clone(other.arguments_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromOperatorExpression(node->kind());
-  }
-
-  auto op() const -> Operator { return op_; }
-  auto arguments() const -> llvm::ArrayRef<Nonnull<Expression*>> {
-    return arguments_;
-  }
-  auto arguments() -> llvm::MutableArrayRef<Nonnull<Expression*>> {
-    return arguments_;
-  }
-
- private:
-  Operator op_;
-  std::vector<Nonnull<Expression*>> arguments_;
-};
-
-class CallExpression : public Expression {
- public:
-  explicit CallExpression(SourceLocation source_loc,
-                          Nonnull<Expression*> function,
-                          Nonnull<Expression*> argument)
-      : Expression(AstNodeKind::CallExpression, source_loc),
-        function_(function),
-        argument_(argument),
-        bindings_({}, {}) {}
-
-  explicit CallExpression(CloneContext& context, const CallExpression& other)
-      : Expression(context, other),
-        function_(context.Clone(other.function_)),
-        argument_(context.Clone(other.argument_)),
-        bindings_(context.Clone(other.bindings_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromCallExpression(node->kind());
-  }
-
-  auto function() const -> const Expression& { return *function_; }
-  auto function() -> Expression& { return *function_; }
-  auto argument() const -> const Expression& { return *argument_; }
-  auto argument() -> Expression& { return *argument_; }
-
-  auto bindings() -> const Bindings& { return bindings_; }
-
-  // Can only be called once, during typechecking.
-  void set_bindings(Bindings bindings) {
-    CARBON_CHECK(bindings_.args().empty() && bindings_.witnesses().empty());
-    bindings_ = std::move(bindings);
-  }
-
-  auto deduced_args() const -> const BindingMap& { return bindings_.args(); }
-
-  // Maps each of `function`'s impl bindings to a witness.
-  // Should not be called before typechecking, or if `function` is not
-  // a generic function.
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_.witnesses();
-  }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_argument(Nonnull<Expression*> argument) { argument_ = argument; }
-
- private:
-  Nonnull<Expression*> function_;
-  Nonnull<Expression*> argument_;
-  Bindings bindings_;
-};
-
-class FunctionTypeLiteral : public ConstantValueLiteral {
- public:
-  explicit FunctionTypeLiteral(SourceLocation source_loc,
-                               Nonnull<TupleLiteral*> parameter,
-                               Nonnull<Expression*> return_type)
-      : ConstantValueLiteral(AstNodeKind::FunctionTypeLiteral, source_loc),
-        parameter_(parameter),
-        return_type_(return_type) {}
-
-  explicit FunctionTypeLiteral(CloneContext& context,
-                               const FunctionTypeLiteral& other)
-      : ConstantValueLiteral(context, other),
-        parameter_(context.Clone(other.parameter_)),
-        return_type_(context.Clone(other.return_type_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromFunctionTypeLiteral(node->kind());
-  }
-
-  auto parameter() const -> const TupleLiteral& { return *parameter_; }
-  auto parameter() -> TupleLiteral& { return *parameter_; }
-  auto return_type() const -> const Expression& { return *return_type_; }
-  auto return_type() -> Expression& { return *return_type_; }
-
- private:
-  Nonnull<TupleLiteral*> parameter_;
-  Nonnull<Expression*> return_type_;
-};
-
-class BoolTypeLiteral : public Expression {
- public:
-  explicit BoolTypeLiteral(SourceLocation source_loc)
-      : Expression(AstNodeKind::BoolTypeLiteral, source_loc) {}
-
-  explicit BoolTypeLiteral(CloneContext& context, const BoolTypeLiteral& other)
-      : Expression(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBoolTypeLiteral(node->kind());
-  }
-};
-
-class IntTypeLiteral : public Expression {
- public:
-  explicit IntTypeLiteral(SourceLocation source_loc)
-      : Expression(AstNodeKind::IntTypeLiteral, source_loc) {}
-
-  explicit IntTypeLiteral(CloneContext& context, const IntTypeLiteral& other)
-      : Expression(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIntTypeLiteral(node->kind());
-  }
-};
-
-class TypeTypeLiteral : public Expression {
- public:
-  explicit TypeTypeLiteral(SourceLocation source_loc)
-      : Expression(AstNodeKind::TypeTypeLiteral, source_loc) {}
-
-  explicit TypeTypeLiteral(CloneContext& context, const TypeTypeLiteral& other)
-      : Expression(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromTypeTypeLiteral(node->kind());
-  }
-};
-
-// A literal value. This is used in desugaring, and can't be expressed in
-// source syntax.
-class ValueLiteral : public ConstantValueLiteral {
- public:
-  // Value literals are created by type-checking, and so are created with their
-  // type and value category already known.
-  ValueLiteral(SourceLocation source_loc, Nonnull<const Value*> value,
-               Nonnull<const Value*> type,
-               ExpressionCategory expression_category)
-      : ConstantValueLiteral(AstNodeKind::ValueLiteral, source_loc, value) {
-    set_static_type(type);
-    set_expression_category(expression_category);
-  }
-
-  explicit ValueLiteral(CloneContext& context, const ValueLiteral& other)
-      : ConstantValueLiteral(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromValueLiteral(node->kind());
-  }
-};
-
-class IntrinsicExpression : public RewritableMixin<Expression> {
- public:
-  enum class Intrinsic {
-    Print,
-    Alloc,
-    Dealloc,
-    PrintAllocs,
-    Rand,
-    ImplicitAs,
-    ImplicitAsConvert,
-    IntEq,
-    StrEq,
-    StrCompare,
-    IntCompare,
-    IntBitAnd,
-    IntBitOr,
-    IntBitXor,
-    IntBitComplement,
-    IntLeftShift,
-    IntRightShift,
-    Assert,
-  };
-
-  // Returns the enumerator corresponding to the intrinsic named `name`,
-  // or raises a fatal compile error if there is no such enumerator.
-  static auto FindIntrinsic(std::string_view name, SourceLocation source_loc)
-      -> ErrorOr<Intrinsic>;
-
-  explicit IntrinsicExpression(Intrinsic intrinsic, Nonnull<TupleLiteral*> args,
-                               SourceLocation source_loc)
-      : RewritableMixin(AstNodeKind::IntrinsicExpression, source_loc),
-        intrinsic_(intrinsic),
-        args_(args) {}
-
-  explicit IntrinsicExpression(CloneContext& context,
-                               const IntrinsicExpression& other)
-      : RewritableMixin(context, other),
-        intrinsic_(other.intrinsic_),
-        args_(context.Clone(other.args_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIntrinsicExpression(node->kind());
-  }
-
-  auto intrinsic() const -> Intrinsic { return intrinsic_; }
-  auto name() const -> std::string_view;
-  auto args() const -> const TupleLiteral& { return *args_; }
-  auto args() -> TupleLiteral& { return *args_; }
-
- private:
-  Intrinsic intrinsic_;
-  Nonnull<TupleLiteral*> args_;
-};
-
-class IfExpression : public Expression {
- public:
-  explicit IfExpression(SourceLocation source_loc,
-                        Nonnull<Expression*> condition,
-                        Nonnull<Expression*> then_expression,
-                        Nonnull<Expression*> else_expression)
-      : Expression(AstNodeKind::IfExpression, source_loc),
-        condition_(condition),
-        then_expression_(then_expression),
-        else_expression_(else_expression) {}
-
-  explicit IfExpression(CloneContext& context, const IfExpression& other)
-      : Expression(context, other),
-        condition_(context.Clone(other.condition_)),
-        then_expression_(context.Clone(other.then_expression_)),
-        else_expression_(context.Clone(other.else_expression_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIfExpression(node->kind());
-  }
-
-  auto condition() const -> const Expression& { return *condition_; }
-  auto condition() -> Expression& { return *condition_; }
-
-  auto then_expression() const -> const Expression& {
-    return *then_expression_;
-  }
-  auto then_expression() -> Expression& { return *then_expression_; }
-
-  auto else_expression() const -> const Expression& {
-    return *else_expression_;
-  }
-  auto else_expression() -> Expression& { return *else_expression_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_condition(Nonnull<Expression*> condition) { condition_ = condition; }
-
- private:
-  Nonnull<Expression*> condition_;
-  Nonnull<Expression*> then_expression_;
-  Nonnull<Expression*> else_expression_;
-};
-
-// A clause appearing on the right-hand side of a `where` operator that forms a
-// more precise constraint from a more general one.
-class WhereClause : public AstNode {
- public:
-  ~WhereClause() override = 0;
-
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromWhereClause(node->kind());
-  }
-
-  auto kind() const -> WhereClauseKind {
-    return static_cast<WhereClauseKind>(root_kind());
-  }
-
- protected:
-  explicit WhereClause(WhereClauseKind kind, SourceLocation source_loc)
-      : AstNode(static_cast<AstNodeKind>(kind), source_loc) {}
-
-  explicit WhereClause(CloneContext& context, const WhereClause& other)
-      : AstNode(context, other) {}
-};
-
-// An `impls` where clause.
-//
-// For example, `ConstraintA where .Type impls ConstraintB` requires that the
-// associated type `.Type` implements the constraint `ConstraintB`.
-class ImplsWhereClause : public WhereClause {
- public:
-  explicit ImplsWhereClause(SourceLocation source_loc,
-                            Nonnull<Expression*> type,
-                            Nonnull<Expression*> constraint)
-      : WhereClause(WhereClauseKind::ImplsWhereClause, source_loc),
-        type_(type),
-        constraint_(constraint) {}
-
-  explicit ImplsWhereClause(CloneContext& context,
-                            const ImplsWhereClause& other)
-      : WhereClause(context, other),
-        type_(context.Clone(other.type_)),
-        constraint_(context.Clone(other.constraint_)) {}
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromImplsWhereClause(node->kind());
-  }
-
-  auto type() const -> const Expression& { return *type_; }
-  auto type() -> Expression& { return *type_; }
-
-  auto constraint() const -> const Expression& { return *constraint_; }
-  auto constraint() -> Expression& { return *constraint_; }
-
- private:
-  Nonnull<Expression*> type_;
-  Nonnull<Expression*> constraint_;
-};
-
-// An `==` where clause.
-//
-// For example, `Constraint where .Type == i32` requires that the associated
-// type `.Type` is `i32`.
-class EqualsWhereClause : public WhereClause {
- public:
-  explicit EqualsWhereClause(SourceLocation source_loc,
-                             Nonnull<Expression*> lhs, Nonnull<Expression*> rhs)
-      : WhereClause(WhereClauseKind::EqualsWhereClause, source_loc),
-        lhs_(lhs),
-        rhs_(rhs) {}
-
-  explicit EqualsWhereClause(CloneContext& context,
-                             const EqualsWhereClause& other)
-      : WhereClause(context, other),
-        lhs_(context.Clone(other.lhs_)),
-        rhs_(context.Clone(other.rhs_)) {}
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromEqualsWhereClause(node->kind());
-  }
-
-  auto lhs() const -> const Expression& { return *lhs_; }
-  auto lhs() -> Expression& { return *lhs_; }
-
-  auto rhs() const -> const Expression& { return *rhs_; }
-  auto rhs() -> Expression& { return *rhs_; }
-
- private:
-  Nonnull<Expression*> lhs_;
-  Nonnull<Expression*> rhs_;
-};
-
-// An `=` where clause.
-//
-// For example, `Constraint where .Type = i32` specifies that the associated
-// type `.Type` is rewritten to `i32` whenever used.
-class RewriteWhereClause : public WhereClause {
- public:
-  explicit RewriteWhereClause(SourceLocation source_loc,
-                              std::string member_name,
-                              Nonnull<Expression*> replacement)
-      : WhereClause(WhereClauseKind::RewriteWhereClause, source_loc),
-        member_name_(std::move(member_name)),
-        replacement_(replacement) {}
-
-  explicit RewriteWhereClause(CloneContext& context,
-                              const RewriteWhereClause& other)
-      : WhereClause(context, other),
-        member_name_(other.member_name_),
-        replacement_(context.Clone(other.replacement_)) {}
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromRewriteWhereClause(node->kind());
-  }
-
-  auto member_name() const -> std::string_view { return member_name_; }
-
-  auto replacement() const -> const Expression& { return *replacement_; }
-  auto replacement() -> Expression& { return *replacement_; }
-
- private:
-  std::string member_name_;
-  Nonnull<Expression*> replacement_;
-};
-
-// A `where` expression: `AddableWith(i32) where .Result == i32`.
-//
-// The first operand is rewritten to a generic binding, for example
-// `.Self:! AddableWith(i32)`, which may be used in the clauses.
-class WhereExpression : public RewritableMixin<Expression> {
- public:
-  explicit WhereExpression(SourceLocation source_loc,
-                           Nonnull<GenericBinding*> self_binding,
-                           std::vector<Nonnull<WhereClause*>> clauses)
-      : RewritableMixin(AstNodeKind::WhereExpression, source_loc),
-        self_binding_(self_binding),
-        clauses_(std::move(clauses)) {}
-
-  explicit WhereExpression(CloneContext& context, const WhereExpression& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromWhereExpression(node->kind());
-  }
-
-  auto self_binding() const -> const GenericBinding& { return *self_binding_; }
-  auto self_binding() -> GenericBinding& { return *self_binding_; }
-
-  auto enclosing_dot_self() const
-      -> std::optional<Nonnull<const GenericBinding*>> {
-    return enclosing_dot_self_;
-  }
-  // Sets the enclosing value of `.Self`. Can only be called during name
-  // resolution.
-  void set_enclosing_dot_self(Nonnull<const GenericBinding*> dot_self) {
-    CARBON_CHECK(!enclosing_dot_self_ || enclosing_dot_self_ == dot_self);
-    enclosing_dot_self_ = dot_self;
-  }
-
-  auto clauses() const -> llvm::ArrayRef<Nonnull<const WhereClause*>> {
-    return clauses_;
-  }
-  auto clauses() -> llvm::ArrayRef<Nonnull<WhereClause*>> { return clauses_; }
-
- private:
-  Nonnull<GenericBinding*> self_binding_;
-  std::vector<Nonnull<WhereClause*>> clauses_;
-  std::optional<Nonnull<const GenericBinding*>> enclosing_dot_self_;
-};
-
-// A builtin conversion to a type determined by type-checking. These are
-// created by type-checking when a type conversion is found to be necessary but
-// that conversion is implemented directly rather than by an `ImplicitAs`
-// implementation.
-class BuiltinConvertExpression : public RewritableMixin<Expression> {
- public:
-  BuiltinConvertExpression(Nonnull<Expression*> source_expression)
-      : RewritableMixin(AstNodeKind::BuiltinConvertExpression,
-                        source_expression->source_loc()),
-        source_expression_(source_expression) {}
-
-  explicit BuiltinConvertExpression(CloneContext& context,
-                                    const BuiltinConvertExpression& other)
-      : RewritableMixin(context, other),
-        source_expression_(context.Clone(other.source_expression_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBuiltinConvertExpression(node->kind());
-  }
-
-  auto source_expression() -> Nonnull<Expression*> {
-    return source_expression_;
-  }
-  auto source_expression() const -> Nonnull<const Expression*> {
-    return source_expression_;
-  }
-
- private:
-  Nonnull<Expression*> source_expression_;
-};
-
-// An expression whose semantics have not been implemented. This can be used
-// as a placeholder during development, in order to implement and test parsing
-// of a new expression syntax without having to implement its semantics.
-class UnimplementedExpression : public Expression {
- public:
-  // Constructs an UnimplementedExpression with the given label and the given
-  // children, which must all be convertible to Nonnull<AstNode*>. The label
-  // should correspond roughly to the name of the class that will eventually
-  // replace this usage of UnimplementedExpression.
-  template <typename... Children>
-  UnimplementedExpression(SourceLocation source_loc, std::string label,
-                          Children... children)
-      : Expression(AstNodeKind::UnimplementedExpression, source_loc),
-        label_(std::move(label)) {
-    AddChildren(children...);
-  }
-
-  explicit UnimplementedExpression(CloneContext& context,
-                                   const UnimplementedExpression& other)
-      : Expression(context, other),
-        label_(other.label_),
-        children_(context.Clone(other.children_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromUnimplementedExpression(node->kind());
-  }
-
-  auto label() const -> std::string_view { return label_; }
-  auto children() const -> llvm::ArrayRef<Nonnull<const AstNode*>> {
-    return children_;
-  }
-
- private:
-  void AddChildren() {}
-
-  template <typename... Children>
-  void AddChildren(Nonnull<AstNode*> child, Children... children) {
-    children_.push_back(child);
-    AddChildren(children...);
-  }
-
-  std::string label_;
-  std::vector<Nonnull<AstNode*>> children_;
-};
-
-// A literal representing a statically-sized array type.
-class ArrayTypeLiteral : public ConstantValueLiteral {
- public:
-  // Constructs an array type literal which uses the given expressions to
-  // represent the element type and size.
-  explicit ArrayTypeLiteral(
-      SourceLocation source_loc, Nonnull<Expression*> element_type_expression,
-      std::optional<Nonnull<Expression*>> size_expression = std::nullopt)
-      : ConstantValueLiteral(AstNodeKind::ArrayTypeLiteral, source_loc),
-        element_type_expression_(element_type_expression),
-        size_expression_(size_expression) {}
-
-  explicit ArrayTypeLiteral(CloneContext& context,
-                            const ArrayTypeLiteral& other)
-      : ConstantValueLiteral(context, other),
-        element_type_expression_(context.Clone(other.element_type_expression_)),
-        size_expression_(context.Clone(other.size_expression_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromArrayTypeLiteral(node->kind());
-  }
-
-  auto element_type_expression() const -> const Expression& {
-    return *element_type_expression_;
-  }
-  auto element_type_expression() -> Expression& {
-    return *element_type_expression_;
-  }
-
-  auto has_size_expression() const -> bool {
-    return size_expression_.has_value();
-  }
-
-  auto size_expression() const -> const Expression& {
-    CARBON_CHECK(size_expression_.has_value());
-    return **size_expression_;
-  }
-  auto size_expression() -> Expression& {
-    CARBON_CHECK(size_expression_.has_value());
-    return **size_expression_;
-  }
-
- private:
-  Nonnull<Expression*> element_type_expression_;
-  std::optional<Nonnull<Expression*>> size_expression_;
-};
-
-// Converts paren_contents to an Expression, interpreting the parentheses as
-// grouping if their contents permit that interpretation, or as forming a
-// tuple otherwise.
-auto ExpressionFromParenContents(
-    Nonnull<Arena*> arena, SourceLocation source_loc,
-    const ParenContents<Expression>& paren_contents) -> Nonnull<Expression*>;
-
-// Converts paren_contents to an Expression, interpreting the parentheses as
-// forming a tuple.
-auto TupleExpressionFromParenContents(
-    Nonnull<Arena*> arena, SourceLocation source_loc,
-    const ParenContents<Expression>& paren_contents) -> Nonnull<TupleLiteral*>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_EXPRESSION_H_

+ 0 - 27
explorer/ast/expression_category.h

@@ -1,27 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_
-#define CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_
-
-#include "llvm/ADT/StringRef.h"
-
-namespace Carbon {
-
-// The category of a Carbon expression indicates whether it evaluates
-// to a value, reference, or initialization.
-enum class ExpressionCategory {
-  // A "value expression" produces a value (with no associated location).
-  Value,
-  // A "reference expression" produces a location of an existing value.
-  Reference,
-  // An "initializing expression" takes a location and initialize it.
-  Initializing,
-};
-
-auto ExpressionCategoryToString(ExpressionCategory cat) -> llvm::StringRef;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_

+ 0 - 138
explorer/ast/expression_test.cpp

@@ -1,138 +0,0 @@
-// 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 "explorer/ast/expression.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <string>
-
-#include "explorer/ast/paren_contents.h"
-#include "explorer/base/arena.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-namespace {
-
-using llvm::cast;
-using testing::ElementsAre;
-using testing::IsEmpty;
-
-// Matches any `IntLiteral`.
-MATCHER(IntField, "") { return arg->kind() == ExpressionKind::IntLiteral; }
-
-static auto FakeSourceLoc(int line_num) -> SourceLocation {
-  return SourceLocation("<test>", line_num, FileKind::Main);
-}
-
-class ExpressionTest : public ::testing::Test {
- protected:
-  Arena arena;
-};
-
-TEST_F(ExpressionTest, EmptyAsExpression) {
-  ParenContents<Expression> contents = {.elements = {},
-                                        .has_trailing_comma = false};
-  Nonnull<const Expression*> expression =
-      ExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(expression->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(expression->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*expression).fields(), IsEmpty());
-}
-
-TEST_F(ExpressionTest, EmptyAsTuple) {
-  ParenContents<Expression> contents = {.elements = {},
-                                        .has_trailing_comma = false};
-  Nonnull<const Expression*> tuple =
-      TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(tuple->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(), IsEmpty());
-}
-
-TEST_F(ExpressionTest, UnaryNoCommaAsExpression) {
-  // Equivalent to a code fragment like
-  // ```
-  // (
-  //   42
-  // )
-  // ```
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-      .has_trailing_comma = false};
-
-  Nonnull<const Expression*> expression =
-      ExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(expression->source_loc(), FakeSourceLoc(2));
-  ASSERT_EQ(expression->kind(), ExpressionKind::IntLiteral);
-}
-
-TEST_F(ExpressionTest, UnaryNoCommaAsTuple) {
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-      .has_trailing_comma = false};
-
-  Nonnull<const Expression*> tuple =
-      TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(tuple->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(), ElementsAre(IntField()));
-}
-
-TEST_F(ExpressionTest, UnaryWithCommaAsExpression) {
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-      .has_trailing_comma = true};
-
-  Nonnull<const Expression*> expression =
-      ExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(expression->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(expression->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*expression).fields(),
-              ElementsAre(IntField()));
-}
-
-TEST_F(ExpressionTest, UnaryWithCommaAsTuple) {
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-      .has_trailing_comma = true};
-
-  Nonnull<const Expression*> tuple =
-      TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(tuple->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(), ElementsAre(IntField()));
-}
-
-TEST_F(ExpressionTest, BinaryAsExpression) {
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42),
-                   arena.New<IntLiteral>(FakeSourceLoc(3), 42)},
-      .has_trailing_comma = true};
-
-  Nonnull<const Expression*> expression =
-      ExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(expression->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(expression->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*expression).fields(),
-              ElementsAre(IntField(), IntField()));
-}
-
-TEST_F(ExpressionTest, BinaryAsTuple) {
-  ParenContents<Expression> contents = {
-      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42),
-                   arena.New<IntLiteral>(FakeSourceLoc(3), 42)},
-      .has_trailing_comma = true};
-
-  Nonnull<const Expression*> tuple =
-      TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  ASSERT_EQ(tuple->kind(), ExpressionKind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(),
-              ElementsAre(IntField(), IntField()));
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 18
explorer/ast/impl_binding.cpp

@@ -1,18 +0,0 @@
-// 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 "explorer/ast/impl_binding.h"
-
-#include "explorer/ast/pattern.h"
-
-namespace Carbon {
-
-ImplBinding::ImplBinding(CloneContext& context, const ImplBinding& other)
-    : AstNode(context, other),
-      type_var_(context.Remap(other.type_var_)),
-      iface_(context.Clone(other.iface_)),
-      symbolic_identity_(context.Clone(other.symbolic_identity_)),
-      original_(context.Remap(other.original_)) {}
-
-}  // namespace Carbon

+ 0 - 105
explorer/ast/impl_binding.h

@@ -1,105 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_IMPL_BINDING_H_
-#define CARBON_EXPLORER_AST_IMPL_BINDING_H_
-
-#include <map>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/expression_category.h"
-
-namespace Carbon {
-
-class Value;
-class Expression;
-class GenericBinding;
-
-// `ImplBinding` plays the role of the parameter for passing witness
-// tables to a generic. However, unlike regular parameters
-// (`BindingPattern`) there is no explicit syntax that corresponds to
-// an `ImplBinding`, so they are not created during parsing. Instances
-// of `ImplBinding` are created during type checking, when processing
-// a type parameter (a `GenericBinding`), or an `impls` requirement in
-// a `where` clause.
-class ImplBinding : public AstNode {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  ImplBinding(SourceLocation source_loc,
-              Nonnull<const GenericBinding*> type_var,
-              std::optional<Nonnull<const Value*>> iface)
-      : AstNode(AstNodeKind::ImplBinding, source_loc),
-        type_var_(type_var),
-        iface_(iface) {}
-
-  explicit ImplBinding(CloneContext& context, const ImplBinding& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromImplBinding(node->kind());
-  }
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  // The binding for the type variable.
-  auto type_var() const -> Nonnull<const GenericBinding*> { return type_var_; }
-  // The constraint being implemented.
-  // TODO: Rename this to `constraint`.
-  auto interface() const -> Nonnull<const Value*> {
-    CARBON_CHECK(iface_, "interface has not been set yet");
-    return *iface_;
-  }
-
-  // Set the interface being implemented, if not set by the constructor. Should
-  // only be called by typechecking.
-  void set_interface(Nonnull<const Value*> iface) {
-    CARBON_CHECK(!iface_, "interface set twice");
-    iface_ = iface;
-  }
-
-  // Required for the ValueNode interface
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return std::nullopt;
-  }
-  auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
-    return symbolic_identity_;
-  }
-  void set_symbolic_identity(Nonnull<const Value*> value) {
-    CARBON_CHECK(!symbolic_identity_.has_value());
-    symbolic_identity_ = value;
-  }
-
-  // These functions exist only so that an `ImplBinding` can be used as a
-  // `ValueNodeView` as a key in a `StaticScope`.
-  auto static_type() const -> const Value& {
-    CARBON_FATAL("an ImplBinding has no type");
-  }
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  // Return the original impl binding.
-  auto original() const -> Nonnull<const ImplBinding*> {
-    if (original_.has_value()) {
-      return *original_;
-    } else {
-      return this;
-    }
-  }
-
-  // Set the original impl binding.
-  void set_original(Nonnull<const ImplBinding*> orig) { original_ = orig; }
-
- private:
-  Nonnull<const GenericBinding*> type_var_;
-  std::optional<Nonnull<const Value*>> iface_;
-  std::optional<Nonnull<const Value*>> symbolic_identity_;
-  std::optional<Nonnull<const ImplBinding*>> original_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_IMPL_BINDING_H_

+ 0 - 25
explorer/ast/library_name.h

@@ -1,25 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_LIBRARY_NAME_H_
-#define CARBON_EXPLORER_AST_LIBRARY_NAME_H_
-
-#include <string>
-
-namespace Carbon {
-
-// Identifies a particular library. For example, "Geometry//Objects/FourSides"
-// will have package="Geometry" and path="Objects/FourSides".
-struct LibraryName {
-  // The library's package.
-  std::string package;
-
-  // The package-relative path of the library. This defaults to the empty
-  // string.
-  std::string path;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_LIBRARY_NAME_H_

+ 0 - 51
explorer/ast/paren_contents.h

@@ -1,51 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_PAREN_CONTENTS_H_
-#define CARBON_EXPLORER_AST_PAREN_CONTENTS_H_
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#include "explorer/base/error_builders.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-// Represents the syntactic contents of an expression or pattern delimited by
-// parentheses. In those syntaxes, parentheses can be used either for grouping
-// or for forming a tuple, depending on their context and the syntax of their
-// contents; this class helps calling code resolve that ambiguity. Since that
-// ambiguity is purely syntactic, this class should only be needed during
-// parsing.
-//
-// `Term` is the type of the syntactic grouping being built, and the type of
-// the individual syntactic units it's built from; typically it should be
-// either `Expression` or `Pattern`.
-template <typename Term>
-struct ParenContents {
-  // If this object represents a single term with no trailing comma, this
-  // method returns that term. This typically means the parentheses can be
-  // interpreted as grouping.
-  auto SingleTerm() const -> std::optional<Nonnull<Term*>>;
-
-  std::vector<Nonnull<Term*>> elements;
-  bool has_trailing_comma;
-};
-
-// Implementation details only below here.
-
-template <typename Term>
-auto ParenContents<Term>::SingleTerm() const -> std::optional<Nonnull<Term*>> {
-  if (elements.size() == 1 && !has_trailing_comma) {
-    return elements.front();
-  } else {
-    return std::nullopt;
-  }
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_PAREN_CONTENTS_H_

+ 0 - 197
explorer/ast/pattern.cpp

@@ -1,197 +0,0 @@
-// 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 "explorer/ast/pattern.h"
-
-#include <string>
-
-#include "common/ostream.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/impl_binding.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/error_builders.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-
-Pattern::~Pattern() = default;
-
-void Pattern::Print(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case PatternKind::AutoPattern:
-      out << "auto";
-      break;
-    case PatternKind::BindingPattern: {
-      const auto& binding = cast<BindingPattern>(*this);
-      out << binding.name() << ": " << binding.type();
-      break;
-    }
-    case PatternKind::GenericBinding: {
-      const auto& binding = cast<GenericBinding>(*this);
-      switch (binding.binding_kind()) {
-        case GenericBinding::BindingKind::Checked:
-          break;
-        case GenericBinding::BindingKind::Template:
-          out << "template ";
-          break;
-      }
-      out << binding.name() << ":! " << binding.type();
-      if (auto value = binding.constant_value()) {
-        out << " = " << **value;
-      }
-      break;
-    }
-    case PatternKind::TuplePattern: {
-      const auto& tuple = cast<TuplePattern>(*this);
-      out << "(";
-      llvm::ListSeparator sep;
-      for (Nonnull<const Pattern*> field : tuple.fields()) {
-        out << sep << *field;
-      }
-      out << ")";
-      break;
-    }
-    case PatternKind::AlternativePattern: {
-      const auto& alternative = cast<AlternativePattern>(*this);
-      out << alternative.choice_type() << "." << alternative.alternative_name()
-          << alternative.arguments();
-      break;
-    }
-    case PatternKind::ExpressionPattern:
-      out << cast<ExpressionPattern>(*this).expression();
-      break;
-    case PatternKind::VarPattern:
-      out << "var" << cast<VarPattern>(*this).pattern();
-      break;
-    case PatternKind::AddrPattern:
-      out << "addr" << cast<AddrPattern>(*this).binding();
-      break;
-  }
-}
-
-void Pattern::PrintID(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case PatternKind::AutoPattern:
-      out << "auto";
-      break;
-    case PatternKind::BindingPattern: {
-      const auto& binding = cast<BindingPattern>(*this);
-      out << binding.name();
-      break;
-    }
-    case PatternKind::GenericBinding: {
-      const auto& binding = cast<GenericBinding>(*this);
-      out << binding.name();
-      break;
-    }
-    case PatternKind::TuplePattern: {
-      out << "(...)";
-      break;
-    }
-    case PatternKind::AlternativePattern: {
-      const auto& alternative = cast<AlternativePattern>(*this);
-      out << alternative.choice_type() << "." << alternative.alternative_name()
-          << "(...)";
-      break;
-    }
-    case PatternKind::VarPattern:
-      out << "var ...";
-      break;
-    case PatternKind::AddrPattern:
-      out << "addr ...";
-      break;
-    case PatternKind::ExpressionPattern:
-      out << "...";
-      break;
-  }
-}
-
-auto VisitNestedPatterns(const Pattern& pattern,
-                         llvm::function_ref<bool(const Pattern&)> visitor)
-    -> bool {
-  if (!visitor(pattern)) {
-    return false;
-  }
-  switch (pattern.kind()) {
-    case PatternKind::TuplePattern:
-      for (const Pattern* field : cast<TuplePattern>(pattern).fields()) {
-        if (!VisitNestedPatterns(*field, visitor)) {
-          return false;
-        }
-      }
-      return true;
-    case PatternKind::AlternativePattern:
-      return VisitNestedPatterns(cast<AlternativePattern>(pattern).arguments(),
-                                 visitor);
-    case PatternKind::VarPattern:
-      return VisitNestedPatterns(cast<VarPattern>(pattern).pattern(), visitor);
-    case PatternKind::AddrPattern:
-      return VisitNestedPatterns(cast<AddrPattern>(pattern).binding(), visitor);
-    case PatternKind::BindingPattern:
-    case PatternKind::AutoPattern:
-    case PatternKind::ExpressionPattern:
-    case PatternKind::GenericBinding:
-      return true;
-  }
-}
-
-auto PatternFromParenContents(Nonnull<Arena*> arena, SourceLocation source_loc,
-                              const ParenContents<Pattern>& paren_contents)
-    -> Nonnull<Pattern*> {
-  std::optional<Nonnull<Pattern*>> single_term = paren_contents.SingleTerm();
-  if (single_term.has_value()) {
-    return *single_term;
-  } else {
-    return TuplePatternFromParenContents(arena, source_loc, paren_contents);
-  }
-}
-
-auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
-                                   SourceLocation source_loc,
-                                   const ParenContents<Pattern>& paren_contents)
-    -> Nonnull<TuplePattern*> {
-  return arena->New<TuplePattern>(source_loc, paren_contents.elements);
-}
-
-// Used by AlternativePattern for constructor initialization. Produces a helpful
-// error for incorrect expressions, rather than letting a default cast error
-// apply.
-auto AlternativePattern::RequireSimpleMemberAccess(
-    Nonnull<Expression*> alternative)
-    -> ErrorOr<Nonnull<SimpleMemberAccessExpression*>> {
-  if (alternative->kind() != ExpressionKind::SimpleMemberAccessExpression) {
-    return ProgramError(alternative->source_loc())
-           << "Alternative pattern must have the form of a field access.";
-  }
-  return &cast<SimpleMemberAccessExpression>(*alternative);
-}
-
-auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
-                                   const ParenContents<Expression>& contents)
-    -> ParenContents<Pattern> {
-  ParenContents<Pattern> result = {
-      .elements = {}, .has_trailing_comma = contents.has_trailing_comma};
-  for (const auto& element : contents.elements) {
-    result.elements.push_back(arena->New<ExpressionPattern>(element));
-  }
-  return result;
-}
-
-GenericBinding::GenericBinding(CloneContext& context,
-                               const GenericBinding& other)
-    : Pattern(context, other),
-      name_(other.name_),
-      type_(context.Clone(other.type_)),
-      binding_kind_(other.binding_kind_),
-      template_value_(context.Clone(other.template_value_)),
-      symbolic_identity_(context.Clone(other.symbolic_identity_)),
-      impl_binding_(context.Clone(other.impl_binding_)),
-      original_(context.Remap(other.original_)),
-      named_as_type_via_dot_self_(other.named_as_type_via_dot_self_) {}
-
-}  // namespace Carbon

+ 0 - 484
explorer/ast/pattern.h

@@ -1,484 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_PATTERN_H_
-#define CARBON_EXPLORER_AST_PATTERN_H_
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/ast_rtti.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/STLFunctionalExtras.h"
-
-namespace Carbon {
-
-class Value;
-
-// Abstract base class of all AST nodes representing patterns.
-//
-// Pattern and its derived classes support LLVM-style RTTI, including
-// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
-// class derived from Pattern must provide a `classof` operation, and
-// every concrete derived class must have a corresponding enumerator
-// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
-// details.
-class Pattern : public AstNode {
- public:
-  explicit Pattern(CloneContext& context, const Pattern& other)
-      : AstNode(context, other),
-        static_type_(context.Clone(other.static_type_)),
-        value_(context.Clone(other.value_)) {}
-
-  Pattern(const Pattern&) = delete;
-  auto operator=(const Pattern&) -> Pattern& = delete;
-
-  ~Pattern() override = 0;
-
-  void Print(llvm::raw_ostream& out) const override;
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromPattern(node->kind());
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> PatternKind {
-    return static_cast<PatternKind>(root_kind());
-  }
-
-  // The static type of this pattern. Cannot be called before typechecking.
-  auto static_type() const -> const Value& {
-    CARBON_CHECK(static_type_.has_value());
-    return **static_type_;
-  }
-  auto has_static_type() const -> bool { return static_type_.has_value(); }
-
-  // Sets the static type of this expression. Can only be called once, during
-  // typechecking.
-  void set_static_type(Nonnull<const Value*> type) {
-    CARBON_CHECK(!static_type_.has_value());
-    static_type_ = type;
-  }
-
-  // The value of this pattern. Cannot be called before typechecking.
-  // TODO: Rename to avoid confusion with BindingPattern::constant_value
-  auto value() const -> const Value& { return **value_; }
-
-  // Sets the value of this pattern. Can only be called once, during
-  // typechecking.
-  void set_value(Nonnull<const Value*> value) {
-    CARBON_CHECK(!value_, "set_value called more than once");
-    value_ = value;
-  }
-
-  // Returns whether the value has been set. Should only be called
-  // during typechecking: before typechecking it's guaranteed to be false,
-  // and after typechecking it's guaranteed to be true.
-  auto has_value() const -> bool { return value_.has_value(); }
-
-  // Determines whether the pattern has already been type-checked. Should
-  // only be used by type-checking.
-  auto is_type_checked() const -> bool {
-    return static_type_.has_value() && value_.has_value();
-  }
-
- protected:
-  // Constructs a Pattern representing syntax at the given line number.
-  // `kind` must be the enumerator corresponding to the most-derived type being
-  // constructed.
-  Pattern(AstNodeKind kind, SourceLocation source_loc)
-      : AstNode(kind, source_loc) {}
-
- private:
-  std::optional<Nonnull<const Value*>> static_type_;
-  std::optional<Nonnull<const Value*>> value_;
-};
-
-// Call the given `visitor` on all patterns nested within the given pattern,
-// including `pattern` itself, in a preorder traversal. Aborts and returns
-// `false` if `visitor` returns `false`, otherwise returns `true`.
-auto VisitNestedPatterns(const Pattern& pattern,
-                         llvm::function_ref<bool(const Pattern&)> visitor)
-    -> bool;
-inline auto VisitNestedPatterns(Pattern& pattern,
-                                llvm::function_ref<bool(Pattern&)> visitor)
-    -> bool {
-  // The non-const version is implemented in terms of the const version. The
-  // const_cast is safe because every pattern reachable through a non-const
-  // pattern is also non-const.
-  const Pattern& const_pattern = pattern;
-  return VisitNestedPatterns(const_pattern, [&](const Pattern& inner) {
-    return visitor(const_cast<Pattern&>(inner));
-  });
-}
-
-// A pattern consisting of the `auto` keyword.
-class AutoPattern : public Pattern {
- public:
-  explicit AutoPattern(SourceLocation source_loc)
-      : Pattern(AstNodeKind::AutoPattern, source_loc) {}
-
-  explicit AutoPattern(CloneContext& context, const AutoPattern& other)
-      : Pattern(context, other) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAutoPattern(node->kind());
-  }
-};
-
-class VarPattern : public Pattern {
- public:
-  explicit VarPattern(SourceLocation source_loc, Nonnull<Pattern*> pattern)
-      : Pattern(AstNodeKind::VarPattern, source_loc), pattern_(pattern) {}
-
-  explicit VarPattern(CloneContext& context, const VarPattern& other)
-      : Pattern(context, other), pattern_(context.Clone(other.pattern_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromVarPattern(node->kind());
-  }
-
-  auto pattern() const -> const Pattern& { return *pattern_; }
-  auto pattern() -> Pattern& { return *pattern_; }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Reference;
-  }
-
- private:
-  Nonnull<Pattern*> pattern_;
-};
-
-// A pattern that matches a value of a specified type, and optionally binds
-// a name to it.
-class BindingPattern : public Pattern {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  BindingPattern(SourceLocation source_loc, std::string name,
-                 Nonnull<Pattern*> type,
-                 std::optional<ExpressionCategory> expression_category)
-      : Pattern(AstNodeKind::BindingPattern, source_loc),
-        name_(std::move(name)),
-        type_(type),
-        expression_category_(expression_category) {}
-
-  explicit BindingPattern(CloneContext& context, const BindingPattern& other)
-      : Pattern(context, other),
-        name_(other.name_),
-        type_(context.Clone(other.type_)),
-        expression_category_(other.expression_category_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBindingPattern(node->kind());
-  }
-
-  // The name this pattern binds, if any. If equal to AnonymousName, indicates
-  // that this BindingPattern does not bind a name, which in turn means it
-  // should not be used as a ValueNode.
-  auto name() const -> const std::string& { return name_; }
-
-  // The pattern specifying the type of values that this pattern matches.
-  auto type() const -> const Pattern& { return *type_; }
-  auto type() -> Pattern& { return *type_; }
-
-  // Returns the value category of this pattern. Can only be called after
-  // typechecking.
-  auto expression_category() const -> ExpressionCategory {
-    return expression_category_.value();
-  }
-
-  // Returns whether the value category has been set. Should only be called
-  // during typechecking.
-  auto has_expression_category() const -> bool {
-    return expression_category_.has_value();
-  }
-
-  // Sets the value category of the variable being bound. Can only be called
-  // once during typechecking
-  void set_expression_category(ExpressionCategory vc) {
-    CARBON_CHECK(!expression_category_.has_value());
-    expression_category_ = vc;
-  }
-
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return std::nullopt;
-  }
-  auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
-    return std::nullopt;
-  }
-
- private:
-  std::string name_;
-  Nonnull<Pattern*> type_;
-  std::optional<ExpressionCategory> expression_category_;
-};
-
-class AddrPattern : public Pattern {
- public:
-  explicit AddrPattern(SourceLocation source_loc,
-                       Nonnull<BindingPattern*> binding)
-      : Pattern(AstNodeKind::AddrPattern, source_loc), binding_(binding) {}
-
-  explicit AddrPattern(CloneContext& context, const AddrPattern& other)
-      : Pattern(context, other), binding_(context.Clone(other.binding_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAddrPattern(node->kind());
-  }
-
-  auto binding() const -> const BindingPattern& { return *binding_; }
-  auto binding() -> BindingPattern& { return *binding_; }
-
- private:
-  Nonnull<BindingPattern*> binding_;
-};
-
-// A pattern that matches a tuple value field-wise.
-class TuplePattern : public Pattern {
- public:
-  TuplePattern(SourceLocation source_loc, std::vector<Nonnull<Pattern*>> fields)
-      : Pattern(AstNodeKind::TuplePattern, source_loc),
-        fields_(std::move(fields)) {}
-
-  explicit TuplePattern(CloneContext& context, const TuplePattern& other)
-      : Pattern(context, other), fields_(context.Clone(other.fields_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromTuplePattern(node->kind());
-  }
-
-  auto fields() const -> llvm::ArrayRef<Nonnull<const Pattern*>> {
-    return fields_;
-  }
-  auto fields() -> llvm::ArrayRef<Nonnull<Pattern*>> { return fields_; }
-
- private:
-  std::vector<Nonnull<Pattern*>> fields_;
-};
-
-class GenericBinding : public Pattern {
- public:
-  using ImplementsCarbonValueNode = void;
-
-  enum class BindingKind {
-    // A checked generic binding, `T:! type`.
-    Checked,
-    // A template generic binding, `template T:! type`.
-    Template,
-  };
-
-  explicit GenericBinding(SourceLocation source_loc, std::string name,
-                          Nonnull<Expression*> type, BindingKind binding_kind)
-      : Pattern(AstNodeKind::GenericBinding, source_loc),
-        name_(std::move(name)),
-        type_(type),
-        binding_kind_(binding_kind) {}
-
-  explicit GenericBinding(CloneContext& context, const GenericBinding& other);
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromGenericBinding(node->kind());
-  }
-
-  auto name() const -> const std::string& { return name_; }
-  auto type() const -> const Expression& { return *type_; }
-  auto type() -> Expression& { return *type_; }
-  auto binding_kind() const -> BindingKind { return binding_kind_; }
-
-  // The index of this binding, which is the number of bindings that are in
-  // scope at the point where this binding is declared.
-  auto index() const -> int {
-    CARBON_CHECK(index_);
-    return *index_;
-  }
-
-  // Set the index of this binding. Should be called only during type-checking.
-  void set_index(int index) {
-    CARBON_CHECK(!index_, "should only set depth and index once");
-    index_ = index;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return ExpressionCategory::Value;
-  }
-
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return template_value_;
-  }
-
-  auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
-    return symbolic_identity_;
-  }
-  void set_symbolic_identity(Nonnull<const Value*> value) {
-    CARBON_CHECK(!symbolic_identity_.has_value());
-    symbolic_identity_ = value;
-  }
-
-  void set_template_value(Nonnull<const Value*> template_value) {
-    CARBON_CHECK(binding_kind_ == BindingKind::Template);
-    template_value_ = template_value;
-  }
-  auto has_template_value() const -> bool {
-    CARBON_CHECK(binding_kind_ == BindingKind::Template);
-    return template_value_.has_value();
-  }
-
-  // The impl binding associated with this type variable.
-  auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
-    return impl_binding_;
-  }
-  // Set the impl binding.
-  void set_impl_binding(Nonnull<const ImplBinding*> binding) {
-    CARBON_CHECK(!impl_binding_.has_value());
-    impl_binding_ = binding;
-  }
-
-  // Return the original generic binding.
-  auto original() const -> Nonnull<const GenericBinding*> {
-    if (original_.has_value()) {
-      return *original_;
-    } else {
-      return this;
-    }
-  }
-  // Set the original generic binding.
-  void set_original(Nonnull<const GenericBinding*> orig) { original_ = orig; }
-
-  // Returns whether this binding has been named as a type within its own type
-  // expression via `.Self`. Set by type-checking.
-  auto named_as_type_via_dot_self() const -> bool {
-    return named_as_type_via_dot_self_;
-  }
-  // Set that this binding was named as a type within its own type expression
-  // via `.Self`. May only be called during type-checking.
-  void set_named_as_type_via_dot_self() { named_as_type_via_dot_self_ = true; }
-
- private:
-  std::string name_;
-  Nonnull<Expression*> type_;
-  BindingKind binding_kind_;
-  std::optional<int> index_;
-  std::optional<Nonnull<const Value*>> template_value_;
-  std::optional<Nonnull<const Value*>> symbolic_identity_;
-  std::optional<Nonnull<const ImplBinding*>> impl_binding_;
-  std::optional<Nonnull<const GenericBinding*>> original_;
-  bool named_as_type_via_dot_self_ = false;
-};
-
-// Converts paren_contents to a Pattern, interpreting the parentheses as
-// grouping if their contents permit that interpretation, or as forming a
-// tuple otherwise.
-auto PatternFromParenContents(Nonnull<Arena*> arena, SourceLocation source_loc,
-                              const ParenContents<Pattern>& paren_contents)
-    -> Nonnull<Pattern*>;
-
-// Converts paren_contents to a TuplePattern, interpreting the parentheses as
-// forming a tuple.
-auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
-                                   SourceLocation source_loc,
-                                   const ParenContents<Pattern>& paren_contents)
-    -> Nonnull<TuplePattern*>;
-
-// Converts `contents` to ParenContents<Pattern> by replacing each Expression
-// with an ExpressionPattern.
-auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
-                                   const ParenContents<Expression>& contents)
-    -> ParenContents<Pattern>;
-
-// A pattern that matches an alternative of a choice type.
-class AlternativePattern : public Pattern {
- public:
-  // Constructs an AlternativePattern that matches the alternative specified
-  // by `alternative`, if its arguments match `arguments`.
-  static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
-                     Nonnull<Expression*> alternative,
-                     Nonnull<TuplePattern*> arguments)
-      -> ErrorOr<Nonnull<AlternativePattern*>> {
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<SimpleMemberAccessExpression*> member_access,
-        RequireSimpleMemberAccess(alternative));
-    return arena->New<AlternativePattern>(source_loc, &member_access->object(),
-                                          member_access->member_name(),
-                                          arguments);
-  }
-
-  // Constructs an AlternativePattern that matches a value of the type
-  // specified by choice_type if it represents an alternative named
-  // alternative_name, and its arguments match `arguments`.
-  AlternativePattern(SourceLocation source_loc,
-                     Nonnull<Expression*> choice_type,
-                     std::string alternative_name,
-                     Nonnull<TuplePattern*> arguments)
-      : Pattern(AstNodeKind::AlternativePattern, source_loc),
-        choice_type_(choice_type),
-        alternative_name_(std::move(alternative_name)),
-        arguments_(arguments) {}
-
-  explicit AlternativePattern(CloneContext& context,
-                              const AlternativePattern& other)
-      : Pattern(context, other),
-        choice_type_(context.Clone(other.choice_type_)),
-        alternative_name_(other.alternative_name_),
-        arguments_(context.Clone(other.arguments_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAlternativePattern(node->kind());
-  }
-
-  auto choice_type() const -> const Expression& { return *choice_type_; }
-  auto choice_type() -> Expression& { return *choice_type_; }
-  auto alternative_name() const -> const std::string& {
-    return alternative_name_;
-  }
-  auto arguments() const -> const TuplePattern& { return *arguments_; }
-  auto arguments() -> TuplePattern& { return *arguments_; }
-
- private:
-  static auto RequireSimpleMemberAccess(Nonnull<Expression*> alternative)
-      -> ErrorOr<Nonnull<SimpleMemberAccessExpression*>>;
-
-  Nonnull<Expression*> choice_type_;
-  std::string alternative_name_;
-  Nonnull<TuplePattern*> arguments_;
-};
-
-// A pattern that matches a value if it is equal to the value of a given
-// expression.
-class ExpressionPattern : public Pattern {
- public:
-  explicit ExpressionPattern(Nonnull<Expression*> expression)
-      : Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()),
-        expression_(expression) {}
-
-  explicit ExpressionPattern(CloneContext& context,
-                             const ExpressionPattern& other)
-      : Pattern(context, other),
-        expression_(context.Clone(other.expression_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromExpressionPattern(node->kind());
-  }
-
-  auto expression() const -> const Expression& { return *expression_; }
-  auto expression() -> Expression& { return *expression_; }
-
- private:
-  Nonnull<Expression*> expression_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_PATTERN_H_

+ 0 - 132
explorer/ast/pattern_test.cpp

@@ -1,132 +0,0 @@
-// 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 "explorer/ast/pattern.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "explorer/ast/expression.h"
-#include "explorer/ast/paren_contents.h"
-#include "explorer/base/arena.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-namespace {
-
-using llvm::cast;
-using llvm::isa;
-using testing::ElementsAre;
-using testing::IsEmpty;
-
-// Matches any `AutoPattern`.
-MATCHER(AutoField, "") { return isa<AutoPattern>(*arg); }
-
-static auto FakeSourceLoc(int line_num) -> SourceLocation {
-  return SourceLocation("<test>", line_num, FileKind::Main);
-}
-
-class PatternTest : public ::testing::Test {
- protected:
-  Arena arena;
-};
-
-TEST_F(PatternTest, EmptyAsPattern) {
-  ParenContents<Pattern> contents = {.elements = {},
-                                     .has_trailing_comma = false};
-  Nonnull<const Pattern*> pattern =
-      PatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(1));
-  ASSERT_TRUE(isa<TuplePattern>(*pattern));
-  EXPECT_THAT(cast<TuplePattern>(*pattern).fields(), IsEmpty());
-}
-
-TEST_F(PatternTest, EmptyAsTuplePattern) {
-  ParenContents<Pattern> contents = {.elements = {},
-                                     .has_trailing_comma = false};
-  Nonnull<const TuplePattern*> tuple =
-      TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->fields(), IsEmpty());
-}
-
-TEST_F(PatternTest, UnaryNoCommaAsPattern) {
-  // Equivalent to a code fragment like
-  // ```
-  // (
-  //   auto
-  // )
-  // ```
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = false};
-
-  Nonnull<const Pattern*> pattern =
-      PatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(2));
-  ASSERT_TRUE(isa<AutoPattern>(*pattern));
-}
-
-TEST_F(PatternTest, UnaryNoCommaAsTuplePattern) {
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = false};
-
-  Nonnull<const TuplePattern*> tuple =
-      TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->fields(), ElementsAre(AutoField()));
-}
-
-TEST_F(PatternTest, UnaryWithCommaAsPattern) {
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = true};
-
-  Nonnull<const Pattern*> pattern =
-      PatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(1));
-  ASSERT_TRUE(isa<TuplePattern>(*pattern));
-  EXPECT_THAT(cast<TuplePattern>(*pattern).fields(), ElementsAre(AutoField()));
-}
-
-TEST_F(PatternTest, UnaryWithCommaAsTuplePattern) {
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = true};
-
-  Nonnull<const TuplePattern*> tuple =
-      TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->fields(), ElementsAre(AutoField()));
-}
-
-TEST_F(PatternTest, BinaryAsPattern) {
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2)),
-                   arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = true};
-
-  Nonnull<const Pattern*> pattern =
-      PatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(1));
-  ASSERT_TRUE(isa<TuplePattern>(*pattern));
-  EXPECT_THAT(cast<TuplePattern>(*pattern).fields(),
-              ElementsAre(AutoField(), AutoField()));
-}
-
-TEST_F(PatternTest, BinaryAsTuplePattern) {
-  ParenContents<Pattern> contents = {
-      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2)),
-                   arena.New<AutoPattern>(FakeSourceLoc(2))},
-      .has_trailing_comma = true};
-
-  Nonnull<const TuplePattern*> tuple =
-      TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
-  EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->fields(), ElementsAre(AutoField(), AutoField()));
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 106
explorer/ast/return_term.h

@@ -1,106 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_RETURN_TERM_H_
-#define CARBON_EXPLORER_AST_RETURN_TERM_H_
-
-#include <optional>
-#include <utility>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/ast/expression.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-class Value;
-
-// The syntactic representation of a function declaration's return type.
-// This syntax can take one of three forms:
-// - An _explicit_ term consists of `->` followed by a type expression.
-// - An _auto_ term consists of `-> auto`.
-// - An _omitted_ term consists of no tokens at all.
-// Each of these forms has a corresponding factory function.
-class ReturnTerm : public Printable<ReturnTerm> {
- public:
-  explicit ReturnTerm(CloneContext& context, const ReturnTerm& other)
-      : kind_(other.kind_),
-        type_expression_(context.Clone(other.type_expression_)),
-        static_type_(context.Clone(other.static_type_)),
-        source_loc_(other.source_loc_) {}
-
-  ReturnTerm(const ReturnTerm&) = default;
-  auto operator=(const ReturnTerm&) -> ReturnTerm& = default;
-
-  // Represents an omitted return term at `source_loc`.
-  static auto Omitted(SourceLocation source_loc) -> ReturnTerm {
-    return ReturnTerm(ReturnKind::Omitted, source_loc);
-  }
-
-  // Represents an auto return term at `source_loc`.
-  static auto Auto(SourceLocation source_loc) -> ReturnTerm {
-    return ReturnTerm(ReturnKind::Auto, source_loc);
-  }
-
-  // Represents an explicit return term with the given type expression.
-  static auto Explicit(Nonnull<Expression*> type_expression) -> ReturnTerm {
-    return ReturnTerm(type_expression);
-  }
-
-  // Returns true if this represents an omitted return term.
-  auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; }
-
-  // Returns true if this represents an auto return term.
-  auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; }
-
-  // If this represents an explicit return term, returns the type expression.
-  // Otherwise, returns nullopt.
-  auto type_expression() const -> std::optional<Nonnull<const Expression*>> {
-    return type_expression_;
-  }
-  auto type_expression() -> std::optional<Nonnull<Expression*>> {
-    return type_expression_;
-  }
-
-  // The static return type this term resolves to. Cannot be called before
-  // typechecking.
-  auto static_type() const -> const Value& { return **static_type_; }
-
-  // Sets the value of static_type(). Can only be called once, during
-  // typechecking.
-  void set_static_type(Nonnull<const Value*> type) {
-    CARBON_CHECK(!static_type_.has_value());
-    static_type_ = type;
-  }
-
-  auto source_loc() const -> SourceLocation { return source_loc_; }
-
-  void Print(llvm::raw_ostream& out) const;
-
- private:
-  enum class ReturnKind { Omitted, Auto, Expression };
-
-  explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc)
-      : kind_(kind), source_loc_(source_loc) {
-    CARBON_CHECK(kind != ReturnKind::Expression);
-  }
-
-  explicit ReturnTerm(Nonnull<Expression*> type_expression)
-      : kind_(ReturnKind::Expression),
-        type_expression_(type_expression),
-        source_loc_(type_expression->source_loc()) {}
-
-  ReturnKind kind_;
-  std::optional<Nonnull<Expression*>> type_expression_;
-  std::optional<Nonnull<const Value*>> static_type_;
-
-  SourceLocation source_loc_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_RETURN_TERM_H_

+ 0 - 217
explorer/ast/statement.cpp

@@ -1,217 +0,0 @@
-// 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 "explorer/ast/statement.h"
-
-#include "common/check.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/base/arena.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-
-Statement::~Statement() = default;
-
-void Statement::PrintID(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case StatementKind::Match:
-      out << "match (...) { ... }";
-      break;
-    case StatementKind::While:
-      out << "while (...) { ... }";
-      break;
-    case StatementKind::For:
-      out << "for (...) { ... }";
-      break;
-    case StatementKind::Break:
-      out << "break;";
-      break;
-    case StatementKind::Continue:
-      out << "continue;";
-      break;
-    case StatementKind::VariableDefinition: {
-      const auto& var = cast<VariableDefinition>(*this);
-      if (var.is_returned()) {
-        out << "returned ";
-      }
-      out << "var ...";
-      if (var.has_init()) {
-        out << " = ...";
-      }
-      out << ";";
-      break;
-    }
-    case StatementKind::ExpressionStatement:
-      out << "<expression>;";
-      break;
-    case StatementKind::Assign: {
-      const auto& assign = cast<Assign>(*this);
-      out << "... " << AssignOperatorToString(assign.op()) << " ...;";
-      break;
-    }
-    case StatementKind::IncrementDecrement: {
-      const auto& inc_dec = cast<IncrementDecrement>(*this);
-      out << (inc_dec.is_increment() ? "++" : "--") << "...;";
-      break;
-    }
-    case StatementKind::If: {
-      const auto& if_stmt = cast<If>(*this);
-      out << "if (...) { ... }";
-      if (if_stmt.else_block()) {
-        out << " else { ... }";
-      }
-      break;
-    }
-    case StatementKind::ReturnVar:
-      out << "return var;";
-      break;
-    case StatementKind::ReturnExpression: {
-      const auto& ret = cast<ReturnExpression>(*this);
-      if (ret.is_omitted_expression()) {
-        out << "return;";
-      } else {
-        out << "return ...;";
-      }
-      break;
-    }
-    case StatementKind::Block:
-      out << "{ ... }";
-      break;
-  }
-}
-
-void Statement::PrintIndent(int indent_num_spaces,
-                            llvm::raw_ostream& out) const {
-  out.indent(indent_num_spaces);
-
-  switch (kind()) {
-    case StatementKind::Match: {
-      const auto& match = cast<Match>(*this);
-      out << "match (" << match.expression() << ") {\n";
-      for (const auto& clause : match.clauses()) {
-        out.indent(indent_num_spaces + 2)
-            << "case " << clause.pattern() << " =>\n";
-        clause.statement().PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-    case StatementKind::While: {
-      const auto& while_stmt = cast<While>(*this);
-      out << "while (" << while_stmt.condition() << ")\n";
-      while_stmt.body().PrintIndent(indent_num_spaces, out);
-      break;
-    }
-    case StatementKind::For: {
-      const auto& for_stmt = cast<For>(*this);
-      out << "for (" << for_stmt.variable_declaration() << " in "
-          << for_stmt.loop_target() << ")\n";
-      for_stmt.body().PrintIndent(indent_num_spaces, out);
-      break;
-    }
-    case StatementKind::Break:
-      out << "break;";
-      break;
-    case StatementKind::Continue:
-      out << "continue;";
-      break;
-    case StatementKind::VariableDefinition: {
-      const auto& var = cast<VariableDefinition>(*this);
-      if (var.is_returned()) {
-        out << "returned ";
-      }
-      out << "var " << var.pattern();
-      if (var.has_init()) {
-        out << " = " << var.init();
-      }
-      out << ";";
-      break;
-    }
-    case StatementKind::ExpressionStatement:
-      out << cast<ExpressionStatement>(*this).expression() << ";";
-      break;
-    case StatementKind::Assign: {
-      const auto& assign = cast<Assign>(*this);
-      out << assign.lhs() << " " << AssignOperatorToString(assign.op()) << " "
-          << assign.rhs() << ";";
-      break;
-    }
-    case StatementKind::IncrementDecrement: {
-      const auto& inc_dec = cast<IncrementDecrement>(*this);
-      out << (inc_dec.is_increment() ? "++" : "--") << inc_dec.argument()
-          << ";";
-      break;
-    }
-    case StatementKind::If: {
-      const auto& if_stmt = cast<If>(*this);
-      out << "if (" << if_stmt.condition() << ")\n";
-      if_stmt.then_block().PrintIndent(indent_num_spaces, out);
-      if (if_stmt.else_block()) {
-        out << "\n";
-        out.indent(indent_num_spaces) << "else\n";
-        (*if_stmt.else_block())->PrintIndent(indent_num_spaces, out);
-      }
-      break;
-    }
-    case StatementKind::ReturnVar: {
-      out << "return var;";
-      break;
-    }
-    case StatementKind::ReturnExpression: {
-      const auto& ret = cast<ReturnExpression>(*this);
-      if (ret.is_omitted_expression()) {
-        out << "return;";
-      } else {
-        out << "return " << ret.expression() << ";";
-      }
-      break;
-    }
-    case StatementKind::Block: {
-      const auto& block = cast<Block>(*this);
-      const auto statements = block.statements();
-      out << "{\n";
-      for (const auto* statement : statements) {
-        statement->PrintIndent(indent_num_spaces + 2, out);
-        out << "\n";
-      }
-      out.indent(indent_num_spaces) << "}";
-      break;
-    }
-  }
-}
-
-auto AssignOperatorToString(AssignOperator op) -> std::string_view {
-  switch (op) {
-    case AssignOperator::Plain:
-      return "=";
-    case AssignOperator::Add:
-      return "+=";
-    case AssignOperator::Div:
-      return "/=";
-    case AssignOperator::Mul:
-      return "*=";
-    case AssignOperator::Mod:
-      return "%=";
-    case AssignOperator::Sub:
-      return "-=";
-    case AssignOperator::And:
-      return "&=";
-    case AssignOperator::Or:
-      return "|=";
-    case AssignOperator::Xor:
-      return "^=";
-    case AssignOperator::ShiftLeft:
-      return "<<=";
-    case AssignOperator::ShiftRight:
-      return ">>=";
-  }
-}
-
-Return::Return(CloneContext& context, const Return& other)
-    : Statement(context, other), function_(context.Remap(other.function_)) {}
-
-}  // namespace Carbon

+ 0 - 587
explorer/ast/statement.h

@@ -1,587 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_STATEMENT_H_
-#define CARBON_EXPLORER_AST_STATEMENT_H_
-
-#include <utility>
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/return_term.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class CallableDeclaration;
-
-class Statement : public AstNode {
- public:
-  ~Statement() override = 0;
-
-  void Print(llvm::raw_ostream& out) const override { PrintIndent(0, out); }
-  void PrintID(llvm::raw_ostream& out) const override;
-
-  void PrintIndent(int indent_num_spaces, llvm::raw_ostream& out) const;
-
-  static auto classof(const AstNode* node) {
-    return InheritsFromStatement(node->kind());
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> StatementKind {
-    return static_cast<StatementKind>(root_kind());
-  }
-
- protected:
-  explicit Statement(AstNodeKind kind, SourceLocation source_loc)
-      : AstNode(kind, source_loc) {}
-
-  explicit Statement(CloneContext& context, const Statement& other)
-      : AstNode(context, other) {}
-};
-
-class Block : public Statement {
- public:
-  Block(SourceLocation source_loc, std::vector<Nonnull<Statement*>> statements)
-      : Statement(AstNodeKind::Block, source_loc),
-        statements_(std::move(statements)) {}
-
-  explicit Block(CloneContext& context, const Block& other)
-      : Statement(context, other),
-        statements_(context.Clone(other.statements_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBlock(node->kind());
-  }
-
-  auto statements() const -> llvm::ArrayRef<Nonnull<const Statement*>> {
-    return statements_;
-  }
-  auto statements() -> llvm::MutableArrayRef<Nonnull<Statement*>> {
-    return statements_;
-  }
-
- private:
-  std::vector<Nonnull<Statement*>> statements_;
-};
-
-class ExpressionStatement : public Statement {
- public:
-  ExpressionStatement(SourceLocation source_loc,
-                      Nonnull<Expression*> expression)
-      : Statement(AstNodeKind::ExpressionStatement, source_loc),
-        expression_(expression) {}
-
-  explicit ExpressionStatement(CloneContext& context,
-                               const ExpressionStatement& other)
-      : Statement(context, other),
-        expression_(context.Clone(other.expression_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromExpressionStatement(node->kind());
-  }
-
-  auto expression() const -> const Expression& { return *expression_; }
-  auto expression() -> Expression& { return *expression_; }
-
- private:
-  Nonnull<Expression*> expression_;
-};
-
-enum class AssignOperator {
-  Plain,
-  Add,
-  Div,
-  Mul,
-  Mod,
-  Sub,
-  And,
-  Or,
-  Xor,
-  ShiftLeft,
-  ShiftRight,
-};
-
-// Returns the spelling of this assignment operator token.
-auto AssignOperatorToString(AssignOperator op) -> std::string_view;
-
-class Assign : public Statement {
- public:
-  Assign(SourceLocation source_loc, Nonnull<Expression*> lhs, AssignOperator op,
-         Nonnull<Expression*> rhs)
-      : Statement(AstNodeKind::Assign, source_loc),
-        lhs_(lhs),
-        rhs_(rhs),
-        op_(op) {}
-
-  explicit Assign(CloneContext& context, const Assign& other)
-      : Statement(context, other),
-        lhs_(context.Clone(other.lhs_)),
-        rhs_(context.Clone(other.rhs_)),
-        op_(other.op_),
-        rewritten_form_(context.Clone(other.rewritten_form_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromAssign(node->kind());
-  }
-
-  auto lhs() const -> const Expression& { return *lhs_; }
-  auto lhs() -> Expression& { return *lhs_; }
-  auto rhs() const -> const Expression& { return *rhs_; }
-  auto rhs() -> Expression& { return *rhs_; }
-
-  auto op() const -> AssignOperator { return op_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_rhs(Nonnull<Expression*> rhs) { rhs_ = rhs; }
-
-  // Set the rewritten form of this statement. Can only be called during type
-  // checking.
-  auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
-    rewritten_form_ = rewritten_form;
-  }
-
-  // Get the rewritten form of this statement. A rewritten form is used when
-  // the statement is rewritten as a function call on an interface. A
-  // rewritten form is not used when providing built-in operator semantics for
-  // a plain assignment.
-  auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
-    return rewritten_form_;
-  }
-
- private:
-  Nonnull<Expression*> lhs_;
-  Nonnull<Expression*> rhs_;
-  AssignOperator op_;
-  std::optional<Nonnull<const Expression*>> rewritten_form_;
-};
-
-class IncrementDecrement : public Statement {
- public:
-  IncrementDecrement(SourceLocation source_loc, Nonnull<Expression*> argument,
-                     bool is_increment)
-      : Statement(AstNodeKind::IncrementDecrement, source_loc),
-        argument_(argument),
-        is_increment_(is_increment) {}
-
-  explicit IncrementDecrement(CloneContext& context,
-                              const IncrementDecrement& other)
-      : Statement(context, other),
-        argument_(context.Clone(other.argument_)),
-        is_increment_(other.is_increment_),
-        rewritten_form_(context.Clone(other.rewritten_form_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIncrementDecrement(node->kind());
-  }
-
-  auto argument() const -> const Expression& { return *argument_; }
-  auto argument() -> Expression& { return *argument_; }
-
-  auto is_increment() const -> bool { return is_increment_; }
-
-  // Set the rewritten form of this statement. Can only be called during type
-  // checking.
-  auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
-    CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
-    rewritten_form_ = rewritten_form;
-  }
-
-  // Get the rewritten form of this statement.
-  auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
-    return rewritten_form_;
-  }
-
- private:
-  Nonnull<Expression*> argument_;
-  bool is_increment_;
-  std::optional<Nonnull<const Expression*>> rewritten_form_;
-};
-
-class VariableDefinition : public Statement {
- public:
-  enum DefinitionType {
-    Var,
-    Returned,
-  };
-
-  VariableDefinition(SourceLocation source_loc, Nonnull<Pattern*> pattern,
-                     std::optional<Nonnull<Expression*>> init,
-                     ExpressionCategory expression_category,
-                     DefinitionType def_type)
-      : Statement(AstNodeKind::VariableDefinition, source_loc),
-        pattern_(pattern),
-        init_(init),
-        expression_category_(expression_category),
-        def_type_(def_type) {}
-
-  explicit VariableDefinition(CloneContext& context,
-                              const VariableDefinition& other)
-      : Statement(context, other),
-        pattern_(context.Clone(other.pattern_)),
-        init_(context.Clone(other.init_)),
-        expression_category_(other.expression_category_),
-        def_type_(other.def_type_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromVariableDefinition(node->kind());
-  }
-
-  auto pattern() const -> const Pattern& { return *pattern_; }
-  auto pattern() -> Pattern& { return *pattern_; }
-
-  auto init() const -> const Expression& {
-    CARBON_CHECK(has_init());
-    return **init_;
-  }
-  auto init() -> Expression& {
-    CARBON_CHECK(has_init());
-    return **init_;
-  }
-
-  auto has_init() const -> bool { return init_.has_value(); }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_init(Nonnull<Expression*> init) {
-    CARBON_CHECK(has_init(), "should not add a new initializer");
-    init_ = init;
-  }
-
-  auto expression_category() const -> ExpressionCategory {
-    return expression_category_;
-  }
-
-  auto is_returned() const -> bool { return def_type_ == Returned; }
-
- private:
-  Nonnull<Pattern*> pattern_;
-  std::optional<Nonnull<Expression*>> init_;
-  ExpressionCategory expression_category_;
-  const DefinitionType def_type_;
-};
-
-class If : public Statement {
- public:
-  If(SourceLocation source_loc, Nonnull<Expression*> condition,
-     Nonnull<Block*> then_block, std::optional<Nonnull<Block*>> else_block)
-      : Statement(AstNodeKind::If, source_loc),
-        condition_(condition),
-        then_block_(then_block),
-        else_block_(else_block) {}
-
-  explicit If(CloneContext& context, const If& other)
-      : Statement(context, other),
-        condition_(context.Clone(other.condition_)),
-        then_block_(context.Clone(other.then_block_)),
-        else_block_(context.Clone(other.else_block_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromIf(node->kind());
-  }
-
-  auto condition() const -> const Expression& { return *condition_; }
-  auto condition() -> Expression& { return *condition_; }
-  auto then_block() const -> const Block& { return *then_block_; }
-  auto then_block() -> Block& { return *then_block_; }
-  auto else_block() const -> std::optional<Nonnull<const Block*>> {
-    return else_block_;
-  }
-  auto else_block() -> std::optional<Nonnull<Block*>> { return else_block_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_condition(Nonnull<Expression*> condition) { condition_ = condition; }
-
- private:
-  Nonnull<Expression*> condition_;
-  Nonnull<Block*> then_block_;
-  std::optional<Nonnull<Block*>> else_block_;
-};
-
-class Return : public Statement {
- public:
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromReturn(node->kind());
-  }
-
-  // The AST node representing the function body this statement returns from.
-  // Can only be called after ResolveControlFlow has visited this node.
-  //
-  // Note that this function does not represent an edge in the tree
-  // structure of the AST: the return value is not a child of this node,
-  // but an ancestor.
-  auto function() const -> const CallableDeclaration& { return **function_; }
-  auto function() -> CallableDeclaration& { return **function_; }
-
-  // Can only be called once, by ResolveControlFlow.
-  void set_function(Nonnull<CallableDeclaration*> function) {
-    CARBON_CHECK(!function_.has_value());
-    function_ = function;
-  }
-
- protected:
-  Return(AstNodeKind node_kind, SourceLocation source_loc)
-      : Statement(node_kind, source_loc) {}
-
-  explicit Return(CloneContext& context, const Return& other);
-
- private:
-  std::optional<Nonnull<CallableDeclaration*>> function_;
-};
-
-class ReturnVar : public Return {
- public:
-  explicit ReturnVar(SourceLocation source_loc)
-      : Return(AstNodeKind::ReturnVar, source_loc) {}
-
-  explicit ReturnVar(CloneContext& context, const ReturnVar& other)
-      : Return(context, other), value_node_(context.Clone(other.value_node_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromReturnVar(node->kind());
-  }
-
-  // Returns the value node of the BindingPattern of the returned var
-  // definition. Cannot be called before name resolution.
-  auto value_node() const -> const ValueNodeView& { return *value_node_; }
-
-  // Can only be called once, by ResolveNames.
-  void set_value_node(ValueNodeView value_node) {
-    CARBON_CHECK(!value_node_.has_value());
-    value_node_ = value_node;
-  }
-
- private:
-  // The value node of the BindingPattern of the returned var definition.
-  std::optional<ValueNodeView> value_node_;
-};
-
-class ReturnExpression : public Return {
- public:
-  ReturnExpression(Nonnull<Arena*> arena, SourceLocation source_loc)
-      : ReturnExpression(source_loc, arena->New<TupleLiteral>(source_loc),
-                         true) {}
-  ReturnExpression(SourceLocation source_loc, Nonnull<Expression*> expression,
-                   bool is_omitted_expression)
-      : Return(AstNodeKind::ReturnExpression, source_loc),
-        expression_(expression),
-        is_omitted_expression_(is_omitted_expression) {}
-
-  explicit ReturnExpression(CloneContext& context,
-                            const ReturnExpression& other)
-      : Return(context, other),
-        expression_(context.Clone(other.expression_)),
-        is_omitted_expression_(other.is_omitted_expression_) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromReturnExpression(node->kind());
-  }
-
-  auto expression() const -> const Expression& { return *expression_; }
-  auto expression() -> Expression& { return *expression_; }
-  auto is_omitted_expression() const -> bool { return is_omitted_expression_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_expression(Nonnull<Expression*> expression) {
-    expression_ = expression;
-  }
-
- private:
-  Nonnull<Expression*> expression_;
-  bool is_omitted_expression_;
-};
-
-class While : public Statement {
- public:
-  While(SourceLocation source_loc, Nonnull<Expression*> condition,
-        Nonnull<Block*> body)
-      : Statement(AstNodeKind::While, source_loc),
-        condition_(condition),
-        body_(body) {}
-
-  explicit While(CloneContext& context, const While& other)
-      : Statement(context, other),
-        condition_(context.Clone(other.condition_)),
-        body_(context.Clone(other.body_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromWhile(node->kind());
-  }
-
-  auto condition() const -> const Expression& { return *condition_; }
-  auto condition() -> Expression& { return *condition_; }
-  auto body() const -> const Block& { return *body_; }
-  auto body() -> Block& { return *body_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_condition(Nonnull<Expression*> condition) { condition_ = condition; }
-
- private:
-  Nonnull<Expression*> condition_;
-  Nonnull<Block*> body_;
-};
-
-class For : public Statement {
- public:
-  For(SourceLocation source_loc, Nonnull<BindingPattern*> variable_declaration,
-      Nonnull<Expression*> loop_target, Nonnull<Block*> body)
-      : Statement(AstNodeKind::For, source_loc),
-        variable_declaration_(variable_declaration),
-        loop_target_(loop_target),
-        body_(body) {}
-
-  explicit For(CloneContext& context, const For& other)
-      : Statement(context, other),
-        variable_declaration_(context.Clone(other.variable_declaration_)),
-        loop_target_(context.Clone(other.loop_target_)),
-        body_(context.Clone(other.body_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromFor(node->kind());
-  }
-
-  auto variable_declaration() const -> const BindingPattern& {
-    return *variable_declaration_;
-  }
-  auto variable_declaration() -> BindingPattern& {
-    return *variable_declaration_;
-  }
-
-  auto loop_target() const -> const Expression& { return *loop_target_; }
-  auto loop_target() -> Expression& { return *loop_target_; }
-
-  auto body() const -> const Block& { return *body_; }
-  auto body() -> Block& { return *body_; }
-
- private:
-  Nonnull<BindingPattern*> variable_declaration_;
-  Nonnull<Expression*> loop_target_;
-  Nonnull<Block*> body_;
-};
-
-class Break : public Statement {
- public:
-  explicit Break(SourceLocation source_loc)
-      : Statement(AstNodeKind::Break, source_loc) {}
-
-  explicit Break(CloneContext& context, const Break& other)
-      : Statement(context, other), loop_(context.Clone(other.loop_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromBreak(node->kind());
-  }
-
-  // The AST node representing the loop this statement breaks out of.
-  // Can only be called after ResolveControlFlow has visited this node.
-  //
-  // Note that this function does not represent an edge in the tree
-  // structure of the AST: the return value is not a child of this node,
-  // but an ancestor.
-  auto loop() const -> const Statement& { return **loop_; }
-
-  // Can only be called once, by ResolveControlFlow.
-  void set_loop(Nonnull<const Statement*> loop) {
-    CARBON_CHECK(!loop_.has_value());
-    loop_ = loop;
-  }
-
- private:
-  std::optional<Nonnull<const Statement*>> loop_;
-};
-
-class Continue : public Statement {
- public:
-  explicit Continue(SourceLocation source_loc)
-      : Statement(AstNodeKind::Continue, source_loc) {}
-
-  explicit Continue(CloneContext& context, const Continue& other)
-      : Statement(context, other), loop_(context.Clone(other.loop_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromContinue(node->kind());
-  }
-
-  // The AST node representing the loop this statement continues.
-  // Can only be called after ResolveControlFlow has visited this node.
-  //
-  // Note that this function does not represent an edge in the tree
-  // structure of the AST: the return value is not a child of this node,
-  // but an ancestor.
-  auto loop() const -> const Statement& { return **loop_; }
-
-  // Can only be called once, by ResolveControlFlow.
-  void set_loop(Nonnull<const Statement*> loop) {
-    CARBON_CHECK(!loop_.has_value());
-    loop_ = loop;
-  }
-
- private:
-  std::optional<Nonnull<const Statement*>> loop_;
-};
-
-class Match : public Statement {
- public:
-  class Clause {
-   public:
-    explicit Clause(Nonnull<Pattern*> pattern, Nonnull<Statement*> statement)
-        : pattern_(pattern), statement_(statement) {}
-
-    explicit Clause(CloneContext& context, const Clause& other)
-        : pattern_(context.Clone(other.pattern_)),
-          statement_(context.Clone(other.statement_)) {}
-
-    auto pattern() const -> const Pattern& { return *pattern_; }
-    auto pattern() -> Pattern& { return *pattern_; }
-    auto statement() const -> const Statement& { return *statement_; }
-    auto statement() -> Statement& { return *statement_; }
-
-   private:
-    Nonnull<Pattern*> pattern_;
-    Nonnull<Statement*> statement_;
-  };
-
-  Match(SourceLocation source_loc, Nonnull<Expression*> expression,
-        std::vector<Clause> clauses)
-      : Statement(AstNodeKind::Match, source_loc),
-        expression_(expression),
-        clauses_(std::move(clauses)) {}
-
-  explicit Match(CloneContext& context, const Match& other)
-      : Statement(context, other),
-        expression_(context.Clone(other.expression_)),
-        clauses_(context.Clone(other.clauses_)) {}
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMatch(node->kind());
-  }
-
-  auto expression() const -> const Expression& { return *expression_; }
-  auto expression() -> Expression& { return *expression_; }
-  auto clauses() const -> llvm::ArrayRef<Clause> { return clauses_; }
-  auto clauses() -> llvm::MutableArrayRef<Clause> { return clauses_; }
-
-  // Can only be called by type-checking, if a conversion was required.
-  void set_expression(Nonnull<Expression*> expression) {
-    expression_ = expression;
-  }
-
- private:
-  Nonnull<Expression*> expression_;
-  std::vector<Clause> clauses_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_STATEMENT_H_

+ 0 - 173
explorer/ast/static_scope.cpp

@@ -1,173 +0,0 @@
-// 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 "explorer/ast/static_scope.h"
-
-#include <optional>
-
-#include "common/ostream.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/print_as_id.h"
-#include "llvm/ADT/ScopeExit.h"
-
-namespace Carbon {
-
-auto StaticScope::Add(std::string_view name, ValueNodeView entity,
-                      NameStatus status) -> ErrorOr<Success> {
-  auto [it, inserted] = declared_names_.insert({name, {entity, status}});
-  if (!inserted) {
-    if (it->second.entity != entity) {
-      return ProgramError(entity.base().source_loc())
-             << "Duplicate name `" << name << "` also found at "
-             << it->second.entity.base().source_loc();
-    }
-    if (static_cast<int>(status) > static_cast<int>(it->second.status)) {
-      it->second.status = status;
-    }
-  } else {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result()
-          << "declared `" << name << "` as `" << entity << "` in `"
-          << PrintAsID(*this) << "` (" << entity.base().source_loc() << ")\n";
-    }
-  }
-  return Success();
-}
-
-template <typename Action>
-void StaticScope::PrintCommon(Action action) const {
-  if (ast_node_) {
-    action(ast_node_.value());
-  } else {
-    *trace_stream_ << "package";
-  }
-}
-
-void StaticScope::Print(llvm::raw_ostream& out) const {
-  PrintCommon([&out](auto node) { node->Print(out); });
-}
-
-void StaticScope::PrintID(llvm::raw_ostream& out) const {
-  PrintCommon([&out](auto node) { node->PrintID(out); });
-}
-
-void StaticScope::MarkDeclared(std::string_view name) {
-  auto it = declared_names_.find(name);
-  CARBON_CHECK(it != declared_names_.end(), "{0} not found", name);
-  if (it->second.status == NameStatus::KnownButNotDeclared) {
-    it->second.status = NameStatus::DeclaredButNotUsable;
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result()
-          << "marked `" << name << "` declared but not usable in `"
-          << PrintAsID(*this) << "`\n";
-    }
-  }
-}
-
-void StaticScope::MarkUsable(std::string_view name) {
-  auto it = declared_names_.find(name);
-  CARBON_CHECK(it != declared_names_.end(), "{0} not found", name);
-  it->second.status = NameStatus::Usable;
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result()
-        << "marked `" << name << "` usable in `" << PrintAsID(*this) << "`\n";
-  }
-}
-
-auto StaticScope::Resolve(std::string_view name,
-                          SourceLocation source_loc) const
-    -> ErrorOr<ValueNodeView> {
-  CARBON_ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
-                          TryResolve(name, source_loc));
-  if (!result) {
-    return ProgramError(source_loc) << "could not resolve '" << name << "'";
-  }
-  return *result;
-}
-
-auto StaticScope::ResolveHere(std::optional<ValueNodeView> this_scope,
-                              std::string_view name, SourceLocation source_loc,
-                              bool allow_undeclared) const
-    -> ErrorOr<ValueNodeView> {
-  CARBON_ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
-                          TryResolveHere(name, source_loc, allow_undeclared));
-  if (!result) {
-    if (this_scope) {
-      return ProgramError(source_loc)
-             << "name '" << name << "' has not been declared in "
-             << PrintAsID(this_scope->base());
-    } else {
-      return ProgramError(source_loc)
-             << "name '" << name << "' has not been declared in this scope";
-    }
-  }
-  return *result;
-}
-
-auto StaticScope::TryResolve(std::string_view name,
-                             SourceLocation source_loc) const
-    -> ErrorOr<std::optional<ValueNodeView>> {
-  for (const StaticScope* scope = this; scope;
-       scope = scope->parent_scope_.value_or(nullptr)) {
-    CARBON_ASSIGN_OR_RETURN(
-        std::optional<ValueNodeView> value,
-        scope->TryResolveHere(name, source_loc, /*allow_undeclared=*/false));
-    if (value) {
-      return value;
-    }
-  }
-  return {std::nullopt};
-}
-
-auto StaticScope::TryResolveHere(std::string_view name,
-                                 SourceLocation source_loc,
-                                 bool allow_undeclared) const
-    -> ErrorOr<std::optional<ValueNodeView>> {
-  auto it = declared_names_.find(name);
-  if (it == declared_names_.end()) {
-    return {std::nullopt};
-  }
-
-  auto exit_scope_function = llvm::make_scope_exit([&]() {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result()
-          << "resolved `" << name << "` as `" << it->second.entity << "` in `"
-          << PrintAsID(*this) << "` (" << source_loc << ")\n";
-    }
-  });
-
-  if (allow_undeclared || it->second.status == NameStatus::Usable) {
-    return {it->second.entity};
-  }
-  return ProgramError(source_loc)
-         << "'" << name
-         << (it->second.status == NameStatus::KnownButNotDeclared
-                 ? "' has not been declared yet"
-                 : "' is not usable until after it has been completely "
-                   "declared");
-}
-
-auto StaticScope::AddReturnedVar(ValueNodeView returned_var_def_view)
-    -> ErrorOr<Success> {
-  std::optional<ValueNodeView> resolved_returned_var = ResolveReturned();
-  if (resolved_returned_var.has_value()) {
-    return ProgramError(returned_var_def_view.base().source_loc())
-           << "Duplicate definition of returned var also found at "
-           << resolved_returned_var->base().source_loc();
-  }
-  returned_var_def_view_ = std::move(returned_var_def_view);
-  return Success();
-}
-
-auto StaticScope::ResolveReturned() const -> std::optional<ValueNodeView> {
-  for (const StaticScope* scope = this; scope;
-       scope = scope->parent_scope_.value_or(nullptr)) {
-    if (scope->returned_var_def_view_.has_value()) {
-      return scope->returned_var_def_view_;
-    }
-  }
-  return std::nullopt;
-}
-
-}  // namespace Carbon

+ 0 - 127
explorer/ast/static_scope.h

@@ -1,127 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_STATIC_SCOPE_H_
-#define CARBON_EXPLORER_AST_STATIC_SCOPE_H_
-
-#include <string>
-#include <string_view>
-
-#include "common/error.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-#include "explorer/base/trace_stream.h"
-#include "llvm/ADT/StringMap.h"
-
-namespace Carbon {
-
-// Maps the names visible in a given scope to the entities they name.
-// A scope may have parent scopes, whose names will also be visible in the
-// child scope.
-class StaticScope {
- public:
-  // The status of a name. Later enumerators with higher values correspond to
-  // more completely declared names.
-  enum class NameStatus {
-    // The name is known to exist in this scope, and any lookups finding it
-    // should be rejected because it's not declared yet.
-    KnownButNotDeclared,
-    // We've started processing a declaration of this name, but it's not yet
-    // fully declared, so any lookups finding it should be rejected.
-    DeclaredButNotUsable,
-    // The name is usable in this context.
-    Usable,
-  };
-
-  // Construct a root scope.
-  explicit StaticScope(Nonnull<TraceStream*> trace_stream)
-      : ast_node_(std::nullopt), trace_stream_(trace_stream) {}
-
-  // Construct a scope that is nested within the given scope.
-  explicit StaticScope(Nonnull<const StaticScope*> parent,
-                       std::optional<Nonnull<const AstNode*>> ast_node)
-      : parent_scope_(parent),
-        ast_node_(ast_node),
-        trace_stream_(parent->trace_stream_) {}
-
-  StaticScope() = default;
-
-  // Defines `name` to be `entity` in this scope, or reports a compilation error
-  // if `name` is already defined to be a different entity in this scope.
-  // If `usable` is `false`, `name` cannot yet be referenced and `Resolve()`
-  // methods will fail for it.
-  auto Add(std::string_view name, ValueNodeView entity,
-           NameStatus status = NameStatus::Usable) -> ErrorOr<Success>;
-
-  template <typename Action>
-  void PrintCommon(Action action) const;
-
-  void Print(llvm::raw_ostream& out) const;
-
-  void PrintID(llvm::raw_ostream& out) const;
-
-  // Marks `name` as being past its point of declaration.
-  void MarkDeclared(std::string_view name);
-  // Marks `name` as being completely declared and hence usable.
-  void MarkUsable(std::string_view name);
-
-  // Returns the nearest declaration of `name` in the ancestor graph of this
-  // scope, or reports a compilation error at `source_loc` there isn't such a
-  // declaration.
-  // TODO: This should also diagnose if there's a shadowed declaration of the
-  // name in an enclosing scope, but does not do so yet.
-  auto Resolve(std::string_view name, SourceLocation source_loc) const
-      -> ErrorOr<ValueNodeView>;
-
-  // Returns the declaration of `name` in this scope, or reports a compilation
-  // error at `source_loc` if the name is not declared in this scope. If
-  // `allow_undeclared` is `true`, names that have been added but not yet marked
-  // declared or usable do not result in an error.
-  auto ResolveHere(std::optional<ValueNodeView> this_scope,
-                   std::string_view name, SourceLocation source_loc,
-                   bool allow_undeclared) const -> ErrorOr<ValueNodeView>;
-
-  // Returns the value node of the BindingPattern of the returned var definition
-  // if it exists in the ancestor graph.
-  auto ResolveReturned() const -> std::optional<ValueNodeView>;
-
-  // Adds the value node of the BindingPattern of the returned var definition to
-  // this scope. Returns a compilation error when there is an existing returned
-  // var in the ancestor graph.
-  auto AddReturnedVar(ValueNodeView returned_var_def_view) -> ErrorOr<Success>;
-
- private:
-  // Equivalent to Resolve, but returns `nullopt` instead of raising an error
-  // if no declaration can be found.
-  auto TryResolve(std::string_view name, SourceLocation source_loc) const
-      -> ErrorOr<std::optional<ValueNodeView>>;
-
-  // Equivalent to ResolveHere, but returns `nullopt` if no definition can be
-  // found. Raises an error if the name is found but is not usable yet.
-  auto TryResolveHere(std::string_view name, SourceLocation source_loc,
-                      bool allow_undeclared) const
-      -> ErrorOr<std::optional<ValueNodeView>>;
-
-  struct Entry {
-    ValueNodeView entity;
-    NameStatus status;
-  };
-  // Maps locally declared names to their entities.
-  llvm::StringMap<Entry> declared_names_;
-
-  // The parent scope of this scope, if it not the root scope.
-  std::optional<Nonnull<const StaticScope*>> parent_scope_;
-
-  // Stores the value node of the BindingPattern of the returned var definition.
-  std::optional<ValueNodeView> returned_var_def_view_;
-
-  std::optional<Nonnull<const AstNode*>> ast_node_;
-
-  Nonnull<TraceStream*> trace_stream_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_STATIC_SCOPE_H_

+ 0 - 1370
explorer/ast/value.cpp

@@ -1,1370 +0,0 @@
-// 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 "explorer/ast/value.h"
-
-#include <algorithm>
-#include <optional>
-#include <string_view>
-
-#include "common/check.h"
-#include "common/error.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/element.h"
-#include "explorer/ast/element_path.h"
-#include "explorer/ast/value_transform.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/error_builders.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-using llvm::dyn_cast;
-using llvm::dyn_cast_or_null;
-using llvm::isa;
-
-namespace {
-// A visitor that walks the Value*s nested within a value.
-struct NestedValueVisitor {
-  template <typename T>
-  auto VisitParts(const T& decomposable) -> bool {
-    return decomposable.Decompose(
-        [&](const auto&... parts) { return (Visit(parts) && ...); });
-  }
-
-  auto Visit(Nonnull<const Value*> value) -> bool {
-    if (!callback(value)) {
-      return false;
-    }
-
-    return value->Visit<bool>(
-        [&](const auto* derived_value) { return VisitParts(*derived_value); });
-  }
-
-  auto Visit(Nonnull<const Bindings*> bindings) -> bool {
-    for (auto [binding, value] : bindings->args()) {
-      if (!Visit(value)) {
-        return false;
-      }
-    }
-    for (auto [binding, value] : bindings->witnesses()) {
-      if (!Visit(value)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  template <typename T>
-  auto Visit(const std::vector<T>& vec) -> bool {
-    for (auto& v : vec) {
-      if (!Visit(v)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  template <typename T>
-  auto Visit(const std::optional<T>& opt) -> bool {
-    return !opt || Visit(*opt);
-  }
-
-  template <typename T,
-            typename = std::enable_if_t<IsRecursivelyTransformable<T>>>
-  auto Visit(Nonnull<const T*> value) -> bool {
-    return VisitParts(*value);
-  }
-  template <typename T,
-            typename = std::enable_if_t<IsRecursivelyTransformable<T>>>
-  auto Visit(const T& value) -> bool {
-    return VisitParts(value);
-  }
-
-  // Other value components can't refer to a value.
-  auto Visit(Nonnull<const AstNode*>) -> bool { return true; }
-  auto Visit(ValueNodeView) -> bool { return true; }
-  auto Visit(int) -> bool { return true; }
-  auto Visit(Address) -> bool { return true; }
-  auto Visit(ExpressionCategory) -> bool { return true; }
-  auto Visit(const std::string&) -> bool { return true; }
-  auto Visit(Nonnull<const NominalClassValue**>) -> bool {
-    // This is the pointer to the most-derived value within a class value,
-    // which is not "within" this value, so we shouldn't visit it.
-    return true;
-  }
-  auto Visit(const VTable*) -> bool { return true; }
-
-  llvm::function_ref<bool(const Value*)> callback;
-};
-}  // namespace
-
-auto VisitNestedValues(Nonnull<const Value*> value,
-                       llvm::function_ref<bool(const Value*)> visitor) -> bool {
-  return NestedValueVisitor{.callback = visitor}.Visit(value);
-}
-
-auto StructValue::FindField(std::string_view name) const
-    -> std::optional<Nonnull<const Value*>> {
-  for (const NamedValue& element : elements_) {
-    if (element.name == name) {
-      return element.value;
-    }
-  }
-  return std::nullopt;
-}
-
-NominalClassValue::NominalClassValue(
-    Nonnull<const Value*> type, Nonnull<const Value*> inits,
-    std::optional<Nonnull<const NominalClassValue*>> base,
-    Nonnull<const NominalClassValue** const> class_value_ptr)
-    : Value(Kind::NominalClassValue),
-      type_(type),
-      inits_(inits),
-      base_(base),
-      class_value_ptr_(class_value_ptr) {
-  CARBON_CHECK(!base || (*base)->class_value_ptr() == class_value_ptr);
-  // Update ancestors's class value to point to latest child.
-  *class_value_ptr_ = this;
-}
-
-static auto FindClassField(Nonnull<const NominalClassValue*> object,
-                           std::string_view name)
-    -> std::optional<Nonnull<const Value*>> {
-  if (auto field = cast<StructValue>(object->inits()).FindField(name)) {
-    return field;
-  }
-  if (object->base().has_value()) {
-    return FindClassField(object->base().value(), name);
-  }
-  return std::nullopt;
-}
-
-static auto GetBaseElement(Nonnull<const NominalClassValue*> class_value,
-                           SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  const auto base = cast<NominalClassValue>(class_value)->base();
-  if (!base.has_value()) {
-    return ProgramError(source_loc)
-           << "Non-existent base class for " << *class_value;
-  }
-  return base.value();
-}
-
-static auto GetPositionalElement(Nonnull<const TupleValue*> tuple,
-                                 const ElementPath::Component& path_comp,
-                                 SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement,
-               "Invalid non-tuple member");
-  const auto* tuple_element = cast<PositionalElement>(path_comp.element());
-  const size_t index = tuple_element->index();
-  if (index < 0 || index >= tuple->elements().size()) {
-    return ProgramError(source_loc)
-           << "index " << index << " out of range for " << *tuple;
-  }
-  return tuple->elements()[index];
-}
-
-static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
-                            const ElementPath::Component& field,
-                            SourceLocation source_loc,
-                            std::optional<Nonnull<const Value*>> me_value)
-    -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement,
-               "Invalid element, expecting NamedElement");
-  const auto* member = cast<NamedElement>(field.element());
-  const auto f = member->name();
-  if (field.witness().has_value()) {
-    const auto* witness = cast<Witness>(*field.witness());
-
-    // Associated constants.
-    if (const auto* assoc_const =
-            dyn_cast_or_null<AssociatedConstantDeclaration>(
-                member->declaration().value_or(nullptr))) {
-      CARBON_CHECK(field.interface(), "have witness but no interface");
-      // TODO: Use witness to find the value of the constant.
-      return arena->New<AssociatedConstant>(v, *field.interface(), assoc_const,
-                                            witness);
-    }
-
-    // Associated functions.
-    if (const auto* impl_witness = dyn_cast<ImplWitness>(witness)) {
-      if (std::optional<Nonnull<const Declaration*>> mem_decl =
-              FindMember(f, impl_witness->declaration().members());
-          mem_decl.has_value()) {
-        const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
-        if (fun_decl.is_method()) {
-          return arena->New<BoundMethodValue>(&fun_decl, *me_value,
-                                              &impl_witness->bindings());
-        } else {
-          // Class function.
-          const auto* fun = cast<FunctionValue>(*fun_decl.constant_value());
-          return arena->New<FunctionValue>(&fun->declaration(),
-                                           &impl_witness->bindings());
-        }
-      } else {
-        return ProgramError(source_loc)
-               << "member " << f << " not in " << *witness;
-      }
-    } else {
-      return ProgramError(source_loc)
-             << "member lookup for " << f << " in symbolic " << *witness;
-    }
-  }
-  switch (v->kind()) {
-    case Value::Kind::StructValue: {
-      std::optional<Nonnull<const Value*>> field =
-          cast<StructValue>(*v).FindField(f);
-      if (field == std::nullopt) {
-        return ProgramError(source_loc) << "member " << f << " not in " << *v;
-      }
-      return *field;
-    }
-    case Value::Kind::NominalClassValue: {
-      const auto& object = cast<NominalClassValue>(*v);
-      // Look for a field.
-      if (std::optional<Nonnull<const Value*>> field =
-              FindClassField(&object, f)) {
-        return *field;
-      } else {
-        // Look for a method in the object's class
-        const auto& class_type = cast<NominalClassType>(object.type());
-        std::optional<Nonnull<const FunctionValue*>> func =
-            FindFunctionWithParents(f, class_type.declaration());
-        if (!func) {
-          return ProgramError(source_loc) << "member " << f << " not in " << *v
-                                          << " or its " << class_type;
-        } else if ((*func)->declaration().is_method()) {
-          // Found a method. Turn it into a bound method.
-          const auto& m = cast<FunctionValue>(**func);
-          if (m.declaration().virt_override() == VirtualOverride::None) {
-            return arena->New<BoundMethodValue>(&m.declaration(), *me_value,
-                                                &class_type.bindings());
-          }
-          // Method is virtual, get child-most class value and perform vtable
-          // lookup.
-          const auto& last_child_value = **object.class_value_ptr();
-          const auto& last_child_type =
-              cast<NominalClassType>(last_child_value.type());
-          const auto res = last_child_type.vtable().find(f);
-          CARBON_CHECK(res != last_child_type.vtable().end());
-          const auto [virtual_method, level] = res->second;
-          const auto level_diff = last_child_type.hierarchy_level() - level;
-          const auto* m_class_value = &last_child_value;
-          // Get class value matching the virtual method, and turn it into a
-          // bound method.
-          for (int i = 0; i < level_diff; ++i) {
-            CARBON_CHECK(m_class_value->base(),
-                         "Error trying to access function class value");
-            m_class_value = *m_class_value->base();
-          }
-          return arena->New<BoundMethodValue>(
-              cast<FunctionDeclaration>(virtual_method), m_class_value,
-              &class_type.bindings());
-        } else {
-          // Found a class function
-          // TODO: This should not be reachable.
-          return arena->New<FunctionValue>(&(*func)->declaration(),
-                                           &class_type.bindings());
-        }
-      }
-    }
-    case Value::Kind::ChoiceType: {
-      const auto& choice = cast<ChoiceType>(*v);
-      auto alt = choice.declaration().FindAlternative(f);
-      if (!alt) {
-        return ProgramError(source_loc)
-               << "alternative " << f << " not in " << *v;
-      }
-      if ((*alt)->parameters()) {
-        return arena->New<AlternativeConstructorValue>(&choice, *alt);
-      }
-      return arena->New<AlternativeValue>(&choice, *alt, std::nullopt);
-    }
-    case Value::Kind::NominalClassType: {
-      // Access a class function.
-      const auto& class_type = cast<NominalClassType>(*v);
-      std::optional<Nonnull<const FunctionValue*>> fun =
-          FindFunctionWithParents(f, class_type.declaration());
-      if (fun == std::nullopt) {
-        return ProgramError(source_loc)
-               << "class function " << f << " not in " << *v;
-      }
-      return arena->New<FunctionValue>(&(*fun)->declaration(),
-                                       &class_type.bindings());
-    }
-    default:
-      CARBON_FATAL("named element access not supported for value {0}", *v);
-  }
-}
-
-static auto GetElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
-                       const ElementPath::Component& path_comp,
-                       SourceLocation source_loc,
-                       std::optional<Nonnull<const Value*>> me_value)
-    -> ErrorOr<Nonnull<const Value*>> {
-  switch (path_comp.element()->kind()) {
-    case ElementKind::NamedElement:
-      return GetNamedElement(arena, v, path_comp, source_loc, me_value);
-    case ElementKind::PositionalElement: {
-      if (const auto* tuple = dyn_cast<TupleValue>(v)) {
-        return GetPositionalElement(tuple, path_comp, source_loc);
-      } else {
-        CARBON_FATAL("Invalid value for positional element");
-      }
-    }
-    case ElementKind::BaseElement:
-      switch (v->kind()) {
-        case Value::Kind::NominalClassValue:
-          return GetBaseElement(cast<NominalClassValue>(v), source_loc);
-        case Value::Kind::PointerValue: {
-          const auto* ptr = cast<PointerValue>(v);
-          return arena->New<PointerValue>(
-              ptr->address().ElementAddress(path_comp.element()));
-        }
-        default:
-          CARBON_FATAL("Invalid value for base element");
-      }
-  }
-}
-
-auto Value::GetElement(Nonnull<Arena*> arena, const ElementPath& path,
-                       SourceLocation source_loc,
-                       std::optional<Nonnull<const Value*>> me_value) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  Nonnull<const Value*> value(this);
-  for (const ElementPath::Component& field : path.components_) {
-    CARBON_ASSIGN_OR_RETURN(
-        value, Carbon::GetElement(arena, value, field, source_loc, me_value));
-  }
-  return value;
-}
-
-static auto SetFieldImpl(
-    Nonnull<Arena*> arena, Nonnull<const Value*> value,
-    std::vector<ElementPath::Component>::const_iterator path_begin,
-    std::vector<ElementPath::Component>::const_iterator path_end,
-    Nonnull<const Value*> field_value, SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  if (path_begin == path_end) {
-    return field_value;
-  }
-  switch (value->kind()) {
-    case Value::Kind::StructValue: {
-      std::vector<NamedValue> elements = cast<StructValue>(*value).elements();
-      auto it =
-          llvm::find_if(elements, [path_begin](const NamedValue& element) {
-            return (*path_begin).IsNamed(element.name);
-          });
-      if (it == elements.end()) {
-        return ProgramError(source_loc)
-               << "field " << *path_begin << " not in " << *value;
-      }
-      CARBON_ASSIGN_OR_RETURN(
-          it->value, SetFieldImpl(arena, it->value, path_begin + 1, path_end,
-                                  field_value, source_loc));
-      return arena->New<StructValue>(elements);
-    }
-    case Value::Kind::NominalClassValue: {
-      const auto& object = cast<NominalClassValue>(*value);
-      if (auto inits = SetFieldImpl(arena, &object.inits(), path_begin,
-                                    path_end, field_value, source_loc);
-          inits.ok()) {
-        auto* class_value_ptr = arena->New<const NominalClassValue*>();
-        std::vector<const NominalClassValue*> base_path;
-        for (auto base = object.base(); base; base = (*base)->base()) {
-          base_path.push_back(*base);
-        }
-        std::optional<Nonnull<const NominalClassValue*>> base;
-        for (auto* base_path_elem : llvm::reverse(base_path)) {
-          base = arena->New<NominalClassValue>(&base_path_elem->type(),
-                                               &base_path_elem->inits(), base,
-                                               class_value_ptr);
-        }
-        return arena->New<NominalClassValue>(&object.type(), *inits, base,
-                                             class_value_ptr);
-      } else if (object.base().has_value()) {
-        auto new_base = SetFieldImpl(arena, object.base().value(), path_begin,
-                                     path_end, field_value, source_loc);
-        if (new_base.ok()) {
-          auto as_nominal_class_value = cast<NominalClassValue>(*new_base);
-          return arena->New<NominalClassValue>(
-              &object.type(), &object.inits(), as_nominal_class_value,
-              as_nominal_class_value->class_value_ptr());
-        }
-      }
-      // Failed to match, show full object content
-      return ProgramError(source_loc)
-             << "field " << *path_begin << " not in " << *value;
-    }
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue: {
-      CARBON_CHECK(
-          (*path_begin).element()->kind() == ElementKind::PositionalElement,
-          "Invalid non-positional member for tuple");
-      std::vector<Nonnull<const Value*>> elements =
-          cast<TupleValueBase>(*value).elements();
-      const size_t index =
-          cast<PositionalElement>((*path_begin).element())->index();
-      if (index < 0 || index >= elements.size()) {
-        return ProgramError(source_loc)
-               << "index " << index << " out of range in " << *value;
-      }
-      CARBON_ASSIGN_OR_RETURN(
-          elements[index], SetFieldImpl(arena, elements[index], path_begin + 1,
-                                        path_end, field_value, source_loc));
-      if (isa<TupleType>(value)) {
-        return arena->New<TupleType>(elements);
-      } else {
-        return arena->New<TupleValue>(elements);
-      }
-    }
-    default:
-      CARBON_FATAL("field access not allowed for value {0}", *value);
-  }
-}
-
-auto Value::SetField(Nonnull<Arena*> arena, const ElementPath& path,
-                     Nonnull<const Value*> field_value,
-                     SourceLocation source_loc) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  return SetFieldImpl(arena, static_cast<Nonnull<const Value*>>(this),
-                      path.components_.begin(), path.components_.end(),
-                      field_value, source_loc);
-}
-
-static auto PrintNameWithBindings(llvm::raw_ostream& out,
-                                  Nonnull<const Declaration*> declaration,
-                                  const BindingMap& args) {
-  out << GetName(*declaration).value_or("(anonymous)");
-  // TODO: Print '()' if declaration is parameterized but no args are provided.
-  if (!args.empty()) {
-    out << "(";
-    llvm::ListSeparator sep;
-    for (const auto& [bind, val] : args) {
-      out << sep << bind->name() << " = " << *val;
-    }
-    out << ")";
-  }
-}
-
-void Value::Print(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case Value::Kind::AlternativeConstructorValue: {
-      const auto& alt = cast<AlternativeConstructorValue>(*this);
-      out << alt.choice().declaration().name() << "."
-          << alt.alternative().name();
-      break;
-    }
-    case Value::Kind::BindingPlaceholderValue: {
-      const auto& placeholder = cast<BindingPlaceholderValue>(*this);
-      out << "Placeholder<";
-      if (placeholder.value_node().has_value()) {
-        out << (*placeholder.value_node());
-      } else {
-        out << "_";
-      }
-      out << ">";
-      break;
-    }
-    case Value::Kind::AddrValue: {
-      const auto& addr = cast<AddrValue>(*this);
-      out << "Addr<" << addr.pattern() << ">";
-      break;
-    }
-    case Value::Kind::AlternativeValue: {
-      const auto& alt = cast<AlternativeValue>(*this);
-      out << alt.choice().declaration().name() << "."
-          << alt.alternative().name();
-      if (auto arg = alt.argument()) {
-        out << **arg;
-      }
-      break;
-    }
-    case Value::Kind::StructValue: {
-      const auto& struct_val = cast<StructValue>(*this);
-      out << "{";
-      llvm::ListSeparator sep;
-      for (const NamedValue& element : struct_val.elements()) {
-        out << sep << "." << element.name << " = " << *element.value;
-      }
-      out << "}";
-      break;
-    }
-    case Value::Kind::NominalClassValue: {
-      const auto& s = cast<NominalClassValue>(*this);
-      out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
-      if (s.base().has_value()) {
-        out << " base " << *s.base().value();
-      }
-      break;
-    }
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue: {
-      out << "(";
-      llvm::ListSeparator sep;
-      const auto elements = cast<TupleValueBase>(*this).elements();
-      for (Nonnull<const Value*> element : elements) {
-        out << sep << *element;
-      }
-      // Print trailing comma for single element tuples: (i32,).
-      if (elements.size() == 1) {
-        out << ",";
-      }
-      out << ")";
-      break;
-    }
-    case Value::Kind::IntValue:
-      out << cast<IntValue>(*this).value();
-      break;
-    case Value::Kind::BoolValue:
-      out << (cast<BoolValue>(*this).value() ? "true" : "false");
-      break;
-    case Value::Kind::DestructorValue: {
-      const auto& destructor = cast<DestructorValue>(*this);
-      out << "destructor [ ";
-      out << destructor.declaration().self_pattern();
-      out << " ]";
-      break;
-    }
-    case Value::Kind::FunctionValue: {
-      const auto& fun = cast<FunctionValue>(*this);
-      out << "fun<" << fun.declaration().name() << ">";
-      if (!fun.type_args().empty()) {
-        out << "[";
-        llvm::ListSeparator sep;
-        for (const auto& [ty_var, ty_arg] : fun.type_args()) {
-          out << sep << *ty_var << "=" << *ty_arg;
-        }
-        out << "]";
-      }
-      if (!fun.witnesses().empty()) {
-        out << "{|";
-        llvm::ListSeparator sep;
-        for (const auto& [impl_bind, witness] : fun.witnesses()) {
-          out << sep << *witness;
-        }
-        out << "|}";
-      }
-      break;
-    }
-    case Value::Kind::BoundMethodValue: {
-      const auto& method = cast<BoundMethodValue>(*this);
-      out << "bound_method<" << method.declaration().name() << ">";
-      if (!method.type_args().empty()) {
-        out << "[";
-        llvm::ListSeparator sep;
-        for (const auto& [ty_var, ty_arg] : method.type_args()) {
-          out << sep << *ty_var << "=" << *ty_arg;
-        }
-        out << "]";
-      }
-      if (!method.witnesses().empty()) {
-        out << "{|";
-        llvm::ListSeparator sep;
-        for (const auto& [impl_bind, witness] : method.witnesses()) {
-          out << sep << *witness;
-        }
-        out << "|}";
-      }
-      break;
-    }
-    case Value::Kind::PointerValue:
-      out << "ptr<" << cast<PointerValue>(*this).address() << ">";
-      break;
-    case Value::Kind::LocationValue:
-      out << "lval<" << cast<LocationValue>(*this).address() << ">";
-      break;
-    case Value::Kind::ReferenceExpressionValue:
-      out << "ref_expr<" << cast<ReferenceExpressionValue>(*this).address()
-          << ">";
-      break;
-    case Value::Kind::BoolType:
-      out << "bool";
-      break;
-    case Value::Kind::IntType:
-      out << "i32";
-      break;
-    case Value::Kind::TypeType:
-      out << "type";
-      break;
-    case Value::Kind::AutoType:
-      out << "auto";
-      break;
-    case Value::Kind::PointerType:
-      out << cast<PointerType>(*this).pointee_type() << "*";
-      break;
-    case Value::Kind::FunctionType: {
-      const auto& fn_type = cast<FunctionType>(*this);
-      out << "fn ";
-      auto self = fn_type.method_self();
-      if (!fn_type.deduced_bindings().empty() || self.has_value()) {
-        out << "[";
-        llvm::ListSeparator sep;
-        for (Nonnull<const GenericBinding*> deduced :
-             fn_type.deduced_bindings()) {
-          out << sep << *deduced;
-        }
-        if (self.has_value()) {
-          if (self->addr_self) {
-            out << sep << "addr self: " << *self->self_type << "*";
-          } else {
-            out << sep << "self: " << *self->self_type;
-          }
-        }
-        out << "]";
-      }
-      out << fn_type.parameters() << " -> " << fn_type.return_type();
-      break;
-    }
-    case Value::Kind::StructType: {
-      out << "{";
-      llvm::ListSeparator sep;
-      for (const auto& [name, type] : cast<StructType>(*this).fields()) {
-        out << sep << "." << name << ": " << *type;
-      }
-      out << "}";
-      break;
-    }
-    case Value::Kind::UninitializedValue: {
-      const auto& uninit = cast<UninitializedValue>(*this);
-      out << "Uninit<" << uninit.pattern() << ">";
-      break;
-    }
-    case Value::Kind::NominalClassType: {
-      const auto& class_type = cast<NominalClassType>(*this);
-      out << "class ";
-      PrintNameWithBindings(out, &class_type.declaration(),
-                            class_type.type_args());
-      if (!class_type.witnesses().empty()) {
-        out << " witnesses ";
-        llvm::ListSeparator sep;
-        for (const auto& [impl_bind, witness] : class_type.witnesses()) {
-          out << sep << *witness;
-        }
-      }
-      break;
-    }
-    case Value::Kind::ChoiceType: {
-      const auto& choice_type = cast<ChoiceType>(*this);
-      out << "choice ";
-      PrintNameWithBindings(out, &choice_type.declaration(),
-                            choice_type.type_args());
-      break;
-    }
-    case Value::Kind::MixinPseudoType: {
-      const auto& mixin_type = cast<MixinPseudoType>(*this);
-      out << "mixin ";
-      PrintNameWithBindings(out, &mixin_type.declaration(), mixin_type.args());
-      if (!mixin_type.witnesses().empty()) {
-        out << " witnesses ";
-        llvm::ListSeparator sep;
-        for (const auto& [impl_bind, witness] : mixin_type.witnesses()) {
-          out << sep << *witness;
-        }
-      }
-      // TODO: print the import interface
-      break;
-    }
-    case Value::Kind::InterfaceType: {
-      const auto& iface_type = cast<InterfaceType>(*this);
-      out << "interface ";
-      PrintNameWithBindings(out, &iface_type.declaration(),
-                            iface_type.bindings().args());
-      break;
-    }
-    case Value::Kind::NamedConstraintType: {
-      const auto& constraint_type = cast<NamedConstraintType>(*this);
-      out << "constraint ";
-      PrintNameWithBindings(out, &constraint_type.declaration(),
-                            constraint_type.bindings().args());
-      break;
-    }
-    case Value::Kind::ConstraintType: {
-      const auto& constraint = cast<ConstraintType>(*this);
-      llvm::ListSeparator combine(" & ");
-      for (const LookupContext& ctx : constraint.lookup_contexts()) {
-        out << combine << *ctx.context;
-      }
-      if (constraint.lookup_contexts().empty()) {
-        out << "type";
-      }
-      out << " where ";
-      llvm::ListSeparator sep(" and ");
-      for (const RewriteConstraint& rewrite :
-           constraint.rewrite_constraints()) {
-        out << sep << ".(";
-        PrintNameWithBindings(out, &rewrite.constant->interface().declaration(),
-                              rewrite.constant->interface().args());
-        out << "." << *GetName(rewrite.constant->constant())
-            << ") = " << *rewrite.unconverted_replacement;
-      }
-      for (const ImplsConstraint& impl : constraint.impls_constraints()) {
-        // TODO: Skip cases where `impl.type` is `.Self` and the interface is
-        // in `lookup_contexts()`.
-        out << sep << *impl.type << " impls " << *impl.interface;
-      }
-      for (const EqualityConstraint& equality :
-           constraint.equality_constraints()) {
-        out << sep;
-        llvm::ListSeparator equal(" == ");
-        for (Nonnull<const Value*> value : equality.values) {
-          out << equal << *value;
-        }
-      }
-      break;
-    }
-    case Value::Kind::ImplWitness: {
-      const auto& witness = cast<ImplWitness>(*this);
-      out << "witness for impl " << *witness.declaration().impl_type() << " as "
-          << witness.declaration().interface();
-      break;
-    }
-    case Value::Kind::BindingWitness: {
-      const auto& witness = cast<BindingWitness>(*this);
-      out << "witness for " << *witness.binding()->type_var();
-      break;
-    }
-    case Value::Kind::ConstraintWitness: {
-      const auto& witness = cast<ConstraintWitness>(*this);
-      out << "(";
-      llvm::ListSeparator sep;
-      for (const auto* elem : witness.witnesses()) {
-        out << sep << *elem;
-      }
-      out << ")";
-      break;
-    }
-    case Value::Kind::ConstraintImplWitness: {
-      const auto& witness = cast<ConstraintImplWitness>(*this);
-      out << "witness " << witness.index() << " of "
-          << *witness.constraint_witness();
-      break;
-    }
-    case Value::Kind::ParameterizedEntityName:
-      out << *GetName(cast<ParameterizedEntityName>(*this).declaration());
-      break;
-    case Value::Kind::MemberName: {
-      const auto& member_name = cast<MemberName>(*this);
-      if (member_name.base_type().has_value()) {
-        out << *member_name.base_type().value();
-      }
-      if (member_name.base_type().has_value() &&
-          member_name.interface().has_value()) {
-        out << "(";
-      }
-      if (member_name.interface().has_value()) {
-        out << *member_name.interface().value();
-      }
-      out << "." << member_name.member();
-      if (member_name.base_type().has_value() &&
-          member_name.interface().has_value()) {
-        out << ")";
-      }
-      break;
-    }
-    case Value::Kind::VariableType:
-      out << cast<VariableType>(*this).binding().name();
-      break;
-    case Value::Kind::AssociatedConstant: {
-      const auto& assoc = cast<AssociatedConstant>(*this);
-      out << "(" << assoc.base() << ").(";
-      PrintNameWithBindings(out, &assoc.interface().declaration(),
-                            assoc.interface().args());
-      out << "." << *GetName(assoc.constant()) << ")";
-      break;
-    }
-    case Value::Kind::StringType:
-      out << "String";
-      break;
-    case Value::Kind::StringValue:
-      out << "\"";
-      out.write_escaped(cast<StringValue>(*this).value());
-      out << "\"";
-      break;
-    case Value::Kind::TypeOfMixinPseudoType:
-      out << "typeof("
-          << cast<TypeOfMixinPseudoType>(*this)
-                 .mixin_type()
-                 .declaration()
-                 .name()
-          << ")";
-      break;
-    case Value::Kind::TypeOfParameterizedEntityName:
-      out << "parameterized entity name "
-          << cast<TypeOfParameterizedEntityName>(*this).name();
-      break;
-    case Value::Kind::TypeOfMemberName: {
-      out << "member name " << cast<TypeOfMemberName>(*this).member();
-      break;
-    }
-    case Value::Kind::TypeOfNamespaceName: {
-      cast<TypeOfNamespaceName>(*this).namespace_decl()->PrintID(out);
-      break;
-    }
-    case Value::Kind::StaticArrayType: {
-      const auto& array_type = cast<StaticArrayType>(*this);
-      out << "[" << array_type.element_type() << ";";
-      if (array_type.has_size()) {
-        out << " " << array_type.size();
-      }
-      out << "]";
-      break;
-    }
-  }
-}
-
-void IntrinsicConstraint::Print(llvm::raw_ostream& out) const {
-  out << *type << " is ";
-  switch (kind) {
-    case IntrinsicConstraint::ImplicitAs:
-      out << "__intrinsic_implicit_as";
-      break;
-  }
-  if (!arguments.empty()) {
-    out << "(";
-    llvm::ListSeparator comma;
-    for (Nonnull<const Value*> argument : arguments) {
-      out << comma << *argument;
-    }
-    out << ")";
-  }
-}
-
-// Check whether two binding maps, which are assumed to have the same keys, are
-// equal.
-static auto BindingMapEqual(
-    const BindingMap& map1, const BindingMap& map2,
-    std::optional<Nonnull<const EqualityContext*>> equality_ctx) -> bool {
-  CARBON_CHECK(map1.size() == map2.size(), "maps should have same keys");
-  for (const auto& [key, value] : map1) {
-    if (!ValueEqual(value, map2.at(key), equality_ctx)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
-               std::optional<Nonnull<const EqualityContext*>> equality_ctx)
-    -> bool {
-  if (t1 == t2) {
-    return true;
-  }
-  if (t1->kind() != t2->kind()) {
-    if (IsValueKindDependent(t1) || IsValueKindDependent(t2)) {
-      return ValueEqual(t1, t2, equality_ctx);
-    }
-    return false;
-  }
-  switch (t1->kind()) {
-    case Value::Kind::PointerType:
-      return TypeEqual(&cast<PointerType>(*t1).pointee_type(),
-                       &cast<PointerType>(*t2).pointee_type(), equality_ctx);
-    case Value::Kind::FunctionType: {
-      const auto& fn1 = cast<FunctionType>(*t1);
-      const auto& fn2 = cast<FunctionType>(*t2);
-      // Verify `self` parameters match
-      auto self1 = fn1.method_self();
-      auto self2 = fn2.method_self();
-      if (self1.has_value() != self2.has_value()) {
-        return false;
-      }
-      if (self1) {
-        if (self1->addr_self != self2->addr_self ||
-            !TypeEqual(self1->self_type, self2->self_type, equality_ctx)) {
-          return false;
-        }
-      }
-      // Verify parameters and return types match
-      return TypeEqual(&fn1.parameters(), &fn2.parameters(), equality_ctx) &&
-             TypeEqual(&fn1.return_type(), &fn2.return_type(), equality_ctx);
-    }
-    case Value::Kind::StructType: {
-      const auto& struct1 = cast<StructType>(*t1);
-      const auto& struct2 = cast<StructType>(*t2);
-      if (struct1.fields().size() != struct2.fields().size()) {
-        return false;
-      }
-      for (size_t i = 0; i < struct1.fields().size(); ++i) {
-        if (struct1.fields()[i].name != struct2.fields()[i].name ||
-            !TypeEqual(struct1.fields()[i].value, struct2.fields()[i].value,
-                       equality_ctx)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::NominalClassType: {
-      const auto& class1 = cast<NominalClassType>(*t1);
-      const auto& class2 = cast<NominalClassType>(*t2);
-      return DeclaresSameEntity(class1.declaration(), class2.declaration()) &&
-             BindingMapEqual(class1.bindings().args(), class2.bindings().args(),
-                             equality_ctx);
-    }
-    case Value::Kind::InterfaceType: {
-      const auto& iface1 = cast<InterfaceType>(*t1);
-      const auto& iface2 = cast<InterfaceType>(*t2);
-      return DeclaresSameEntity(iface1.declaration(), iface2.declaration()) &&
-             BindingMapEqual(iface1.bindings().args(), iface2.bindings().args(),
-                             equality_ctx);
-    }
-    case Value::Kind::NamedConstraintType: {
-      const auto& constraint1 = cast<NamedConstraintType>(*t1);
-      const auto& constraint2 = cast<NamedConstraintType>(*t2);
-      return DeclaresSameEntity(constraint1.declaration(),
-                                constraint2.declaration()) &&
-             BindingMapEqual(constraint1.bindings().args(),
-                             constraint2.bindings().args(), equality_ctx);
-    }
-    case Value::Kind::AssociatedConstant:
-      // Associated constants are sometimes types.
-      return ValueEqual(t1, t2, equality_ctx);
-    case Value::Kind::ConstraintType: {
-      const auto& constraint1 = cast<ConstraintType>(*t1);
-      const auto& constraint2 = cast<ConstraintType>(*t2);
-      if (constraint1.impls_constraints().size() !=
-              constraint2.impls_constraints().size() ||
-          constraint1.equality_constraints().size() !=
-              constraint2.equality_constraints().size() ||
-          constraint1.lookup_contexts().size() !=
-              constraint2.lookup_contexts().size()) {
-        return false;
-      }
-      for (size_t i = 0; i < constraint1.impls_constraints().size(); ++i) {
-        const auto& impl1 = constraint1.impls_constraints()[i];
-        const auto& impl2 = constraint2.impls_constraints()[i];
-        if (!TypeEqual(impl1.type, impl2.type, equality_ctx) ||
-            !TypeEqual(impl1.interface, impl2.interface, equality_ctx)) {
-          return false;
-        }
-      }
-      for (size_t i = 0; i < constraint1.equality_constraints().size(); ++i) {
-        const auto& equality1 = constraint1.equality_constraints()[i];
-        const auto& equality2 = constraint2.equality_constraints()[i];
-        if (equality1.values.size() != equality2.values.size()) {
-          return false;
-        }
-        for (size_t j = 0; j < equality1.values.size(); ++j) {
-          if (!ValueEqual(equality1.values[j], equality2.values[j],
-                          equality_ctx)) {
-            return false;
-          }
-        }
-      }
-      for (size_t i = 0; i < constraint1.lookup_contexts().size(); ++i) {
-        const auto& context1 = constraint1.lookup_contexts()[i];
-        const auto& context2 = constraint2.lookup_contexts()[i];
-        if (!TypeEqual(context1.context, context2.context, equality_ctx)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::ChoiceType: {
-      const auto& choice1 = cast<ChoiceType>(*t1);
-      const auto& choice2 = cast<ChoiceType>(*t2);
-      return DeclaresSameEntity(choice1.declaration(), choice2.declaration()) &&
-             BindingMapEqual(choice1.type_args(), choice2.type_args(),
-                             equality_ctx);
-    }
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue: {
-      const auto& tup1 = cast<TupleValueBase>(*t1);
-      const auto& tup2 = cast<TupleValueBase>(*t2);
-      if (tup1.elements().size() != tup2.elements().size()) {
-        return false;
-      }
-      for (size_t i = 0; i < tup1.elements().size(); ++i) {
-        if (!TypeEqual(tup1.elements()[i], tup2.elements()[i], equality_ctx)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::StringType:
-      return true;
-    case Value::Kind::VariableType:
-      return &cast<VariableType>(*t1).binding() ==
-             &cast<VariableType>(*t2).binding();
-    case Value::Kind::StaticArrayType: {
-      const auto& array1 = cast<StaticArrayType>(*t1);
-      const auto& array2 = cast<StaticArrayType>(*t2);
-      return TypeEqual(&array1.element_type(), &array2.element_type(),
-                       equality_ctx) &&
-             array1.size() == array2.size();
-    }
-    case Value::Kind::IntValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::StructValue:
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::AlternativeValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::StringValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::UninitializedValue:
-    case Value::Kind::ParameterizedEntityName:
-    case Value::Kind::MemberName:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfNamespaceName:
-      CARBON_FATAL("TypeEqual used to compare non-type values\n{0}\n{1}", *t1,
-                   *t2);
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-      CARBON_FATAL("TypeEqual: unexpected Witness");
-      break;
-    case Value::Kind::AutoType:
-      CARBON_FATAL("TypeEqual: unexpected AutoType");
-      break;
-  }
-}
-
-// Returns true if the two values are known to be equal and are written in the
-// same way at the top level.
-static auto ValueStructurallyEqual(
-    Nonnull<const Value*> v1, Nonnull<const Value*> v2,
-    std::optional<Nonnull<const EqualityContext*>> equality_ctx) -> bool {
-  if (v1 == v2) {
-    return true;
-  }
-  if (v1->kind() != v2->kind()) {
-    return false;
-  }
-  switch (v1->kind()) {
-    case Value::Kind::IntValue:
-      return cast<IntValue>(*v1).value() == cast<IntValue>(*v2).value();
-    case Value::Kind::BoolValue:
-      return cast<BoolValue>(*v1).value() == cast<BoolValue>(*v2).value();
-    case Value::Kind::FunctionValue: {
-      std::optional<Nonnull<const Statement*>> body1 =
-          cast<FunctionValue>(*v1).declaration().body();
-      std::optional<Nonnull<const Statement*>> body2 =
-          cast<FunctionValue>(*v2).declaration().body();
-      return body1.has_value() == body2.has_value() &&
-             (!body1.has_value() || *body1 == *body2);
-    }
-    case Value::Kind::DestructorValue:
-      return false;
-    case Value::Kind::BoundMethodValue: {
-      const auto& m1 = cast<BoundMethodValue>(*v1);
-      const auto& m2 = cast<BoundMethodValue>(*v2);
-      std::optional<Nonnull<const Statement*>> body1 = m1.declaration().body();
-      std::optional<Nonnull<const Statement*>> body2 = m2.declaration().body();
-      return ValueEqual(m1.receiver(), m2.receiver(), equality_ctx) &&
-             body1.has_value() == body2.has_value() &&
-             (!body1.has_value() || *body1 == *body2);
-    }
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue: {
-      const std::vector<Nonnull<const Value*>>& elements1 =
-          cast<TupleValueBase>(*v1).elements();
-      const std::vector<Nonnull<const Value*>>& elements2 =
-          cast<TupleValueBase>(*v2).elements();
-      if (elements1.size() != elements2.size()) {
-        return false;
-      }
-      for (size_t i = 0; i < elements1.size(); ++i) {
-        if (!ValueEqual(elements1[i], elements2[i], equality_ctx)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::StructValue: {
-      const auto& struct_v1 = cast<StructValue>(*v1);
-      const auto& struct_v2 = cast<StructValue>(*v2);
-      CARBON_CHECK(struct_v1.elements().size() == struct_v2.elements().size());
-      for (size_t i = 0; i < struct_v1.elements().size(); ++i) {
-        CARBON_CHECK(struct_v1.elements()[i].name ==
-                     struct_v2.elements()[i].name);
-        if (!ValueEqual(struct_v1.elements()[i].value,
-                        struct_v2.elements()[i].value, equality_ctx)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::AlternativeValue: {
-      const auto& alt1 = cast<AlternativeValue>(*v1);
-      const auto& alt2 = cast<AlternativeValue>(*v2);
-      if (!TypeEqual(&alt1.choice(), &alt2.choice(), equality_ctx) ||
-          &alt1.alternative() != &alt2.alternative()) {
-        return false;
-      }
-      CARBON_CHECK(alt1.argument().has_value() == alt2.argument().has_value());
-      return !alt1.argument().has_value() ||
-             ValueEqual(*alt1.argument(), *alt2.argument(), equality_ctx);
-    }
-    case Value::Kind::StringValue:
-      return cast<StringValue>(*v1).value() == cast<StringValue>(*v2).value();
-    case Value::Kind::ParameterizedEntityName: {
-      std::optional<std::string_view> name1 =
-          GetName(cast<ParameterizedEntityName>(v1)->declaration());
-      std::optional<std::string_view> name2 =
-          GetName(cast<ParameterizedEntityName>(v2)->declaration());
-      CARBON_CHECK(name1.has_value() && name2.has_value(),
-                   "parameterized name refers to unnamed declaration");
-      return *name1 == *name2;
-    }
-    case Value::Kind::AssociatedConstant: {
-      // The witness value is not part of determining value equality.
-      const auto& assoc1 = cast<AssociatedConstant>(*v1);
-      const auto& assoc2 = cast<AssociatedConstant>(*v2);
-      return DeclaresSameEntity(assoc1.constant(), assoc2.constant()) &&
-             TypeEqual(&assoc1.base(), &assoc2.base(), equality_ctx) &&
-             TypeEqual(&assoc1.interface(), &assoc2.interface(), equality_ctx);
-    }
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::FunctionType:
-    case Value::Kind::PointerType:
-    case Value::Kind::AutoType:
-    case Value::Kind::StructType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::VariableType:
-    case Value::Kind::StringType:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName:
-    case Value::Kind::StaticArrayType:
-      return TypeEqual(v1, v2, equality_ctx);
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::UninitializedValue:
-    case Value::Kind::MemberName:
-      // TODO: support pointer comparisons once we have a clearer distinction
-      // between pointers and lvalues.
-      CARBON_FATAL("ValueEqual does not support this kind of value: {0}", *v1);
-  }
-}
-
-// Returns true if the two values are equal and returns false otherwise.
-//
-// This function implements the `==` operator of Carbon.
-auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
-                std::optional<Nonnull<const EqualityContext*>> equality_ctx)
-    -> bool {
-  if (v1 == v2) {
-    return true;
-  }
-
-  // If we're given an equality context, check to see if it knows these values
-  // are equal. Only perform the check if one or the other value is an
-  // associated constant; otherwise we should be able to do better by looking
-  // at the structures of the values.
-  if (equality_ctx) {
-    if (IsValueKindDependent(v1)) {
-      auto visitor = [&](Nonnull<const Value*> maybe_v2) {
-        return !ValueStructurallyEqual(v2, maybe_v2, equality_ctx);
-      };
-      if (!(*equality_ctx)->VisitEqualValues(v1, visitor)) {
-        return true;
-      }
-    }
-    if (IsValueKindDependent(v2)) {
-      auto visitor = [&](Nonnull<const Value*> maybe_v1) {
-        return !ValueStructurallyEqual(v1, maybe_v1, equality_ctx);
-      };
-      if (!(*equality_ctx)->VisitEqualValues(v2, visitor)) {
-        return true;
-      }
-    }
-  }
-
-  return ValueStructurallyEqual(v1, v2, equality_ctx);
-}
-
-auto EqualityConstraint::VisitEqualValues(
-    Nonnull<const Value*> value,
-    llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
-  // See if the given value is part of this constraint.
-  auto first_equal = llvm::find_if(values, [value](Nonnull<const Value*> val) {
-    return ValueEqual(value, val, std::nullopt);
-  });
-  if (first_equal == values.end()) {
-    return true;
-  }
-
-  // The value is in this group; pass all non-identical values in the group
-  // to the visitor. First visit the values we already compared.
-  for (const auto* val : llvm::make_range(values.begin(), first_equal)) {
-    if (!visitor(val)) {
-      return false;
-    }
-  }
-  // Then visit any remaining non-identical values, skipping the one we already
-  // found was identical.
-  ++first_equal;
-  for (const auto* val : llvm::make_range(first_equal, values.end())) {
-    if (!ValueEqual(value, val, std::nullopt) && !visitor(val)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-auto ConstraintType::VisitEqualValues(
-    Nonnull<const Value*> value,
-    llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
-  for (const auto& eq : equality_constraints()) {
-    if (!eq.VisitEqualValues(value, visitor)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-auto FindFunction(std::string_view name,
-                  llvm::ArrayRef<Nonnull<Declaration*>> members)
-    -> std::optional<Nonnull<const FunctionValue*>> {
-  for (const auto& member : members) {
-    switch (member->kind()) {
-      case DeclarationKind::MixDeclaration: {
-        const auto& mix_decl = cast<MixDeclaration>(*member);
-        Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
-        const auto res = mixin->FindFunction(name);
-        if (res.has_value()) {
-          return res;
-        }
-        break;
-      }
-      case DeclarationKind::FunctionDeclaration: {
-        const auto& fun = cast<FunctionDeclaration>(*member);
-        if (fun.name().inner_name() == name) {
-          return &cast<FunctionValue>(**fun.constant_value());
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-  return std::nullopt;
-}
-
-// TODO: Find out a way to remove code duplication
-auto MixinPseudoType::FindFunction(const std::string_view& name) const
-    -> std::optional<Nonnull<const FunctionValue*>> {
-  for (const auto& member : declaration().members()) {
-    switch (member->kind()) {
-      case DeclarationKind::MixDeclaration: {
-        const auto& mix_decl = cast<MixDeclaration>(*member);
-        Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
-        const auto res = mixin->FindFunction(name);
-        if (res.has_value()) {
-          return res;
-        }
-        break;
-      }
-      case DeclarationKind::FunctionDeclaration: {
-        const auto& fun = cast<FunctionDeclaration>(*member);
-        if (fun.name().inner_name() == name) {
-          return &cast<FunctionValue>(**fun.constant_value());
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-  return std::nullopt;
-}
-
-auto FindFunctionWithParents(std::string_view name,
-                             const ClassDeclaration& class_decl)
-    -> std::optional<Nonnull<const FunctionValue*>> {
-  if (auto fun = FindFunction(name, class_decl.members()); fun.has_value()) {
-    return fun;
-  }
-  if (const auto base_type = class_decl.base_type(); base_type.has_value()) {
-    return FindFunctionWithParents(name, base_type.value()->declaration());
-  }
-  return std::nullopt;
-}
-
-auto FindMember(std::string_view name,
-                llvm::ArrayRef<Nonnull<Declaration*>> members)
-    -> std::optional<Nonnull<const Declaration*>> {
-  for (Nonnull<const Declaration*> member : members) {
-    if (std::optional<std::string_view> mem_name = GetName(*member);
-        mem_name.has_value()) {
-      if (*mem_name == name) {
-        return member;
-      }
-    }
-  }
-  return std::nullopt;
-}
-
-void ImplBinding::Print(llvm::raw_ostream& out) const {
-  out << "impl binding " << *type_var_ << " as " << **iface_;
-}
-
-void ImplBinding::PrintID(llvm::raw_ostream& out) const {
-  out << *type_var_ << " as " << **iface_;
-}
-
-auto NominalClassType::InheritsClass(Nonnull<const Value*> other) const
-    -> bool {
-  const auto* other_class = dyn_cast<NominalClassType>(other);
-  if (!other_class) {
-    return false;
-  }
-  std::optional<Nonnull<const NominalClassType*>> ancestor_class = this;
-  while (ancestor_class) {
-    if (TypeEqual(*ancestor_class, other_class, std::nullopt)) {
-      return true;
-    }
-    ancestor_class = (*ancestor_class)->base();
-  }
-  return false;
-}
-
-auto ExpressionCategoryToString(ExpressionCategory cat) -> llvm::StringRef {
-  switch (cat) {
-    case ExpressionCategory::Value:
-      return "value";
-    case ExpressionCategory::Reference:
-      return "reference";
-    case ExpressionCategory::Initializing:
-      return "initializing";
-  }
-}
-
-}  // namespace Carbon

+ 0 - 1744
explorer/ast/value.h

@@ -1,1744 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_VALUE_H_
-#define CARBON_EXPLORER_AST_VALUE_H_
-
-#include <optional>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/ast/address.h"
-#include "explorer/ast/bindings.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/element.h"
-#include "explorer/ast/element_path.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/statement.h"
-#include "explorer/base/nonnull.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class AssociatedConstant;
-class ChoiceType;
-class TupleValue;
-
-// A trait type that describes how to allocate an instance of `T` in an arena.
-// Returns the created object, which is not required to be of type `T`.
-template <typename T>
-struct AllocateTrait {
-  template <typename... Args>
-  static auto New(Nonnull<Arena*> arena, Args&&... args) -> Nonnull<const T*> {
-    return arena->New<T>(std::forward<Args>(args)...);
-  }
-};
-
-using VTable =
-    llvm::StringMap<std::pair<Nonnull<const CallableDeclaration*>, int>>;
-
-// Returns a pointer to an empty VTable that will never be deallocated.
-//
-// Using this instead of `new VTable()` avoids unnecessary allocations, and
-// takes better advantage of Arena canonicalization when a VTable pointer is
-// used as a constructor argument.
-inline auto EmptyVTable() -> Nonnull<const VTable*> {
-  static Nonnull<const VTable*> result = new VTable();
-  return result;
-}
-
-// Abstract base class of all AST nodes representing values.
-//
-// Value and its derived classes support LLVM-style RTTI, including
-// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
-// class derived from Value must provide a `classof` operation, and
-// every concrete derived class must have a corresponding enumerator
-// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
-// details.
-//
-// Arena's canonicalization support is enabled for Value and all derived types.
-// As a result, all Values must be immutable, and all their constructor
-// arguments must be copyable, equality-comparable, and hashable. See
-// Arena's documentation for details.
-class Value : public Printable<Value> {
- public:
-  using EnableCanonicalizedAllocation = void;
-  enum class Kind {
-#define CARBON_VALUE_KIND(kind) kind,
-#include "explorer/ast/value_kinds.def"
-  };
-
-  Value(const Value&) = delete;
-  auto operator=(const Value&) -> Value& = delete;
-
-  // Call `f` on this value, cast to its most-derived type. `R` specifies the
-  // expected return type of `f`.
-  template <typename R, typename F>
-  auto Visit(F f) const -> R;
-
-  void Print(llvm::raw_ostream& out) const;
-
-  // Returns the sub-Value specified by `path`, which must be a valid element
-  // path for *this. If the sub-Value is a method and its self_pattern is an
-  // AddrPattern, then pass the LocationValue representing the receiver as
-  // `me_value`, otherwise pass `*this`.
-  auto GetElement(Nonnull<Arena*> arena, const ElementPath& path,
-                  SourceLocation source_loc,
-                  std::optional<Nonnull<const Value*>> me_value) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Returns a copy of *this, but with the sub-Value specified by `path`
-  // set to `field_value`. `path` must be a valid field path for *this.
-  auto SetField(Nonnull<Arena*> arena, const ElementPath& path,
-                Nonnull<const Value*> field_value,
-                SourceLocation source_loc) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> Kind { return kind_; }
-
- protected:
-  // Constructs a Value. `kind` must be the enumerator corresponding to the
-  // most-derived type being constructed.
-  explicit Value(Kind kind) : kind_(kind) {}
-
- private:
-  const Kind kind_;
-};
-
-// Returns whether the fully-resolved kind that this value will eventually have
-// is currently unknown, because it depends on a generic parameter.
-inline auto IsValueKindDependent(Nonnull<const Value*> type) -> bool {
-  return type->kind() == Value::Kind::VariableType ||
-         type->kind() == Value::Kind::AssociatedConstant;
-}
-
-// Base class for types holding contextual information by which we can
-// determine whether values are equal.
-class EqualityContext {
- public:
-  virtual auto VisitEqualValues(
-      Nonnull<const Value*> value,
-      llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const
-      -> bool = 0;
-
- protected:
-  virtual ~EqualityContext() = default;
-};
-
-auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
-               std::optional<Nonnull<const EqualityContext*>> equality_ctx)
-    -> bool;
-auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
-                std::optional<Nonnull<const EqualityContext*>> equality_ctx)
-    -> bool;
-
-// Call the given `visitor` on all values nested within the given value,
-// including `value` itself, in a preorder traversal. Aborts and returns
-// `false` if `visitor` returns `false`, otherwise returns `true`.
-auto VisitNestedValues(Nonnull<const Value*> value,
-                       llvm::function_ref<bool(const Value*)> visitor) -> bool;
-
-// An integer value.
-class IntValue : public Value {
- public:
-  explicit IntValue(int value) : Value(Kind::IntValue), value_(value) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::IntValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_);
-  }
-
-  auto value() const -> int { return value_; }
-
- private:
-  int value_;
-};
-
-// A function or bound method value.
-class FunctionOrMethodValue : public Value {
- public:
-  explicit FunctionOrMethodValue(
-      Kind kind, Nonnull<const FunctionDeclaration*> declaration,
-      Nonnull<const Bindings*> bindings)
-      : Value(kind), declaration_(declaration), bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::FunctionValue ||
-           value->kind() == Kind::BoundMethodValue;
-  }
-
-  auto declaration() const -> const FunctionDeclaration& {
-    return *declaration_;
-  }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto type_args() const -> const BindingMap& { return bindings_->args(); }
-
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_->witnesses();
-  }
-
- private:
-  Nonnull<const FunctionDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_;
-};
-
-// A function value.
-class FunctionValue : public FunctionOrMethodValue {
- public:
-  explicit FunctionValue(Nonnull<const FunctionDeclaration*> declaration,
-                         Nonnull<const Bindings*> bindings)
-      : FunctionOrMethodValue(Kind::FunctionValue, declaration, bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::FunctionValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(&declaration(), &bindings());
-  }
-};
-
-// A bound method value. It includes the receiver object.
-class BoundMethodValue : public FunctionOrMethodValue {
- public:
-  explicit BoundMethodValue(Nonnull<const FunctionDeclaration*> declaration,
-                            Nonnull<const Value*> receiver,
-                            Nonnull<const Bindings*> bindings)
-      : FunctionOrMethodValue(Kind::BoundMethodValue, declaration, bindings),
-        receiver_(receiver) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::BoundMethodValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(&declaration(), receiver_, &bindings());
-  }
-
-  auto receiver() const -> Nonnull<const Value*> { return receiver_; }
-
- private:
-  Nonnull<const Value*> receiver_;
-};
-
-// A destructor value.
-class DestructorValue : public Value {
- public:
-  explicit DestructorValue(Nonnull<const DestructorDeclaration*> declaration)
-      : Value(Kind::DestructorValue), declaration_(declaration) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::DestructorValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_);
-  }
-
-  auto declaration() const -> const DestructorDeclaration& {
-    return *declaration_;
-  }
-
- private:
-  Nonnull<const DestructorDeclaration*> declaration_;
-};
-
-// The value of a location in memory.
-class LocationValue : public Value {
- public:
-  explicit LocationValue(Address value)
-      : Value(Kind::LocationValue), value_(std::move(value)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::LocationValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_);
-  }
-
-  auto address() const -> const Address& { return value_; }
-
- private:
-  Address value_;
-};
-
-// Contains the result of the evaluation of an expression, including a value,
-// the original expression category, and an optional address if available.
-class ExpressionResult {
- public:
-  static auto Value(Nonnull<const Carbon::Value*> v) -> ExpressionResult {
-    return ExpressionResult(v, std::nullopt, ExpressionCategory::Value);
-  }
-  static auto Reference(Nonnull<const Carbon::Value*> v, Address address)
-      -> ExpressionResult {
-    return ExpressionResult(v, std::move(address),
-                            ExpressionCategory::Reference);
-  }
-  static auto Initializing(Nonnull<const Carbon::Value*> v, Address address)
-      -> ExpressionResult {
-    return ExpressionResult(v, std::move(address),
-                            ExpressionCategory::Initializing);
-  }
-
-  ExpressionResult(Nonnull<const Carbon::Value*> v,
-                   std::optional<Address> address, ExpressionCategory cat)
-      : value_(v), address_(std::move(address)), expr_cat_(cat) {}
-
-  auto value() const -> Nonnull<const Carbon::Value*> { return value_; }
-  auto address() const -> const std::optional<Address>& { return address_; }
-  auto expression_category() const -> ExpressionCategory { return expr_cat_; }
-
- private:
-  Nonnull<const Carbon::Value*> value_;
-  std::optional<Address> address_;
-  ExpressionCategory expr_cat_;
-};
-
-// Represents the result of the evaluation of a reference expression, and
-// holds the resulting `Value*` and its `Address`.
-class ReferenceExpressionValue : public Value {
- public:
-  ReferenceExpressionValue(Nonnull<const Value*> value, Address address)
-      : Value(Kind::ReferenceExpressionValue),
-        value_(value),
-        address_(std::move(address)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ReferenceExpressionValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_, address_);
-  }
-
-  auto value() const -> Nonnull<const Value*> { return value_; }
-  auto address() const -> const Address& { return address_; }
-
- private:
-  Nonnull<const Value*> value_;
-  Address address_;
-};
-
-// A pointer value
-class PointerValue : public Value {
- public:
-  explicit PointerValue(Address value)
-      : Value(Kind::PointerValue), value_(std::move(value)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::PointerValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_);
-  }
-
-  auto address() const -> const Address& { return value_; }
-
- private:
-  Address value_;
-};
-
-// A bool value.
-class BoolValue : public Value {
- public:
-  explicit BoolValue(bool value) : Value(Kind::BoolValue), value_(value) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::BoolValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_);
-  }
-
-  auto value() const -> bool { return value_; }
-
- private:
-  bool value_;
-};
-
-// A value of a struct type. Note that the expression `{}` is a value of type
-// `{} as type`; the former is a `StructValue` and the latter is a
-// `StructType`.
-class StructValue : public Value {
- public:
-  explicit StructValue(std::vector<NamedValue> elements)
-      : Value(Kind::StructValue), elements_(std::move(elements)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::StructValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(elements_);
-  }
-
-  auto elements() const -> llvm::ArrayRef<NamedValue> { return elements_; }
-
-  // Returns the value of the field named `name` in this struct, or
-  // nullopt if there is no such field.
-  auto FindField(std::string_view name) const
-      -> std::optional<Nonnull<const Value*>>;
-
- private:
-  std::vector<NamedValue> elements_;
-};
-
-// A value of a nominal class type, i.e., an object.
-class NominalClassValue : public Value {
- public:
-  static constexpr llvm::StringLiteral BaseField{"base"};
-
-  // Takes the class type, inits, an optional base, a pointer to a
-  // NominalClassValue*, that must be common to all NominalClassValue of the
-  // same object. The pointee is updated, when `NominalClassValue`s are
-  // constructed, to point to the `NominalClassValue` corresponding to the
-  // child-most class type. Sets *class_value_ptr = this, which corresponds to
-  // the static type of the value matching its dynamic type.
-  NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits,
-                    std::optional<Nonnull<const NominalClassValue*>> base,
-                    Nonnull<const NominalClassValue** const> class_value_ptr);
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::NominalClassValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(type_, inits_, base_, class_value_ptr_);
-  }
-
-  auto type() const -> const Value& { return *type_; }
-  auto inits() const -> const Value& { return *inits_; }
-  auto base() const -> std::optional<Nonnull<const NominalClassValue*>> {
-    return base_;
-  }
-  // Returns a pointer of pointer to the child-most class value.
-  auto class_value_ptr() const -> Nonnull<const NominalClassValue**> {
-    return class_value_ptr_;
-  }
-
- private:
-  Nonnull<const Value*> type_;
-  Nonnull<const Value*> inits_;  // The initializing StructValue.
-  std::optional<Nonnull<const NominalClassValue*>> base_;
-  Nonnull<const NominalClassValue** const> class_value_ptr_;
-};
-
-// An alternative constructor value.
-class AlternativeConstructorValue : public Value {
- public:
-  AlternativeConstructorValue(Nonnull<const ChoiceType*> choice,
-                              Nonnull<const AlternativeSignature*> alternative)
-      : Value(Kind::AlternativeConstructorValue),
-        choice_(choice),
-        alternative_(alternative) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::AlternativeConstructorValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(&choice(), &alternative());
-  }
-
-  auto choice() const -> const ChoiceType& { return *choice_; }
-  auto alternative() const -> const AlternativeSignature& {
-    return *alternative_;
-  }
-
- private:
-  Nonnull<const ChoiceType*> choice_;
-  Nonnull<const AlternativeSignature*> alternative_;
-};
-
-// An alternative value.
-class AlternativeValue : public Value {
- public:
-  AlternativeValue(Nonnull<const ChoiceType*> choice,
-                   Nonnull<const AlternativeSignature*> alternative,
-                   std::optional<Nonnull<const TupleValue*>> argument)
-      : Value(Kind::AlternativeValue),
-        choice_(choice),
-        alternative_(alternative),
-        argument_(argument) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::AlternativeValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(&choice(), &alternative(), argument_);
-  }
-
-  auto choice() const -> const ChoiceType& { return *choice_; }
-  auto alternative() const -> const AlternativeSignature& {
-    return *alternative_;
-  }
-  auto argument() const -> std::optional<Nonnull<const TupleValue*>> {
-    return argument_;
-  }
-
- private:
-  Nonnull<const ChoiceType*> choice_;
-  Nonnull<const AlternativeSignature*> alternative_;
-  std::optional<Nonnull<const TupleValue*>> argument_;
-};
-
-// Base class for tuple types and tuple values. These are the same other than
-// their type-of-type, but we separate them to make it easier to tell types and
-// values apart.
-class TupleValueBase : public Value {
- public:
-  explicit TupleValueBase(Value::Kind kind,
-                          std::vector<Nonnull<const Value*>> elements)
-      : Value(kind), elements_(std::move(elements)) {}
-
-  auto elements() const -> llvm::ArrayRef<Nonnull<const Value*>> {
-    return elements_;
-  }
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TupleValue ||
-           value->kind() == Kind::TupleType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(elements_);
-  }
-
- private:
-  std::vector<Nonnull<const Value*>> elements_;
-};
-
-// A tuple value.
-class TupleValue : public TupleValueBase {
- public:
-  // An empty tuple.
-  static auto Empty() -> Nonnull<const TupleValue*> {
-    static const TupleValue empty =
-        TupleValue(std::vector<Nonnull<const Value*>>());
-    return static_cast<Nonnull<const TupleValue*>>(&empty);
-  }
-
-  explicit TupleValue(std::vector<Nonnull<const Value*>> elements)
-      : TupleValueBase(Kind::TupleValue, std::move(elements)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TupleValue;
-  }
-};
-
-// A tuple type. These values are produced by converting a tuple value
-// containing only types to type `type`.
-class TupleType : public TupleValueBase {
- public:
-  // The unit type.
-  static auto Empty() -> Nonnull<const TupleType*> {
-    static const TupleType empty =
-        TupleType(std::vector<Nonnull<const Value*>>());
-    return static_cast<Nonnull<const TupleType*>>(&empty);
-  }
-
-  explicit TupleType(std::vector<Nonnull<const Value*>> elements)
-      : TupleValueBase(Kind::TupleType, std::move(elements)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TupleType;
-  }
-};
-
-// A binding placeholder value.
-class BindingPlaceholderValue : public Value {
- public:
-  // Represents the `_` placeholder.
-  explicit BindingPlaceholderValue() : Value(Kind::BindingPlaceholderValue) {}
-
-  // Represents a named placeholder.
-  explicit BindingPlaceholderValue(ValueNodeView value_node)
-      : Value(Kind::BindingPlaceholderValue),
-        value_node_(std::move(value_node)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::BindingPlaceholderValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return value_node_ ? f(*value_node_) : f();
-  }
-
-  auto value_node() const -> const std::optional<ValueNodeView>& {
-    return value_node_;
-  }
-
- private:
-  std::optional<ValueNodeView> value_node_;
-};
-
-// Value for addr pattern
-class AddrValue : public Value {
- public:
-  explicit AddrValue(Nonnull<const Value*> pattern)
-      : Value(Kind::AddrValue), pattern_(pattern) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::AddrValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(pattern_);
-  }
-
-  auto pattern() const -> const Value& { return *pattern_; }
-
- private:
-  Nonnull<const Value*> pattern_;
-};
-
-// Value for uninitialized local variables.
-class UninitializedValue : public Value {
- public:
-  explicit UninitializedValue(Nonnull<const Value*> pattern)
-      : Value(Kind::UninitializedValue), pattern_(pattern) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::UninitializedValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(pattern_);
-  }
-
-  auto pattern() const -> const Value& { return *pattern_; }
-
- private:
-  Nonnull<const Value*> pattern_;
-};
-
-// The int type.
-class IntType : public Value {
- public:
-  IntType() : Value(Kind::IntType) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::IntType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f();
-  }
-};
-
-// The bool type.
-class BoolType : public Value {
- public:
-  BoolType() : Value(Kind::BoolType) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::BoolType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f();
-  }
-};
-
-// A type type.
-class TypeType : public Value {
- public:
-  TypeType() : Value(Kind::TypeType) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TypeType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f();
-  }
-};
-
-// A function type.
-class FunctionType : public Value {
- public:
-  // An explicit function parameter that is a `:!` binding:
-  //
-  //     fn MakeEmptyVector(T:! type) -> Vector(T);
-  struct GenericParameter : public HashFromDecompose<GenericParameter> {
-    template <typename F>
-    auto Decompose(F f) const {
-      return f(index, binding);
-    }
-
-    size_t index;
-    Nonnull<const GenericBinding*> binding;
-  };
-
-  // For methods with unbound `self` parameters.
-  struct MethodSelf : public HashFromDecompose<MethodSelf> {
-    template <typename F>
-    auto Decompose(F f) const {
-      return f(addr_self, self_type);
-    }
-
-    // True if `self` parameter uses an `addr` pattern.
-    bool addr_self;
-    // Type of `self` parameter.
-    const Value* self_type;
-  };
-
-  FunctionType(std::optional<MethodSelf> method_self,
-               Nonnull<const Value*> parameters,
-               Nonnull<const Value*> return_type)
-      : FunctionType(method_self, parameters, {}, return_type, {}, {},
-                     /*is_initializing=*/false) {}
-
-  FunctionType(std::optional<MethodSelf> method_self,
-               Nonnull<const Value*> parameters,
-               std::vector<GenericParameter> generic_parameters,
-               Nonnull<const Value*> return_type,
-               std::vector<Nonnull<const GenericBinding*>> deduced_bindings,
-               std::vector<Nonnull<const ImplBinding*>> impl_bindings,
-               bool is_initializing)
-      : Value(Kind::FunctionType),
-        method_self_(method_self),
-        parameters_(parameters),
-        generic_parameters_(std::move(generic_parameters)),
-        return_type_(return_type),
-        deduced_bindings_(std::move(deduced_bindings)),
-        impl_bindings_(std::move(impl_bindings)),
-        is_initializing_(is_initializing) {}
-
-  struct ExceptSelf : public HashFromDecompose<ExceptSelf> {
-    template <typename F>
-    auto Decompose(F f) const {
-      return f();
-    }
-  };
-
-  FunctionType(ExceptSelf, const FunctionType* clone)
-      : FunctionType(std::nullopt, clone->parameters_,
-                     clone->generic_parameters_, clone->return_type_,
-                     clone->deduced_bindings_, clone->impl_bindings_,
-                     clone->is_initializing_) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::FunctionType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(method_self_, parameters_, generic_parameters_, return_type_,
-             deduced_bindings_, impl_bindings_, is_initializing_);
-  }
-
-  // The type of the function parameter tuple.
-  auto parameters() const -> const Value& { return *parameters_; }
-  // Parameters that use a generic `:!` binding at the top level.
-  auto generic_parameters() const -> llvm::ArrayRef<GenericParameter> {
-    return generic_parameters_;
-  }
-  // The function return type.
-  auto return_type() const -> const Value& { return *return_type_; }
-  // All generic bindings in this function's signature that should be deduced
-  // in a call. This excludes any generic parameters.
-  auto deduced_bindings() const
-      -> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
-    return deduced_bindings_;
-  }
-  // The bindings for the impl witness tables required by the
-  // bounds on the type parameters of the generic function.
-  auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
-    return impl_bindings_;
-  }
-  // Return whether the function type is an initializing expression or not.
-  auto is_initializing() const -> bool { return is_initializing_; }
-
-  // Binding for the implicit `self` parameter, if this is an unbound method.
-  auto method_self() const -> std::optional<MethodSelf> { return method_self_; }
-
- private:
-  std::optional<MethodSelf> method_self_;
-  Nonnull<const Value*> parameters_;
-  std::vector<GenericParameter> generic_parameters_;
-  Nonnull<const Value*> return_type_;
-  std::vector<Nonnull<const GenericBinding*>> deduced_bindings_;
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
-  bool is_initializing_;
-};
-
-// A pointer type.
-class PointerType : public Value {
- public:
-  // Constructs a pointer type with the given pointee type.
-  explicit PointerType(Nonnull<const Value*> pointee_type)
-      : Value(Kind::PointerType), pointee_type_(pointee_type) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::PointerType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(pointee_type_);
-  }
-
-  auto pointee_type() const -> const Value& { return *pointee_type_; }
-
- private:
-  Nonnull<const Value*> pointee_type_;
-};
-
-// The `auto` type.
-class AutoType : public Value {
- public:
-  AutoType() : Value(Kind::AutoType) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::AutoType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f();
-  }
-};
-
-// A struct type.
-class StructType : public Value {
- public:
-  StructType() : StructType(std::vector<NamedValue>{}) {}
-
-  explicit StructType(std::vector<NamedValue> fields)
-      : Value(Kind::StructType), fields_(std::move(fields)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::StructType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(fields_);
-  }
-
-  auto fields() const -> llvm::ArrayRef<NamedValue> { return fields_; }
-
- private:
-  std::vector<NamedValue> fields_;
-};
-
-// A class type.
-class NominalClassType : public Value {
- public:
-  explicit NominalClassType(
-      Nonnull<const ClassDeclaration*> declaration,
-      Nonnull<const Bindings*> bindings,
-      std::optional<Nonnull<const NominalClassType*>> base,
-      Nonnull<const VTable*> class_vtable)
-      : Value(Kind::NominalClassType),
-        declaration_(declaration),
-        bindings_(bindings),
-        base_(base),
-        vtable_(class_vtable),
-        hierarchy_level_(base ? (*base)->hierarchy_level() + 1 : 0) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::NominalClassType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_, base_, vtable_);
-  }
-
-  auto declaration() const -> const ClassDeclaration& { return *declaration_; }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto base() const -> std::optional<Nonnull<const NominalClassType*>> {
-    return base_;
-  }
-
-  auto type_args() const -> const BindingMap& { return bindings_->args(); }
-
-  // Witnesses for each of the class's impl bindings.
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_->witnesses();
-  }
-
-  auto vtable() const -> const VTable& { return *vtable_; }
-
-  // Returns how many levels from the top ancestor class it is. i.e. a class
-  // with no base returns `0`, while a class with a `.base` and `.base.base`
-  // returns `2`.
-  auto hierarchy_level() const -> int { return hierarchy_level_; }
-
-  // Returns whether this a parameterized class. That is, a class with
-  // parameters and no corresponding arguments.
-  auto IsParameterized() const -> bool {
-    return declaration_->type_params().has_value() && type_args().empty();
-  }
-
-  // Returns whether this class is, or inherits `other`.
-  auto InheritsClass(Nonnull<const Value*> other) const -> bool;
-
- private:
-  Nonnull<const ClassDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_ = Bindings::None();
-  const std::optional<Nonnull<const NominalClassType*>> base_;
-  Nonnull<const VTable*> vtable_;
-  int hierarchy_level_;
-};
-
-class MixinPseudoType : public Value {
- public:
-  explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration)
-      : Value(Kind::MixinPseudoType), declaration_(declaration) {
-    CARBON_CHECK(!declaration->params().has_value(),
-                 "missing arguments for parameterized mixin type");
-  }
-  explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration,
-                           Nonnull<const Bindings*> bindings)
-      : Value(Kind::MixinPseudoType),
-        declaration_(declaration),
-        bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::MixinPseudoType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_);
-  }
-
-  auto declaration() const -> const MixinDeclaration& { return *declaration_; }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto args() const -> const BindingMap& { return bindings_->args(); }
-
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_->witnesses();
-  }
-
-  auto FindFunction(const std::string_view& name) const
-      -> std::optional<Nonnull<const FunctionValue*>>;
-
- private:
-  Nonnull<const MixinDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_ = Bindings::None();
-};
-
-// Returns the value of the function named `name` in this class, or
-// nullopt if there is no such function.
-auto FindFunction(std::string_view name,
-                  llvm::ArrayRef<Nonnull<Declaration*>> members)
-    -> std::optional<Nonnull<const FunctionValue*>>;
-
-// Returns the value of the function named `name` in this class and its
-// parents, or nullopt if there is no such function.
-auto FindFunctionWithParents(std::string_view name,
-                             const ClassDeclaration& class_decl)
-    -> std::optional<Nonnull<const FunctionValue*>>;
-
-// Return the declaration of the member with the given name.
-auto FindMember(std::string_view name,
-                llvm::ArrayRef<Nonnull<Declaration*>> members)
-    -> std::optional<Nonnull<const Declaration*>>;
-
-// An interface type.
-class InterfaceType : public Value {
- public:
-  explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration)
-      : Value(Kind::InterfaceType), declaration_(declaration) {
-    CARBON_CHECK(!declaration->params().has_value(),
-                 "missing arguments for parameterized interface type");
-  }
-  explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration,
-                         Nonnull<const Bindings*> bindings)
-      : Value(Kind::InterfaceType),
-        declaration_(declaration),
-        bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::InterfaceType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_);
-  }
-
-  auto declaration() const -> const InterfaceDeclaration& {
-    return *declaration_;
-  }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto args() const -> const BindingMap& { return bindings_->args(); }
-
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_->witnesses();
-  }
-
- private:
-  Nonnull<const InterfaceDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_ = Bindings::None();
-};
-
-// A named constraint type.
-class NamedConstraintType : public Value {
- public:
-  explicit NamedConstraintType(
-      Nonnull<const ConstraintDeclaration*> declaration,
-      Nonnull<const Bindings*> bindings)
-      : Value(Kind::NamedConstraintType),
-        declaration_(declaration),
-        bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::NamedConstraintType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_);
-  }
-
-  auto declaration() const -> const ConstraintDeclaration& {
-    return *declaration_;
-  }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
- private:
-  Nonnull<const ConstraintDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_ = Bindings::None();
-};
-
-// A constraint that requires implementation of an interface.
-struct ImplsConstraint : public HashFromDecompose<ImplsConstraint> {
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(type, interface);
-  }
-
-  // The type that is required to implement the interface.
-  Nonnull<const Value*> type;
-  // The interface that is required to be implemented.
-  Nonnull<const InterfaceType*> interface;
-};
-
-// A constraint that requires an intrinsic property of a type.
-struct IntrinsicConstraint : public HashFromDecompose<IntrinsicConstraint>,
-                             public Printable<IntrinsicConstraint> {
-  enum Kind {
-    // `type` intrinsically implicitly converts to `parameters[0]`.
-    // TODO: Split ImplicitAs into more specific constraints (such as
-    // derived-to-base pointer conversions).
-    ImplicitAs,
-  };
-
-  explicit IntrinsicConstraint(Nonnull<const Value*> type, Kind kind,
-                               std::vector<Nonnull<const Value*>> arguments)
-      : type(type), kind(kind), arguments(std::move(arguments)) {}
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(type, kind, arguments);
-  }
-
-  // Print the intrinsic constraint.
-  void Print(llvm::raw_ostream& out) const;
-
-  // The type that is required to satisfy the intrinsic property.
-  Nonnull<const Value*> type;
-  // The kind of the intrinsic property.
-  Kind kind;
-  // Arguments for the intrinsic property. The meaning of these depends on
-  // `kind`.
-  std::vector<Nonnull<const Value*>> arguments;
-};
-
-// A constraint that a collection of values are known to be the same.
-struct EqualityConstraint : public HashFromDecompose<EqualityConstraint> {
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(values);
-  }
-
-  // Visit the values in this equality constraint that are a single step away
-  // from the given value according to this equality constraint. That is: if
-  // `value` is identical to a value in `values`, then call the visitor on all
-  // values in `values` that are not identical to `value`. Otherwise, do not
-  // call the visitor.
-  //
-  // Stops and returns `false` if any call to the visitor returns `false`,
-  // otherwise returns `true`.
-  auto VisitEqualValues(
-      Nonnull<const Value*> value,
-      llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool;
-
-  std::vector<Nonnull<const Value*>> values;
-};
-
-// A constraint indicating that access to an associated constant should be
-// replaced by another value.
-struct RewriteConstraint : public HashFromDecompose<RewriteConstraint> {
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(constant, unconverted_replacement, unconverted_replacement_type,
-             converted_replacement);
-  }
-
-  // The associated constant value that is rewritten.
-  Nonnull<const AssociatedConstant*> constant;
-  // The replacement in its original type.
-  Nonnull<const Value*> unconverted_replacement;
-  // The type of the replacement.
-  Nonnull<const Value*> unconverted_replacement_type;
-  // The replacement after conversion to the type of the associated constant.
-  Nonnull<const Value*> converted_replacement;
-};
-
-// A context in which we might look up a name.
-struct LookupContext : public HashFromDecompose<LookupContext> {
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(context);
-  }
-
-  Nonnull<const Value*> context;
-};
-
-// A type-of-type for an unknown constrained type.
-//
-// These types are formed by the `&` operator that combines constraints and by
-// `where` expressions.
-//
-// A constraint has three main properties:
-//
-// * A collection of (type, interface) pairs for interfaces that are known to
-//   be implemented by a type satisfying the constraint.
-// * A collection of (type, intrinsic) pairs for intrinsic properties that are
-//   known to be satisfied by a type satisfying the constraint.
-// * A collection of sets of values, typically associated constants, that are
-//   known to be the same.
-// * A collection of contexts in which member name lookups will be performed
-//   for a type variable whose type is this constraint.
-//
-// Within these properties, the constrained type can be referred to with a
-// `VariableType` naming the `self_binding`.
-class ConstraintType : public Value {
- public:
-  explicit ConstraintType(
-      Nonnull<const GenericBinding*> self_binding,
-      std::vector<ImplsConstraint> impls_constraints,
-      std::vector<IntrinsicConstraint> intrinsic_constraints,
-      std::vector<EqualityConstraint> equality_constraints,
-      std::vector<RewriteConstraint> rewrite_constraints,
-      std::vector<LookupContext> lookup_contexts)
-      : Value(Kind::ConstraintType),
-        self_binding_(self_binding),
-        impls_constraints_(std::move(impls_constraints)),
-        intrinsic_constraints_(std::move(intrinsic_constraints)),
-        equality_constraints_(std::move(equality_constraints)),
-        rewrite_constraints_(std::move(rewrite_constraints)),
-        lookup_contexts_(std::move(lookup_contexts)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ConstraintType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(self_binding_, impls_constraints_, intrinsic_constraints_,
-             equality_constraints_, rewrite_constraints_, lookup_contexts_);
-  }
-
-  auto self_binding() const -> Nonnull<const GenericBinding*> {
-    return self_binding_;
-  }
-
-  auto impls_constraints() const -> llvm::ArrayRef<ImplsConstraint> {
-    return impls_constraints_;
-  }
-
-  auto intrinsic_constraints() const -> llvm::ArrayRef<IntrinsicConstraint> {
-    return intrinsic_constraints_;
-  }
-
-  auto equality_constraints() const -> llvm::ArrayRef<EqualityConstraint> {
-    return equality_constraints_;
-  }
-
-  auto rewrite_constraints() const -> llvm::ArrayRef<RewriteConstraint> {
-    return rewrite_constraints_;
-  }
-
-  auto lookup_contexts() const -> llvm::ArrayRef<LookupContext> {
-    return lookup_contexts_;
-  }
-
-  // Visit the values in that are a single step away from the given value
-  // according to equality constraints in this constraint type, that is, the
-  // values `v` that are not identical to `value` but for which we have a
-  // `value == v` equality constraint in this constraint type.
-  //
-  // Stops and returns `false` if any call to the visitor returns `false`,
-  // otherwise returns `true`.
-  auto VisitEqualValues(
-      Nonnull<const Value*> value,
-      llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool;
-
- private:
-  Nonnull<const GenericBinding*> self_binding_;
-  std::vector<ImplsConstraint> impls_constraints_;
-  std::vector<IntrinsicConstraint> intrinsic_constraints_;
-  std::vector<EqualityConstraint> equality_constraints_;
-  std::vector<RewriteConstraint> rewrite_constraints_;
-  std::vector<LookupContext> lookup_contexts_;
-};
-
-// A witness table.
-class Witness : public Value {
- protected:
-  explicit Witness(Value::Kind kind) : Value(kind) {}
-
- public:
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ImplWitness ||
-           value->kind() == Kind::BindingWitness ||
-           value->kind() == Kind::ConstraintWitness ||
-           value->kind() == Kind::ConstraintImplWitness;
-  }
-};
-
-// The witness table for an impl.
-class ImplWitness : public Witness {
- public:
-  // Construct a witness for an impl.
-  explicit ImplWitness(Nonnull<const ImplDeclaration*> declaration,
-                       Nonnull<const Bindings*> bindings)
-      : Witness(Kind::ImplWitness),
-        declaration_(declaration),
-        bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ImplWitness;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_);
-  }
-
-  auto declaration() const -> const ImplDeclaration& { return *declaration_; }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto type_args() const -> const BindingMap& { return bindings_->args(); }
-
-  auto witnesses() const -> const ImplWitnessMap& {
-    return bindings_->witnesses();
-  }
-
- private:
-  Nonnull<const ImplDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_ = Bindings::None();
-};
-
-// The symbolic witness corresponding to an unresolved impl binding.
-class BindingWitness : public Witness {
- public:
-  // Construct a witness for an impl binding.
-  explicit BindingWitness(Nonnull<const ImplBinding*> binding)
-      : Witness(Kind::BindingWitness), binding_(binding) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::BindingWitness;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(binding_);
-  }
-
-  auto binding() const -> Nonnull<const ImplBinding*> { return binding_; }
-
- private:
-  Nonnull<const ImplBinding*> binding_;
-};
-
-// A witness for a constraint type, expressed as a tuple of witnesses for the
-// individual impls constraints in the constraint type.
-class ConstraintWitness : public Witness {
- public:
-  explicit ConstraintWitness(std::vector<Nonnull<const Witness*>> witnesses)
-      : Witness(Kind::ConstraintWitness), witnesses_(std::move(witnesses)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ConstraintWitness;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(witnesses_);
-  }
-
-  auto witnesses() const -> llvm::ArrayRef<Nonnull<const Witness*>> {
-    return witnesses_;
-  }
-
- private:
-  std::vector<Nonnull<const Witness*>> witnesses_;
-};
-
-// A witness for an impls constraint in a constraint type, expressed in terms of
-// a symbolic witness for the constraint type.
-class ConstraintImplWitness : public Witness {
- public:
-  // Make a witness for the given impls_constraint of the given `ConstraintType`
-  // witness. If we're indexing into a known tuple of witnesses, pull out the
-  // element.
-  static auto Make(Nonnull<Arena*> arena, Nonnull<const Witness*> witness,
-                   int index) -> Nonnull<const Witness*> {
-    CARBON_CHECK(!llvm::isa<ImplWitness>(witness),
-                 "impl witness has no components to access");
-    if (const auto* constraint_witness =
-            llvm::dyn_cast<ConstraintWitness>(witness)) {
-      return constraint_witness->witnesses()[index];
-    }
-    return arena->New<ConstraintImplWitness>(witness, index);
-  }
-
-  explicit ConstraintImplWitness(Nonnull<const Witness*> constraint_witness,
-                                 int index)
-      : Witness(Kind::ConstraintImplWitness),
-        constraint_witness_(constraint_witness),
-        index_(index) {
-    CARBON_CHECK(!llvm::isa<ConstraintWitness>(constraint_witness),
-                 "should have resolved element from constraint witness");
-  }
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ConstraintImplWitness;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(constraint_witness_, index_);
-  }
-
-  // Get the witness for the complete `ConstraintType`.
-  auto constraint_witness() const -> Nonnull<const Witness*> {
-    return constraint_witness_;
-  }
-
-  // Get the index of the impls constraint within the constraint type.
-  auto index() const -> int { return index_; }
-
- private:
-  Nonnull<const Witness*> constraint_witness_;
-  int index_;
-};
-
-// Allocate a `ConstraintImplWitness` using the custom `Make` function.
-template <>
-struct AllocateTrait<ConstraintImplWitness> {
-  template <typename... Args>
-  static auto New(Nonnull<Arena*> arena, Args&&... args)
-      -> Nonnull<const Witness*> {
-    return ConstraintImplWitness::Make(arena, std::forward<Args>(args)...);
-  }
-};
-
-// A choice type.
-class ChoiceType : public Value {
- public:
-  ChoiceType(Nonnull<const ChoiceDeclaration*> declaration,
-             Nonnull<const Bindings*> bindings)
-      : Value(Kind::ChoiceType),
-        declaration_(declaration),
-        bindings_(bindings) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ChoiceType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, bindings_);
-  }
-
-  auto bindings() const -> const Bindings& { return *bindings_; }
-
-  auto type_args() const -> const BindingMap& { return bindings_->args(); }
-
-  auto declaration() const -> const ChoiceDeclaration& { return *declaration_; }
-
-  auto IsParameterized() const -> bool {
-    return declaration_->type_params().has_value();
-  }
-
- private:
-  Nonnull<const ChoiceDeclaration*> declaration_;
-  Nonnull<const Bindings*> bindings_;
-};
-
-// A variable type.
-class VariableType : public Value {
- public:
-  explicit VariableType(Nonnull<const GenericBinding*> binding)
-      : Value(Kind::VariableType), binding_(binding) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::VariableType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(binding_);
-  }
-
-  auto binding() const -> const GenericBinding& { return *binding_; }
-
- private:
-  Nonnull<const GenericBinding*> binding_;
-};
-
-// A name of an entity that has explicit parameters, such as a parameterized
-// class or interface. When arguments for those parameters are provided in a
-// call, the result will be a class type or interface type.
-class ParameterizedEntityName : public Value {
- public:
-  explicit ParameterizedEntityName(Nonnull<const Declaration*> declaration,
-                                   Nonnull<const TuplePattern*> params)
-      : Value(Kind::ParameterizedEntityName),
-        declaration_(declaration),
-        params_(params) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::ParameterizedEntityName;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(declaration_, params_);
-  }
-
-  auto declaration() const -> const Declaration& { return *declaration_; }
-  auto params() const -> const TuplePattern& { return *params_; }
-
- private:
-  Nonnull<const Declaration*> declaration_;
-  Nonnull<const TuplePattern*> params_;
-};
-
-// The name of a member of a class or interface.
-//
-// These values are used to represent the second operand of a compound member
-// access expression: `x.(A.B)`, and can also be the value of an alias
-// declaration, but cannot be used in most other contexts.
-class MemberName : public Value, public Printable<MemberName> {
- public:
-  MemberName(std::optional<Nonnull<const Value*>> base_type,
-             std::optional<Nonnull<const InterfaceType*>> interface,
-             Nonnull<const NamedElement*> member)
-      : Value(Kind::MemberName),
-        base_type_(base_type),
-        interface_(interface),
-        member_(member) {
-    CARBON_CHECK(base_type || interface,
-                 "member name must be in a type, an interface, or both");
-  }
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::MemberName;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(base_type_, interface_, member_);
-  }
-
-  // The type for which `name` is a member or a member of an `impl`.
-  auto base_type() const -> std::optional<Nonnull<const Value*>> {
-    return base_type_;
-  }
-  // The interface for which `name` is a member, if any.
-  auto interface() const -> std::optional<Nonnull<const InterfaceType*>> {
-    return interface_;
-  }
-  // The member.
-  auto member() const -> const NamedElement& { return *member_; }
-  // The name of the member.
-  auto name() const -> std::string_view { return member().name(); }
-
- private:
-  std::optional<Nonnull<const Value*>> base_type_;
-  std::optional<Nonnull<const InterfaceType*>> interface_;
-  Nonnull<const NamedElement*> member_;
-};
-
-// A symbolic value representing an associated constant.
-//
-// This is a value of the form `A.B` or `A.B.C` or similar, where `A` is a
-// `VariableType`.
-class AssociatedConstant : public Value {
- public:
-  explicit AssociatedConstant(
-      Nonnull<const Value*> base, Nonnull<const InterfaceType*> interface,
-      Nonnull<const AssociatedConstantDeclaration*> constant,
-      Nonnull<const Witness*> witness)
-      : Value(Kind::AssociatedConstant),
-        base_(base),
-        interface_(interface),
-        constant_(constant),
-        witness_(witness) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::AssociatedConstant;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(base_, interface_, constant_, witness_);
-  }
-
-  // The type for which we denote an associated constant.
-  auto base() const -> const Value& { return *base_; }
-
-  // The interface within which the constant was declared.
-  auto interface() const -> const InterfaceType& { return *interface_; }
-
-  // The associated constant whose value is being denoted.
-  auto constant() const -> const AssociatedConstantDeclaration& {
-    return *constant_;
-  }
-
-  // Witness within which the constant's value can be found.
-  auto witness() const -> const Witness& { return *witness_; }
-
- private:
-  Nonnull<const Value*> base_;
-  Nonnull<const InterfaceType*> interface_;
-  Nonnull<const AssociatedConstantDeclaration*> constant_;
-  Nonnull<const Witness*> witness_;
-};
-
-// The String type.
-class StringType : public Value {
- public:
-  StringType() : Value(Kind::StringType) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::StringType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f();
-  }
-};
-
-// A string value.
-class StringValue : public Value {
- public:
-  explicit StringValue(std::string value)
-      : Value(Kind::StringValue), value_(std::move(value)) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::StringValue;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(value_);
-  }
-
-  auto value() const -> const std::string& { return value_; }
-
- private:
-  std::string value_;
-};
-
-class TypeOfMixinPseudoType : public Value {
- public:
-  explicit TypeOfMixinPseudoType(Nonnull<const MixinPseudoType*> class_type)
-      : Value(Kind::TypeOfMixinPseudoType), mixin_type_(class_type) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TypeOfMixinPseudoType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(mixin_type_);
-  }
-
-  auto mixin_type() const -> const MixinPseudoType& { return *mixin_type_; }
-
- private:
-  Nonnull<const MixinPseudoType*> mixin_type_;
-};
-
-// The type of an expression whose value is the name of a parameterized entity.
-// Such an expression can only be used as the operand of a call expression that
-// provides arguments for the parameters.
-class TypeOfParameterizedEntityName : public Value {
- public:
-  explicit TypeOfParameterizedEntityName(
-      Nonnull<const ParameterizedEntityName*> name)
-      : Value(Kind::TypeOfParameterizedEntityName), name_(name) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TypeOfParameterizedEntityName;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(name_);
-  }
-
-  auto name() const -> const ParameterizedEntityName& { return *name_; }
-
- private:
-  Nonnull<const ParameterizedEntityName*> name_;
-};
-
-// The type of a member name expression.
-//
-// This is used for member names that don't denote a specific object or value
-// until used on the right-hand side of a `.`, such as an instance method or
-// field name, or any member function in an interface.
-//
-// Such expressions can appear only as the target of an `alias` declaration or
-// as the member name in a compound member access.
-class TypeOfMemberName : public Value {
- public:
-  explicit TypeOfMemberName(Nonnull<const NamedElement*> member)
-      : Value(Kind::TypeOfMemberName), member_(member) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TypeOfMemberName;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(member_);
-  }
-
-  // TODO: consider removing this or moving it elsewhere in the AST,
-  // since it's arguably part of the expression value rather than its type.
-  auto member() const -> const NamedElement& { return *member_; }
-
- private:
-  Nonnull<const NamedElement*> member_;
-};
-
-// The type of a namespace name.
-//
-// Such expressions can appear only as the target of an `alias` declaration or
-// as the left-hand side of a simple member access expression.
-class TypeOfNamespaceName : public Value {
- public:
-  explicit TypeOfNamespaceName(
-      Nonnull<const NamespaceDeclaration*> namespace_decl)
-      : Value(Kind::TypeOfNamespaceName), namespace_decl_(namespace_decl) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::TypeOfNamespaceName;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(namespace_decl_);
-  }
-
-  auto namespace_decl() const -> Nonnull<const NamespaceDeclaration*> {
-    return namespace_decl_;
-  }
-
- private:
-  Nonnull<const NamespaceDeclaration*> namespace_decl_;
-};
-
-// The type of a statically-sized array.
-//
-// Note that values of this type are represented as tuples.
-class StaticArrayType : public Value {
- public:
-  // Constructs a statically-sized array type with the given element type and
-  // size.
-  StaticArrayType(Nonnull<const Value*> element_type,
-                  std::optional<size_t> size)
-      : Value(Kind::StaticArrayType),
-        element_type_(element_type),
-        size_(size) {}
-
-  static auto classof(const Value* value) -> bool {
-    return value->kind() == Kind::StaticArrayType;
-  }
-
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(element_type_, size_);
-  }
-
-  auto element_type() const -> const Value& { return *element_type_; }
-  auto size() const -> size_t {
-    CARBON_CHECK(has_size());
-    return *size_;
-  }
-  auto has_size() const -> bool { return size_.has_value(); }
-
- private:
-  Nonnull<const Value*> element_type_;
-  std::optional<size_t> size_;
-};
-
-template <typename R, typename F>
-auto Value::Visit(F f) const -> R {
-  switch (kind()) {
-#define CARBON_VALUE_KIND(kind) \
-  case Kind::kind:              \
-    return f(static_cast<const kind*>(this));
-#include "explorer/ast/value_kinds.def"
-  }
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_VALUE_H_

+ 0 - 59
explorer/ast/value_kinds.def

@@ -1,59 +0,0 @@
-// 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 .def file expands the CARBON_VALUE_KIND macro once for each kind of
-// Value. The macro should be defined as taking a single argument, which is the
-// name of the Value type.
-
-#ifndef CARBON_VALUE_KIND
-#error #define CARBON_VALUE_KIND(kind) before including this header
-#endif
-
-CARBON_VALUE_KIND(IntValue)
-CARBON_VALUE_KIND(FunctionValue)
-CARBON_VALUE_KIND(DestructorValue)
-CARBON_VALUE_KIND(BoundMethodValue)
-CARBON_VALUE_KIND(PointerValue)
-CARBON_VALUE_KIND(LocationValue)
-CARBON_VALUE_KIND(ReferenceExpressionValue)
-CARBON_VALUE_KIND(BoolValue)
-CARBON_VALUE_KIND(StructValue)
-CARBON_VALUE_KIND(NominalClassValue)
-CARBON_VALUE_KIND(AlternativeValue)
-CARBON_VALUE_KIND(TupleValue)
-CARBON_VALUE_KIND(UninitializedValue)
-CARBON_VALUE_KIND(ImplWitness)
-CARBON_VALUE_KIND(BindingWitness)
-CARBON_VALUE_KIND(ConstraintWitness)
-CARBON_VALUE_KIND(ConstraintImplWitness)
-CARBON_VALUE_KIND(IntType)
-CARBON_VALUE_KIND(BoolType)
-CARBON_VALUE_KIND(TypeType)
-CARBON_VALUE_KIND(FunctionType)
-CARBON_VALUE_KIND(PointerType)
-CARBON_VALUE_KIND(AutoType)
-CARBON_VALUE_KIND(StructType)
-CARBON_VALUE_KIND(NominalClassType)
-CARBON_VALUE_KIND(TupleType)
-CARBON_VALUE_KIND(MixinPseudoType)
-CARBON_VALUE_KIND(InterfaceType)
-CARBON_VALUE_KIND(NamedConstraintType)
-CARBON_VALUE_KIND(ConstraintType)
-CARBON_VALUE_KIND(ChoiceType)
-CARBON_VALUE_KIND(VariableType)
-CARBON_VALUE_KIND(AssociatedConstant)
-CARBON_VALUE_KIND(ParameterizedEntityName)
-CARBON_VALUE_KIND(MemberName)
-CARBON_VALUE_KIND(BindingPlaceholderValue)
-CARBON_VALUE_KIND(AddrValue)
-CARBON_VALUE_KIND(AlternativeConstructorValue)
-CARBON_VALUE_KIND(StringType)
-CARBON_VALUE_KIND(StringValue)
-CARBON_VALUE_KIND(TypeOfMixinPseudoType)
-CARBON_VALUE_KIND(TypeOfParameterizedEntityName)
-CARBON_VALUE_KIND(TypeOfMemberName)
-CARBON_VALUE_KIND(TypeOfNamespaceName)
-CARBON_VALUE_KIND(StaticArrayType)
-
-#undef CARBON_VALUE_KIND

+ 0 - 158
explorer/ast/value_node.h

@@ -1,158 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_VALUE_NODE_H_
-#define CARBON_EXPLORER_AST_VALUE_NODE_H_
-
-#include <functional>
-#include <optional>
-#include <string_view>
-
-#include "explorer/ast/ast_node.h"
-#include "explorer/ast/clone_context.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/base/nonnull.h"
-
-namespace Carbon {
-
-class Value;
-
-// The placeholder name exposed by anonymous ValueNodes.
-static constexpr std::string_view AnonymousName = "_";
-
-// ImplementsValueNode is true if NodeType::ImplementsCarbonValueNode
-// is valid and names a type, indicating that NodeType implements the
-// ValueNode interface, defined below.
-
-template <typename NodeType, typename = void>
-static constexpr bool ImplementsValueNode = false;
-
-// ValueNode is an interface implemented by AstNodes that can be associated
-// with a value, such as declarations and bindings. The interface consists of
-// the following methods:
-//
-// // Returns the constant associated with the node.
-// // This is called by the interpreter, not the type checker.
-// auto constant_value() const -> std::optional<Nonnull<const Value*>>;
-//
-// // Returns the symbolic compile-time identity of the node.
-// // This is called by the type checker, not the interpreter.
-// auto symbolic_identity() const -> std::optional<Nonnull<const Value*>>;
-//
-// // Returns the static type of an IdentifierExpression that names *this.
-// auto static_type() const -> const Value&;
-//
-// // Returns the value category of an IdentifierExpression that names *this.
-// auto expression_category() const -> ExpressionCategory;
-//
-// // Print the node's identity (e.g. its name).
-// void PrintID(llvm::raw_ostream& out) const;
-//
-// TODO: consider turning the above documentation into real code, as sketched
-// at https://godbolt.org/z/186oEozhc
-
-template <typename T>
-static constexpr bool
-    ImplementsValueNode<T, typename T::ImplementsCarbonValueNode> = true;
-
-class ValueNodeView : public Printable<ValueNodeView> {
- public:
-  template <typename NodeType,
-            typename = std::enable_if_t<ImplementsValueNode<NodeType>>>
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  ValueNodeView(Nonnull<const NodeType*> node)
-      // Type-erase NodeType, retaining a pointer to the base class AstNode
-      // and using std::function to encapsulate the ability to call
-      // the derived class's methods.
-      : base_(node),
-        constant_value_(
-            [](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
-              return llvm::cast<NodeType>(base).constant_value();
-            }),
-        symbolic_identity_(
-            [](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
-              return llvm::cast<NodeType>(base).symbolic_identity();
-            }),
-        print_([](const AstNode& base, llvm::raw_ostream& out) -> void {
-          // TODO: change this to print a summary of the node
-          return llvm::cast<NodeType>(base).PrintID(out);
-        }),
-        static_type_([](const AstNode& base) -> const Value& {
-          return llvm::cast<NodeType>(base).static_type();
-        }),
-        expression_category_([](const AstNode& base) -> ExpressionCategory {
-          return llvm::cast<NodeType>(base).expression_category();
-        }) {}
-
-  explicit ValueNodeView(CloneContext& context, const ValueNodeView& other)
-      : base_(context.Remap(other.base_)),
-        // We assume the clone is the same kind of node as the original.
-        constant_value_(other.constant_value_),
-        symbolic_identity_(other.symbolic_identity_),
-        print_(other.print_),
-        static_type_(other.static_type_),
-        expression_category_(other.expression_category_) {}
-
-  ValueNodeView(const ValueNodeView&) = default;
-  ValueNodeView(ValueNodeView&&) = default;
-  auto operator=(const ValueNodeView&) -> ValueNodeView& = default;
-  auto operator=(ValueNodeView&&) -> ValueNodeView& = default;
-
-  // Returns `node` as an instance of the base class AstNode.
-  auto base() const -> const AstNode& { return *base_; }
-
-  // Returns node->constant_value()
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_(*base_);
-  }
-
-  // Returns node->symbolic_identity()
-  auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
-    return symbolic_identity_(*base_);
-  }
-
-  void Print(llvm::raw_ostream& out) const { print_(*base_, out); }
-
-  // Returns node->static_type()
-  auto static_type() const -> const Value& { return static_type_(*base_); }
-
-  // Returns node->expression_category()
-  auto expression_category() const -> ExpressionCategory {
-    return expression_category_(*base_);
-  }
-
-  friend auto operator==(const ValueNodeView& lhs, const ValueNodeView& rhs)
-      -> bool {
-    return lhs.base_ == rhs.base_;
-  }
-
-  friend auto operator!=(const ValueNodeView& lhs, const ValueNodeView& rhs)
-      -> bool {
-    return lhs.base_ != rhs.base_;
-  }
-
-  friend auto operator<(const ValueNodeView& lhs, const ValueNodeView& rhs)
-      -> bool {
-    return std::less<>()(lhs.base_, rhs.base_);
-  }
-
-  friend auto hash_value(const ValueNodeView& view) -> llvm::hash_code {
-    using llvm::hash_value;
-    return hash_value(view.base_);
-  }
-
- private:
-  Nonnull<const AstNode*> base_;
-  std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
-      constant_value_;
-  std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
-      symbolic_identity_;
-  std::function<void(const AstNode&, llvm::raw_ostream&)> print_;
-  std::function<const Value&(const AstNode&)> static_type_;
-  std::function<ExpressionCategory(const AstNode&)> expression_category_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_VALUE_NODE_H_

+ 0 - 312
explorer/ast/value_transform.h

@@ -1,312 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_
-#define CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_
-
-#include "common/error.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/value.h"
-
-namespace Carbon {
-
-// Constructs a T instance by direct-list-initialization from the given
-// components (which must have been produced by Decompose).
-template <typename T, typename... Args>
-auto ConstructFromComponents(Args&&... args)
-    -> decltype(T{std::declval<Args>()...}) {
-  return T{std::forward<Args>(args)...};
-}
-
-// Overload of the above to accommodate the case where T is an aggregate and
-// has a CRTP base class, in which case the initializer list must start with
-// an empty initializer for the base class.
-template <typename T, typename... Args>
-auto ConstructFromComponents(Args&&... args)
-    -> decltype(T{{}, std::declval<Args>()...}) {
-  return T{{}, std::forward<Args>(args)...};
-}
-
-template <typename T, typename, typename... Args>
-constexpr bool IsConstructibleFromComponentsImpl = false;
-
-template <typename T, typename... Args>
-constexpr bool IsConstructibleFromComponentsImpl<
-    T, decltype(ConstructFromComponents<T>(std::declval<Args>()...)), Args...> =
-    true;
-
-// A no-op visitor used to implement `IsRecursivelyTransformable`. The
-// `operator()` function returns `true_type` if it's called with arguments that
-// can be used to direct-list-initialize `T`, and `false_type` otherwise.
-template <typename T>
-struct IsRecursivelyTransformableVisitor {
-  template <typename... Args>
-  auto operator()(Args&&... args) -> std::integral_constant<
-      bool, IsConstructibleFromComponentsImpl<T, T, Args...>>;
-};
-
-// A type trait that indicates whether `T` is transformable. A transformable
-// type provides a function
-//
-// template<typename F> void Decompose(F f) const;
-//
-// that takes a callable `f` and passes it an argument list that can be passed
-// to the constructor of `T` to create an equivalent value.
-template <typename T, typename = std::true_type>
-constexpr bool IsRecursivelyTransformable = false;
-template <typename T>
-// NOLINTNEXTLINE(misc-definitions-in-headers)
-constexpr bool IsRecursivelyTransformable<
-    T, decltype(std::declval<const T>().Decompose(
-           IsRecursivelyTransformableVisitor<T>{}))> = true;
-
-// Unwrapper for the case where there's nothing to unwrap.
-class NoOpUnwrapper {
- public:
-  template <typename T, typename U>
-  auto UnwrapOr(T&& value, const U&) -> T {
-    return std::forward<T>(value);
-  }
-
-  template <typename T>
-  auto Wrap(T&& value) -> T&& {
-    return std::forward<T>(value);
-  }
-
-  constexpr bool failed() const { return false; }
-};
-
-// Helper to temporarily unwrap the ErrorOr around a value, and then put it
-// back when we're done with the overall computation.
-class ErrorUnwrapper {
- public:
-  // Unwrap the `ErrorOr` from the given value, or collect the error and return
-  // the given fallback value on failure.
-  template <typename T, typename U>
-  auto UnwrapOr(ErrorOr<T> value, const U& fallback) -> T {
-    if (!value.ok()) {
-      status_ = std::move(value).error();
-      return fallback;
-    }
-    return std::move(*value);
-  }
-  template <typename T, typename U>
-  auto UnwrapOr(T&& value, const U&) -> T {
-    return std::forward<T>(value);
-  }
-
-  // Wrap the given value into `ErrorOr`, returning our collected error if any,
-  // or the given value if we succeeded.
-  template <typename T>
-  auto Wrap(T&& value) -> ErrorOr<T> {
-    if (!status_.ok()) {
-      Error error = std::move(status_).error();
-      status_ = Success();
-      return error;
-    }
-    return std::forward<T>(value);
-  }
-
-  bool failed() const { return !status_.ok(); }
-
- private:
-  ErrorOr<Success> status_ = Success();
-};
-
-// Base class for transforms of visitable data types.
-template <typename Derived, typename ResultUnwrapper>
-class TransformBase {
- public:
-  explicit TransformBase(Nonnull<Arena*> arena) : arena_(arena) {}
-
-  // Transform the given value, and produce either the transformed value or an
-  // error.
-  template <typename T>
-  auto Transform(const T& v) -> decltype(auto) {
-    return unwrapper_.Wrap(TransformOrOriginal(v));
-  }
-
- protected:
-  // Transform the given value, or return the original if transformation fails.
-  template <typename T>
-  auto TransformOrOriginal(const T& v)
-      -> decltype(std::declval<ResultUnwrapper>().UnwrapOr(
-          std::declval<Derived>()(v), v)) {
-    // If we've already failed, don't do any more transformations.
-    if (unwrapper_.failed()) {
-      return v;
-    }
-    return unwrapper_.UnwrapOr(static_cast<Derived&>(*this)(v), v);
-  }
-
-  // Transformable values are recursively transformed by default.
-  template <typename T,
-            std::enable_if_t<IsRecursivelyTransformable<T>, void*> = nullptr>
-  auto operator()(const T& value) -> T {
-    return value.Decompose([&](const auto&... elements) {
-      return [&](auto&&... transformed_elements) {
-        if (unwrapper_.failed()) {
-          return value;
-        }
-        return ConstructFromComponents<T>(
-            decltype(transformed_elements)(transformed_elements)...);
-      }(TransformOrOriginal(elements)...);
-    });
-  }
-
-  // Transformable pointers are recursively transformed and reallocated by
-  // default.
-  template <typename T,
-            std::enable_if_t<IsRecursivelyTransformable<T>, void*> = nullptr>
-  auto operator()(Nonnull<const T*> value) -> auto {
-    return value->Decompose([&](const auto&... elements) {
-      return [&](auto&&... transformed_elements)
-                 -> decltype(AllocateTrait<T>::New(
-                     arena_,
-                     decltype(transformed_elements)(transformed_elements)...)) {
-        if (unwrapper_.failed()) {
-          return value;
-        }
-        return AllocateTrait<T>::New(
-            arena_, decltype(transformed_elements)(transformed_elements)...);
-      }(TransformOrOriginal(elements)...);
-    });
-  }
-
-  // Fundamental types like `int` are assumed to not need transformation.
-  template <typename T>
-  auto operator()(const T& v) -> std::enable_if_t<std::is_fundamental_v<T>, T> {
-    return v;
-  }
-  auto operator()(const std::string& str) -> const std::string& { return str; }
-  auto operator()(llvm::StringRef str) -> llvm::StringRef { return str; }
-
-  // Transform `optional<T>` by transforming the `T` if it's present.
-  template <typename T>
-  auto operator()(const std::optional<T>& v) -> std::optional<T> {
-    if (!v) {
-      return std::nullopt;
-    }
-    return TransformOrOriginal(*v);
-  }
-
-  // Transform `pair<T, U>` by transforming T and U.
-  template <typename T, typename U>
-  auto operator()(const std::pair<T, U>& pair) -> std::pair<T, U> {
-    return std::pair<T, U>{TransformOrOriginal(pair.first),
-                           TransformOrOriginal(pair.second)};
-  }
-
-  // Transform `vector<T>` by transforming its elements.
-  template <typename T>
-  auto operator()(const std::vector<T>& vec) -> std::vector<T> {
-    std::vector<T> result;
-    result.reserve(vec.size());
-    for (auto& value : vec) {
-      result.push_back(TransformOrOriginal(value));
-    }
-    return result;
-  }
-
-  // Transform `map<T, U>` by transforming its keys and values.
-  template <typename T, typename U>
-  auto operator()(const std::map<T, U>& map) -> std::map<T, U> {
-    std::map<T, U> result;
-    for (auto& [key, value] : map) {
-      result.insert({TransformOrOriginal(key), TransformOrOriginal(value)});
-    }
-    return result;
-  }
-
-  // Transform `llvm::StringMap<T>` by transforming its keys and values.
-  template <typename T>
-  auto operator()(const llvm::StringMap<T>& map) -> llvm::StringMap<T> {
-    llvm::StringMap<T> result;
-    for (const auto& it : map) {
-      result.insert(
-          {TransformOrOriginal(it.first()), TransformOrOriginal(it.second)});
-    }
-    return result;
-  }
-
- private:
-  Nonnull<Arena*> arena_;
-  // Unwrapper for results. Used to remove an ErrorOr<...> wrapper temporarily
-  // during recursive transformations and re-apply it when we're done.
-  ResultUnwrapper unwrapper_;
-};
-
-// Base class for transforms of `Value`s.
-template <typename Derived, typename ResultUnwrapper>
-class ValueTransform : public TransformBase<Derived, ResultUnwrapper> {
- public:
-  using TransformBase<Derived, ResultUnwrapper>::TransformBase;
-  using TransformBase<Derived, ResultUnwrapper>::operator();
-
-  // Leave references to AST nodes alone by default.
-  // The 'int = 0' parameter avoids this function hiding the `operator()(const
-  // T*)` in the base class. We can remove this once we start using a compiler
-  // that implements P1787R6.
-  template <typename NodeT>
-  auto operator()(Nonnull<const NodeT*> node, int /*unused*/ = 0)
-      -> std::enable_if_t<std::is_base_of_v<AstNode, NodeT>,
-                          Nonnull<const NodeT*>> {
-    return node;
-  }
-
-  auto operator()(Address addr) -> Address { return addr; }
-
-  auto operator()(ExpressionCategory cat) -> ExpressionCategory { return cat; }
-
-  auto operator()(ValueNodeView value_node) -> ValueNodeView {
-    return value_node;
-  }
-
-  // For a type that provides a `Visit` function to visit the most-derived
-  // object, visit and transform that most-derived object.
-  template <typename R, typename T>
-  auto TransformDerived(Nonnull<const T*> value) -> R {
-    return value->template Visit<R>([&](const auto* derived_value) {
-      using DerivedType = std::remove_pointer_t<decltype(derived_value)>;
-      static_assert(IsRecursivelyTransformable<DerivedType>);
-      return this->TransformOrOriginal(derived_value);
-    });
-  }
-
-  // For values, dispatch on the value kind and recursively transform.
-  auto operator()(Nonnull<const Value*> value) -> Nonnull<const Value*> {
-    return TransformDerived<Nonnull<const Value*>>(value);
-  }
-
-  // Provide a more precise type from transforming a `Witness`.
-  auto operator()(Nonnull<const Witness*> value) -> Nonnull<const Witness*> {
-    return llvm::cast<Witness>(
-        this->TransformOrOriginal(llvm::cast<Value>(value)));
-  }
-
-  // For elements, dispatch on the element kind and recursively transform.
-  auto operator()(Nonnull<const Element*> elem) -> Nonnull<const Element*> {
-    return TransformDerived<Nonnull<const Element*>>(elem);
-  }
-
-  // Preserve vtable during transformation.
-  auto operator()(Nonnull<const VTable*> vtable) -> Nonnull<const VTable*> {
-    return vtable;
-  }
-
-  // Preserve class value ptr during transformation.
-  auto operator()(Nonnull<const NominalClassValue**> value_ptr)
-      -> Nonnull<const NominalClassValue**> {
-    return value_ptr;
-  }
-
-  // Preserve constraint kind for intrinsic constraints.
-  auto operator()(IntrinsicConstraint::Kind kind) -> IntrinsicConstraint::Kind {
-    return kind;
-  }
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_

+ 0 - 164
explorer/base/BUILD

@@ -1,164 +0,0 @@
-# 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
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-
-package(default_visibility = ["//explorer:__subpackages__"])
-
-cc_library(
-    name = "arena",
-    hdrs = ["arena.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":nonnull",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "arena_test",
-    size = "small",
-    srcs = ["arena_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":arena",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "decompose",
-    hdrs = ["decompose.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "decompose_test",
-    size = "small",
-    srcs = ["decompose_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":decompose",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "error_builders",
-    hdrs = ["error_builders.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":source_location",
-        "//common:error",
-    ],
-)
-
-cc_test(
-    name = "error_builders_test",
-    size = "small",
-    srcs = ["error_builders_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":error_builders",
-        ":source_location",
-        "//common:raw_string_ostream",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "nonnull",
-    hdrs = ["nonnull.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:check",
-    ],
-)
-
-cc_library(
-    name = "print_as_id",
-    hdrs = ["print_as_id.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:ostream",
-    ],
-)
-
-cc_library(
-    name = "source_location",
-    hdrs = ["source_location.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":nonnull",
-        "//common:ostream",
-    ],
-)
-
-cc_library(
-    name = "trace_stream",
-    hdrs = ["trace_stream.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":source_location",
-        "//common:check",
-        "//common:ostream",
-        "//explorer/base:nonnull",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_test(
-    name = "set_program_phase_raii_test",
-    size = "small",
-    srcs = ["set_program_phase_raii_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":trace_stream",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "set_file_context_raii_test",
-    size = "small",
-    srcs = ["set_file_context_raii_test.cpp"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":source_location",
-        ":trace_stream",
-        "//testing/base:gtest_main",
-        "@googletest//:gtest",
-    ],
-)

+ 0 - 280
explorer/base/arena.h

@@ -1,280 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_ARENA_H_
-#define CARBON_EXPLORER_BASE_ARENA_H_
-
-#include <any>
-#include <map>
-#include <memory>
-#include <type_traits>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "explorer/base/nonnull.h"
-#include "llvm/ADT/Hashing.h"
-
-namespace Carbon {
-
-// Adapter metafunction that converts T to a form that is usable as part of
-// a key in a hash map.
-//
-// ArgKey<T>::type must be implicitly convertible from T, equality-comparable,
-// and have a hash_value overload as defined in llvm/ADT/Hashing.h. This
-// should only be customized in cases where we cannot modify T itself to
-// satisfy those requirements.
-template <typename T, typename = void>
-struct ArgKey {
-  using type = T;
-};
-
-template <typename T>
-using ArgKeyType = typename ArgKey<T>::type;
-
-// Allocates and maintains ownership of arbitrary objects, so that their
-// lifetimes all end at the same time. It can also canonicalize the allocated
-// objects (see the documentation of New).
-class Arena {
-  // CanonicalizeAllocation<T>::value is true if canonicalization is enabled
-  // for T, and false otherwise.
-  template <typename T, typename = void>
-  struct CanonicalizeAllocation;
-
- public:
-  // Values of this type can be passed as the first argument to New in order to
-  // have the address of the created object written to the given pointer before
-  // the constructor is run. This is used during cloning to support pointer
-  // cycles within the AST.
-  template <typename T>
-  struct WriteAddressTo {
-    Nonnull<T**> target;
-  };
-  template <typename T>
-  WriteAddressTo(T** target) -> WriteAddressTo<T>;
-
-  // Returns a pointer to an object constructed as if by `T(args...)`, owned
-  // by this Arena.
-  //
-  // If T::EnableCanonicalizedAllocation exists and names a type, this method
-  // will canonicalize the allocated objects, meaning that two calls to this
-  // method with the same T and equal arguments will return pointers to the same
-  // object. If canonicalization is enabled, all types in Args... must be
-  // copyable, equality-comparable, and have a hash_value overload as defined in
-  // llvm/ADT/Hashing.h. If it's not possible to modify an argument type A to
-  // satisfy those requirements, the ArgKey<A> customization point can be used
-  // instead.
-  //
-  // Canonically-allocated objects must not be mutated, because those mutations
-  // would be visible to all users that happened to allocate a T object with
-  // the same constructor arguments. To help enforce this, the returned pointer
-  // will be const when canonicalization is enabled. Since that means there
-  // is no way to allocate a mutable instance of T, canonicalization should
-  // only be enabled for types that are inherently immutable.
-  //
-  // Canonicalization does not guarantee that equal objects will be identical,
-  // but it can substantially reduce the incidence of equal-but-not-identical
-  // objects, which can facilitate various optimizations.
-  template <
-      typename T, typename... Args,
-      typename std::enable_if_t<std::is_constructible_v<T, Args...> &&
-                                !CanonicalizeAllocation<T>::value>* = nullptr>
-  auto New(Args&&... args) -> Nonnull<T*>;
-
-  template <
-      typename T, typename... Args,
-      typename std::enable_if_t<std::is_constructible_v<T, Args...> &&
-                                CanonicalizeAllocation<T>::value>* = nullptr>
-  auto New(Args&&... args) -> Nonnull<const T*>;
-
-  // Allocates an object in the arena, writing its address to the given pointer.
-  template <
-      typename T, typename U, typename... Args,
-      typename std::enable_if_t<std::is_constructible_v<T, Args...>>* = nullptr>
-  void New(WriteAddressTo<U> addr, Args&&... args);
-
-  auto allocated() -> int64_t { return allocated_; }
-
- private:
-  // Virtualizes arena entries so that a single vector can contain many types,
-  // avoiding templated statics.
-  class ArenaEntry {
-   public:
-    virtual ~ArenaEntry() = default;
-  };
-
-  // Templated destruction of a pointer.
-  template <typename T>
-  class ArenaEntryTyped;
-
-  // Hash functor implemented in terms of hash_value (see llvm/ADT/Hashing.h).
-  struct LlvmHasher {
-    template <typename T>
-    auto operator()(const T& t) const -> size_t {
-      using llvm::hash_value;
-      return hash_value(t);
-    }
-  };
-
-  // Factory metafunction for globally unique type IDs.
-  // &TypeId<T>::id == &TypeId<U>::id if and only if std::is_same_v<T,U>.
-  //
-  // Inspired by llvm::Any::TypeId.
-  template <typename T>
-  struct TypeId {
-    // This is only used for an address to compare; the value is unimportant.
-    static char id;
-  };
-
-  // A canonicalization table maps a tuple of constructor argument values to
-  // a non-null pointer to a T object constructed with those arguments.
-  template <typename T, typename... Args>
-  using CanonicalizationTable =
-      std::unordered_map<std::tuple<ArgKeyType<Args>...>, Nonnull<const T*>,
-                         LlvmHasher>;
-
-  // Allocates an object in the arena. Unlike New, this will always allocate
-  // and construct a new object.
-  template <typename T, typename... Args>
-  auto UniqueNew(Args&&... args) -> Nonnull<T*>;
-
-  // Returns a pointer to the canonical instance of T constructed from
-  // `args...`, or null if there is no such instance yet. Returns a mutable
-  // reference so that a null entry can be updated.
-  template <typename T, typename... Args>
-  auto CanonicalInstance(const Args&... args) -> const T*&;
-
-  // Manages allocations in an arena for destruction at shutdown.
-  std::vector<std::unique_ptr<ArenaEntry>> arena_;
-  int64_t allocated_ = 0;
-
-  // Maps a CanonicalizationTable type to a unique instance of that type for
-  // this arena. For a key equal to &TypeId<T>::id for some T, the corresponding
-  // value contains a T*.
-  std::map<char*, std::any> canonical_tables_;
-};
-
-// ---------------------------------------
-// Implementation details only below here.
-// ---------------------------------------
-
-template <>
-struct ArgKey<std::nullopt_t> {
-  using type = struct NulloptProxy {
-    NulloptProxy(std::nullopt_t) {}
-    friend auto operator==(NulloptProxy, NulloptProxy) -> bool { return true; }
-    friend auto hash_value(NulloptProxy) -> llvm::hash_code {
-      return llvm::hash_combine();
-    }
-  };
-};
-
-template <typename T>
-struct ArgKey<std::vector<T>> {
-  using type = class VectorProxy {
-   public:
-    VectorProxy(std::vector<T> vec) : vec_(std::move(vec)) {}
-    friend auto operator==(const VectorProxy& lhs, const VectorProxy& rhs) {
-      return lhs.vec_ == rhs.vec_;
-    }
-    friend auto hash_value(const VectorProxy& v) {
-      return llvm::hash_combine(
-          llvm::hash_combine_range(v.vec_.begin(), v.vec_.end()),
-          v.vec_.size());
-    }
-
-   private:
-    std::vector<T> vec_;
-  };
-};
-
-template <typename T, typename... Args,
-          typename std::enable_if_t<std::is_constructible_v<T, Args...> &&
-                                    !Arena::CanonicalizeAllocation<T>::value>*>
-auto Arena::New(Args&&... args) -> Nonnull<T*> {
-  return UniqueNew<T>(std::forward<Args>(args)...);
-}
-
-template <typename T, typename... Args,
-          typename std::enable_if_t<std::is_constructible_v<T, Args...> &&
-                                    Arena::CanonicalizeAllocation<T>::value>*>
-auto Arena::New(Args&&... args) -> Nonnull<const T*> {
-  const T*& canonical_instance = CanonicalInstance<T>(args...);
-  if (canonical_instance == nullptr) {
-    canonical_instance = UniqueNew<T>(std::forward<Args>(args)...);
-  }
-  return canonical_instance;
-}
-
-template <typename T, typename U, typename... Args,
-          typename std::enable_if_t<std::is_constructible_v<T, Args...>>*>
-void Arena::New(WriteAddressTo<U> addr, Args&&... args) {
-  static_assert(!CanonicalizeAllocation<T>::value,
-                "This form of New does not support canonicalization yet");
-  arena_.push_back(
-      std::make_unique<ArenaEntryTyped<T>>(addr, std::forward<Args>(args)...));
-  allocated_ += sizeof(T);
-}
-
-template <typename T, typename... Args>
-auto Arena::UniqueNew(Args&&... args) -> Nonnull<T*> {
-  auto smart_ptr =
-      std::make_unique<ArenaEntryTyped<T>>(std::forward<Args>(args)...);
-  Nonnull<T*> ptr = smart_ptr->Instance();
-  arena_.push_back(std::move(smart_ptr));
-  allocated_ += sizeof(T);
-  return ptr;
-}
-
-template <typename T, typename>
-struct Arena::CanonicalizeAllocation : public std::false_type {};
-
-template <typename T>
-struct Arena::CanonicalizeAllocation<
-    T, std::void_t<typename T::EnableCanonicalizedAllocation>>
-    : public std::true_type {};
-
-template <typename T, typename... Args>
-auto Arena::CanonicalInstance(const Args&... args) -> const T*& {
-  using MapType = CanonicalizationTable<T, Args...>;
-  std::any& wrapped_table = canonical_tables_[&TypeId<MapType>::id];
-  if (!wrapped_table.has_value()) {
-    wrapped_table.emplace<MapType>();
-  }
-  MapType& table = std::any_cast<MapType&>(wrapped_table);
-  return table[typename MapType::key_type(args...)];
-}
-
-// Templated destruction of a pointer.
-template <typename T>
-class Arena::ArenaEntryTyped : public ArenaEntry {
- public:
-  struct WriteAddressHelper {};
-
-  template <typename... Args>
-  explicit ArenaEntryTyped(Args&&... args)
-      : instance_(std::forward<Args>(args)...) {}
-
-  template <typename... Args>
-  explicit ArenaEntryTyped(WriteAddressHelper, Args&&... args)
-      : ArenaEntryTyped(std::forward<Args>(args)...) {}
-
-  template <typename U, typename... Args>
-  explicit ArenaEntryTyped(WriteAddressTo<U> write_address, Args&&... args)
-      : ArenaEntryTyped(
-            (*write_address.target = &instance_, WriteAddressHelper{}),
-            std::forward<Args>(args)...) {}
-
-  auto Instance() -> Nonnull<T*> { return Nonnull<T*>(&instance_); }
-
- private:
-  T instance_;
-};
-
-template <typename T>
-char Arena::TypeId<T>::id = 1;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_ARENA_H_

+ 0 - 96
explorer/base/arena_test.cpp

@@ -1,96 +0,0 @@
-// 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 "explorer/base/arena.h"
-
-#include <gtest/gtest.h>
-
-#include <optional>
-#include <vector>
-
-namespace Carbon {
-
-class ReportDestruction {
- public:
-  explicit ReportDestruction(bool* destroyed) : destroyed_(destroyed) {}
-
-  ~ReportDestruction() { *destroyed_ = true; }
-
- private:
-  bool* destroyed_;
-};
-
-TEST(ArenaTest, BasicAllocation) {
-  bool destroyed = false;
-  {
-    Arena arena;
-    (void)arena.New<ReportDestruction>(&destroyed);
-  }
-  EXPECT_TRUE(destroyed);
-}
-
-struct CanonicalizedDummy {
-  explicit CanonicalizedDummy(int) {}
-  explicit CanonicalizedDummy(int*) {}
-  explicit CanonicalizedDummy(int, int*) {}
-  explicit CanonicalizedDummy(std::vector<int>, std::nullopt_t) {}
-  using EnableCanonicalizedAllocation = void;
-};
-
-TEST(ArenaTest, Canonicalize) {
-  Arena arena;
-  auto* dummy1 = arena.New<CanonicalizedDummy>(1);
-  EXPECT_TRUE(std::is_const_v<std::remove_pointer_t<decltype(dummy1)>>);
-  auto* dummy2 = arena.New<CanonicalizedDummy>(1);
-  EXPECT_TRUE(dummy1 == dummy2);
-}
-
-TEST(ArenaTest, CanonicalizeArgMismatch) {
-  Arena arena;
-  auto* dummy1 = arena.New<CanonicalizedDummy>(1);
-  auto* dummy2 = arena.New<CanonicalizedDummy>(2);
-  EXPECT_TRUE(dummy1 != dummy2);
-}
-
-TEST(ArenaTest, CanonicalizeDifferentArenas) {
-  Arena arena1;
-  Arena arena2;
-  auto* dummy1 = arena1.New<CanonicalizedDummy>(1);
-  auto* dummy2 = arena2.New<CanonicalizedDummy>(1);
-
-  EXPECT_TRUE(dummy1 != dummy2);
-}
-
-TEST(ArenaTest, CanonicalizeIsShallow) {
-  Arena arena;
-  int i1 = 1;
-  int i2 = 1;
-  auto* dummy1 = arena.New<CanonicalizedDummy>(&i1);
-  auto* dummy2 = arena.New<CanonicalizedDummy>(&i2);
-  EXPECT_TRUE(dummy1 != dummy2);
-}
-
-TEST(ArenaTest, CanonicalizeMultipleArgs) {
-  Arena arena;
-  int i;
-  auto* dummy1 = arena.New<CanonicalizedDummy>(1, &i);
-  auto* dummy2 = arena.New<CanonicalizedDummy>(1, &i);
-  EXPECT_TRUE(dummy1 == dummy2);
-}
-
-TEST(ArenaTest, CanonicalizeStdTypes) {
-  Arena arena;
-  std::vector<int> v1 = {1, 2, 3};
-  std::vector<int> v2 = {1, 2, 3};
-  std::vector<int> v3 = {1, 2, 3, 4};
-
-  auto* dummy1 = arena.New<CanonicalizedDummy>(v1, std::nullopt);
-  auto* dummy2 = arena.New<CanonicalizedDummy>(v2, std::nullopt);
-  EXPECT_TRUE(dummy1 == dummy2);
-
-  auto* dummy3 = arena.New<CanonicalizedDummy>(v3, std::nullopt);
-  EXPECT_TRUE(dummy1 != dummy3);
-}
-
-}  // namespace Carbon

+ 0 - 54
explorer/base/decompose.h

@@ -1,54 +0,0 @@
-// 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
-//
-// Utilities for types that support the `Decompose` API.
-
-#ifndef CARBON_EXPLORER_BASE_DECOMPOSE_H_
-#define CARBON_EXPLORER_BASE_DECOMPOSE_H_
-
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Hashing.h"
-
-namespace Carbon {
-
-// CRTP base class which extends `Base` to support hashing and equality
-// comparison. `Base` must support the `Decompose` API.
-template <typename Base>
-class HashFromDecompose {
- public:
-  friend auto operator==(const HashFromDecompose& lhs,
-                         const HashFromDecompose& rhs) -> bool {
-    return static_cast<const Base*>(&lhs)->Decompose(
-        [&](auto&&... lhs_elements) {
-          return static_cast<const Base*>(&rhs)->Decompose(
-              [&](auto&&... rhs_elements) {
-                return ((lhs_elements == rhs_elements) && ...);
-              });
-        });
-  }
-
-  friend auto hash_value(const HashFromDecompose& self) -> llvm::hash_code {
-    return static_cast<const Base*>(&self)->Decompose(
-        [&](auto&&... lhs_elements) {
-          return llvm::hash_combine(WrapForHash(lhs_elements)...);
-        });
-  }
-
- private:
-  // Wraps T in a form that supports `hash_value`. Used for adapting types that
-  // we can't extend directly.
-  template <typename T>
-  static auto WrapForHash(const T& t) -> const T& {
-    return t;
-  }
-
-  template <typename T>
-  static auto WrapForHash(const std::vector<T>& vec) -> llvm::ArrayRef<T> {
-    return vec;
-  }
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_DECOMPOSE_H_

+ 0 - 39
explorer/base/decompose_test.cpp

@@ -1,39 +0,0 @@
-// 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 "explorer/base/decompose.h"
-
-#include <gtest/gtest.h>
-
-namespace Carbon {
-namespace {
-
-struct Decomposeable : public HashFromDecompose<Decomposeable> {
-  template <typename F>
-  auto Decompose(F f) const {
-    return f(i, s);
-  }
-
-  int i = 0;
-  std::string s;
-};
-
-TEST(HashFromDecomposeTest, EqualValues) {
-  Decomposeable d1 = {.i = 42, .s = "foo"};
-  Decomposeable d2 = {.i = 42, .s = "foo"};
-
-  EXPECT_TRUE(d1 == d2);
-  EXPECT_TRUE(hash_value(d1) == hash_value(d2));
-}
-
-TEST(HashFromDecomposeTest, NonEqualValues) {
-  Decomposeable d1 = {.i = 42, .s = "foo"};
-  Decomposeable d2 = {.i = 42, .s = "bar"};
-
-  EXPECT_FALSE(d1 == d2);
-  EXPECT_FALSE(hash_value(d1) == hash_value(d2));
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 26
explorer/base/error_builders.h

@@ -1,26 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_ERROR_BUILDERS_H_
-#define CARBON_EXPLORER_BASE_ERROR_BUILDERS_H_
-
-#include "common/error.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-// Builds an Error instance with the specified message. This should be used
-// for errors in the user-supplied Carbon code is incorrect. Use CHECK/FATAL
-// instead for errors that indicate bugs in Explorer itself.
-//
-// For example:
-//   return ProgramError(line_num) << "Line is bad!";
-
-inline auto ProgramError(SourceLocation loc) -> ErrorBuilder {
-  return ErrorBuilder(loc.ToString());
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_ERROR_BUILDERS_H_

+ 0 - 26
explorer/base/error_builders_test.cpp

@@ -1,26 +0,0 @@
-// 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 "explorer/base/error_builders.h"
-
-#include <gtest/gtest.h>
-
-#include "common/raw_string_ostream.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-namespace {
-
-TEST(ErrorBuildersTest, ProgramError) {
-  Error err = ProgramError(SourceLocation("x", 1, FileKind::Main)) << "test";
-  EXPECT_EQ(err.location(), "x:1");
-  EXPECT_EQ(err.message(), "test");
-
-  RawStringOstream out;
-  out << err;
-  EXPECT_EQ(out.TakeStr(), "x:1: test");
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 22
explorer/base/nonnull.h

@@ -1,22 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_NONNULL_H_
-#define CARBON_EXPLORER_BASE_NONNULL_H_
-
-#include <type_traits>
-
-namespace Carbon {
-
-// A non-nullable pointer. Written as `Nonnull<T*>` instead of `T*`.
-//
-// Sanitizers enforce this dynamically on assignment, return, and when passing
-// as an argument. Static analysis will also track erroneous uses of `nullptr`.
-template <typename T,
-          typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
-using Nonnull = T _Nonnull;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_NONNULL_H_

+ 0 - 36
explorer/base/print_as_id.h

@@ -1,36 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_PRINT_AS_ID_H_
-#define CARBON_EXPLORER_BASE_PRINT_AS_ID_H_
-
-#include "common/ostream.h"
-
-namespace Carbon {
-
-// Helper to support printing the ID for a type that has a method
-// `void PrintID(llvm::raw_ostream& out) const`. Usage:
-//
-//     out << PrintAsID(obj);
-template <typename T>
-class PrintAsID {
- public:
-  explicit PrintAsID(const T& object) : object_(&object) {}
-
-  friend auto operator<<(llvm::raw_ostream& out, const PrintAsID& self)
-      -> llvm::raw_ostream& {
-    self.object_->PrintID(out);
-    return out;
-  }
-
- private:
-  const T* object_;
-};
-
-template <typename T>
-PrintAsID(const T&) -> PrintAsID<T>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_PRINT_AS_ID_H_

+ 0 - 37
explorer/base/set_file_context_raii_test.cpp

@@ -1,37 +0,0 @@
-// 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 <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-namespace {
-
-TEST(SetFileContextRaiiTest, UpdateFileContext) {
-  TraceStream trace_stream;
-  trace_stream.set_stream(&llvm::nulls());
-  trace_stream.set_allowed_phases({ProgramPhase::All});
-  trace_stream.set_allowed_file_kinds({FileKind::Main});
-
-  {
-    SetFileContext set_file_ctx(
-        trace_stream,
-        SourceLocation("example/prelude.carbon", 9, FileKind::Prelude));
-    EXPECT_FALSE(trace_stream.is_enabled());
-    set_file_ctx.update_source_loc(
-        SourceLocation("example/main.carbon", 9, FileKind::Main));
-    EXPECT_TRUE(trace_stream.is_enabled());
-    set_file_ctx.update_source_loc(
-        SourceLocation("example/import.carbon", 9, FileKind::Import));
-    EXPECT_FALSE(trace_stream.is_enabled());
-  }
-
-  // The trace stream should be enabled when we're not in any particular file.
-  EXPECT_TRUE(trace_stream.is_enabled());
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 36
explorer/base/set_program_phase_raii_test.cpp

@@ -1,36 +0,0 @@
-// 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 <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-namespace {
-
-TEST(SetProgramPhaseRaiiTest, Simple) {
-  TraceStream trace_stream;
-  trace_stream.set_current_phase(ProgramPhase::Unknown);
-  {
-    SetProgramPhase set_prog_phase(trace_stream, ProgramPhase::Execution);
-    EXPECT_TRUE(trace_stream.current_phase() == ProgramPhase::Execution);
-  }
-  EXPECT_TRUE(trace_stream.current_phase() == ProgramPhase::Unknown);
-}
-
-TEST(SetProgramPhaseRaiiTest, UpdatePhase) {
-  TraceStream trace_stream;
-  trace_stream.set_current_phase(ProgramPhase::Unknown);
-  {
-    SetProgramPhase set_prog_phase(trace_stream, ProgramPhase::Execution);
-    EXPECT_TRUE(trace_stream.current_phase() == ProgramPhase::Execution);
-    set_prog_phase.update_phase(ProgramPhase::TypeChecking);
-    EXPECT_TRUE(trace_stream.current_phase() == ProgramPhase::TypeChecking);
-  }
-  EXPECT_TRUE(trace_stream.current_phase() == ProgramPhase::Unknown);
-}
-
-}  // namespace
-}  // namespace Carbon

+ 0 - 73
explorer/base/source_location.h

@@ -1,73 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_SOURCE_LOCATION_H_
-#define CARBON_EXPLORER_BASE_SOURCE_LOCATION_H_
-
-#include <string>
-#include <string_view>
-
-#include "common/ostream.h"
-#include "explorer/base/nonnull.h"
-
-namespace Carbon {
-
-// Describes the kind of file that the source location is within.
-enum class FileKind { Main, Prelude, Import, Unknown, Last = Unknown };
-
-class SourceLocation : public Printable<SourceLocation> {
- public:
-  // Produce a source location that is known to not be used, because it is fed
-  // into an operation that creates no AST nodes and whose diagnostics are
-  // discarded.
-  static auto DiagnosticsIgnored() -> SourceLocation {
-    return SourceLocation("", 0, FileKind::Unknown);
-  }
-
-  // The filename should be eternal or arena-allocated to eliminate copies.
-  explicit constexpr SourceLocation(std::string_view filename, int line_num,
-                                    FileKind file_kind)
-      : filename_(filename), line_num_(line_num), file_kind_(file_kind) {}
-  explicit SourceLocation(Nonnull<const std::string*> filename, int line_num,
-                          FileKind file_kind)
-      : filename_(*filename), line_num_(line_num), file_kind_(file_kind) {}
-
-  SourceLocation(const SourceLocation&) = default;
-  SourceLocation(SourceLocation&&) = default;
-  auto operator=(const SourceLocation&) -> SourceLocation& = default;
-  auto operator=(SourceLocation&&) -> SourceLocation& = default;
-
-  auto operator==(SourceLocation other) const -> bool {
-    return filename_ == other.filename_ && line_num_ == other.line_num_ &&
-           file_kind_ == other.file_kind_;
-  }
-
-  auto filename() const -> std::string_view { return filename_; }
-
-  auto file_kind() const -> FileKind { return file_kind_; }
-
-  void Print(llvm::raw_ostream& out) const {
-    if (file_kind_ == FileKind::Prelude) {
-      out << llvm::StringRef(filename_).rsplit("/").second << ":" << line_num_;
-    } else {
-      out << filename_ << ":" << line_num_;
-    }
-  }
-
-  auto ToString() const -> std::string {
-    std::string result;
-    llvm::raw_string_ostream out(result);
-    Print(out);
-    return result;
-  }
-
- private:
-  std::string_view filename_;
-  int line_num_;
-  FileKind file_kind_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_SOURCE_LOCATION_H_

+ 0 - 226
explorer/base/trace_stream.h

@@ -1,226 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_BASE_TRACE_STREAM_H_
-#define CARBON_EXPLORER_BASE_TRACE_STREAM_H_
-
-#include <bitset>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/ArrayRef.h"
-
-namespace Carbon {
-
-class TraceStream;
-
-// Enumerates the phases of the program used for tracing and controlling which
-// program phases are included for tracing.
-enum class ProgramPhase {
-  SourceProgram,               // Phase for the source program.
-  NameResolution,              // Phase for name resolution.
-  ControlFlowResolution,       // Phase for control flow resolution.
-  TypeChecking,                // Phase for type checking.
-  UnformedVariableResolution,  // Phase for unformed variables resolution.
-  Declarations,                // Phase for printing declarations.
-  Execution,                   // Phase for program execution.
-  Timing,                      // Phase for timing logs.
-  Unknown,                     // Represents an unknown program phase.
-  All,                         // Represents all program phases.
-  Last = All                   // Last program phase indicator.
-};
-
-// Encapsulates the trace stream so that we can cleanly disable tracing while
-// the prelude is being processed. The prelude is expected to take a
-// disproportionate amount of time to log, so we try to avoid it.
-class TraceStream {
- public:
-  explicit TraceStream() = default;
-
-  // Returns true if tracing is currently enabled.
-  auto is_enabled() const -> bool {
-    return stream_.has_value() && !in_prelude_ &&
-           allowed_phases_[static_cast<int>(current_phase_)] &&
-           (!source_loc_ ||
-            allowed_file_kinds_[static_cast<int>(source_loc_->file_kind())]);
-  }
-
-  // Sets whether the prelude is being skipped.
-  auto set_in_prelude(bool in_prelude) -> void { in_prelude_ = in_prelude; }
-
-  // Sets the trace stream. This should only be called from the main.
-  auto set_stream(Nonnull<llvm::raw_ostream*> stream) -> void {
-    stream_ = stream;
-  }
-
-  auto set_current_phase(ProgramPhase current_phase) -> void {
-    current_phase_ = current_phase;
-  }
-
-  auto set_allowed_phases(llvm::ArrayRef<ProgramPhase> allowed_phases_list)
-      -> void {
-    if (allowed_phases_list.empty()) {
-      allowed_phases_.set(static_cast<int>(ProgramPhase::Execution));
-    } else {
-      for (auto phase : allowed_phases_list) {
-        if (phase == ProgramPhase::All) {
-          allowed_phases_.set();
-        } else {
-          allowed_phases_.set(static_cast<int>(phase));
-        }
-      }
-    }
-  }
-
-  auto set_allowed_file_kinds(llvm::ArrayRef<FileKind> kind_list) -> void {
-    for (auto kind : kind_list) {
-      allowed_file_kinds_.set(static_cast<int>(kind));
-    }
-  }
-
-  auto set_source_loc(std::optional<SourceLocation> source_loc) -> void {
-    source_loc_ = source_loc;
-  }
-
-  auto source_loc() const -> std::optional<SourceLocation> {
-    return source_loc_;
-  }
-
-  // Returns the internal stream. Requires is_enabled.
-  auto stream() const -> llvm::raw_ostream& {
-    CARBON_CHECK(is_enabled() && stream_.has_value());
-    return **stream_;
-  }
-
-  auto current_phase() const -> ProgramPhase { return current_phase_; }
-
-  auto add_blank_lines(int num_blank_lines) const -> void {
-    CARBON_CHECK(is_enabled() && stream_);
-    if (!is_trace_empty_) {
-      for (int i = 0; i < num_blank_lines; ++i) {
-        **stream_ << "\n";
-      }
-    }
-  }
-
-  // Outputs a trace message. Requires is_enabled.
-  template <typename T>
-  auto operator<<(T&& message) const -> llvm::raw_ostream& {
-    CARBON_CHECK(is_enabled() && stream_);
-    if (is_trace_empty_) {
-      is_trace_empty_ = false;
-    }
-    **stream_ << message;
-    return **stream_;
-  }
-
-  // These functions can be used for adding line prefixes in the trace output.
-  auto Indent() const -> llvm::raw_ostream& { return *this << "    "; }
-  auto Start() const -> llvm::raw_ostream& { return *this << "->> "; }
-  auto End() const -> llvm::raw_ostream& { return *this << "<<- "; }
-  auto Call() const -> llvm::raw_ostream& { return *this << "-() "; }
-  auto Match() const -> llvm::raw_ostream& { return *this << "=== "; }
-  auto Result() const -> llvm::raw_ostream& { return *this << "==> "; }
-  auto Add() const -> llvm::raw_ostream& { return *this << " +  "; }
-  auto Remove() const -> llvm::raw_ostream& { return *this << " -  "; }
-  auto Read() const -> llvm::raw_ostream& { return *this << "<-- "; }
-  auto Write() const -> llvm::raw_ostream& { return *this << "--> "; }
-  auto Allocate() const -> llvm::raw_ostream& { return *this << "++# "; }
-  auto Deallocate() const -> llvm::raw_ostream& { return *this << "--# "; }
-  auto Substitute() const -> llvm::raw_ostream& { return *this << "->+ "; }
-  auto Push() const -> llvm::raw_ostream& { return *this << ">[] "; }
-  auto Pop() const -> llvm::raw_ostream& { return *this << "<[] "; }
-  auto Not() const -> llvm::raw_ostream& { return *this << "-!- "; }
-  auto Skip() const -> llvm::raw_ostream& { return *this << ">>> "; }
-  auto Source() const -> llvm::raw_ostream& {
-    // add a blank line before prefix.
-    add_blank_lines(1);
-    return *this << "*** ";
-  }
-
-  // Format utility methods
-  void Heading(std::string_view heading) const {
-    add_blank_lines(2);
-    const std::string_view stars = "* * * * * * * * * *";
-    const std::string dashed_line(stars.size() * 2 + heading.size() + 4, '-');
-    *this << stars << "  " << heading << "  " << stars << "\n"
-          << dashed_line << "\n";
-  }
-
-  void SubHeading(std::string_view sub_heading) const {
-    add_blank_lines(1);
-    const std::string_view dashes = "- - - - -";
-    const std::string dashed_line(dashes.size() * 2 + sub_heading.size() + 4,
-                                  '-');
-    *this << dashes << "  " << sub_heading << "  " << dashes << "\n"
-          << dashed_line << "\n";
-  }
-
- private:
-  bool in_prelude_ = false;
-  mutable bool is_trace_empty_ = true;
-  ProgramPhase current_phase_ = ProgramPhase::Unknown;
-  std::optional<SourceLocation> source_loc_ = std::nullopt;
-  std::optional<Nonnull<llvm::raw_ostream*>> stream_;
-  std::bitset<static_cast<int>(ProgramPhase::Last) + 1> allowed_phases_;
-  std::bitset<static_cast<int>(FileKind::Last) + 1> allowed_file_kinds_;
-};
-
-// This is a RAII class to set the current program phase, destructor invocation
-// restores the previous phase.
-class SetProgramPhase {
- public:
-  explicit SetProgramPhase(TraceStream& trace_stream,
-                           ProgramPhase program_phase)
-      : trace_stream_(trace_stream),
-        initial_phase_(trace_stream.current_phase()) {
-    trace_stream.set_current_phase(program_phase);
-  }
-
-  ~SetProgramPhase() { trace_stream_.set_current_phase(initial_phase_); }
-
-  // This can be used for cases when current phase is set multiple times within
-  // the same scope.
-  auto update_phase(ProgramPhase program_phase) -> void {
-    trace_stream_.set_current_phase(program_phase);
-  }
-
- private:
-  TraceStream& trace_stream_;
-  ProgramPhase initial_phase_;
-};
-
-// This is a RAII class to set the source location in trace stream, destructor
-// invocation restores the initial source location.
-class SetFileContext {
- public:
-  explicit SetFileContext(TraceStream& trace_stream,
-                          std::optional<SourceLocation> source_loc)
-      : trace_stream_(trace_stream),
-        initial_source_loc_(trace_stream.source_loc()) {
-    trace_stream_.set_source_loc(source_loc);
-  }
-
-  ~SetFileContext() { trace_stream_.set_source_loc(initial_source_loc_); }
-
-  // This can be used for cases when source location needs to be updated
-  // multiple times within the same scope.
-  auto update_source_loc(std::optional<SourceLocation> source_loc) {
-    trace_stream_.set_source_loc(source_loc);
-  }
-
- private:
-  TraceStream& trace_stream_;
-  std::optional<SourceLocation> initial_source_loc_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_BASE_TRACE_STREAM_H_

+ 0 - 715
explorer/data/prelude.carbon

@@ -1,715 +0,0 @@
-// 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 Carbon api;
-
-// ----------------------
-// Conversion interfaces.
-// ----------------------
-
-// Explicitly convert `Self` to `T`.
-interface As(T:! type) {
-  fn Convert[self: Self]() -> T;
-}
-
-// Implicitly convert `Self` to `T`.
-interface ImplicitAs(T:! type) {
-  extend As(T);
-}
-
-// TODO: This should be private.
-interface __EqualConverter {
-  let T:! type;
-  fn Convert(t: T) -> Self;
-}
-fn __EqualConvert[T:! type](t: T, U:! __EqualConverter where .T = T) -> U {
-  return U.Convert(t);
-}
-impl forall [U:! type] U as __EqualConverter where .T = U {
-  fn Convert(u: U) -> U { return u; }
-}
-
-__match_first {
-  // Pick up implicit conversions that are built into the compiler.
-  // TODO: Split these into individual categories and implement as many as we can
-  // in the prelude.
-  impl forall [template U:! type, template T:! __intrinsic_implicit_as(U)]
-      T as ImplicitAs(U) {
-    fn Convert[self: Self]() -> U {
-      return __intrinsic_implicit_as_convert(self, U);
-    }
-  }
-
-  // Every type implicitly converts to single-step-equal types.
-  impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U) {
-    fn Convert[self: Self]() -> U { return __EqualConvert(self, U); }
-  }
-}
-
-// A tuple explicitly converts to another tuple if all its elements do.
-// TODO: Simplify this once we have variadics.
-// TODO: Should these be final?
-impl forall [U1:! type, T1:! As(U1)]
-    (T1,) as As((U1,)) {
-  fn Convert[self: Self]() -> (U1,) {
-    let (v1: T1,) = self;
-    return (v1.Convert(),);
-  }
-}
-impl forall [U1:! type, U2:! type, T1:! As(U1), T2:! As(U2)]
-    (T1, T2) as As((U1, U2)) {
-  fn Convert[self: Self]() -> (U1, U2) {
-    let (v1: T1, v2: T2) = self;
-    return (v1.Convert(), v2.Convert());
-  }
-}
-impl forall [U1:! type, U2:! type, U3:! type,
-             T1:! As(U1), T2:! As(U2), T3:! As(U3)]
-    (T1, T2, T3) as As((U1, U2, U3)) {
-  fn Convert[self: Self]() -> (U1, U2, U3) {
-    let (v1: T1, v2: T2, v3: T3) = self;
-    return (v1.Convert(), v2.Convert(), v3.Convert());
-  }
-}
-
-// ----------------------
-// Comparison interfaces.
-// ----------------------
-
-// ----------------------
-// EQUAL
-// ----------------------
-
-interface EqWith(U:! type) {
-  fn Equal[self: Self](other: U) -> bool;
-  fn NotEqual[self: Self](other: U) -> bool;
-}
-
-constraint Eq {
-  extend EqWith(Self);
-}
-
-// TODO: Simplify this once we have variadics
-impl forall [T2:! type, U2:! type, T1:! EqWith(T2), U1:! EqWith(U2)]
-    (T1, U1) as EqWith((T2, U2)) {
-  fn Equal[self: Self](other: (T2, U2)) -> bool {
-    let (l1: T1, l2: U1) = self;
-    let (r1: T2, r2: U2) = other;
-    return l1 == r1 and l2 == r2;
-  }
-  fn NotEqual[self: Self](other: (T2, U2)) -> bool {
-      let (l1: T1, l2: U1) = self;
-      let (r1: T2, r2: U2) = other;
-      return l1 != r1 or l2 != r2;
-    }
-}
-
-impl bool as EqWith(Self) {
-  fn Equal[self: Self](other: Self) -> bool {
-    return if self then other else not other;
-  }
-  fn NotEqual[self: Self](other: Self) -> bool {
-    return if self then not other else other;
-  }
-}
-
-impl i32 as EqWith(Self) {
-  fn Equal[self: Self](other: Self) -> bool {
-    return __intrinsic_int_eq(self, other);
-  }
-
-  fn NotEqual[self: Self](other: Self) -> bool {
-    return not __intrinsic_int_eq(self, other);
-  }
-}
-
-impl String as EqWith(Self) {
-  fn Equal[self: Self](other: Self) -> bool {
-    return __intrinsic_str_eq(self, other);
-  }
-
-  fn NotEqual[self: Self](other: Self) -> bool {
-    return not __intrinsic_str_eq(self, other);
-  }
-}
-
-// ----------------------
-// COMPARE
-// ----------------------
-
-choice Ordering {
-  Less,
-  Equivalent,
-  Greater,
-  Incomparable
-}
-
-// TODO: Per the design, this should be named `OrderedWith`.
-interface CompareWith(U:! type) {
-  fn Compare[self: Self](u: U) -> Ordering;
-  // TODO: Add `default fn` for Less, LessOrEquivalent, Greater, and GreaterOrEquivalent once it's available.
-}
-constraint Ordered {
-  extend CompareWith(Self);
-}
-
-impl i32 as CompareWith(Self) {
-  fn Compare[self: Self](other: Self) -> Ordering {
-    var comp: i32 = __intrinsic_int_compare(self, other);
-    if (comp == -1) {
-      return Ordering.Less;
-    }
-    if (comp == 0) {
-      return Ordering.Equivalent;
-    }
-    if (comp == 1) {
-      return Ordering.Greater;
-    }
-    return Ordering.Incomparable;
-
-  }
-}
-
-impl String as CompareWith(Self) {
-  fn Compare[self: Self](other: Self) -> Ordering {
-    var comp: i32 = __intrinsic_str_compare(self, other);
-    if (comp == -1) {
-      return Ordering.Less;
-    }
-    if (comp == 0) {
-      return Ordering.Equivalent;
-    }
-    if (comp == 1) {
-      return Ordering.Greater;
-    }
-    return Ordering.Incomparable;
-  }
-}
-
-interface LessWith(U:! type) {
-  fn Less[self: Self](other: U) -> bool;
-}
-
-interface LessEqWith(U:! type) {
-  fn LessEq[self: Self](other: U) -> bool;
-}
-
-interface GreaterWith(U:! type) {
-  fn Greater[self: Self](other: U) -> bool;
-}
-
-interface GreaterEqWith(U:! type) {
-  fn GreaterEq[self: Self](other: U) -> bool;
-}
-
-impl i32 as LessWith(Self) {
-  fn Less[self: Self](other: Self) -> bool {
-    var comp: Ordering = self.(CompareWith(i32).Compare)(other);
-    match (comp) {
-      case Ordering.Less => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl String as LessWith(Self) {
-  fn Less[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(String).Compare)(other);
-    match(comp){
-      case Ordering.Less => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl i32 as LessEqWith(Self) {
-  fn LessEq[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(i32).Compare)(other);
-    match(comp){
-      case Ordering.Less => {
-        return true;
-      }
-      case Ordering.Equivalent => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl String as LessEqWith(Self) {
-  fn LessEq[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(String).Compare)(other);
-    match(comp){
-      case Ordering.Less => {
-        return true;
-      }
-      case Ordering.Equivalent => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl i32 as GreaterWith(Self) {
-  fn Greater[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(i32).Compare)(other);
-    match(comp){
-      case Ordering.Greater => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl String as GreaterWith(Self) {
-  fn Greater[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(String).Compare)(other);
-    match(comp){
-      case Ordering.Greater => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl i32 as GreaterEqWith(Self) {
-  fn GreaterEq[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(i32).Compare)(other);
-    match(comp){
-      case Ordering.Greater => {
-        return true;
-      }
-      case Ordering.Equivalent => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-impl String as GreaterEqWith(Self) {
-  fn GreaterEq[self: Self](other: Self) -> bool {
-    var comp: Ordering =  self.(CompareWith(String).Compare)(other);
-    match(comp){
-      case Ordering.Greater => {
-        return true;
-      }
-      case Ordering.Equivalent => {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-// ----------------------
-// Arithmetic interfaces.
-// ----------------------
-
-interface Negate {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self]() -> Result;
-}
-
-interface AddWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint Add {
-  extend AddWith(Self) where .Result = Self;
-}
-
-interface SubWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint Sub {
-  extend SubWith(Self) where .Result = Self;
-}
-
-interface MulWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint Mul {
-  extend MulWith(Self) where .Result = Self;
-}
-
-interface DivWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint Div {
-  extend DivWith(Self) where .Result = Self;
-}
-
-interface ModWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint Mod {
-  extend ModWith(Self) where .Result = Self;
-}
-
-// Note, these impl declarations use the builtin addition for i32.
-impl i32 as Negate where .Result = i32 {
-  fn Op[self: i32]() -> i32 { return -self; }
-}
-impl i32 as AddWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 { return self + other; }
-}
-impl i32 as SubWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 { return self - other; }
-}
-impl i32 as MulWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 { return self * other; }
-}
-impl i32 as DivWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 { return self / other; }
-}
-impl i32 as ModWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 { return self % other; }
-}
-
-// ---------------------------------
-// Bitwise and bit-shift interfaces.
-// ---------------------------------
-
-// Unary `^`.
-interface BitComplement {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self]() -> Result;
-}
-
-// Binary `&`.
-interface BitAndWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint BitAnd {
-  extend BitAndWith(Self) where .Result = Self;
-}
-
-// Binary `|`.
-interface BitOrWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint BitOr {
-  extend BitOrWith(Self) where .Result = Self;
-}
-
-// Binary `^`.
-interface BitXorWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint BitXor {
-  extend BitXorWith(Self) where .Result = Self;
-}
-
-// Binary `<<`.
-interface LeftShiftWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint LeftShift {
-  extend LeftShiftWith(Self) where .Result = Self;
-}
-
-// Binary `>>`.
-interface RightShiftWith(U:! type) {
-  // TODO: = Self
-  let Result:! type;
-  fn Op[self: Self](other: U) -> Result;
-}
-constraint RightShift {
-  extend RightShiftWith(Self) where .Result = Self;
-}
-
-impl i32 as BitComplement where .Result = i32 {
-  fn Op[self: i32]() -> i32 {
-    return __intrinsic_int_bit_complement(self);
-  }
-}
-impl i32 as BitAndWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 {
-    return __intrinsic_int_bit_and(self, other);
-  }
-}
-impl i32 as BitOrWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 {
-    return __intrinsic_int_bit_or(self, other);
-  }
-}
-impl i32 as BitXorWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 {
-    return __intrinsic_int_bit_xor(self, other);
-  }
-}
-impl i32 as LeftShiftWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 {
-    return __intrinsic_int_left_shift(self, other);
-  }
-}
-impl i32 as RightShiftWith(i32) where .Result = i32 {
-  fn Op[self: i32](other: i32) -> i32 {
-    return __intrinsic_int_right_shift(self, other);
-  }
-}
-
-// -----------------------------------
-// Assignment and compound assignment.
-// -----------------------------------
-
-interface AssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint Assign { extend AssignWith(Self); }
-
-interface AddAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint AddAssign { extend AddAssignWith(Self); }
-
-interface SubAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint SubAssign { extend SubAssignWith(Self); }
-
-interface MulAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint MulAssign { extend MulAssignWith(Self); }
-
-interface DivAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint DivAssign { extend DivAssignWith(Self); }
-
-interface ModAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint ModAssign { extend ModAssignWith(Self); }
-
-interface BitAndAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint BitAssignAnd { extend BitAndAssignWith(Self); }
-
-interface BitOrAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint BitAssignOr { extend BitOrAssignWith(Self); }
-
-interface BitXorAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint BitAssignXor { extend BitXorAssignWith(Self); }
-
-interface LeftShiftAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint LeftShiftAssign { extend LeftShiftAssignWith(Self); }
-
-interface RightShiftAssignWith(U:! type) {
-  fn Op[addr self: Self*](other: U);
-}
-constraint RightShiftAssign { extend RightShiftAssignWith(Self); }
-
-// TODO: This is temporary, and should eventually be replaced by
-// something more fine-grained. Not all class types should be
-// assignable.
-impl forall [T:! type, U:! ImplicitAs(T)]
-    T as AssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = other.Convert();
-  }
-}
-
-// TODO: Should `AddWith(U) & AssignWith(.Self.(AddWith(U).Result))` work?
-impl forall [U:! type, T:! AddWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as AddAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self + other;
-  }
-}
-
-impl forall [U:! type, T:! SubWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as SubAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self - other;
-  }
-}
-
-impl forall [U:! type, T:! MulWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as MulAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self * other;
-  }
-}
-
-impl forall [U:! type, T:! DivWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as DivAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self / other;
-  }
-}
-
-impl forall [U:! type, T:! ModWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as ModAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self % other;
-  }
-}
-
-impl forall [U:! type, T:! BitAndWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as BitAndAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self & other;
-  }
-}
-
-impl forall [U:! type, T:! BitOrWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as BitOrAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self | other;
-  }
-}
-
-impl forall [U:! type, T:! BitXorWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as BitXorAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self ^ other;
-  }
-}
-
-impl forall [U:! type, T:! LeftShiftWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as LeftShiftAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self << other;
-  }
-}
-
-impl forall [U:! type, T:! RightShiftWith(U) where .Self impls AssignWith(.Self.Result)]
-     T as RightShiftAssignWith(U) {
-  fn Op[addr self: Self*](other: U) {
-    *self = *self >> other;
-  }
-}
-
-// ------------------------
-// Increment and decrement.
-// ------------------------
-
-interface Inc {
-  fn Op[addr self: Self*]();
-}
-interface Dec {
-  fn Op[addr self: Self*]();
-}
-
-impl i32 as Inc {
-  fn Op[addr self: Self*]() {
-    *self = *self + 1;
-  }
-}
-impl i32 as Dec {
-  fn Op[addr self: Self*]() {
-    *self = *self - 1;
-  }
-}
-
-// ------------------------
-// Miscellaneous utilities.
-// ------------------------
-
-// Note that Print is experimental, and not part of an accepted proposal, but
-// is included here for printing state in tests.
-// TODO: Remove Print special casing once we have variadics or overloads.
-// fn Print(format_str: String) {
-//   __intrinsic_print(format_str);
-// }
-
-fn Assert(condition: bool, message: String){
-    __intrinsic_assert(condition, message);
-}
-
-fn Rand(low: i32, high: i32) -> i32{
-    return __intrinsic_rand(low,high);
-}
-
-//-------------------------
-// Optional.
-//-------------------------
-choice OptionalElement(T:! type) {
-  None,
-  Element(T)
-}
-
-class Optional(T:! type) {
-  fn CreateEmpty() -> Optional(T) {
-    return {.element = OptionalElement(T).None};
-  }
-  fn Create(value: T) -> Optional(T) {
-    return {.element = OptionalElement(T).Element(value)};
-  }
-
-  fn HasValue[self: Self]() -> bool {
-    match(self.element) {
-      case OptionalElement(T).None => { return false; }
-    }
-    return true;
-  }
-
-  fn Get[self: Self]() -> T {
-    match(self.element) {
-      case OptionalElement(T).Element(x: T) => {
-        return x;
-      }
-    }
-    Assert(false, "Attempted to unwrap empty Optional");
-    // TODO: Drop return when we can flag unreachable paths.
-    return self.Get();
-  }
-
-  var element: OptionalElement(T);
-}
-
-//-------------------------
-// Heap.
-//-------------------------
-
-class Heap {
-  fn New[T:! type, self: Self](x : T) -> T* {
-    return __intrinsic_new(x);
-  }
-  fn Delete[T:! type, self: Self](p : T*) {
-    __intrinsic_delete(p);
-  }
-  fn PrintAllocs[self: Self]() {
-    __intrinsic_print_allocs();
-  }
-}
-
-var heap: Heap = {};

+ 0 - 133
explorer/file_test.cpp

@@ -1,133 +0,0 @@
-// 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 "absl/flags/flag.h"
-#include "absl/strings/str_split.h"
-#include "common/raw_string_ostream.h"
-#include "explorer/main.h"
-#include "re2/re2.h"
-#include "testing/base/file_helpers.h"
-#include "testing/file_test/file_test_base.h"
-#include "testing/file_test/manifest.h"
-
-ABSL_FLAG(bool, trace, false,
-          "Set to true to run tests with tracing enabled, even if they don't "
-          "otherwise specify it. This does not result in checking trace output "
-          "contents; it essentially only verifies there's not a crash bug.");
-ABSL_FLAG(std::string, explorer_test_targets_file, "",
-          "A path to a file containing repo-relative names of test files.");
-
-namespace Carbon::Testing {
-namespace {
-
-class ExplorerFileTest : public FileTestBase {
- public:
-  explicit ExplorerFileTest(llvm::StringRef /*exe_path*/,
-                            llvm::StringRef test_name)
-      : FileTestBase(test_name),
-        prelude_line_re_(R"(prelude.carbon:(\d+))"),
-        timing_re_(R"((Time elapsed in \w+: )\d+(ms))") {
-    CARBON_CHECK(prelude_line_re_.ok(), "{0}", prelude_line_re_.error());
-    CARBON_CHECK(timing_re_.ok(), "{0}", timing_re_.error());
-  }
-
-  auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
-           llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
-           FILE* /*input_stream*/, llvm::raw_pwrite_stream& output_stream,
-           llvm::raw_pwrite_stream& error_stream) const
-      -> ErrorOr<RunResult> override {
-    // Add the prelude.
-    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
-        llvm::MemoryBuffer::getFile("explorer/data/prelude.carbon");
-    if (prelude.getError()) {
-      return ErrorBuilder() << prelude.getError().message();
-    }
-    // TODO: This path is long with a prefix / because of the path expectations
-    // in tests. Change those to allow a shorter path (e.g., `prelude.carbon`)
-    // here.
-    static constexpr llvm::StringLiteral PreludePath =
-        "/explorer/data/prelude.carbon";
-    if (!fs->addFile(PreludePath, /*ModificationTime=*/0,
-                     std::move(*prelude))) {
-      return ErrorBuilder() << "Duplicate prelude.carbon";
-    }
-
-    llvm::SmallVector<const char*> args = {"explorer"};
-    for (auto arg : test_args) {
-      args.push_back(arg.data());
-    }
-
-    RawStringOstream trace_stream;
-    int exit_code =
-        ExplorerMain(args.size(), args.data(), /*install_path=*/"", PreludePath,
-                     output_stream, error_stream,
-                     check_trace_output() ? output_stream : trace_stream, *fs);
-
-    // Skip trace test check as they use stdout stream instead of
-    // trace_stream_ostream
-    if (absl::GetFlag(FLAGS_trace) && trace_stream.TakeStr().empty()) {
-      return Error("Tracing should always do something");
-    }
-
-    return {{.success = exit_code == EXIT_SUCCESS}};
-  }
-
-  auto GetDefaultArgs() const -> llvm::SmallVector<std::string> override {
-    llvm::SmallVector<std::string> args;
-    if (absl::GetFlag(FLAGS_trace)) {
-      args.push_back("--trace_file=-");
-      args.push_back("--trace_phase=all");
-    }
-    args.push_back("%s");
-    return args;
-  }
-
-  auto GetLineNumberReplacements(llvm::ArrayRef<llvm::StringRef> filenames)
-      const -> llvm::SmallVector<LineNumberReplacement> override {
-    if (check_trace_output()) {
-      return {};
-    }
-    return FileTestBase::GetLineNumberReplacements(filenames);
-  }
-
-  auto DoExtraCheckReplacements(std::string& check_line) const
-      -> void override {
-    // Ignore the resulting column of EndOfFile because it's often the end of
-    // the CHECK comment.
-    RE2::GlobalReplace(&check_line, prelude_line_re_,
-                       R"(prelude.carbon:{{\\d+}})");
-    if (check_trace_output()) {
-      // Replace timings in trace output.
-      RE2::GlobalReplace(&check_line, timing_re_, R"(\1{{\\d+}}\2)");
-    }
-  }
-
-  // Cannot execute in parallel.
-  auto AllowParallelRun() const -> bool override { return false; }
-
- private:
-  // Trace output is directly checked for a few tests.
-  auto check_trace_output() const -> bool {
-    return test_name().find("/trace/") != std::string::npos;
-  }
-
-  RE2 prelude_line_re_;
-  RE2 timing_re_;
-};
-
-}  // namespace
-
-// Explorer uses a non-standard approach to getting the manifest path.
-auto GetFileTestManifest() -> llvm::SmallVector<std::string> {
-  llvm::SmallVector<std::string> manifest;
-  auto content = ReadFile(absl::GetFlag(FLAGS_explorer_test_targets_file));
-  for (const auto& line : absl::StrSplit(*content, '\n', absl::SkipEmpty())) {
-    manifest.push_back(std::string(line));
-  }
-  return manifest;
-}
-
-CARBON_FILE_TEST_FACTORY(ExplorerFileTest)
-
-}  // namespace Carbon::Testing

+ 0 - 368
explorer/interpreter/BUILD

@@ -1,368 +0,0 @@
-# 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
-
-load("@rules_cc//cc:defs.bzl", "cc_library")
-
-package(default_visibility = ["//explorer/parse_and_execute:__pkg__"])
-
-cc_library(
-    name = "action",
-    srcs = [
-        "action.cpp",
-    ],
-    hdrs = [
-        "action.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":dictionary",
-        ":heap_allocation_interface",
-        ":stack",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/base:arena",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:source_location",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "action_stack",
-    srcs = [
-        "action_stack.cpp",
-    ],
-    hdrs = [
-        "action_stack.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":action",
-        ":stack",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "dictionary",
-    hdrs = ["dictionary.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = ["//explorer/base:arena"],
-)
-
-cc_library(
-    name = "exec_program",
-    srcs = ["exec_program.cpp"],
-    hdrs = ["exec_program.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":interpreter",
-        ":resolve_control_flow",
-        ":resolve_names",
-        ":resolve_unformed",
-        ":type_checker",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/base:arena",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "heap",
-    srcs = ["heap.cpp"],
-    hdrs = ["heap.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":action",
-        ":heap_allocation_interface",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:source_location",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "heap_allocation_interface",
-    hdrs = ["heap_allocation_interface.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:error",
-        "//explorer/ast",
-        "//explorer/base:arena",
-        "//explorer/base:nonnull",
-        "//explorer/base:source_location",
-    ],
-)
-
-cc_library(
-    name = "interpreter",
-    srcs = [
-        "interpreter.cpp",
-    ],
-    hdrs = [
-        "interpreter.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":action",
-        ":action_stack",
-        ":heap",
-        ":pattern_match",
-        ":stack",
-        ":type_utils",
-        "//common:check",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/ast:expression_category",
-        "//explorer/base:arena",
-        "//explorer/base:error_builders",
-        "//explorer/base:print_as_id",
-        "//explorer/base:source_location",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "resolve_control_flow",
-    srcs = ["resolve_control_flow.cpp"],
-    hdrs = ["resolve_control_flow.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:check",
-        "//explorer/ast",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "resolve_names",
-    srcs = ["resolve_names.cpp"],
-    hdrs = ["resolve_names.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":stack_space",
-        "//common:check",
-        "//explorer/ast",
-        "//explorer/ast:static_scope",
-        "//explorer/base:arena",
-        "//explorer/base:print_as_id",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "stack",
-    hdrs = ["stack.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = ["//common:check"],
-)
-
-cc_library(
-    name = "pattern_analysis",
-    srcs = [
-        "pattern_analysis.cpp",
-    ],
-    hdrs = [
-        "pattern_analysis.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":action",
-        "//common:error",
-        "//explorer/ast",
-        "//explorer/base:nonnull",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "type_checker",
-    srcs = [
-        "builtins.cpp",
-        "impl_scope.cpp",
-        "matching_impl_set.cpp",
-        "type_checker.cpp",
-    ],
-    hdrs = [
-        "builtins.h",
-        "impl_scope.h",
-        "matching_impl_set.h",
-        "type_checker.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    textual_hdrs = [
-        "builtins.def",
-    ],
-    deps = [
-        ":action",
-        ":dictionary",
-        ":interpreter",
-        ":pattern_analysis",
-        ":pattern_match",
-        ":stack_space",
-        ":type_structure",
-        ":type_utils",
-        "//common:check",
-        "//common:enum_base",
-        "//common:error",
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/base:arena",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:source_location",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "resolve_unformed",
-    srcs = [
-        "resolve_unformed.cpp",
-    ],
-    hdrs = [
-        "resolve_unformed.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":stack_space",
-        "//common:check",
-        "//explorer/ast",
-        "//explorer/ast:static_scope",
-        "//explorer/base:error_builders",
-        "//explorer/base:nonnull",
-        "//explorer/base:print_as_id",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "stack_space",
-    srcs = ["stack_space.cpp"],
-    hdrs = ["stack_space.h"],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:check",
-        "//common:error",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "type_structure",
-    srcs = [
-        "type_structure.cpp",
-    ],
-    hdrs = [
-        "type_structure.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//common:ostream",
-        "//explorer/ast",
-        "//explorer/ast:expression_category",
-        "//explorer/base:nonnull",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "type_utils",
-    srcs = [
-        "type_utils.cpp",
-    ],
-    hdrs = [
-        "type_utils.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        "//explorer/ast",
-        "//explorer/base:nonnull",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
-cc_library(
-    name = "pattern_match",
-    srcs = [
-        "pattern_match.cpp",
-    ],
-    hdrs = [
-        "pattern_match.h",
-    ],
-    # Running clang-tidy is slow, and explorer is currently feature frozen, so
-    # don't spend time linting it.
-    tags = ["no-clang-tidy"],
-    deps = [
-        ":action",
-        ":type_utils",
-        "//explorer/ast",
-        "//explorer/base:arena",
-        "//explorer/base:nonnull",
-        "//explorer/base:source_location",
-        "//explorer/base:trace_stream",
-        "@llvm-project//llvm:Support",
-    ],
-)

+ 0 - 114
explorer/interpreter/README.md

@@ -1,114 +0,0 @@
-# `explorer` execution
-
-<!--
-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
--->
-
-The code in this directory defines all phases of program execution after
-parsing, including typechecking, name resolution, and execution. The overall
-flow can be found in [`ExecProgram`](exec_program.cpp), which executes each
-phase in sequence.
-
-## The Carbon abstract machine
-
-Execution is specified in terms of an abstract machine, which executes minimal
-program steps in a loop until the program terminates. The state of the Carbon
-program (including the stack as well as the heap) is represented explicitly in
-C++ data structures, rather than implicitly in the C++ call stack. The
-[`Interpreter`](interpreter.cpp) class represents an instance of the abstract
-machine, and is responsible for maintaining those data structures and
-implementing the steps of abstract machine execution.
-
-The control-flow state of the abstract machine is encapsulated in an
-[`ActionStack`](action_stack.h) object, which a represents a stack of
-[`Action`s](action.h). An `Action` represents a self-contained computation (such
-as evaluation of an expression or execution of a statement) as a state machine,
-and the abstract machine proceeds by repeatedly executing the next state
-transition of the `Action` at the top of the stack. Executing a step may modify
-the internal state of the `Action`, and may also modify the `Action` stack, for
-example by pushing a new `Action` onto it. When an `Action` is done executing,
-it can optionally produce a value as its result, which is made available to the
-`Action` below it on the stack.
-
-Carbon values are represented as [`Value`](../ast/value.h) objects, both at
-compile time and at run time. Note that in Carbon, a type is a kind of value, so
-types are represented as `Value`s. More subtly, `Value` can also represent
-information that isn't a true Carbon value, but needs to be propagated through
-channels that use `Value`. Most notably, certain kinds of `Value` are used to
-represent the result of "evaluating" a `Pattern`, which evaluates all the
-subexpressions nested within it, while preserving the structure of the
-non-expression parts for use in pattern matching.
-
-`Value`s are always immutable. The abstract machine's mutable memory is
-represented using the [`Heap`](heap.h) class, which is essentially a mapping of
-[`Address`es](../ast/address.h) to `Value`s.
-
-### Example
-
-To evaluate the expression `((1 + 2) + 4)`, the interpreter starts by pushing an
-`Action` onto the stack that corresponds to the whole expression:
-
-    ((1 + 2) + 4) .0. ## ...
-
-In this notation, we're expressing the stack as a sequence of `Action`s
-separated by `##`, with the top at the left, and representing each `Action` as
-the expression it evaluates, followed by its state. An `Action` consists of:
-
--   The syntax for the part of the program being executed, in this case
-    `((1 + 2) + 4)`.
--   An integer `pos` for position, which is initially 0 and usually counts the
-    number of steps executed. Here that's denoted with a number between two
-    periods.
--   A vector `results`, which collects the results of any sub-`Action`s spawned
-    by the `Action`. Above the results are omitted because they are currently
-    empty.
--   A `scope` mapping variables to their values, for those variables whose
-    lifetimes are associated with this action.
-
-Then the interpreter proceeds by repeatedly taking the next step of the `Action`
-at the top of the stack. For expression `Action`s, `pos` typically identifies
-the operand that the next step should begin evaluation of. In this case, that
-operand is the expression `(1 + 2)`, so we push a new `Action` onto the stack,
-and increment `pos` on the old one:
-
-    (1 + 2) .0. ## ((1 + 2) + 4) .1. ## ...
-
-The next step spawns an action to evaluate `1`:
-
-    1 .0. ## (1 + 2) .1. ## ((1 + 2) + 4) .1. ## ...
-
-That expression can be fully evaluated in a single step, so the next step
-evaluates it, appends the result to the next `Action` down the stack, and pops
-the now-completed `Action` off the stack:
-
-    (1 + 2) .1. [[1]] ## ((1 + 2) + 4) .1. ## ...
-
-The result `1` has been stored in the `results` list of the top `Action`, which
-is displayed between `[[` and `]]`. The top `Action`'s `pos` is 1, so the next
-step begins evaluation of the second operand:
-
-    2 .0. ## (1 + 2) .2. [[1]] ## ((1 + 2) + 4) .1. ## ...
-
-Which again can be evaluated immediately:
-
-    (1 + 2) .2. [[1, 2]] ## ((1 + 2) + 4) .1. ## ...
-
-This expression has two operands, so now that `pos` is 2, all operands have been
-evaluated, and their results are in the corresponding entries of `results`.
-Thus, the next step can compute the expression value, passing it down to the
-parent `Action` and popping the completed action as before:
-
-    ((1 + 2) + 4) .1. [[3]] ## ...
-
-Evaluation now proceeds to the second operand:
-
-    4 .0. ## ((1 + 2) + 4) .2. [[3]] ## ...
-
-Which, again, can be evaluated immediately:
-
-    ((1 + 2) + 4) .2. [[3, 4]] ## ...
-
-`pos` now indicates that all subexpressions have been evaluated, so the next
-step computes the final result of `7`.

+ 0 - 212
explorer/interpreter/action.cpp

@@ -1,212 +0,0 @@
-// 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 "explorer/interpreter/action.h"
-
-#include <iterator>
-#include <map>
-#include <optional>
-#include <utility>
-#include <vector>
-
-#include "common/check.h"
-#include "common/error.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/print_as_id.h"
-#include "explorer/base/source_location.h"
-#include "explorer/interpreter/stack.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-
-RuntimeScope::RuntimeScope(RuntimeScope&& other) noexcept
-    : locals_(std::move(other.locals_)),
-      bound_values_(std::move(other.bound_values_)),
-      // To transfer ownership of other.allocations_, we have to empty it out.
-      allocations_(std::exchange(other.allocations_, {})),
-      heap_(other.heap_) {}
-
-auto RuntimeScope::operator=(RuntimeScope&& rhs) noexcept -> RuntimeScope& {
-  locals_ = std::move(rhs.locals_);
-  bound_values_ = std::move(rhs.bound_values_);
-  // To transfer ownership of rhs.allocations_, we have to empty it out.
-  allocations_ = std::exchange(rhs.allocations_, {});
-  heap_ = rhs.heap_;
-  return *this;
-}
-
-void RuntimeScope::Print(llvm::raw_ostream& out) const {
-  out << "scope: [";
-  llvm::ListSeparator sep;
-  for (const auto& [value_node, value] : locals_) {
-    out << sep << "`" << value_node.base() << "`: `" << *value << "`";
-  }
-  out << "]";
-}
-
-void RuntimeScope::Bind(ValueNodeView value_node, Address address) {
-  CARBON_CHECK(!value_node.constant_value().has_value());
-  bool success =
-      locals_.insert({value_node, heap_->arena().New<LocationValue>(address)})
-          .second;
-  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
-}
-
-void RuntimeScope::BindAndPin(ValueNodeView value_node, Address address) {
-  Bind(value_node, address);
-  bool success = bound_values_.insert(&value_node.base()).second;
-  CARBON_CHECK(success, "Duplicate pinned node for {0}", value_node.base());
-  heap_->BindValueToReference(value_node, address);
-}
-
-void RuntimeScope::BindLifetimeToScope(Address address) {
-  CARBON_CHECK(address.element_path_.IsEmpty(),
-               "Cannot extend lifetime of a specific sub-element");
-  allocations_.push_back(address.allocation_);
-}
-
-void RuntimeScope::BindValue(ValueNodeView value_node,
-                             Nonnull<const Value*> value) {
-  CARBON_CHECK(!value_node.constant_value().has_value());
-  CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
-  bool success = locals_.insert({value_node, value}).second;
-  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
-}
-
-auto RuntimeScope::Initialize(ValueNodeView value_node,
-                              Nonnull<const Value*> value)
-    -> Nonnull<const LocationValue*> {
-  CARBON_CHECK(!value_node.constant_value().has_value());
-  CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
-  allocations_.push_back(heap_->AllocateValue(value));
-  const auto* location =
-      heap_->arena().New<LocationValue>(Address(allocations_.back()));
-  bool success = locals_.insert({value_node, location}).second;
-  CARBON_CHECK(success, "Duplicate definition of {0}", value_node.base());
-  return location;
-}
-
-void RuntimeScope::Merge(RuntimeScope other) {
-  CARBON_CHECK(heap_ == other.heap_);
-  for (auto& element : other.locals_) {
-    bool success = locals_.insert(element).second;
-    CARBON_CHECK(success, "Duplicate definition of {0}", element.first);
-  }
-  for (const auto* element : other.bound_values_) {
-    bool success = bound_values_.insert(element).second;
-    CARBON_CHECK(success, "Duplicate bound value.");
-  }
-  allocations_.insert(allocations_.end(), other.allocations_.begin(),
-                      other.allocations_.end());
-  other.allocations_.clear();
-}
-
-auto RuntimeScope::Get(ValueNodeView value_node,
-                       SourceLocation source_loc) const
-    -> ErrorOr<std::optional<Nonnull<const Value*>>> {
-  auto it = locals_.find(value_node);
-  if (it == locals_.end()) {
-    return {std::nullopt};
-  }
-  if (bound_values_.contains(&value_node.base())) {
-    // Check if the bound value is still alive.
-    CARBON_CHECK(it->second->kind() == Value::Kind::LocationValue);
-    if (!heap_->is_bound_value_alive(
-            value_node, cast<LocationValue>(it->second)->address())) {
-      return ProgramError(source_loc)
-             << "Reference has changed since this value was bound.";
-    }
-  }
-  return {it->second};
-}
-
-auto RuntimeScope::Capture(
-    const std::vector<Nonnull<const RuntimeScope*>>& scopes) -> RuntimeScope {
-  CARBON_CHECK(!scopes.empty());
-  RuntimeScope result(scopes.front()->heap_);
-  for (Nonnull<const RuntimeScope*> scope : scopes) {
-    CARBON_CHECK(scope->heap_ == result.heap_);
-    for (const auto& entry : scope->locals_) {
-      // Intentionally disregards duplicates later in the vector.
-      result.locals_.insert(entry);
-    }
-  }
-  return result;
-}
-
-void Action::Print(llvm::raw_ostream& out) const {
-  out << kind_string() << " pos: " << pos_ << " ";
-  switch (kind()) {
-    case Action::Kind::LocationAction:
-      out << "`" << cast<LocationAction>(*this).expression() << "`";
-      break;
-    case Action::Kind::ValueExpressionAction:
-      out << "`" << cast<ValueExpressionAction>(*this).expression() << "`";
-      break;
-    case Action::Kind::ExpressionAction:
-      out << "`" << cast<ExpressionAction>(*this).expression() << "`";
-      break;
-    case Action::Kind::WitnessAction:
-      out << "`" << *cast<WitnessAction>(*this).witness() << "`";
-      break;
-    case Action::Kind::StatementAction:
-      out << "`" << PrintAsID(cast<StatementAction>(*this).statement()) << "`";
-      break;
-    case Action::Kind::DeclarationAction:
-      out << "`" << PrintAsID(cast<DeclarationAction>(*this).declaration())
-          << "`";
-      break;
-    case Action::Kind::TypeInstantiationAction:
-      out << "`" << *cast<TypeInstantiationAction>(*this).type() << "`";
-      break;
-    default:
-      break;
-  }
-  if (!results_.empty()) {
-    out << " results: [";
-    llvm::ListSeparator sep;
-    for (const auto& result : results_) {
-      out << sep << "`" << *result << "`";
-    }
-    out << "] ";
-  }
-  if (scope_.has_value()) {
-    out << " " << *scope_;
-  }
-}
-
-auto Action::kind_string() const -> std::string_view {
-  switch (kind()) {
-    case Action::Kind::LocationAction:
-      return "LocationAction";
-    case Action::Kind::ValueExpressionAction:
-      return "ValueExpressionAction";
-    case Action::Kind::ExpressionAction:
-      return "ExpressionAction";
-    case Action::Kind::WitnessAction:
-      return "WitnessAction";
-    case Action::Kind::StatementAction:
-      return "StatementAction";
-    case Action::Kind::DeclarationAction:
-      return "DeclarationAction";
-    case Action::Kind::TypeInstantiationAction:
-      return "TypeInstantiationAction";
-    case Action::Kind::ScopeAction:
-      return "ScopeAction";
-    case Action::Kind::RecursiveAction:
-      return "RecursiveAction";
-    case Action::Kind::CleanUpAction:
-      return "CleanUpAction";
-    case Action::Kind::DestroyAction:
-      return "DestroyAction";
-  }
-}
-
-}  // namespace Carbon

+ 0 - 465
explorer/interpreter/action.h

@@ -1,465 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_ACTION_H_
-#define CARBON_EXPLORER_INTERPRETER_ACTION_H_
-
-#include <list>
-#include <map>
-#include <optional>
-#include <tuple>
-#include <vector>
-
-#include "common/check.h"
-#include "common/ostream.h"
-#include "explorer/ast/address.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/statement.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/source_location.h"
-#include "explorer/interpreter/dictionary.h"
-#include "explorer/interpreter/heap_allocation_interface.h"
-#include "explorer/interpreter/stack.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/MapVector.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-// A RuntimeScope manages and provides access to the storage for names that are
-// not compile-time constants.
-class RuntimeScope : public Printable<RuntimeScope> {
- public:
-  // Returns a RuntimeScope whose Get() operation for a given name returns the
-  // storage owned by the first entry in `scopes` that defines that name. This
-  // behavior is closely analogous to a `[&]` capture in C++, hence the name.
-  // `scopes` must contain at least one entry, and all entries must be backed
-  // by the same Heap.
-  static auto Capture(const std::vector<Nonnull<const RuntimeScope*>>& scopes)
-      -> RuntimeScope;
-
-  // Constructs a RuntimeScope that allocates storage in `heap`.
-  explicit RuntimeScope(Nonnull<HeapAllocationInterface*> heap) : heap_(heap) {}
-
-  // Moving a RuntimeScope transfers ownership of its allocations.
-  RuntimeScope(RuntimeScope&&) noexcept;
-  auto operator=(RuntimeScope&&) noexcept -> RuntimeScope&;
-
-  void Print(llvm::raw_ostream& out) const;
-
-  // Allocates storage for `value_node` in `heap`, and initializes it with
-  // `value`.
-  auto Initialize(ValueNodeView value_node, Nonnull<const Value*> value)
-      -> Nonnull<const LocationValue*>;
-
-  // Bind allocation lifetime to scope. Should only be called with unowned
-  // allocations to avoid a double free.
-  void BindLifetimeToScope(Address address);
-
-  // Binds location `address` of a reference value to `value_node` without
-  // allocating local storage.
-  void Bind(ValueNodeView value_node, Address address);
-
-  // Binds location `address` of a reference value to `value_node` without
-  // allocating local storage, and pins the value, making it immutable.
-  void BindAndPin(ValueNodeView value_node, Address address);
-
-  // Binds unlocated `value` to `value_node` without allocating local storage.
-  // TODO: BindValue should pin the lifetime of `value` and make sure it isn't
-  // mutated.
-  void BindValue(ValueNodeView value_node, Nonnull<const Value*> value);
-
-  // Transfers the names and allocations from `other` into *this. The two
-  // scopes must not define the same name, and must be backed by the same Heap.
-  void Merge(RuntimeScope other);
-
-  // Given node `value_node`, returns:
-  // - its `LocationValue*` if bound to a reference expression in this scope,
-  // - a `Value*` if bound to a value expression in this scope, or
-  // - `nullptr` if not bound.
-  auto Get(ValueNodeView value_node, SourceLocation source_loc) const
-      -> ErrorOr<std::optional<Nonnull<const Value*>>>;
-
-  // Returns the local values with allocation in created order.
-  auto allocations() const -> const std::vector<AllocationId>& {
-    return allocations_;
-  }
-
- private:
-  llvm::MapVector<ValueNodeView, Nonnull<const Value*>,
-                  std::map<ValueNodeView, unsigned>>
-      locals_;
-  llvm::DenseSet<const AstNode*> bound_values_;
-  std::vector<AllocationId> allocations_;
-  Nonnull<HeapAllocationInterface*> heap_;
-};
-
-// An Action represents the current state of a self-contained computation,
-// usually associated with some AST node, such as evaluation of an expression or
-// execution of a statement. Execution of an action is divided into a series of
-// steps, and the `pos` field typically counts the number of steps executed.
-//
-// They should be destroyed as soon as they are done executing, in order to
-// clean up the associated Carbon scope, and consequently they should not be
-// allocated on an Arena. Actions are typically owned by the ActionStack.
-//
-// The actual behavior of an Action step is defined by Interpreter::Step, not by
-// Action or its subclasses.
-// TODO: consider moving this logic to a virtual method `Step`.
-class Action : public Printable<Action> {
- public:
-  enum class Kind {
-    LocationAction,
-    ValueExpressionAction,
-    ExpressionAction,
-    WitnessAction,
-    StatementAction,
-    DeclarationAction,
-    ScopeAction,
-    RecursiveAction,
-    CleanUpAction,
-    DestroyAction,
-    TypeInstantiationAction
-  };
-
-  Action(const Value&) = delete;
-  auto operator=(const Value&) -> Action& = delete;
-
-  virtual ~Action() = default;
-
-  void Print(llvm::raw_ostream& out) const;
-
-  // Resets this Action to its initial state.
-  void Clear() {
-    CARBON_CHECK(!scope_.has_value());
-    pos_ = 0;
-    results_.clear();
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> Kind { return kind_; }
-
-  auto kind_string() const -> std::string_view;
-
-  // The position or state of the action. Starts at 0 and is typically
-  // incremented after each step.
-  auto pos() const -> int { return pos_; }
-  void set_pos(int pos) { this->pos_ = pos; }
-
-  // The results of any Actions spawned by this Action.
-  auto results() const -> const std::vector<Nonnull<const Value*>>& {
-    return results_;
-  }
-  void ReplaceResult(std::size_t index, Nonnull<const Value*> value) {
-    CARBON_CHECK(index < results_.size());
-    results_[index] = value;
-  }
-  // Appends `result` to `results`.
-  void AddResult(Nonnull<const Value*> result) { results_.push_back(result); }
-
-  // Returns the scope associated with this Action, if any.
-  auto scope() -> std::optional<RuntimeScope>& { return scope_; }
-  auto scope() const -> const std::optional<RuntimeScope>& { return scope_; }
-
-  // Associates this action with a new scope, with initial state `scope`.
-  // Values that are local to this scope will be deallocated when this
-  // Action is completed or unwound. Can only be called once on a given
-  // Action.
-  void StartScope(RuntimeScope scope) {
-    CARBON_CHECK(!scope_.has_value());
-    scope_ = std::move(scope);
-  }
-
-  auto source_loc() const -> std::optional<SourceLocation> {
-    return source_loc_;
-  }
-
- protected:
-  // Constructs an Action. `kind` must be the enumerator corresponding to the
-  // most-derived type being constructed.
-  explicit Action(std::optional<SourceLocation> source_loc, Kind kind)
-      : source_loc_(source_loc), kind_(kind) {}
-  std::optional<SourceLocation> source_loc_;
-
- private:
-  int pos_ = 0;
-  std::vector<Nonnull<const Value*>> results_;
-  std::optional<RuntimeScope> scope_;
-
-  const Kind kind_;
-};
-
-// An Action which implements evaluation of an Expression to produce an
-// LocationValue.
-class LocationAction : public Action {
- public:
-  explicit LocationAction(Nonnull<const Expression*> expression)
-      : Action(expression->source_loc(), Kind::LocationAction),
-        expression_(expression) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::LocationAction;
-  }
-
-  // The Expression this Action evaluates.
-  auto expression() const -> const Expression& { return *expression_; }
-
- private:
-  Nonnull<const Expression*> expression_;
-};
-
-// An Action which implements evaluation of an Expression to produce a `Value*`.
-class ValueExpressionAction : public Action {
- public:
-  explicit ValueExpressionAction(
-      Nonnull<const Expression*> expression,
-      std::optional<AllocationId> initialized_location = std::nullopt)
-      : Action(expression->source_loc(), Kind::ValueExpressionAction),
-        expression_(expression),
-        location_received_(initialized_location) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::ValueExpressionAction;
-  }
-
-  // The Expression this Action evaluates.
-  auto expression() const -> const Expression& { return *expression_; }
-
-  // The location provided for the initializing expression, if any.
-  auto location_received() const -> std::optional<AllocationId> {
-    return location_received_;
-  }
-
- private:
-  Nonnull<const Expression*> expression_;
-  std::optional<AllocationId> location_received_;
-};
-
-// An Action which implements evaluation of a reference Expression to produce an
-// `ReferenceExpressionValue*`. The `preserve_nested_categories` flag can be
-// used to preserve values as `ReferenceExpressionValue` in nested value types,
-// such as tuples.
-class ExpressionAction : public Action {
- public:
-  ExpressionAction(
-      Nonnull<const Expression*> expression, bool preserve_nested_categories,
-      std::optional<AllocationId> initialized_location = std::nullopt)
-      : Action(expression->source_loc(), Kind::ExpressionAction),
-        expression_(expression),
-        location_received_(initialized_location),
-        preserve_nested_categories_(preserve_nested_categories) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::ExpressionAction;
-  }
-
-  // The Expression this Action evaluates.
-  auto expression() const -> const Expression& { return *expression_; }
-
-  // Returns whether direct descendent actions should preserve values as
-  // `ReferenceExpressionValue*`s.
-  auto preserve_nested_categories() const -> bool {
-    return preserve_nested_categories_;
-  }
-
-  // The location provided for the initializing expression, if any.
-  auto location_received() const -> std::optional<AllocationId> {
-    return location_received_;
-  }
-
- private:
-  Nonnull<const Expression*> expression_;
-  std::optional<AllocationId> location_received_;
-  bool preserve_nested_categories_;
-};
-
-// An Action which implements the Instantiation of Type. The result is expressed
-// as a Value.
-class TypeInstantiationAction : public Action {
- public:
-  explicit TypeInstantiationAction(Nonnull<const Value*> type,
-                                   SourceLocation source_loc)
-      : Action(source_loc, Kind::TypeInstantiationAction),
-        type_(type),
-        source_loc_(source_loc) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::TypeInstantiationAction;
-  }
-
-  auto type() const -> Nonnull<const Value*> { return type_; }
-  auto source_loc() const -> SourceLocation { return source_loc_; }
-
- private:
-  Nonnull<const Value*> type_;
-  SourceLocation source_loc_;
-};
-
-// An Action which implements evaluation of a Witness to resolve it in the
-// local context.
-class WitnessAction : public Action {
- public:
-  explicit WitnessAction(Nonnull<const Witness*> witness,
-                         SourceLocation source_loc)
-      : Action(source_loc, Kind::WitnessAction), witness_(witness) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::WitnessAction;
-  }
-
-  auto source_loc() -> SourceLocation {
-    CARBON_CHECK(source_loc_);
-    return *source_loc_;
-  }
-
-  // The Witness this Action resolves.
-  auto witness() const -> Nonnull<const Witness*> { return witness_; }
-
- private:
-  Nonnull<const Witness*> witness_;
-};
-
-// An Action which implements execution of a Statement. Does not produce a
-// result.
-class StatementAction : public Action {
- public:
-  explicit StatementAction(Nonnull<const Statement*> statement,
-                           std::optional<AllocationId> location_received)
-      : Action(statement->source_loc(), Kind::StatementAction),
-        statement_(statement),
-        location_received_(location_received) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::StatementAction;
-  }
-
-  // The Statement this Action executes.
-  auto statement() const -> const Statement& { return *statement_; }
-
-  // The location provided for the initializing expression, if any.
-  auto location_received() const -> std::optional<AllocationId> {
-    return location_received_;
-  }
-
-  // Sets the location provided to an initializing expression.
-  auto set_location_created(AllocationId location_created) {
-    CARBON_CHECK(!location_created_, "location created set twice");
-    location_created_ = location_created;
-  }
-  // Returns the location provided to an initializing expression, if any.
-  auto location_created() const -> std::optional<AllocationId> {
-    return location_created_;
-  }
-
- private:
-  Nonnull<const Statement*> statement_;
-  std::optional<AllocationId> location_received_;
-  std::optional<AllocationId> location_created_;
-};
-
-// Action which implements the run-time effects of executing a Declaration.
-// Does not produce a result.
-class DeclarationAction : public Action {
- public:
-  explicit DeclarationAction(Nonnull<const Declaration*> declaration)
-      : Action(declaration->source_loc(), Kind::DeclarationAction),
-        declaration_(declaration) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::DeclarationAction;
-  }
-
-  // The Declaration this Action executes.
-  auto declaration() const -> const Declaration& { return *declaration_; }
-
- private:
-  Nonnull<const Declaration*> declaration_;
-};
-
-// An Action which implements destroying all local allocations in a scope.
-class CleanUpAction : public Action {
- public:
-  explicit CleanUpAction(RuntimeScope scope, SourceLocation source_loc)
-      : Action(source_loc, Kind::CleanUpAction),
-        allocations_count_(scope.allocations().size()) {
-    StartScope(std::move(scope));
-  }
-
-  auto allocations_count() const -> int { return allocations_count_; }
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::CleanUpAction;
-  }
-
- private:
-  int allocations_count_;
-};
-
-// An Action which implements destroying a single value, including all nested
-// values.
-class DestroyAction : public Action {
- public:
-  // location: Location of the object to be destroyed
-  // value:    The value to be destroyed
-  //           In most cases the location address points to value
-  //           In the case that the member of a class is to be destroyed,
-  //           the location points to the address of the class object
-  //           and the value is the member of the class
-  explicit DestroyAction(Nonnull<const LocationValue*> location,
-                         Nonnull<const Value*> value)
-      : Action(std::nullopt, Kind::DestroyAction),
-        location_(location),
-        value_(value) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::DestroyAction;
-  }
-
-  auto location() const -> Nonnull<const LocationValue*> { return location_; }
-
-  auto value() const -> Nonnull<const Value*> { return value_; }
-
- private:
-  Nonnull<const LocationValue*> location_;
-  Nonnull<const Value*> value_;
-};
-
-// Action which does nothing except introduce a new scope into the action
-// stack. This is useful when a distinct scope doesn't otherwise have an
-// Action it can naturally be associated with. ScopeActions are not associated
-// with AST nodes.
-class ScopeAction : public Action {
- public:
-  explicit ScopeAction(RuntimeScope scope)
-      : Action(std::nullopt, Kind::ScopeAction) {
-    StartScope(std::move(scope));
-  }
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::ScopeAction;
-  }
-};
-
-// Action which contains another action and does nothing further once that
-// action completes. This action therefore acts as a marker on the action stack
-// that indicates that the interpreter should stop when the inner action has
-// finished, and holds the result of that inner action. This is useful to allow
-// a sequence of steps for an action to be run immediately rather than as part
-// of the normal step queue.
-//
-// Should be avoided where possible.
-class RecursiveAction : public Action {
- public:
-  explicit RecursiveAction() : Action(std::nullopt, Kind::RecursiveAction) {}
-
-  static auto classof(const Action* action) -> bool {
-    return action->kind() == Kind::RecursiveAction;
-  }
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_ACTION_H_

+ 0 - 299
explorer/interpreter/action_stack.cpp

@@ -1,299 +0,0 @@
-// 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 "explorer/interpreter/action_stack.h"
-
-#include "common/error.h"
-#include "explorer/interpreter/action.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Error.h"
-
-namespace Carbon {
-
-void ActionStack::Print(llvm::raw_ostream& out) const {
-  llvm::ListSeparator sep(" ## ");
-  for (const std::unique_ptr<Action>& action : todo_) {
-    out << sep << *action;
-  }
-}
-
-void ActionStack::Start(std::unique_ptr<Action> action) {
-  result_ = std::nullopt;
-  CARBON_CHECK(todo_.empty());
-  Push(std::move(action));
-}
-
-void ActionStack::Initialize(ValueNodeView value_node,
-                             Nonnull<const Value*> value) {
-  for (const std::unique_ptr<Action>& action : todo_) {
-    if (action->scope().has_value()) {
-      action->scope()->Initialize(value_node, value);
-      return;
-    }
-  }
-  globals_->Initialize(value_node, value);
-}
-
-auto ActionStack::ValueOfNode(ValueNodeView value_node,
-                              SourceLocation source_loc) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  std::optional<const Value*> constant_value = value_node.constant_value();
-  if (constant_value.has_value()) {
-    return *constant_value;
-  }
-  for (const std::unique_ptr<Action>& action : todo_) {
-    // TODO: have static name resolution identify the scope of value_node
-    // as an AstNode, and then perform lookup _only_ on the Action associated
-    // with that node. This will help keep unwanted dynamic-scoping behavior
-    // from sneaking in.
-    if (action->scope().has_value()) {
-      CARBON_ASSIGN_OR_RETURN(auto result,
-                              action->scope()->Get(value_node, source_loc));
-      if (result.has_value()) {
-        return *result;
-      }
-    }
-  }
-  if (globals_.has_value()) {
-    CARBON_ASSIGN_OR_RETURN(auto result, globals_->Get(value_node, source_loc));
-    if (result.has_value()) {
-      return *result;
-    }
-  }
-  // We don't know the value of this node, but at compile time we may still be
-  // able to form a symbolic value for it. For example, in
-  //
-  //   fn F[T:! type](x: T) {}
-  //
-  // ... we don't know the value of `T` but can still symbolically evaluate it
-  // to a `VariableType`. At runtime we need actual values.
-  if (phase_ == Phase::CompileTime) {
-    std::optional<const Value*> symbolic_identity =
-        value_node.symbolic_identity();
-    if (symbolic_identity.has_value()) {
-      return *symbolic_identity;
-    }
-  }
-  // TODO: Move these errors to compile time and explain them more clearly.
-  return ProgramError(source_loc)
-         << "could not find `" << value_node.base() << "`";
-}
-
-void ActionStack::MergeScope(RuntimeScope scope) {
-  for (const std::unique_ptr<Action>& action : todo_) {
-    if (action->scope().has_value()) {
-      action->scope()->Merge(std::move(scope));
-      return;
-    }
-  }
-  if (globals_.has_value()) {
-    globals_->Merge(std::move(scope));
-    return;
-  }
-  CARBON_FATAL("No current scope");
-}
-
-namespace {
-// The way in which FinishAction should be called for a particular kind of
-// action.
-enum class FinishActionKind {
-  // FinishAction should not be passed a value.
-  NoValue,
-  // FinishAction should be passed a value.
-  Value,
-  // FinishAction should not be called. The Action needs custom handling.
-  NeverCalled,
-};
-}  // namespace
-
-static auto FinishActionKindFor(Action::Kind kind) -> FinishActionKind {
-  switch (kind) {
-    case Action::Kind::ValueExpressionAction:
-    case Action::Kind::ExpressionAction:
-    case Action::Kind::WitnessAction:
-    case Action::Kind::LocationAction:
-    case Action::Kind::TypeInstantiationAction:
-      return FinishActionKind::Value;
-    case Action::Kind::StatementAction:
-    case Action::Kind::DeclarationAction:
-    case Action::Kind::RecursiveAction:
-      return FinishActionKind::NoValue;
-    case Action::Kind::ScopeAction:
-    case Action::Kind::CleanUpAction:
-    case Action::Kind::DestroyAction:
-      return FinishActionKind::NeverCalled;
-  }
-}
-
-auto ActionStack::FinishAction() -> ErrorOr<Success> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy;
-  std::unique_ptr<Action> act = Pop();
-  switch (FinishActionKindFor(act->kind())) {
-    case FinishActionKind::Value:
-      CARBON_FATAL("This kind of action must produce a result: {0}", *act);
-    case FinishActionKind::NeverCalled:
-      CARBON_FATAL("Should not call FinishAction for: {0}", *act);
-    case FinishActionKind::NoValue:
-      PopScopes(scopes_to_destroy);
-      break;
-  }
-  PushCleanUpAction(std::move(act));
-  PushCleanUpActions(std::move(scopes_to_destroy));
-  return Success();
-}
-
-auto ActionStack::FinishAction(Nonnull<const Value*> result)
-    -> ErrorOr<Success> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy;
-  std::unique_ptr<Action> act = Pop();
-  switch (FinishActionKindFor(act->kind())) {
-    case FinishActionKind::NoValue:
-      CARBON_FATAL("This kind of action cannot produce results: {0}", *act);
-    case FinishActionKind::NeverCalled:
-      CARBON_FATAL("Should not call FinishAction for: {0}", *act);
-    case FinishActionKind::Value:
-      PopScopes(scopes_to_destroy);
-      SetResult(result);
-      break;
-  }
-  PushCleanUpAction(std::move(act));
-  PushCleanUpActions(std::move(scopes_to_destroy));
-  return Success();
-}
-
-auto ActionStack::Spawn(std::unique_ptr<Action> child) -> ErrorOr<Success> {
-  Action& action = *todo_.Top();
-  action.set_pos(action.pos() + 1);
-  Push(std::move(child));
-  return Success();
-}
-
-auto ActionStack::Spawn(std::unique_ptr<Action> child, RuntimeScope scope)
-    -> ErrorOr<Success> {
-  Action& action = *todo_.Top();
-  action.set_pos(action.pos() + 1);
-  Push(std::make_unique<ScopeAction>(std::move(scope)));
-  Push(std::move(child));
-  return Success();
-}
-
-auto ActionStack::ReplaceWith(std::unique_ptr<Action> replacement)
-    -> ErrorOr<Success> {
-  std::unique_ptr<Action> old = Pop();
-  CARBON_CHECK(FinishActionKindFor(old->kind()) ==
-                   FinishActionKindFor(replacement->kind()),
-               "Can't replace action {0} with {1}", *old, *replacement);
-  Push(std::move(replacement));
-  return Success();
-}
-
-auto ActionStack::RunAgain() -> ErrorOr<Success> {
-  Action& action = *todo_.Top();
-  action.set_pos(action.pos() + 1);
-  return Success();
-}
-
-auto ActionStack::UnwindToWithCaptureScopesToDestroy(
-    Nonnull<const Statement*> ast_node) -> std::stack<std::unique_ptr<Action>> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy;
-  while (true) {
-    if (const auto* statement_action =
-            llvm::dyn_cast<StatementAction>(todo_.Top().get());
-        statement_action != nullptr &&
-        &statement_action->statement() == ast_node) {
-      break;
-    }
-    auto item = Pop();
-    auto& scope = item->scope();
-    if (scope && item->kind() != Action::Kind::CleanUpAction) {
-      std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
-          std::move(*scope), ast_node->source_loc());
-      scopes_to_destroy.push(std::move(cleanup_action));
-    }
-  }
-  return scopes_to_destroy;
-}
-
-auto ActionStack::UnwindTo(Nonnull<const Statement*> ast_node)
-    -> ErrorOr<Success> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy =
-      UnwindToWithCaptureScopesToDestroy(ast_node);
-  PushCleanUpActions(std::move(scopes_to_destroy));
-  return Success();
-}
-
-auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node)
-    -> ErrorOr<Success> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy =
-      UnwindPastWithCaptureScopesToDestroy(ast_node);
-  PushCleanUpActions(std::move(scopes_to_destroy));
-
-  return Success();
-}
-
-auto ActionStack::UnwindPastWithCaptureScopesToDestroy(
-    Nonnull<const Statement*> ast_node) -> std::stack<std::unique_ptr<Action>> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy =
-      UnwindToWithCaptureScopesToDestroy(ast_node);
-  auto item = Pop();
-  scopes_to_destroy.push(std::move(item));
-  PopScopes(scopes_to_destroy);
-  return scopes_to_destroy;
-}
-
-auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node,
-                             Nonnull<const Value*> result) -> ErrorOr<Success> {
-  std::stack<std::unique_ptr<Action>> scopes_to_destroy =
-      UnwindPastWithCaptureScopesToDestroy(ast_node);
-  SetResult(result);
-  PushCleanUpActions(std::move(scopes_to_destroy));
-  return Success();
-}
-
-void ActionStack::PopScopes(
-    std::stack<std::unique_ptr<Action>>& cleanup_stack) {
-  while (!todo_.empty() && llvm::isa<ScopeAction>(*todo_.Top())) {
-    auto act = Pop();
-    if (act->scope()) {
-      cleanup_stack.push(std::move(act));
-    }
-  }
-}
-
-void ActionStack::SetResult(Nonnull<const Value*> result) {
-  if (todo_.empty()) {
-    result_ = result;
-  } else {
-    todo_.Top()->AddResult(result);
-  }
-}
-
-void ActionStack::PushCleanUpActions(
-    std::stack<std::unique_ptr<Action>> actions) {
-  while (!actions.empty()) {
-    auto& act = actions.top();
-    if (act->scope()) {
-      // TODO: Provide a real source location.
-      std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
-          std::move(*act->scope()),
-          SourceLocation("stack cleanup", 1, FileKind::Unknown));
-      Push(std::move(cleanup_action));
-    }
-    actions.pop();
-  }
-}
-
-void ActionStack::PushCleanUpAction(std::unique_ptr<Action> act) {
-  auto& scope = act->scope();
-  if (scope && act->kind() != Action::Kind::CleanUpAction) {
-    // TODO: Provide a real source location.
-    std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
-        std::move(*scope),
-        SourceLocation("stack cleanup", 1, FileKind::Unknown));
-    Push(std::move(cleanup_action));
-  }
-}
-
-}  // namespace Carbon

+ 0 - 163
explorer/interpreter/action_stack.h

@@ -1,163 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_
-#define CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_
-
-#include <memory>
-#include <optional>
-#include <stack>
-
-#include "common/ostream.h"
-#include "explorer/ast/statement.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/action.h"
-
-namespace Carbon {
-
-// Selects between compile-time and run-time behavior.
-enum class Phase { CompileTime, RunTime };
-
-// The stack of Actions currently being executed by the interpreter.
-class ActionStack : public Printable<ActionStack> {
- public:
-  // Constructs an empty compile-time ActionStack.
-  explicit ActionStack(Nonnull<TraceStream*> trace_stream)
-      : phase_(Phase::CompileTime), trace_stream_(trace_stream) {}
-
-  // Constructs an empty run-time ActionStack that allocates global variables
-  // on `heap`.
-  explicit ActionStack(Nonnull<TraceStream*> trace_stream,
-                       Nonnull<HeapAllocationInterface*> heap)
-      : globals_(RuntimeScope(heap)),
-        phase_(Phase::RunTime),
-        trace_stream_(trace_stream) {}
-
-  void Print(llvm::raw_ostream& out) const;
-
-  // Starts execution with `action` at the top of the stack. Cannot be called
-  // when IsEmpty() is false.
-  void Start(std::unique_ptr<Action> action);
-
-  // True if the stack is empty.
-  auto empty() const -> bool { return todo_.empty(); }
-
-  // The Action currently at the top of the stack. This will never be a
-  // ScopeAction.
-  auto CurrentAction() -> Action& { return *todo_.Top(); }
-
-  // Allocates storage for `value_node`, and initializes it to `value`.
-  void Initialize(ValueNodeView value_node, Nonnull<const Value*> value);
-
-  // Returns the value bound to `value_node`. If `value_node` is a local
-  // variable, this will be an LocationValue.
-  auto ValueOfNode(ValueNodeView value_node, SourceLocation source_loc) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Merges `scope` into the innermost scope currently on the stack.
-  void MergeScope(RuntimeScope scope);
-
-  // The result produced by the `action` argument of the most recent
-  // Start call. Cannot be called if IsEmpty() is false, or if `action`
-  // was an action that doesn't produce results.
-  auto result() const -> Nonnull<const Value*> { return *result_; }
-
-  // The following methods, called "transition methods", update the state of
-  // the ActionStack and/or the current Action to reflect the effects of
-  // executing a step of that Action. Execution of an Action step should always
-  // invoke exactly one transition method, as the very last operation. This is a
-  // matter of safety as well as convention: most transition methods modify the
-  // state of the current action, and some of them destroy it. To help enforce
-  // this requirement, we have a convention of making these methods return an
-  // ErrorOr<Success> even when a method can't actually fail, and calling the
-  // methods as part of return statements, e.g. `return todo_.FinishAction()`.
-
-  // Finishes execution of the current Action. If `result` is specified, it
-  // represents the result of that Action.
-  auto FinishAction() -> ErrorOr<Success>;
-  auto FinishAction(Nonnull<const Value*> result) -> ErrorOr<Success>;
-
-  // Advances the current action one step, and push `child` onto the stack.
-  // If `scope` is specified, `child` will be executed in that scope.
-  auto Spawn(std::unique_ptr<Action> child) -> ErrorOr<Success>;
-  auto Spawn(std::unique_ptr<Action> child, RuntimeScope scope)
-      -> ErrorOr<Success>;
-  // Replace the current action with another action that produces the same kind
-  // of result and run it next.
-  auto ReplaceWith(std::unique_ptr<Action> replacement) -> ErrorOr<Success>;
-
-  // Start a new recursive action.
-  auto BeginRecursiveAction() {
-    todo_.Push(std::make_unique<RecursiveAction>());
-  }
-
-  // Advances the current action one step.
-  auto RunAgain() -> ErrorOr<Success>;
-
-  // Unwinds Actions from the stack until the StatementAction associated with
-  // `ast_node` is at the top of the stack.
-  auto UnwindTo(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
-
-  // Unwinds Actions from the stack until the StatementAction associated with
-  // `ast_node` has been removed from the stack. If `result` is specified,
-  // it represents the result of that Action (StatementActions normally cannot
-  // produce results, but the body of a function can).
-  auto UnwindPast(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
-  auto UnwindPast(Nonnull<const Statement*> ast_node,
-                  Nonnull<const Value*> result) -> ErrorOr<Success>;
-
-  auto Pop() -> std::unique_ptr<Action> {
-    auto popped_action = todo_.Pop();
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Pop() << "stack-pop:  " << *popped_action << " ("
-                           << popped_action->source_loc() << ")\n";
-    }
-    return popped_action;
-  }
-
-  void Push(std::unique_ptr<Action> action) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Push()
-          << "stack-push: " << *action << " (" << action->source_loc() << ")\n";
-    }
-    todo_.Push(std::move(action));
-  }
-
-  auto size() const -> int { return todo_.size(); }
-
- private:
-  // Pop any ScopeActions from the top of the stack, propagating results as
-  // needed, to restore the invariant that todo_.Top() is not a ScopeAction.
-  // Store the popped scope action into cleanup_stack, so that the destructor
-  // can be called for the variables
-  void PopScopes(std::stack<std::unique_ptr<Action>>& cleanup_stack);
-
-  // Set `result` as the result of the Action most recently removed from the
-  // stack.
-  void SetResult(Nonnull<const Value*> result);
-
-  auto UnwindToWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
-      -> std::stack<std::unique_ptr<Action>>;
-
-  auto UnwindPastWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
-      -> std::stack<std::unique_ptr<Action>>;
-
-  // Create CleanUpActions for all actions
-  void PushCleanUpActions(std::stack<std::unique_ptr<Action>> actions);
-
-  // Create and push a CleanUpAction on the stack
-  void PushCleanUpAction(std::unique_ptr<Action> act);
-
-  // TODO: consider defining a non-nullable unique_ptr-like type to use here.
-  Stack<std::unique_ptr<Action>> todo_;
-  std::optional<Nonnull<const Value*>> result_;
-  std::optional<RuntimeScope> globals_;
-  Phase phase_;
-  Nonnull<TraceStream*> trace_stream_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_

+ 0 - 49
explorer/interpreter/builtins.cpp

@@ -1,49 +0,0 @@
-// 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 "explorer/interpreter/builtins.h"
-
-#include "explorer/base/error_builders.h"
-
-using llvm::dyn_cast;
-
-namespace Carbon {
-
-CARBON_DEFINE_ENUM_CLASS_NAMES(Builtin) = {
-#define CARBON_BUILTIN(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
-#include "explorer/interpreter/builtins.def"
-};
-
-void Builtins::Register(Nonnull<const Declaration*> decl) {
-  if (const auto* interface = dyn_cast<InterfaceDeclaration>(decl)) {
-    if (interface->name().is_qualified()) {
-      return;
-    }
-
-    static std::map<std::string, int, std::less<>>* builtin_indexes = [] {
-      std::map<std::string, int, std::less<>> builtin_indexes;
-      for (int index = 0; index < Builtin::NumBuiltins; ++index) {
-        builtin_indexes.emplace(Builtin::FromInt(index).name(), index);
-      }
-      return new auto(std::move(builtin_indexes));
-    }();
-
-    auto it = builtin_indexes->find(interface->name().inner_name());
-    if (it != builtin_indexes->end()) {
-      builtins_[it->second] = interface;
-    }
-  }
-}
-
-auto Builtins::Get(SourceLocation source_loc, Builtin builtin) const
-    -> ErrorOr<Nonnull<const Declaration*>> {
-  std::optional<const Declaration*> result = builtins_[builtin.AsInt()];
-  if (!result.has_value()) {
-    return ProgramError(source_loc)
-           << "missing declaration for builtin `" << builtin << "`";
-  }
-  return result.value();
-}
-
-}  // namespace Carbon

+ 0 - 68
explorer/interpreter/builtins.def

@@ -1,68 +0,0 @@
-// 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 X-macro header. It does not use `#include` guards, and instead is
-// designed to be `#include`ed after the x-macro is defined in order for its
-// inclusion to expand to the desired output. Macro definitions are cleaned up
-// at the end of this file.
-//
-// Supported x-macros are:
-// - CARBON_BUILTIN(Name)
-//   Defines a builtin.
-
-#ifndef CARBON_BUILTIN
-#error "Must define the x-macro to use this file."
-#endif
-
-// Conversions.
-CARBON_BUILTIN(As)
-CARBON_BUILTIN(ImplicitAs)
-
-// Comparison.
-CARBON_BUILTIN(EqWith)
-CARBON_BUILTIN(LessWith)
-CARBON_BUILTIN(LessEqWith)
-CARBON_BUILTIN(GreaterWith)
-CARBON_BUILTIN(GreaterEqWith)
-CARBON_BUILTIN(CompareWith)
-
-// Arithmetic.
-CARBON_BUILTIN(Negate)
-CARBON_BUILTIN(AddWith)
-CARBON_BUILTIN(SubWith)
-CARBON_BUILTIN(MulWith)
-CARBON_BUILTIN(DivWith)
-CARBON_BUILTIN(ModWith)
-
-// Bitwise and shift.
-CARBON_BUILTIN(BitComplement)
-CARBON_BUILTIN(BitAndWith)
-CARBON_BUILTIN(BitOrWith)
-CARBON_BUILTIN(BitXorWith)
-CARBON_BUILTIN(LeftShiftWith)
-CARBON_BUILTIN(RightShiftWith)
-
-// Simple assignment.
-CARBON_BUILTIN(AssignWith)
-
-// Compound assignment.
-CARBON_BUILTIN(AddAssignWith)
-CARBON_BUILTIN(SubAssignWith)
-CARBON_BUILTIN(MulAssignWith)
-CARBON_BUILTIN(DivAssignWith)
-CARBON_BUILTIN(ModAssignWith)
-CARBON_BUILTIN(BitAndAssignWith)
-CARBON_BUILTIN(BitOrAssignWith)
-CARBON_BUILTIN(BitXorAssignWith)
-CARBON_BUILTIN(LeftShiftAssignWith)
-CARBON_BUILTIN(RightShiftAssignWith)
-
-// Increment and decrement.
-CARBON_BUILTIN(Inc)
-CARBON_BUILTIN(Dec)
-
-// Last for the number of builtins.
-CARBON_BUILTIN(Invalid)
-
-#undef CARBON_BUILTIN

+ 0 - 62
explorer/interpreter/builtins.h

@@ -1,62 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_BUILTINS_H_
-#define CARBON_EXPLORER_INTERPRETER_BUILTINS_H_
-
-#include <array>
-#include <optional>
-#include <string_view>
-
-#include "common/enum_base.h"
-#include "common/error.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-CARBON_DEFINE_RAW_ENUM_CLASS(Builtin, int) {
-#define CARBON_BUILTIN(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
-#include "explorer/interpreter/builtins.def"
-};
-
-class Builtin : public CARBON_ENUM_BASE(Builtin) {
- public:
-#define CARBON_BUILTIN(Name) CARBON_ENUM_CONSTANT_DECL(Name)
-#include "explorer/interpreter/builtins.def"
-
-  static const int NumBuiltins;
-
-  // Support conversion to and from an int for array indexing.
-  using EnumBase::AsInt;
-  using EnumBase::FromInt;
-};
-
-#define CARBON_BUILTIN(Name) CARBON_ENUM_CONSTANT_DEFINITION(Builtin, Name)
-#include "explorer/interpreter/builtins.def"
-
-constexpr int Builtin::NumBuiltins = Invalid.AsInt();
-
-class Builtins {
- public:
-  explicit Builtins() = default;
-
-  // Register a declaration that might be a builtin.
-  void Register(Nonnull<const Declaration*> decl);
-
-  // Get a registered builtin.
-  auto Get(SourceLocation source_loc, Builtin builtin) const
-      -> ErrorOr<Nonnull<const Declaration*>>;
-
- private:
-  std::optional<Nonnull<const Declaration*>> builtins_[Builtin::NumBuiltins] =
-      {};
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_BUILTINS_H_

+ 0 - 104
explorer/interpreter/dictionary.h

@@ -1,104 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_DICTIONARY_H_
-#define CARBON_EXPLORER_INTERPRETER_DICTIONARY_H_
-
-#include <iterator>
-#include <optional>
-
-#include "explorer/base/arena.h"
-
-namespace Carbon {
-
-// A persistent dictionary with a simple implementation.
-// Copying the dictionary is O(1) time.
-template <class K, class V>
-class Dictionary {
- public:
-  struct Node {
-    using ValueType = std::pair<K, V>;
-
-    Node(ValueType e, std::optional<Nonnull<Node*>> n) : curr(e), next(n) {}
-
-    const ValueType curr;
-    const std::optional<Nonnull<Node*>> next;
-
-    // Node cells are part of a "persistent data structure" and are thus
-    // immutable.
-    auto operator=(const Node&) -> Node& = delete;
-    auto operator=(Node&&) -> Node& = delete;
-  };
-
-  // A forward iterator over elements of a `Node` list.
-  struct Iterator {
-    // NOLINTNEXTLINE(readability-identifier-naming)
-    using value_type = typename Node::ValueType;
-    // NOLINTNEXTLINE(readability-identifier-naming)
-    using difference_type = std::ptrdiff_t;
-    // NOLINTNEXTLINE(readability-identifier-naming)
-    using pointer = const value_type*;
-    // NOLINTNEXTLINE(readability-identifier-naming)
-    using reference = const value_type&;
-    // NOLINTNEXTLINE(readability-identifier-naming)
-    using iterator_category = std::forward_iterator_tag;
-
-    explicit Iterator(std::optional<Nonnull<Node*>> x) : p_(x) {}
-    Iterator(const Iterator& iter) : p_(iter.p_) {}
-    auto operator++() -> Iterator& {
-      p_ = (*p_)->next;
-      return *this;
-    }
-    auto operator++(int) -> Iterator {
-      Iterator tmp(*this);
-      operator++();
-      return tmp;
-    }
-    auto operator==(const Iterator& rhs) const -> bool { return p_ == rhs.p_; }
-    auto operator!=(const Iterator& rhs) const -> bool { return p_ != rhs.p_; }
-    auto operator*() -> const value_type& { return (*p_)->curr; }
-    auto operator->() -> const value_type* { return &(*p_)->curr; }
-
-   private:
-    std::optional<Nonnull<Node*>> p_;
-  };
-
-  // Create an empty dictionary.
-  explicit Dictionary(Nonnull<Arena*> arena) : arena_(arena) {}
-
-  // Return the value associated with the given key.
-  // Time complexity: O(n) where n is the number of times
-  //    any value has been set across all keys.
-  auto Get(const K& key) -> std::optional<V> {
-    for (auto kv : *this) {
-      if (kv.first == key) {
-        return kv.second;
-      }
-    }
-    return std::nullopt;
-  }
-
-  // Associate the value v with key k in the dictionary.
-  // Time complexity: O(1).
-  auto Set(const K& k, const V& v) -> void {
-    head_ = arena_->New<Node>(std::make_pair(k, v), head_);
-  }
-
-  auto IsEmpty() -> bool { return !head_; }
-
-  // The position of the first element of the dictionary
-  // or `end()` if the dictionary is empty.
-  auto begin() const -> Iterator { return Iterator(head_); }
-
-  // The position one past that of the last element.
-  auto end() const -> Iterator { return Iterator(std::nullopt); }
-
- private:
-  std::optional<Nonnull<Node*>> head_;
-  Nonnull<Arena*> arena_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_DICTIONARY_H_

+ 0 - 109
explorer/interpreter/exec_program.cpp

@@ -1,109 +0,0 @@
-// 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 "explorer/interpreter/exec_program.h"
-
-#include <variant>
-
-#include "common/check.h"
-#include "common/error.h"
-#include "common/ostream.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/interpreter.h"
-#include "explorer/interpreter/resolve_control_flow.h"
-#include "explorer/interpreter/resolve_names.h"
-#include "explorer/interpreter/resolve_unformed.h"
-#include "explorer/interpreter/type_checker.h"
-#include "llvm/Support/Error.h"
-
-namespace Carbon {
-
-auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
-                    Nonnull<TraceStream*> trace_stream,
-                    Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<AST> {
-  SetProgramPhase set_prog_phase(*trace_stream, ProgramPhase::SourceProgram);
-  SetFileContext set_file_ctx(*trace_stream, std::nullopt);
-
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("source program");
-    llvm::ListSeparator sep("\n\n");
-    for (auto& declaration : ast.declarations) {
-      set_file_ctx.update_source_loc(declaration->source_loc());
-      if (trace_stream->is_enabled()) {
-        *trace_stream << sep << *declaration;
-      }
-    }
-    if (trace_stream->is_enabled()) {
-      *trace_stream << "\n";
-    }
-  }
-
-  SourceLocation source_loc("<Main()>", 0, FileKind::Main);
-  ast.main_call = arena->New<CallExpression>(
-      source_loc, arena->New<IdentifierExpression>(source_loc, "Main"),
-      arena->New<TupleLiteral>(source_loc));
-
-  // Although name resolution is currently done once, generic programming
-  // (particularly templates) may require more passes.
-  set_prog_phase.update_phase(ProgramPhase::NameResolution);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("resolving names");
-  }
-  CARBON_RETURN_IF_ERROR(ResolveNames(ast, trace_stream));
-
-  set_prog_phase.update_phase(ProgramPhase::ControlFlowResolution);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("resolving control flow");
-  }
-  CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, ast));
-
-  set_prog_phase.update_phase(ProgramPhase::TypeChecking);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("type checking");
-  }
-  CARBON_RETURN_IF_ERROR(
-      TypeChecker(arena, trace_stream, print_stream).TypeCheck(ast));
-
-  set_prog_phase.update_phase(ProgramPhase::UnformedVariableResolution);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("resolving unformed variables");
-  }
-  CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, ast));
-
-  set_prog_phase.update_phase(ProgramPhase::Declarations);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("printing declarations");
-    llvm::ListSeparator sep("\n\n");
-    for (auto& declaration : ast.declarations) {
-      set_file_ctx.update_source_loc(declaration->source_loc());
-      if (trace_stream->is_enabled()) {
-        *trace_stream << sep << *declaration;
-      }
-    }
-    if (trace_stream->is_enabled()) {
-      *trace_stream << "\n";
-    }
-  }
-  return ast;
-}
-
-auto ExecProgram(Nonnull<Arena*> arena, AST ast,
-                 Nonnull<TraceStream*> trace_stream,
-                 Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
-  SetProgramPhase set_program_phase(*trace_stream, ProgramPhase::Execution);
-  if (trace_stream->is_enabled()) {
-    trace_stream->Heading("starting execution");
-  }
-  CARBON_ASSIGN_OR_RETURN(
-      auto interpreter_result,
-      InterpProgram(ast, arena, trace_stream, print_stream));
-  if (trace_stream->is_enabled()) {
-    trace_stream->Result() << "interpreter result: " << interpreter_result
-                           << "\n";
-  }
-  return interpreter_result;
-}
-
-}  // namespace Carbon

+ 0 - 30
explorer/interpreter/exec_program.h

@@ -1,30 +0,0 @@
-// 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
-
-// Helpers should be added here when logic in syntax.ypp is more than a single
-// statement. The intent is to minimize the amount of C++ in the .ypp file, to
-// improve ease of maintenance.
-
-#ifndef CARBON_EXPLORER_INTERPRETER_EXEC_PROGRAM_H_
-#define CARBON_EXPLORER_INTERPRETER_EXEC_PROGRAM_H_
-
-#include "explorer/ast/ast.h"
-#include "explorer/base/trace_stream.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace Carbon {
-
-// Perform semantic analysis on the AST.
-auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
-                    Nonnull<TraceStream*> trace_stream,
-                    Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<AST>;
-
-// Run the program's `Main` function.
-auto ExecProgram(Nonnull<Arena*> arena, AST ast,
-                 Nonnull<TraceStream*> trace_stream,
-                 Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_EXEC_PROGRAM_H_

+ 0 - 209
explorer/interpreter/heap.cpp

@@ -1,209 +0,0 @@
-// 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 "explorer/interpreter/heap.h"
-
-#include "common/check.h"
-#include "common/error.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/source_location.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Error.h"
-
-namespace Carbon {
-
-auto Heap::AllocateValue(Nonnull<const Value*> v) -> AllocationId {
-  // Putting the following two side effects together in this function
-  // ensures that we don't do anything else in between, which would be really
-  // bad! Consider whether to include a copy of the input v in this function or
-  // to leave it up to the caller.
-  AllocationId a(values_.size());
-  values_.push_back(v);
-  bool is_uninitialized = false;
-
-  if (v->kind() == Carbon::Value::Kind::UninitializedValue) {
-    states_.push_back(ValueState::Uninitialized);
-    is_uninitialized = true;
-  } else {
-    states_.push_back(ValueState::Alive);
-  }
-  bound_values_.push_back(llvm::DenseMap<const AstNode*, Address>{});
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Allocate()
-        << "memory-alloc: #" << a.index_ << " `" << *v << "`"
-        << (is_uninitialized ? " uninitialized" : "") << "\n";
-  }
-
-  return a;
-}
-
-auto Heap::Read(const Address& a, SourceLocation source_loc) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_RETURN_IF_ERROR(this->CheckInit(a.allocation_, source_loc));
-  CARBON_RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
-  Nonnull<const Value*> value = values_[a.allocation_.index_];
-  ErrorOr<Nonnull<const Value*>> read_value =
-      value->GetElement(arena_, a.element_path_, source_loc, value);
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Read() << "memory-read: #" << a.allocation_.index_ << " `"
-                          << **read_value << "`\n";
-  }
-
-  return read_value;
-}
-
-auto Heap::Write(const Address& a, Nonnull<const Value*> v,
-                 SourceLocation source_loc) -> ErrorOr<Success> {
-  CARBON_RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
-  if (states_[a.allocation_.index_] == ValueState::Uninitialized) {
-    if (!a.element_path_.IsEmpty()) {
-      return ProgramError(source_loc)
-             << "undefined behavior: store to subobject of uninitialized value "
-             << *values_[a.allocation_.index_];
-    }
-    states_[a.allocation_.index_] = ValueState::Alive;
-  }
-  CARBON_ASSIGN_OR_RETURN(values_[a.allocation_.index_],
-                          values_[a.allocation_.index_]->SetField(
-                              arena_, a.element_path_, v, source_loc));
-  auto& bound_values_map = bound_values_[a.allocation_.index_];
-  // End lifetime of all values bound to this address and its subobjects.
-  if (a.element_path_.IsEmpty()) {
-    bound_values_map.clear();
-  } else {
-    for (auto value_it = bound_values_map.begin();
-         value_it != bound_values_map.end(); ++value_it) {
-      if (AddressesAreStrictlyNested(a, value_it->second)) {
-        bound_values_map.erase(value_it);
-      }
-    }
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Write() << "memory-write: #" << a.allocation_.index_ << " `"
-                           << *values_[a.allocation_.index_] << "`\n";
-  }
-
-  return Success();
-}
-
-auto Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) const
-    -> ErrorOr<Success> {
-  const auto state = states_[allocation.index_];
-  if (state == ValueState::Dead || state == ValueState::Discarded) {
-    return ProgramError(source_loc)
-           << "undefined behavior: access to dead or discarded value "
-           << *values_[allocation.index_];
-  }
-  return Success();
-}
-
-auto Heap::CheckInit(AllocationId allocation, SourceLocation source_loc) const
-    -> ErrorOr<Success> {
-  if (states_[allocation.index_] == ValueState::Uninitialized) {
-    return ProgramError(source_loc)
-           << "undefined behavior: access to uninitialized value "
-           << *values_[allocation.index_];
-  }
-  return Success();
-}
-
-auto Heap::Deallocate(AllocationId allocation) -> ErrorOr<Success> {
-  if (states_[allocation.index_] != ValueState::Dead) {
-    states_[allocation.index_] = ValueState::Dead;
-  } else {
-    CARBON_FATAL("deallocating an already dead value: {0}",
-                 *values_[allocation.index_]);
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Deallocate() << "memory-dealloc: #" << allocation.index_
-                                << " `" << *values_[allocation.index_] << "`\n";
-  }
-
-  return Success();
-}
-
-auto Heap::Deallocate(const Address& a) -> ErrorOr<Success> {
-  return Deallocate(a.allocation_);
-}
-
-auto Heap::is_initialized(AllocationId allocation) const -> bool {
-  return states_[allocation.index_] != ValueState::Uninitialized;
-}
-
-auto Heap::is_discarded(AllocationId allocation) const -> bool {
-  return states_[allocation.index_] == ValueState::Discarded;
-}
-
-void Heap::Discard(AllocationId allocation) {
-  CARBON_CHECK(states_[allocation.index_] == ValueState::Uninitialized);
-  states_[allocation.index_] = ValueState::Discarded;
-}
-
-void Heap::BindValueToReference(const ValueNodeView& node, const Address& a) {
-  // Update mapped node ignoring any previous mapping.
-  bound_values_[a.allocation_.index_].insert({&node.base(), a});
-}
-
-auto Heap::is_bound_value_alive(const ValueNodeView& node,
-                                const Address& a) const -> bool {
-  return bound_values_[a.allocation_.index_].contains(&node.base());
-}
-
-void Heap::Print(llvm::raw_ostream& out) const {
-  llvm::ListSeparator sep;
-  for (size_t i = 0; i < values_.size(); ++i) {
-    out << sep;
-    out << i << ": ";
-    if (states_[i] == ValueState::Uninitialized) {
-      out << "!";
-    } else if (states_[i] == ValueState::Dead) {
-      out << "!!";
-    }
-    out << *values_[i];
-  }
-}
-
-auto Heap::AddressesAreStrictlyNested(const Address& first,
-                                      const Address& second) -> bool {
-  if (first.allocation_.index_ != second.allocation_.index_) {
-    return false;
-  }
-  return PathsAreStrictlyNested(first.element_path_, second.element_path_);
-}
-
-auto Heap::PathsAreStrictlyNested(const ElementPath& first,
-                                  const ElementPath& second) -> bool {
-  for (size_t i = 0;
-       i < std::min(first.components_.size(), second.components_.size()); ++i) {
-    Nonnull<const Element*> element = first.components_[i].element();
-    Nonnull<const Element*> other_element = second.components_[i].element();
-    if (element->kind() != other_element->kind()) {
-      return false;
-    }
-    switch (element->kind()) {
-      case Carbon::ElementKind::NamedElement:
-        if (!element->IsNamed(
-                llvm::cast<NamedElement>(other_element)->name())) {
-          return false;
-        }
-        break;
-      case Carbon::ElementKind::PositionalElement:
-        if (llvm::cast<PositionalElement>(element)->index() !=
-            llvm::cast<PositionalElement>(other_element)->index()) {
-          return false;
-        }
-        break;
-      case Carbon::ElementKind::BaseElement:
-        // Nothing to test.
-        break;
-    }
-  }
-  return true;
-}
-}  // namespace Carbon

+ 0 - 104
explorer/interpreter/heap.h

@@ -1,104 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_HEAP_H_
-#define CARBON_EXPLORER_INTERPRETER_HEAP_H_
-
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/ast/address.h"
-#include "explorer/ast/value.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/heap_allocation_interface.h"
-
-namespace Carbon {
-
-// A Heap represents the abstract machine's dynamically allocated memory.
-class Heap : public HeapAllocationInterface, public Printable<Heap> {
- public:
-  enum class ValueState {
-    Uninitialized,
-    Discarded,
-    Alive,
-    Dead,
-  };
-
-  // Constructs an empty Heap.
-  explicit Heap(Nonnull<TraceStream*> trace_stream, Nonnull<Arena*> arena)
-      : arena_(arena), trace_stream_(trace_stream) {}
-
-  Heap(const Heap&) = delete;
-  auto operator=(const Heap&) -> Heap& = delete;
-
-  // Returns the value at the given address in the heap after
-  // checking that it is alive.
-  auto Read(const Address& a, SourceLocation source_loc) const
-      -> ErrorOr<Nonnull<const Value*>> override;
-
-  // Writes the given value at the address in the heap after
-  // checking that the address is alive.
-  auto Write(const Address& a, Nonnull<const Value*> v,
-             SourceLocation source_loc) -> ErrorOr<Success> override;
-
-  // Returns whether the value bound at the given node is still alive.
-  auto is_bound_value_alive(const ValueNodeView& node, const Address& a) const
-      -> bool override;
-
-  void BindValueToReference(const ValueNodeView& node,
-                            const Address& a) override;
-
-  // Put the given value on the heap and mark its state.
-  // Mark UninitializedValue as uninitialized and other values as alive.
-  auto AllocateValue(Nonnull<const Value*> v) -> AllocationId override;
-
-  // Marks this allocation, and all of its sub-objects, as dead.
-  auto Deallocate(AllocationId allocation) -> ErrorOr<Success> override;
-  auto Deallocate(const Address& a) -> ErrorOr<Success>;
-
-  // Marks this allocation, and all its sub-objects, as discarded.
-  void Discard(AllocationId allocation);
-  // Returns whether the given allocation was unused and discarded.
-  auto is_discarded(AllocationId allocation) const -> bool;
-  // Returns whether the given allocation was initialized.
-  auto is_initialized(AllocationId allocation) const -> bool;
-
-  // Print all the values on the heap to the stream `out`.
-  void Print(llvm::raw_ostream& out) const;
-
-  auto arena() const -> Arena& override { return *arena_; }
-
- private:
-  // Returns whether the address have the same AllocationdId and their path
-  // are strictly nested.
-  static auto AddressesAreStrictlyNested(const Address& first,
-                                         const Address& second) -> bool;
-
-  // Returns whether the provided paths are strictly nested. This checks the
-  // name, index, and base element only, and might not valid if used to
-  // compare paths based on a different AllocationId.
-  static auto PathsAreStrictlyNested(const ElementPath& first,
-                                     const ElementPath& second) -> bool;
-
-  // Signal an error if the allocation is no longer alive.
-  auto CheckAlive(AllocationId allocation, SourceLocation source_loc) const
-      -> ErrorOr<Success>;
-
-  // Signal an error if the allocation has not been initialized.
-  auto CheckInit(AllocationId allocation, SourceLocation source_loc) const
-      -> ErrorOr<Success>;
-
-  Nonnull<Arena*> arena_;
-  std::vector<Nonnull<const Value*>> values_;
-  std::vector<ValueState> states_;
-  std::vector<llvm::DenseMap<const AstNode*, Address>> bound_values_;
-  Nonnull<TraceStream*> trace_stream_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_HEAP_H_

+ 0 - 61
explorer/interpreter/heap_allocation_interface.h

@@ -1,61 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_HEAP_ALLOCATION_INTERFACE_H_
-#define CARBON_EXPLORER_INTERPRETER_HEAP_ALLOCATION_INTERFACE_H_
-
-#include "common/error.h"
-#include "explorer/ast/address.h"
-#include "explorer/ast/value_node.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-class Value;
-
-// The allocation interface for Heap, factored out as an interface in order to
-// resolve a layering issue. No other class should derive from this.
-class HeapAllocationInterface {
- public:
-  HeapAllocationInterface(const HeapAllocationInterface&) = delete;
-  auto operator=(const HeapAllocationInterface&)
-      -> HeapAllocationInterface& = delete;
-
-  // Returns the value at the given address in the heap after
-  // checking that it is alive.
-  virtual auto Read(const Address& a, SourceLocation source_loc) const
-      -> ErrorOr<Nonnull<const Value*>> = 0;
-
-  // Writes the given value at the address in the heap after
-  // checking that the address is alive.
-  virtual auto Write(const Address& a, Nonnull<const Value*> v,
-                     SourceLocation source_loc) -> ErrorOr<Success> = 0;
-
-  // Put the given value on the heap and mark it as alive.
-  virtual auto AllocateValue(Nonnull<const Value*> v) -> AllocationId = 0;
-
-  // Marks this allocation, and all of its sub-objects, as dead.
-  virtual auto Deallocate(AllocationId allocation) -> ErrorOr<Success> = 0;
-
-  // Returns the arena used to allocate the values in this heap.
-  virtual auto arena() const -> Arena& = 0;
-
-  // Binds a value node to a reference, and manages its lifetime.
-  virtual void BindValueToReference(const ValueNodeView& node,
-                                    const Address& a) = 0;
-
-  // Returns whether the value bound at the given node is still alive.
-  virtual auto is_bound_value_alive(const ValueNodeView& node,
-                                    const Address& a) const -> bool = 0;
-
- protected:
-  HeapAllocationInterface() = default;
-  virtual ~HeapAllocationInterface() = default;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_HEAP_ALLOCATION_INTERFACE_H_

+ 0 - 445
explorer/interpreter/impl_scope.cpp

@@ -1,445 +0,0 @@
-// 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 "explorer/interpreter/impl_scope.h"
-
-#include "explorer/ast/value.h"
-#include "explorer/interpreter/type_checker.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-
-using llvm::cast;
-using llvm::dyn_cast;
-
-namespace Carbon {
-
-void ImplScope::Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
-                    Nonnull<const Witness*> witness,
-                    const TypeChecker& type_checker) {
-  Add(iface, {}, type, {}, witness, type_checker);
-}
-
-void ImplScope::Add(Nonnull<const Value*> iface,
-                    llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
-                    Nonnull<const Value*> type,
-                    llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-                    Nonnull<const Witness*> witness,
-                    const TypeChecker& type_checker,
-                    std::optional<TypeStructureSortKey> sort_key) {
-  if (const auto* constraint = dyn_cast<ConstraintType>(iface)) {
-    CARBON_CHECK(!sort_key,
-                 "should only be given a sort key for an impl of an interface");
-    // The caller should have substituted `.Self` for `type` already.
-    Add(constraint->impls_constraints(), deduced, impl_bindings, witness,
-        type_checker);
-    // A parameterized impl declaration doesn't contribute any equality
-    // constraints to the scope. Instead, we'll resolve the equality
-    // constraints by resolving a witness when needed.
-    if (deduced.empty()) {
-      for (const auto& equality_constraint :
-           constraint->equality_constraints()) {
-        equalities_.push_back(&equality_constraint);
-      }
-    }
-    return;
-  }
-
-  ImplFact new_impl = {.interface = cast<InterfaceType>(iface),
-                       .deduced = deduced,
-                       .type = type,
-                       .impl_bindings = impl_bindings,
-                       .witness = witness,
-                       .sort_key = std::move(sort_key)};
-
-  // Find the first impl that's more specific than this one, and place this
-  // impl right before it. This keeps the impls with the same type structure
-  // sorted in lexical order, which is important for `match_first` semantics.
-  auto insert_pos =
-      std::upper_bound(impl_facts_.begin(), impl_facts_.end(), new_impl,
-                       [](const ImplFact& a, const ImplFact& b) {
-                         return a.sort_key < b.sort_key;
-                       });
-
-  impl_facts_.insert(insert_pos, std::move(new_impl));
-}
-
-void ImplScope::Add(llvm::ArrayRef<ImplsConstraint> impls_constraints,
-                    llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
-                    llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-                    Nonnull<const Witness*> witness,
-                    const TypeChecker& type_checker) {
-  for (size_t i = 0; i != impls_constraints.size(); ++i) {
-    ImplsConstraint impl = impls_constraints[i];
-    Add(impl.interface, deduced, impl.type, impl_bindings,
-        type_checker.MakeConstraintWitnessAccess(witness, i), type_checker);
-  }
-}
-
-// Diagnose that `a_evaluated != b_evaluated` for the purpose of an equality
-// constraint.
-static auto DiagnoseUnequalValues(SourceLocation source_loc,
-                                  Nonnull<const Value*> a_written,
-                                  Nonnull<const Value*> a_evaluated,
-                                  Nonnull<const Value*> b_written,
-                                  Nonnull<const Value*> b_evaluated,
-                                  Nonnull<const EqualityContext*> equality_ctx)
-    -> Error {
-  CARBON_CHECK(!ValueEqual(a_evaluated, b_evaluated, equality_ctx),
-               "expected unequal values");
-  auto error = ProgramError(source_loc);
-  error << "constraint requires that " << *a_written;
-  if (!ValueEqual(a_written, a_evaluated, std::nullopt)) {
-    error << " (with value " << *a_evaluated << ")";
-  }
-  error << " == " << *b_written;
-  if (!ValueEqual(b_written, b_evaluated, std::nullopt)) {
-    error << " (with value " << *b_evaluated << ")";
-  }
-  error << ", which is not known to be true";
-  return std::move(error);
-}
-
-auto ImplScope::Resolve(Nonnull<const Value*> constraint_type,
-                        Nonnull<const Value*> impl_type,
-                        SourceLocation source_loc,
-                        const TypeChecker& type_checker,
-                        const Bindings& bindings) const
-    -> ErrorOr<Nonnull<const Witness*>> {
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<Nonnull<const Witness*>> witness,
-      TryResolve(constraint_type, impl_type, source_loc, type_checker, bindings,
-                 /*diagnose_missing_impl=*/true));
-  CARBON_CHECK(witness, "should have diagnosed missing impl");
-  return *witness;
-}
-
-auto ImplScope::TryResolve(Nonnull<const Value*> constraint_type,
-                           Nonnull<const Value*> impl_type,
-                           SourceLocation source_loc,
-                           const TypeChecker& type_checker,
-                           const Bindings& bindings,
-                           bool diagnose_missing_impl) const
-    -> ErrorOr<std::optional<Nonnull<const Witness*>>> {
-  if (const auto* iface_type = dyn_cast<InterfaceType>(constraint_type)) {
-    CARBON_ASSIGN_OR_RETURN(
-        iface_type,
-        type_checker.SubstituteCast<InterfaceType>(bindings, iface_type));
-    return TryResolveInterface(iface_type, impl_type, source_loc, type_checker,
-                               diagnose_missing_impl);
-  }
-  if (const auto* constraint = dyn_cast<ConstraintType>(constraint_type)) {
-    std::vector<Nonnull<const Witness*>> witnesses;
-    for (auto impl : constraint->impls_constraints()) {
-      // Note that later impls constraints can refer to earlier impls
-      // constraints via impl bindings. For example, in
-      //   `C where .Self.AssocType impls D`,
-      // ... the `.Self.AssocType impls D` constraint refers to the
-      // `.Self impls C` constraint when naming `AssocType`. So incrementally
-      // build up a partial constraint witness as we go.
-      std::optional<Nonnull<const Witness*>> witness;
-      if (constraint->self_binding()->impl_binding()) {
-        // Note, this is a partial impl binding covering only the impl
-        // constraints that we've already seen. Earlier impls constraints should
-        // not be able to refer to impl bindings for later impls constraints.
-        witness = type_checker.MakeConstraintWitness(witnesses);
-      }
-      Bindings local_bindings = bindings;
-      local_bindings.Add(constraint->self_binding(), impl_type, witness);
-
-      CARBON_ASSIGN_OR_RETURN(const auto* subst_interface,
-                              type_checker.SubstituteCast<InterfaceType>(
-                                  local_bindings, impl.interface));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> subst_type,
-          type_checker.Substitute(local_bindings, impl.type));
-      CARBON_ASSIGN_OR_RETURN(
-          std::optional<Nonnull<const Witness*>> result,
-          TryResolveInterface(subst_interface, subst_type, source_loc,
-                              type_checker, diagnose_missing_impl));
-      if (!result) {
-        return {std::nullopt};
-      }
-      witnesses.push_back(*result);
-    }
-
-    // Check that all intrinsic, equality, and rewrite constraints
-    // are satisfied in this scope.
-    llvm::ArrayRef<IntrinsicConstraint> intrinsics =
-        constraint->intrinsic_constraints();
-    llvm::ArrayRef<EqualityConstraint> equals =
-        constraint->equality_constraints();
-    llvm::ArrayRef<RewriteConstraint> rewrites =
-        constraint->rewrite_constraints();
-    if (!intrinsics.empty() || !equals.empty() || !rewrites.empty()) {
-      std::optional<Nonnull<const Witness*>> witness;
-      if (constraint->self_binding()->impl_binding()) {
-        witness = type_checker.MakeConstraintWitness(witnesses);
-      }
-      Bindings local_bindings = bindings;
-      local_bindings.Add(constraint->self_binding(), impl_type, witness);
-      SingleStepEqualityContext equality_ctx(this);
-      for (const auto& intrinsic : intrinsics) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> type,
-            type_checker.Substitute(local_bindings, intrinsic.type));
-        IntrinsicConstraint converted(type, intrinsic.kind, {});
-        converted.arguments.reserve(intrinsic.arguments.size());
-        for (Nonnull<const Value*> argument : intrinsic.arguments) {
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> subst_arg,
-              type_checker.Substitute(local_bindings, argument));
-          converted.arguments.push_back(subst_arg);
-        }
-        CARBON_ASSIGN_OR_RETURN(bool intrinsic_satisfied,
-                                type_checker.IsIntrinsicConstraintSatisfied(
-                                    source_loc, converted, *this));
-        if (!intrinsic_satisfied) {
-          if (!diagnose_missing_impl) {
-            return {std::nullopt};
-          }
-          return ProgramError(source_loc)
-                 << "constraint requires that " << converted;
-        }
-      }
-      for (const auto& equal : equals) {
-        auto it = equal.values.begin();
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> first,
-                                type_checker.Substitute(local_bindings, *it++));
-        for (; it != equal.values.end(); ++it) {
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> current,
-                                  type_checker.Substitute(local_bindings, *it));
-          if (!ValueEqual(first, current, &equality_ctx)) {
-            if (!diagnose_missing_impl) {
-              return {std::nullopt};
-            }
-            return DiagnoseUnequalValues(source_loc, equal.values.front(),
-                                         first, *it, current, &equality_ctx);
-          }
-        }
-      }
-      for (const auto& rewrite : rewrites) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> constant,
-            type_checker.Substitute(local_bindings, rewrite.constant));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> value,
-            type_checker.Substitute(local_bindings,
-                                    rewrite.converted_replacement));
-        if (!ValueEqual(constant, value, &equality_ctx)) {
-          if (!diagnose_missing_impl) {
-            return {std::nullopt};
-          }
-          return DiagnoseUnequalValues(source_loc, rewrite.constant, constant,
-                                       rewrite.converted_replacement, value,
-                                       &equality_ctx);
-        }
-      }
-    }
-    return {type_checker.MakeConstraintWitness(std::move(witnesses))};
-  }
-  CARBON_FATAL("expected a constraint, not {0}", *constraint_type);
-}
-
-auto ImplScope::VisitEqualValues(
-    Nonnull<const Value*> value,
-    llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
-  for (Nonnull<const EqualityConstraint*> eq : equalities_) {
-    if (!eq->VisitEqualValues(value, visitor)) {
-      return false;
-    }
-  }
-  return !parent_scope_ || (*parent_scope_)->VisitEqualValues(value, visitor);
-}
-
-auto ImplScope::TryResolveInterface(Nonnull<const InterfaceType*> iface_type,
-                                    Nonnull<const Value*> type,
-                                    SourceLocation source_loc,
-                                    const TypeChecker& type_checker,
-                                    bool diagnose_missing_impl) const
-    -> ErrorOr<std::optional<Nonnull<const Witness*>>> {
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<ResolveResult> result,
-      TryResolveInterfaceRecursively(iface_type, type, source_loc, *this,
-                                     type_checker));
-  if (!result.has_value() && diagnose_missing_impl) {
-    return ProgramError(source_loc) << "could not find implementation of "
-                                    << *iface_type << " for " << *type;
-  }
-  return result ? std::optional(result->witness) : std::nullopt;
-}
-
-// Do these two witnesses refer to `impl` declarations in the same
-// `match_first` block?
-static auto InSameMatchFirst(Nonnull<const Witness*> a,
-                             Nonnull<const Witness*> b) -> bool {
-  const auto* impl_a = dyn_cast<ImplWitness>(a);
-  const auto* impl_b = dyn_cast<ImplWitness>(b);
-  if (!impl_a || !impl_b) {
-    return false;
-  }
-
-  // TODO: Once we support an impl being declared more than once, we will need
-  // to check this more carefully.
-  return impl_a->declaration().match_first() &&
-         impl_a->declaration().match_first() ==
-             impl_b->declaration().match_first();
-}
-
-// Determine whether this result is definitely right -- that there can be no
-// specialization that would give a better match.
-static auto IsEffectivelyFinal(ImplScope::ResolveResult result) -> bool {
-  // TODO: Once we support 'final', check whether this is a final impl
-  // declaration if it's parameterized.
-  return result.impl->deduced.empty();
-}
-
-// Combines the results of two impl lookups. In the event of a tie, arbitrarily
-// prefer `a` over `b`.
-static auto CombineResults(Nonnull<const InterfaceType*> iface_type,
-                           Nonnull<const Value*> type,
-                           SourceLocation source_loc,
-                           std::optional<ImplScope::ResolveResult> a,
-                           std::optional<ImplScope::ResolveResult> b)
-    -> ErrorOr<std::optional<ImplScope::ResolveResult>> {
-  // If only one lookup succeeded, return that.
-  if (!b) {
-    return a;
-  }
-  if (!a) {
-    return b;
-  }
-
-  // If exactly one of them is effectively final, prefer that result.
-  bool a_is_final = IsEffectivelyFinal(*a);
-  bool b_is_final = IsEffectivelyFinal(*b);
-  if (a_is_final && !b_is_final) {
-    return a;
-  } else if (b_is_final && !a_is_final) {
-    return b;
-  }
-
-  const auto* impl_a = dyn_cast<ImplWitness>(a->witness);
-  const auto* impl_b = dyn_cast<ImplWitness>(b->witness);
-
-  // If both are effectively final, prefer an impl declaration over a
-  // symbolic ImplBinding, because we get more information from the impl
-  // declaration. If they're both symbolic, arbitrarily pick a.
-  if (a_is_final && b_is_final) {
-    if (!impl_b) {
-      return a;
-    }
-    if (!impl_a) {
-      return b;
-    }
-  }
-  CARBON_CHECK(impl_a && impl_b, "non-final impl should not be symbolic");
-
-  // At this point, we're comparing two `impl` declarations, and either they're
-  // both final or neither of them is.
-  // TODO: We should reject the case where both are final when checking their
-  // declarations, but we don't do so yet, so for now we report it as an
-  // ambiguity.
-  //
-  // If they refer to the same `impl` declaration, it doesn't matter which one
-  // we pick, so we pick `a`.
-  // TODO: Compare the identities of the `impl`s, not the declarations.
-  if (&impl_a->declaration() == &impl_b->declaration()) {
-    return a;
-  }
-
-  return ProgramError(source_loc)
-         << "ambiguous implementations of " << *iface_type << " for " << *type;
-}
-
-auto ImplScope::TryResolveInterfaceRecursively(
-    Nonnull<const InterfaceType*> iface_type, Nonnull<const Value*> type,
-    SourceLocation source_loc, const ImplScope& original_scope,
-    const TypeChecker& type_checker) const
-    -> ErrorOr<std::optional<ResolveResult>> {
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<ResolveResult> result,
-      TryResolveInterfaceHere(iface_type, type, source_loc, original_scope,
-                              type_checker));
-  if (parent_scope_) {
-    CARBON_ASSIGN_OR_RETURN(
-        std::optional<ResolveResult> parent_result,
-        (*parent_scope_)
-            ->TryResolveInterfaceRecursively(iface_type, type, source_loc,
-                                             original_scope, type_checker));
-    CARBON_ASSIGN_OR_RETURN(result, CombineResults(iface_type, type, source_loc,
-                                                   result, parent_result));
-  }
-  return result;
-}
-
-auto ImplScope::TryResolveInterfaceHere(
-    Nonnull<const InterfaceType*> iface_type, Nonnull<const Value*> impl_type,
-    SourceLocation source_loc, const ImplScope& original_scope,
-    const TypeChecker& type_checker) const
-    -> ErrorOr<std::optional<ResolveResult>> {
-  std::optional<ResolveResult> result = std::nullopt;
-  for (const ImplFact& impl : impl_facts_) {
-    // If we've passed the final impl with a sort key matching our best impl,
-    // all further are worse and don't need to be checked.
-    if (result && result->impl->sort_key < impl.sort_key) {
-      break;
-    }
-
-    // If this impl appears later in the same match_first block as our best
-    // result, we should not consider it.
-    //
-    // TODO: This should apply transitively: if we have
-    //   match_first { impl a; impl b; }
-    //   match_first { impl b; impl c; }
-    // then we should not consider c once we match a. For now, because each
-    // impl is only declared once, this is not a problem.
-    if (result && InSameMatchFirst(result->impl->witness, impl.witness)) {
-      continue;
-    }
-
-    // Try matching this impl against our query.
-    CARBON_ASSIGN_OR_RETURN(std::optional<Nonnull<const Witness*>> witness,
-                            type_checker.MatchImpl(*iface_type, impl_type, impl,
-                                                   original_scope, source_loc));
-    if (witness) {
-      CARBON_ASSIGN_OR_RETURN(
-          result,
-          CombineResults(iface_type, impl_type, source_loc, result,
-                         ResolveResult{.impl = &impl, .witness = *witness}));
-    }
-  }
-  return result;
-}
-
-// TODO: Add indentation when printing the parents.
-void ImplScope::Print(llvm::raw_ostream& out) const {
-  llvm::ListSeparator sep(",\n    ");
-  out << "    "
-      << "[";
-  for (const ImplFact& impl : impl_facts_) {
-    out << sep << "`" << *(impl.type) << "` as `" << *(impl.interface) << "`";
-    if (impl.sort_key) {
-      out << " " << *impl.sort_key;
-    }
-  }
-  for (Nonnull<const EqualityConstraint*> eq : equalities_) {
-    out << sep;
-    llvm::ListSeparator equal(" == ");
-    for (Nonnull<const Value*> value : eq->values) {
-      out << equal << "`" << *value << "`";
-    }
-  }
-  out << "]\n";
-  if (parent_scope_) {
-    out << **parent_scope_;
-  }
-}
-
-auto SingleStepEqualityContext::VisitEqualValues(
-    Nonnull<const Value*> value,
-    llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
-  return impl_scope_->VisitEqualValues(value, visitor);
-}
-
-}  // namespace Carbon

+ 0 - 206
explorer/interpreter/impl_scope.h

@@ -1,206 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_IMPL_SCOPE_H_
-#define CARBON_EXPLORER_INTERPRETER_IMPL_SCOPE_H_
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/value.h"
-#include "explorer/interpreter/type_structure.h"
-
-namespace Carbon {
-
-class TypeChecker;
-
-// The `ImplScope` class is responsible for mapping a type and
-// interface to the location of the witness table for the `impl` for
-// that type and interface.  A scope may have parent scopes, whose
-// implementations will also be visible in the child scope.
-//
-// There is typically one instance of `ImplScope` class per scope
-// because the implementationss that are visible for a given type and
-// interface can vary from scope to scope. For example, consider the
-// `bar` and `baz` methods in the following class C and nested class D.
-//
-//     class C(U:! type, T:! type)  {
-//       class D(V:! type where U impls Fooable(T)) {
-//         fn bar[self: Self](x: U, y : T) -> T{
-//           return x.foo(y)
-//         }
-//       }
-//       fn baz[self: Self](x: U, y : T) -> T {
-//         return x.foo(y);
-//       }
-//     }
-//
-//  The call to `x.foo` in `bar` is valid because the `U is Fooable(T)`
-//  impl is visible in the body of `bar`. In contrast, the call to
-//  `x.foo` in `baz` is not valid because there is no visible impl for
-//  `U` and `Fooable` in that scope.
-//
-// `ImplScope` also tracks the type equalities that are known in a particular
-// scope.
-class ImplScope : public Printable<ImplScope> {
- public:
-  // The `ImplFact` struct is a key-value pair where the key is the
-  // combination of a type and an interface, e.g., `List` and `Container`,
-  // and the value is the result of statically resolving to the `impl`
-  // for `List` as `Container`, which is an `Expression` that produces
-  // the witness for that `impl`.
-  //
-  // When the `impl` is parameterized, `deduced` and `impl_bindings`
-  // are non-empty. The former contains the type parameters and the
-  // later are impl bindings, that is, parameters for witnesses. In this case,
-  // `sort_key` indicates the order in which this impl should be considered
-  // relative to other matching impls.
-  struct ImplFact {
-    Nonnull<const InterfaceType*> interface;
-    std::vector<Nonnull<const GenericBinding*>> deduced;
-    Nonnull<const Value*> type;
-    std::vector<Nonnull<const ImplBinding*>> impl_bindings;
-    Nonnull<const Witness*> witness;
-    std::optional<TypeStructureSortKey> sort_key;
-  };
-
-  // Internal type used to represent the result of resolving a lookup in a
-  // particular impl scope.
-  struct ResolveResult {
-    Nonnull<const ImplFact*> impl;
-    Nonnull<const Witness*> witness;
-  };
-
-  explicit ImplScope() {}
-  explicit ImplScope(Nonnull<const ImplScope*> parent)
-      : parent_scope_(parent) {}
-
-  // Associates `iface` and `type` with the `impl` in this scope. If `iface` is
-  // a constraint type, it will be split into its constituent components, and
-  // any references to `.Self` are expected to have been substituted for the
-  // type implementing the constraint.
-  void Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
-           Nonnull<const Witness*> witness, const TypeChecker& type_checker);
-  // For a parameterized impl, associates `iface` and `type`
-  // with the `impl` in this scope. Otherwise, the same as the previous
-  // overload.
-  void Add(Nonnull<const Value*> iface,
-           llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
-           Nonnull<const Value*> type,
-           llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-           Nonnull<const Witness*> witness, const TypeChecker& type_checker,
-           std::optional<TypeStructureSortKey> sort_key = std::nullopt);
-  // Adds a list of impls constraints from a constraint type into scope. Any
-  // references to `.Self` are expected to have already been substituted for
-  // the type implementing the constraint.
-  void Add(llvm::ArrayRef<ImplsConstraint> impls_constraints,
-           llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
-           llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-           Nonnull<const Witness*> witness, const TypeChecker& type_checker);
-
-  // Adds a type equality constraint.
-  void AddEqualityConstraint(Nonnull<const EqualityConstraint*> equal) {
-    equalities_.push_back(equal);
-  }
-
-  // Returns the associated impl for the given `constraint` and `type` in
-  // the ancestor graph of this scope, or reports a compilation error
-  // at `source_loc` there isn't exactly one matching impl.
-  //
-  // If any substitutions should be made into the constraint before resolving
-  // it, those should be passed in `bindings`. The witness returned will be for
-  // `constraint`, not for the result of substituting the bindings into the
-  // constraint. The substituted type might in general have a different shape
-  // of witness due to deduplication.
-  auto Resolve(Nonnull<const Value*> constraint, Nonnull<const Value*> type,
-               SourceLocation source_loc, const TypeChecker& type_checker,
-               const Bindings& bindings = {}) const
-      -> ErrorOr<Nonnull<const Witness*>>;
-
-  // Same as Resolve, except that failure due to a missing implementation of a
-  // constraint produces `nullopt` instead of an error if
-  // `diagnose_missing_impl` is `false`. This is intended for cases where we're
-  // selecting between options based on whether constraints are satisfied, such
-  // as during `impl` selection.
-  auto TryResolve(Nonnull<const Value*> constraint, Nonnull<const Value*> type,
-                  SourceLocation source_loc, const TypeChecker& type_checker,
-                  const Bindings& bindings, bool diagnose_missing_impl) const
-      -> ErrorOr<std::optional<Nonnull<const Witness*>>>;
-
-  // Visits the values that are a single step away from `value` according to an
-  // equality constraint that is in scope. That is, the values `v` such that we
-  // have a `value == v` equality constraint in scope.
-  //
-  // Stops and returns `false` if any call to the visitor returns `false`,
-  // otherwise returns `true`.
-  auto VisitEqualValues(
-      Nonnull<const Value*> value,
-      llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool;
-
-  void Print(llvm::raw_ostream& out) const;
-
- private:
-  // Returns the associated impl for the given `iface` and `type` in
-  // the ancestor graph of this scope. Reports a compilation error
-  // at `source_loc` if there's an ambiguity, or if `diagnose_missing_impl` is
-  // set and there's no matching impl.
-  auto TryResolveInterface(Nonnull<const InterfaceType*> iface,
-                           Nonnull<const Value*> type,
-                           SourceLocation source_loc,
-                           const TypeChecker& type_checker,
-                           bool diagnose_missing_impl) const
-      -> ErrorOr<std::optional<Nonnull<const Witness*>>>;
-
-  // Returns the associated impl for the given `iface` and `type` in
-  // the ancestor graph of this scope, returns std::nullopt if there
-  // is none, or reports a compilation error is there is not a most
-  // specific impl for the given `iface` and `type`.
-  // Use `original_scope` to satisfy requirements of any generic impl
-  // that matches `iface` and `type`.
-  auto TryResolveInterfaceRecursively(Nonnull<const InterfaceType*> iface_type,
-                                      Nonnull<const Value*> type,
-                                      SourceLocation source_loc,
-                                      const ImplScope& original_scope,
-                                      const TypeChecker& type_checker) const
-      -> ErrorOr<std::optional<ResolveResult>>;
-
-  // Returns the associated impl for the given `iface` and `type` in
-  // this scope, returns std::nullopt if there is none, or reports
-  // a compilation error is there is not a most specific impl for the
-  // given `iface` and `type`.
-  // Use `original_scope` to satisfy requirements of any generic impl
-  // that matches `iface` and `type`.
-  auto TryResolveInterfaceHere(Nonnull<const InterfaceType*> iface_type,
-                               Nonnull<const Value*> impl_type,
-                               SourceLocation source_loc,
-                               const ImplScope& original_scope,
-                               const TypeChecker& type_checker) const
-      -> ErrorOr<std::optional<ResolveResult>>;
-
-  std::vector<ImplFact> impl_facts_;
-  std::vector<Nonnull<const EqualityConstraint*>> equalities_;
-  std::optional<Nonnull<const ImplScope*>> parent_scope_;
-};
-
-// An equality context that considers two values to be equal if they are a
-// single step apart according to an equality constraint in the given impl
-// scope.
-struct SingleStepEqualityContext : public EqualityContext {
- public:
-  explicit SingleStepEqualityContext(Nonnull<const ImplScope*> impl_scope)
-      : impl_scope_(impl_scope) {}
-
-  // Visits the values that are equal to the given value and a single step away
-  // according to an equality constraint that is in the given impl scope. Stops
-  // and returns `false` if the visitor returns `false`, otherwise returns
-  // `true`.
-  auto VisitEqualValues(Nonnull<const Value*> value,
-                        llvm::function_ref<bool(Nonnull<const Value*>)> visitor)
-      const -> bool override;
-
- private:
-  Nonnull<const ImplScope*> impl_scope_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_IMPL_SCOPE_H_

+ 0 - 2637
explorer/interpreter/interpreter.cpp

@@ -1,2637 +0,0 @@
-// 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 "explorer/interpreter/interpreter.h"
-
-#include <iterator>
-#include <limits>
-#include <map>
-#include <memory>
-#include <optional>
-#include <random>
-#include <utility>
-#include <vector>
-
-#include "common/check.h"
-#include "common/error.h"
-#include "explorer/ast/address.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/element.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/print_as_id.h"
-#include "explorer/base/source_location.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/action.h"
-#include "explorer/interpreter/action_stack.h"
-#include "explorer/interpreter/heap.h"
-#include "explorer/interpreter/pattern_match.h"
-#include "explorer/interpreter/type_utils.h"
-#include "llvm/ADT/APInt.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/raw_ostream.h"
-
-using llvm::cast;
-using llvm::dyn_cast;
-using llvm::isa;
-
-namespace Carbon {
-
-// Limits for various overflow conditions.
-static constexpr int64_t MaxTodoSize = 1e3;
-static constexpr int64_t MaxStepsTaken = 1e6;
-static constexpr int64_t MaxArenaAllocated = 1e9;
-
-// Constructs an ActionStack suitable for the specified phase.
-static auto MakeTodo(Phase phase, Nonnull<Heap*> heap,
-                     Nonnull<TraceStream*> trace_stream) -> ActionStack {
-  switch (phase) {
-    case Phase::CompileTime:
-      return ActionStack(trace_stream);
-    case Phase::RunTime:
-      return ActionStack(trace_stream, heap);
-  }
-}
-
-// An Interpreter represents an instance of the Carbon abstract machine. It
-// manages the state of the abstract machine, and executes the steps of Actions
-// passed to it.
-class Interpreter {
- public:
-  // Constructs an Interpreter which allocates values on `arena`, and prints
-  // traces if `trace` is true. `phase` indicates whether it executes at
-  // compile time or run time.
-  Interpreter(Phase phase, Nonnull<Arena*> arena,
-              Nonnull<TraceStream*> trace_stream,
-              Nonnull<llvm::raw_ostream*> print_stream)
-      : arena_(arena),
-        heap_(trace_stream, arena),
-        todo_(MakeTodo(phase, &heap_, trace_stream)),
-        trace_stream_(trace_stream),
-        print_stream_(print_stream),
-        phase_(phase) {}
-
-  // Runs all the steps of `action`.
-  // It's not safe to call `RunAllSteps()` or `result()` after an error.
-  auto RunAllSteps(std::unique_ptr<Action> action) -> ErrorOr<Success>;
-
-  // The result produced by the `action` argument of the most recent
-  // RunAllSteps call. Cannot be called if `action` was an action that doesn't
-  // produce results.
-  auto result() const -> Nonnull<const Value*> { return todo_.result(); }
-
- private:
-  auto Step() -> ErrorOr<Success>;
-
-  // State transitions for expressions value generation.
-  auto StepValueExp() -> ErrorOr<Success>;
-  // State transitions for expressions.
-  auto StepExp() -> ErrorOr<Success>;
-  // State transitions for lvalues.
-  auto StepLocation() -> ErrorOr<Success>;
-  // State transitions for witnesses.
-  auto StepWitness() -> ErrorOr<Success>;
-  // State transition for statements.
-  auto StepStmt() -> ErrorOr<Success>;
-  // State transition for declarations.
-  auto StepDeclaration() -> ErrorOr<Success>;
-  // State transition for object destruction.
-  auto StepCleanUp() -> ErrorOr<Success>;
-  auto StepDestroy() -> ErrorOr<Success>;
-  // State transition for type instantiation.
-  auto StepInstantiateType() -> ErrorOr<Success>;
-
-  auto CreateStruct(const std::vector<FieldInitializer>& fields,
-                    const std::vector<Nonnull<const Value*>>& values)
-      -> Nonnull<const Value*>;
-
-  auto EvalPrim(Operator op, Nonnull<const Value*> static_type,
-                const std::vector<Nonnull<const Value*>>& args,
-                SourceLocation source_loc) -> ErrorOr<Nonnull<const Value*>>;
-
-  // Returns the result of converting `value` to type `destination_type`.
-  auto Convert(Nonnull<const Value*> value,
-               Nonnull<const Value*> destination_type,
-               SourceLocation source_loc) -> ErrorOr<Nonnull<const Value*>>;
-
-  // Create a class value and its base class(es) from an init struct.
-  auto ConvertStructToClass(Nonnull<const StructValue*> init,
-                            Nonnull<const NominalClassType*> class_type,
-                            SourceLocation source_loc)
-      -> ErrorOr<Nonnull<const NominalClassValue*>>;
-
-  // Evaluate an expression immediately, recursively, and return its result.
-  //
-  // TODO: Stop using this.
-  auto EvalRecursively(std::unique_ptr<Action> action)
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Evaluate an associated constant by evaluating its witness and looking
-  // inside the impl for the corresponding value.
-  //
-  // TODO: This approach doesn't provide values that are known because they
-  // appear in constraints:
-  //
-  //   interface Iface { let N:! i32; }
-  //   fn PickType(N: i32) -> type { return i32; }
-  //   fn F[T:! Iface where .N == 5](x: T) {
-  //     var x: PickType(T.N) = 0;
-  //   }
-  //
-  // ... will fail because we can't resolve T.N to 5 at compile time.
-  auto EvalAssociatedConstant(Nonnull<const AssociatedConstant*> assoc,
-                              SourceLocation source_loc)
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Instantiate a type by replacing all type variables that occur inside the
-  // type by the current values of those variables.
-  //
-  // For example, suppose T=i32 and U=bool. Then
-  //     __Fn (Point(T)) -> Point(U)
-  // becomes
-  //     __Fn (Point(i32)) -> Point(bool)
-  //
-  // TODO: This should be an Action.
-  auto InstantiateType(Nonnull<const Value*> type, SourceLocation source_loc)
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Instantiate a set of bindings by replacing all type variables that occur
-  // within it by the current values of those variables.
-  auto InstantiateBindings(Nonnull<const Bindings*> bindings,
-                           SourceLocation source_loc)
-      -> ErrorOr<Nonnull<const Bindings*>>;
-
-  // Instantiate a witness by replacing all type variables and impl binding
-  // references that occur within it by the current values of those variables.
-  auto InstantiateWitness(Nonnull<const Witness*> witness,
-                          SourceLocation source_loc)
-      -> ErrorOr<Nonnull<const Witness*>>;
-
-  // Call the function `fun` with the given `arg` and the `witnesses`
-  // for the function's impl bindings.
-  auto CallFunction(const CallExpression& call, Nonnull<const Value*> fun,
-                    Nonnull<const Value*> arg, ImplWitnessMap&& witnesses,
-                    std::optional<AllocationId> location_received)
-      -> ErrorOr<Success>;
-
-  // Call the destructor method in `fun`, with any self argument bound to
-  // `receiver`.
-  auto CallDestructor(Nonnull<const DestructorDeclaration*> fun,
-                      ExpressionResult receiver) -> ErrorOr<Success>;
-
-  // If the given method or destructor `decl` has a self argument, bind it to
-  // `receiver`.
-  void BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
-                         ExpressionResult receiver, RuntimeScope& method_scope,
-                         BindingMap& generic_args,
-                         const SourceLocation& source_location);
-
-  auto phase() const -> Phase { return phase_; }
-
-  Nonnull<Arena*> arena_;
-
-  Heap heap_;
-  ActionStack todo_;
-
-  Nonnull<TraceStream*> trace_stream_;
-
-  // The stream for the Print intrinsic.
-  Nonnull<llvm::raw_ostream*> print_stream_;
-
-  Phase phase_;
-
-  // The number of steps taken by the interpreter. Used for infinite loop
-  // detection.
-  int64_t steps_taken_ = 0;
-};
-
-//
-// State Operations
-//
-
-auto Interpreter::EvalPrim(Operator op, Nonnull<const Value*> /*static_type*/,
-                           const std::vector<Nonnull<const Value*>>& args,
-                           SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  switch (op) {
-    case Operator::Neg:
-    case Operator::Add:
-    case Operator::Sub:
-    case Operator::Div:
-    case Operator::Mul: {
-      llvm::APInt op0(64, cast<IntValue>(*args[0]).value());
-      llvm::APInt result;
-      if (op == Operator::Neg) {
-        result = -op0;
-      } else {
-        llvm::APInt op1(64, cast<IntValue>(*args[1]).value());
-        if (op == Operator::Add) {
-          result = op0 + op1;
-        } else if (op == Operator::Sub) {
-          result = op0 - op1;
-        } else if (op == Operator::Mul) {
-          result = op0 * op1;
-        } else if (op == Operator::Div) {
-          if (op1.getSExtValue() == 0) {
-            return ProgramError(source_loc) << "division by zero";
-          }
-          result = op0.sdiv(op1);
-        }
-      }
-      if (result.isSignedIntN(32)) {
-        return arena_->New<IntValue>(result.getSExtValue());
-      } else {
-        return ProgramError(source_loc) << "integer overflow";
-      }
-    }
-    case Operator::Mod: {
-      const auto& lhs = cast<IntValue>(*args[0]).value();
-      const auto& rhs = cast<IntValue>(*args[1]).value();
-      if (rhs == 0) {
-        return ProgramError(source_loc) << "division by zero";
-      }
-      return arena_->New<IntValue>(lhs % rhs);
-    }
-    case Operator::Not:
-      return arena_->New<BoolValue>(!cast<BoolValue>(*args[0]).value());
-    case Operator::And:
-      return arena_->New<BoolValue>(cast<BoolValue>(*args[0]).value() &&
-                                    cast<BoolValue>(*args[1]).value());
-    case Operator::Or:
-      return arena_->New<BoolValue>(cast<BoolValue>(*args[0]).value() ||
-                                    cast<BoolValue>(*args[1]).value());
-    case Operator::Ptr:
-      return arena_->New<PointerType>(args[0]);
-    case Operator::Deref: {
-      CARBON_ASSIGN_OR_RETURN(
-          const auto* value,
-          heap_.Read(cast<PointerValue>(*args[0]).address(), source_loc));
-      return arena_->New<ReferenceExpressionValue>(
-          value, cast<PointerValue>(*args[0]).address());
-    }
-    case Operator::AddressOf:
-      return arena_->New<PointerValue>(cast<LocationValue>(*args[0]).address());
-    case Operator::As:
-    case Operator::Eq:
-    case Operator::NotEq:
-    case Operator::Less:
-    case Operator::LessEq:
-    case Operator::Greater:
-    case Operator::GreaterEq:
-    case Operator::BitwiseAnd:
-    case Operator::BitwiseOr:
-    case Operator::BitwiseXor:
-    case Operator::BitShiftLeft:
-    case Operator::BitShiftRight:
-    case Operator::Complement:
-      CARBON_FATAL("operator {0} should always be rewritten",
-                   OperatorToString(op));
-  }
-}
-
-auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
-                               const std::vector<Nonnull<const Value*>>& values)
-    -> Nonnull<const Value*> {
-  std::vector<NamedValue> elements;
-  for (const auto [field, value] : llvm::zip_equal(fields, values)) {
-    elements.push_back({field.name(), value});
-  }
-
-  return arena_->New<StructValue>(std::move(elements));
-}
-
-auto Interpreter::StepLocation() -> ErrorOr<Success> {
-  Action& act = todo_.CurrentAction();
-  const Expression& exp = cast<LocationAction>(act).expression();
-
-  switch (exp.kind()) {
-    case ExpressionKind::IdentifierExpression: {
-      //    { {x :: C, E, F} :: S, H}
-      // -> { {E(x) :: C, E, F} :: S, H}
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> value,
-          todo_.ValueOfNode(cast<IdentifierExpression>(exp).value_node(),
-                            exp.source_loc()));
-      CARBON_CHECK(isa<LocationValue>(value), "{0}", *value);
-      return todo_.FinishAction(value);
-    }
-    case ExpressionKind::SimpleMemberAccessExpression: {
-      const auto& access = cast<SimpleMemberAccessExpression>(exp);
-      const auto constant_value = access.constant_value();
-      if (auto rewrite = access.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<LocationAction>(*rewrite));
-      }
-      if (act.pos() == 0) {
-        //    { {e.f :: C, E, F} :: S, H}
-        // -> { e :: [].f :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<LocationAction>(&access.object()));
-      } else if (act.pos() == 1 && constant_value) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            *constant_value, access.source_loc()));
-      } else {
-        if (constant_value) {
-          return todo_.FinishAction(act.results().back());
-        } else {
-          //    { v :: [].f :: C, E, F} :: S, H}
-          // -> { { &v.f :: C, E, F} :: S, H }
-          Address object = cast<LocationValue>(*act.results()[0]).address();
-          Address member = object.ElementAddress(&access.member());
-          return todo_.FinishAction(arena_->New<LocationValue>(member));
-        }
-      }
-    }
-    case ExpressionKind::CompoundMemberAccessExpression: {
-      const auto& access = cast<CompoundMemberAccessExpression>(exp);
-      const auto constant_value = access.constant_value();
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<LocationAction>(&access.object()));
-      }
-      if (act.pos() == 1 && constant_value) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            *constant_value, access.source_loc()));
-      } else {
-        if (constant_value) {
-          return todo_.FinishAction(act.results().back());
-        }
-        CARBON_CHECK(!access.member().interface().has_value(),
-                     "unexpected location interface member");
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> val,
-            Convert(act.results()[0], *access.member().base_type(),
-                    exp.source_loc()));
-        Address object = cast<LocationValue>(*val).address();
-        Address field = object.ElementAddress(&access.member().member());
-        return todo_.FinishAction(arena_->New<LocationValue>(field));
-      }
-    }
-    case ExpressionKind::BaseAccessExpression: {
-      const auto& access = cast<BaseAccessExpression>(exp);
-      if (act.pos() == 0) {
-        // Get LocationValue for expression.
-        return todo_.Spawn(std::make_unique<LocationAction>(&access.object()));
-      } else {
-        // Append `.base` element to the address, and return the new
-        // LocationValue.
-        Address object = cast<LocationValue>(*act.results()[0]).address();
-        Address base = object.ElementAddress(&access.element());
-        return todo_.FinishAction(arena_->New<LocationValue>(base));
-      }
-    }
-    case ExpressionKind::IndexExpression: {
-      if (act.pos() == 0) {
-        //    { {e[i] :: C, E, F} :: S, H}
-        // -> { e :: [][i] :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<LocationAction>(
-            &cast<IndexExpression>(exp).object()));
-
-      } else if (act.pos() == 1) {
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<IndexExpression>(exp).offset()));
-      } else {
-        //    { v :: [][i] :: C, E, F} :: S, H}
-        // -> { { &v[i] :: C, E, F} :: S, H }
-        Address object = cast<LocationValue>(*act.results()[0]).address();
-        const auto index = cast<IntValue>(*act.results()[1]).value();
-        Address field = object.ElementAddress(
-            arena_->New<PositionalElement>(index, &exp.static_type()));
-        return todo_.FinishAction(arena_->New<LocationValue>(field));
-      }
-    }
-    case ExpressionKind::OperatorExpression: {
-      const auto& op = cast<OperatorExpression>(exp);
-      if (auto rewrite = op.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<LocationAction>(*rewrite));
-      }
-      if (op.op() != Operator::Deref) {
-        CARBON_FATAL(
-            "Can't treat primitive operator expression as location: {0}", exp);
-      }
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(op.arguments()[0]));
-      } else {
-        const auto& res = cast<PointerValue>(*act.results()[0]);
-        return todo_.FinishAction(arena_->New<LocationValue>(res.address()));
-      }
-      break;
-    }
-    case ExpressionKind::TupleLiteral:
-    case ExpressionKind::StructLiteral:
-    case ExpressionKind::StructTypeLiteral:
-    case ExpressionKind::IntLiteral:
-    case ExpressionKind::BoolLiteral:
-    case ExpressionKind::CallExpression:
-    case ExpressionKind::IntTypeLiteral:
-    case ExpressionKind::BoolTypeLiteral:
-    case ExpressionKind::TypeTypeLiteral:
-    case ExpressionKind::FunctionTypeLiteral:
-    case ExpressionKind::StringLiteral:
-    case ExpressionKind::StringTypeLiteral:
-    case ExpressionKind::ValueLiteral:
-    case ExpressionKind::IntrinsicExpression:
-    case ExpressionKind::IfExpression:
-    case ExpressionKind::WhereExpression:
-    case ExpressionKind::DotSelfExpression:
-    case ExpressionKind::ArrayTypeLiteral:
-    case ExpressionKind::BuiltinConvertExpression:
-      CARBON_FATAL("Can't treat expression as location: {0}", exp);
-    case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL("Unimplemented: {0}", exp);
-  }
-}
-
-auto Interpreter::EvalRecursively(std::unique_ptr<Action> action)
-    -> ErrorOr<Nonnull<const Value*>> {
-  todo_.BeginRecursiveAction();
-  CARBON_RETURN_IF_ERROR(todo_.Spawn(std::move(action)));
-  // Note that the only `RecursiveAction` we can encounter here is our own --
-  // if a nested action begins a recursive action, it will run until that
-  // action is finished and popped off the queue before returning to us.
-  while (!isa<RecursiveAction>(todo_.CurrentAction())) {
-    CARBON_RETURN_IF_ERROR(Step());
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "recursive eval done\n";
-  }
-  Nonnull<const Value*> result =
-      cast<RecursiveAction>(todo_.CurrentAction()).results()[0];
-  CARBON_RETURN_IF_ERROR(todo_.FinishAction());
-  return result;
-}
-
-auto Interpreter::EvalAssociatedConstant(
-    Nonnull<const AssociatedConstant*> assoc, SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  // Instantiate the associated constant.
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> interface,
-                          InstantiateType(&assoc->interface(), source_loc));
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Witness*> witness,
-                          InstantiateWitness(&assoc->witness(), source_loc));
-
-  const auto* impl_witness = dyn_cast<ImplWitness>(witness);
-  if (!impl_witness) {
-    CARBON_CHECK(phase() == Phase::CompileTime,
-                 "symbolic witnesses should only be formed at compile time");
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base,
-                            InstantiateType(&assoc->base(), source_loc));
-    return arena_->New<AssociatedConstant>(base, cast<InterfaceType>(interface),
-                                           &assoc->constant(), witness);
-  }
-
-  // We have an impl. Extract the value from it.
-  Nonnull<const ConstraintType*> constraint =
-      impl_witness->declaration().constraint_type();
-  std::optional<Nonnull<const Value*>> result;
-  for (const auto& rewrite : constraint->rewrite_constraints()) {
-    if (&rewrite.constant->constant() == &assoc->constant() &&
-        TypeEqual(&rewrite.constant->interface(), interface, std::nullopt)) {
-      // TODO: The value might depend on the parameters of the impl. We need to
-      // substitute impl_witness->type_args() into the value.
-      result = rewrite.converted_replacement;
-      break;
-    }
-  }
-  if (!result) {
-    CARBON_FATAL(
-        "{0} with constraint {1} is missing value for associated constant "
-        "{2}.{3}",
-        impl_witness->declaration(), *constraint, *interface,
-        assoc->constant().binding().name());
-  }
-  return *result;
-}
-
-auto Interpreter::InstantiateType(Nonnull<const Value*> type,
-                                  SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "instantiating type `" << *type << "` ("
-                           << source_loc << ")\n";
-  }
-
-  const Value* value = nullptr;
-  switch (type->kind()) {
-    case Value::Kind::VariableType: {
-      CARBON_ASSIGN_OR_RETURN(
-          value,
-          todo_.ValueOfNode(&cast<VariableType>(*type).binding(), source_loc));
-      if (const auto* location = dyn_cast<LocationValue>(value)) {
-        CARBON_ASSIGN_OR_RETURN(value,
-                                heap_.Read(location->address(), source_loc));
-      }
-      break;
-    }
-    case Value::Kind::InterfaceType: {
-      const auto& interface_type = cast<InterfaceType>(*type);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Bindings*> bindings,
-          InstantiateBindings(&interface_type.bindings(), source_loc));
-      value =
-          arena_->New<InterfaceType>(&interface_type.declaration(), bindings);
-      break;
-    }
-    case Value::Kind::NamedConstraintType: {
-      const auto& constraint_type = cast<NamedConstraintType>(*type);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Bindings*> bindings,
-          InstantiateBindings(&constraint_type.bindings(), source_loc));
-      value = arena_->New<NamedConstraintType>(&constraint_type.declaration(),
-                                               bindings);
-      break;
-    }
-    case Value::Kind::ChoiceType: {
-      const auto& choice_type = cast<ChoiceType>(*type);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Bindings*> bindings,
-          InstantiateBindings(&choice_type.bindings(), source_loc));
-      value = arena_->New<ChoiceType>(&choice_type.declaration(), bindings);
-      break;
-    }
-    case Value::Kind::AssociatedConstant: {
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> type_value,
-          EvalAssociatedConstant(cast<AssociatedConstant>(type), source_loc));
-      value = type_value;
-      break;
-    }
-    default:
-      value = type;
-      break;
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "instantiated type `" << *type << "` as `" << *value
-                         << "` (" << source_loc << ")\n";
-  }
-
-  return value;
-}
-
-auto Interpreter::InstantiateBindings(Nonnull<const Bindings*> bindings,
-                                      SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Bindings*>> {
-  BindingMap args = bindings->args();
-  for (auto& [var, arg] : args) {
-    CARBON_ASSIGN_OR_RETURN(arg, InstantiateType(arg, source_loc));
-  }
-
-  ImplWitnessMap witnesses = bindings->witnesses();
-  for (auto& [bind, witness] : witnesses) {
-    CARBON_ASSIGN_OR_RETURN(
-        witness, InstantiateWitness(cast<Witness>(witness), source_loc));
-  }
-
-  if (args == bindings->args() && witnesses == bindings->witnesses()) {
-    return bindings;
-  }
-  return arena_->New<Bindings>(std::move(args), std::move(witnesses));
-}
-
-auto Interpreter::InstantiateWitness(Nonnull<const Witness*> witness,
-                                     SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Witness*>> {
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const Value*> value,
-      EvalRecursively(std::make_unique<WitnessAction>(witness, source_loc)));
-  return cast<Witness>(value);
-}
-
-auto Interpreter::ConvertStructToClass(
-    Nonnull<const StructValue*> init_struct,
-    Nonnull<const NominalClassType*> class_type, SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const NominalClassValue*>> {
-  std::vector<NamedValue> struct_values;
-  std::optional<Nonnull<const NominalClassValue*>> base_instance;
-  // Instantiate the `destination_type` to obtain the runtime
-  // type of the object.
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_class,
-                          InstantiateType(class_type, source_loc));
-  for (const auto& field : init_struct->elements()) {
-    if (field.name == NominalClassValue::BaseField) {
-      CARBON_CHECK(class_type->base().has_value(),
-                   "Invalid 'base' field for class '{0}' without base class.",
-                   class_type->declaration().name());
-      CARBON_ASSIGN_OR_RETURN(
-          auto base,
-          Convert(field.value, class_type->base().value(), source_loc));
-      base_instance = cast<NominalClassValue>(base);
-    } else {
-      struct_values.push_back(field);
-    }
-  }
-  CARBON_CHECK(!cast<NominalClassType>(inst_class)->base() || base_instance,
-               "Invalid conversion for `{0}`: base class missing", *inst_class);
-  auto* converted_init_struct =
-      arena_->New<StructValue>(std::move(struct_values));
-  Nonnull<const NominalClassValue** const> class_value_ptr =
-      base_instance ? (*base_instance)->class_value_ptr()
-                    : arena_->New<const NominalClassValue*>();
-  return arena_->New<NominalClassValue>(inst_class, converted_init_struct,
-                                        base_instance, class_value_ptr);
-}
-
-auto Interpreter::Convert(Nonnull<const Value*> value,
-                          Nonnull<const Value*> destination_type,
-                          SourceLocation source_loc)
-    -> ErrorOr<Nonnull<const Value*>> {
-  switch (value->kind()) {
-    case Value::Kind::IntValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::AlternativeValue:
-    case Value::Kind::UninitializedValue:
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::FunctionType:
-    case Value::Kind::PointerType:
-    case Value::Kind::TupleType:
-    case Value::Kind::StructType:
-    case Value::Kind::AutoType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-    case Value::Kind::ParameterizedEntityName:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::StringType:
-    case Value::Kind::StringValue:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName:
-    case Value::Kind::StaticArrayType:
-    case Value::Kind::MemberName:
-      // TODO: add `CARBON_CHECK(TypeEqual(type, value->dynamic_type()))`, once
-      // we have Value::dynamic_type.
-      return value;
-    case Value::Kind::StructValue: {
-      const auto& struct_val = cast<StructValue>(*value);
-      switch (destination_type->kind()) {
-        case Value::Kind::StructType: {
-          const auto& destination_struct_type =
-              cast<StructType>(*destination_type);
-          std::vector<NamedValue> new_elements;
-          for (const auto& [field_name, field_type] :
-               destination_struct_type.fields()) {
-            std::optional<Nonnull<const Value*>> old_value =
-                struct_val.FindField(field_name);
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> val,
-                Convert(*old_value, field_type, source_loc));
-            new_elements.push_back({field_name, val});
-          }
-          return arena_->New<StructValue>(std::move(new_elements));
-        }
-        case Value::Kind::NominalClassType: {
-          CARBON_ASSIGN_OR_RETURN(
-              auto class_value,
-              ConvertStructToClass(cast<StructValue>(value),
-                                   cast<NominalClassType>(destination_type),
-                                   source_loc));
-          return class_value;
-        }
-        case Value::Kind::TypeType:
-        case Value::Kind::ConstraintType:
-        case Value::Kind::NamedConstraintType:
-        case Value::Kind::InterfaceType: {
-          CARBON_CHECK(struct_val.elements().empty(),
-                       "only empty structs convert to `type`");
-          return arena_->New<StructType>();
-        }
-        default: {
-          CARBON_CHECK(IsValueKindDependent(destination_type) ||
-                           (isa<TypeType, ConstraintType>(destination_type)),
-                       "Can't convert value {0} to type {1}", *value,
-                       *destination_type);
-          return value;
-        }
-      }
-    }
-    case Value::Kind::TupleValue: {
-      const auto* tuple = cast<TupleValue>(value);
-      std::vector<Nonnull<const Value*>> destination_element_types;
-      switch (destination_type->kind()) {
-        case Value::Kind::TupleType:
-          destination_element_types =
-              cast<TupleType>(destination_type)->elements();
-          break;
-        case Value::Kind::StaticArrayType: {
-          const auto& array_type = cast<StaticArrayType>(*destination_type);
-          CARBON_CHECK(array_type.has_size());
-          destination_element_types.resize(array_type.size(),
-                                           &array_type.element_type());
-          break;
-        }
-        case Value::Kind::TypeType:
-        case Value::Kind::ConstraintType:
-        case Value::Kind::NamedConstraintType:
-        case Value::Kind::InterfaceType: {
-          std::vector<Nonnull<const Value*>> new_elements;
-          Nonnull<const Value*> type_type = arena_->New<TypeType>();
-          for (Nonnull<const Value*> value : tuple->elements()) {
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value_as_type,
-                                    Convert(value, type_type, source_loc));
-            new_elements.push_back(value_as_type);
-          }
-          return arena_->New<TupleType>(std::move(new_elements));
-        }
-        default: {
-          CARBON_CHECK(IsValueKindDependent(destination_type) ||
-                           (isa<TypeType, ConstraintType>(destination_type)),
-                       "Can't convert value {0} to type {1}", *value,
-                       *destination_type);
-          return value;
-        }
-      }
-      std::vector<Nonnull<const Value*>> new_elements;
-      for (const auto [element, dest_type] :
-           llvm::zip_equal(tuple->elements(), destination_element_types)) {
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> val,
-                                Convert(element, dest_type, source_loc));
-        new_elements.push_back(val);
-      }
-      return arena_->New<TupleValue>(std::move(new_elements));
-    }
-    case Value::Kind::VariableType: {
-      std::optional<Nonnull<const Value*>> source_type;
-      // While type-checking a `where` expression, we can evaluate a reference
-      // to its self binding before we know its type. In this case, the self
-      // binding is always a type.
-      //
-      // TODO: Add a conversion kind to BuiltinConvertExpression so that we
-      // don't need to look at the types and reconstruct what kind of
-      // conversion is being performed from here.
-      if (cast<VariableType>(value)->binding().is_type_checked()) {
-        CARBON_ASSIGN_OR_RETURN(
-            source_type,
-            InstantiateType(&cast<VariableType>(value)->binding().static_type(),
-                            source_loc));
-      }
-      if (isa<TypeType, ConstraintType, NamedConstraintType, InterfaceType>(
-              destination_type) &&
-          (!source_type ||
-           isa<TypeType, ConstraintType, NamedConstraintType, InterfaceType>(
-               *source_type))) {
-        // No further conversions are required.
-        return value;
-      }
-      // We need to convert this, and we don't know how because we don't have
-      // the value yet.
-      return ProgramError(source_loc)
-             << "value of generic binding " << *value << " is not known";
-    }
-    case Value::Kind::AssociatedConstant: {
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> value,
-          EvalAssociatedConstant(cast<AssociatedConstant>(value), source_loc));
-      if (const auto* new_const = dyn_cast<AssociatedConstant>(value)) {
-        // TODO: Detect whether conversions are required in type-checking.
-        if (isa<TypeType, ConstraintType, NamedConstraintType, InterfaceType>(
-                destination_type) &&
-            isa<TypeType, ConstraintType, NamedConstraintType, InterfaceType>(
-                new_const->constant().static_type())) {
-          // No further conversions are required.
-          return value;
-        }
-        // We need to convert this, and we don't know how because we don't have
-        // the value yet.
-        return ProgramError(source_loc)
-               << "value of associated constant " << *value << " is not known";
-      }
-      return Convert(value, destination_type, source_loc);
-    }
-    case Value::Kind::PointerValue: {
-      if (destination_type->kind() != Value::Kind::PointerType ||
-          cast<PointerType>(destination_type)->pointee_type().kind() !=
-              Value::Kind::NominalClassType) {
-        // No conversion needed.
-        return value;
-      }
-
-      // Get pointee value.
-      const auto* src_ptr = cast<PointerValue>(value);
-      CARBON_ASSIGN_OR_RETURN(const auto* pointee,
-                              heap_.Read(src_ptr->address(), source_loc))
-      CARBON_CHECK(pointee->kind() == Value::Kind::NominalClassValue,
-                   "Unexpected pointer type");
-
-      // Conversion logic for subtyping for function arguments only.
-      // TODO: Drop when able to rewrite subtyping in TypeChecker for arguments.
-      const auto* dest_ptr = cast<PointerType>(destination_type);
-      std::optional<Nonnull<const NominalClassValue*>> class_subobj =
-          cast<NominalClassValue>(pointee);
-      auto new_addr = src_ptr->address();
-      while (class_subobj) {
-        if (TypeEqual(&(*class_subobj)->type(), &dest_ptr->pointee_type(),
-                      std::nullopt)) {
-          return arena_->New<PointerValue>(new_addr);
-        }
-        class_subobj = (*class_subobj)->base();
-        new_addr = new_addr.ElementAddress(
-            arena_->New<BaseElement>(&dest_ptr->pointee_type()));
-      }
-
-      // Unable to resolve, return as-is.
-      // TODO: Produce error instead once we can properly substitute
-      // parameterized types for pointers in function call parameters.
-      return value;
-    }
-    case Value::Kind::ReferenceExpressionValue: {
-      const auto* expr_value = cast<ReferenceExpressionValue>(value);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> converted,
-          Convert(expr_value->value(), destination_type, source_loc));
-      if (converted == expr_value->value()) {
-        return expr_value;
-      } else {
-        return converted;
-      }
-    }
-  }
-}
-
-auto Interpreter::CallFunction(const CallExpression& call,
-                               Nonnull<const Value*> fun,
-                               Nonnull<const Value*> arg,
-                               ImplWitnessMap&& witnesses,
-                               std::optional<AllocationId> location_received)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Call() << "calling function: " << *fun << "\n";
-  }
-  switch (fun->kind()) {
-    case Value::Kind::AlternativeConstructorValue: {
-      const auto& alt = cast<AlternativeConstructorValue>(*fun);
-      return todo_.FinishAction(arena_->New<AlternativeValue>(
-          &alt.choice(), &alt.alternative(), cast<TupleValue>(arg)));
-    }
-    case Value::Kind::FunctionValue:
-    case Value::Kind::BoundMethodValue: {
-      const auto* func_val = cast<FunctionOrMethodValue>(fun);
-
-      const FunctionDeclaration& function = func_val->declaration();
-      if (!function.body().has_value()) {
-        return ProgramError(call.source_loc())
-               << "attempt to call function `" << function.name()
-               << "` that has not been defined";
-      }
-      if (!function.is_type_checked()) {
-        return ProgramError(call.source_loc())
-               << "attempt to call function `" << function.name()
-               << "` that has not been fully type-checked";
-      }
-
-      // Enter the binding scope to make any deduced arguments visible before
-      // we resolve the self type and parameter type.
-      auto& binding_scope = todo_.CurrentAction().scope().value();
-
-      // Bring the deduced arguments and their witnesses into scope.
-      for (const auto& [bind, val] : call.deduced_args()) {
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_val,
-                                InstantiateType(val, call.source_loc()));
-        binding_scope.BindValue(bind->original(), inst_val);
-      }
-      for (const auto& [impl_bind, witness] : witnesses) {
-        binding_scope.BindValue(impl_bind->original(), witness);
-      }
-
-      // Bring the arguments that are determined by the function value into
-      // scope. This includes the arguments for the class of which the function
-      // is a member.
-      for (const auto& [bind, val] : func_val->type_args()) {
-        binding_scope.BindValue(bind->original(), val);
-      }
-      for (const auto& [impl_bind, witness] : func_val->witnesses()) {
-        binding_scope.BindValue(impl_bind->original(), witness);
-      }
-
-      RuntimeScope function_scope(&heap_);
-      BindingMap generic_args;
-
-      // Bind the receiver to the `self` parameter, if there is one.
-      if (const auto* method_val = dyn_cast<BoundMethodValue>(func_val)) {
-        BindSelfIfPresent(&function,
-                          ExpressionResult::Value(method_val->receiver()),
-                          function_scope, generic_args, call.source_loc());
-      }
-
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> converted_args,
-          Convert(arg, &function.param_pattern().static_type(),
-                  call.source_loc()));
-
-      // Bind the arguments to the parameters.
-      bool success = PatternMatch(&function.param_pattern().value(),
-                                  ExpressionResult::Value(converted_args),
-                                  call.source_loc(), &function_scope,
-                                  generic_args, trace_stream_, this->arena_);
-      CARBON_CHECK(success, "Failed to bind arguments to parameters");
-      return todo_.Spawn(std::make_unique<StatementAction>(*function.body(),
-                                                           location_received),
-                         std::move(function_scope));
-    }
-    case Value::Kind::ParameterizedEntityName: {
-      const auto& name = cast<ParameterizedEntityName>(*fun);
-      const Declaration& decl = name.declaration();
-      RuntimeScope params_scope(&heap_);
-      BindingMap generic_args;
-      CARBON_CHECK(PatternMatch(&name.params().value(),
-                                ExpressionResult::Value(arg), call.source_loc(),
-                                &params_scope, generic_args, trace_stream_,
-                                this->arena_));
-      Nonnull<const Bindings*> bindings =
-          arena_->New<Bindings>(std::move(generic_args), std::move(witnesses));
-      switch (decl.kind()) {
-        case DeclarationKind::ClassDeclaration: {
-          const auto& class_decl = cast<ClassDeclaration>(decl);
-          return todo_.FinishAction(arena_->New<NominalClassType>(
-              &class_decl, bindings, class_decl.base_type(), EmptyVTable()));
-        }
-        case DeclarationKind::InterfaceDeclaration:
-          return todo_.FinishAction(arena_->New<InterfaceType>(
-              &cast<InterfaceDeclaration>(decl), bindings));
-        case DeclarationKind::ConstraintDeclaration:
-          return todo_.FinishAction(arena_->New<NamedConstraintType>(
-              &cast<ConstraintDeclaration>(decl), bindings));
-        case DeclarationKind::ChoiceDeclaration:
-          return todo_.FinishAction(arena_->New<ChoiceType>(
-              &cast<ChoiceDeclaration>(decl), bindings));
-        default:
-          CARBON_FATAL("unknown kind of ParameterizedEntityName {0}", decl);
-      }
-    }
-    default:
-      return ProgramError(call.source_loc())
-             << "in call, expected a function, not " << *fun;
-  }
-}
-
-auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
-                                 ExpressionResult receiver)
-    -> ErrorOr<Success> {
-  const DestructorDeclaration& method = *fun;
-  CARBON_CHECK(method.is_method());
-
-  RuntimeScope method_scope(&heap_);
-  BindingMap generic_args;
-  BindSelfIfPresent(fun, receiver, method_scope, generic_args,
-                    SourceLocation::DiagnosticsIgnored());
-
-  CARBON_CHECK(method.body().has_value(),
-               "Calling a method that's missing a body");
-
-  auto act = std::make_unique<StatementAction>(*method.body(), std::nullopt);
-  return todo_.Spawn(std::unique_ptr<Action>(std::move(act)),
-                     std::move(method_scope));
-}
-
-void Interpreter::BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
-                                    ExpressionResult receiver,
-                                    RuntimeScope& method_scope,
-                                    BindingMap& generic_args,
-                                    const SourceLocation& source_location) {
-  CARBON_CHECK(decl->is_method());
-  const auto* self_pattern = &decl->self_pattern().value();
-  if (const auto* placeholder =
-          dyn_cast<BindingPlaceholderValue>(self_pattern)) {
-    // Immutable self with `[self: Self]`
-    if (placeholder->value_node().has_value()) {
-      bool success =
-          PatternMatch(placeholder, receiver, source_location, &method_scope,
-                       generic_args, trace_stream_, this->arena_);
-      CARBON_CHECK(success, "Failed to bind self");
-    }
-  } else {
-    // Mutable self with `[addr self: Self*]`
-    CARBON_CHECK(isa<AddrValue>(self_pattern));
-    ExpressionResult v = receiver;
-    // See if we need to make a LocationValue from the address provided in the
-    // ExpressionResult
-    if (receiver.value()->kind() != Value::Kind::LocationValue) {
-      CARBON_CHECK(receiver.expression_category() ==
-                   ExpressionCategory::Reference);
-      CARBON_CHECK(receiver.address().has_value());
-      v = ExpressionResult::Value(
-          arena_->New<LocationValue>(receiver.address().value()));
-    }
-    bool success = PatternMatch(self_pattern, v, source_location, &method_scope,
-                                generic_args, trace_stream_, this->arena_);
-    CARBON_CHECK(success, "Failed to bind addr self");
-  }
-}
-
-// Returns true if the format string is okay to pass to formatv. This only
-// supports `{{` and `{N}` as special syntax.
-static auto ValidateFormatString(SourceLocation source_loc,
-                                 const char* format_string, int num_args)
-    -> ErrorOr<Success> {
-  const char* cursor = format_string;
-  while (true) {
-    switch (*cursor) {
-      case '\0':
-        // End of string.
-        return Success();
-      case '{':
-        // `{` is a special character.
-        ++cursor;
-        switch (*cursor) {
-          case '\0':
-            return ProgramError(source_loc)
-                   << "`{` must be followed by a second `{` or index in `"
-                   << format_string << "`";
-          case '{':
-            // Escaped `{`.
-            ++cursor;
-            break;
-          case '}':
-            return ProgramError(source_loc)
-                   << "Invalid `{}` in `" << format_string << "`";
-          default:
-            int index = 0;
-            while (*cursor != '}') {
-              if (*cursor == '\0') {
-                return ProgramError(source_loc)
-                       << "Index incomplete in `" << format_string << "`";
-              }
-              if (*cursor < '0' || *cursor > '9') {
-                return ProgramError(source_loc)
-                       << "Non-numeric character in index at offset "
-                       << cursor - format_string << " in `" << format_string
-                       << "`";
-              }
-              index = (10 * index) + (*cursor - '0');
-              if (index >= num_args) {
-                return ProgramError(source_loc)
-                       << "Index invalid with argument count of " << num_args
-                       << " at offset " << cursor - format_string << " in `"
-                       << format_string << "`";
-              }
-              ++cursor;
-            }
-            // Move past the `}`.
-            ++cursor;
-        }
-        break;
-      default:
-        // Arbitrary text.
-        ++cursor;
-    }
-  }
-  llvm_unreachable("Loop returns directly");
-}
-
-auto Interpreter::StepInstantiateType() -> ErrorOr<Success> {
-  const Action& act = todo_.CurrentAction();
-  const Nonnull<const Value*> type = cast<TypeInstantiationAction>(act).type();
-  SourceLocation source_loc = cast<TypeInstantiationAction>(act).source_loc();
-
-  switch (type->kind()) {
-    case Value::Kind::NominalClassType: {
-      const auto& class_type = cast<NominalClassType>(*type);
-      std::optional<Nonnull<const NominalClassType*>> base = class_type.base();
-      if (act.pos() == 0 && base.has_value()) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            base.value(), source_loc));
-      } else {
-        if (base.has_value()) {
-          base = cast<NominalClassType>(act.results().back());
-        }
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Bindings*> bindings,
-            InstantiateBindings(&class_type.bindings(), source_loc));
-        return todo_.FinishAction(arena_->New<NominalClassType>(
-            &class_type.declaration(), bindings, base, &class_type.vtable()));
-      }
-    }
-    case Value::Kind::PointerType: {
-      const auto* ptr = cast<PointerType>(type);
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            &ptr->pointee_type(), source_loc));
-      } else {
-        const auto* actual_type = act.results().back();
-        return todo_.FinishAction(arena_->New<PointerType>(actual_type));
-      }
-    }
-    default:
-      CARBON_ASSIGN_OR_RETURN(auto inst_type, InstantiateType(type, source_loc))
-      return todo_.FinishAction(inst_type);
-  }
-}
-
-auto Interpreter::StepValueExp() -> ErrorOr<Success> {
-  auto& act = cast<ValueExpressionAction>(todo_.CurrentAction());
-
-  if (act.pos() == 0) {
-    return todo_.Spawn(std::make_unique<ExpressionAction>(
-        &act.expression(), /*preserve_nested_categories=*/false,
-        act.location_received()));
-  } else {
-    CARBON_CHECK(act.results().size() == 1);
-    if (const auto* expr_value =
-            dyn_cast<ReferenceExpressionValue>(act.results()[0])) {
-      // Unwrap the ExpressionAction to only keep the resulting
-      // `Value*`.
-      return todo_.FinishAction(expr_value->value());
-    } else {
-      return todo_.FinishAction(act.results()[0]);
-    }
-  }
-}
-
-auto Interpreter::StepExp() -> ErrorOr<Success> {
-  auto& act = cast<ExpressionAction>(todo_.CurrentAction());
-  const Expression& exp = act.expression();
-
-  switch (exp.kind()) {
-    case ExpressionKind::IndexExpression: {
-      if (act.pos() == 0) {
-        //    { { e[i] :: C, E, F} :: S, H}
-        // -> { { e :: [][i] :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<IndexExpression>(exp).object()));
-      } else if (act.pos() == 1) {
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<IndexExpression>(exp).offset()));
-      } else {
-        //    { { v :: [][i] :: C, E, F} :: S, H}
-        // -> { { v_i :: C, E, F} : S, H}
-        CARBON_ASSIGN_OR_RETURN(
-            auto converted,
-            Convert(act.results()[0],
-                    &cast<IndexExpression>(exp).object().static_type(),
-                    exp.source_loc()));
-        const auto& tuple = cast<TupleValue>(*converted);
-        int i = cast<IntValue>(*act.results()[1]).value();
-        if (i < 0 || i >= static_cast<int>(tuple.elements().size())) {
-          return ProgramError(exp.source_loc())
-                 << "index " << i << " out of range in " << tuple;
-        }
-        return todo_.FinishAction(tuple.elements()[i]);
-      }
-    }
-    case ExpressionKind::TupleLiteral: {
-      if (act.pos() <
-          static_cast<int>(cast<TupleLiteral>(exp).fields().size())) {
-        //    { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S,
-        //    H}
-        // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
-        // H}
-        const auto* field = cast<TupleLiteral>(exp).fields()[act.pos()];
-        if (act.preserve_nested_categories()) {
-          return todo_.Spawn(std::make_unique<ExpressionAction>(field, false));
-        } else {
-          return todo_.Spawn(std::make_unique<ValueExpressionAction>(field));
-        }
-      } else {
-        return todo_.FinishAction(arena_->New<TupleValue>(act.results()));
-      }
-    }
-    case ExpressionKind::StructLiteral: {
-      const auto& literal = cast<StructLiteral>(exp);
-      if (act.pos() < static_cast<int>(literal.fields().size())) {
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &literal.fields()[act.pos()].expression()));
-      } else {
-        return todo_.FinishAction(
-            CreateStruct(literal.fields(), act.results()));
-      }
-    }
-    case ExpressionKind::SimpleMemberAccessExpression: {
-      const auto& access = cast<SimpleMemberAccessExpression>(exp);
-      if (auto rewrite = access.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
-            *rewrite, act.preserve_nested_categories(),
-            act.location_received()));
-      }
-      if (act.pos() == 0) {
-        // First, evaluate the first operand.
-        if (access.is_addr_me_method()) {
-          return todo_.Spawn(
-              std::make_unique<LocationAction>(&access.object()));
-        } else {
-          return todo_.Spawn(std::make_unique<ExpressionAction>(
-              &access.object(), /*preserve_nested_categories=*/false));
-        }
-      } else {
-        if (auto constant_value = access.constant_value()) {
-          if (act.pos() == 1) {
-            return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                *constant_value, access.source_loc()));
-          } else {
-            return todo_.FinishAction(act.results().back());
-          }
-        } else if (const auto* member_name_type =
-                       dyn_cast<TypeOfMemberName>(&access.static_type())) {
-          // The result is a member name, such as in `Type.field_name`. Form a
-          // suitable member name value.
-          CARBON_CHECK(phase() == Phase::CompileTime,
-                       "should not form MemberNames at runtime");
-          auto found_in_interface = access.found_in_interface();
-          if (act.pos() == 1 && found_in_interface) {
-            return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                *found_in_interface, exp.source_loc()));
-          } else {
-            if (found_in_interface) {
-              found_in_interface = cast<InterfaceType>(act.results().back());
-            }
-            std::optional<const Value*> type_result;
-            const auto* result =
-                act.results()[0]->kind() ==
-                        Value::Kind::ReferenceExpressionValue
-                    ? cast<ReferenceExpressionValue>(act.results()[0])->value()
-                    : act.results()[0];
-            if (!isa<InterfaceType, NamedConstraintType, ConstraintType>(
-                    result)) {
-              type_result = result;
-            }
-            const auto* member_name = arena_->New<MemberName>(
-                type_result, found_in_interface, &member_name_type->member());
-            return todo_.FinishAction(member_name);
-          }
-        } else {
-          // The result is the value of the named field, such as in
-          // `value.field_name`. Extract the value within the given object.
-          auto impl_has_value = access.impl().has_value();
-          if (act.pos() == 1) {
-            // Next, if we're accessing an interface member, evaluate the `impl`
-            // expression to find the corresponding witness.
-            if (impl_has_value) {
-              return todo_.Spawn(std::make_unique<WitnessAction>(
-                  access.impl().value(), access.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else if (act.pos() == 2) {
-            if (auto found_in_interface = access.found_in_interface()) {
-              return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                  *found_in_interface, exp.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else if (act.pos() == 3) {
-            if (access.is_type_access()) {
-              return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                  &access.object().static_type(), access.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else {
-            auto found_in_interface = access.found_in_interface();
-            if (found_in_interface) {
-              found_in_interface = cast<InterfaceType>(
-                  impl_has_value ? act.results()[2] : act.results()[1]);
-            }
-            std::optional<Nonnull<const Witness*>> witness;
-            if (access.impl().has_value()) {
-              witness = cast<Witness>(act.results()[1]);
-            }
-            ElementPath::Component member(&access.member(), found_in_interface,
-                                          witness);
-            const Value* aggregate;
-            std::optional<Nonnull<const Value*>> me_value;
-            std::optional<Address> lhs_address;
-            if (access.is_type_access()) {
-              aggregate = act.results().back();
-            } else if (const auto* location =
-                           dyn_cast<LocationValue>(act.results()[0])) {
-              lhs_address = location->address();
-              me_value = act.results()[0];
-              CARBON_ASSIGN_OR_RETURN(
-                  aggregate,
-                  this->heap_.Read(location->address(), exp.source_loc()));
-            } else if (const auto* expr_value =
-                           dyn_cast<ReferenceExpressionValue>(
-                               act.results()[0])) {
-              lhs_address = expr_value->address();
-              aggregate = expr_value->value();
-              me_value = aggregate;
-            } else {
-              aggregate = act.results()[0];
-              me_value = aggregate;
-            }
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> member_value,
-                aggregate->GetElement(arena_, ElementPath(member),
-                                      exp.source_loc(), me_value));
-            if (lhs_address) {
-              return todo_.FinishAction(arena_->New<ReferenceExpressionValue>(
-                  member_value, lhs_address->ElementAddress(member.element())));
-            } else {
-              return todo_.FinishAction(member_value);
-            }
-          }
-        }
-      }
-    }
-    case ExpressionKind::CompoundMemberAccessExpression: {
-      const auto& access = cast<CompoundMemberAccessExpression>(exp);
-      bool forming_member_name = isa<TypeOfMemberName>(&access.static_type());
-      if (act.pos() == 0) {
-        // First, evaluate the first operand.
-        if (access.is_addr_me_method()) {
-          return todo_.Spawn(
-              std::make_unique<LocationAction>(&access.object()));
-        } else {
-          return todo_.Spawn(
-              std::make_unique<ValueExpressionAction>(&access.object()));
-        }
-      } else {
-        if (auto constant_value = access.constant_value()) {
-          if (act.pos() == 1) {
-            return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                *constant_value, access.source_loc()));
-          } else {
-            return todo_.FinishAction(act.results().back());
-          }
-        } else if (forming_member_name) {
-          CARBON_CHECK(phase() == Phase::CompileTime,
-                       "should not form MemberNames at runtime");
-          if (auto found_in_interface = access.member().interface();
-              found_in_interface && act.pos() == 1) {
-            return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                *found_in_interface, exp.source_loc()));
-          } else {
-            // If we're forming a member name, we must be in the outer
-            // evaluation in `Type.(Interface.method)`. Produce the same method
-            // name with its `type` field set.
-            if (found_in_interface) {
-              found_in_interface = cast<InterfaceType>(act.results().back());
-            }
-            CARBON_CHECK(!access.member().base_type().has_value(),
-                         "compound member access forming a member name should "
-                         "be performing impl lookup");
-            auto* member_name =
-                arena_->New<MemberName>(act.results()[0], found_in_interface,
-                                        &access.member().member());
-            return todo_.FinishAction(member_name);
-          }
-        } else {
-          auto impl_has_value = access.impl().has_value();
-          if (act.pos() == 1) {
-            if (impl_has_value) {
-              // Next, if we're accessing an interface member, evaluate the
-              // `impl` expression to find the corresponding witness.
-              return todo_.Spawn(std::make_unique<WitnessAction>(
-                  access.impl().value(), access.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else if (act.pos() == 2) {
-            if (auto found_in_interface = access.member().interface()) {
-              return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                  *found_in_interface, exp.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else if (act.pos() == 3) {
-            if (access.is_type_access()) {
-              return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-                  &access.object().static_type(), access.source_loc()));
-            } else {
-              return todo_.RunAgain();
-            }
-          } else {
-            // Access the object to find the named member.
-            auto found_in_interface = access.member().interface();
-            if (found_in_interface) {
-              found_in_interface = cast<InterfaceType>(
-                  impl_has_value ? act.results()[2] : act.results()[1]);
-            }
-
-            Nonnull<const Value*> object = act.results()[0];
-            if (access.is_type_access()) {
-              object = act.results().back();
-            }
-            std::optional<Nonnull<const Witness*>> witness;
-            if (access.impl().has_value()) {
-              witness = cast<Witness>(act.results()[1]);
-            } else {
-              CARBON_CHECK(access.member().base_type().has_value(),
-                           "compound access should have base type or impl");
-              CARBON_ASSIGN_OR_RETURN(
-                  object, Convert(object, *access.member().base_type(),
-                                  exp.source_loc()));
-            }
-            ElementPath::Component field(&access.member().member(),
-                                         found_in_interface, witness);
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> member,
-                object->GetElement(arena_, ElementPath(field), exp.source_loc(),
-                                   object));
-            return todo_.FinishAction(member);
-          }
-        }
-      }
-    }
-    case ExpressionKind::BaseAccessExpression: {
-      const auto& access = cast<BaseAccessExpression>(exp);
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&access.object()));
-      } else {
-        ElementPath::Component base_elt(&access.element(), std::nullopt,
-                                        std::nullopt);
-        const Value* value = act.results()[0];
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base_value,
-                                value->GetElement(arena_, ElementPath(base_elt),
-                                                  exp.source_loc(), value));
-        return todo_.FinishAction(base_value);
-      }
-    }
-    case ExpressionKind::IdentifierExpression: {
-      CARBON_CHECK(act.pos() == 0);
-      const auto& ident = cast<IdentifierExpression>(exp);
-      // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> value,
-          todo_.ValueOfNode(ident.value_node(), ident.source_loc()));
-      if (const auto* location = dyn_cast<LocationValue>(value)) {
-        CARBON_ASSIGN_OR_RETURN(
-            value, heap_.Read(location->address(), exp.source_loc()));
-        if (ident.expression_category() == ExpressionCategory::Reference) {
-          return todo_.FinishAction(arena_->New<ReferenceExpressionValue>(
-              value, location->address()));
-        }
-      }
-      return todo_.FinishAction(value);
-    }
-    case ExpressionKind::DotSelfExpression: {
-      CARBON_CHECK(act.pos() == 0);
-      const auto& dot_self = cast<DotSelfExpression>(exp);
-      return todo_.FinishAction(*dot_self.self_binding().symbolic_identity());
-    }
-    case ExpressionKind::IntLiteral:
-      CARBON_CHECK(act.pos() == 0);
-      // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
-      return todo_.FinishAction(
-          arena_->New<IntValue>(cast<IntLiteral>(exp).value()));
-    case ExpressionKind::BoolLiteral:
-      CARBON_CHECK(act.pos() == 0);
-      // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
-      return todo_.FinishAction(
-          arena_->New<BoolValue>(cast<BoolLiteral>(exp).value()));
-    case ExpressionKind::OperatorExpression: {
-      const auto& op = cast<OperatorExpression>(exp);
-      if (auto rewrite = op.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
-            *rewrite, act.preserve_nested_categories(),
-            act.location_received()));
-      }
-      if (act.pos() != static_cast<int>(op.arguments().size())) {
-        //    { {v :: op(vs,[],e,es) :: C, E, F} :: S, H}
-        // -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H}
-        Nonnull<const Expression*> arg = op.arguments()[act.pos()];
-        if (op.op() == Operator::AddressOf) {
-          return todo_.Spawn(std::make_unique<LocationAction>(arg));
-        } else if ((op.op() == Operator::And || op.op() == Operator::Or) &&
-                   act.pos() == 1) {
-          // Short-circuit evaluation for 'and' & 'or'
-          const auto* operand_value =
-              cast<BoolValue>(act.results()[act.pos() - 1]);
-          if ((op.op() == Operator::Or && operand_value->value()) ||
-              (op.op() == Operator::And && !operand_value->value())) {
-            return todo_.FinishAction(operand_value);
-          }
-          // No short-circuit, fall through to evaluate 2nd operand.
-        }
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(arg));
-      } else {
-        //    { {v :: op(vs,[]) :: C, E, F} :: S, H}
-        // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H}
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value,
-                                EvalPrim(op.op(), &op.static_type(),
-                                         act.results(), exp.source_loc()));
-        return todo_.FinishAction(value);
-      }
-    }
-    case ExpressionKind::CallExpression: {
-      const auto& call = cast<CallExpression>(exp);
-      CARBON_CHECK(call.argument().kind() == ExpressionKind::TupleLiteral);
-      const auto& args = cast<TupleLiteral>(call.argument());
-      const int num_args = args.fields().size();
-      const int num_witnesses = call.witnesses().size();
-      const int function_call_pos = 1 + num_args + num_witnesses;
-      if (act.pos() == 0) {
-        //    { {e1(e2) :: C, E, F} :: S, H}
-        // -> { {e1 :: [](e2) :: C, E, F} :: S, H}
-        act.StartScope(RuntimeScope(&heap_));
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&call.function()));
-      } else if (act.pos() < 1 + num_args) {
-        const auto* field = args.fields()[act.pos() - 1];
-        std::optional<AllocationId> alloc;
-        if (field->expression_category() == ExpressionCategory::Initializing) {
-          alloc = heap_.AllocateValue(
-              arena_->New<UninitializedValue>(&field->static_type()));
-          act.scope()->BindLifetimeToScope(Address(*alloc));
-        }
-        return todo_.Spawn(
-            std::make_unique<ExpressionAction>(field, false, alloc));
-      } else if (act.pos() < function_call_pos) {
-        auto iter = call.witnesses().begin();
-        std::advance(iter, act.pos() - 1 - num_args);
-        return todo_.Spawn(std::make_unique<WitnessAction>(
-            cast<Witness>(iter->second), call.source_loc()));
-      } else if (act.pos() == function_call_pos) {
-        //    { { v2 :: v1([]) :: C, E, F} :: S, H}
-        // -> { {C',E',F'} :: {C, E, F} :: S, H}
-        // Prepare parameters tuple.
-        std::vector<Nonnull<const Value*>> param_values;
-        for (const auto& arg_result :
-             llvm::ArrayRef(act.results()).slice(1, num_args)) {
-          param_values.push_back(arg_result);
-        }
-        const auto* param_tuple = arena_->New<TupleValue>(param_values);
-        // Prepare witnesses.
-        ImplWitnessMap witnesses;
-        if (num_witnesses > 0) {
-          for (const auto [witness, result] : llvm::zip(
-                   call.witnesses(),
-                   llvm::ArrayRef(act.results()).drop_front(1 + num_args))) {
-            witnesses[witness.first] = result;
-          }
-        }
-        return CallFunction(call, act.results()[0], param_tuple,
-                            std::move(witnesses), act.location_received());
-      } else if (act.pos() == 1 + function_call_pos) {
-        if (static_cast<int>(act.results().size()) < 1 + function_call_pos) {
-          // Control fell through without explicit return.
-          return todo_.FinishAction(TupleValue::Empty());
-        } else {
-          return todo_.FinishAction(act.results()[function_call_pos]);
-        }
-      } else {
-        CARBON_FATAL("in StepValueExp with Call pos {0}", act.pos());
-      }
-    }
-    case ExpressionKind::IntrinsicExpression: {
-      const auto& intrinsic = cast<IntrinsicExpression>(exp);
-      if (auto rewrite = intrinsic.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
-            *rewrite, act.preserve_nested_categories(),
-            act.location_received()));
-      }
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&intrinsic.args()));
-      }
-      // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
-      const auto& args = cast<TupleValue>(*act.results()[0]).elements();
-      switch (cast<IntrinsicExpression>(exp).intrinsic()) {
-        case IntrinsicExpression::Intrinsic::Print: {
-          if (phase_ != Phase::RunTime) {
-            return ProgramError(exp.source_loc())
-                   << "Print called before run time";
-          }
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> format_string_value,
-              Convert(args[0], arena_->New<StringType>(), exp.source_loc()));
-          const char* format_string =
-              cast<StringValue>(*format_string_value).value().c_str();
-          int num_format_args = args.size() - 1;
-          CARBON_RETURN_IF_ERROR(ValidateFormatString(
-              intrinsic.source_loc(), format_string, num_format_args));
-          switch (num_format_args) {
-            case 0:
-              *print_stream_ << llvm::formatv(format_string);
-              break;
-            case 1: {
-              *print_stream_ << llvm::formatv(format_string,
-                                              cast<IntValue>(*args[1]).value());
-              break;
-            }
-            default:
-              CARBON_FATAL("Too many format args: {0}", num_format_args);
-          }
-          // Implicit newline; currently no way to disable it.
-          *print_stream_ << "\n";
-          return todo_.FinishAction(TupleValue::Empty());
-        }
-        case IntrinsicExpression::Intrinsic::Assert: {
-          CARBON_CHECK(args.size() == 2);
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> condition,
-              Convert(args[0], arena_->New<BoolType>(), exp.source_loc()));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> string_value,
-              Convert(args[1], arena_->New<StringType>(), exp.source_loc()));
-          bool condition_value = cast<BoolValue>(condition)->value();
-          if (!condition_value) {
-            return ProgramError(exp.source_loc()) << *string_value;
-          }
-          return todo_.FinishAction(TupleValue::Empty());
-        }
-        case IntrinsicExpression::Intrinsic::Alloc: {
-          CARBON_CHECK(args.size() == 1);
-          Address addr(heap_.AllocateValue(args[0]));
-          return todo_.FinishAction(arena_->New<PointerValue>(addr));
-        }
-        case IntrinsicExpression::Intrinsic::Dealloc: {
-          CARBON_CHECK(args.size() == 1);
-          CARBON_CHECK(act.pos() > 0);
-          const auto* ptr = cast<PointerValue>(args[0]);
-          CARBON_ASSIGN_OR_RETURN(const auto* pointee,
-                                  heap_.Read(ptr->address(), exp.source_loc()));
-          if (const auto* class_value = dyn_cast<NominalClassValue>(pointee)) {
-            // Handle destruction from base class pointer.
-            const auto* child_class_value = *class_value->class_value_ptr();
-            bool is_subtyped = child_class_value != class_value;
-            if (is_subtyped) {
-              // Error if destructor is not virtual.
-              const auto& class_type =
-                  cast<NominalClassType>(class_value->type());
-              const auto& class_decl = class_type.declaration();
-              if ((*class_decl.destructor())->virt_override() ==
-                  VirtualOverride::None) {
-                return ProgramError(exp.source_loc())
-                       << "Deallocating a derived class from base class "
-                          "pointer requires a virtual destructor";
-              }
-            }
-            const Address obj_addr = is_subtyped
-                                         ? ptr->address().DowncastedAddress()
-                                         : ptr->address();
-            if (act.pos() == 1) {
-              return todo_.Spawn(std::make_unique<DestroyAction>(
-                  arena_->New<LocationValue>(obj_addr), child_class_value));
-            } else {
-              CARBON_RETURN_IF_ERROR(heap_.Deallocate(obj_addr));
-              return todo_.FinishAction(TupleValue::Empty());
-            }
-          } else {
-            if (act.pos() == 1) {
-              return todo_.Spawn(std::make_unique<DestroyAction>(
-                  arena_->New<LocationValue>(ptr->address()), pointee));
-            } else {
-              CARBON_RETURN_IF_ERROR(heap_.Deallocate(ptr->address()));
-              return todo_.FinishAction(TupleValue::Empty());
-            }
-          }
-        }
-        case IntrinsicExpression::Intrinsic::PrintAllocs: {
-          CARBON_CHECK(args.empty());
-          heap_.Print(*print_stream_);
-          *print_stream_ << "\n";
-          return todo_.FinishAction(TupleValue::Empty());
-        }
-        case IntrinsicExpression::Intrinsic::Rand: {
-          CARBON_CHECK(args.size() == 2);
-          const int64_t low = cast<IntValue>(*args[0]).value();
-          const int64_t high = cast<IntValue>(*args[1]).value();
-          if (low >= high) {
-            return ProgramError(exp.source_loc())
-                   << "Rand inputs must be ordered for a non-empty range: "
-                   << low << " must be less than " << high;
-          }
-          // Use 64-bit to handle large ranges where `high - low` might exceed
-          // int32_t maximums.
-          static std::mt19937_64 generator(12);
-          const int64_t range = high - low;
-          // We avoid using std::uniform_int_distribution because it's not
-          // reproducible across builds/platforms.
-          int64_t r = (generator() % range) + low;
-          CARBON_CHECK(r >= std::numeric_limits<int32_t>::min() &&
-                           r <= std::numeric_limits<int32_t>::max(),
-                       "Non-int32 result: {0}", r);
-          CARBON_CHECK(r >= low && r <= high, "Out-of-range result: {0}", r);
-          return todo_.FinishAction(arena_->New<IntValue>(r));
-        }
-        case IntrinsicExpression::Intrinsic::ImplicitAs: {
-          CARBON_CHECK(args.size() == 1);
-          // Build a constraint type that constrains its .Self type to satisfy
-          // the "ImplicitAs" intrinsic constraint. This involves creating a
-          // number of objects that all point to each other.
-          // TODO: Factor out a simple version of ConstraintTypeBuilder and
-          // use it from here.
-          auto* self_binding = arena_->New<GenericBinding>(
-              exp.source_loc(), ".Self",
-              arena_->New<TypeTypeLiteral>(exp.source_loc()),
-              GenericBinding::BindingKind::Checked);
-          auto* self = arena_->New<VariableType>(self_binding);
-          auto* impl_binding = arena_->New<ImplBinding>(
-              exp.source_loc(), self_binding, std::nullopt);
-          impl_binding->set_symbolic_identity(
-              arena_->New<BindingWitness>(impl_binding));
-          self_binding->set_symbolic_identity(self);
-          self_binding->set_value(self);
-          self_binding->set_impl_binding(impl_binding);
-          IntrinsicConstraint constraint(self, IntrinsicConstraint::ImplicitAs,
-                                         args);
-          auto* result = arena_->New<ConstraintType>(
-              self_binding, std::vector<ImplsConstraint>{},
-              std::vector<IntrinsicConstraint>{std::move(constraint)},
-              std::vector<EqualityConstraint>{},
-              std::vector<RewriteConstraint>{}, std::vector<LookupContext>{});
-          impl_binding->set_interface(result);
-          return todo_.FinishAction(result);
-        }
-        case IntrinsicExpression::Intrinsic::ImplicitAsConvert: {
-          CARBON_FATAL(
-              "__intrinsic_implicit_as_convert should have been rewritten");
-        }
-        case IntrinsicExpression::Intrinsic::IntEq: {
-          CARBON_CHECK(args.size() == 2);
-          auto lhs = cast<IntValue>(*args[0]).value();
-          auto rhs = cast<IntValue>(*args[1]).value();
-          auto* result = arena_->New<BoolValue>(lhs == rhs);
-          return todo_.FinishAction(result);
-        }
-        case IntrinsicExpression::Intrinsic::StrEq: {
-          CARBON_CHECK(args.size() == 2);
-          const auto& lhs = cast<StringValue>(*args[0]).value();
-          const auto& rhs = cast<StringValue>(*args[1]).value();
-          auto* result = arena_->New<BoolValue>(lhs == rhs);
-          return todo_.FinishAction(result);
-        }
-        case IntrinsicExpression::Intrinsic::IntCompare: {
-          CARBON_CHECK(args.size() == 2);
-          auto lhs = cast<IntValue>(*args[0]).value();
-          auto rhs = cast<IntValue>(*args[1]).value();
-          if (lhs < rhs) {
-            auto* result = arena_->New<IntValue>(-1);
-            return todo_.FinishAction(result);
-          }
-          if (lhs == rhs) {
-            auto* result = arena_->New<IntValue>(0);
-            return todo_.FinishAction(result);
-          }
-          auto* result = arena_->New<IntValue>(1);
-          return todo_.FinishAction(result);
-        }
-        case IntrinsicExpression::Intrinsic::StrCompare: {
-          CARBON_CHECK(args.size() == 2);
-          const auto& lhs = cast<StringValue>(*args[0]).value();
-          const auto& rhs = cast<StringValue>(*args[1]).value();
-          if (lhs < rhs) {
-            auto* result = arena_->New<IntValue>(-1);
-            return todo_.FinishAction(result);
-          }
-          if (lhs == rhs) {
-            auto* result = arena_->New<IntValue>(0);
-            return todo_.FinishAction(result);
-          }
-          auto* result = arena_->New<IntValue>(1);
-          return todo_.FinishAction(result);
-        }
-        case IntrinsicExpression::Intrinsic::IntBitComplement: {
-          CARBON_CHECK(args.size() == 1);
-          return todo_.FinishAction(
-              arena_->New<IntValue>(~cast<IntValue>(*args[0]).value()));
-        }
-        case IntrinsicExpression::Intrinsic::IntBitAnd: {
-          CARBON_CHECK(args.size() == 2);
-          return todo_.FinishAction(
-              arena_->New<IntValue>(cast<IntValue>(*args[0]).value() &
-                                    cast<IntValue>(*args[1]).value()));
-        }
-        case IntrinsicExpression::Intrinsic::IntBitOr: {
-          CARBON_CHECK(args.size() == 2);
-          return todo_.FinishAction(
-              arena_->New<IntValue>(cast<IntValue>(*args[0]).value() |
-                                    cast<IntValue>(*args[1]).value()));
-        }
-        case IntrinsicExpression::Intrinsic::IntBitXor: {
-          CARBON_CHECK(args.size() == 2);
-          return todo_.FinishAction(
-              arena_->New<IntValue>(cast<IntValue>(*args[0]).value() ^
-                                    cast<IntValue>(*args[1]).value()));
-        }
-        case IntrinsicExpression::Intrinsic::IntLeftShift: {
-          CARBON_CHECK(args.size() == 2);
-          const auto& lhs = cast<IntValue>(*args[0]).value();
-          const auto& rhs = cast<IntValue>(*args[1]).value();
-          if (rhs >= 0 && rhs < 32) {
-            return todo_.FinishAction(
-                arena_->New<IntValue>(static_cast<uint32_t>(lhs) << rhs));
-          }
-          return ProgramError(exp.source_loc()) << "Integer overflow";
-        }
-        case IntrinsicExpression::Intrinsic::IntRightShift: {
-          CARBON_CHECK(args.size() == 2);
-          const auto& lhs = cast<IntValue>(*args[0]).value();
-          const auto& rhs = cast<IntValue>(*args[1]).value();
-          if (rhs >= 0 && rhs < 32) {
-            return todo_.FinishAction(arena_->New<IntValue>(lhs >> rhs));
-          }
-          return ProgramError(exp.source_loc()) << "Integer overflow";
-        }
-      }
-    }
-    case ExpressionKind::IntTypeLiteral: {
-      CARBON_CHECK(act.pos() == 0);
-      return todo_.FinishAction(arena_->New<IntType>());
-    }
-    case ExpressionKind::BoolTypeLiteral: {
-      CARBON_CHECK(act.pos() == 0);
-      return todo_.FinishAction(arena_->New<BoolType>());
-    }
-    case ExpressionKind::TypeTypeLiteral: {
-      CARBON_CHECK(act.pos() == 0);
-      return todo_.FinishAction(arena_->New<TypeType>());
-    }
-    case ExpressionKind::StringLiteral:
-      CARBON_CHECK(act.pos() == 0);
-      // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
-      return todo_.FinishAction(
-          arena_->New<StringValue>(cast<StringLiteral>(exp).value()));
-    case ExpressionKind::StringTypeLiteral: {
-      CARBON_CHECK(act.pos() == 0);
-      return todo_.FinishAction(arena_->New<StringType>());
-    }
-    case ExpressionKind::FunctionTypeLiteral:
-    case ExpressionKind::StructTypeLiteral:
-    case ExpressionKind::ArrayTypeLiteral:
-    case ExpressionKind::ValueLiteral: {
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            &exp.static_type(), exp.source_loc()));
-      } else {
-        const auto* value = &cast<ConstantValueLiteral>(exp).constant_value();
-        Nonnull<const Value*> destination = act.results().back();
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> result,
-                                Convert(value, destination, exp.source_loc()));
-        return todo_.FinishAction(result);
-      }
-    }
-    case ExpressionKind::IfExpression: {
-      const auto& if_expr = cast<IfExpression>(exp);
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&if_expr.condition()));
-      } else if (act.pos() == 1) {
-        const auto& condition = cast<BoolValue>(*act.results()[0]);
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            condition.value() ? &if_expr.then_expression()
-                              : &if_expr.else_expression()));
-      } else {
-        return todo_.FinishAction(act.results()[1]);
-      }
-      break;
-    }
-    case ExpressionKind::WhereExpression: {
-      auto rewrite = cast<WhereExpression>(exp).rewritten_form();
-      CARBON_CHECK(rewrite, "where expression should be rewritten");
-      return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
-          *rewrite, act.preserve_nested_categories(), act.location_received()));
-    }
-    case ExpressionKind::BuiltinConvertExpression: {
-      const auto& convert_expr = cast<BuiltinConvertExpression>(exp);
-      if (auto rewrite = convert_expr.rewritten_form()) {
-        return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
-            *rewrite, act.preserve_nested_categories(),
-            act.location_received()));
-      }
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            convert_expr.source_expression()));
-      } else if (act.pos() == 1) {
-        return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
-            &convert_expr.static_type(), convert_expr.source_loc()));
-      } else {
-        // TODO: Remove all calls to Convert other than this one. We shouldn't
-        // need them any more.
-        Nonnull<const Value*> destination = act.results().back();
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> result,
-            Convert(act.results()[0], destination, convert_expr.source_loc()));
-        return todo_.FinishAction(result);
-      }
-    }
-    case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL("Unimplemented: {0}", exp);
-  }  // switch (exp->kind)
-}
-
-auto Interpreter::StepWitness() -> ErrorOr<Success> {
-  auto& act = cast<WitnessAction>(todo_.CurrentAction());
-  const Witness* witness = act.witness();
-
-  switch (witness->kind()) {
-    case Value::Kind::BindingWitness: {
-      const ImplBinding* binding = cast<BindingWitness>(witness)->binding();
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> value,
-          todo_.ValueOfNode(binding, binding->type_var()->source_loc()));
-      if (const auto* location = dyn_cast<LocationValue>(value)) {
-        // TODO: Why do we store values for impl bindings on the heap?
-        CARBON_ASSIGN_OR_RETURN(
-            value,
-            heap_.Read(location->address(), binding->type_var()->source_loc()));
-      }
-      return todo_.FinishAction(value);
-    }
-
-    case Value::Kind::ConstraintWitness: {
-      llvm::ArrayRef<Nonnull<const Witness*>> witnesses =
-          cast<ConstraintWitness>(witness)->witnesses();
-      if (act.pos() < static_cast<int>(witnesses.size())) {
-        return todo_.Spawn(std::make_unique<WitnessAction>(witnesses[act.pos()],
-                                                           act.source_loc()));
-      }
-      std::vector<Nonnull<const Witness*>> new_witnesses;
-      new_witnesses.reserve(witnesses.size());
-      for (const auto* witness : act.results()) {
-        new_witnesses.push_back(cast<Witness>(witness));
-      }
-      return todo_.FinishAction(
-          arena_->New<ConstraintWitness>(std::move(new_witnesses)));
-    }
-
-    case Value::Kind::ConstraintImplWitness: {
-      const auto* constraint_impl = cast<ConstraintImplWitness>(witness);
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<WitnessAction>(
-            constraint_impl->constraint_witness(), act.source_loc()));
-      }
-      return todo_.FinishAction(ConstraintImplWitness::Make(
-          arena_, cast<Witness>(act.results()[0]), constraint_impl->index()));
-    }
-
-    case Value::Kind::ImplWitness: {
-      const auto* impl_witness = cast<ImplWitness>(witness);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Bindings*> new_bindings,
-          InstantiateBindings(&impl_witness->bindings(),
-                              impl_witness->declaration().source_loc()));
-      return todo_.FinishAction(
-          new_bindings == &impl_witness->bindings()
-              ? impl_witness
-              : arena_->New<ImplWitness>(&impl_witness->declaration(),
-                                         new_bindings));
-    }
-
-    default:
-      CARBON_FATAL("unexpected kind of witness {0}", *witness);
-  }
-}
-
-auto Interpreter::StepStmt() -> ErrorOr<Success> {
-  auto& act = cast<StatementAction>(todo_.CurrentAction());
-  const Statement& stmt = act.statement();
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Source() << "statement at (" << stmt.source_loc() << ")\n";
-    *trace_stream_ << "```\n" << stmt << "\n```\n";
-  }
-
-  switch (stmt.kind()) {
-    case StatementKind::Match: {
-      const auto& match_stmt = cast<Match>(stmt);
-      if (act.pos() == 0) {
-        //    { { (match (e) ...) :: C, E, F} :: S, H}
-        // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H}
-        act.StartScope(RuntimeScope(&heap_));
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&match_stmt.expression()));
-      } else {
-        int clause_num = act.pos() - 1;
-        if (clause_num >= static_cast<int>(match_stmt.clauses().size())) {
-          return todo_.FinishAction();
-        }
-        auto c = match_stmt.clauses()[clause_num];
-        RuntimeScope matches(&heap_);
-        BindingMap generic_args;
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> val,
-            Convert(act.results()[0], &c.pattern().static_type(),
-                    stmt.source_loc()));
-        if (PatternMatch(&c.pattern().value(), ExpressionResult::Value(val),
-                         stmt.source_loc(), &matches, generic_args,
-                         trace_stream_, this->arena_)) {
-          // Ensure we don't process any more clauses.
-          act.set_pos(match_stmt.clauses().size() + 1);
-          todo_.MergeScope(std::move(matches));
-          return todo_.Spawn(
-              std::make_unique<StatementAction>(&c.statement(), std::nullopt));
-        } else {
-          return todo_.RunAgain();
-        }
-      }
-    }
-    case StatementKind::For: {
-      constexpr int TargetVarPosInResult = 0;
-      constexpr int CurrentIndexPosInResult = 1;
-      constexpr int EndIndexPosInResult = 2;
-      const auto* loop_var = &cast<BindingPlaceholderValue>(
-          cast<For>(stmt).variable_declaration().value());
-      if (act.pos() == 0) {
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<For>(stmt).loop_target()));
-      }
-      if (act.pos() == 1) {
-        const auto* source_array =
-            cast<TupleValue>(act.results()[TargetVarPosInResult]);
-
-        int start_index = 0;
-        auto end_index = static_cast<int>(source_array->elements().size());
-        if (end_index == 0) {
-          return todo_.FinishAction();
-        }
-        act.AddResult(arena_->New<IntValue>(start_index));
-        act.AddResult(arena_->New<IntValue>(end_index));
-        todo_.Initialize(*(loop_var->value_node()),
-                         source_array->elements()[start_index]);
-        act.ReplaceResult(CurrentIndexPosInResult,
-                          arena_->New<IntValue>(start_index + 1));
-        return todo_.Spawn(std::make_unique<StatementAction>(
-            &cast<For>(stmt).body(), std::nullopt));
-      }
-      if (act.pos() >= 2) {
-        auto current_index =
-            cast<IntValue>(act.results()[CurrentIndexPosInResult])->value();
-        auto end_index =
-            cast<IntValue>(act.results()[EndIndexPosInResult])->value();
-
-        if (current_index < end_index) {
-          const auto* source_array =
-              cast<const TupleValue>(act.results()[TargetVarPosInResult]);
-
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> assigned_array_element,
-              todo_.ValueOfNode(*(loop_var->value_node()), stmt.source_loc()));
-
-          const auto* location = cast<LocationValue>(assigned_array_element);
-          CARBON_RETURN_IF_ERROR(heap_.Write(
-              location->address(), source_array->elements()[current_index],
-              stmt.source_loc()));
-
-          act.ReplaceResult(CurrentIndexPosInResult,
-                            arena_->New<IntValue>(current_index + 1));
-          return todo_.Spawn(std::make_unique<StatementAction>(
-              &cast<For>(stmt).body(), std::nullopt));
-        }
-      }
-      return todo_.FinishAction();
-    }
-    case StatementKind::While:
-      // TODO: Rewrite While to use ReplaceResult to store condition result.
-      //       This will remove the inconsistency between the while and for
-      //       loops.
-      if (act.pos() % 2 == 0) {
-        //    { { (while (e) s) :: C, E, F} :: S, H}
-        // -> { { e :: (while ([]) s) :: C, E, F} :: S, H}
-        act.Clear();
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<While>(stmt).condition()));
-      } else {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> condition,
-            Convert(act.results().back(), arena_->New<BoolType>(),
-                    stmt.source_loc()));
-        if (cast<BoolValue>(*condition).value()) {
-          //    { {true :: (while ([]) s) :: C, E, F} :: S, H}
-          // -> { { s :: (while (e) s) :: C, E, F } :: S, H}
-          return todo_.Spawn(std::make_unique<StatementAction>(
-              &cast<While>(stmt).body(), std::nullopt));
-        } else {
-          //    { {false :: (while ([]) s) :: C, E, F} :: S, H}
-          // -> { { C, E, F } :: S, H}
-          return todo_.FinishAction();
-        }
-      }
-    case StatementKind::Break: {
-      CARBON_CHECK(act.pos() == 0);
-      //    { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H}
-      // -> { { C, E', F} :: S, H}
-      return todo_.UnwindPast(&cast<Break>(stmt).loop());
-    }
-    case StatementKind::Continue: {
-      CARBON_CHECK(act.pos() == 0);
-      //    { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H}
-      // -> { { (while (e) s) :: C, E', F} :: S, H}
-      return todo_.UnwindTo(&cast<Continue>(stmt).loop());
-    }
-    case StatementKind::Block: {
-      const auto& block = cast<Block>(stmt);
-      if (act.pos() >= static_cast<int>(block.statements().size())) {
-        // If the position is past the end of the block, end processing. Note
-        // that empty blocks immediately end.
-        return todo_.FinishAction();
-      }
-      // Initialize a scope when starting a block.
-      if (act.pos() == 0) {
-        act.StartScope(RuntimeScope(&heap_));
-      }
-      // Process the next statement in the block. The position will be
-      // incremented as part of Spawn.
-      return todo_.Spawn(std::make_unique<StatementAction>(
-          block.statements()[act.pos()], act.location_received()));
-    }
-    case StatementKind::VariableDefinition: {
-      const auto& definition = cast<VariableDefinition>(stmt);
-      const bool has_initializing_expr =
-          definition.has_init() &&
-          definition.init().kind() == ExpressionKind::CallExpression &&
-          definition.init().expression_category() ==
-              ExpressionCategory::Initializing;
-      auto init_location = (act.location_received() && definition.is_returned())
-                               ? act.location_received()
-                               : act.location_created();
-      if (act.pos() == 0 && definition.has_init()) {
-        //    { {(var x = e) :: C, E, F} :: S, H}
-        // -> { {e :: (var x = []) :: C, E, F} :: S, H}
-        if (has_initializing_expr && !init_location) {
-          // Allocate storage for initializing expression.
-          const auto allocation_id =
-              heap_.AllocateValue(arena_->New<UninitializedValue>(
-                  &definition.init().static_type()));
-          act.set_location_created(allocation_id);
-          init_location = allocation_id;
-          RuntimeScope scope(&heap_);
-          scope.BindLifetimeToScope(Address(allocation_id));
-          todo_.MergeScope(std::move(scope));
-        }
-        return todo_.Spawn(std::make_unique<ExpressionAction>(
-            &definition.init(), /*preserve_nested_categories=*/false,
-            init_location));
-      } else {
-        //    { { v :: (x = []) :: C, E, F} :: S, H}
-        // -> { { C, E(x := a), F} :: S, H(a := copy(v))}
-        Nonnull<const Value*> p = &definition.pattern().value();
-        Nonnull<const Value*> v;
-        std::optional<Address> v_location;
-        ExpressionCategory expr_category =
-            definition.has_init() ? definition.init().expression_category()
-                                  : ExpressionCategory::Value;
-        if (definition.has_init()) {
-          Nonnull<const Value*> result = act.results()[0];
-          std::optional<Nonnull<const ReferenceExpressionValue*>> v_expr =
-              (result->kind() == Value::Kind::ReferenceExpressionValue)
-                  ? std::optional{cast<ReferenceExpressionValue>(result)}
-                  : std::nullopt;
-          const auto init_location = act.location_created();
-          v = v_expr ? (*v_expr)->value() : result;
-          if (expr_category == ExpressionCategory::Reference) {
-            CARBON_CHECK(
-                v_expr,
-                "Expecting ReferenceExpressionValue from reference expression");
-            v_location = (*v_expr)->address();
-            CARBON_CHECK(v_location,
-                         "Expecting a valid address from reference expression");
-          } else if (has_initializing_expr && init_location &&
-                     heap_.is_initialized(*init_location)) {
-            // Bind even if a conversion is necessary.
-            v_location = Address(*init_location);
-            CARBON_ASSIGN_OR_RETURN(
-                result, heap_.Read(*v_location, definition.source_loc()));
-            CARBON_CHECK(v == result);
-          } else {
-            // TODO: Prevent copies for Value expressions from Reference
-            // expression, once able to prevent mutations.
-            if (init_location && act.location_created()) {
-              // Location provided to initializing expression was not used.
-              heap_.Discard(*init_location);
-            }
-            expr_category = ExpressionCategory::Value;
-            const auto* dest_type = &definition.pattern().static_type();
-            CARBON_ASSIGN_OR_RETURN(v,
-                                    Convert(v, dest_type, stmt.source_loc()));
-          }
-        } else {
-          v = arena_->New<UninitializedValue>(p);
-        }
-
-        // If declaring a returned var, bind name to the location provided to
-        // initializing expression, if any.
-        RuntimeScope scope(&heap_);
-        if (definition.is_returned() && init_location) {
-          CARBON_CHECK(p->kind() == Value::Kind::BindingPlaceholderValue);
-          const auto value_node =
-              cast<BindingPlaceholderValue>(*p).value_node();
-          CARBON_CHECK(value_node);
-          const auto address = Address(*init_location);
-          scope.Bind(*value_node, address);
-          CARBON_RETURN_IF_ERROR(heap_.Write(address, v, stmt.source_loc()));
-        } else {
-          BindingMap generic_args;
-          bool matched =
-              PatternMatch(p, ExpressionResult(v, v_location, expr_category),
-                           stmt.source_loc(), &scope, generic_args,
-                           trace_stream_, this->arena_);
-          CARBON_CHECK(
-              matched,
-              "{0}: internal error in variable definition, match failed",
-              stmt.source_loc());
-        }
-        todo_.MergeScope(std::move(scope));
-        return todo_.FinishAction();
-      }
-    }
-    case StatementKind::ExpressionStatement:
-      if (act.pos() == 0) {
-        //    { {e :: C, E, F} :: S, H}
-        // -> { {e :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<ExpressionStatement>(stmt).expression()));
-      } else {
-        return todo_.FinishAction();
-      }
-    case StatementKind::Assign: {
-      const auto& assign = cast<Assign>(stmt);
-      if (auto rewrite = assign.rewritten_form()) {
-        if (act.pos() == 0) {
-          return todo_.Spawn(std::make_unique<ValueExpressionAction>(*rewrite));
-        } else {
-          return todo_.FinishAction();
-        }
-      }
-      if (act.pos() == 0) {
-        //    { {(lv = e) :: C, E, F} :: S, H}
-        // -> { {lv :: ([] = e) :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<LocationAction>(&assign.lhs()));
-      } else if (act.pos() == 1) {
-        //    { { a :: ([] = e) :: C, E, F} :: S, H}
-        // -> { { e :: (a = []) :: C, E, F} :: S, H}
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(&assign.rhs()));
-      } else {
-        //    { { v :: (a = []) :: C, E, F} :: S, H}
-        // -> { { C, E, F} :: S, H(a := v)}
-        const auto& lval = cast<LocationValue>(*act.results()[0]);
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> rval,
-            Convert(act.results()[1], &assign.lhs().static_type(),
-                    stmt.source_loc()));
-        CARBON_RETURN_IF_ERROR(
-            heap_.Write(lval.address(), rval, stmt.source_loc()));
-        return todo_.FinishAction();
-      }
-    }
-    case StatementKind::IncrementDecrement: {
-      const auto& inc_dec = cast<IncrementDecrement>(stmt);
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ValueExpressionAction>(*inc_dec.rewritten_form()));
-      } else {
-        return todo_.FinishAction();
-      }
-    }
-    case StatementKind::If:
-      if (act.pos() == 0) {
-        //    { {(if (e) then_stmt else else_stmt) :: C, E, F} :: S, H}
-        // -> { { e :: (if ([]) then_stmt else else_stmt) :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<If>(stmt).condition()));
-      } else if (act.pos() == 1) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> condition,
-            Convert(act.results()[0], arena_->New<BoolType>(),
-                    stmt.source_loc()));
-        if (cast<BoolValue>(*condition).value()) {
-          //    { {true :: if ([]) then_stmt else else_stmt :: C, E, F} ::
-          //      S, H}
-          // -> { { then_stmt :: C, E, F } :: S, H}
-          return todo_.Spawn(std::make_unique<StatementAction>(
-              &cast<If>(stmt).then_block(), std::nullopt));
-        } else if (cast<If>(stmt).else_block()) {
-          //    { {false :: if ([]) then_stmt else else_stmt :: C, E, F} ::
-          //      S, H}
-          // -> { { else_stmt :: C, E, F } :: S, H}
-          return todo_.Spawn(std::make_unique<StatementAction>(
-              *cast<If>(stmt).else_block(), std::nullopt));
-        } else {
-          return todo_.FinishAction();
-        }
-      } else {
-        return todo_.FinishAction();
-      }
-    case StatementKind::ReturnVar: {
-      const auto& ret_var = cast<ReturnVar>(stmt);
-      const ValueNodeView& value_node = ret_var.value_node();
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value,
-                              todo_.ValueOfNode(value_node, stmt.source_loc()));
-      if (const auto* location = dyn_cast<LocationValue>(value)) {
-        CARBON_ASSIGN_OR_RETURN(
-            value, heap_.Read(location->address(), ret_var.source_loc()));
-      }
-      const CallableDeclaration& function = cast<Return>(stmt).function();
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> return_value,
-          Convert(value, &function.return_term().static_type(),
-                  stmt.source_loc()));
-      return todo_.UnwindPast(*function.body(), return_value);
-    }
-    case StatementKind::ReturnExpression:
-      if (act.pos() == 0) {
-        //    { {return e :: C, E, F} :: S, H}
-        // -> { {e :: return [] :: C, E, F} :: S, H}
-        return todo_.Spawn(std::make_unique<ValueExpressionAction>(
-            &cast<ReturnExpression>(stmt).expression()));
-      } else {
-        //    { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H}
-        // -> { {v :: C', E', F'} :: S, H}
-        const CallableDeclaration& function = cast<Return>(stmt).function();
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> return_value,
-            Convert(act.results()[0], &function.return_term().static_type(),
-                    stmt.source_loc()));
-        // Write to initialized storage location, if any.
-        if (const auto location = act.location_received()) {
-          CARBON_RETURN_IF_ERROR(
-              heap_.Write(Address(*location), return_value, stmt.source_loc()));
-        }
-        return todo_.UnwindPast(*function.body(), return_value);
-      }
-  }
-}
-
-auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
-  Action& act = todo_.CurrentAction();
-  const Declaration& decl = cast<DeclarationAction>(act).declaration();
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Source() << "declaration at (" << decl.source_loc() << ")\n";
-    *trace_stream_ << "```\n" << decl << "\n```\n";
-  }
-
-  switch (decl.kind()) {
-    case DeclarationKind::VariableDeclaration: {
-      const auto& var_decl = cast<VariableDeclaration>(decl);
-      if (var_decl.has_initializer()) {
-        if (act.pos() == 0) {
-          return todo_.Spawn(
-              std::make_unique<ValueExpressionAction>(&var_decl.initializer()));
-        } else {
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> v,
-              Convert(act.results()[0], &var_decl.binding().static_type(),
-                      var_decl.source_loc()));
-          todo_.Initialize(&var_decl.binding(), v);
-          return todo_.FinishAction();
-        }
-      } else {
-        Nonnull<const Value*> v =
-            arena_->New<UninitializedValue>(&var_decl.binding().value());
-        todo_.Initialize(&var_decl.binding(), v);
-        return todo_.FinishAction();
-      }
-    }
-    case DeclarationKind::NamespaceDeclaration:
-    case DeclarationKind::DestructorDeclaration:
-    case DeclarationKind::FunctionDeclaration:
-    case DeclarationKind::ClassDeclaration:
-    case DeclarationKind::MixinDeclaration:
-    case DeclarationKind::MixDeclaration:
-    case DeclarationKind::ChoiceDeclaration:
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration:
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration:
-    case DeclarationKind::ImplDeclaration:
-    case DeclarationKind::MatchFirstDeclaration:
-    case DeclarationKind::SelfDeclaration:
-    case DeclarationKind::AliasDeclaration:
-    case DeclarationKind::ExtendBaseDeclaration:
-      // These declarations have no run-time effects.
-      return todo_.FinishAction();
-  }
-}
-
-auto Interpreter::StepDestroy() -> ErrorOr<Success> {
-  const Action& act = todo_.CurrentAction();
-  const auto& destroy_act = cast<DestroyAction>(act);
-
-  switch (destroy_act.value()->kind()) {
-    case Value::Kind::NominalClassValue: {
-      const auto* class_obj = cast<NominalClassValue>(destroy_act.value());
-      const auto& class_decl =
-          cast<NominalClassType>(class_obj->type()).declaration();
-      const int member_count = class_decl.members().size();
-      if (act.pos() == 0) {
-        // Run the destructor, if there is one.
-        if (auto destructor = class_decl.destructor()) {
-          return CallDestructor(
-              *destructor, ExpressionResult::Reference(
-                               class_obj, destroy_act.location()->address()));
-        } else {
-          return todo_.RunAgain();
-        }
-      } else if (act.pos() <= member_count) {
-        // Destroy members.
-        const int index = class_decl.members().size() - act.pos();
-        const auto& member = class_decl.members()[index];
-        if (const auto* var = dyn_cast<VariableDeclaration>(member)) {
-          const Address object = destroy_act.location()->address();
-          const Address var_addr =
-              object.ElementAddress(arena_->New<NamedElement>(var));
-          const auto v = heap_.Read(var_addr, var->source_loc());
-          CARBON_CHECK(v.ok(), "Failed to read member `{0}` from class `{1}`",
-                       var->binding().name(), class_decl.name());
-          return todo_.Spawn(std::make_unique<DestroyAction>(
-              arena_->New<LocationValue>(var_addr), *v));
-        } else {
-          return todo_.RunAgain();
-        }
-      } else if (act.pos() == member_count + 1) {
-        // Destroy the parent, if there is one.
-        if (auto base = class_obj->base()) {
-          const Address obj_addr = destroy_act.location()->address();
-          const Address base_addr =
-              obj_addr.ElementAddress(arena_->New<BaseElement>(class_obj));
-          return todo_.Spawn(std::make_unique<DestroyAction>(
-              arena_->New<LocationValue>(base_addr), base.value()));
-        } else {
-          return todo_.RunAgain();
-        }
-      } else {
-        todo_.Pop();
-        return Success();
-      }
-    }
-    case Value::Kind::TupleValue: {
-      const auto* tuple = cast<TupleValue>(destroy_act.value());
-      const auto element_count = tuple->elements().size();
-      if (static_cast<size_t>(act.pos()) < element_count) {
-        const size_t index = element_count - act.pos() - 1;
-        const auto& item = tuple->elements()[index];
-        const auto object_addr = destroy_act.location()->address();
-        Address field_address = object_addr.ElementAddress(
-            arena_->New<PositionalElement>(index, item));
-        if (item->kind() == Value::Kind::NominalClassValue ||
-            item->kind() == Value::Kind::TupleValue) {
-          return todo_.Spawn(std::make_unique<DestroyAction>(
-              arena_->New<LocationValue>(field_address), item));
-        } else {
-          // The tuple element's type is an integral type (e.g., i32)
-          // or the type doesn't support destruction.
-          return todo_.RunAgain();
-        }
-      } else {
-        todo_.Pop();
-        return Success();
-      }
-    }
-    default:
-      // These declarations have no run-time effects.
-      todo_.Pop();
-      return Success();
-  }
-  CARBON_FATAL("Unreachable");
-}
-
-auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
-  const Action& act = todo_.CurrentAction();
-  const auto& cleanup = cast<CleanUpAction>(act);
-
-  if (act.pos() < cleanup.allocations_count() * 2) {
-    const size_t alloc_index = cleanup.allocations_count() - act.pos() / 2 - 1;
-    auto allocation = act.scope()->allocations()[alloc_index];
-    if (heap_.is_discarded(allocation)) {
-      // Initializing expressions can generate discarded allocations.
-      return todo_.RunAgain();
-    }
-    if (act.pos() % 2 == 0) {
-      auto* location = arena_->New<LocationValue>(Address(allocation));
-      auto value = heap_.Read(location->address(), *cleanup.source_loc());
-      // Step over uninitialized values.
-      if (value.ok()) {
-        return todo_.Spawn(std::make_unique<DestroyAction>(location, *value));
-      } else {
-        return todo_.RunAgain();
-      }
-    } else {
-      CARBON_RETURN_IF_ERROR(heap_.Deallocate(allocation));
-      return todo_.RunAgain();
-    }
-  }
-  todo_.Pop();
-  return Success();
-}
-
-// State transition.
-auto Interpreter::Step() -> ErrorOr<Success> {
-  Action& act = todo_.CurrentAction();
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "step " << act << " (" << act.source_loc()
-                           << ") --->\n";
-  }
-
-  auto error_builder = [&] {
-    if (auto loc = act.source_loc()) {
-      return ProgramError(*loc);
-    }
-    return ErrorBuilder();
-  };
-
-  // Check for various overflow conditions before stepping.
-  if (todo_.size() > MaxTodoSize) {
-    return error_builder()
-           << "stack overflow: too many interpreter actions on stack";
-  }
-  if (++steps_taken_ > MaxStepsTaken) {
-    return error_builder()
-           << "possible infinite loop: too many interpreter steps executed";
-  }
-  if (arena_->allocated() > MaxArenaAllocated) {
-    return error_builder() << "out of memory: exceeded arena allocation limit";
-  }
-
-  switch (act.kind()) {
-    case Action::Kind::LocationAction:
-      CARBON_RETURN_IF_ERROR(StepLocation());
-      break;
-    case Action::Kind::ValueExpressionAction:
-      CARBON_RETURN_IF_ERROR(StepValueExp());
-      break;
-    case Action::Kind::ExpressionAction:
-      CARBON_RETURN_IF_ERROR(StepExp());
-      break;
-    case Action::Kind::WitnessAction:
-      CARBON_RETURN_IF_ERROR(StepWitness());
-      break;
-    case Action::Kind::StatementAction:
-      CARBON_RETURN_IF_ERROR(StepStmt());
-      break;
-    case Action::Kind::DeclarationAction:
-      CARBON_RETURN_IF_ERROR(StepDeclaration());
-      break;
-    case Action::Kind::CleanUpAction:
-      CARBON_RETURN_IF_ERROR(StepCleanUp());
-      break;
-    case Action::Kind::DestroyAction:
-      CARBON_RETURN_IF_ERROR(StepDestroy());
-      break;
-    case Action::Kind::TypeInstantiationAction:
-      CARBON_RETURN_IF_ERROR(StepInstantiateType());
-      break;
-    case Action::Kind::ScopeAction:
-      CARBON_FATAL("ScopeAction escaped ActionStack");
-    case Action::Kind::RecursiveAction:
-      CARBON_FATAL("Tried to step a RecursiveAction");
-  }  // switch
-  return Success();
-}
-
-auto Interpreter::RunAllSteps(std::unique_ptr<Action> action)
-    -> ErrorOr<Success> {
-  todo_.Start(std::move(action));
-  while (!todo_.empty()) {
-    CARBON_RETURN_IF_ERROR(Step());
-  }
-  return Success();
-}
-
-auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
-                   Nonnull<TraceStream*> trace_stream,
-                   Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
-  Interpreter interpreter(Phase::RunTime, arena, trace_stream, print_stream);
-  if (trace_stream->is_enabled()) {
-    trace_stream->SubHeading("initializing globals");
-  }
-
-  SetFileContext set_file_ctx(*trace_stream,
-                              ast.declarations.front()->source_loc());
-  for (Nonnull<Declaration*> declaration : ast.declarations) {
-    set_file_ctx.update_source_loc(declaration->source_loc());
-    CARBON_RETURN_IF_ERROR(interpreter.RunAllSteps(
-        std::make_unique<DeclarationAction>(declaration)));
-  }
-
-  if (trace_stream->is_enabled()) {
-    trace_stream->SubHeading("calling main function");
-  }
-
-  CARBON_CHECK(ast.main_call);
-  set_file_ctx.update_source_loc(ast.main_call.value()->source_loc());
-  CARBON_RETURN_IF_ERROR(interpreter.RunAllSteps(
-      std::make_unique<ValueExpressionAction>(*ast.main_call)));
-
-  return cast<IntValue>(*interpreter.result()).value();
-}
-
-auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
-               Nonnull<TraceStream*> trace_stream,
-               Nonnull<llvm::raw_ostream*> print_stream)
-    -> ErrorOr<Nonnull<const Value*>> {
-  Interpreter interpreter(Phase::CompileTime, arena, trace_stream,
-                          print_stream);
-  CARBON_RETURN_IF_ERROR(
-      interpreter.RunAllSteps(std::make_unique<ValueExpressionAction>(e)));
-  return interpreter.result();
-}
-
-}  // namespace Carbon

+ 0 - 31
explorer/interpreter/interpreter.h

@@ -1,31 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_
-#define CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_
-
-#include "common/ostream.h"
-#include "explorer/ast/ast.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-
-// Interprets the program defined by `ast`, allocating values on `arena` and
-// printing traces if `trace` is true.
-auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
-                   Nonnull<TraceStream*> trace_stream,
-                   Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
-
-// Interprets `e` at compile-time, allocating values on `arena` and
-// printing traces if `trace` is true. The caller must ensure that all the
-// code this evaluates has been typechecked.
-auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
-               Nonnull<TraceStream*> trace_stream,
-               Nonnull<llvm::raw_ostream*> print_stream)
-    -> ErrorOr<Nonnull<const Value*>>;
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_

+ 0 - 176
explorer/interpreter/matching_impl_set.cpp

@@ -1,176 +0,0 @@
-// 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 "common/check.h"
-#include "common/error.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-#include "explorer/interpreter/type_checker.h"
-
-namespace Carbon {
-
-// A visitor for type values that collects leaf 'labels', such as class names,
-// and adds them to the signature of a `Match` object.
-class MatchingImplSet::LeafCollector {
- public:
-  explicit LeafCollector(Match* match) : match_(match) {}
-
-  void Collect(const Value* value) {
-    value->Visit<void>(
-        [&](const auto* derived_value) { VisitValue(derived_value); });
-  }
-
-  void Collect(Label label) { ++match_->signature_[label]; }
-
- private:
-  // Most kinds of value don't contribute to the signature.
-  void VisitValue(const Value* /*unused*/) {}
-
-  void VisitValue(const TypeType* /*unused*/) { Collect(Label::TypeType); }
-
-  void VisitValue(const BoolType* /*unused*/) { Collect(Label::BoolType); }
-
-  void VisitValue(const IntType* /*unused*/) { Collect(Label::IntType); }
-
-  void VisitValue(const StringType* /*unused*/) { Collect(Label::StringType); }
-
-  void VisitValue(const StaticArrayType* array) {
-    Collect(Label::ArrayType);
-    Collect(&array->element_type());
-  }
-
-  void VisitValue(const PointerType* pointer) {
-    Collect(Label::PointerType);
-    Collect(&pointer->pointee_type());
-  }
-
-  void VisitValue(const StructType* struct_type) {
-    Collect(Label::StructType);
-    for (const auto& [name, type] : struct_type->fields()) {
-      Collect(type);
-    }
-  }
-
-  void VisitValue(const TupleType* tuple_type) {
-    Collect(Label::TupleType);
-    for (const auto* elem_type : tuple_type->elements()) {
-      Collect(elem_type);
-    }
-  }
-
-  void VisitValue(const NominalClassType* class_type) {
-    VisitDeclarationAndArgs(class_type->declaration(), class_type->bindings());
-  }
-
-  void VisitValue(const MixinPseudoType* mixin_type) {
-    VisitDeclarationAndArgs(mixin_type->declaration(), mixin_type->bindings());
-  }
-
-  void VisitValue(const InterfaceType* iface_type) {
-    VisitDeclarationAndArgs(iface_type->declaration(), iface_type->bindings());
-  }
-
-  void VisitValue(const NamedConstraintType* constraint_type) {
-    VisitDeclarationAndArgs(constraint_type->declaration(),
-                            constraint_type->bindings());
-  }
-
-  void VisitValue(const ChoiceType* choice_type) {
-    VisitDeclarationAndArgs(choice_type->declaration(),
-                            choice_type->bindings());
-  }
-
-  void VisitDeclarationAndArgs(const Declaration& declaration,
-                               const Bindings& bindings) {
-    Collect(match_->parent_->GetLabelForDeclaration(declaration));
-    for (auto [key, value] : bindings.args()) {
-      Collect(value);
-    }
-  }
-
-  Match* match_;
-};
-
-auto MatchingImplSet::GetLabelForDeclaration(const Declaration& declaration)
-    -> Label {
-  auto [it, added] = declaration_labels_.insert(
-      {&declaration,
-       static_cast<Label>(static_cast<int>(Label::FirstDeclarationLabel) +
-                          declaration_labels_.size())});
-  return it->second;
-}
-
-MatchingImplSet::Match::Match(Nonnull<MatchingImplSet*> parent,
-                              Nonnull<const ImplScope::ImplFact*> impl,
-                              Nonnull<const Value*> type,
-                              Nonnull<const Value*> interface)
-    : parent_(parent), impl_(impl), type_(type), interface_(interface) {
-  // Build our signature.
-  LeafCollector collector(this);
-  collector.Collect(type);
-  collector.Collect(interface);
-
-  parent_->matches_.push_back(this);
-}
-
-MatchingImplSet::Match::~Match() {
-  CARBON_CHECK(parent_->matches_.back() == this, "match stack broken");
-  parent_->matches_.pop_back();
-}
-
-auto MatchingImplSet::Match::DiagnosePotentialCycle(SourceLocation source_loc)
-    -> ErrorOr<Success> {
-  // Determine whether any labels in 'a' have a higher count than in 'b'.
-  auto any_labels_with_higher_count = [](const Signature& a,
-                                         const Signature& b) {
-    if (a.size() > b.size()) {
-      // Every label in a signature has a count of at least one.
-      return true;
-    }
-    for (auto [key, a_value] : a) {
-      int b_value = b.lookup(key);
-      if (a_value > b_value) {
-        return true;
-      }
-    }
-    return false;
-  };
-
-  for (auto* match : parent_->matches_) {
-    if (match != this && match->impl_ == impl_ &&
-        !any_labels_with_higher_count(match->signature_, signature_)) {
-      // No label in the outer match has a higher count than the same label in
-      // the inner match. We might have reached a cycle.
-      if (any_labels_with_higher_count(signature_, match->signature_)) {
-        // The inner match has a higher count for some label. This query is
-        // strictly more complex than the outer one, so reject this potential
-        // cycle.
-        // TODO: Track which label has a higher count, map it back to a string,
-        // and include it in this diagnostic.
-        return ProgramError(source_loc)
-               << "impl matching recursively performed a more complex match "
-                  "using the same impl\n"
-               << "  outer match: " << *match->type_ << " as "
-               << *match->interface_ << "\n"
-               << "  inner match: " << *type_ << " as " << *interface_;
-      }
-
-      if (ValueEqual(match->type_, type_, std::nullopt) &&
-          ValueEqual(match->interface_, interface_, std::nullopt)) {
-        // We hit the same query twice recursively. This is definitely a cycle.
-        return ProgramError(source_loc)
-               << "impl matching for " << *type_ << " as " << *interface_
-               << " recursively performed a match for the same type and "
-                  "interface";
-      }
-    }
-  }
-
-  return Success();
-}
-
-}  // namespace Carbon

+ 0 - 136
explorer/interpreter/matching_impl_set.h

@@ -1,136 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_
-#define CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_
-
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/interpreter/impl_scope.h"
-#include "llvm/ADT/DenseMap.h"
-
-namespace Carbon {
-
-// A set of impl matches that we're currently performing. Each `Match`
-// represents an attempt to match a `type as interface` query against an `impl`
-// declaration. This is used to detect and reject non-termination when impl
-// matching recursively triggers further impl matching.
-//
-// The language rule we use to detect potential non-termination is to count the
-// number of times each "label" appears within the type and interface, where a
-// label is the name of a declared entity such as a class or interface, or a
-// primitive like `type` or `bool`. For example, `Optional(i32*)` contains the
-// labels for `Optional`, `i32`, and `*` (built-in pointer type) once each. If
-// we ever try matching the same `impl` twice, where the inner match contains
-// at least as many appearances of each label as the outer match, we reject the
-// program as invalid. We also reject if a query results in the exact same
-// query being performed again.
-//
-// This class is an implementation detail of `TypeChecker::MatchImpl`.
-class MatchingImplSet {
- private:
-  class LeafCollector;
-  enum class Label : int;
-  using Signature = llvm::DenseMap<Label, int>;
-
- public:
-  // An RAII type that tracks an impl match that we're currently performing.
-  // One instance of this class will exist for each in-progress call to
-  // `MatchImpl`.
-  class Match {
-   public:
-    explicit Match(Nonnull<MatchingImplSet*> parent,
-                   Nonnull<const ImplScope::ImplFact*> impl,
-                   Nonnull<const Value*> type, Nonnull<const Value*> interface);
-    ~Match();
-
-    Match(const Match&) = delete;
-    auto operator=(const Match&) -> Match& = delete;
-
-    // Check to see if this match duplicates any prior one within the same set,
-    // or if there's a simpler form of this match in the set. If so, returns a
-    // suitable error. This should be delayed until we know that the impl
-    // structurally matches the type and interface.
-    auto DiagnosePotentialCycle(SourceLocation source_loc) -> ErrorOr<Success>;
-
-   private:
-    friend class LeafCollector;
-
-    // The set that this match is part of.
-    Nonnull<MatchingImplSet*> parent_;
-    // The `impl` that is being matched against.
-    Nonnull<const ImplScope::ImplFact*> impl_;
-    // The type that is being matched against the impl.
-    Nonnull<const Value*> type_;
-    // The interface that is being matched against the impl.
-    Nonnull<const Value*> interface_;
-    // The number of times each label appears in the type or interface.
-    Signature signature_;
-  };
-
- private:
-  friend class llvm::DenseMapInfo<Label>;
-
-  // An opaque integer used to identify a particular label appearing in a type,
-  // such as a class name. The named enumerators represent builtins, and values
-  // >= `FirstDeclarationLabel` represent declarations from the program.
-  enum class Label : int {
-    // Label for `type` type constant.
-    TypeType,
-    // Label for `bool` type constant.
-    BoolType,
-    // Label for `i32` type constant.
-    IntType,
-    // Label for `String` type constant.
-    StringType,
-    // Label for `[_;_]` type constructor.
-    ArrayType,
-    // Label for `_*` type constructor.
-    PointerType,
-    // Label for `{.a: _, .b: _, ...}` struct type constructor. We use the same
-    // label regardless of the arity of the struct type and any field names.
-    StructType,
-    // Label for `(_, _, ..., _)` tuple type constructor. We use the same label
-    // regardless of the arity of the tuple type.
-    TupleType,
-    // First Label value corresponding to a Declaration. Must be kept at the
-    // end of the enum.
-    FirstDeclarationLabel
-  };
-
-  // Get the Label that represents a given declaration.
-  auto GetLabelForDeclaration(const Declaration& declaration) -> Label;
-
-  // The known declarations and their labels.
-  llvm::DenseMap<const Declaration*, Label> declaration_labels_;
-
-  // The matches that are currently being performed, in order from outermost to
-  // innermost.
-  std::vector<Match*> matches_;
-};
-
-}  // namespace Carbon
-
-// Support use of Label as a DenseMap key.
-template <>
-struct llvm::DenseMapInfo<Carbon::MatchingImplSet::Label> {
-  using Base = llvm::DenseMapInfo<int>;
-  using Label = Carbon::MatchingImplSet::Label;
-  static inline auto getEmptyKey() -> Label {
-    return static_cast<Label>(Base::getEmptyKey());
-  }
-  static inline auto getTombstoneKey() -> Label {
-    return static_cast<Label>(Base::getTombstoneKey());
-  }
-  static inline auto getHashValue(Label label) -> unsigned {
-    return Base::getHashValue(static_cast<int>(label));
-  }
-  static auto isEqual(Label a, Label b) -> bool { return a == b; }
-};
-
-#endif  // CARBON_EXPLORER_INTERPRETER_MATCHING_IMPL_SET_H_

+ 0 - 313
explorer/interpreter/pattern_analysis.cpp

@@ -1,313 +0,0 @@
-// 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 "explorer/interpreter/pattern_analysis.h"
-
-#include <set>
-
-using llvm::cast;
-using llvm::dyn_cast;
-using llvm::isa;
-
-namespace Carbon {
-
-auto AbstractPattern::kind() const -> Kind {
-  if (isa<const Pattern*>(value_)) {
-    return Compound;
-  }
-  if (const auto* value = value_.dyn_cast<const Value*>()) {
-    if (isa<TupleValue, AlternativeValue, BoolValue>(value)) {
-      return Compound;
-    }
-    return Primitive;
-  }
-  CARBON_CHECK(isa<const WildcardTag*>(value_));
-  return Wildcard;
-}
-
-auto AbstractPattern::discriminator() const -> std::string_view {
-  CARBON_CHECK(kind() == Compound);
-  if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
-    if (const auto* alt_pattern = dyn_cast<AlternativePattern>(pattern)) {
-      return alt_pattern->alternative_name();
-    }
-  } else if (const auto* value = value_.dyn_cast<const Value*>()) {
-    if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
-      return alt->alternative().name();
-    } else if (const auto* bool_val = dyn_cast<BoolValue>(value)) {
-      return bool_val->value() ? "true" : "false";
-    }
-  }
-  return {};
-}
-
-auto AbstractPattern::elements_size() const -> int {
-  if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
-    if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
-      return tuple_pattern->fields().size();
-    } else if (isa<AlternativePattern>(pattern)) {
-      return 1;
-    }
-  } else if (const auto* value = value_.dyn_cast<const Value*>()) {
-    if (const auto* tuple = dyn_cast<TupleValue>(value)) {
-      return tuple->elements().size();
-    } else if (isa<AlternativeValue>(value)) {
-      return 1;
-    }
-  }
-  return 0;
-}
-
-void AbstractPattern::AppendElementsTo(
-    std::vector<AbstractPattern>& out) const {
-  if (const auto* pattern = value_.dyn_cast<const Pattern*>()) {
-    if (const auto* tuple_pattern = dyn_cast<TuplePattern>(pattern)) {
-      auto fields = tuple_pattern->fields();
-      out.insert(out.end(), fields.begin(), fields.end());
-    } else if (const auto* alt_pattern =
-                   dyn_cast<AlternativePattern>(pattern)) {
-      out.push_back(&alt_pattern->arguments());
-    }
-  } else if (const auto* value = value_.dyn_cast<const Value*>()) {
-    if (const auto* tuple = dyn_cast<TupleValue>(value)) {
-      const auto* tuple_type = cast<TupleType>(type_);
-      CARBON_CHECK(tuple->elements().size() == tuple_type->elements().size());
-      for (size_t i = 0; i != tuple->elements().size(); ++i) {
-        out.push_back(
-            AbstractPattern(tuple->elements()[i], tuple_type->elements()[i]));
-      }
-    } else if (const auto* alt = dyn_cast<AlternativeValue>(value)) {
-      if (auto arg = alt->argument()) {
-        out.push_back(AbstractPattern(
-            *arg, *alt->alternative().parameters_static_type()));
-      } else {
-        // There's no value to match for this alternative, so just insert a
-        // wildcard.
-        out.push_back(AbstractPattern::MakeWildcard());
-      }
-    }
-  }
-}
-
-auto AbstractPattern::value() const -> const Value& {
-  CARBON_CHECK(kind() == Primitive);
-  return *cast<const Value*>(value_);
-}
-
-auto AbstractPattern::type() const -> const Value& {
-  CARBON_CHECK(kind() != Wildcard);
-  return *type_;
-}
-
-void AbstractPattern::Set(Nonnull<const Pattern*> pattern) {
-  type_ = &pattern->static_type();
-  switch (pattern->kind()) {
-    case PatternKind::AddrPattern:
-    case PatternKind::AutoPattern:
-    case PatternKind::BindingPattern:
-    case PatternKind::GenericBinding:
-      value_ = static_cast<const WildcardTag*>(nullptr);
-      break;
-
-    case PatternKind::TuplePattern:
-    case PatternKind::AlternativePattern:
-      value_ = pattern;
-      break;
-
-    case PatternKind::ExpressionPattern:
-      value_ = &pattern->value();
-      break;
-
-    case PatternKind::VarPattern:
-      Set(&cast<VarPattern>(pattern)->pattern());
-      break;
-  }
-}
-
-auto PatternMatrix::IsUseful(llvm::ArrayRef<AbstractPattern> pattern,
-                             int max_exponential_depth) const -> bool {
-  if (matrix_.empty()) {
-    return true;
-  }
-
-  CARBON_CHECK(pattern.size() == matrix_[0].size());
-  if (matrix_[0].empty()) {
-    return false;
-  }
-
-  switch (pattern[0].kind()) {
-    case AbstractPattern::Wildcard: {
-      auto discrim = FirstColumnDiscriminators();
-      // Check if we hit the depth limit. If so, we act as if the
-      // constructors present in this position are not exhaustive, that is,
-      // as if the type we're matching has some other constructor not
-      // corresponding to anything written in the pattern in this position.
-      // This can lead us to conclude that a pattern is useful if it is not,
-      // and that a set of patterns is not exhaustive when it is.
-      int new_depth =
-          max_exponential_depth - (discrim.found.size() > 1 ? 1 : 0);
-      if (!discrim.any_missing && new_depth >= 0) {
-        for (auto found : discrim.found) {
-          if (Specialize(found).IsUseful(*SpecializeRow(pattern, found),
-                                         new_depth)) {
-            return true;
-          }
-        }
-        return false;
-      }
-      return Default().IsUseful(pattern.slice(1), max_exponential_depth);
-    }
-
-    case AbstractPattern::Compound: {
-      DiscriminatorInfo discrim = {.discriminator = pattern[0].discriminator(),
-                                   .size = pattern[0].elements_size()};
-      return Specialize(discrim).IsUseful(*SpecializeRow(pattern, discrim),
-                                          max_exponential_depth);
-    }
-
-    case AbstractPattern::Primitive: {
-      return Specialize(pattern[0].value())
-          .IsUseful(pattern.slice(1), max_exponential_depth);
-    }
-  }
-}
-
-auto PatternMatrix::FirstColumnDiscriminators() const -> DiscriminatorSet {
-  std::set<std::string_view> discrims;
-  std::optional<int> num_discrims;
-  std::optional<int> elem_size;
-
-  for (const auto& row : matrix_) {
-    CARBON_CHECK(!row.empty());
-    switch (row[0].kind()) {
-      case AbstractPattern::Wildcard:
-        continue;
-      case AbstractPattern::Compound: {
-        const Value& type = row[0].type();
-        if (const auto* tuple = dyn_cast<TupleType>(&type)) {
-          // If we find a tuple match, we've found all constructors (there's
-          // only one!) and none were missing.
-          return {
-              .found = {{.discriminator = {},
-                         .size = static_cast<int>(tuple->elements().size())}},
-              .any_missing = false};
-        } else if (const auto* choice = dyn_cast<ChoiceType>(&type)) {
-          num_discrims = choice->declaration().alternatives().size();
-          elem_size = 1;
-        } else if (isa<BoolType>(type)) {
-          // `bool` behaves like a choice type with two alternatives,
-          // and with no nested patterns for either of them.
-          num_discrims = 2;
-          elem_size = 0;
-        } else {
-          llvm_unreachable("unexpected compound type");
-        }
-        discrims.insert(row[0].discriminator());
-        break;
-      }
-      case AbstractPattern::Primitive: {
-        // We assume that primitive value matches are always incomplete, even
-        // for types like `i8` where a covering match might be possible.
-        return {.found = {}, .any_missing = true};
-      }
-    }
-  }
-
-  if (!num_discrims || *num_discrims != static_cast<int>(discrims.size())) {
-    return {.found = {}, .any_missing = true};
-  }
-
-  DiscriminatorSet result = {.found = {}, .any_missing = false};
-  result.found.reserve(discrims.size());
-  for (auto s : discrims) {
-    result.found.push_back({.discriminator = s, .size = *elem_size});
-  }
-  return result;
-}
-
-auto PatternMatrix::SpecializeRow(llvm::ArrayRef<AbstractPattern> row,
-                                  DiscriminatorInfo discriminator)
-    -> std::optional<std::vector<AbstractPattern>> {
-  CARBON_CHECK(!row.empty());
-  std::vector<AbstractPattern> new_row;
-  switch (row[0].kind()) {
-    case AbstractPattern::Wildcard:
-      new_row.reserve(discriminator.size + row.size() - 1);
-      new_row.insert(new_row.end(), discriminator.size,
-                     AbstractPattern::MakeWildcard());
-      break;
-    case AbstractPattern::Compound: {
-      if (row[0].discriminator() != discriminator.discriminator) {
-        return std::nullopt;
-      }
-      CARBON_CHECK(static_cast<int>(row[0].elements_size()) ==
-                   discriminator.size);
-      new_row.reserve(discriminator.size + row.size() - 1);
-      row[0].AppendElementsTo(new_row);
-      break;
-    }
-    case AbstractPattern::Primitive:
-      // These cases should be rejected by the type checker.
-      llvm_unreachable("matched primitive against compound");
-  }
-  new_row.insert(new_row.end(), row.begin() + 1, row.end());
-  return std::move(new_row);
-}
-
-auto PatternMatrix::Specialize(DiscriminatorInfo discriminator) const
-    -> PatternMatrix {
-  PatternMatrix specialized;
-  for (const auto& row : matrix_) {
-    // TODO: If we add support for "or" patterns, specialization might
-    // produce multiple rows here.
-    if (auto new_row = SpecializeRow(row, discriminator)) {
-      specialized.Add(std::move(new_row.value()));
-    }
-  }
-  return specialized;
-}
-
-// Specialize the pattern matrix for the case where the first value is known
-// to be `value`, and is not matched.
-auto PatternMatrix::Specialize(const Value& value) const -> PatternMatrix {
-  PatternMatrix specialized;
-  for (const auto& row : matrix_) {
-    CARBON_CHECK(!row.empty());
-    switch (row[0].kind()) {
-      case AbstractPattern::Wildcard:
-        break;
-      case AbstractPattern::Compound:
-        llvm_unreachable("matched compound against primitive");
-      case AbstractPattern::Primitive:
-        // TODO: Use an equality context here?
-        if (!ValueEqual(&row[0].value(), &value, std::nullopt)) {
-          continue;
-        }
-        break;
-    }
-    specialized.Add(std::vector<AbstractPattern>(row.begin() + 1, row.end()));
-  }
-  return specialized;
-}
-
-// Specialize the pattern matrix for the case where the first value uses a
-// discriminator matching none of the non-wildcard patterns.
-auto PatternMatrix::Default() const -> PatternMatrix {
-  PatternMatrix default_matrix;
-  for (const auto& row : matrix_) {
-    CARBON_CHECK(!row.empty());
-    switch (row[0].kind()) {
-      case AbstractPattern::Wildcard:
-        default_matrix.Add(
-            std::vector<AbstractPattern>(row.begin() + 1, row.end()));
-        break;
-      case AbstractPattern::Compound:
-      case AbstractPattern::Primitive:
-        break;
-    }
-  }
-  return default_matrix;
-}
-
-}  // namespace Carbon

+ 0 - 162
explorer/interpreter/pattern_analysis.h

@@ -1,162 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_
-#define CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_
-
-#include <vector>
-
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/nonnull.h"
-#include "llvm/ADT/PointerUnion.h"
-
-namespace Carbon {
-
-// An abstracted view of a pattern or constant value (which we view as a
-// particular kind of pattern).
-class AbstractPattern {
- public:
-  enum Kind {
-    // A pattern that matches anything.
-    Wildcard,
-    // A pattern that matches a compound value with sub-patterns to match
-    // elements. A compound value is modeled as a discriminator name applied to
-    // a sequence of nested values: the alternative `Optional.Element(E)` has
-    // discriminator `Element` and nested value `E`, and the tuple `(A,B,C)`
-    // has an empty discriminator and nested values `A`, `B`, and `C`.
-    Compound,
-    // A pattern that matches a particular primitive value.
-    Primitive
-  };
-
-  // This is intentionally implicit to allow easy conversion from a container
-  // of `const Pattern*` to a container of `AbstractPattern`s.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  AbstractPattern(Nonnull<const Pattern*> pattern) { Set(pattern); }
-
-  AbstractPattern(Nonnull<const Value*> value, Nonnull<const Value*> type)
-      : value_(value), type_(type) {}
-
-  // Make a match-anything wildcard pattern.
-  static auto MakeWildcard() -> AbstractPattern {
-    return AbstractPattern(WildcardTag());
-  }
-
-  // Get the kind for this pattern.
-  auto kind() const -> Kind;
-
-  // Get the type, for a non-wildcard pattern.
-  auto type() const -> const Value&;
-
-  // Get the discriminator used for a compound pattern.
-  auto discriminator() const -> std::string_view;
-
-  // Get the number of nested patterns for a compound pattern.
-  auto elements_size() const -> int;
-
-  // Append the nested patterns in this compound pattern to `out`.
-  void AppendElementsTo(std::vector<AbstractPattern>& out) const;
-
-  // Get the value for a primitive pattern.
-  auto value() const -> const Value&;
-
- private:
-  // This is aligned so that we can use it in the `PointerUnion` below.
-  struct alignas(8) WildcardTag {};
-  explicit AbstractPattern(WildcardTag /*unused*/)
-      : value_(static_cast<const WildcardTag*>(nullptr)), type_(nullptr) {}
-
-  void Set(Nonnull<const Pattern*> pattern);
-
-  // The underlying pattern: either a syntactic pattern, or a constant value,
-  // or a placeholder indicating that this is a wildcard pattern.
-  llvm::PointerUnion<Nonnull<const Pattern*>, Nonnull<const Value*>,
-                     const WildcardTag*>
-      value_;
-  // Values don't always know their types, so store the type here. We only
-  // really need it for the `const Value*` case but also store it for the
-  // `const Pattern*` case for convenience.
-  const Value* type_;
-};
-
-// A matrix of patterns, used for determining exhaustiveness and redundancy of
-// patterns in a match statement.
-//
-// See http://moscova.inria.fr/~maranget/papers/warn/index.html for details on
-// the algorithm used here.
-class PatternMatrix {
- public:
-  // Add a pattern vector row to this collection of pattern vectors.
-  void Add(std::vector<AbstractPattern> pattern_vector) {
-    CARBON_CHECK(matrix_.empty() || matrix_[0].size() == pattern_vector.size());
-    matrix_.push_back(std::move(pattern_vector));
-  }
-
-  // Returns true if the given pattern vector is redundant if it appears after
-  // the patterns in this matrix. That is, if it will never match following the
-  // other patterns because everything it matches is matched by some other
-  // pattern.
-  auto IsRedundant(llvm::ArrayRef<AbstractPattern> pattern) const -> bool {
-    return !IsUseful(pattern, MaxExponentialDepth);
-  }
-
- private:
-  // The maximum number of times we will consider all alternatives when
-  // recursively expanding the pattern. Allowing this to happen an arbitrary
-  // number of times leads to exponential growth in the runtime of the
-  // algorithm.
-  static constexpr int MaxExponentialDepth = 8;
-
-  // Information about a constructor for a compound type.
-  struct DiscriminatorInfo {
-    // For an alternative, the name. Otherwise, empty.
-    std::string_view discriminator;
-    // The number of elements. For a tuple, the size. Always 1 for an
-    // alternative.
-    int size;
-  };
-
-  struct DiscriminatorSet {
-    std::vector<DiscriminatorInfo> found;
-    bool any_missing;
-  };
-
-  // Determine whether the given pattern vector is useful: that is, whether
-  // adding it to the matrix would allow any more values to be matched.
-  auto IsUseful(llvm::ArrayRef<AbstractPattern> pattern,
-                int max_exponential_depth) const -> bool;
-
-  // Find the discriminators used by the first column and check whether we
-  // found all of them.
-  auto FirstColumnDiscriminators() const -> DiscriminatorSet;
-
-  // Specialize the pattern vector `row` for the case that the first value
-  // matched uses `discriminator`.
-  static auto SpecializeRow(llvm::ArrayRef<AbstractPattern> row,
-                            DiscriminatorInfo discriminator)
-      -> std::optional<std::vector<AbstractPattern>>;
-
-  // Specialize the pattern matrix for the case that the first value matched
-  // uses `discriminator`, and its elements are matched.
-  auto Specialize(DiscriminatorInfo discriminator) const -> PatternMatrix;
-
-  // Specialize the pattern matrix for the case where the first value is known
-  // to be `value`, and is not matched.
-  auto Specialize(const Value& value) const -> PatternMatrix;
-
-  // Specialize the pattern matrix for the case where the first value uses a
-  // discriminator matching none of the non-wildcard patterns.
-  auto Default() const -> PatternMatrix;
-
-  // The pattern matrix itself, in row-major order. Each element of `matrix_`
-  // is a distinct sequence of patterns that can be matched against a
-  // corresponding sequence of values. Each such row has the same length and
-  // has elements of the same type.
-  std::vector<std::vector<AbstractPattern>> matrix_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_PATTERN_ANALYSIS_H_

+ 0 - 251
explorer/interpreter/pattern_match.cpp

@@ -1,251 +0,0 @@
-// 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 "explorer/interpreter/pattern_match.h"
-
-#include <algorithm>
-
-#include "explorer/ast/value.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/action.h"
-#include "explorer/interpreter/type_utils.h"
-#include "llvm/Support/Casting.h"
-
-using llvm::cast;
-using llvm::dyn_cast;
-
-namespace Carbon {
-
-static auto InitializePlaceholderValue(const ValueNodeView& value_node,
-                                       ExpressionResult v,
-                                       Nonnull<RuntimeScope*> bindings) {
-  switch (value_node.expression_category()) {
-    case ExpressionCategory::Reference:
-      if (v.expression_category() == ExpressionCategory::Value ||
-          v.expression_category() == ExpressionCategory::Reference) {
-        // Build by copying from value or reference expression.
-        bindings->Initialize(value_node, v.value());
-      } else {
-        // Location initialized by initializing expression, bind node to
-        // address.
-        CARBON_CHECK(v.address(),
-                     "Missing location from initializing expression");
-        bindings->Bind(value_node, *v.address());
-      }
-      break;
-    case ExpressionCategory::Value:
-      if (v.expression_category() == ExpressionCategory::Value) {
-        // We assume values are strictly nested for now.
-        bindings->BindValue(value_node, v.value());
-      } else if (v.expression_category() == ExpressionCategory::Reference) {
-        // Bind the reference expression value directly.
-        CARBON_CHECK(v.address(), "Missing location from reference expression");
-        bindings->BindAndPin(value_node, *v.address());
-      } else {
-        // Location initialized by initializing expression, bind node to
-        // address.
-        CARBON_CHECK(v.address(),
-                     "Missing location from initializing expression");
-        bindings->Bind(value_node, *v.address());
-      }
-      break;
-    case ExpressionCategory::Initializing:
-      CARBON_FATAL("Cannot pattern match an initializing expression");
-      break;
-  }
-}
-
-auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
-                  SourceLocation source_loc,
-                  std::optional<Nonnull<RuntimeScope*>> bindings,
-                  BindingMap& generic_args, Nonnull<TraceStream*> trace_stream,
-                  Nonnull<Arena*> arena) -> bool {
-  if (trace_stream->is_enabled()) {
-    trace_stream->Match() << "match pattern `" << *p << "`\n";
-    trace_stream->Indent() << "from "
-                           << ExpressionCategoryToString(
-                                  v.expression_category())
-                           << " expression with value `" << *v.value() << "`\n";
-  }
-  const auto make_expr_result =
-      [](Nonnull<const Value*> v) -> ExpressionResult {
-    if (const auto* expr_v = dyn_cast<ReferenceExpressionValue>(v)) {
-      return ExpressionResult::Reference(expr_v->value(), expr_v->address());
-    }
-    return ExpressionResult::Value(v);
-  };
-  if (v.value()->kind() == Value::Kind::ReferenceExpressionValue) {
-    return PatternMatch(p, make_expr_result(v.value()), source_loc, bindings,
-                        generic_args, trace_stream, arena);
-  }
-  switch (p->kind()) {
-    case Value::Kind::BindingPlaceholderValue: {
-      CARBON_CHECK(bindings.has_value());
-      const auto& placeholder = cast<BindingPlaceholderValue>(*p);
-      if (placeholder.value_node().has_value()) {
-        InitializePlaceholderValue(*placeholder.value_node(), v, *bindings);
-      }
-      return true;
-    }
-    case Value::Kind::AddrValue: {
-      const auto& addr = cast<AddrValue>(*p);
-      CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue);
-      const auto& location = cast<LocationValue>(*v.value());
-      return PatternMatch(
-          &addr.pattern(),
-          ExpressionResult::Value(arena->New<PointerValue>(location.address())),
-          source_loc, bindings, generic_args, trace_stream, arena);
-    }
-    case Value::Kind::VariableType: {
-      const auto& var_type = cast<VariableType>(*p);
-      generic_args[&var_type.binding()] = v.value();
-      return true;
-    }
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue:
-      switch (v.value()->kind()) {
-        case Value::Kind::TupleType:
-        case Value::Kind::TupleValue: {
-          const auto& p_tup = cast<TupleValueBase>(*p);
-          const auto& v_tup = cast<TupleValueBase>(*v.value());
-          CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size());
-          for (size_t i = 0; i < p_tup.elements().size(); ++i) {
-            if (!PatternMatch(p_tup.elements()[i],
-                              make_expr_result(v_tup.elements()[i]), source_loc,
-                              bindings, generic_args, trace_stream, arena)) {
-              return false;
-            }
-          }  // for
-          return true;
-        }
-        case Value::Kind::UninitializedValue: {
-          const auto& p_tup = cast<TupleValueBase>(*p);
-          for (const auto& ele : p_tup.elements()) {
-            if (!PatternMatch(ele,
-                              ExpressionResult::Value(
-                                  arena->New<UninitializedValue>(ele)),
-                              source_loc, bindings, generic_args, trace_stream,
-                              arena)) {
-              return false;
-            }
-          }
-          return true;
-        }
-        default:
-          CARBON_FATAL("expected a tuple value in pattern, not {0}",
-                       *v.value());
-      }
-    case Value::Kind::StructValue: {
-      const auto& p_struct = cast<StructValue>(*p);
-      const auto& v_struct = cast<StructValue>(*v.value());
-      CARBON_CHECK(p_struct.elements().size() == v_struct.elements().size());
-      for (size_t i = 0; i < p_struct.elements().size(); ++i) {
-        CARBON_CHECK(p_struct.elements()[i].name ==
-                     v_struct.elements()[i].name);
-        if (!PatternMatch(p_struct.elements()[i].value,
-                          ExpressionResult::Value(v_struct.elements()[i].value),
-                          source_loc, bindings, generic_args, trace_stream,
-                          arena)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    case Value::Kind::AlternativeValue:
-      switch (v.value()->kind()) {
-        case Value::Kind::AlternativeValue: {
-          const auto& p_alt = cast<AlternativeValue>(*p);
-          const auto& v_alt = cast<AlternativeValue>(*v.value());
-          if (&p_alt.alternative() != &v_alt.alternative()) {
-            return false;
-          }
-          CARBON_CHECK(p_alt.argument().has_value() ==
-                       v_alt.argument().has_value());
-          if (!p_alt.argument().has_value()) {
-            return true;
-          }
-          return PatternMatch(
-              *p_alt.argument(), ExpressionResult::Value(*v_alt.argument()),
-              source_loc, bindings, generic_args, trace_stream, arena);
-        }
-        default:
-          CARBON_FATAL("expected a choice alternative in pattern, not {0}",
-                       *v.value());
-      }
-    case Value::Kind::UninitializedValue:
-      CARBON_FATAL("uninitialized value is not allowed in pattern {0}",
-                   *v.value());
-    case Value::Kind::FunctionType:
-      switch (v.value()->kind()) {
-        case Value::Kind::FunctionType: {
-          const auto& p_fn = cast<FunctionType>(*p);
-          const auto& v_fn = cast<FunctionType>(*v.value());
-          if (!PatternMatch(&p_fn.parameters(),
-                            ExpressionResult::Value(&v_fn.parameters()),
-                            source_loc, bindings, generic_args, trace_stream,
-                            arena)) {
-            return false;
-          }
-          if (!PatternMatch(&p_fn.return_type(),
-                            ExpressionResult::Value(&v_fn.return_type()),
-                            source_loc, bindings, generic_args, trace_stream,
-                            arena)) {
-            return false;
-          }
-          return true;
-        }
-        default:
-          return false;
-      }
-    case Value::Kind::AutoType:
-      // `auto` matches any type, without binding any new names. We rely
-      // on the typechecker to ensure that `v.value()` is a type.
-      return true;
-    case Value::Kind::StaticArrayType: {
-      const auto& p_arr = cast<StaticArrayType>(*p);
-      switch (v.value()->kind()) {
-        case Value::Kind::TupleType:
-        case Value::Kind::TupleValue: {
-          const auto& v_tup = cast<TupleValueBase>(*v.value());
-          if (v_tup.elements().empty()) {
-            return !TypeIsDeduceable(&p_arr.element_type());
-          }
-
-          std::vector<Nonnull<const Value*>> deduced_types;
-          deduced_types.reserve(v_tup.elements().size());
-          for (const auto& tup_elem : v_tup.elements()) {
-            if (!PatternMatch(&p_arr.element_type(), make_expr_result(tup_elem),
-                              source_loc, bindings, generic_args, trace_stream,
-                              arena)) {
-              return false;
-            }
-
-            deduced_types.emplace_back(
-                DeducePatternType(&p_arr.element_type(), tup_elem, arena));
-          }  // for
-
-          return std::adjacent_find(deduced_types.begin(), deduced_types.end(),
-                                    [](const auto& lhs, const auto& rhs) {
-                                      return !TypeEqual(lhs, rhs, std::nullopt);
-                                    }) == deduced_types.end();
-        }
-        case Value::Kind::StaticArrayType: {
-          const auto& v_arr = cast<StaticArrayType>(*v.value());
-          if (!v_arr.has_size()) {
-            return false;
-          }
-          return PatternMatch(
-              &p_arr.element_type(), make_expr_result(&v_arr.element_type()),
-              source_loc, bindings, generic_args, trace_stream, arena);
-        }
-        default:
-          return false;
-      }
-    }
-    default:
-      return ValueEqual(p, v.value(), std::nullopt);
-  }
-}
-}  // namespace Carbon

+ 0 - 37
explorer/interpreter/pattern_match.h

@@ -1,37 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_PATTERN_MATCH_H_
-#define CARBON_EXPLORER_INTERPRETER_PATTERN_MATCH_H_
-
-#include <optional>
-
-#include "explorer/ast/bindings.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/source_location.h"
-
-namespace Carbon {
-
-class RuntimeScope;
-class TraceStream;
-class Arena;
-
-// Attempts to match `v` against the pattern `p`, returning whether matching
-// is successful. If it is, populates **bindings with the variables bound by
-// the match; `bindings` should only be nullopt in contexts where `p`
-// is not permitted to bind variables. **bindings may be modified even if the
-// match is unsuccessful, so it should typically be created for the
-// PatternMatch call and then merged into an existing scope on success.
-// The matches for generic variables in the pattern are output in
-// `generic_args`.
-[[nodiscard]] auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
-                                SourceLocation source_loc,
-                                std::optional<Nonnull<RuntimeScope*>> bindings,
-                                BindingMap& generic_args,
-                                Nonnull<TraceStream*> trace_stream,
-                                Nonnull<Arena*> arena) -> bool;
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_PATTERN_MATCH_H_

+ 0 - 247
explorer/interpreter/resolve_control_flow.cpp

@@ -1,247 +0,0 @@
-// 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 "explorer/interpreter/resolve_control_flow.h"
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/return_term.h"
-#include "explorer/ast/statement.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/print_as_id.h"
-#include "llvm/Support/Casting.h"
-
-using llvm::cast;
-
-namespace Carbon {
-
-// Aggregate information about a function being analyzed.
-struct FunctionData {
-  // The function declaration.
-  Nonnull<CallableDeclaration*> declaration;
-
-  // True if the function has a deduced return type, and we've already seen
-  // a `return` statement in its body.
-  bool saw_return_in_auto = false;
-};
-
-// Resolves control-flow edges such as `Return::function()` and `Break::loop()`
-// in the AST rooted at `statement`. `loop` is the innermost loop that
-// statically encloses `statement`, or nullopt if there is no such loop.
-// `function` carries information about the function body that `statement`
-// belongs to, and that information may be updated by this call. `function`
-// can be nullopt if `statement` does not belong to a function body, for
-// example if it is part of a continuation body instead.
-static auto ResolveControlFlow(Nonnull<TraceStream*> trace_stream,
-                               Nonnull<Statement*> statement,
-                               std::optional<Nonnull<const Statement*>> loop,
-                               std::optional<Nonnull<FunctionData*>> function)
-    -> ErrorOr<Success> {
-  SetFileContext set_file_ctx(*trace_stream, statement->source_loc());
-
-  switch (statement->kind()) {
-    case StatementKind::ReturnVar:
-    case StatementKind::ReturnExpression: {
-      if (!function.has_value()) {
-        return ProgramError(statement->source_loc())
-               << "return is not within a function body";
-      }
-      const ReturnTerm& function_return =
-          (*function)->declaration->return_term();
-      if (function_return.is_auto()) {
-        if ((*function)->saw_return_in_auto) {
-          return ProgramError(statement->source_loc())
-                 << "Only one return is allowed in a function with an `auto` "
-                    "return type.";
-        }
-        (*function)->saw_return_in_auto = true;
-      }
-      auto& ret = cast<Return>(*statement);
-      ret.set_function((*function)->declaration);
-      if (statement->kind() == StatementKind::ReturnVar &&
-          function_return.is_omitted()) {
-        return ProgramError(statement->source_loc())
-               << *statement
-               << " should not provide a return value, to match the function's "
-                  "signature.";
-      }
-      if (statement->kind() == StatementKind::ReturnExpression) {
-        auto& ret_exp = cast<ReturnExpression>(*statement);
-        if (ret_exp.is_omitted_expression() != function_return.is_omitted()) {
-          return ProgramError(ret_exp.source_loc())
-                 << ret_exp << " should"
-                 << (function_return.is_omitted() ? " not" : "")
-                 << " provide a return value, to match the function's "
-                    "signature.";
-        }
-      }
-
-      if (trace_stream->is_enabled()) {
-        trace_stream->Result()
-            << "flow-resolved return statement `" << *statement << "` in `"
-            << PrintAsID(*((*function)->declaration)) << "` ("
-            << statement->source_loc() << ")\n";
-      }
-
-      return Success();
-    }
-    case StatementKind::Break:
-      if (!loop.has_value()) {
-        return ProgramError(statement->source_loc())
-               << "break is not within a loop body";
-      }
-      cast<Break>(*statement).set_loop(*loop);
-
-      if (trace_stream->is_enabled()) {
-        trace_stream->Result()
-            << "flow-resolved break statement `" << *statement << "` for `"
-            << PrintAsID(**loop) << "`\n";
-      }
-
-      return Success();
-    case StatementKind::Continue:
-      if (!loop.has_value()) {
-        return ProgramError(statement->source_loc())
-               << "continue is not within a loop body";
-      }
-      cast<Continue>(*statement).set_loop(*loop);
-
-      if (trace_stream->is_enabled()) {
-        trace_stream->Result()
-            << "flow-resolved continue statement `" << *statement << "` in `"
-            << PrintAsID(**loop) << "` (" << statement->source_loc() << ")\n";
-      }
-
-      return Success();
-    case StatementKind::If: {
-      auto& if_stmt = cast<If>(*statement);
-      CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-          trace_stream, &if_stmt.then_block(), loop, function));
-      if (if_stmt.else_block().has_value()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-            trace_stream, *if_stmt.else_block(), loop, function));
-      }
-      return Success();
-    }
-    case StatementKind::Block: {
-      auto& block = cast<Block>(*statement);
-      for (auto* block_statement : block.statements()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveControlFlow(trace_stream, block_statement, loop, function));
-      }
-      return Success();
-    }
-    case StatementKind::For: {
-      CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-          trace_stream, &cast<For>(*statement).body(), statement, function));
-
-      if (trace_stream->is_enabled()) {
-        trace_stream->Result()
-            << "flow-resolved for statement `" << PrintAsID(*statement) << "` ("
-            << statement->source_loc() << ")\n";
-      }
-
-      return Success();
-    }
-    case StatementKind::While:
-      CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-          trace_stream, &cast<While>(*statement).body(), statement, function));
-
-      if (trace_stream->is_enabled()) {
-        trace_stream->Result()
-            << "flow-resolved while statement `" << PrintAsID(*statement)
-            << "` (" << statement->source_loc() << ")\n";
-      }
-
-      return Success();
-    case StatementKind::Match: {
-      auto& match = cast<Match>(*statement);
-      for (Match::Clause& clause : match.clauses()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-            trace_stream, &clause.statement(), loop, function));
-      }
-      return Success();
-    }
-    case StatementKind::ExpressionStatement:
-    case StatementKind::Assign:
-    case StatementKind::IncrementDecrement:
-    case StatementKind::VariableDefinition:
-      return Success();
-  }
-}
-
-static auto ResolveControlFlow(Nonnull<TraceStream*> trace_stream,
-                               Nonnull<Declaration*> declaration)
-    -> ErrorOr<Success> {
-  switch (declaration->kind()) {
-    case DeclarationKind::DestructorDeclaration:
-    case DeclarationKind::FunctionDeclaration: {
-      auto& callable = cast<CallableDeclaration>(*declaration);
-      if (callable.body().has_value()) {
-        FunctionData data = {.declaration = &callable};
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(
-            trace_stream, *callable.body(), std::nullopt, &data));
-      }
-      break;
-    }
-    case DeclarationKind::ClassDeclaration: {
-      auto& class_decl = cast<ClassDeclaration>(*declaration);
-      for (Nonnull<Declaration*> member : class_decl.members()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, member));
-      }
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      auto& mixin_decl = cast<MixinDeclaration>(*declaration);
-      for (Nonnull<Declaration*> member : mixin_decl.members()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, member));
-      }
-      break;
-    }
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      auto& iface_decl = cast<ConstraintTypeDeclaration>(*declaration);
-      for (Nonnull<Declaration*> member : iface_decl.members()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, member));
-      }
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      auto& impl_decl = cast<ImplDeclaration>(*declaration);
-      for (Nonnull<Declaration*> member : impl_decl.members()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, member));
-      }
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration: {
-      auto& match_first_decl = cast<MatchFirstDeclaration>(*declaration);
-      for (Nonnull<Declaration*> impl : match_first_decl.impl_declarations()) {
-        CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, impl));
-      }
-      break;
-    }
-    case DeclarationKind::NamespaceDeclaration:
-    case DeclarationKind::ChoiceDeclaration:
-    case DeclarationKind::VariableDeclaration:
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration:
-    case DeclarationKind::SelfDeclaration:
-    case DeclarationKind::AliasDeclaration:
-    case DeclarationKind::MixDeclaration:
-    case DeclarationKind::ExtendBaseDeclaration:
-      // do nothing
-      break;
-  }
-  return Success();
-}
-
-auto ResolveControlFlow(Nonnull<TraceStream*> trace_stream, AST& ast)
-    -> ErrorOr<Success> {
-  for (auto* declaration : ast.declarations) {
-    CARBON_RETURN_IF_ERROR(ResolveControlFlow(trace_stream, declaration));
-  }
-  return Success();
-}
-
-}  // namespace Carbon

+ 0 - 23
explorer/interpreter/resolve_control_flow.h

@@ -1,23 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_RESOLVE_CONTROL_FLOW_H_
-#define CARBON_EXPLORER_INTERPRETER_RESOLVE_CONTROL_FLOW_H_
-
-#include "explorer/ast/ast.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-
-// Resolves non-local control-flow edges, such as `break` and `return`, in the
-// given AST.
-// On failure, `ast` is left in a partial state and should not be further
-// processed.
-auto ResolveControlFlow(Nonnull<TraceStream*> trace_stream, AST& ast)
-    -> ErrorOr<Success>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_RESOLVE_CONTROL_FLOW_H_

+ 0 - 984
explorer/interpreter/resolve_names.cpp

@@ -1,984 +0,0 @@
-// 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 "explorer/interpreter/resolve_names.h"
-
-#include <set>
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/statement.h"
-#include "explorer/ast/static_scope.h"
-#include "explorer/base/print_as_id.h"
-#include "explorer/interpreter/stack_space.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/Support/Casting.h"
-
-using llvm::cast;
-using llvm::dyn_cast;
-using llvm::isa;
-
-namespace Carbon {
-namespace {
-
-// The name resolver implements a pass that traverses the AST, builds scope
-// objects for each scope encountered, and updates all name references to point
-// at the value node referenced by the corresponding name.
-//
-// In scopes where names are only visible below their point of declaration
-// (such as block scopes in C++), this is implemented as a single pass,
-// recursively calling ResolveNames on the elements of the scope in order. In
-// scopes where names are also visible above their point of declaration (such
-// as class scopes in C++), this is done in three passes: first calling
-// AddExposedNames on each element of the scope to populate a StaticScope, and
-// then calling ResolveNames on each element, passing it the already-populated
-// StaticScope but skipping member function bodies, and finally calling
-// ResolvedNames again on each element, and this time resolving member function
-// bodies.
-class NameResolver {
- public:
-  explicit NameResolver(Nonnull<TraceStream*> trace_stream)
-      : trace_stream_(trace_stream) {}
-  enum class ResolveFunctionBodies {
-    // Do not resolve names in function bodies.
-    Skip,
-    // Resolve all names. When visiting a declaration with members, resolve
-    // names in member function bodies after resolving the names in all member
-    // declarations, as if the bodies appeared after all the declarations.
-    AfterDeclarations,
-    // Resolve names in function bodies immediately. This is appropriate when
-    // the declarations of all members of enclosing classes, interfaces, and
-    // similar have already been resolved.
-    Immediately,
-  };
-
-  // Resolve the qualifier of the given declared name to a scope.
-  auto ResolveQualifier(DeclaredName name, StaticScope& enclosing_scope,
-                        bool allow_undeclared = false)
-      -> ErrorOr<Nonnull<StaticScope*>>;
-
-  // Add the given name to enclosing_scope. Returns the scope in which the name
-  // was declared.
-  auto AddExposedName(DeclaredName name, ValueNodeView value,
-                      StaticScope& enclosing_scope, bool allow_qualified_names)
-      -> ErrorOr<Nonnull<StaticScope*>>;
-
-  // Add the names exposed by the given AST node to enclosing_scope.
-  auto AddExposedNames(const Declaration& declaration,
-                       StaticScope& enclosing_scope,
-                       bool allow_qualified_names = false) -> ErrorOr<Success>;
-
-  // Resolve all names within the given expression by looking them up in the
-  // enclosing scope. The value returned is the value of the expression, if it
-  // is an expression within which we can immediately do further name lookup,
-  // such as a namespace.
-  auto ResolveNames(Expression& expression, const StaticScope& enclosing_scope)
-      -> ErrorOr<std::optional<ValueNodeView>>;
-  // For RunWithExtraStack.
-  auto ResolveNamesImpl(Expression& expression,
-                        const StaticScope& enclosing_scope)
-      -> ErrorOr<std::optional<ValueNodeView>>;
-
-  // Resolve all names within the given where clause by looking them up in the
-  // enclosing scope.
-  auto ResolveNames(WhereClause& clause, const StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-  // For RunWithExtraStack.
-  auto ResolveNamesImpl(WhereClause& clause, const StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-
-  // Resolve all names within the given pattern, extending the given scope with
-  // any introduced names.
-  auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-  // For RunWithExtraStack.
-  auto ResolveNamesImpl(Pattern& pattern, StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-
-  // Resolve all names within the given statement, extending the given scope
-  // with any names introduced by declaration statements.
-  auto ResolveNames(Statement& statement, StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-  // For RunWithExtraStack.
-  auto ResolveNamesImpl(Statement& statement, StaticScope& enclosing_scope)
-      -> ErrorOr<Success>;
-
-  // Resolve all names within the given declaration, extending the given scope
-  // with the any names introduced by the declaration if they're not already
-  // present.
-  auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
-                    ResolveFunctionBodies bodies) -> ErrorOr<Success>;
-  // For RunWithExtraStack.
-  auto ResolveNamesImpl(Declaration& declaration, StaticScope& enclosing_scope,
-                        ResolveFunctionBodies bodies) -> ErrorOr<Success>;
-
-  auto ResolveMemberNames(llvm::ArrayRef<Nonnull<Declaration*>> members,
-                          StaticScope& scope, ResolveFunctionBodies bodies)
-      -> ErrorOr<Success>;
-
- private:
-  // Mapping from namespaces to their scopes.
-  llvm::DenseMap<const NamespaceDeclaration*, StaticScope> namespace_scopes_;
-
-  // Mapping from declarations to the scope in which they expose a name.
-  llvm::DenseMap<const Declaration*, StaticScope*> exposed_name_scopes_;
-
-  Nonnull<TraceStream*> trace_stream_;
-};
-
-}  // namespace
-
-auto NameResolver::ResolveQualifier(DeclaredName name,
-                                    StaticScope& enclosing_scope,
-                                    bool allow_undeclared)
-    -> ErrorOr<Nonnull<StaticScope*>> {
-  Nonnull<StaticScope*> scope = &enclosing_scope;
-  std::optional<ValueNodeView> scope_node;
-
-  for (const auto& [loc, qualifier] : name.qualifiers()) {
-    // TODO: If we permit qualified names anywhere other than the top level, we
-    // will need to decide whether the first name in the qualifier is looked up
-    // only in the innermost enclosing scope or in all enclosing scopes.
-    CARBON_ASSIGN_OR_RETURN(
-        ValueNodeView node,
-        scope->ResolveHere(scope_node, qualifier, loc, allow_undeclared));
-
-    scope_node = node;
-    if (const auto* namespace_decl =
-            dyn_cast<NamespaceDeclaration>(&node.base())) {
-      scope = &namespace_scopes_[namespace_decl];
-    } else {
-      return ProgramError(name.source_loc())
-             << PrintAsID(node.base()) << " cannot be used as a name qualifier";
-    }
-  }
-  return scope;
-}
-
-auto NameResolver::AddExposedName(DeclaredName name, ValueNodeView value,
-                                  StaticScope& enclosing_scope,
-                                  bool allow_qualified_names)
-    -> ErrorOr<Nonnull<StaticScope*>> {
-  if (name.is_qualified() && !allow_qualified_names) {
-    return ProgramError(name.source_loc())
-           << "qualified declaration names are not permitted in this context";
-  }
-
-  // We are just collecting names at this stage, so nothing is marked as
-  // declared yet. Therefore we don't complain if the qualifier contains a
-  // known but not declared namespace name.
-  CARBON_ASSIGN_OR_RETURN(Nonnull<StaticScope*> scope,
-                          ResolveQualifier(name, enclosing_scope,
-                                           /*allow_undeclared=*/true));
-  CARBON_RETURN_IF_ERROR(scope->Add(
-      name.inner_name(), value, StaticScope::NameStatus::KnownButNotDeclared));
-  return scope;
-}
-
-auto NameResolver::AddExposedNames(const Declaration& declaration,
-                                   StaticScope& enclosing_scope,
-                                   bool allow_qualified_names)
-    -> ErrorOr<Success> {
-  switch (declaration.kind()) {
-    case DeclarationKind::NamespaceDeclaration: {
-      const auto& namespace_decl = cast<NamespaceDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<StaticScope*> scope,
-          AddExposedName(namespace_decl.name(), &namespace_decl,
-                         enclosing_scope, allow_qualified_names));
-      namespace_scopes_.try_emplace(&namespace_decl, scope, &namespace_decl);
-      break;
-    }
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      const auto& iface_decl = cast<ConstraintTypeDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(iface_decl.name(), &iface_decl,
-                                            enclosing_scope,
-                                            allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::DestructorDeclaration: {
-      // TODO: It should not be possible to name the destructor by unqualified
-      // name.
-      const auto& func = cast<DestructorDeclaration>(declaration);
-      // TODO: Add support for qualified destructor declarations. Currently the
-      // syntax for this is
-      //   destructor Class [self: Self] { ... }
-      // but see #2567.
-      CARBON_RETURN_IF_ERROR(enclosing_scope.Add(
-          "destructor", &func, StaticScope::NameStatus::KnownButNotDeclared));
-      break;
-    }
-    case DeclarationKind::FunctionDeclaration: {
-      const auto& func = cast<FunctionDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(func.name(), &func, enclosing_scope,
-                                            allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::ClassDeclaration: {
-      const auto& class_decl = cast<ClassDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(class_decl.name(), &class_decl,
-                                            enclosing_scope,
-                                            allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      const auto& mixin_decl = cast<MixinDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(mixin_decl.name(), &mixin_decl,
-                                            enclosing_scope,
-                                            allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration: {
-      const auto& choice = cast<ChoiceDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(
-          choice.name(), &choice, enclosing_scope, allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::VariableDeclaration: {
-      const auto& var = cast<VariableDeclaration>(declaration);
-      if (var.binding().name() != AnonymousName) {
-        CARBON_RETURN_IF_ERROR(
-            enclosing_scope.Add(var.binding().name(), &var.binding(),
-                                StaticScope::NameStatus::KnownButNotDeclared));
-      }
-      break;
-    }
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      const auto& let = cast<AssociatedConstantDeclaration>(declaration);
-      if (let.binding().name() != AnonymousName) {
-        CARBON_RETURN_IF_ERROR(
-            enclosing_scope.Add(let.binding().name(), &let,
-                                StaticScope::NameStatus::KnownButNotDeclared));
-      }
-      break;
-    }
-    case DeclarationKind::SelfDeclaration: {
-      const auto& self = cast<SelfDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(enclosing_scope.Add("Self", &self));
-      break;
-    }
-    case DeclarationKind::AliasDeclaration: {
-      const auto& alias = cast<AliasDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(AddExposedName(
-          alias.name(), &alias, enclosing_scope, allow_qualified_names));
-      break;
-    }
-    case DeclarationKind::ImplDeclaration:
-    case DeclarationKind::MatchFirstDeclaration:
-    case DeclarationKind::MixDeclaration:
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::ExtendBaseDeclaration: {
-      // These declarations don't have a name to expose.
-      break;
-    }
-  }
-  return Success();
-}
-
-auto NameResolver::ResolveNames(Expression& expression,
-                                const StaticScope& enclosing_scope)
-    -> ErrorOr<std::optional<ValueNodeView>> {
-  return RunWithExtraStack(
-      [&]() { return ResolveNamesImpl(expression, enclosing_scope); });
-}
-
-auto NameResolver::ResolveNamesImpl(Expression& expression,
-                                    const StaticScope& enclosing_scope)
-    -> ErrorOr<std::optional<ValueNodeView>> {
-  switch (expression.kind()) {
-    case ExpressionKind::CallExpression: {
-      auto& call = cast<CallExpression>(expression);
-      CARBON_RETURN_IF_ERROR(ResolveNames(call.function(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(call.argument(), enclosing_scope));
-      break;
-    }
-    case ExpressionKind::FunctionTypeLiteral: {
-      auto& fun_type = cast<FunctionTypeLiteral>(expression);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(fun_type.parameter(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(fun_type.return_type(), enclosing_scope));
-      break;
-    }
-    case ExpressionKind::SimpleMemberAccessExpression: {
-      // If the left-hand side of the `.` is a namespace or alias to namespace,
-      // resolve the name.
-      auto& access = cast<SimpleMemberAccessExpression>(expression);
-      CARBON_ASSIGN_OR_RETURN(std::optional<ValueNodeView> scope,
-                              ResolveNames(access.object(), enclosing_scope));
-      if (!scope) {
-        break;
-      }
-
-      Nonnull<const AstNode*> base = &scope->base();
-      // recursively resolve aliases.
-      while (const auto* alias = dyn_cast<AliasDeclaration>(base)) {
-        if (auto resolved = alias->resolved_declaration()) {
-          base = *resolved;
-        } else {
-          break;
-        }
-      }
-      if (const auto* namespace_decl = dyn_cast<NamespaceDeclaration>(base)) {
-        auto ns_it = namespace_scopes_.find(namespace_decl);
-        CARBON_CHECK(ns_it != namespace_scopes_.end(),
-                     "name resolved to undeclared namespace");
-        CARBON_ASSIGN_OR_RETURN(
-            const auto value_node,
-            ns_it->second.ResolveHere(scope, access.member_name(),
-                                      access.source_loc(),
-                                      /*allow_undeclared=*/false));
-        access.set_value_node(value_node);
-        return {value_node};
-      }
-      break;
-    }
-    case ExpressionKind::CompoundMemberAccessExpression: {
-      auto& access = cast<CompoundMemberAccessExpression>(expression);
-      CARBON_RETURN_IF_ERROR(ResolveNames(access.object(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(access.path(), enclosing_scope));
-      break;
-    }
-    case ExpressionKind::IndexExpression: {
-      auto& index = cast<IndexExpression>(expression);
-      CARBON_RETURN_IF_ERROR(ResolveNames(index.object(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(index.offset(), enclosing_scope));
-      break;
-    }
-    case ExpressionKind::OperatorExpression:
-      for (Nonnull<Expression*> operand :
-           cast<OperatorExpression>(expression).arguments()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*operand, enclosing_scope));
-      }
-      break;
-    case ExpressionKind::TupleLiteral:
-      for (Nonnull<Expression*> field :
-           cast<TupleLiteral>(expression).fields()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*field, enclosing_scope));
-      }
-      break;
-    case ExpressionKind::StructLiteral: {
-      std::set<std::string_view> member_names;
-      for (FieldInitializer& init : cast<StructLiteral>(expression).fields()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(init.expression(), enclosing_scope));
-        if (!member_names.insert(init.name()).second) {
-          return ProgramError(init.expression().source_loc())
-                 << "Duplicate name `" << init.name() << "` in struct literal";
-        }
-      }
-      break;
-    }
-    case ExpressionKind::StructTypeLiteral: {
-      std::set<std::string_view> member_names;
-      for (FieldInitializer& init :
-           cast<StructTypeLiteral>(expression).fields()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(init.expression(), enclosing_scope));
-        if (!member_names.insert(init.name()).second) {
-          return ProgramError(init.expression().source_loc())
-                 << "Duplicate name `" << init.name()
-                 << "` in struct type literal";
-        }
-      }
-      break;
-    }
-    case ExpressionKind::IdentifierExpression: {
-      auto& identifier = cast<IdentifierExpression>(expression);
-      CARBON_ASSIGN_OR_RETURN(
-          const auto value_node,
-          enclosing_scope.Resolve(identifier.name(), identifier.source_loc()));
-      identifier.set_value_node(value_node);
-      return {value_node};
-    }
-    case ExpressionKind::DotSelfExpression: {
-      auto& dot_self = cast<DotSelfExpression>(expression);
-      CARBON_ASSIGN_OR_RETURN(
-          const auto value_node,
-          enclosing_scope.Resolve(".Self", dot_self.source_loc()));
-      dot_self.set_self_binding(const_cast<GenericBinding*>(
-          &cast<GenericBinding>(value_node.base())));
-      break;
-    }
-    case ExpressionKind::IntrinsicExpression:
-      CARBON_RETURN_IF_ERROR(ResolveNames(
-          cast<IntrinsicExpression>(expression).args(), enclosing_scope));
-      break;
-    case ExpressionKind::IfExpression: {
-      auto& if_expr = cast<IfExpression>(expression);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(if_expr.condition(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(if_expr.then_expression(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(if_expr.else_expression(), enclosing_scope));
-      break;
-    }
-    case ExpressionKind::WhereExpression: {
-      auto& where = cast<WhereExpression>(expression);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(where.self_binding().type(), enclosing_scope));
-      // If we're already in a `.Self` context, remember it so that we can
-      // reuse its value for the inner `.Self`.
-      if (auto enclosing_dot_self =
-              enclosing_scope.Resolve(".Self", where.source_loc());
-          enclosing_dot_self.ok()) {
-        where.set_enclosing_dot_self(
-            &cast<GenericBinding>(enclosing_dot_self->base()));
-      }
-      // Introduce `.Self` into scope on the right of the `where` keyword.
-      StaticScope where_scope(&enclosing_scope, &where);
-      CARBON_RETURN_IF_ERROR(where_scope.Add(".Self", &where.self_binding()));
-      for (Nonnull<WhereClause*> clause : where.clauses()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*clause, where_scope));
-      }
-      break;
-    }
-    case ExpressionKind::ArrayTypeLiteral: {
-      auto& array_literal = cast<ArrayTypeLiteral>(expression);
-      CARBON_RETURN_IF_ERROR(ResolveNames(
-          array_literal.element_type_expression(), enclosing_scope));
-      if (array_literal.has_size_expression()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(array_literal.size_expression(), enclosing_scope));
-      }
-      break;
-    }
-    case ExpressionKind::BoolTypeLiteral:
-    case ExpressionKind::BoolLiteral:
-    case ExpressionKind::IntTypeLiteral:
-    case ExpressionKind::IntLiteral:
-    case ExpressionKind::StringLiteral:
-    case ExpressionKind::StringTypeLiteral:
-    case ExpressionKind::TypeTypeLiteral:
-      break;
-    case ExpressionKind::ValueLiteral:
-    case ExpressionKind::BuiltinConvertExpression:
-    case ExpressionKind::BaseAccessExpression:
-      CARBON_FATAL("should not exist before type checking");
-    case ExpressionKind::UnimplementedExpression:
-      return ProgramError(expression.source_loc()) << "Unimplemented";
-  }
-
-  return {std::nullopt};
-}
-
-auto NameResolver::ResolveNames(WhereClause& clause,
-                                const StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack(
-      [&]() { return ResolveNamesImpl(clause, enclosing_scope); });
-}
-
-auto NameResolver::ResolveNamesImpl(WhereClause& clause,
-                                    const StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  switch (clause.kind()) {
-    case WhereClauseKind::ImplsWhereClause: {
-      auto& impls_clause = cast<ImplsWhereClause>(clause);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(impls_clause.type(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(impls_clause.constraint(), enclosing_scope));
-      break;
-    }
-    case WhereClauseKind::EqualsWhereClause: {
-      auto& equals_clause = cast<EqualsWhereClause>(clause);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(equals_clause.lhs(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(equals_clause.rhs(), enclosing_scope));
-      break;
-    }
-    case WhereClauseKind::RewriteWhereClause: {
-      auto& rewrite_clause = cast<RewriteWhereClause>(clause);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(rewrite_clause.replacement(), enclosing_scope));
-      break;
-    }
-  }
-
-  return Success();
-}
-
-auto NameResolver::ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack(
-      [&]() { return ResolveNamesImpl(pattern, enclosing_scope); });
-}
-
-auto NameResolver::ResolveNamesImpl(Pattern& pattern,
-                                    StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  switch (pattern.kind()) {
-    case PatternKind::BindingPattern: {
-      auto& binding = cast<BindingPattern>(pattern);
-      CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), enclosing_scope));
-      if (binding.name() != AnonymousName) {
-        CARBON_RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
-      }
-      break;
-    }
-    case PatternKind::GenericBinding: {
-      auto& binding = cast<GenericBinding>(pattern);
-      // `.Self` is in scope in the context of the type.
-      StaticScope self_scope(&enclosing_scope, &binding);
-      CARBON_RETURN_IF_ERROR(self_scope.Add(".Self", &binding));
-      CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), self_scope));
-      if (binding.name() != AnonymousName) {
-        CARBON_RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
-      }
-      break;
-    }
-    case PatternKind::TuplePattern:
-      for (Nonnull<Pattern*> field : cast<TuplePattern>(pattern).fields()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*field, enclosing_scope));
-      }
-      break;
-    case PatternKind::AlternativePattern: {
-      auto& alternative = cast<AlternativePattern>(pattern);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(alternative.choice_type(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(alternative.arguments(), enclosing_scope));
-      break;
-    }
-    case PatternKind::ExpressionPattern:
-      CARBON_RETURN_IF_ERROR(ResolveNames(
-          cast<ExpressionPattern>(pattern).expression(), enclosing_scope));
-      break;
-    case PatternKind::AutoPattern:
-      break;
-    case PatternKind::VarPattern:
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(cast<VarPattern>(pattern).pattern(), enclosing_scope));
-      break;
-    case PatternKind::AddrPattern:
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(cast<AddrPattern>(pattern).binding(), enclosing_scope));
-      break;
-  }
-
-  return Success();
-}
-
-auto NameResolver::ResolveNames(Statement& statement,
-                                StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack(
-      [&]() { return ResolveNamesImpl(statement, enclosing_scope); });
-}
-
-auto NameResolver::ResolveNamesImpl(Statement& statement,
-                                    StaticScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "resolving stmt `" << PrintAsID(statement)
-                           << "` (" << statement.source_loc() << ")\n";
-  }
-  switch (statement.kind()) {
-    case StatementKind::ExpressionStatement:
-      CARBON_RETURN_IF_ERROR(ResolveNames(
-          cast<ExpressionStatement>(statement).expression(), enclosing_scope));
-      break;
-    case StatementKind::Assign: {
-      auto& assign = cast<Assign>(statement);
-      CARBON_RETURN_IF_ERROR(ResolveNames(assign.lhs(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(assign.rhs(), enclosing_scope));
-      break;
-    }
-    case StatementKind::IncrementDecrement: {
-      auto& inc_dec = cast<IncrementDecrement>(statement);
-      CARBON_RETURN_IF_ERROR(ResolveNames(inc_dec.argument(), enclosing_scope));
-      break;
-    }
-    case StatementKind::VariableDefinition: {
-      auto& def = cast<VariableDefinition>(statement);
-      if (def.has_init()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(def.init(), enclosing_scope));
-      }
-      CARBON_RETURN_IF_ERROR(ResolveNames(def.pattern(), enclosing_scope));
-      if (def.is_returned()) {
-        CARBON_CHECK(def.pattern().kind() == PatternKind::BindingPattern,
-                     "{0}returned var definition can only be a binding pattern",
-                     def.pattern().source_loc());
-        CARBON_RETURN_IF_ERROR(enclosing_scope.AddReturnedVar(
-            ValueNodeView(&cast<BindingPattern>(def.pattern()))));
-      }
-      break;
-    }
-    case StatementKind::If: {
-      auto& if_stmt = cast<If>(statement);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(if_stmt.condition(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(if_stmt.then_block(), enclosing_scope));
-      if (auto else_block = if_stmt.else_block()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**else_block, enclosing_scope));
-      }
-      break;
-    }
-    case StatementKind::ReturnVar: {
-      auto& ret_var_stmt = cast<ReturnVar>(statement);
-      std::optional<ValueNodeView> returned_var_def_view =
-          enclosing_scope.ResolveReturned();
-      if (!returned_var_def_view.has_value()) {
-        return ProgramError(ret_var_stmt.source_loc())
-               << "`return var` is not allowed without a returned var defined "
-                  "in scope.";
-      }
-      ret_var_stmt.set_value_node(*returned_var_def_view);
-      break;
-    }
-    case StatementKind::ReturnExpression: {
-      auto& ret_exp_stmt = cast<ReturnExpression>(statement);
-      std::optional<ValueNodeView> returned_var_def_view =
-          enclosing_scope.ResolveReturned();
-      if (returned_var_def_view.has_value()) {
-        return ProgramError(ret_exp_stmt.source_loc())
-               << "`return <expression>` is not allowed with a returned var "
-                  "defined in scope: "
-               << returned_var_def_view->base().source_loc();
-      }
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(ret_exp_stmt.expression(), enclosing_scope));
-      break;
-    }
-    case StatementKind::Block: {
-      auto& block = cast<Block>(statement);
-      StaticScope block_scope(&enclosing_scope, &block);
-      for (Nonnull<Statement*> sub_statement : block.statements()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*sub_statement, block_scope));
-      }
-      break;
-    }
-    case StatementKind::While: {
-      auto& while_stmt = cast<While>(statement);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(while_stmt.condition(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(while_stmt.body(), enclosing_scope));
-      break;
-    }
-    case StatementKind::For: {
-      auto& for_stmt = cast<For>(statement);
-      StaticScope statement_scope(&enclosing_scope, &for_stmt);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(for_stmt.loop_target(), statement_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(for_stmt.variable_declaration(), statement_scope));
-      CARBON_RETURN_IF_ERROR(ResolveNames(for_stmt.body(), statement_scope));
-
-      break;
-    }
-    case StatementKind::Match: {
-      auto& match = cast<Match>(statement);
-      CARBON_RETURN_IF_ERROR(ResolveNames(match.expression(), enclosing_scope));
-      for (Match::Clause& clause : match.clauses()) {
-        StaticScope clause_scope(&enclosing_scope, &clause.statement());
-        CARBON_RETURN_IF_ERROR(ResolveNames(clause.pattern(), clause_scope));
-        CARBON_RETURN_IF_ERROR(ResolveNames(clause.statement(), clause_scope));
-      }
-      break;
-    }
-    case StatementKind::Break:
-    case StatementKind::Continue:
-      break;
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished resolving stmt `" << PrintAsID(statement)
-                         << "` (" << statement.source_loc() << ")\n";
-  }
-
-  return Success();
-}
-
-auto NameResolver::ResolveMemberNames(
-    llvm::ArrayRef<Nonnull<Declaration*>> members, StaticScope& scope,
-    ResolveFunctionBodies bodies) -> ErrorOr<Success> {
-  for (Nonnull<Declaration*> member : members) {
-    CARBON_RETURN_IF_ERROR(AddExposedNames(*member, scope));
-  }
-  if (bodies != ResolveFunctionBodies::Immediately) {
-    for (Nonnull<Declaration*> member : members) {
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(*member, scope, ResolveFunctionBodies::Skip));
-    }
-  }
-  if (bodies != ResolveFunctionBodies::Skip) {
-    for (Nonnull<Declaration*> member : members) {
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(*member, scope, ResolveFunctionBodies::Immediately));
-    }
-  }
-  return Success();
-}
-
-auto NameResolver::ResolveNames(Declaration& declaration,
-                                StaticScope& enclosing_scope,
-                                ResolveFunctionBodies bodies)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack(
-      [&]() { return ResolveNamesImpl(declaration, enclosing_scope, bodies); });
-}
-
-auto NameResolver::ResolveNamesImpl(Declaration& declaration,
-                                    StaticScope& enclosing_scope,
-                                    ResolveFunctionBodies bodies)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "resolving decl `" << PrintAsID(declaration)
-                           << "` (" << declaration.source_loc() << ")\n";
-  }
-
-  switch (declaration.kind()) {
-    case DeclarationKind::NamespaceDeclaration: {
-      auto& namespace_decl = cast<NamespaceDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<StaticScope*> scope,
-          ResolveQualifier(namespace_decl.name(), enclosing_scope));
-      scope->MarkUsable(namespace_decl.name().inner_name());
-      break;
-    }
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      auto& iface = cast<ConstraintTypeDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(Nonnull<StaticScope*> scope,
-                              ResolveQualifier(iface.name(), enclosing_scope));
-      StaticScope iface_scope(scope, &iface);
-      scope->MarkDeclared(iface.name().inner_name());
-      if (auto params = iface.params()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**params, iface_scope));
-      }
-      scope->MarkUsable(iface.name().inner_name());
-      // Don't resolve names in the type of the self binding. The
-      // ConstraintTypeDeclaration constructor already did that.
-      CARBON_RETURN_IF_ERROR(iface_scope.Add("Self", iface.self()));
-      CARBON_RETURN_IF_ERROR(
-          ResolveMemberNames(iface.members(), iface_scope, bodies));
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      auto& impl = cast<ImplDeclaration>(declaration);
-      StaticScope impl_scope(&enclosing_scope, &impl);
-      for (Nonnull<GenericBinding*> binding : impl.deduced_parameters()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(binding->type(), impl_scope));
-        CARBON_RETURN_IF_ERROR(impl_scope.Add(binding->name(), binding));
-      }
-      CARBON_RETURN_IF_ERROR(ResolveNames(*impl.impl_type(), impl_scope));
-      // Only add `Self` to the impl_scope if it is not already in the enclosing
-      // scope. Add `Self` after we resolve names for the impl_type, so you
-      // can't write something like `impl Vector(Self) as ...`. Add `Self`
-      // before resolving names in the interface, so you can write something
-      // like `impl VeryLongTypeName as AddWith(Self)`
-      if (!enclosing_scope.Resolve("Self", impl.source_loc()).ok()) {
-        CARBON_RETURN_IF_ERROR(AddExposedNames(*impl.self(), impl_scope));
-      }
-      CARBON_RETURN_IF_ERROR(ResolveNames(impl.interface(), impl_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveMemberNames(impl.members(), impl_scope, bodies));
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration: {
-      // A `match_first` declaration does not introduce a scope.
-      for (auto* impl :
-           cast<MatchFirstDeclaration>(declaration).impl_declarations()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*impl, enclosing_scope, bodies));
-      }
-      break;
-    }
-    case DeclarationKind::DestructorDeclaration:
-    case DeclarationKind::FunctionDeclaration: {
-      auto& function = cast<CallableDeclaration>(declaration);
-      // TODO: Destructors should track their qualified name.
-      const DeclaredName& name =
-          isa<FunctionDeclaration>(declaration)
-              ? cast<FunctionDeclaration>(declaration).name()
-              : DeclaredName(function.source_loc(), "destructor");
-      CARBON_ASSIGN_OR_RETURN(Nonnull<StaticScope*> scope,
-                              ResolveQualifier(name, enclosing_scope));
-      StaticScope function_scope(scope, &function);
-      scope->MarkDeclared(name.inner_name());
-      for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*binding, function_scope));
-      }
-      if (function.is_method()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(function.self_pattern(), function_scope));
-      }
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(function.param_pattern(), function_scope));
-      if (auto return_type_expr = function.return_term().type_expression()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(**return_type_expr, function_scope));
-      }
-      scope->MarkUsable(name.inner_name());
-      if (auto body = function.body();
-          body.has_value() && bodies != ResolveFunctionBodies::Skip) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**body, function_scope));
-      }
-      break;
-    }
-    case DeclarationKind::ClassDeclaration: {
-      auto& class_decl = cast<ClassDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<StaticScope*> scope,
-          ResolveQualifier(class_decl.name(), enclosing_scope));
-      StaticScope class_scope(scope, &class_decl);
-      scope->MarkDeclared(class_decl.name().inner_name());
-      if (auto type_params = class_decl.type_params()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**type_params, class_scope));
-      }
-      scope->MarkUsable(class_decl.name().inner_name());
-      CARBON_RETURN_IF_ERROR(AddExposedNames(*class_decl.self(), class_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveMemberNames(class_decl.members(), class_scope, bodies));
-      break;
-    }
-    case DeclarationKind::ExtendBaseDeclaration: {
-      auto& extend_base_decl = cast<ExtendBaseDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(*extend_base_decl.base_class(), enclosing_scope));
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      auto& mixin_decl = cast<MixinDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<StaticScope*> scope,
-          ResolveQualifier(mixin_decl.name(), enclosing_scope));
-      StaticScope mixin_scope(scope, &mixin_decl);
-      scope->MarkDeclared(mixin_decl.name().inner_name());
-      if (auto params = mixin_decl.params()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**params, mixin_scope));
-      }
-      scope->MarkUsable(mixin_decl.name().inner_name());
-      CARBON_RETURN_IF_ERROR(mixin_scope.Add("Self", mixin_decl.self()));
-      CARBON_RETURN_IF_ERROR(
-          ResolveMemberNames(mixin_decl.members(), mixin_scope, bodies));
-      break;
-    }
-    case DeclarationKind::MixDeclaration: {
-      auto& mix_decl = cast<MixDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(ResolveNames(mix_decl.mixin(), enclosing_scope));
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration: {
-      auto& choice = cast<ChoiceDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(Nonnull<StaticScope*> scope,
-                              ResolveQualifier(choice.name(), enclosing_scope));
-      StaticScope choice_scope(scope, &choice);
-      scope->MarkDeclared(choice.name().inner_name());
-      if (auto type_params = choice.type_params()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(**type_params, choice_scope));
-      }
-      // Alternative names are never used unqualified, so we don't need to
-      // add the alternatives to a scope, or introduce a new scope; we only
-      // need to check for duplicates.
-      std::set<std::string_view> alternative_names;
-      for (Nonnull<AlternativeSignature*> alternative : choice.alternatives()) {
-        if (auto params = alternative->parameters()) {
-          CARBON_RETURN_IF_ERROR(ResolveNames(**params, choice_scope));
-        }
-        if (!alternative_names.insert(alternative->name()).second) {
-          return ProgramError(alternative->source_loc())
-                 << "Duplicate name `" << alternative->name()
-                 << "` in choice type";
-        }
-      }
-      scope->MarkUsable(choice.name().inner_name());
-      break;
-    }
-    case DeclarationKind::VariableDeclaration: {
-      auto& var = cast<VariableDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(ResolveNames(var.binding(), enclosing_scope));
-      if (var.has_initializer()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveNames(var.initializer(), enclosing_scope));
-      }
-      break;
-    }
-    case DeclarationKind::InterfaceExtendDeclaration: {
-      auto& extends = cast<InterfaceExtendDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(ResolveNames(*extends.base(), enclosing_scope));
-      break;
-    }
-    case DeclarationKind::InterfaceRequireDeclaration: {
-      auto& require = cast<InterfaceRequireDeclaration>(declaration);
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(*require.impl_type(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(*require.constraint(), enclosing_scope));
-      break;
-    }
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      auto& let = cast<AssociatedConstantDeclaration>(declaration);
-      StaticScope constant_scope(&enclosing_scope, &let);
-      enclosing_scope.MarkDeclared(let.binding().name());
-      CARBON_RETURN_IF_ERROR(ResolveNames(let.binding(), constant_scope));
-      enclosing_scope.MarkUsable(let.binding().name());
-      break;
-    }
-
-    case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL("Unreachable: resolving names for `Self` declaration");
-    }
-
-    case DeclarationKind::AliasDeclaration: {
-      auto& alias = cast<AliasDeclaration>(declaration);
-      CARBON_ASSIGN_OR_RETURN(Nonnull<StaticScope*> scope,
-                              ResolveQualifier(alias.name(), enclosing_scope));
-      scope->MarkDeclared(alias.name().inner_name());
-      CARBON_ASSIGN_OR_RETURN(auto target,
-                              ResolveNames(alias.target(), *scope));
-      if (target && isa<Declaration>(target->base())) {
-        if (auto resolved_declaration = alias.resolved_declaration()) {
-          // Skip if the declaration is already resolved in a previous name
-          // resolution phase.
-          CARBON_CHECK(*resolved_declaration == &target->base());
-        } else {
-          alias.set_resolved_declaration(&cast<Declaration>(target->base()));
-        }
-      }
-      scope->MarkUsable(alias.name().inner_name());
-      break;
-    }
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished resolving decl `"
-                         << PrintAsID(declaration) << "` ("
-                         << declaration.source_loc() << ")\n";
-  }
-
-  return Success();
-}
-
-auto ResolveNames(AST& ast, Nonnull<TraceStream*> trace_stream)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack([&]() -> ErrorOr<Success> {
-    NameResolver resolver(trace_stream);
-    SetFileContext set_file_ctx(*trace_stream, std::nullopt);
-    StaticScope file_scope(trace_stream);
-
-    for (auto* declaration : ast.declarations) {
-      set_file_ctx.update_source_loc(declaration->source_loc());
-      CARBON_RETURN_IF_ERROR(resolver.AddExposedNames(
-          *declaration, file_scope, /*allow_qualified_names=*/true));
-    }
-
-    for (auto* declaration : ast.declarations) {
-      set_file_ctx.update_source_loc(declaration->source_loc());
-      CARBON_RETURN_IF_ERROR(resolver.ResolveNames(
-          *declaration, file_scope,
-          NameResolver::ResolveFunctionBodies::AfterDeclarations));
-    }
-    CARBON_RETURN_IF_ERROR(resolver.ResolveNames(**ast.main_call, file_scope));
-    return Success();
-  });
-}
-
-}  // namespace Carbon

+ 0 - 22
explorer/interpreter/resolve_names.h

@@ -1,22 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_RESOLVE_NAMES_H_
-#define CARBON_EXPLORER_INTERPRETER_RESOLVE_NAMES_H_
-
-#include "explorer/ast/ast.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-
-// Resolves names (IdentifierExpressions) in the AST.
-// On failure, `ast` is left in a partial state and should not be further
-// processed.
-auto ResolveNames(AST& ast, Nonnull<TraceStream*> trace_stream)
-    -> ErrorOr<Success>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_RESOLVE_NAMES_H_

+ 0 - 471
explorer/interpreter/resolve_unformed.cpp

@@ -1,471 +0,0 @@
-// 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 "explorer/interpreter/resolve_unformed.h"
-
-#include <unordered_map>
-
-#include "common/check.h"
-#include "explorer/ast/ast.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/print_as_id.h"
-#include "explorer/interpreter/stack_space.h"
-
-using llvm::cast;
-
-namespace Carbon {
-
-auto FlowFacts::action_type_string(ActionType action) const
-    -> std::string_view {
-  switch (action) {
-    case ActionType::AddInit:
-      return "add init";
-    case ActionType::AddUninit:
-      return "add uninit";
-    case ActionType::Form:
-      return "form";
-    case ActionType::Check:
-      return "check";
-    case ActionType::None:
-      return "none";
-  }
-}
-
-auto FlowFacts::TakeAction(Nonnull<const AstNode*> node, ActionType action,
-                           SourceLocation source_loc, const std::string& name)
-    -> ErrorOr<Success> {
-  switch (action) {
-    case ActionType::AddInit: {
-      AddFact(node, FormedState::MustBeFormed);
-      break;
-    }
-    case ActionType::AddUninit: {
-      AddFact(node, FormedState::Unformed);
-      break;
-    }
-    case ActionType::Form: {
-      // TODO: Use CARBON_CHECK when we are able to handle global variables.
-      auto entry = facts_.find(node);
-      if (entry != facts_.end() &&
-          entry->second.formed_state == FormedState::Unformed) {
-        entry->second.formed_state = FormedState::MayBeFormed;
-      }
-      break;
-    }
-    case ActionType::Check: {
-      // TODO: @slaterlatiao add all available value nodes to flow facts and use
-      // CARBON_CHECK on the following line.
-      auto entry = facts_.find(node);
-      if (entry != facts_.end() &&
-          entry->second.formed_state == FormedState::Unformed) {
-        return ProgramError(source_loc)
-               << "use of uninitialized variable " << name;
-      }
-      break;
-    }
-    case ActionType::None:
-      break;
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result() << action_type_string(action) << " `" << name
-                            << "` (" << source_loc << ")\n";
-  }
-
-  return Success();
-}
-
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Expression*> expression,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success>;
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Pattern*> pattern,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success>;
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Statement*> statement,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success>;
-
-// Traverses the sub-AST rooted at the given node, resolving the formed/unformed
-// states of local variables within it and updating the flow facts.
-template <typename T>
-static auto ResolveUnformed(Nonnull<TraceStream*> trace_stream,
-                            Nonnull<const T*> expression, FlowFacts& flow_facts,
-                            FlowFacts::ActionType action) -> ErrorOr<Success> {
-  return RunWithExtraStack([&] {
-    return ResolveUnformedImpl(trace_stream, expression, flow_facts, action);
-  });
-}
-
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Expression*> expression,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success> {
-  switch (expression->kind()) {
-    case ExpressionKind::IdentifierExpression: {
-      const auto& identifier = cast<IdentifierExpression>(*expression);
-      CARBON_RETURN_IF_ERROR(
-          flow_facts.TakeAction(&identifier.value_node().base(), action,
-                                identifier.source_loc(), identifier.name()));
-      break;
-    }
-    case ExpressionKind::CallExpression: {
-      const auto& call = cast<CallExpression>(*expression);
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &call.argument(), flow_facts, action));
-      break;
-    }
-    case ExpressionKind::IntrinsicExpression: {
-      const auto& intrin = cast<IntrinsicExpression>(*expression);
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &intrin.args(), flow_facts, action));
-      break;
-    }
-    case ExpressionKind::TupleLiteral:
-      for (Nonnull<const Expression*> field :
-           cast<TupleLiteral>(*expression).fields()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveUnformed(trace_stream, field, flow_facts, action));
-      }
-      break;
-    case ExpressionKind::OperatorExpression: {
-      const auto& opt_exp = cast<OperatorExpression>(*expression);
-      if (opt_exp.op() == Operator::AddressOf) {
-        CARBON_CHECK(opt_exp.arguments().size() == 1,
-                     "OperatorExpression with op & can only have 1 argument");
-        CARBON_RETURN_IF_ERROR(
-            // When a variable is taken address of, defer the unformed check to
-            // runtime. A more sound analysis can be implemented when a
-            // points-to analysis is available.
-            // TODO: This isn't enough to permit &x.y or &x[i] when x is
-            // uninitialized, because x.y and x[i] both require x to be
-            // initialized.
-            ResolveUnformed(trace_stream, opt_exp.arguments().front(),
-                            flow_facts, FlowFacts::ActionType::Form));
-      } else {
-        for (Nonnull<const Expression*> operand : opt_exp.arguments()) {
-          CARBON_RETURN_IF_ERROR(
-              ResolveUnformed(trace_stream, operand, flow_facts, action));
-        }
-      }
-      break;
-    }
-    case ExpressionKind::StructLiteral:
-      for (const FieldInitializer& init :
-           cast<StructLiteral>(*expression).fields()) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &init.expression(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::Check));
-      }
-      break;
-    case ExpressionKind::SimpleMemberAccessExpression:
-    case ExpressionKind::CompoundMemberAccessExpression:
-    case ExpressionKind::BaseAccessExpression:
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &cast<MemberAccessExpression>(*expression).object(),
-          flow_facts, FlowFacts::ActionType::Check));
-      break;
-    case ExpressionKind::BuiltinConvertExpression:
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream,
-          cast<BuiltinConvertExpression>(*expression).source_expression(),
-          flow_facts, FlowFacts::ActionType::Check));
-      break;
-    case ExpressionKind::IndexExpression:
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &cast<IndexExpression>(*expression).object(),
-          flow_facts, FlowFacts::ActionType::Check));
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &cast<IndexExpression>(*expression).offset(),
-          flow_facts, FlowFacts::ActionType::Check));
-      break;
-    case ExpressionKind::IfExpression: {
-      const auto& if_exp = cast<IfExpression>(*expression);
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &if_exp.condition(),
-                                             flow_facts,
-                                             FlowFacts::ActionType::Check));
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &if_exp.then_expression(), flow_facts, action));
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &if_exp.else_expression(), flow_facts, action));
-      break;
-    }
-    case ExpressionKind::DotSelfExpression:
-    case ExpressionKind::IntLiteral:
-    case ExpressionKind::BoolLiteral:
-    case ExpressionKind::BoolTypeLiteral:
-    case ExpressionKind::IntTypeLiteral:
-    case ExpressionKind::StringLiteral:
-    case ExpressionKind::StringTypeLiteral:
-    case ExpressionKind::TypeTypeLiteral:
-    case ExpressionKind::ValueLiteral:
-    case ExpressionKind::WhereExpression:
-    case ExpressionKind::StructTypeLiteral:
-    case ExpressionKind::UnimplementedExpression:
-    case ExpressionKind::FunctionTypeLiteral:
-    case ExpressionKind::ArrayTypeLiteral:
-      break;
-  }
-
-  return Success();
-}
-
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Pattern*> pattern,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success> {
-  switch (pattern->kind()) {
-    case PatternKind::BindingPattern: {
-      const auto& binding_pattern = cast<BindingPattern>(*pattern);
-      CARBON_RETURN_IF_ERROR(flow_facts.TakeAction(&binding_pattern, action,
-                                                   binding_pattern.source_loc(),
-                                                   binding_pattern.name()));
-    } break;
-    case PatternKind::TuplePattern:
-      for (Nonnull<const Pattern*> field :
-           cast<TuplePattern>(*pattern).fields()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveUnformed(trace_stream, field, flow_facts, action));
-      }
-      break;
-    case PatternKind::GenericBinding:
-    case PatternKind::AlternativePattern:
-    case PatternKind::ExpressionPattern:
-    case PatternKind::AutoPattern:
-    case PatternKind::VarPattern:
-    case PatternKind::AddrPattern:
-      // do nothing
-      break;
-  }
-  return Success();
-}
-
-static auto ResolveUnformedImpl(Nonnull<TraceStream*> trace_stream,
-                                Nonnull<const Statement*> statement,
-                                FlowFacts& flow_facts,
-                                FlowFacts::ActionType action)
-    -> ErrorOr<Success> {
-  if (trace_stream->is_enabled()) {
-    trace_stream->Start() << "resolving-unformed in stmt `"
-                          << PrintAsID(*statement) << "` ("
-                          << statement->source_loc() << ")\n";
-  }
-  switch (statement->kind()) {
-    case StatementKind::Block: {
-      const auto& block = cast<Block>(*statement);
-      for (const auto* block_statement : block.statements()) {
-        CARBON_RETURN_IF_ERROR(
-            ResolveUnformed(trace_stream, block_statement, flow_facts, action));
-      }
-      break;
-    }
-    case StatementKind::VariableDefinition: {
-      const auto& def = cast<VariableDefinition>(*statement);
-      if (def.has_init()) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &def.pattern(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::AddInit));
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &def.init(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::Check));
-      } else {
-        CARBON_RETURN_IF_ERROR(
-            ResolveUnformed(trace_stream, &def.pattern(), flow_facts,
-                            FlowFacts::ActionType::AddUninit));
-      }
-      break;
-    }
-    case StatementKind::ReturnVar: {
-      const auto& ret_var = cast<ReturnVar>(*statement);
-      const auto& binding_pattern =
-          cast<BindingPattern>(ret_var.value_node().base());
-      CARBON_RETURN_IF_ERROR(
-          flow_facts.TakeAction(&binding_pattern, FlowFacts::ActionType::Check,
-                                ret_var.source_loc(), binding_pattern.name()));
-      break;
-    }
-    case StatementKind::ReturnExpression: {
-      const auto& ret_exp_stmt = cast<ReturnExpression>(*statement);
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &ret_exp_stmt.expression(), flow_facts,
-                          FlowFacts::ActionType::Check));
-      break;
-    }
-    case StatementKind::Assign: {
-      const auto& assign = cast<Assign>(*statement);
-      if (assign.op() != AssignOperator::Plain) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &assign.lhs(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::Check));
-      } else if (assign.lhs().kind() == ExpressionKind::IdentifierExpression) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &assign.lhs(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::Form));
-      } else {
-        // TODO: Support checking non-identifier lhs expression.
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &assign.lhs(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::None));
-      }
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &assign.rhs(),
-                                             flow_facts,
-                                             FlowFacts::ActionType::Check));
-      break;
-    }
-    case StatementKind::IncrementDecrement: {
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &cast<IncrementDecrement>(statement)->argument(),
-          flow_facts, FlowFacts::ActionType::Check));
-      break;
-    }
-    case StatementKind::ExpressionStatement: {
-      const auto& exp_stmt = cast<ExpressionStatement>(*statement);
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &exp_stmt.expression(), flow_facts, action));
-      break;
-    }
-    case StatementKind::If: {
-      const auto& if_stmt = cast<If>(*statement);
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &if_stmt.condition(),
-                                             flow_facts,
-                                             FlowFacts::ActionType::Check));
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(
-          trace_stream, &if_stmt.then_block(), flow_facts, action));
-      if (if_stmt.else_block().has_value()) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(
-            trace_stream, *if_stmt.else_block(), flow_facts, action));
-      }
-      break;
-    }
-    case StatementKind::While: {
-      const auto& while_stmt = cast<While>(*statement);
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &while_stmt.condition(), flow_facts,
-                          FlowFacts::ActionType::Check));
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &while_stmt.body(),
-                                             flow_facts, action));
-      break;
-    }
-    case StatementKind::Match: {
-      const auto& match = cast<Match>(*statement);
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &match.expression(),
-                                             flow_facts,
-                                             FlowFacts::ActionType::Check));
-      for (const auto& clause : match.clauses()) {
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, &clause.pattern(),
-                                               flow_facts,
-                                               FlowFacts::ActionType::Check));
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(
-            trace_stream, &clause.statement(), flow_facts, action));
-      }
-      break;
-    }
-    case StatementKind::For: {
-      const auto& for_stmt = cast<For>(*statement);
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &for_stmt.loop_target(), flow_facts,
-                          FlowFacts::ActionType::Check));
-      CARBON_RETURN_IF_ERROR(
-          ResolveUnformed(trace_stream, &for_stmt.body(), flow_facts, action));
-      break;
-    }
-    case StatementKind::Break:
-    case StatementKind::Continue:
-      // do nothing
-      break;
-  }
-  return Success();
-}
-
-static auto ResolveUnformed(Nonnull<TraceStream*> trace_stream,
-                            Nonnull<const Declaration*> declaration)
-    -> ErrorOr<Success>;
-
-static auto ResolveUnformed(
-    Nonnull<TraceStream*> trace_stream,
-    llvm::ArrayRef<Nonnull<const Declaration*>> declarations)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack([trace_stream, declarations]() -> ErrorOr<Success> {
-    for (Nonnull<const Declaration*> declaration : declarations) {
-      CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, declaration));
-    }
-    return Success();
-  });
-}
-
-static auto ResolveUnformed(Nonnull<TraceStream*> trace_stream,
-                            Nonnull<const Declaration*> declaration)
-    -> ErrorOr<Success> {
-  SetFileContext set_file_ctx(*trace_stream, declaration->source_loc());
-
-  if (trace_stream->is_enabled()) {
-    trace_stream->Start() << "resolving-unformed in decl `"
-                          << PrintAsID(*declaration) << "` ("
-                          << declaration->source_loc() << ")\n";
-  }
-  switch (declaration->kind()) {
-    // Checks formed/unformed state intraprocedurally.
-    // Can be extended to an interprocedural analysis when a call graph is
-    // available.
-    case DeclarationKind::FunctionDeclaration:
-    case DeclarationKind::DestructorDeclaration: {
-      const auto& callable = cast<CallableDeclaration>(*declaration);
-      const auto callable_body = callable.body();
-      if (callable_body) {
-        FlowFacts flow_facts(trace_stream);
-        CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, *callable_body,
-                                               flow_facts,
-                                               FlowFacts::ActionType::None));
-      }
-      break;
-    }
-    case DeclarationKind::NamespaceDeclaration:
-    case DeclarationKind::MixDeclaration:
-    case DeclarationKind::MatchFirstDeclaration:
-    case DeclarationKind::ChoiceDeclaration:
-    case DeclarationKind::VariableDeclaration:
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration:
-    case DeclarationKind::SelfDeclaration:
-    case DeclarationKind::AliasDeclaration:
-    case DeclarationKind::ExtendBaseDeclaration:
-      // do nothing
-      break;
-    case DeclarationKind::ClassDeclaration:
-      return ResolveUnformed(trace_stream,
-                             cast<ClassDeclaration>(declaration)->members());
-    case DeclarationKind::MixinDeclaration:
-      return ResolveUnformed(trace_stream,
-                             cast<MixinDeclaration>(declaration)->members());
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration:
-      return ResolveUnformed(
-          trace_stream,
-          cast<ConstraintTypeDeclaration>(declaration)->members());
-    case DeclarationKind::ImplDeclaration:
-      return ResolveUnformed(trace_stream,
-                             cast<ImplDeclaration>(declaration)->members());
-  }
-  return Success();
-}
-
-auto ResolveUnformed(Nonnull<TraceStream*> trace_stream, const AST& ast)
-    -> ErrorOr<Success> {
-  for (auto* declaration : ast.declarations) {
-    CARBON_RETURN_IF_ERROR(ResolveUnformed(trace_stream, declaration));
-  }
-  return Success();
-}
-
-}  // namespace Carbon

+ 0 - 75
explorer/interpreter/resolve_unformed.h

@@ -1,75 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_RESOLVE_UNFORMED_H_
-#define CARBON_EXPLORER_INTERPRETER_RESOLVE_UNFORMED_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "explorer/ast/ast.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/trace_stream.h"
-
-namespace Carbon {
-
-// Maps AST nodes to flow facts within a function.
-class FlowFacts {
- public:
-  explicit FlowFacts(Nonnull<TraceStream*> trace_stream)
-      : trace_stream_(trace_stream) {}
-
-  enum class ActionType {
-    // Adds a must-be-formed flow fact.
-    // Used at `VariableDefinition` with initialization.
-    AddInit,
-    // Adds an unformed flow fact.
-    // Used at `VariableDefinition` without initialization.
-    AddUninit,
-    // Marks an unformed flow fact as may-be-formed.
-    // Used at AST nodes that potentially initializes a variable.
-    Form,
-    // Returns compilation error if the AST node is impossible to be formed.
-    // Used at AST nodes that uses a variable.
-    Check,
-    // Used in traversing children nodes without an acion to take.
-    None,
-  };
-
-  auto action_type_string(ActionType action) const -> std::string_view;
-
-  // Take action on flow facts based on `ActionType`.
-  auto TakeAction(Nonnull<const AstNode*> node, ActionType action,
-                  SourceLocation source_loc, const std::string& name)
-      -> ErrorOr<Success>;
-
- private:
-  enum class FormedState {
-    MustBeFormed,
-    MayBeFormed,
-    Unformed,
-  };
-  // Aggregate information about a AstNode being analyzed.
-  struct Fact {
-    FormedState formed_state;
-  };
-
-  void AddFact(Nonnull<const AstNode*> node, const FormedState state) {
-    CARBON_CHECK(facts_.find(node) == facts_.end());
-    facts_.insert({node, {state}});
-  }
-
-  std::unordered_map<Nonnull<const AstNode*>, Fact> facts_;
-  Nonnull<TraceStream*> trace_stream_;
-};
-
-// An intraprocedural forward analysis that checks the may-be-formed states on
-// local variables. Returns compilation error on usage of must-be-unformed
-// variables.
-auto ResolveUnformed(Nonnull<TraceStream*> trace_stream, const AST& ast)
-    -> ErrorOr<Success>;
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_RESOLVE_UNFORMED_H_

+ 0 - 75
explorer/interpreter/stack.h

@@ -1,75 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_STACK_H_
-#define CARBON_EXPLORER_INTERPRETER_STACK_H_
-
-#include <cstddef>
-#include <iterator>
-#include <vector>
-
-#include "common/check.h"
-
-namespace Carbon {
-
-// A stack data structure.
-template <class T>
-struct Stack {
-  // NOLINTNEXTLINE(readability-identifier-naming)
-  using const_iterator = typename std::vector<T>::const_reverse_iterator;
-
-  // Creates an empty instance.
-  Stack() = default;
-
-  // Creates an instance containing just `x`.
-  explicit Stack(T x) : Stack() { Push(std::move(x)); }
-
-  // Pushes `x` onto the top of the stack.
-  void Push(T x) { elements_.push_back(std::move(x)); }
-
-  // Removes and returns the top element of the stack.
-  //
-  // - Requires: !this->IsEmpty()
-  auto Pop() -> T {
-    CARBON_CHECK(!empty(), "Can't pop from empty stack.");
-    auto r = std::move(elements_.back());
-    elements_.pop_back();
-    return r;
-  }
-
-  // Removes the top `n` elements of the stack.
-  //
-  // - Requires: n >= 0 && n <= Count()
-  void Pop(int n) {
-    CARBON_CHECK(n >= 0, "Negative pop count disallowed.");
-    CARBON_CHECK(static_cast<size_t>(n) <= elements_.size(),
-                 "Can only pop as many elements as stack has.");
-    elements_.erase(elements_.end() - n, elements_.end());
-  }
-
-  // Returns the top element of the stack.
-  //
-  // - Requires: !this->IsEmpty()
-  auto Top() const -> const T& {
-    CARBON_CHECK(!empty(), "Empty stack has no Top().");
-    return elements_.back();
-  }
-
-  // Returns `true` iff `Count() > 0`.
-  auto empty() const -> bool { return elements_.empty(); }
-
-  // Returns the number of elements in `*this`.
-  auto size() const -> int { return elements_.size(); }
-
-  // Iterates over the Stack from top to bottom.
-  auto begin() const -> const_iterator { return elements_.crbegin(); }
-  auto end() const -> const_iterator { return elements_.crend(); }
-
- private:
-  std::vector<T> elements_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_STACK_H_

+ 0 - 49
explorer/interpreter/stack_space.cpp

@@ -1,49 +0,0 @@
-// 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 "explorer/interpreter/stack_space.h"
-
-#include "common/check.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/CrashRecoveryContext.h"
-
-namespace Carbon::Internal {
-
-static constexpr int64_t SufficientStack = 256 << 10;
-static constexpr int64_t DesiredStackSpace = 8 << 20;
-
-static LLVM_THREAD_LOCAL intptr_t bottom_of_stack = 0;
-
-// Returns the current bottom of stack.
-LLVM_NO_SANITIZE("address")
-LLVM_ATTRIBUTE_NOINLINE static auto GetStackPointer() -> intptr_t {
-#if __GNUC__ || __has_builtin(__builtin_frame_address)
-  return reinterpret_cast<intptr_t>(__builtin_frame_address(0));
-#else
-  char char_on_stack = 0;
-  char* volatile ptr = &char_on_stack;
-  return reinterpret_cast<intptr_t>(ptr);
-#endif
-}
-
-auto IsStackSpaceNearlyExhausted() -> bool {
-  if (bottom_of_stack == 0) {
-    // Not initialized on the thread; always start a new thread.
-    return true;
-  }
-  return std::abs(GetStackPointer() - bottom_of_stack) >
-         (DesiredStackSpace - SufficientStack);
-}
-
-auto RunWithExtraStackHelper(llvm::function_ref<void()> fn) -> void {
-  llvm::CrashRecoveryContext context;
-  context.RunSafelyOnThread(
-      [&] {
-        bottom_of_stack = GetStackPointer();
-        fn();
-      },
-      DesiredStackSpace);
-}
-
-}  // namespace Carbon::Internal

+ 0 - 48
explorer/interpreter/stack_space.h

@@ -1,48 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_STACK_SPACE_H_
-#define CARBON_EXPLORER_INTERPRETER_STACK_SPACE_H_
-
-#include <optional>
-
-#include "llvm/ADT/STLFunctionalExtras.h"
-
-namespace Carbon {
-
-namespace Internal {
-
-// Returns true if a new thread should be started for more stack space.
-auto IsStackSpaceNearlyExhausted() -> bool;
-
-// Starts a thread to run the function.
-auto RunWithExtraStackHelper(llvm::function_ref<void()> fn) -> void;
-
-}  // namespace Internal
-
-// Runs `fn` after ensuring there is a reasonable amount of space left on the
-// stack for it to run in. This will run `fn` in a separate thread if there is
-// not enough space left on the current stack, or if RunWithExtraStack didn't
-// create the current thread.
-//
-// Usage:
-//   return RunWithExtraStack([&]() -> ReturnType {
-//         <function body>
-//       });
-template <typename Fn>
-auto RunWithExtraStack(Fn fn) -> decltype(fn()) {
-  using ReturnType = decltype(fn());
-  static_assert(!std::is_reference_v<ReturnType>);
-  if (Internal::IsStackSpaceNearlyExhausted()) {
-    std::optional<ReturnType> result;
-    Internal::RunWithExtraStackHelper([&] { result = fn(); });
-    return std::move(*result);
-  } else {
-    return fn();
-  }
-}
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_STACK_SPACE_H_

+ 0 - 6662
explorer/interpreter/type_checker.cpp

@@ -1,6662 +0,0 @@
-// 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 "explorer/interpreter/type_checker.h"
-
-#include <deque>
-#include <map>
-#include <optional>
-#include <set>
-#include <string>
-#include <string_view>
-#include <tuple>
-#include <vector>
-
-#include "common/check.h"
-#include "common/error.h"
-#include "common/ostream.h"
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/pattern.h"
-#include "explorer/ast/value.h"
-#include "explorer/ast/value_transform.h"
-#include "explorer/base/arena.h"
-#include "explorer/base/error_builders.h"
-#include "explorer/base/print_as_id.h"
-#include "explorer/base/source_location.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/impl_scope.h"
-#include "explorer/interpreter/interpreter.h"
-#include "explorer/interpreter/pattern_analysis.h"
-#include "explorer/interpreter/pattern_match.h"
-#include "explorer/interpreter/type_structure.h"
-#include "explorer/interpreter/type_utils.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/TinyPtrVector.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/SaveAndRestore.h"
-
-using llvm::cast;
-using llvm::dyn_cast;
-using llvm::isa;
-
-namespace Carbon {
-
-auto TypeChecker::IsSameType(Nonnull<const Value*> type1,
-                             Nonnull<const Value*> type2,
-                             const ImplScope& /*impl_scope*/) const -> bool {
-  return TypeEqual(type1, type2, std::nullopt);
-}
-
-auto TypeChecker::ExpectExactType(SourceLocation source_loc,
-                                  std::string_view context,
-                                  Nonnull<const Value*> expected,
-                                  Nonnull<const Value*> actual,
-                                  const ImplScope& impl_scope) const
-    -> ErrorOr<Success> {
-  if (!IsSameType(expected, actual, impl_scope)) {
-    return ProgramError(source_loc) << "type error in " << context << "\n"
-                                    << "expected: " << *expected << "\n"
-                                    << "actual: " << *actual;
-  }
-  return Success();
-}
-
-static auto ExpectPointerType(SourceLocation source_loc,
-                              std::string_view context,
-                              Nonnull<const Value*> actual)
-    -> ErrorOr<Success> {
-  // TODO: Try to resolve in equality context.
-  if (actual->kind() != Value::Kind::PointerType) {
-    return ProgramError(source_loc) << "type error in " << context << "\n"
-                                    << "expected a pointer type\n"
-                                    << "actual: " << *actual;
-  }
-  return Success();
-}
-
-// Expect that a type is complete. Issue a diagnostic if not.
-static auto ExpectCompleteType(SourceLocation source_loc,
-                               std::string_view context,
-                               Nonnull<const Value*> type) -> ErrorOr<Success> {
-  CARBON_CHECK(IsType(type));
-
-  switch (type->kind()) {
-    case Value::Kind::IntValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::StructValue:
-    case Value::Kind::TupleValue:
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::AlternativeValue:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::StringValue:
-    case Value::Kind::UninitializedValue:
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-    case Value::Kind::ParameterizedEntityName:
-    case Value::Kind::MemberName:
-    case Value::Kind::MixinPseudoType:
-      CARBON_FATAL("should not see non-type values");
-
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::StringType:
-    case Value::Kind::PointerType:
-    case Value::Kind::TypeType:
-    case Value::Kind::FunctionType:
-    case Value::Kind::StructType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::VariableType:
-    case Value::Kind::AssociatedConstant:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfNamespaceName: {
-      // These types are always complete.
-      return Success();
-    }
-
-    case Value::Kind::StaticArrayType:
-      // TODO: This should probably be complete only if the element type is
-      // complete.
-      return Success();
-
-    case Value::Kind::TupleType: {
-      // TODO: Tuple types should be complete only if all element types are
-      // complete.
-      return Success();
-    }
-
-    // TODO: Once we support forward-declarations, make sure we have an actual
-    // definition in these cases.
-    case Value::Kind::NominalClassType: {
-      if (cast<NominalClassType>(type)->declaration().is_declared()) {
-        return Success();
-      }
-      break;
-    }
-    case Value::Kind::NamedConstraintType: {
-      if (cast<NamedConstraintType>(type)->declaration().is_declared()) {
-        return Success();
-      }
-      break;
-    }
-    case Value::Kind::InterfaceType: {
-      if (cast<InterfaceType>(type)->declaration().is_declared()) {
-        return Success();
-      }
-      break;
-    }
-    case Value::Kind::ChoiceType: {
-      if (cast<ChoiceType>(type)->declaration().is_declared()) {
-        return Success();
-      }
-      break;
-    }
-
-    case Value::Kind::AutoType: {
-      // Undeduced `auto` is considered incomplete.
-      break;
-    }
-  }
-
-  return ProgramError(source_loc)
-         << "incomplete type `" << *type << "` used in " << context;
-}
-
-// Expect that a type is concrete. Issue a diagnostic if not.
-static auto ExpectConcreteType(SourceLocation source_loc,
-                               Nonnull<const Value*> type) -> ErrorOr<Success> {
-  CARBON_CHECK(IsType(type));
-
-  if (const auto* dest_class = dyn_cast<NominalClassType>(type)) {
-    if (dest_class->declaration().extensibility() ==
-        ClassExtensibility::Abstract) {
-      return ProgramError(source_loc) << "Cannot instantiate abstract class "
-                                      << dest_class->declaration().name();
-    }
-  }
-  return Success();
-}
-
-// Returns whether `type` is a placeholder type, which is a second-class type
-// that cannot be the type of a binding but can be the type of an expression.
-static auto IsPlaceholderType(Nonnull<const Value*> type) -> bool {
-  CARBON_CHECK(IsType(type), "expected a type, but found {0}", *type);
-  return isa<TypeOfParameterizedEntityName, TypeOfMemberName,
-             TypeOfMixinPseudoType, TypeOfNamespaceName>(type);
-}
-
-static auto ExpectResolvedBindingType(const BindingPattern& binding,
-                                      Nonnull<const Value*> type)
-    -> ErrorOr<Success> {
-  switch (type->kind()) {
-    case Value::Kind::AutoType: {
-      auto error = ProgramError(binding.source_loc());
-      error << "cannot deduce `auto` type for ";
-      if (type != &binding.type().value()) {
-        error << *type << " in ";
-      }
-      return error << binding;
-    }
-    case Value::Kind::StructType: {
-      const auto fields = cast<StructType>(type)->fields();
-      for (const auto& field : fields) {
-        if (auto result = ExpectResolvedBindingType(binding, field.value);
-            !result.ok()) {
-          return result;
-        }
-      }
-      return Success();
-    }
-    case Value::Kind::TupleType: {
-      const auto elems = cast<TupleType>(type)->elements();
-      for (const auto* elem : elems) {
-        if (auto result = ExpectResolvedBindingType(binding, elem);
-            !result.ok()) {
-          return result;
-        }
-      }
-      return Success();
-    }
-    case Value::Kind::PointerType:
-      return ExpectResolvedBindingType(
-          binding, &cast<PointerType>(type)->pointee_type());
-    case Value::Kind::StaticArrayType: {
-      const auto* array_type = cast<StaticArrayType>(type);
-      if (!array_type->has_size()) {
-        auto error = ProgramError(binding.source_loc());
-        error << "cannot deduce size for ";
-        if (type != &binding.type().value()) {
-          error << *array_type << " in ";
-        }
-        return error << binding;
-      }
-      return ExpectResolvedBindingType(binding, &array_type->element_type());
-    }
-    default:
-      return Success();
-  }
-}
-
-// Returns whether the given value is template-dependent, that is, if it
-// depends on any template parameter.
-static auto DependsOnTemplateParameter(Nonnull<const Value*> value) -> bool {
-  bool mentions_no_template_parameters =
-      VisitNestedValues(value, [](Nonnull<const Value*> nested) -> bool {
-        if (const auto* var_type = dyn_cast<VariableType>(nested)) {
-          return var_type->binding().binding_kind() !=
-                 GenericBinding::BindingKind::Template;
-        }
-        return true;
-      });
-  return !mentions_no_template_parameters;
-}
-
-// Returns whether all template parameters in `bindings` are saturated: that
-// is, they have arguments that are not dependent on any template parameter.
-// This indicates that we're ready to perform template instantiation.
-static auto IsTemplateSaturated(const Bindings& bindings) -> bool {
-  for (auto [binding, value] : bindings.args()) {
-    if (binding->binding_kind() == GenericBinding::BindingKind::Template &&
-        DependsOnTemplateParameter(value)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Returns whether all template parameters in `params` are saturated: that they
-// have template argument values specified.
-static auto IsTemplateSaturated(
-    llvm::ArrayRef<Nonnull<const GenericBinding*>> bindings) -> bool {
-  for (const auto* binding : bindings) {
-    if (binding->binding_kind() == GenericBinding::BindingKind::Template &&
-        !binding->has_template_value()) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Returns the named field, or None if not found.
-static auto FindField(llvm::ArrayRef<NamedValue> fields,
-                      const std::string& field_name)
-    -> std::optional<NamedValue> {
-  const auto* it = std::find_if(
-      fields.begin(), fields.end(),
-      [&](const NamedValue& field) { return field.name == field_name; });
-  if (it == fields.end()) {
-    return std::nullopt;
-  }
-  return *it;
-}
-
-auto TypeChecker::FieldTypes(SourceLocation source_loc,
-                             std::string_view context,
-                             const NominalClassType& class_type) const
-    -> ErrorOr<std::vector<NamedValue>> {
-  CARBON_RETURN_IF_ERROR(ExpectCompleteType(source_loc, context, &class_type));
-
-  std::vector<NamedValue> field_types;
-  for (Nonnull<Declaration*> m : class_type.declaration().members()) {
-    switch (m->kind()) {
-      case DeclarationKind::VariableDeclaration: {
-        const auto& var = cast<VariableDeclaration>(*m);
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> field_type,
-            Substitute(class_type.bindings(), &var.binding().static_type()));
-        field_types.push_back({var.binding().name(), field_type});
-        break;
-      }
-      default:
-        break;
-    }
-  }
-  return field_types;
-}
-
-auto TypeChecker::FieldTypesWithBase(SourceLocation source_loc,
-                                     std::string_view context,
-                                     const NominalClassType& class_type) const
-    -> ErrorOr<std::vector<NamedValue>> {
-  CARBON_ASSIGN_OR_RETURN(auto fields,
-                          FieldTypes(source_loc, context, class_type));
-  if (const auto base_type = class_type.base()) {
-    CARBON_ASSIGN_OR_RETURN(
-        std::vector<NamedValue> base_fields,
-        FieldTypesWithBase(source_loc, context, *base_type.value()));
-    fields.emplace_back(NamedValue{std::string(NominalClassValue::BaseField),
-                                   base_type.value()});
-  }
-  return fields;
-}
-
-auto TypeChecker::IsImplicitlyConvertible(
-    SourceLocation source_loc, Nonnull<const Value*> source,
-    Nonnull<const Value*> destination, const ImplScope& impl_scope,
-    bool allow_user_defined_conversions) const -> ErrorOr<bool> {
-  // Check for an exact match to avoid impl lookup in this common case.
-  CARBON_CHECK(IsNonDeduceableType(source));
-  CARBON_CHECK(IsNonDeduceableType(destination));
-  if (IsSameType(source, destination, impl_scope)) {
-    return true;
-  }
-
-  // If the source is a type, or a type-like tuple, then it might convert to
-  // another type-of-type. This can't be done by `ImplicitAs` because it
-  // depends on the value, not only on the type.
-  //
-  // TODO: We can't tell whether the conversion to this type-of-type would
-  // work, because we don't have the source value, only its type. So we allow
-  // this conversion if the source converts to `type`, even if it won't convert
-  // to the actual destination type. We'll catch any problems when we actually
-  // come to perform the conversion.
-  if (isa<TupleType>(source) && IsTypeOfType(destination)) {
-    return IsBuiltinConversion(source_loc, source, arena_->New<TypeType>(),
-                               impl_scope, allow_user_defined_conversions);
-  }
-  if (IsTypeOfType(source) && IsTypeOfType(destination)) {
-    return true;
-  }
-
-  // If we're not supposed to look for a user-defined conversion, check for
-  // builtin conversions, which are normally found by impl lookup.
-  if (!allow_user_defined_conversions) {
-    return IsBuiltinConversion(source_loc, source, destination, impl_scope,
-                               allow_user_defined_conversions);
-  }
-
-  // We didn't find a builtin implicit conversion. Check if a user-defined one
-  // exists.
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const InterfaceType*> iface_type,
-      GetBuiltinInterfaceType(
-          source_loc, BuiltinInterfaceName{Builtin::ImplicitAs, destination}));
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<Nonnull<const Witness*>> conversion_witness,
-      impl_scope.TryResolve(iface_type, source, source_loc, *this,
-                            /*bindings=*/{}, /*diagnose_missing_impl=*/false));
-  return conversion_witness.has_value();
-}
-
-auto TypeChecker::IsBuiltinConversion(SourceLocation source_loc,
-                                      Nonnull<const Value*> source,
-                                      Nonnull<const Value*> destination,
-                                      const ImplScope& impl_scope,
-                                      bool allow_user_defined_conversions) const
-    -> ErrorOr<bool> {
-  switch (source->kind()) {
-    case Value::Kind::StructType:
-      switch (destination->kind()) {
-        case Value::Kind::StructType: {
-          llvm::ArrayRef<NamedValue> source_fields =
-              cast<StructType>(*source).fields();
-          llvm::ArrayRef<NamedValue> destination_fields =
-              cast<StructType>(*destination).fields();
-          // Ensure every source field exists in the destination type.
-          for (const auto& source_field : source_fields) {
-            if (!FindField(destination_fields, source_field.name)) {
-              return false;
-            }
-          }
-          // Ensure every destination field is initialized.
-          for (const auto& destination_field : destination_fields) {
-            std::optional<NamedValue> source_field =
-                FindField(source_fields, destination_field.name);
-            if (!source_field.has_value()) {
-              return false;
-            }
-            CARBON_ASSIGN_OR_RETURN(
-                bool convertible,
-                IsImplicitlyConvertible(source_loc, source_field->value,
-                                        destination_field.value, impl_scope,
-                                        allow_user_defined_conversions));
-            if (!convertible) {
-              return false;
-            }
-          }
-          return true;
-        }
-        case Value::Kind::NominalClassType: {
-          CARBON_ASSIGN_OR_RETURN(
-              std::vector<NamedValue> field_types,
-              FieldTypesWithBase(source_loc, "implicit conversion",
-                                 cast<NominalClassType>(*destination)));
-          CARBON_ASSIGN_OR_RETURN(
-              bool convertible,
-              IsImplicitlyConvertible(
-                  source_loc, source, arena_->New<StructType>(field_types),
-                  impl_scope, allow_user_defined_conversions));
-          if (convertible) {
-            return true;
-          }
-          break;
-        }
-        case Value::Kind::TypeType:
-        case Value::Kind::InterfaceType:
-        case Value::Kind::NamedConstraintType:
-        case Value::Kind::ConstraintType:
-          // A value of empty struct type implicitly converts to a type.
-          if (cast<StructType>(*source).fields().empty()) {
-            return true;
-          }
-          break;
-        default:
-          break;
-      }
-      break;
-    case Value::Kind::TupleType: {
-      const auto& source_tuple = cast<TupleType>(*source);
-      switch (destination->kind()) {
-        case Value::Kind::TupleType: {
-          const auto& destination_tuple = cast<TupleType>(*destination);
-          if (source_tuple.elements().size() !=
-              destination_tuple.elements().size()) {
-            break;
-          }
-          bool all_ok = true;
-          for (const auto [source_elem, dest_elem] : llvm::zip_equal(
-                   source_tuple.elements(), destination_tuple.elements())) {
-            CARBON_ASSIGN_OR_RETURN(
-                bool convertible,
-                IsImplicitlyConvertible(source_loc, source_elem, dest_elem,
-                                        impl_scope,
-                                        allow_user_defined_conversions));
-            if (!convertible) {
-              all_ok = false;
-              break;
-            }
-          }
-          if (all_ok) {
-            return true;
-          }
-          break;
-        }
-        case Value::Kind::StaticArrayType: {
-          const auto& destination_array = cast<StaticArrayType>(*destination);
-          if (destination_array.size() != source_tuple.elements().size()) {
-            break;
-          }
-          bool all_ok = true;
-          for (Nonnull<const Value*> source_element : source_tuple.elements()) {
-            CARBON_ASSIGN_OR_RETURN(
-                bool convertible,
-                IsImplicitlyConvertible(source_loc, source_element,
-                                        &destination_array.element_type(),
-                                        impl_scope,
-                                        allow_user_defined_conversions));
-            if (!convertible) {
-              all_ok = false;
-              break;
-            }
-          }
-          if (all_ok) {
-            return true;
-          }
-          break;
-        }
-        case Value::Kind::TypeType: {
-          // A tuple value converts to `type` if all of its fields do.
-          bool all_types = true;
-          for (Nonnull<const Value*> source_element : source_tuple.elements()) {
-            CARBON_ASSIGN_OR_RETURN(
-                bool convertible,
-                IsImplicitlyConvertible(source_loc, source_element, destination,
-                                        impl_scope,
-                                        allow_user_defined_conversions));
-            if (!convertible) {
-              all_types = false;
-              break;
-            }
-          }
-          if (all_types) {
-            return true;
-          }
-          break;
-        }
-        default:
-          break;
-      }
-      break;
-    }
-    case Value::Kind::PointerType: {
-      if (destination->kind() != Value::Kind::PointerType) {
-        break;
-      }
-      const auto* src_ptr = cast<PointerType>(source);
-      const auto* dest_ptr = cast<PointerType>(destination);
-      if (src_ptr->pointee_type().kind() != Value::Kind::NominalClassType ||
-          dest_ptr->pointee_type().kind() != Value::Kind::NominalClassType) {
-        break;
-      }
-      const auto& src_class = cast<NominalClassType>(src_ptr->pointee_type());
-      if (src_class.InheritsClass(&dest_ptr->pointee_type())) {
-        return true;
-      }
-      break;
-    }
-    default:
-      break;
-  }
-
-  return false;
-}
-
-auto TypeChecker::BuildSubtypeConversion(Nonnull<Expression*> source,
-                                         Nonnull<const PointerType*> src_ptr,
-                                         Nonnull<const PointerType*> dest_ptr)
-    -> ErrorOr<Nonnull<Expression*>> {
-  const auto* src_class = cast<NominalClassType>(&src_ptr->pointee_type());
-  const auto* dest_class = cast<NominalClassType>(&dest_ptr->pointee_type());
-  const auto dest = dest_class->declaration().name();
-  Nonnull<Expression*> last_expr = source;
-  const auto* cur_class = src_class;
-  while (!TypeEqual(cur_class, dest_class, std::nullopt)) {
-    const auto src = src_class->declaration().name();
-    const auto base_class = cur_class->base();
-    CARBON_CHECK(base_class, "Invalid subtyping conversion");
-    auto* base_expr = arena_->New<BaseAccessExpression>(
-        source->source_loc(), last_expr,
-        arena_->New<BaseElement>(arena_->New<PointerType>(*base_class)));
-    last_expr = base_expr;
-    cur_class = *base_class;
-  }
-  CARBON_CHECK(last_expr, "Error, no conversion was needed");
-  return last_expr;
-}
-
-auto TypeChecker::BuildBuiltinConversion(Nonnull<Expression*> source,
-                                         Nonnull<const Value*> destination,
-                                         const ImplScope& impl_scope)
-    -> ErrorOr<Nonnull<Expression*>> {
-  Nonnull<const Value*> source_type = &source->static_type();
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "building builtin conversion from `"
-                           << *source_type << "` to `" << *destination << "`\n";
-  }
-
-  // Build a simple conversion that the interpreter can perform directly.
-  auto make_builtin_conversion = [&](Nonnull<Expression*> from) {
-    auto* result = arena_->New<BuiltinConvertExpression>(from);
-    result->set_static_type(destination);
-    result->set_expression_category(ExpressionCategory::Value);
-    return result;
-  };
-
-  // Report that the conversion was not possible. This error should only be
-  // visible if __builtin_implicit_as_convert is called directly.
-  auto conversion_failed = [&] {
-    return ProgramError(source->source_loc())
-           << "no builtin conversion from " << *source_type << " to "
-           << *destination << " is known";
-  };
-
-  // Note that the conversion expression that we build may evaluate `source`
-  // more than once. This is OK because the __builtin_implicit_as_convert
-  // intrinsic is only intended to be called from within the prelude's impl of
-  // ImplicitAs, where `source` has no side effects.
-
-  switch (source_type->kind()) {
-    case Value::Kind::StructType:
-      switch (destination->kind()) {
-        case Value::Kind::StructType: {
-          llvm::ArrayRef<NamedValue> source_fields =
-              cast<StructType>(*source_type).fields();
-          llvm::ArrayRef<NamedValue> destination_fields =
-              cast<StructType>(*destination).fields();
-          // Ensure every source field exists in the destination type.
-          for (const auto& source_field : source_fields) {
-            if (!FindField(destination_fields, source_field.name)) {
-              return conversion_failed();
-            }
-          }
-          // Initialize every destination field.
-          std::vector<FieldInitializer> result_fields;
-          for (const auto& destination_field : destination_fields) {
-            std::optional<NamedValue> source_field =
-                FindField(source_fields, destination_field.name);
-            if (!source_field.has_value()) {
-              return conversion_failed();
-            }
-            auto* elem = arena_->New<SimpleMemberAccessExpression>(
-                source->source_loc(), source, source_field->name);
-            CARBON_RETURN_IF_ERROR(TypeCheckExp(elem, impl_scope));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<Expression*> converted,
-                ImplicitlyConvert("implicit conversion", impl_scope, elem,
-                                  destination_field.value));
-            result_fields.push_back(
-                FieldInitializer(destination_field.name, converted));
-          }
-          auto* result = arena_->New<StructLiteral>(source->source_loc(),
-                                                    std::move(result_fields));
-          CARBON_RETURN_IF_ERROR(TypeCheckExp(result, impl_scope));
-          return result;
-        }
-        case Value::Kind::NominalClassType: {
-          CARBON_ASSIGN_OR_RETURN(
-              std::vector<NamedValue> field_types,
-              FieldTypesWithBase(source->source_loc(), "implicit conversion",
-                                 cast<NominalClassType>(*destination)));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<Expression*> result,
-              ImplicitlyConvert("implicit conversion", impl_scope, source,
-                                arena_->New<StructType>(field_types)));
-          // Perform a builtin conversion from struct to class.
-          return make_builtin_conversion(result);
-        }
-        case Value::Kind::TypeType:
-          // A value of empty struct type implicitly converts to type `type`.
-          if (cast<StructType>(*source_type).fields().empty()) {
-            return make_builtin_conversion(source);
-          }
-          return conversion_failed();
-        default:
-          return conversion_failed();
-      }
-      return conversion_failed();
-    case Value::Kind::TupleType: {
-      const auto& source_tuple = cast<TupleType>(*source_type);
-      switch (destination->kind()) {
-        case Value::Kind::TupleType: {
-          const auto& destination_tuple = cast<TupleType>(*destination);
-          if (source_tuple.elements().size() !=
-              destination_tuple.elements().size()) {
-            return conversion_failed();
-          }
-          std::vector<Nonnull<Expression*>> converted_elements;
-          for (const auto [i, dest_elem] :
-               llvm::enumerate(destination_tuple.elements())) {
-            auto* elem = arena_->New<IndexExpression>(
-                source->source_loc(), source,
-                arena_->New<IntLiteral>(source->source_loc(), i));
-            CARBON_RETURN_IF_ERROR(TypeCheckExp(elem, impl_scope));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<Expression*> converted,
-                ImplicitlyConvert("implicit conversion", impl_scope, elem,
-                                  dest_elem));
-            converted_elements.push_back(converted);
-          }
-          auto* result = arena_->New<TupleLiteral>(
-              source->source_loc(), std::move(converted_elements));
-          CARBON_RETURN_IF_ERROR(TypeCheckExp(result, impl_scope));
-          return result;
-        }
-        case Value::Kind::StaticArrayType: {
-          const auto& destination_array = cast<StaticArrayType>(*destination);
-          // First, convert each tuple element to the array element type if
-          // necessary.
-          if (!std::all_of(source_tuple.elements().begin(),
-                           source_tuple.elements().end(),
-                           [&](Nonnull<const Value*> element_type) {
-                             return TypeEqual(element_type,
-                                              &destination_array.element_type(),
-                                              std::nullopt);
-                           })) {
-            auto* destination_tuple_type = arena_->New<TupleType>(std::vector(
-                destination_array.size(), &destination_array.element_type()));
-            CARBON_ASSIGN_OR_RETURN(
-                source, BuildBuiltinConversion(source, destination_tuple_type,
-                                               impl_scope));
-          }
-          // Perform a builtin conversion from tuple to array.
-          return make_builtin_conversion(source);
-        }
-        case Value::Kind::TypeType: {
-          // First, convert each tuple element to 'type' if necessary.
-          if (!std::all_of(source_tuple.elements().begin(),
-                           source_tuple.elements().end(),
-                           [](Nonnull<const Value*> element_type) {
-                             return isa<TypeType>(element_type);
-                           })) {
-            auto* destination_tuple_type = arena_->New<TupleType>(
-                std::vector(source_tuple.elements().size(), destination));
-            CARBON_ASSIGN_OR_RETURN(
-                source, BuildBuiltinConversion(source, destination_tuple_type,
-                                               impl_scope));
-          }
-          // Perform a builtin conversion from tuple of types to type.
-          return make_builtin_conversion(source);
-        }
-        default:
-          return conversion_failed();
-      }
-      return conversion_failed();
-    }
-    case Value::Kind::PointerType: {
-      if (destination->kind() != Value::Kind::PointerType) {
-        return conversion_failed();
-      }
-      const auto* src_ptr = cast<PointerType>(source_type);
-      const auto* dest_ptr = cast<PointerType>(destination);
-      if (src_ptr->pointee_type().kind() != Value::Kind::NominalClassType ||
-          dest_ptr->pointee_type().kind() != Value::Kind::NominalClassType) {
-        return conversion_failed();
-      }
-      const auto& src_class = cast<NominalClassType>(src_ptr->pointee_type());
-      if (src_class.InheritsClass(&dest_ptr->pointee_type())) {
-        return BuildSubtypeConversion(source, src_ptr, dest_ptr);
-      }
-      return conversion_failed();
-    }
-    default:
-      return conversion_failed();
-  }
-
-  CARBON_FATAL("unreachable");
-}
-
-auto TypeChecker::ImplicitlyConvert(std::string_view context,
-                                    const ImplScope& impl_scope,
-                                    Nonnull<Expression*> source,
-                                    Nonnull<const Value*> destination)
-    -> ErrorOr<Nonnull<Expression*>> {
-  Nonnull<const Value*> source_type = &source->static_type();
-
-  CARBON_RETURN_IF_ERROR(
-      ExpectNonPlaceholderType(source->source_loc(), source_type));
-
-  if (TypeEqual(source_type, destination, std::nullopt)) {
-    // No conversions are required.
-    return source;
-  }
-
-  // Conversion from a tuple of types to the type `type` is used in the prelude
-  // before the intrinsic impl of `ImplicitAs` is declared. We also need to do
-  // this as a prerequisite to the conversion of tuples to constrained types
-  // below.
-  if (isa<TupleType>(source_type) && IsTypeOfType(destination)) {
-    auto* type_type = arena_->New<TypeType>();
-    CARBON_ASSIGN_OR_RETURN(
-        bool convertible,
-        IsBuiltinConversion(source->source_loc(), source_type, type_type,
-                            impl_scope,
-                            /*allow_user_defined_conversions=*/true));
-    if (convertible) {
-      CARBON_ASSIGN_OR_RETURN(
-          source, BuildBuiltinConversion(source, type_type, impl_scope));
-      source_type = &source->static_type();
-    }
-  }
-
-  // A type of type can be converted to another type of type if the value of
-  // the former satisfies the constraints of the latter. This conversion
-  // depends on the value, not only the type, so isn't supported by
-  // `ImplicitAs`.
-  if (IsTypeOfType(source_type) && IsTypeOfType(destination)) {
-    // Don't require the source value to be constant if the destination is
-    // `type`.
-    // TODO: Instead of excluding the special case where the destination is
-    // `type`, we should check if the source type has a subset of the
-    // constraints of the destination type. In that case, the source should not
-    // be required to be constant. That case should also be supported by
-    // `ImplicitAs`.
-    if (isa<TypeType>(destination)) {
-      return source;
-    }
-
-    // First convert the source expression to type `type`.
-    CARBON_ASSIGN_OR_RETURN(Nonnull<Expression*> source_as_type,
-                            ImplicitlyConvert(context, impl_scope, source,
-                                              arena_->New<TypeType>()));
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
-                            InterpExp(source_as_type));
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const ConstraintType*> destination_constraint,
-        ConvertToConstraintType(source->source_loc(), "implicit conversion",
-                                destination));
-    destination = destination_constraint;
-
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start()
-          << "converting type `" << *converted_value << "` to constraint `"
-          << *destination_constraint << "` for " << context << " in scope:\n"
-          << impl_scope << "\n";
-    }
-    // Note, we discard the witness. We don't actually need it in order to
-    // perform the conversion, but we do want to know it exists.
-    // TODO: A value of constraint type should carry both the type and the
-    // witness.
-    CARBON_RETURN_IF_ERROR(impl_scope.Resolve(
-        destination_constraint, converted_value, source->source_loc(), *this));
-    return arena_->New<ValueLiteral>(source->source_loc(), converted_value,
-                                     destination_constraint,
-                                     ExpressionCategory::Value);
-  }
-
-  // Conversion from a tuple literal to a tuple type converts each element in
-  // turn, rather than converting the tuple as a whole. This is important in
-  // order to evaluate arguments to a function call in a reasonable order, and
-  // this conversion needs to be built-in because we use it while type-checking
-  // the prelude.
-  if (auto* source_tuple = dyn_cast<TupleLiteral>(source)) {
-    if (auto* destination_tuple = dyn_cast<TupleType>(destination)) {
-      if (source_tuple->fields().size() !=
-          destination_tuple->elements().size()) {
-        return ProgramError(source->source_loc())
-               << "type error in " << context << ": `" << *source_type << "`"
-               << " is not implicitly convertible to tuple type "
-               << "`" << *destination << "` of different length";
-      }
-      std::vector<Nonnull<Expression*>> converted_elements;
-      for (const auto [source_field, dest_elem] : llvm::zip_equal(
-               source_tuple->fields(), destination_tuple->elements())) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted,
-            ImplicitlyConvert("implicit conversion", impl_scope, source_field,
-                              dest_elem));
-        converted_elements.push_back(converted);
-      }
-      auto* result = arena_->New<TupleLiteral>(source->source_loc(),
-                                               std::move(converted_elements));
-      // TODO: Should be ExpressionCategory::Initializing.
-      result->set_expression_category(ExpressionCategory::Value);
-      result->set_static_type(destination);
-      return result;
-    }
-  }
-
-  // Build a call to the conversion function.
-  ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
-      impl_scope, source,
-      BuiltinInterfaceName{Builtin::ImplicitAs, destination},
-      BuiltinMethodCall{"Convert"});
-  if (!converted.ok()) {
-    // We couldn't find a matching `impl`.
-    return ProgramError(source->source_loc())
-           << "type error in " << context << ": "
-           << "'" << *source_type << "' is not implicitly convertible to '"
-           << *destination << "'";
-  }
-  return *converted;
-}
-
-auto TypeChecker::IsIntrinsicConstraintSatisfied(
-    SourceLocation source_loc, const IntrinsicConstraint& constraint,
-    const ImplScope& impl_scope) const -> ErrorOr<bool> {
-  // TODO: Check to see if this constraint is known in the current impl scope.
-  switch (constraint.kind) {
-    case IntrinsicConstraint::ImplicitAs:
-      CARBON_CHECK(constraint.arguments.size() == 1,
-                   "wrong number of arguments for `__intrinsic_implicit_as`");
-      CARBON_ASSIGN_OR_RETURN(
-          bool convertible,
-          IsBuiltinConversion(source_loc, constraint.type,
-                              constraint.arguments[0], impl_scope,
-                              /*allow_user_defined_conversions=*/true));
-      if (trace_stream_->is_enabled()) {
-        trace_stream_->Result()
-            << "`" << constraint << "` evaluated to `" << convertible << "`\n";
-      }
-      return convertible;
-  }
-}
-
-auto TypeChecker::GetBuiltinInterfaceType(SourceLocation source_loc,
-                                          BuiltinInterfaceName interface) const
-    -> ErrorOr<Nonnull<const InterfaceType*>> {
-  auto bad_builtin = [&]() -> Error {
-    return ProgramError(source_loc) << "unsupported declaration for builtin `"
-                                    << interface.builtin << "`";
-  };
-
-  // Find the builtin interface declaration.
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Declaration*> builtin_decl,
-                          builtins_.Get(source_loc, interface.builtin));
-  const auto* iface_decl = dyn_cast<InterfaceDeclaration>(builtin_decl);
-  if (!iface_decl || !iface_decl->constant_value()) {
-    return bad_builtin();
-  }
-
-  // Match the interface arguments up with the parameters and build the
-  // interface type.
-  bool has_parameters = iface_decl->params().has_value();
-  bool has_arguments = !interface.arguments.empty();
-  if (has_parameters != has_arguments) {
-    return bad_builtin();
-  }
-  BindingMap binding_args;
-  if (has_arguments) {
-    TupleValue args(interface.arguments);
-    if (!PatternMatch(&iface_decl->params().value()->value(),
-                      ExpressionResult::Value(&args), source_loc, std::nullopt,
-                      binding_args, trace_stream_, this->arena_)) {
-      return bad_builtin();
-    }
-  }
-  Nonnull<const Bindings*> bindings =
-      arena_->New<Bindings>(std::move(binding_args), Bindings::NoWitnesses);
-  return arena_->New<InterfaceType>(iface_decl, bindings);
-}
-
-auto TypeChecker::BuildBuiltinMethodCall(const ImplScope& impl_scope,
-                                         Nonnull<Expression*> source,
-                                         BuiltinInterfaceName interface,
-                                         BuiltinMethodCall method)
-    -> ErrorOr<Nonnull<Expression*>> {
-  const SourceLocation source_loc = source->source_loc();
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const InterfaceType*> iface_type,
-                          GetBuiltinInterfaceType(source_loc, interface));
-
-  if (interface.builtin == Builtin::ImplicitAs) {
-    // Type-checking the below expression resolves the member name to
-    // `As(Destination).Convert`, which allows both implicit and explicit
-    // conversions. So manually check that `ImplicitAs(Destination)` is
-    // actually implemented.
-    // TODO: This check should be performed as part of type-checking the
-    // compound member access expression below. This is a short-term
-    // workaround.
-    CARBON_RETURN_IF_ERROR(impl_scope.Resolve(
-        iface_type, &source->static_type(), source->source_loc(), *this));
-  }
-
-  // Build an expression to perform the call `source.(interface.method)(args)`.
-  Nonnull<Expression*> iface_expr =
-      arena_->New<ValueLiteral>(source_loc, iface_type, arena_->New<TypeType>(),
-                                ExpressionCategory::Value);
-  Nonnull<Expression*> iface_member = arena_->New<SimpleMemberAccessExpression>(
-      source_loc, iface_expr, method.name);
-  Nonnull<Expression*> method_access =
-      arena_->New<CompoundMemberAccessExpression>(source_loc, source,
-                                                  iface_member);
-  Nonnull<Expression*> call_args =
-      arena_->New<TupleLiteral>(source_loc, method.arguments);
-  Nonnull<Expression*> call =
-      arena_->New<CallExpression>(source_loc, method_access, call_args);
-  CARBON_RETURN_IF_ERROR(TypeCheckExp(call, impl_scope));
-  return {call};
-}
-
-// Checks that the given type is not a placeholder type. Diagnoses otherwise.
-auto TypeChecker::ExpectNonPlaceholderType(SourceLocation source_loc,
-                                           Nonnull<const Value*> type)
-    -> ErrorOr<Success> {
-  if (!IsPlaceholderType(type)) {
-    return Success();
-  }
-  if (const auto* member_name = dyn_cast<TypeOfMemberName>(type)) {
-    return ProgramError(source_loc)
-           << *member_name << " can only be used in a member access or alias";
-  }
-  if (const auto* param_entity =
-          dyn_cast<TypeOfParameterizedEntityName>(type)) {
-    return ProgramError(source_loc)
-           << "'" << param_entity->name() << "' must be given an argument list";
-  }
-  if (const auto* mixin_type = dyn_cast<TypeOfMixinPseudoType>(type)) {
-    return ProgramError(source_loc)
-           << "invalid use of mixin "
-           << mixin_type->mixin_type().declaration().name();
-  }
-  if (const auto* namespace_type = dyn_cast<TypeOfNamespaceName>(type)) {
-    return ProgramError(source_loc)
-           << "expected `.member_name` after name of " << *namespace_type;
-  }
-  CARBON_FATAL("unknown kind of placeholder type {0}", *type);
-}
-
-// Argument deduction matches two values and attempts to find a set of
-// substitutions into deduced bindings in one of them that would result in the
-// other.
-class TypeChecker::ArgumentDeduction {
- public:
-  ArgumentDeduction(
-      SourceLocation source_loc, std::string_view context,
-      llvm::ArrayRef<Nonnull<const GenericBinding*>> bindings_to_deduce,
-      Nonnull<TraceStream*> trace_stream)
-      : source_loc_(source_loc),
-        context_(context),
-        deduced_bindings_in_order_(bindings_to_deduce),
-        trace_stream_(trace_stream) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start() << "performing argument deduction for bindings: [";
-      llvm::ListSeparator sep;
-      for (const auto* binding : bindings_to_deduce) {
-        *trace_stream_ << sep << "`" << *binding << "`";
-      }
-      *trace_stream_ << "]\n";
-    }
-    for (const auto* binding : bindings_to_deduce) {
-      deduced_values_.insert({binding, {}});
-    }
-  }
-
-  // Deduces the values of deduced bindings in `param` from the corresponding
-  // values in `arg`. `allow_implicit_conversion` specifies whether implicit
-  // conversions are permitted from the argument to the parameter type.
-  auto Deduce(Nonnull<const Value*> param, Nonnull<const Value*> arg,
-              bool allow_implicit_conversion) -> ErrorOr<Success>;
-
-  // Finds a binding to deduce that has not been deduced, if any exist.
-  auto FindUndeducedBinding() const
-      -> std::optional<Nonnull<const GenericBinding*>> {
-    for (const auto* binding : deduced_bindings_in_order_) {
-      llvm::ArrayRef<Nonnull<const Value*>> values =
-          deduced_values_.find(binding)->second;
-      if (values.empty()) {
-        return binding;
-      }
-    }
-    return std::nullopt;
-  }
-
-  // Adds a value for a binding that is not deduced but still participates in
-  // substitution. For example, the `T` parameter in `fn F(T:! type, x: T)`.
-  void AddNonDeducedBindingValue(Nonnull<const GenericBinding*> binding,
-                                 Nonnull<Expression*> argument) {
-    non_deduced_values_.push_back({binding, argument});
-  }
-
-  // Finishes deduction and forms a set of substitutions that transform `param`
-  // into `arg`.
-  auto Finish(TypeChecker& type_checker, const ImplScope& impl_scope,
-              bool diagnose_deduction_failure) const
-      -> ErrorOr<std::optional<Bindings>>;
-
- private:
-  SourceLocation source_loc_;
-  std::string_view context_;
-  llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings_in_order_;
-  Nonnull<TraceStream*> trace_stream_;
-
-  // Values for deduced bindings.
-  std::map<Nonnull<const GenericBinding*>,
-           llvm::TinyPtrVector<Nonnull<const Value*>>>
-      deduced_values_;
-  // Values for non-deduced bindings, such as parameters with corresponding
-  // argument expressions.
-  std::vector<std::pair<Nonnull<const GenericBinding*>, Nonnull<Expression*>>>
-      non_deduced_values_;
-
-  // Non-deduced mismatches that we deferred until we could perform
-  // substitutions into them.
-  struct NonDeducedMismatch {
-    Nonnull<const Value*> param;
-    Nonnull<const Value*> arg;
-    bool allow_implicit_conversion;
-  };
-  std::vector<NonDeducedMismatch> non_deduced_mismatches_;
-};
-
-auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
-                                            Nonnull<const Value*> arg,
-                                            bool allow_implicit_conversion)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "deducing `" << *param << "` from `" << *arg
-                           << "`\n";
-  }
-
-  // If param is the name of a variable we're deducing, then deduce it.
-  if (const auto* var_type = dyn_cast<VariableType>(param)) {
-    const auto& binding = var_type->binding();
-    if (auto it = deduced_values_.find(&binding); it != deduced_values_.end()) {
-      it->second.push_back(arg);
-      return Success();
-    }
-  }
-
-  // Handle the case where we can't perform deduction, either because the
-  // parameter is a primitive type or because the parameter and argument have
-  // different forms. In this case, we require an implicit conversion to exist,
-  // or for an exact type match if implicit conversions are not permitted.
-  auto handle_non_deduced_value = [&]() -> ErrorOr<Success> {
-    if (ValueEqual(param, arg, std::nullopt)) {
-      return Success();
-    }
-
-    // Defer checking until we can substitute into the parameter and see if it
-    // actually matches.
-    non_deduced_mismatches_.push_back(
-        {.param = param,
-         .arg = arg,
-         .allow_implicit_conversion = allow_implicit_conversion});
-    return Success();
-  };
-
-  // Handle the case where we know we can't have an exact match, but there
-  // might still be an implicit conversion.
-  auto handle_non_matching_value = [&]() -> ErrorOr<Success> {
-    if (!allow_implicit_conversion) {
-      return ProgramError(source_loc_)
-             << "type mismatch in argument deduction\n"
-             << "expected: " << *param << "\n"
-             << "actual: " << *arg;
-    }
-    return handle_non_deduced_value();
-  };
-
-  // If either parameter or argument is an unknown kind of value, we can't
-  // perform any deduction. Keep track of them so we can check they're the same
-  // later.
-  if (IsValueKindDependent(param) || IsValueKindDependent(arg)) {
-    return handle_non_deduced_value();
-  }
-
-  // If the values have different non-dependent kinds, they can't possibly
-  // match after substitution.
-  if (param->kind() != arg->kind()) {
-    return handle_non_matching_value();
-  }
-
-  switch (param->kind()) {
-    case Value::Kind::TupleType: {
-      const auto& param_tup = cast<TupleType>(*param);
-      const auto& arg_tup = cast<TupleType>(*arg);
-      if (param_tup.elements().size() != arg_tup.elements().size()) {
-        return ProgramError(source_loc_)
-               << "mismatch in tuple sizes, expected "
-               << param_tup.elements().size() << " but got "
-               << arg_tup.elements().size();
-      }
-      for (const auto [param_elem, arg_elem] :
-           llvm::zip_equal(param_tup.elements(), arg_tup.elements())) {
-        CARBON_RETURN_IF_ERROR(
-            Deduce(param_elem, arg_elem, allow_implicit_conversion));
-      }
-      return Success();
-    }
-    case Value::Kind::StructType: {
-      const auto& param_struct = cast<StructType>(*param);
-      const auto& arg_struct = cast<StructType>(*arg);
-      auto diagnose_missing_field = [&](const StructType& struct_type,
-                                        const NamedValue& field,
-                                        bool missing_from_source) -> Error {
-        static constexpr const char* SourceOrDestination[2] = {"source",
-                                                               "destination"};
-        return ProgramError(source_loc_)
-               << "mismatch in field names, "
-               << SourceOrDestination[missing_from_source ? 1 : 0] << " field `"
-               << field.name << "` not in "
-               << SourceOrDestination[missing_from_source ? 0 : 1] << " type `"
-               << struct_type << "`";
-      };
-      const auto& param_fields = param_struct.fields();
-      const auto& arg_fields = arg_struct.fields();
-      if (allow_implicit_conversion) {
-        for (const NamedValue& param_field : param_fields) {
-          if (std::optional<NamedValue> arg_field =
-                  FindField(arg_fields, param_field.name)) {
-            CARBON_RETURN_IF_ERROR(Deduce(param_field.value, arg_field->value,
-                                          allow_implicit_conversion));
-          } else {
-            return diagnose_missing_field(arg_struct, param_field, true);
-          }
-        }
-        if (param_fields.size() != arg_fields.size()) {
-          for (const NamedValue& arg_field : arg_fields) {
-            if (!FindField(param_fields, arg_field.name).has_value()) {
-              return diagnose_missing_field(param_struct, arg_field, false);
-            }
-          }
-          CARBON_FATAL(
-              "field count mismatch but no missing field; duplicate field "
-              "name?");
-        }
-      } else {
-        for (const auto [param_field, arg_field] :
-             llvm::zip(param_fields, arg_fields)) {
-          if (param_field.name != arg_field.name) {
-            return ProgramError(source_loc_)
-                   << "mismatch in field names, `" << param_field.name
-                   << "` != `" << arg_field.name << "`";
-          }
-          CARBON_RETURN_IF_ERROR(Deduce(param_field.value, arg_field.value,
-                                        allow_implicit_conversion));
-        }
-        if (param_fields.size() < arg_fields.size()) {
-          return diagnose_missing_field(param_struct,
-                                        arg_fields[param_fields.size()], false);
-        } else if (param_fields.size() > arg_fields.size()) {
-          return diagnose_missing_field(arg_struct,
-                                        param_fields[arg_fields.size()], true);
-        }
-      }
-      return Success();
-    }
-    case Value::Kind::FunctionType: {
-      const auto& param_fn = cast<FunctionType>(*param);
-      const auto& arg_fn = cast<FunctionType>(*arg);
-      // TODO: handle situation when arg has deduced parameters.
-      CARBON_RETURN_IF_ERROR(Deduce(&param_fn.parameters(),
-                                    &arg_fn.parameters(),
-                                    /*allow_implicit_conversion=*/false));
-      CARBON_RETURN_IF_ERROR(Deduce(&param_fn.return_type(),
-                                    &arg_fn.return_type(),
-                                    /*allow_implicit_conversion=*/false));
-      return Success();
-    }
-    case Value::Kind::PointerType: {
-      const auto& param_pointee = cast<PointerType>(param)->pointee_type();
-      const auto& arg_pointee = cast<PointerType>(arg)->pointee_type();
-      if (allow_implicit_conversion) {
-        // TODO: Change based on whether we want to allow
-        // deduce-from-base-class, for parametrized base class. See
-        // https://github.com/carbon-language/carbon-lang/issues/2464.
-        if (const auto* arg_class = dyn_cast<NominalClassType>(&arg_pointee);
-            arg_class && arg_class->InheritsClass(&param_pointee)) {
-          return Success();
-        }
-      }
-      return Deduce(&param_pointee, &arg_pointee,
-                    /*allow_implicit_conversion=*/false);
-    }
-    // Nothing to do in the case for `auto`.
-    case Value::Kind::AutoType: {
-      return Success();
-    }
-    case Value::Kind::NominalClassType: {
-      const auto& param_class_type = cast<NominalClassType>(*param);
-      const auto& arg_class_type = cast<NominalClassType>(*arg);
-      if (!DeclaresSameEntity(param_class_type.declaration(),
-                              arg_class_type.declaration())) {
-        return handle_non_matching_value();
-      }
-      for (const auto& [ty, param_ty] : param_class_type.type_args()) {
-        CARBON_RETURN_IF_ERROR(Deduce(param_ty,
-                                      arg_class_type.type_args().at(ty),
-                                      /*allow_implicit_conversion=*/false));
-      }
-      return Success();
-    }
-    case Value::Kind::InterfaceType: {
-      const auto& param_iface_type = cast<InterfaceType>(*param);
-      const auto& arg_iface_type = cast<InterfaceType>(*arg);
-      if (!DeclaresSameEntity(param_iface_type.declaration(),
-                              arg_iface_type.declaration())) {
-        return handle_non_matching_value();
-      }
-      for (const auto& [ty, param_ty] : param_iface_type.args()) {
-        CARBON_RETURN_IF_ERROR(Deduce(param_ty, arg_iface_type.args().at(ty),
-                                      /*allow_implicit_conversion=*/false));
-      }
-      return Success();
-    }
-    case Value::Kind::NamedConstraintType: {
-      const auto& param_constraint_type = cast<NamedConstraintType>(*param);
-      const auto& arg_constraint_type = cast<NamedConstraintType>(*arg);
-      if (!DeclaresSameEntity(param_constraint_type.declaration(),
-                              arg_constraint_type.declaration())) {
-        return handle_non_matching_value();
-      }
-      for (const auto& [ty, param_ty] :
-           param_constraint_type.bindings().args()) {
-        CARBON_RETURN_IF_ERROR(
-            Deduce(param_ty, arg_constraint_type.bindings().args().at(ty),
-                   /*allow_implicit_conversion=*/false));
-      }
-      return Success();
-    }
-    // For the following cases, we check the type matches.
-    case Value::Kind::VariableType:
-      // We handled deduced variables above; this case covers variables that
-      // are not deduced as part of this deduction step.
-    case Value::Kind::StaticArrayType:
-      // TODO: We could deduce the array type from an array or tuple argument.
-    case Value::Kind::ChoiceType:
-      // TODO: Choice types should be handled like other named declarations.
-    case Value::Kind::ConstraintType:
-    case Value::Kind::AssociatedConstant:
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::StringType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName: {
-      return handle_non_deduced_value();
-    }
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-    case Value::Kind::ParameterizedEntityName:
-    case Value::Kind::MemberName:
-    case Value::Kind::IntValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::StructValue:
-    case Value::Kind::TupleValue:
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::AlternativeValue:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::StringValue:
-    case Value::Kind::UninitializedValue: {
-      // Argument deduction within the parameters of a parameterized class type
-      // or interface type can compare values, rather than types.
-      // TODO: Deduce within the values where possible.
-      return handle_non_deduced_value();
-    }
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::TypeOfMixinPseudoType:
-      CARBON_FATAL("Type expression must not contain Mixin types");
-  }
-}
-
-auto TypeChecker::ArgumentDeduction::Finish(
-    TypeChecker& type_checker, const ImplScope& impl_scope,
-    bool diagnose_deduction_failure) const -> ErrorOr<std::optional<Bindings>> {
-  // Check deduced values and build our resulting `Bindings` set. We do this in
-  // declaration order so that any bindings used in the type of a later binding
-  // have known values before we check that binding.
-  Bindings bindings;
-  for (const auto* binding : deduced_bindings_in_order_) {
-    llvm::ArrayRef<Nonnull<const Value*>> values =
-        deduced_values_.find(binding)->second;
-    if (values.empty()) {
-      if (!diagnose_deduction_failure) {
-        return {std::nullopt};
-      }
-      return ProgramError(source_loc_)
-             << "could not deduce type argument for type parameter "
-             << binding->name() << " in " << context_;
-    }
-
-    CARBON_ASSIGN_OR_RETURN(
-        const Value* binding_type,
-        type_checker.Substitute(bindings, &binding->static_type()));
-    const auto* first_value = values[0];
-    for (const auto* value : values) {
-      // All deductions are required to produce the same value. Note that we
-      // intentionally don't consider equality constraints here; we need the
-      // same symbolic type, otherwise it would be ambiguous which spelling
-      // should be used, and we'd need to check all pairs of types for equality
-      // because our notion of equality is non-transitive.
-      if (!ValueEqual(first_value, value, std::nullopt)) {
-        if (!diagnose_deduction_failure) {
-          return {std::nullopt};
-        }
-        return ProgramError(source_loc_)
-               << "deduced multiple different values for " << *binding
-               << ":\n  " << *first_value << "\n  " << *value;
-      }
-    }
-
-    // Find a witness for the binding if needed.
-    std::optional<Nonnull<const Witness*>> witness;
-    if (binding->impl_binding()) {
-      CARBON_ASSIGN_OR_RETURN(
-          witness, impl_scope.TryResolve(binding_type, first_value, source_loc_,
-                                         type_checker, bindings,
-                                         diagnose_deduction_failure));
-      if (!witness) {
-        return {std::nullopt};
-      }
-    }
-
-    bindings.Add(binding, first_value, witness);
-  }
-
-  // Evaluate and add non-deduced values. These are assumed to lexically follow
-  // the deduced bindings, so any bindings the type might reference are now
-  // known.
-  // TODO: This is not the case for `fn F(T:! type, u: (V:! ImplicitAs(T)))`.
-  // However, we intend to disallow that.
-  for (auto [binding, arg] : non_deduced_values_) {
-    // Form the binding's resolved type and convert the argument expression to
-    // it.
-    const Value* binding_type = &binding->static_type();
-    CARBON_ASSIGN_OR_RETURN(const Value* substituted_type,
-                            type_checker.Substitute(bindings, binding_type));
-    CARBON_ASSIGN_OR_RETURN(
-        arg, type_checker.ImplicitlyConvert(context_, impl_scope, arg,
-                                            substituted_type));
-
-    // Evaluate the argument to get the value.
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value,
-                            type_checker.InterpExp(arg));
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "evaluated generic parameter `" << *binding
-                              << "` as `" << *value << "`\n";
-    }
-
-    // Find a witness for the binding if needed.
-    std::optional<Nonnull<const Witness*>> witness;
-    if (binding->impl_binding()) {
-      CARBON_ASSIGN_OR_RETURN(
-          witness,
-          impl_scope.TryResolve(binding_type, value, source_loc_, type_checker,
-                                bindings, diagnose_deduction_failure));
-      if (!witness) {
-        return {std::nullopt};
-      }
-    }
-
-    bindings.Add(binding, value, witness);
-  }
-
-  // Check non-deduced potential mismatches now we can substitute into them.
-  for (const auto& mismatch : non_deduced_mismatches_) {
-    CARBON_ASSIGN_OR_RETURN(const Value* subst_param,
-                            type_checker.Substitute(bindings, mismatch.param));
-
-    bool type = IsType(subst_param) && IsType(mismatch.arg);
-    if (type && mismatch.allow_implicit_conversion) {
-      CARBON_ASSIGN_OR_RETURN(
-          bool convertible,
-          type_checker.IsImplicitlyConvertible(source_loc_, mismatch.arg,
-                                               subst_param, impl_scope, true));
-      if (!convertible) {
-        if (!diagnose_deduction_failure) {
-          return {std::nullopt};
-        }
-        return ProgramError(source_loc_)
-               << "mismatch in non-deduced types, `" << *mismatch.arg
-               << "` is not implicitly convertible to `" << *subst_param << "`";
-      }
-    } else {
-      if (!ValueEqual(subst_param, mismatch.arg, std::nullopt)) {
-        if (!diagnose_deduction_failure) {
-          return {std::nullopt};
-        }
-        return ProgramError(source_loc_)
-               << "mismatch in non-deduced " << (type ? "types" : "values")
-               << ", `" << *mismatch.arg << "` != `" << *subst_param << "`";
-      }
-    }
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result() << "deduction succeeded with results: [";
-    llvm::ListSeparator sep;
-    for (const auto& [binding, val] : bindings.args()) {
-      *trace_stream_ << sep << "`" << *binding << "` = `" << *val << "`";
-    }
-    for (const auto& [binding, val] : bindings.witnesses()) {
-      *trace_stream_ << sep << "`" << *binding << "` = `" << *val << "`";
-    }
-    *trace_stream_ << "]\n";
-  }
-
-  return {std::move(bindings)};
-}
-
-// Look for a rewrite to use when naming the given interface member in a type
-// that has the given list of rewrites.
-static auto LookupRewrite(llvm::ArrayRef<RewriteConstraint> rewrites,
-                          Nonnull<const InterfaceType*> interface,
-                          Nonnull<const Declaration*> member)
-    -> std::optional<const RewriteConstraint*> {
-  if (!isa<AssociatedConstantDeclaration>(member)) {
-    return std::nullopt;
-  }
-
-  for (const auto& rewrite : rewrites) {
-    if (ValueEqual(interface, &rewrite.constant->interface(), std::nullopt) &&
-        member == &rewrite.constant->constant()) {
-      // A ConstraintType can only have one rewrite per (interface, member)
-      // pair, so we don't need to check the rest.
-      return &rewrite;
-    }
-  }
-
-  return std::nullopt;
-}
-
-// Look for a rewrite to use when naming the given interface member in a type
-// declared with the given type-of-type.
-static auto LookupRewrite(Nonnull<const Value*> type_of_type,
-                          Nonnull<const InterfaceType*> interface,
-                          Nonnull<const Declaration*> member)
-    -> std::optional<const RewriteConstraint*> {
-  // Find the set of rewrites. Only ConstraintTypes have rewrites.
-  // TODO: If we can ever see an InterfaceType here, we should convert it to a
-  // constraint type.
-  llvm::ArrayRef<RewriteConstraint> rewrites;
-  if (const auto* constraint_type = dyn_cast<ConstraintType>(type_of_type)) {
-    rewrites = constraint_type->rewrite_constraints();
-  }
-
-  return LookupRewrite(rewrites, interface, member);
-}
-
-// Builder for constraint types.
-//
-// This type supports incrementally building a constraint type by adding
-// constraints one at a time, and will deduplicate the constraints as it goes.
-//
-// TODO: The deduplication here is very inefficient. We should use value
-// canonicalization or hashing or similar to speed this up.
-class TypeChecker::ConstraintTypeBuilder {
- public:
-  // Information about a rewrite constraint that is currently being rewritten.
-  struct RewriteInfo {
-    Nonnull<const RewriteConstraint*> rewrite;
-    // Whether the rewrite has been found to refer to itself. If so, the
-    // self-reference will not be expanded. `Resolve` uses this to detect
-    // rewrites that cannot be resolved due to cycles.
-    bool rewrite_references_itself = false;
-  };
-
-  ConstraintTypeBuilder(Nonnull<Arena*> arena, SourceLocation source_loc)
-      : ConstraintTypeBuilder(arena, MakeSelfBinding(arena, source_loc)) {}
-  ConstraintTypeBuilder(Nonnull<Arena*> arena,
-                        Nonnull<GenericBinding*> self_binding)
-      : arena_(arena),
-        self_binding_(self_binding),
-        impl_binding_(AddImplBinding(arena, self_binding_)) {}
-  ConstraintTypeBuilder(Nonnull<Arena*> arena,
-                        Nonnull<GenericBinding*> self_binding,
-                        Nonnull<ImplBinding*> impl_binding)
-      : arena_(arena),
-        self_binding_(self_binding),
-        impl_binding_(impl_binding) {}
-
-  // Returns the self binding for this builder.
-  auto self_binding() const -> Nonnull<const GenericBinding*> {
-    return self_binding_;
-  }
-
-  // Returns the current set of rewrite constraints for this builder.
-  auto rewrite_constraints() const -> llvm::ArrayRef<RewriteConstraint> {
-    return rewrite_constraints_;
-  }
-
-  auto current_rewrite_info() -> std::optional<Nonnull<RewriteInfo*>> {
-    return current_rewrite_info_;
-  }
-
-  // Produces a type that refers to the `.Self` type of the constraint.
-  auto GetSelfType() const -> Nonnull<const Value*> {
-    return *self_binding_->symbolic_identity();
-  }
-
-  // Gets a witness that `.Self` implements the eventual constraint type built
-  // by this builder.
-  auto GetSelfWitness() const -> Nonnull<const Witness*> {
-    return cast<Witness>(*impl_binding_->symbolic_identity());
-  }
-
-  // Adds an `impls` constraint -- `T impls C` if not already present.
-  // Returns the index of the impls constraint within the self witness.
-  auto AddImplsConstraint(ImplsConstraint impls) -> int {
-    for (const auto [i, existing] : llvm::enumerate(impls_constraints_)) {
-      if (TypeEqual(existing.type, impls.type, std::nullopt) &&
-          TypeEqual(existing.interface, impls.interface, std::nullopt)) {
-        return i;
-      }
-    }
-    impls_constraints_.push_back(impls);
-    return impls_constraints_.size() - 1;
-  }
-
-  // Adds an intrinsic constraint, if not already present.
-  void AddIntrinsicConstraint(IntrinsicConstraint intrinsic) {
-    // TODO: Consider performing deduplication.
-    intrinsic_constraints_.push_back(std::move(intrinsic));
-  }
-
-  // Adds an equality constraint -- `A == B`.
-  void AddEqualityConstraint(EqualityConstraint equal) {
-    if (equal.values.size() < 2) {
-      // There's no need to track degenerate equality constraints. These can be
-      // formed by rewrites.
-      return;
-    }
-
-    // TODO: Check to see if this constraint is already present and deduplicate
-    // if so. We could also look for a superset / subset and keep the larger
-    // one. We could in theory detect `A == B and B == C and C == A` and merge
-    // into a single `A == B == C` constraint, but that's more work than it's
-    // worth doing here.
-    equality_constraints_.push_back(std::move(equal));
-  }
-
-  void AddRewriteConstraint(RewriteConstraint rewrite) {
-    rewrite_constraints_.push_back(rewrite);
-  }
-
-  // Add a context for qualified name lookup, if not already present.
-  void AddLookupContext(LookupContext context) {
-    for (LookupContext existing : lookup_contexts_) {
-      if (ValueEqual(existing.context, context.context, std::nullopt)) {
-        return;
-      }
-    }
-    lookup_contexts_.push_back(context);
-  }
-
-  // Adds all the constraints from another constraint type. The given value
-  // `self` is substituted for `.Self`, typically specified in terms of this
-  // constraint's self binding. The `self_witness` is the witness for the
-  // resulting constraint, and can be `GetSelfWitness()`. The `bindings`
-  // parameter specifies any additional substitutions to perform.
-  auto AddAndSubstitute(const TypeChecker& type_checker,
-                        Nonnull<const ConstraintType*> constraint,
-                        Nonnull<const Value*> self,
-                        Nonnull<const Witness*> self_witness,
-                        const Bindings& bindings, bool add_lookup_contexts)
-      -> ErrorOr<Success> {
-    if (type_checker.trace_stream_->is_enabled()) {
-      type_checker.trace_stream_->Start()
-          << "merging `" << *constraint << "` into constraint with `"
-          << *constraint->self_binding() << "` ~> `" << *self << "`\n";
-    }
-
-    // First substitute into the impl bindings to form the full witness for
-    // the constraint type.
-    std::vector<Nonnull<const Witness*>> witnesses;
-    for (const auto& impls_constraint : constraint->impls_constraints()) {
-      Bindings local_bindings = bindings;
-      local_bindings.Add(constraint->self_binding(), self,
-                         type_checker.MakeConstraintWitness(witnesses));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> type,
-          type_checker.Substitute(local_bindings, impls_constraint.type));
-      CARBON_ASSIGN_OR_RETURN(const auto* interface,
-                              type_checker.SubstituteCast<InterfaceType>(
-                                  local_bindings, impls_constraint.interface));
-      int index = AddImplsConstraint({.type = type, .interface = interface});
-      witnesses.push_back(
-          type_checker.MakeConstraintWitnessAccess(self_witness, index));
-    }
-
-    // Now form a complete witness and substitute it into the rest of the
-    // constraint.
-    Bindings local_bindings = bindings;
-    local_bindings.Add(
-        constraint->self_binding(), self,
-        type_checker.MakeConstraintWitness(std::move(witnesses)));
-
-    // If lookups into the resulting constraint should look into this added
-    // constraint, then rewrites for this added constraint become rewrites for
-    // the resulting constraint. Otherwise, discard the rewrites and keep only
-    // their corresponding equality constraints.
-    for (const auto& rewrite_constraint : constraint->rewrite_constraints()) {
-      CARBON_ASSIGN_OR_RETURN(
-          const auto* interface,
-          type_checker.SubstituteCast<InterfaceType>(
-              local_bindings, &rewrite_constraint.constant->interface()));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> converted_value,
-          type_checker.Substitute(local_bindings,
-                                  rewrite_constraint.converted_replacement));
-
-      // Form a symbolic value naming the non-rewritten associated constant.
-      // The impls constraint will always already exist.
-      int index = AddImplsConstraint({.type = self, .interface = interface});
-      const auto* witness =
-          type_checker.MakeConstraintWitnessAccess(self_witness, index);
-      const auto* constant_value = arena_->New<AssociatedConstant>(
-          self, interface, &rewrite_constraint.constant->constant(), witness);
-
-      if (add_lookup_contexts) {
-        // Add the constraint `.(I.C) = V`, tracking the value and type prior
-        // to conversion for use in rewrites.
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> value,
-            type_checker.Substitute(
-                local_bindings, rewrite_constraint.unconverted_replacement));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> type,
-            type_checker.Substitute(
-                local_bindings,
-                rewrite_constraint.unconverted_replacement_type));
-        AddRewriteConstraint({.constant = constant_value,
-                              .unconverted_replacement = value,
-                              .unconverted_replacement_type = type,
-                              .converted_replacement = converted_value});
-      } else {
-        // Add the constraint `Self.(I.C) == V`.
-        AddEqualityConstraint({.values = {constant_value, converted_value}});
-      }
-    }
-
-    for (const auto& equality_constraint : constraint->equality_constraints()) {
-      std::vector<Nonnull<const Value*>> values;
-      for (const Value* value : equality_constraint.values) {
-        // Ensure we don't create any duplicates through substitution.
-        if (std::find_if(values.begin(), values.end(), [&](const Value* v) {
-              return ValueEqual(v, value, std::nullopt);
-            }) == values.end()) {
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> subst_value,
-              type_checker.Substitute(local_bindings, value));
-          values.push_back(subst_value);
-        }
-      }
-      AddEqualityConstraint({.values = std::move(values)});
-    }
-
-    for (const auto& intrinsic_constraint :
-         constraint->intrinsic_constraints()) {
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> type,
-          type_checker.Substitute(local_bindings, intrinsic_constraint.type));
-      IntrinsicConstraint converted(type, intrinsic_constraint.kind, {});
-      converted.arguments.reserve(intrinsic_constraint.arguments.size());
-      for (Nonnull<const Value*> argument : intrinsic_constraint.arguments) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> subst_arg,
-            type_checker.Substitute(local_bindings, argument));
-        converted.arguments.push_back(subst_arg);
-      }
-      AddIntrinsicConstraint(std::move(converted));
-    }
-
-    if (add_lookup_contexts) {
-      for (const auto& lookup_context : constraint->lookup_contexts()) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> subst_context,
-            type_checker.Substitute(local_bindings, lookup_context.context));
-        AddLookupContext({.context = subst_context});
-      }
-    }
-
-    return Success();
-  }
-
-  class ConstraintsInScopeTracker {
-    friend class ConstraintTypeBuilder;
-
-   private:
-    int num_impls_constraints_added = 0;
-    int num_equality_constraints_added = 0;
-  };
-
-  // Brings all the constraints accumulated so far into the given impl scope, as
-  // if we built the constraint type and then added it into the scope. If this
-  // will be called more than once, an ConstraintssInScopeTracker can be
-  // provided to avoid adding the same implementations more than once.
-  void BringConstraintsIntoScope(const TypeChecker& type_checker,
-                                 Nonnull<ImplScope*> impl_scope,
-                                 Nonnull<ConstraintsInScopeTracker*> tracker) {
-    // Figure out which constraints we're going to add.
-    int first_impls_constraint_to_add = std::exchange(
-        tracker->num_impls_constraints_added, impls_constraints_.size());
-    int first_equality_constraint_to_add = std::exchange(
-        tracker->num_equality_constraints_added, equality_constraints_.size());
-    auto new_impls_constraints =
-        llvm::ArrayRef<ImplsConstraint>(impls_constraints_)
-            .drop_front(first_impls_constraint_to_add);
-    auto new_equality_constraints =
-        llvm::ArrayRef<EqualityConstraint>(equality_constraints_)
-            .drop_front(first_equality_constraint_to_add);
-
-    // Add all of the new constraints.
-    impl_scope->Add(new_impls_constraints, std::nullopt, std::nullopt,
-                    GetSelfWitness(), type_checker);
-    for (const auto& equal : new_equality_constraints) {
-      impl_scope->AddEqualityConstraint(arena_->New<EqualityConstraint>(equal));
-    }
-  }
-
-  // Resolve this set of constraints. Form values for rewrite constraints,
-  // apply the rewrites within the other constraints, and produce a
-  // self-consistent set of constraints or diagnose if that is not possible.
-  //
-  // This should be done when attaching constraints to a declared name, not
-  // when simply forming a constraint type for later use in the type of a
-  // declared name.
-  auto Resolve(TypeChecker& type_checker, SourceLocation source_loc,
-               const ImplScope& /*impl_scope*/) -> ErrorOr<Success> {
-    CARBON_RETURN_IF_ERROR(DeduplicateRewrites(source_loc));
-    CARBON_RETURN_IF_ERROR(ApplyRewritesToRewrites(type_checker, source_loc));
-    CARBON_RETURN_IF_ERROR(ApplyRewritesToConstraints(type_checker));
-    return Success();
-  }
-
-  // Converts the builder into a ConstraintType. Note that this consumes the
-  // builder.
-  auto Build() && -> Nonnull<const ConstraintType*> {
-    // Create the new type.
-    auto* result = arena_->New<ConstraintType>(
-        self_binding_, std::move(impls_constraints_),
-        std::move(intrinsic_constraints_), std::move(equality_constraints_),
-        std::move(rewrite_constraints_), std::move(lookup_contexts_));
-    // Update the impl binding to denote the constraint type itself.
-    impl_binding_->set_interface(result);
-    return result;
-  }
-
-  // Sets up a `.Self` binding to act as the self type of a constraint.
-  static void PrepareSelfBinding(Nonnull<Arena*> arena,
-                                 Nonnull<GenericBinding*> self_binding) {
-    Nonnull<const Value*> self = arena->New<VariableType>(self_binding);
-    self_binding->set_symbolic_identity(self);
-    self_binding->set_value(self);
-  }
-
- private:
-  // Makes a generic binding to serve as the `.Self` of a constraint type.
-  static auto MakeSelfBinding(Nonnull<Arena*> arena, SourceLocation source_loc)
-      -> Nonnull<GenericBinding*> {
-    // Note, the type-of-type here is a placeholder and isn't really
-    // meaningful.
-    auto* result = arena->New<GenericBinding>(
-        source_loc, ".Self", arena->New<TypeTypeLiteral>(source_loc),
-        GenericBinding::BindingKind::Checked);
-    PrepareSelfBinding(arena, result);
-    return result;
-  }
-
-  // Adds an impl binding to the given self binding.
-  static auto AddImplBinding(Nonnull<Arena*> arena,
-                             Nonnull<GenericBinding*> self_binding)
-      -> Nonnull<ImplBinding*> {
-    // The `.Self` binding for a constraint should always have an
-    // `ImplBinding`. The interface type will be set by `Build`.
-    Nonnull<ImplBinding*> impl_binding = arena->New<ImplBinding>(
-        self_binding->source_loc(), self_binding, std::nullopt);
-    impl_binding->set_symbolic_identity(
-        arena->New<BindingWitness>(impl_binding));
-    self_binding->set_impl_binding(impl_binding);
-    return impl_binding;
-  }
-
-  // Check for conflicting rewrites and deduplicate.
-  auto DeduplicateRewrites(SourceLocation source_loc) -> ErrorOr<Success> {
-    std::vector<RewriteConstraint> new_rewrite_constraints;
-    for (auto& rewrite_a : rewrite_constraints_) {
-      if (auto existing_rewrite = LookupRewrite(
-              new_rewrite_constraints, &rewrite_a.constant->interface(),
-              &rewrite_a.constant->constant())) {
-        const auto& rewrite_b = **existing_rewrite;
-        if (ValueEqual(rewrite_a.unconverted_replacement,
-                       rewrite_b.unconverted_replacement, std::nullopt) &&
-            TypeEqual(rewrite_a.unconverted_replacement_type,
-                      rewrite_b.unconverted_replacement_type, std::nullopt)) {
-          // This is a duplicate, ignore it.
-          continue;
-        }
-        return ProgramError(source_loc)
-               << "multiple different rewrites for `" << *rewrite_a.constant
-               << "`:\n"
-               << "  " << *rewrite_b.unconverted_replacement << "\n"
-               << "  " << *rewrite_a.unconverted_replacement;
-      }
-      new_rewrite_constraints.push_back(rewrite_a);
-    }
-    rewrite_constraints_ = std::move(new_rewrite_constraints);
-    return Success();
-  }
-
-  // Apply rewrites to each other and find a fixed point, or diagnose if there
-  // is a cycle.
-  auto ApplyRewritesToRewrites(TypeChecker& type_checker,
-                               SourceLocation source_loc) -> ErrorOr<Success> {
-    // Add this builder to the type checker's scope so that it considers our
-    // rewrites.
-    type_checker.partial_constraint_types_.push_back(this);
-    auto pop_partial_constraint_type = llvm::make_scope_exit(
-        [&] { type_checker.partial_constraint_types_.pop_back(); });
-
-    std::deque<Nonnull<RewriteConstraint*>> rewrite_queue;
-    for (auto& rewrite : rewrite_constraints_) {
-      if (type_checker.trace_stream_->is_enabled()) {
-        type_checker.trace_stream_->End()
-            << "initial rewrite of `" << *rewrite.constant << "` is `"
-            << *rewrite.converted_replacement << "`\n";
-      }
-      rewrite_queue.push_back(&rewrite);
-    }
-
-    int rewrite_iterations = 0;
-    while (!rewrite_queue.empty()) {
-      auto* rewrite = rewrite_queue.front();
-      rewrite_queue.pop_front();
-
-      // This iteration limit exists only to prevent large fuzzer-generated
-      // examples from leading to long compile times. If the limit is hit in a
-      // real example, it should be increased.
-      if (rewrite_iterations > 1000) {
-        return ProgramError(source_loc)
-               << "reached iteration limit resolving rewrite constraints";
-      }
-      ++rewrite_iterations;
-
-      // Rebuild the rewrite and see if it changed. Also track whether it
-      // attempted to reference itself recursively.
-      RewriteInfo info = {.rewrite = rewrite};
-      current_rewrite_info_ = &info;
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> rebuilt,
-          type_checker.RebuildValue(rewrite->converted_replacement));
-      current_rewrite_info_ = std::nullopt;
-
-      if (info.rewrite_references_itself) {
-        // This would result in an infinite loop.
-        return ProgramError(source_loc)
-               << "rewrite of " << *rewrite->constant
-               << " applies within its own resolved expansion of " << *rebuilt;
-      }
-
-      if (!ValueEqual(rebuilt, rewrite->converted_replacement, std::nullopt)) {
-        if (type_checker.trace_stream_->is_enabled()) {
-          type_checker.trace_stream_->End()
-              << "rewrote rewrite of `" << *rewrite->constant << "` to `"
-              << *rebuilt << "`\n";
-        }
-        rewrite->converted_replacement = rebuilt;
-        // Now we've rewritten this rewrite, we might find more rewrites apply
-        // to the portion we rewrote.
-        rewrite_queue.push_back(rewrite);
-      } else {
-        if (type_checker.trace_stream_->is_enabled()) {
-          type_checker.trace_stream_->End()
-              << "rewrite of `" << *rewrite->constant << "` converged to `"
-              << *rebuilt << "`\n";
-        }
-      }
-    }
-
-    return Success();
-  }
-
-  // Apply the rewrite constraints throughout our constraints.
-  auto ApplyRewritesToConstraints(TypeChecker& type_checker)
-      -> ErrorOr<Success> {
-    // Add this builder to the type checker's scope so that it considers our
-    // rewrites.
-    type_checker.partial_constraint_types_.push_back(this);
-    auto pop_partial_constraint_type = llvm::make_scope_exit(
-        [&] { type_checker.partial_constraint_types_.pop_back(); });
-
-    // Apply rewrites through the rewrite constraints. We assume that the
-    // converted replacements have already been rewritten fully.
-    for (auto& rewrite : rewrite_constraints_) {
-      CARBON_ASSIGN_OR_RETURN(
-          rewrite.unconverted_replacement,
-          type_checker.RebuildValue(rewrite.unconverted_replacement));
-      CARBON_ASSIGN_OR_RETURN(
-          rewrite.unconverted_replacement_type,
-          type_checker.RebuildValue(rewrite.unconverted_replacement_type));
-    }
-
-    // Apply rewrites throughout impls constraints.
-    for (auto& impls_constraint : impls_constraints_) {
-      CARBON_ASSIGN_OR_RETURN(impls_constraint.type,
-                              type_checker.RebuildValue(impls_constraint.type));
-      CARBON_ASSIGN_OR_RETURN(
-          const auto* subst_interface,
-          type_checker.RebuildValue(impls_constraint.interface));
-      impls_constraint.interface = cast<InterfaceType>(subst_interface);
-    }
-
-    // Apply rewrites throughout intrinsic constraints.
-    for (auto& intrinsic_constraint : intrinsic_constraints_) {
-      CARBON_ASSIGN_OR_RETURN(
-          intrinsic_constraint.type,
-          type_checker.RebuildValue(intrinsic_constraint.type));
-      for (auto& argument : intrinsic_constraint.arguments) {
-        CARBON_ASSIGN_OR_RETURN(argument, type_checker.RebuildValue(argument));
-      }
-    }
-
-    // Apply rewrites throughout equality constraints.
-    for (auto& equality_constraint : equality_constraints_) {
-      for (auto*& value : equality_constraint.values) {
-        CARBON_ASSIGN_OR_RETURN(value, type_checker.RebuildValue(value));
-      }
-    }
-
-    // Apply rewrites throughout lookup contexts.
-    for (auto& lookup_context : lookup_contexts_) {
-      CARBON_ASSIGN_OR_RETURN(
-          lookup_context.context,
-          type_checker.RebuildValue(lookup_context.context));
-    }
-
-    return Success();
-  }
-
-  Nonnull<Arena*> arena_;
-  Nonnull<GenericBinding*> self_binding_;
-  Nonnull<ImplBinding*> impl_binding_;
-  std::vector<ImplsConstraint> impls_constraints_;
-  std::vector<IntrinsicConstraint> intrinsic_constraints_;
-  std::vector<EqualityConstraint> equality_constraints_;
-  std::vector<RewriteConstraint> rewrite_constraints_;
-  std::vector<LookupContext> lookup_contexts_;
-  std::optional<RewriteInfo*> current_rewrite_info_;
-};
-
-// A collection of substituted `GenericBinding`s and `ImplBinding`s.
-class TypeChecker::SubstitutedGenericBindings {
- public:
-  SubstitutedGenericBindings(Nonnull<const TypeChecker*> type_checker,
-                             Bindings bindings)
-      : type_checker_(type_checker), bindings_(std::move(bindings)) {}
-
-  // Makes a new impl binding for a generic binding if needed, and returns its
-  // witness.
-  auto MakeImplBinding(Nonnull<GenericBinding*> new_binding,
-                       Nonnull<const GenericBinding*> old_binding)
-      -> std::optional<Nonnull<const Witness*>> {
-    if (!old_binding->impl_binding()) {
-      return std::nullopt;
-    }
-    Nonnull<ImplBinding*> impl_binding =
-        type_checker_->arena_->New<ImplBinding>(new_binding->source_loc(),
-                                                new_binding,
-                                                &new_binding->static_type());
-    impl_binding->set_original(old_binding->impl_binding().value());
-    auto* witness = type_checker_->arena_->New<BindingWitness>(impl_binding);
-    impl_binding->set_symbolic_identity(witness);
-    new_binding->set_impl_binding(impl_binding);
-    impl_bindings_.push_back(impl_binding);
-    return witness;
-  }
-
-  // Substitutes into a generic binding and adds it to the bindings map.
-  auto SubstituteIntoGenericBinding(Nonnull<const GenericBinding*> old_binding)
-      -> ErrorOr<Nonnull<GenericBinding*>> {
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const Value*> new_type,
-        type_checker_->Substitute(bindings_, &old_binding->static_type()));
-    Nonnull<GenericBinding*> new_binding =
-        type_checker_->arena_->New<GenericBinding>(
-            old_binding->source_loc(), old_binding->name(),
-            const_cast<Expression*>(&old_binding->type()),
-            old_binding->binding_kind());
-    new_binding->set_original(old_binding->original());
-    new_binding->set_static_type(new_type);
-    bindings_.Add(old_binding,
-                  type_checker_->arena_->New<VariableType>(new_binding),
-                  MakeImplBinding(new_binding, old_binding));
-    return new_binding;
-  }
-
-  // Gets the current set of bindings, including any remappings for substituted
-  // generic bindings and impl bindings.
-  auto bindings() const -> const Bindings& { return bindings_; }
-
-  // Returns ownership of the collection of created `ImplBinding`s.
-  auto TakeImplBindings() && -> std::vector<Nonnull<const ImplBinding*>> {
-    return std::move(impl_bindings_);
-  }
-
- private:
-  Nonnull<const TypeChecker*> type_checker_;
-  Bindings bindings_;
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
-};
-
-auto TypeChecker::Substitute(const Bindings& bindings,
-                             Nonnull<const Value*> value) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  // Don't waste time recursively rebuilding a type if we have nothing to
-  // substitute.
-  if (bindings.empty()) {
-    return value;
-  }
-
-  CARBON_ASSIGN_OR_RETURN(const auto* result, SubstituteImpl(bindings, value));
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Substitute() << "substitution of [";
-    llvm::ListSeparator sep(", ");
-    for (const auto& [name, value] : bindings.args()) {
-      *trace_stream_ << sep << "`" << *name << "` -> `" << *value << "`";
-    }
-    for (const auto& [name, value] : bindings.witnesses()) {
-      *trace_stream_ << sep << "`" << *name << "` -> `" << *value << "`";
-    }
-    *trace_stream_ << "]\n -  old: `" << *value << "`\n +  new: `" << *result
-                   << "`\n";
-  }
-  return result;
-}
-
-auto TypeChecker::RebuildValue(Nonnull<const Value*> value) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  return SubstituteImpl(Bindings(), value);
-}
-
-class TypeChecker::SubstituteTransform
-    : public ValueTransform<SubstituteTransform, ErrorUnwrapper> {
- public:
-  SubstituteTransform(Nonnull<const TypeChecker*> type_checker,
-                      const Bindings& bindings)
-      : ValueTransform(type_checker->arena_),
-        type_checker_(type_checker),
-        bindings_(bindings) {}
-
-  using ValueTransform::operator();
-
-  // Replace a `VariableType` with its binding value if available.
-  auto operator()(Nonnull<const VariableType*> var_type)
-      -> Nonnull<const Value*> {
-    auto it = bindings_.args().find(&var_type->binding());
-    if (it == bindings_.args().end()) {
-      if (const auto* trace_stream = type_checker_->trace_stream_;
-          trace_stream->is_enabled()) {
-        trace_stream->End() << "substitution: no value for binding `"
-                            << *var_type << "`, leaving alone\n";
-      }
-      return var_type;
-    } else {
-      return it->second;
-    }
-  }
-
-  // Replace a `BindingWitness` with its binding value if available.
-  auto operator()(Nonnull<const BindingWitness*> witness)
-      -> Nonnull<const Value*> {
-    auto it = bindings_.witnesses().find(witness->binding());
-    if (it == bindings_.witnesses().end()) {
-      if (const auto* trace_stream = type_checker_->trace_stream_;
-          trace_stream->is_enabled()) {
-        trace_stream->End() << "substitution: no value for binding `"
-                            << *witness << "`, leaving alone\n";
-      }
-      return witness;
-    } else {
-      return it->second;
-    }
-  }
-
-  // When substituting into the bindings of an `ImplWitness`, we may need to
-  // perform template instantiation.
-  auto operator()(Nonnull<const ImplWitness*> witness)
-      -> ErrorOr<Nonnull<const ImplWitness*>> {
-    CARBON_ASSIGN_OR_RETURN(const auto* bindings,
-                            Transform(&witness->bindings()));
-    const auto* declaration = &witness->declaration();
-    if (!IsTemplateSaturated(witness->bindings()) &&
-        IsTemplateSaturated(*bindings)) {
-      return type_checker_->InstantiateImplDeclaration(declaration, bindings);
-    } else {
-      return type_checker_->arena_->New<ImplWitness>(declaration, bindings);
-    }
-  }
-
-  // For an associated constant, look for a rewrite.
-  auto operator()(Nonnull<const AssociatedConstant*> assoc)
-      -> ErrorOr<Nonnull<const Value*>> {
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base,
-                            Transform(&assoc->base()));
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const InterfaceType*> interface,
-                            Transform(&assoc->interface()));
-    // If we're substituting into an associated constant, we may now be able
-    // to rewrite it to a concrete value.
-    CARBON_ASSIGN_OR_RETURN(auto rewritten_value,
-                            type_checker_->LookupRewriteInTypeOf(
-                                base, interface, &assoc->constant()));
-    if (rewritten_value) {
-      return (*rewritten_value)->converted_replacement;
-    }
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> witness_value,
-                            Transform(&assoc->witness()));
-    const auto* witness = cast<Witness>(witness_value);
-    CARBON_ASSIGN_OR_RETURN(
-        witness, type_checker_->RefineWitness(witness, base, interface));
-    CARBON_ASSIGN_OR_RETURN(rewritten_value,
-                            type_checker_->LookupRewriteInWitness(
-                                witness, interface, &assoc->constant()));
-    if (rewritten_value) {
-      return (*rewritten_value)->converted_replacement;
-    }
-    return type_checker_->arena_->New<AssociatedConstant>(
-        base, interface, &assoc->constant(), witness);
-  }
-
-  // Rebuilding a function type needs special handling to build new bindings.
-  // TODO: This is probably not specific to substitution, and would apply to
-  // other transforms too.
-  auto operator()(Nonnull<const FunctionType*> fn_type)
-      -> ErrorOr<Nonnull<const FunctionType*>> {
-    SubstitutedGenericBindings subst_bindings(type_checker_, bindings_);
-
-    // Apply substitution to into generic parameters and deduced bindings.
-    std::vector<FunctionType::GenericParameter> generic_parameters;
-    for (const FunctionType::GenericParameter& gp :
-         fn_type->generic_parameters()) {
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const GenericBinding*> subst_binding,
-          subst_bindings.SubstituteIntoGenericBinding(gp.binding));
-      generic_parameters.push_back(
-          {.index = gp.index, .binding = subst_binding});
-    }
-    std::vector<Nonnull<const GenericBinding*>> deduced_bindings;
-    for (Nonnull<const GenericBinding*> gb : fn_type->deduced_bindings()) {
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const GenericBinding*> subst_binding,
-                              subst_bindings.SubstituteIntoGenericBinding(gb));
-      deduced_bindings.push_back(subst_binding);
-    }
-
-    // Apply substitution to parameter and return types and create the new
-    // function type.
-    CARBON_ASSIGN_OR_RETURN(const auto* param, type_checker_->SubstituteImpl(
-                                                   subst_bindings.bindings(),
-                                                   &fn_type->parameters()));
-    CARBON_ASSIGN_OR_RETURN(const auto* ret, type_checker_->SubstituteImpl(
-                                                 subst_bindings.bindings(),
-                                                 &fn_type->return_type()));
-    std::optional<FunctionType::MethodSelf> method_self =
-        fn_type->method_self();
-    if (method_self.has_value()) {
-      CARBON_ASSIGN_OR_RETURN(
-          const auto* self_type,
-          type_checker_->SubstituteImpl(subst_bindings.bindings(),
-                                        method_self->self_type));
-      method_self->self_type = self_type;
-    }
-    return type_checker_->arena_->New<FunctionType>(
-        method_self, param, std::move(generic_parameters), ret,
-        std::move(deduced_bindings),
-        std::move(subst_bindings).TakeImplBindings(),
-        fn_type->is_initializing());
-  }
-
-  // Substituting into a `ConstraintType` needs special handling if we replace
-  // its self type.
-  auto operator()(Nonnull<const ConstraintType*> constraint)
-      -> ErrorOr<Nonnull<const Value*>> {
-    if (auto it = bindings_.args().find(constraint->self_binding());
-        it != bindings_.args().end()) {
-      // This happens when we substitute into the parameter type of a
-      // function that takes a `T:! Constraint` parameter. In this case we
-      // produce the new type-of-type of the replacement type.
-      Nonnull<const Value*> type_of_type;
-      if (const auto* var_type = dyn_cast<VariableType>(it->second)) {
-        type_of_type = &var_type->binding().static_type();
-      } else if (const auto* assoc_type =
-                     dyn_cast<AssociatedConstant>(it->second)) {
-        CARBON_ASSIGN_OR_RETURN(
-            type_of_type,
-            type_checker_->GetTypeForAssociatedConstant(assoc_type));
-      } else {
-        type_of_type = type_checker_->arena_->New<TypeType>();
-      }
-      if (const auto* trace_stream = type_checker_->trace_stream_;
-          trace_stream->is_enabled()) {
-        trace_stream->End()
-            << "substitution: self of constraint `" << *constraint
-            << "` is substituted, new type of type is `" << *type_of_type
-            << "`\n";
-      }
-      // TODO: Should we keep any part of the old constraint -- rewrites,
-      // equality constraints, etc?
-      return type_of_type;
-    }
-    ConstraintTypeBuilder builder(type_checker_->arena_,
-                                  constraint->self_binding()->source_loc());
-    CARBON_RETURN_IF_ERROR(builder.AddAndSubstitute(
-        *type_checker_, constraint, builder.GetSelfType(),
-        builder.GetSelfWitness(), bindings_,
-        /*add_lookup_contexts=*/true));
-    Nonnull<const ConstraintType*> new_constraint = std::move(builder).Build();
-    if (const auto* trace_stream = type_checker_->trace_stream_;
-        trace_stream->is_enabled()) {
-      trace_stream->End() << "substitution: " << *constraint << " => "
-                          << *new_constraint << "\n";
-    }
-    return new_constraint;
-  }
-
- private:
-  Nonnull<const TypeChecker*> type_checker_;
-  const Bindings& bindings_;
-};
-
-auto TypeChecker::SubstituteImpl(const Bindings& bindings,
-                                 Nonnull<const Value*> type) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  return SubstituteTransform(this, bindings).Transform(type);
-}
-
-auto TypeChecker::RefineWitness(Nonnull<const Witness*> witness,
-                                Nonnull<const Value*> type,
-                                Nonnull<const Value*> constraint) const
-    -> ErrorOr<Nonnull<const Witness*>> {
-  if (!top_level_impl_scope_) {
-    return witness;
-  }
-
-  // See if this is already resolved as some number of layers of
-  // ConstraintImplWitness applied to an ImplWitness.
-  Nonnull<const Witness*> inner_witness = witness;
-  while (const auto* inner_constraint_impl_witness =
-             dyn_cast<ConstraintImplWitness>(inner_witness)) {
-    inner_witness = inner_constraint_impl_witness->constraint_witness();
-  }
-  if (isa<ImplWitness>(inner_witness)) {
-    return witness;
-  }
-
-  // Attempt to look for an impl witness in the top-level impl scope.
-  // TODO: Provide a location.
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<Nonnull<const Witness*>> refined_witness,
-      (*top_level_impl_scope_)
-          ->TryResolve(constraint, type, SourceLocation::DiagnosticsIgnored(),
-                       *this, /*bindings=*/{},
-                       /*diagnose_missing_impl=*/false));
-  if (refined_witness) {
-    return *refined_witness;
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Not() << "could not refine `" << *witness << "`\n";
-  }
-  return witness;
-}
-
-auto TypeChecker::MatchImpl(const InterfaceType& iface,
-                            Nonnull<const Value*> impl_type,
-                            const ImplScope::ImplFact& impl,
-                            const ImplScope& impl_scope,
-                            SourceLocation source_loc) const
-    -> ErrorOr<std::optional<Nonnull<const Witness*>>> {
-  // Avoid cluttering the trace output with matches that could obviously never
-  // have worked.
-  // TODO: Eventually, ImplScope should filter by type structure before calling
-  // into here.
-  if (!DeclaresSameEntity(impl.interface->declaration(), iface.declaration())) {
-    return {std::nullopt};
-  }
-
-  // Track that we're matching this impl.
-  MatchingImplSet::Match match(&matching_impl_set_, &impl, impl_type, &iface);
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->SubHeading("match impl");
-    trace_stream_->Start() << "looking for `" << *impl_type << "` as `" << iface
-                           << "`\n";
-    trace_stream_->Start() << "checking `" << *impl.type << "` as `"
-                           << *impl.interface << "` (" << source_loc << ")\n";
-  }
-
-  ArgumentDeduction deduction(source_loc, "match", impl.deduced, trace_stream_);
-  if (ErrorOr<Success> e =
-          deduction.Deduce(impl.type, impl_type,
-                           /*allow_implicit_conversion=*/false);
-      !e.ok()) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Not() << "type does not match: " << e.error() << "\n";
-    }
-    return {std::nullopt};
-  }
-
-  if (ErrorOr<Success> e = deduction.Deduce(
-          impl.interface, &iface, /*allow_implicit_conversion=*/false);
-      !e.ok()) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Not() << "interface does not match: " << e.error() << "\n";
-    }
-    return {std::nullopt};
-  }
-
-  // This impl seems to match. Reject if we're already matching this or a
-  // simpler version of it, before we recursively try to satisfy its
-  // constraints.
-  CARBON_RETURN_IF_ERROR(match.DiagnosePotentialCycle(source_loc));
-
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<Bindings> bindings_or_error,
-      deduction.Finish(const_cast<TypeChecker&>(*this), impl_scope,
-                       /*diagnose_deduction_failure=*/false));
-  if (!bindings_or_error) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Not() << "impl does not match\n";
-    }
-    return {std::nullopt};
-  } else {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "matched with `" << *impl.type << "` as `"
-                              << *impl.interface << "`\n\n";
-    }
-    CARBON_ASSIGN_OR_RETURN(
-        const auto* subst_witness,
-        SubstituteCast<Witness>(*bindings_or_error, impl.witness));
-    return {subst_witness};
-  }
-}
-
-auto TypeChecker::MakeConstraintWitness(
-    std::vector<Nonnull<const Witness*>> impls_constraint_witnesses) const
-    -> Nonnull<const Witness*> {
-  return arena_->New<ConstraintWitness>(std::move(impls_constraint_witnesses));
-}
-
-auto TypeChecker::MakeConstraintWitnessAccess(Nonnull<const Witness*> witness,
-                                              int impl_offset) const
-    -> Nonnull<const Witness*> {
-  return ConstraintImplWitness::Make(arena_, witness, impl_offset);
-}
-
-auto TypeChecker::ConvertToConstraintType(
-    SourceLocation source_loc, std::string_view context,
-    Nonnull<const Value*> constraint) const
-    -> ErrorOr<Nonnull<const ConstraintType*>> {
-  if (const auto* constraint_type = dyn_cast<ConstraintType>(constraint)) {
-    return constraint_type;
-  }
-  if (const auto* iface_type = dyn_cast<InterfaceType>(constraint)) {
-    CARBON_RETURN_IF_ERROR(
-        ExpectCompleteType(source_loc, "constraint", iface_type));
-    return SubstituteCast<ConstraintType>(
-        iface_type->bindings(), *iface_type->declaration().constraint_type());
-  }
-  if (const auto* constraint_type = dyn_cast<NamedConstraintType>(constraint)) {
-    CARBON_RETURN_IF_ERROR(
-        ExpectCompleteType(source_loc, "constraint", constraint_type));
-    return SubstituteCast<ConstraintType>(
-        constraint_type->bindings(),
-        *constraint_type->declaration().constraint_type());
-  }
-  if (isa<TypeType>(constraint)) {
-    // TODO: Should we build this once and cache it?
-    ConstraintTypeBuilder builder(arena_, source_loc);
-    return std::move(builder).Build();
-  }
-
-  return ProgramError(source_loc)
-         << "expected a constraint in " << context << ", found " << *constraint;
-}
-
-auto TypeChecker::CombineConstraints(
-    SourceLocation source_loc,
-    llvm::ArrayRef<Nonnull<const ConstraintType*>> constraints)
-    -> ErrorOr<Nonnull<const ConstraintType*>> {
-  ConstraintTypeBuilder builder(arena_, source_loc);
-  for (Nonnull<const ConstraintType*> constraint : constraints) {
-    CARBON_RETURN_IF_ERROR(
-        builder.AddAndSubstitute(*this, constraint, builder.GetSelfType(),
-                                 builder.GetSelfWitness(), Bindings(),
-                                 /*add_lookup_contexts=*/true));
-  }
-  return std::move(builder).Build();
-}
-
-auto TypeChecker::DeduceCallBindings(
-    CallExpression& call, Nonnull<const Value*> params_type,
-    llvm::ArrayRef<FunctionType::GenericParameter> generic_params,
-    llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-    const ImplScope& impl_scope) -> ErrorOr<Success> {
-  llvm::ArrayRef<Nonnull<const Value*>> params =
-      cast<TupleType>(*params_type).elements();
-  llvm::ArrayRef<Nonnull<Expression*>> args =
-      cast<TupleLiteral>(call.argument()).fields();
-  if (params.size() != args.size()) {
-    return ProgramError(call.source_loc())
-           << "wrong number of arguments in function call, expected "
-           << params.size() << " but got " << args.size();
-  }
-
-  // Deductions performed for deduced parameters and generic parameters.
-  ArgumentDeduction deduction(call.source_loc(), "call", deduced_bindings,
-                              trace_stream_);
-
-  // Deduce and/or convert each argument to the corresponding
-  // parameter.
-  for (const auto [i, param, arg] : llvm::enumerate(params, args)) {
-    if (!generic_params.empty() && generic_params.front().index == i) {
-      // The parameter is a `:!` binding. Collect its argument so we can
-      // evaluate it when we're done with deduction.
-      deduction.AddNonDeducedBindingValue(generic_params.front().binding, arg);
-      generic_params = generic_params.drop_front();
-    } else {
-      // Otherwise deduce its type from the corresponding argument.
-      CARBON_RETURN_IF_ERROR(
-          deduction.Deduce(param, &arg->static_type(),
-                           /*allow_implicit_conversion=*/true));
-    }
-  }
-  CARBON_CHECK(generic_params.empty(),
-               "did not find all generic parameters in parameter list");
-
-  CARBON_ASSIGN_OR_RETURN(
-      std::optional<Bindings> bindings,
-      deduction.Finish(*this, impl_scope, /*diagnose_deduction_failure=*/true));
-  CARBON_CHECK(bindings, "should have diagnosed deduction failure");
-  call.set_bindings(std::move(*bindings));
-
-  // Convert the arguments to the deduced and substituted parameter type.
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> param_type,
-                          Substitute(call.bindings(), params_type));
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<Expression*> converted_argument,
-      ImplicitlyConvert("call", impl_scope, &call.argument(), param_type));
-  call.set_argument(converted_argument);
-
-  return Success();
-}
-
-auto TypeChecker::LookupInConstraint(SourceLocation source_loc,
-                                     std::string_view lookup_kind,
-                                     Nonnull<const Value*> type,
-                                     std::string_view member_name)
-    -> ErrorOr<ConstraintLookupResult> {
-  // Find the set of lookup contexts.
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const ConstraintType*> constraint_type,
-      ConvertToConstraintType(source_loc, lookup_kind, type));
-  llvm::ArrayRef<LookupContext> lookup_contexts =
-      constraint_type->lookup_contexts();
-
-  std::optional<ConstraintLookupResult> found;
-  for (LookupContext lookup : lookup_contexts) {
-    if (!isa<InterfaceType>(lookup.context)) {
-      // TODO: Support other kinds of lookup context, notably named
-      // constraints.
-      continue;
-    }
-    const auto& iface_type = cast<InterfaceType>(*lookup.context);
-    if (std::optional<Nonnull<const Declaration*>> member =
-            FindMember(member_name, iface_type.declaration().members());
-        member.has_value()) {
-      if (found.has_value()) {
-        if (ValueEqual(found->interface, &iface_type, std::nullopt)) {
-          continue;
-        }
-        // TODO: If we resolve to the same member either way, this
-        // is not ambiguous.
-        return ProgramError(source_loc)
-               << "ambiguous " << lookup_kind << ", " << member_name
-               << " found in " << *found->interface << " and " << iface_type;
-      }
-      found = {.interface = &iface_type, .member = member.value()};
-    }
-  }
-
-  if (!found) {
-    if (isa<TypeType>(type)) {
-      return ProgramError(source_loc)
-             << lookup_kind << " in unconstrained type";
-    }
-    return ProgramError(source_loc)
-           << lookup_kind << ", " << member_name << " not in " << *type;
-  }
-
-  return found.value();
-}
-
-auto TypeChecker::GetTypeForAssociatedConstant(
-    Nonnull<const AssociatedConstant*> assoc) const
-    -> ErrorOr<Nonnull<const Value*>> {
-  const auto* assoc_type = &assoc->constant().static_type();
-  Bindings bindings = assoc->interface().bindings();
-  bindings.Add(assoc->interface().declaration().self(), &assoc->base(),
-               &assoc->witness());
-  return Substitute(bindings, assoc_type);
-}
-
-auto TypeChecker::LookupRewriteInTypeOf(
-    Nonnull<const Value*> type, Nonnull<const InterfaceType*> interface,
-    Nonnull<const Declaration*> member) const
-    -> ErrorOr<std::optional<const RewriteConstraint*>> {
-  // If the type is the self type of an incomplete `where` expression or a
-  // constraint type we're in the process of resolving, find its set of
-  // rewrites. These rewrites may not be complete -- earlier rewrites will have
-  // been applied to later ones, but not vice versa -- but those are the
-  // intended semantics in this case.
-  for (auto* builder : partial_constraint_types_) {
-    if (ValueEqual(type, builder->GetSelfType(), std::nullopt)) {
-      if (auto result = LookupRewrite(builder->rewrite_constraints(), interface,
-                                      member)) {
-        // If we're in the middle of rewriting this rewrite, let the constraint
-        // type builder know it applies within itself, and don't expand it
-        // within itself.
-        if (auto current_rewrite_info = builder->current_rewrite_info();
-            current_rewrite_info &&
-            (*current_rewrite_info)->rewrite == *result) {
-          (*current_rewrite_info)->rewrite_references_itself = true;
-          return {std::nullopt};
-        }
-        return result;
-      }
-    }
-  }
-
-  // Given `(T:! C).Y`, look in `C` for rewrites.
-  if (const auto* var_type = dyn_cast<VariableType>(type)) {
-    if (!var_type->binding().has_static_type()) {
-      // We looked for a rewrite before we finished type-checking the generic
-      // binding. This happens when forming the type of a generic binding. Just
-      // say there are no rewrites yet; any rewrites will be applied when the
-      // constraint on the binding's type is resolved.
-      return {std::nullopt};
-    }
-    return LookupRewrite(&var_type->binding().static_type(), interface, member);
-  }
-
-  // Given `(T.U).Y` for an associated type `U`, substitute into the type of
-  // `U` to find rewrites.
-  if (const auto* assoc_const = dyn_cast<AssociatedConstant>(type)) {
-    if (!assoc_const->constant().has_static_type()) {
-      // We looked for a rewrite before we finished type-checking the
-      // associated constant. This happens when forming the type of the
-      // associated constant, if `.Self` is used to access an associated
-      // constant. Just say that there are not rewrites yet; any rewrites will
-      // be applied when the constraint on the binding's type is resolved.
-      return {std::nullopt};
-    }
-    // The following is an expanded version of
-    //  return LookupRewrite(GetTypeForAssociatedConstant(assoc_const),
-    //                       interface, member);
-    // where we substitute as little as possible to try to avoid infinite
-    // recursion.
-    if (const auto* constraint =
-            dyn_cast<ConstraintType>(&assoc_const->constant().static_type())) {
-      for (auto rewrite : constraint->rewrite_constraints()) {
-        if (&rewrite.constant->constant() != &assoc_const->constant()) {
-          continue;
-        }
-        Bindings bindings = assoc_const->interface().bindings();
-        bindings.Add(assoc_const->interface().declaration().self(),
-                     &assoc_const->base(), &assoc_const->witness());
-        // TODO: These substitutions can lead to infinite recursion.
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> rewrite_interface,
-            Substitute(bindings, &rewrite.constant->interface()));
-        if (ValueEqual(interface, rewrite_interface, std::nullopt)) {
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> unconverted_replacement,
-              Substitute(bindings, rewrite.unconverted_replacement));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> unconverted_replacement_type,
-              Substitute(bindings, rewrite.unconverted_replacement_type));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> converted_replacement,
-              Substitute(bindings, rewrite.converted_replacement));
-          RewriteConstraint substituted = {
-              // Not substituted, but our callers don't need it.
-              .constant = rewrite.constant,
-              .unconverted_replacement = unconverted_replacement,
-              .unconverted_replacement_type = unconverted_replacement_type,
-              .converted_replacement = converted_replacement};
-          return {arena_->New<RewriteConstraint>(substituted)};
-        }
-      }
-    }
-  }
-
-  return {std::nullopt};
-}
-
-auto TypeChecker::LookupRewriteInWitness(
-    Nonnull<const Witness*> witness, Nonnull<const InterfaceType*> interface,
-    Nonnull<const Declaration*> member) const
-    -> ErrorOr<std::optional<const RewriteConstraint*>> {
-  if (const auto* impl_witness = dyn_cast<ImplWitness>(witness)) {
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const Value*> constraint,
-        Substitute(impl_witness->bindings(),
-                   impl_witness->declaration().constraint_type()));
-    return LookupRewrite(constraint, interface, member);
-  }
-  return {std::nullopt};
-}
-
-// Rewrites a member access expression to produce the given constant value.
-static void RewriteMemberAccess(Nonnull<MemberAccessExpression*> access,
-                                Nonnull<const RewriteConstraint*> value) {
-  access->set_expression_category(ExpressionCategory::Value);
-  access->set_static_type(value->unconverted_replacement_type);
-  access->set_constant_value(value->unconverted_replacement);
-}
-
-// Determine whether the given member declaration declares an instance member.
-static auto IsInstanceMember(Nonnull<const Element*> element) {
-  switch (element->kind()) {
-    case ElementKind::BaseElement:
-    case ElementKind::PositionalElement:
-      return true;
-    case ElementKind::NamedElement:
-      const auto* const nom_element = cast<NamedElement>(element);
-      if (!nom_element->declaration()) {
-        // This is a struct field.
-        return true;
-      }
-      Nonnull<const Declaration*> declaration = *nom_element->declaration();
-      switch (declaration->kind()) {
-        case DeclarationKind::FunctionDeclaration:
-          return cast<FunctionDeclaration>(declaration)->is_method();
-        case DeclarationKind::VariableDeclaration:
-          return true;
-        default:
-          return false;
-      }
-  }
-}
-
-auto TypeChecker::CheckAddrSelfAccess(
-    Nonnull<MemberAccessExpression*> access,
-    Nonnull<const FunctionDeclaration*> func_decl, const Bindings& bindings,
-    const ImplScope& impl_scope) -> ErrorOr<Success> {
-  if (!func_decl->is_method()) {
-    return Success();
-  }
-
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const Value*> self_type,
-      Substitute(bindings, &func_decl->self_pattern().static_type()));
-  if (self_type->kind() == Value::Kind::PointerType &&
-      access->object().static_type().kind() != Value::Kind::PointerType) {
-    return ProgramError(access->source_loc())
-           << "method " << *access
-           << " does not match the target function's self pattern (did you "
-              "forget an `addr`?)";
-  }
-  if (func_decl->self_pattern().kind() == PatternKind::AddrPattern) {
-    access->set_is_addr_me_method();
-    CARBON_RETURN_IF_ERROR(ExpectExactType(
-        access->source_loc(), "method access, receiver type", self_type,
-        &access->object().static_type(), impl_scope));
-    if (access->object().expression_category() !=
-        ExpressionCategory::Reference) {
-      return ProgramError(access->source_loc())
-             << "method " << *access
-             << " requires its receiver to be a reference expression";
-    }
-  }
-  return Success();
-}
-
-auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
-                               const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  return RunWithExtraStack([&]() { return TypeCheckExpImpl(e, impl_scope); });
-}
-
-// NOLINTNEXTLINE(readability-function-size)
-auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
-                                   const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking " << e->kind() << " `" << *e << "` ("
-                           << e->source_loc() << ")\n";
-  }
-  if (e->is_type_checked()) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->End() << "expression `" << *e
-                           << "` has already been type-checked\n";
-    }
-    return Success();
-  }
-  switch (e->kind()) {
-    case ExpressionKind::ValueLiteral:
-    case ExpressionKind::BuiltinConvertExpression:
-    case ExpressionKind::BaseAccessExpression:
-      CARBON_FATAL(
-          "attempting to type check node {0} generated during type checking",
-          *e);
-    case ExpressionKind::IndexExpression: {
-      auto& index = cast<IndexExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&index.object(), impl_scope));
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&index.offset(), impl_scope));
-      const Value& object_type = index.object().static_type();
-      switch (object_type.kind()) {
-        case Value::Kind::TupleType: {
-          const auto& tuple_type = cast<TupleType>(object_type);
-          CARBON_RETURN_IF_ERROR(
-              ExpectExactType(index.offset().source_loc(), "tuple index",
-                              arena_->New<IntType>(),
-                              &index.offset().static_type(), impl_scope));
-          CARBON_ASSIGN_OR_RETURN(auto offset_value,
-                                  InterpExp(&index.offset()));
-          int i = cast<IntValue>(*offset_value).value();
-          if (i < 0 || i >= static_cast<int>(tuple_type.elements().size())) {
-            return ProgramError(e->source_loc())
-                   << "index " << i << " is out of range for type "
-                   << tuple_type;
-          }
-          index.set_static_type(tuple_type.elements()[i]);
-          index.set_expression_category(index.object().expression_category());
-          return Success();
-        }
-        case Value::Kind::StaticArrayType: {
-          CARBON_RETURN_IF_ERROR(
-              ExpectExactType(index.offset().source_loc(), "array index",
-                              arena_->New<IntType>(),
-                              &index.offset().static_type(), impl_scope));
-          index.set_static_type(
-              &cast<StaticArrayType>(object_type).element_type());
-          index.set_expression_category(index.object().expression_category());
-          return Success();
-        }
-        default:
-          return ProgramError(e->source_loc())
-                 << "only arrays and tuples can be indexed, found "
-                 << object_type;
-      }
-    }
-    case ExpressionKind::TupleLiteral: {
-      std::vector<Nonnull<const Value*>> arg_types;
-      for (auto* arg : cast<TupleLiteral>(*e).fields()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(arg, impl_scope));
-        CARBON_RETURN_IF_ERROR(
-            ExpectNonPlaceholderType(arg->source_loc(), &arg->static_type()));
-        arg_types.push_back(&arg->static_type());
-      }
-      e->set_static_type(arena_->New<TupleType>(std::move(arg_types)));
-      e->set_expression_category(ExpressionCategory::Value);
-      return Success();
-    }
-    case ExpressionKind::StructLiteral: {
-      std::vector<NamedValue> arg_types;
-      for (auto& arg : cast<StructLiteral>(*e).fields()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(&arg.expression(), impl_scope));
-        CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
-            arg.expression().source_loc(), &arg.expression().static_type()));
-        arg_types.push_back({arg.name(), &arg.expression().static_type()});
-      }
-      e->set_static_type(arena_->New<StructType>(std::move(arg_types)));
-      e->set_expression_category(ExpressionCategory::Value);
-      return Success();
-    }
-    case ExpressionKind::StructTypeLiteral: {
-      auto& struct_type = cast<StructTypeLiteral>(*e);
-      std::vector<NamedValue> fields;
-      for (auto& arg : struct_type.fields()) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> type,
-            TypeCheckTypeExp(&arg.expression(), impl_scope));
-        fields.push_back({arg.name(), type});
-      }
-      struct_type.set_static_type(arena_->New<TypeType>());
-      struct_type.set_expression_category(ExpressionCategory::Value);
-      struct_type.set_constant_value(
-          arena_->New<StructType>(std::move(fields)));
-      return Success();
-    }
-    case ExpressionKind::SimpleMemberAccessExpression: {
-      auto& access = cast<SimpleMemberAccessExpression>(*e);
-
-      // If name lookup resolved this member access statically, rewrite it to
-      // an identifier expression.
-      if (auto value_node = access.value_node()) {
-        auto* rewritten = arena_->New<IdentifierExpression>(
-            access.source_loc(), access.member_name());
-        rewritten->set_value_node(*value_node);
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(rewritten, impl_scope));
-        access.set_rewritten_form(rewritten);
-        return Success();
-      }
-
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&access.object(), impl_scope));
-      const Value& object_type = access.object().static_type();
-      CARBON_RETURN_IF_ERROR(ExpectCompleteType(access.source_loc(),
-                                                "member access", &object_type));
-      switch (object_type.kind()) {
-        case Value::Kind::StructType: {
-          const auto& struct_type = cast<StructType>(object_type);
-          for (const auto& field : struct_type.fields()) {
-            if (access.member_name() == field.name) {
-              access.set_member(arena_->New<NamedElement>(&field));
-              access.set_static_type(field.value);
-              access.set_expression_category(
-                  access.object().expression_category());
-              return Success();
-            }
-          }
-          return ProgramError(access.source_loc())
-                 << "struct " << struct_type << " does not have a field named "
-                 << access.member_name();
-        }
-        case Value::Kind::NominalClassType: {
-          const auto& t_class = cast<NominalClassType>(object_type);
-          CARBON_ASSIGN_OR_RETURN(
-              const auto res,
-              FindMemberWithParents(access.member_name(), &t_class));
-          if (res.has_value()) {
-            auto [member_type, member, member_t_class] = res.value();
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> field_type,
-                Substitute(member_t_class->bindings(), member_type));
-            access.set_member(arena_->New<NamedElement>(member));
-            access.set_is_type_access(!IsInstanceMember(&access.member()));
-            switch (member->kind()) {
-              case DeclarationKind::VariableDeclaration:
-                access.set_static_type(field_type);
-                access.set_expression_category(
-                    access.object().expression_category());
-                break;
-              case DeclarationKind::FunctionDeclaration: {
-                const auto* func_decl = cast<FunctionDeclaration>(member);
-                CARBON_RETURN_IF_ERROR(CheckAddrSelfAccess(
-                    &access, func_decl, t_class.bindings(), impl_scope));
-                if (access.is_type_access()) {
-                  access.set_static_type(field_type);
-                } else {
-                  // Remove `self` from type since now bound.
-                  auto* function_type = cast<FunctionType>(field_type);
-                  access.set_static_type(arena_->New<FunctionType>(
-                      FunctionType::ExceptSelf{}, function_type));
-                }
-                access.set_expression_category(ExpressionCategory::Value);
-                break;
-              }
-              case DeclarationKind::AliasDeclaration:
-                return ProgramError(access.source_loc())
-                       << "Member access to aliases is not yet supported.";
-              default:
-                CARBON_FATAL("member {0} is not a field or method",
-                             access.member_name());
-                break;
-            }
-            return Success();
-          } else {
-            return ProgramError(e->source_loc())
-                   << "class " << t_class.declaration().name()
-                   << " does not have a field named " << access.member_name();
-          }
-        }
-        case Value::Kind::VariableType:
-        case Value::Kind::AssociatedConstant: {
-          // This case handles access to a method on a receiver whose type is a
-          // type variable or associated constant. For example, `x.foo` where
-          // the type of `x` is `T` and `T` implements an interface that
-          // includes `foo`, or `x.y().foo` where the type of `x` is `T` and
-          // the return type of `y()` is an associated constant from `T`'s
-          // constraint.
-          Nonnull<const Value*> constraint;
-          if (const auto* var_type = dyn_cast<VariableType>(&object_type)) {
-            constraint = &var_type->binding().static_type();
-          } else {
-            CARBON_ASSIGN_OR_RETURN(
-                constraint, GetTypeForAssociatedConstant(
-                                cast<AssociatedConstant>(&object_type)));
-          }
-          CARBON_ASSIGN_OR_RETURN(
-              ConstraintLookupResult result,
-              LookupInConstraint(e->source_loc(), "member access", constraint,
-                                 access.member_name()));
-          if (auto replacement =
-                  LookupRewrite(constraint, result.interface, result.member)) {
-            RewriteMemberAccess(&access, *replacement);
-            return Success();
-          }
-          // Compute a witness that the variable type implements this
-          // interface. This will typically be either a reference to its
-          // `ImplBinding` or, for a constraint, to a witness for an impl
-          // constraint within it.
-          // TODO: We should only need to look at the impl binding for this
-          // variable or witness for this associated constant, not everything in
-          // the impl scope, to find the witness.
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const ConstraintType*> iface_constraint,
-              ConvertToConstraintType(access.source_loc(), "member access",
-                                      result.interface));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Witness*> witness,
-              impl_scope.Resolve(iface_constraint, &object_type,
-                                 e->source_loc(), *this));
-
-          Bindings bindings = result.interface->bindings();
-          bindings.Add(result.interface->declaration().self(), &object_type,
-                       witness);
-
-          const Value& member_type = result.member->static_type();
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_member_type,
-                                  Substitute(bindings, &member_type))
-          access.set_member(arena_->New<NamedElement>(result.member));
-          access.set_found_in_interface(result.interface);
-          access.set_is_type_access(!IsInstanceMember(&access.member()));
-          access.set_expression_category(ExpressionCategory::Value);
-
-          if (const auto* func_decl =
-                  dyn_cast<FunctionDeclaration>(result.member)) {
-            CARBON_RETURN_IF_ERROR(
-                CheckAddrSelfAccess(&access, func_decl, bindings, impl_scope));
-            if (access.is_type_access()) {
-              access.set_static_type(inst_member_type);
-            } else {
-              // Remove `self` from type since now bound.
-              auto* function_type = cast<FunctionType>(inst_member_type);
-              access.set_static_type(arena_->New<FunctionType>(
-                  FunctionType::ExceptSelf{}, function_type));
-            }
-          } else {
-            access.set_static_type(inst_member_type);
-          }
-
-          // TODO: This is just a ConstraintImplWitness into the
-          // iface_constraint. If we can compute the right index, we can avoid
-          // re-resolving it.
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Witness*> impl,
-              impl_scope.Resolve(result.interface, &object_type,
-                                 e->source_loc(), *this));
-          access.set_impl(impl);
-          return Success();
-        }
-        case Value::Kind::InterfaceType:
-        case Value::Kind::NamedConstraintType:
-        case Value::Kind::ConstraintType: {
-          // This case handles access to a class function from a constrained
-          // type variable. If `T` is a type variable and `foo` is a class
-          // function in an interface implemented by `T`, then `T.foo` accesses
-          // the `foo` class function of `T`.
-          //
-          // TODO: Per the language rules, we are supposed to also perform
-          // lookup into `type` and report an ambiguity if the name is found in
-          // both places.
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                                  InterpExp(&access.object()));
-          CARBON_ASSIGN_OR_RETURN(
-              ConstraintLookupResult result,
-              LookupInConstraint(e->source_loc(), "member access", &object_type,
-                                 access.member_name()));
-          if (auto replacement = LookupRewrite(&object_type, result.interface,
-                                               result.member)) {
-            RewriteMemberAccess(&access, *replacement);
-            return Success();
-          }
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const ConstraintType*> iface_constraint,
-              ConvertToConstraintType(access.source_loc(), "member access",
-                                      result.interface));
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Witness*> witness,
-                                  impl_scope.Resolve(iface_constraint, type,
-                                                     e->source_loc(), *this));
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Witness*> impl,
-                                  impl_scope.Resolve(result.interface, type,
-                                                     e->source_loc(), *this));
-          access.set_member(arena_->New<NamedElement>(result.member));
-          access.set_impl(impl);
-          access.set_found_in_interface(result.interface);
-
-          if (IsInstanceMember(&access.member())) {
-            // This is a member name denoting an instance member.
-            // TODO: Consider setting the static type of all instance member
-            // declarations to be member name types, rather than special-casing
-            // member accesses that name them.
-            access.set_static_type(
-                arena_->New<TypeOfMemberName>(&access.member()));
-            access.set_expression_category(ExpressionCategory::Value);
-          } else {
-            // This is a non-instance member whose value is found directly via
-            // the witness table, such as a non-method function or an
-            // associated constant.
-            const Value& member_type = result.member->static_type();
-            Bindings bindings = result.interface->bindings();
-            bindings.Add(result.interface->declaration().self(), type, witness);
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_member_type,
-                                    Substitute(bindings, &member_type));
-            access.set_static_type(inst_member_type);
-            access.set_expression_category(ExpressionCategory::Value);
-          }
-          return Success();
-        }
-        case Value::Kind::TypeType: {
-          // This is member access into an unconstrained type. Evaluate it and
-          // perform lookup in the result.
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                                  InterpExp(&access.object()));
-          CARBON_RETURN_IF_ERROR(
-              ExpectCompleteType(access.source_loc(), "member access", type));
-          switch (type->kind()) {
-            case Value::Kind::StructType: {
-              for (const auto& field : cast<StructType>(type)->fields()) {
-                if (access.member_name() == field.name) {
-                  access.set_member(arena_->New<NamedElement>(&field));
-                  access.set_static_type(
-                      arena_->New<TypeOfMemberName>(&access.member()));
-                  access.set_expression_category(ExpressionCategory::Value);
-                  return Success();
-                }
-              }
-              return ProgramError(access.source_loc())
-                     << "struct " << *type << " does not have a field named "
-                     << " does not have a field named " << access.member_name();
-            }
-            case Value::Kind::ChoiceType: {
-              const auto& choice = cast<ChoiceType>(*type);
-              std::optional<Nonnull<const AlternativeSignature*>> signature =
-                  choice.declaration().FindAlternative(access.member_name());
-              if (!signature.has_value()) {
-                return ProgramError(e->source_loc())
-                       << choice << " does not have an alternative named "
-                       << access.member_name();
-              }
-
-              // If we find an alternative with no declared signature, we are
-              // constructing an unparameterized alternative value.
-              if (!(*signature)->parameters_static_type()) {
-                access.set_member(
-                    arena_->New<NamedElement>(arena_->New<NamedValue>(
-                        NamedValue{access.member_name(), &choice})));
-                access.set_static_type(&choice);
-                access.set_expression_category(ExpressionCategory::Value);
-                return Success();
-              }
-
-              CARBON_ASSIGN_OR_RETURN(
-                  Nonnull<const Value*> parameter_type,
-                  Substitute(choice.bindings(),
-                             *(*signature)->parameters_static_type()));
-              Nonnull<const Value*> type = arena_->New<FunctionType>(
-                  std::nullopt, parameter_type, &choice);
-              // TODO: Should there be a Declaration corresponding to each
-              // choice type alternative?
-              access.set_member(
-                  arena_->New<NamedElement>(arena_->New<NamedValue>(
-                      NamedValue{access.member_name(), type})));
-              access.set_static_type(type);
-              access.set_expression_category(ExpressionCategory::Value);
-              return Success();
-            }
-            case Value::Kind::NominalClassType: {
-              const auto& class_type = cast<NominalClassType>(*type);
-              CARBON_ASSIGN_OR_RETURN(
-                  auto type_member,
-                  FindMixedMemberAndType(access.member_name(),
-                                         class_type.declaration().members(),
-                                         &class_type));
-              if (type_member.has_value()) {
-                auto [member_type, member] = type_member.value();
-                access.set_member(arena_->New<NamedElement>(member));
-                switch (member->kind()) {
-                  case DeclarationKind::FunctionDeclaration: {
-                    const auto& func = cast<FunctionDeclaration>(*member);
-                    if (func.is_method()) {
-                      break;
-                    }
-                    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> field_type,
-                                            Substitute(class_type.bindings(),
-                                                       &member->static_type()));
-                    access.set_static_type(field_type);
-                    access.set_expression_category(ExpressionCategory::Value);
-                    return Success();
-                  }
-                  default:
-                    break;
-                }
-                access.set_static_type(
-                    arena_->New<TypeOfMemberName>(&access.member()));
-                access.set_expression_category(ExpressionCategory::Value);
-                return Success();
-              } else {
-                return ProgramError(access.source_loc())
-                       << class_type << " does not have a member named "
-                       << access.member_name();
-              }
-            }
-            case Value::Kind::InterfaceType:
-            case Value::Kind::NamedConstraintType:
-            case Value::Kind::ConstraintType: {
-              CARBON_ASSIGN_OR_RETURN(
-                  ConstraintLookupResult result,
-                  LookupInConstraint(e->source_loc(), "member access", type,
-                                     access.member_name()));
-              access.set_member(arena_->New<NamedElement>(result.member));
-              access.set_found_in_interface(result.interface);
-              access.set_static_type(
-                  arena_->New<TypeOfMemberName>(&access.member()));
-              access.set_expression_category(ExpressionCategory::Value);
-              return Success();
-            }
-            default:
-              // TODO: We should handle VariableType and AssociatedConstant
-              // here.
-              return ProgramError(access.source_loc())
-                     << "unsupported member access into type " << *type;
-          }
-        }
-        default:
-          return ProgramError(e->source_loc())
-                 << "member access, unexpected " << object_type << " in " << *e;
-      }
-    }
-    case ExpressionKind::CompoundMemberAccessExpression: {
-      auto& access = cast<CompoundMemberAccessExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&access.object(), impl_scope));
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&access.path(), impl_scope));
-      if (!isa<TypeOfMemberName>(access.path().static_type())) {
-        return ProgramError(e->source_loc())
-               << "expected name of instance member or interface member in "
-                  "compound member access, found "
-               << access.path().static_type();
-      }
-
-      // Evaluate the member name expression to determine which member we're
-      // accessing.
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> member_name_value,
-                              InterpExp(&access.path()));
-      const auto& member_name = cast<MemberName>(*member_name_value);
-      access.set_member(&member_name);
-      bool is_instance_member = IsInstanceMember(&member_name.member());
-
-      bool has_instance = true;
-      std::optional<Nonnull<const Value*>> base_type = member_name.base_type();
-      if (!base_type.has_value()) {
-        if (IsTypeOfType(&access.object().static_type())) {
-          // This is `Type.(member_name)`, where `member_name` doesn't specify
-          // a type. This access doesn't perform instance binding.
-          CARBON_ASSIGN_OR_RETURN(base_type, InterpExp(&access.object()));
-          has_instance = false;
-        } else {
-          // This is `value.(member_name)`, where `member_name` doesn't specify
-          // a type. The member will be found in the type of `value`, or in a
-          // corresponding `impl` if `member_name` is an interface member.
-          base_type = &access.object().static_type();
-        }
-      } else {
-        // This is `value.(member_name)`, where `member_name` specifies a type.
-        // `value` is implicitly converted to that type.
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_object,
-            ImplicitlyConvert("compound member access", impl_scope,
-                              &access.object(), *base_type));
-        access.set_object(converted_object);
-      }
-      access.set_is_type_access(has_instance && !is_instance_member);
-
-      // Perform associated constant rewriting and impl selection if necessary.
-      std::optional<Nonnull<const Witness*>> witness;
-      if (std::optional<Nonnull<const InterfaceType*>> iface =
-              member_name.interface()) {
-        // If we're naming an associated constant, we might have a rewrite for
-        // it that we can apply immediately.
-        CARBON_ASSIGN_OR_RETURN(
-            auto replacement,
-            LookupRewriteInTypeOf(*base_type, *iface,
-                                  *member_name.member().declaration()));
-        if (replacement) {
-          RewriteMemberAccess(&access, *replacement);
-          return Success();
-        }
-
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const ConstraintType*> iface_constraint,
-            ConvertToConstraintType(access.source_loc(),
-                                    "compound member access", *iface));
-        // TODO: We should check that the base type implements the specified
-        // interface, not only the interface containing the member.
-        // `x.(ImplicitAs(T).Convert)()` should require that the type of `x`
-        // implements `ImplicitAs(T)`, not only `As(T)`.
-        CARBON_ASSIGN_OR_RETURN(witness,
-                                impl_scope.Resolve(iface_constraint, *base_type,
-                                                   e->source_loc(), *this));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Witness*> impl,
-            impl_scope.Resolve(*iface, *base_type, e->source_loc(), *this));
-        CARBON_ASSIGN_OR_RETURN(
-            replacement,
-            LookupRewriteInWitness(impl, *iface,
-                                   *member_name.member().declaration()));
-        if (replacement) {
-          RewriteMemberAccess(&access, *replacement);
-          return Success();
-        }
-        access.set_impl(impl);
-      }
-
-      auto bindings_for_member = [&]() -> Bindings {
-        if (member_name.interface()) {
-          Nonnull<const InterfaceType*> iface_type = *member_name.interface();
-          Bindings bindings = iface_type->bindings();
-          bindings.Add(iface_type->declaration().self(), *base_type, witness);
-          return bindings;
-        }
-        if (const auto* class_type =
-                dyn_cast<NominalClassType>(base_type.value())) {
-          return class_type->bindings();
-        }
-        return Bindings();
-      };
-
-      auto set_static_type_as_member_type = [&]() -> ErrorOr<Success> {
-        Nonnull<const Value*> member_type = &member_name.member().type();
-        CARBON_ASSIGN_OR_RETURN(member_type,
-                                Substitute(bindings_for_member(), member_type));
-        access.set_static_type(member_type);
-        return Success();
-      };
-
-      auto set_static_type_remove_self = [&]() -> ErrorOr<Success> {
-        Nonnull<const Value*> member_type = &member_name.member().type();
-        CARBON_ASSIGN_OR_RETURN(member_type,
-                                Substitute(bindings_for_member(), member_type));
-        auto* function_type = cast<FunctionType>(member_type);
-        access.set_static_type(arena_->New<FunctionType>(
-            FunctionType::ExceptSelf{}, function_type));
-        return Success();
-      };
-
-      switch (std::optional<Nonnull<const Declaration*>> decl =
-                  member_name.member().declaration();
-              decl ? decl.value()->kind()
-                   : DeclarationKind::VariableDeclaration) {
-        case DeclarationKind::VariableDeclaration:
-          if (has_instance) {
-            CARBON_RETURN_IF_ERROR(set_static_type_as_member_type());
-            access.set_expression_category(
-                access.object().expression_category());
-            return Success();
-          }
-          break;
-        case DeclarationKind::FunctionDeclaration: {
-          if (has_instance || !is_instance_member) {
-            // This should not be possible: the name of a static member
-            // function should have function type not member name type.
-            CARBON_CHECK(!has_instance || is_instance_member ||
-                             !member_name.base_type().has_value(),
-                         "vacuous compound member access");
-            // If this is instance access, remove self bound from function type
-            if (has_instance && is_instance_member) {
-              CARBON_RETURN_IF_ERROR(set_static_type_remove_self());
-            } else {
-              CARBON_RETURN_IF_ERROR(set_static_type_as_member_type());
-            }
-            access.set_expression_category(ExpressionCategory::Value);
-            CARBON_RETURN_IF_ERROR(
-                CheckAddrSelfAccess(&access, cast<FunctionDeclaration>(*decl),
-                                    bindings_for_member(), impl_scope));
-            return Success();
-          }
-          break;
-        }
-        case DeclarationKind::AssociatedConstantDeclaration:
-          CARBON_RETURN_IF_ERROR(set_static_type_as_member_type());
-          access.set_expression_category(access.object().expression_category());
-          return Success();
-        default:
-          CARBON_FATAL("member {0} is not a field or method", member_name);
-          break;
-      }
-
-      access.set_static_type(
-          arena_->New<TypeOfMemberName>(&access.member().member()));
-      access.set_expression_category(ExpressionCategory::Value);
-      return Success();
-    }
-    case ExpressionKind::IdentifierExpression: {
-      auto& ident = cast<IdentifierExpression>(*e);
-      if (ident.value_node().base().kind() ==
-          AstNodeKind::FunctionDeclaration) {
-        const auto& function =
-            cast<FunctionDeclaration>(ident.value_node().base());
-        if (!function.has_static_type()) {
-          CARBON_CHECK(function.return_term().is_auto());
-          return ProgramError(ident.source_loc())
-                 << "Function calls itself, but has a deduced return type";
-        }
-      }
-      ident.set_static_type(&ident.value_node().static_type());
-      ident.set_expression_category(ident.value_node().expression_category());
-      return Success();
-    }
-    case ExpressionKind::DotSelfExpression: {
-      auto& dot_self = cast<DotSelfExpression>(*e);
-      if (dot_self.self_binding().is_type_checked()) {
-        dot_self.set_static_type(&dot_self.self_binding().static_type());
-      } else {
-        dot_self.set_static_type(arena_->New<TypeType>());
-        dot_self.self_binding().set_named_as_type_via_dot_self();
-      }
-      dot_self.set_expression_category(ExpressionCategory::Value);
-      return Success();
-    }
-    case ExpressionKind::IntLiteral:
-      e->set_expression_category(ExpressionCategory::Value);
-      e->set_static_type(arena_->New<IntType>());
-      return Success();
-    case ExpressionKind::BoolLiteral:
-      e->set_expression_category(ExpressionCategory::Value);
-      e->set_static_type(arena_->New<BoolType>());
-      return Success();
-    case ExpressionKind::OperatorExpression: {
-      auto& op = cast<OperatorExpression>(*e);
-      std::vector<Nonnull<const Value*>> ts;
-      for (Nonnull<Expression*> argument : op.arguments()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(argument, impl_scope));
-        ts.push_back(&argument->static_type());
-      }
-
-      auto handle_unary_operator = [&](Builtin builtin) -> ErrorOr<Success> {
-        ErrorOr<Nonnull<Expression*>> result = BuildBuiltinMethodCall(
-            impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin},
-            BuiltinMethodCall{"Op"});
-        if (!result.ok()) {
-          // We couldn't find a matching `impl`.
-          return ProgramError(e->source_loc())
-                 << "type error in `" << OperatorToString(op.op()) << "`:\n"
-                 << result.error().message();
-        }
-        op.set_rewritten_form(*result);
-        return Success();
-      };
-
-      auto handle_binary_operator = [&](Builtin builtin) -> ErrorOr<Success> {
-        ErrorOr<Nonnull<Expression*>> result = BuildBuiltinMethodCall(
-            impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin, ts[1]},
-            BuiltinMethodCall{"Op", {op.arguments()[1]}});
-        if (!result.ok()) {
-          // We couldn't find a matching `impl`.
-          return ProgramError(e->source_loc())
-                 << "type error in `" << OperatorToString(op.op()) << "`:\n"
-                 << result.error().message();
-        }
-        op.set_rewritten_form(*result);
-        return Success();
-      };
-
-      auto handle_binary_arithmetic = [&](Builtin builtin) -> ErrorOr<Success> {
-        // Handle a built-in operator first.
-        // TODO: Replace this with an intrinsic.
-        if (isa<IntType>(ts[0]) && isa<IntType>(ts[1]) &&
-            IsSameType(ts[0], ts[1], impl_scope)) {
-          op.set_static_type(ts[0]);
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-
-        // Now try an overloaded operator.
-        return handle_binary_operator(builtin);
-      };
-
-      auto handle_compare =
-          [&](Builtin builtin, const std::string& method_name,
-              const std::string_view& operator_desc) -> ErrorOr<Success> {
-        ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
-            impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin, ts[1]},
-            BuiltinMethodCall{method_name, op.arguments()[1]});
-        if (!converted.ok()) {
-          // We couldn't find a matching `impl`.
-          return ProgramError(e->source_loc())
-                 << *ts[0] << " is not " << operator_desc << " comparable with "
-                 << *ts[1] << " (" << converted.error().message() << ")";
-        }
-        op.set_rewritten_form(*converted);
-        return Success();
-      };
-
-      switch (op.op()) {
-        case Operator::Neg: {
-          // Handle a built-in negation first.
-          // TODO: Replace this with an intrinsic.
-          if (isa<IntType>(ts[0])) {
-            op.set_static_type(arena_->New<IntType>());
-            op.set_expression_category(ExpressionCategory::Value);
-            return Success();
-          }
-          // Now try an overloaded negation.
-          return handle_unary_operator(Builtin::Negate);
-        }
-        case Operator::Add:
-          return handle_binary_arithmetic(Builtin::AddWith);
-        case Operator::Sub:
-          return handle_binary_arithmetic(Builtin::SubWith);
-        case Operator::Mul:
-          return handle_binary_arithmetic(Builtin::MulWith);
-        case Operator::Div:
-          return handle_binary_arithmetic(Builtin::DivWith);
-        case Operator::Mod:
-          return handle_binary_arithmetic(Builtin::ModWith);
-        case Operator::BitwiseAnd:
-          // `&` between type-of-types performs constraint combination.
-          // TODO: Should this be done via an intrinsic?
-          if (IsTypeOfType(ts[0]) && IsTypeOfType(ts[1])) {
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> lhs,
-                                    InterpExp(op.arguments()[0]));
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> rhs,
-                                    InterpExp(op.arguments()[1]));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const ConstraintType*> lhs_constraint,
-                ConvertToConstraintType(op.arguments()[0]->source_loc(),
-                                        "first operand of `&`", lhs));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const ConstraintType*> rhs_constraint,
-                ConvertToConstraintType(op.arguments()[1]->source_loc(),
-                                        "second operand of `&`", rhs));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const ConstraintType*> result,
-                CombineConstraints(e->source_loc(),
-                                   {lhs_constraint, rhs_constraint}));
-            op.set_rewritten_form(arena_->New<ValueLiteral>(
-                op.source_loc(), result, arena_->New<TypeType>(),
-                ExpressionCategory::Value));
-            return Success();
-          }
-          return handle_binary_operator(Builtin::BitAndWith);
-        case Operator::BitwiseOr:
-          return handle_binary_operator(Builtin::BitOrWith);
-        case Operator::BitwiseXor:
-          return handle_binary_operator(Builtin::BitXorWith);
-        case Operator::BitShiftLeft:
-          return handle_binary_operator(Builtin::LeftShiftWith);
-        case Operator::BitShiftRight:
-          return handle_binary_operator(Builtin::RightShiftWith);
-        case Operator::Complement:
-          return handle_unary_operator(Builtin::BitComplement);
-        case Operator::And:
-          CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "&&(1)",
-                                                 arena_->New<BoolType>(), ts[0],
-                                                 impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "&&(2)",
-                                                 arena_->New<BoolType>(), ts[1],
-                                                 impl_scope));
-          op.set_static_type(arena_->New<BoolType>());
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case Operator::Or:
-          CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "||(1)",
-                                                 arena_->New<BoolType>(), ts[0],
-                                                 impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "||(2)",
-                                                 arena_->New<BoolType>(), ts[1],
-                                                 impl_scope));
-          op.set_static_type(arena_->New<BoolType>());
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case Operator::Not:
-          CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "!",
-                                                 arena_->New<BoolType>(), ts[0],
-                                                 impl_scope));
-          op.set_static_type(arena_->New<BoolType>());
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case Operator::Eq:
-          return handle_compare(Builtin::EqWith, "Equal", "equality");
-        case Operator::NotEq:
-          return handle_compare(Builtin::EqWith, "NotEqual", "equality");
-        case Operator::Less:
-          return handle_compare(Builtin::LessWith, "Less", "less");
-        case Operator::LessEq:
-          return handle_compare(Builtin::LessEqWith, "LessEq", "less equal");
-        case Operator::GreaterEq:
-          return handle_compare(Builtin::GreaterEqWith, "GreaterEq",
-                                "greater equal");
-        case Operator::Greater:
-          return handle_compare(Builtin::GreaterWith, "Greater", "greater");
-        case Operator::Deref:
-          CARBON_RETURN_IF_ERROR(
-              ExpectPointerType(e->source_loc(), "*", ts[0]));
-          op.set_static_type(&cast<PointerType>(*ts[0]).pointee_type());
-          op.set_expression_category(ExpressionCategory::Reference);
-          return Success();
-        case Operator::Ptr: {
-          auto* type_type = arena_->New<TypeType>();
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<Expression*> converted,
-              ImplicitlyConvert("pointee type", impl_scope, op.arguments()[0],
-                                type_type));
-          op.arguments()[0] = converted;
-          op.set_static_type(arena_->New<TypeType>());
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case Operator::AddressOf:
-          if (op.arguments()[0]->expression_category() !=
-              ExpressionCategory::Reference) {
-            return ProgramError(op.arguments()[0]->source_loc())
-                   << "Argument to " << OperatorToString(op.op())
-                   << " should be a reference expression.";
-          }
-          op.set_static_type(arena_->New<PointerType>(ts[0]));
-          op.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case Operator::As: {
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> type,
-              TypeCheckTypeExp(op.arguments()[1], impl_scope));
-          ErrorOr<Nonnull<Expression*>> converted =
-              BuildBuiltinMethodCall(impl_scope, op.arguments()[0],
-                                     BuiltinInterfaceName{Builtin::As, type},
-                                     BuiltinMethodCall{"Convert"});
-          if (!converted.ok()) {
-            // We couldn't find a matching `impl`.
-            return ProgramError(e->source_loc())
-                   << "type error in `as`: `" << *ts[0]
-                   << "` is not explicitly convertible to `" << *type << "`:\n"
-                   << converted.error().message();
-          }
-          op.set_rewritten_form(*converted);
-          return Success();
-        }
-      }
-      break;
-    }
-    case ExpressionKind::CallExpression: {
-      auto& call = cast<CallExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&call.function(), impl_scope));
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&call.argument(), impl_scope));
-      switch (call.function().static_type().kind()) {
-        case Value::Kind::FunctionType: {
-          const auto& fun_t = cast<FunctionType>(call.function().static_type());
-          if (trace_stream_->is_enabled()) {
-            trace_stream_->Start()
-                << "checking call to function of type `" << fun_t
-                << "` with arguments of type `" << call.argument().static_type()
-                << "` (" << call.source_loc() << ")\n";
-          }
-          CARBON_RETURN_IF_ERROR(DeduceCallBindings(
-              call, &fun_t.parameters(), fun_t.generic_parameters(),
-              fun_t.deduced_bindings(), impl_scope));
-
-          // Substitute into the return type to determine the type of the call
-          // expression.
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> return_type,
-              Substitute(call.bindings(), &fun_t.return_type()));
-          call.set_static_type(return_type);
-          call.set_expression_category(fun_t.is_initializing()
-                                           ? ExpressionCategory::Initializing
-                                           : ExpressionCategory::Value);
-          return Success();
-        }
-        case Value::Kind::TypeOfParameterizedEntityName: {
-          // This case handles the application of a parameterized class or
-          // interface to a set of arguments, such as Point(i32) or
-          // AddWith(i32).
-          const ParameterizedEntityName& param_name =
-              cast<TypeOfParameterizedEntityName>(call.function().static_type())
-                  .name();
-
-          // Collect the top-level generic parameters and their constraints.
-          std::vector<FunctionType::GenericParameter> generic_parameters;
-          for (const auto [i, param] :
-               llvm::enumerate(param_name.params().fields())) {
-            // TODO: Should we disallow all other kinds of top-level params?
-            if (const auto* binding = dyn_cast<GenericBinding>(param)) {
-              generic_parameters.push_back({{}, i, binding});
-            }
-          }
-
-          CARBON_RETURN_IF_ERROR(DeduceCallBindings(
-              call, &param_name.params().static_type(), generic_parameters,
-              /*deduced_bindings=*/std::nullopt, impl_scope));
-
-          // Currently the only kinds of parameterized entities we support are
-          // types.
-          CARBON_CHECK((isa<ClassDeclaration, InterfaceDeclaration,
-                            ConstraintDeclaration, ChoiceDeclaration>(
-                           param_name.declaration())),
-                       "unknown type of ParameterizedEntityName for {0}",
-                       param_name);
-          call.set_static_type(arena_->New<TypeType>());
-          call.set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case Value::Kind::ChoiceType: {
-          // Give a better diagnostic for an attempt to call a choice constant.
-          auto* member_access =
-              dyn_cast<SimpleMemberAccessExpression>(&call.function());
-          if (member_access &&
-              isa<TypeType>(member_access->object().static_type())) {
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                                    InterpExp(&member_access->object()));
-            if (isa<ChoiceType>(type)) {
-              return ProgramError(e->source_loc())
-                     << "alternative `" << *type << "."
-                     << member_access->member_name()
-                     << "` does not expect an argument list";
-            }
-          }
-          [[fallthrough]];
-        }
-        default: {
-          return ProgramError(e->source_loc())
-                 << "in call `" << *e
-                 << "`, expected callee to be a function, found `"
-                 << call.function().static_type() << "`";
-        }
-      }
-      break;
-    }
-    case ExpressionKind::FunctionTypeLiteral: {
-      auto& fn = cast<FunctionTypeLiteral>(*e);
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> param,
-                              TypeCheckTypeExp(&fn.parameter(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> ret,
-                              TypeCheckTypeExp(&fn.return_type(), impl_scope));
-      fn.set_static_type(arena_->New<TypeType>());
-      fn.set_expression_category(ExpressionCategory::Value);
-      fn.set_constant_value(
-          arena_->New<FunctionType>(std::nullopt, param, ret));
-      return Success();
-    }
-    case ExpressionKind::StringLiteral:
-      e->set_static_type(arena_->New<StringType>());
-      e->set_expression_category(ExpressionCategory::Value);
-      return Success();
-    case ExpressionKind::IntrinsicExpression: {
-      auto& intrinsic_exp = cast<IntrinsicExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&intrinsic_exp.args(), impl_scope));
-      const auto& args = intrinsic_exp.args().fields();
-      switch (cast<IntrinsicExpression>(*e).intrinsic()) {
-        case IntrinsicExpression::Intrinsic::Print:
-          // TODO: Remove Print special casing once we have variadics or
-          // overloads. Here, that's the name Print instead of __intrinsic_print
-          // in errors.
-          if (args.empty() || args.size() > 2) {
-            return ProgramError(e->source_loc())
-                   << "Print takes 1 or 2 arguments, received " << args.size();
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "Print argument 0", arena_->New<StringType>(),
-              &args[0]->static_type(), impl_scope));
-          if (args.size() >= 2) {
-            CARBON_RETURN_IF_ERROR(ExpectExactType(
-                e->source_loc(), "Print argument 1", arena_->New<IntType>(),
-                &args[1]->static_type(), impl_scope));
-          }
-          e->set_static_type(TupleType::Empty());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case IntrinsicExpression::Intrinsic::Assert: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_assert takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_assert argument 0",
-              arena_->New<BoolType>(), &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_assert argument 1",
-              arena_->New<StringType>(), &args[1]->static_type(), impl_scope));
-          e->set_static_type(TupleType::Empty());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::Alloc: {
-          if (args.size() != 1) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_new takes 1 argument";
-          }
-          const auto* arg_type = &args[0]->static_type();
-          e->set_static_type(arena_->New<PointerType>(arg_type));
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::Dealloc: {
-          if (args.size() != 1) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_delete takes 1 argument";
-          }
-          const auto* arg_type = &args[0]->static_type();
-          CARBON_RETURN_IF_ERROR(
-              ExpectPointerType(e->source_loc(), "*", arg_type));
-          e->set_static_type(TupleType::Empty());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::PrintAllocs: {
-          if (!args.empty()) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_print_allocs takes no arguments";
-          }
-          e->set_static_type(TupleType::Empty());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::Rand: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "Rand takes 2 arguments, received " << args.size();
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "Rand argument 0", arena_->New<IntType>(),
-              &args[0]->static_type(), impl_scope));
-
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "Rand argument 1", arena_->New<IntType>(),
-              &args[1]->static_type(), impl_scope));
-
-          e->set_static_type(arena_->New<IntType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::ImplicitAs: {
-          if (args.size() != 1) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_implicit_as takes 1 argument";
-          }
-          CARBON_RETURN_IF_ERROR(TypeCheckTypeExp(args[0], impl_scope));
-          e->set_static_type(arena_->New<TypeType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::ImplicitAsConvert: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_implicit_as_convert takes 2 arguments";
-          }
-          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> result,
-                                  TypeCheckTypeExp(args[1], impl_scope));
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<Expression*> converted,
-              BuildBuiltinConversion(args[0], result, impl_scope));
-          cast<IntrinsicExpression>(e)->set_rewritten_form(converted);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::IntEq: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_int_eq takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_int_eq argument 1",
-              arena_->New<IntType>(), &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_int_eq argument 2",
-              arena_->New<IntType>(), &args[1]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<BoolType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::IntCompare: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_int_compare takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_int_compare argument 1",
-              arena_->New<IntType>(), &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_int_compare argument 2",
-              arena_->New<IntType>(), &args[1]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<IntType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::StrEq: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_str_eq takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_str_eq argument 1",
-              arena_->New<StringType>(), &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_str_eq argument 2",
-              arena_->New<StringType>(), &args[1]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<BoolType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::StrCompare: {
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << "__intrinsic_str_compare takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_str_compare argument 1",
-              arena_->New<StringType>(), &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_str_compare argument 2",
-              arena_->New<StringType>(), &args[1]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<IntType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        }
-        case IntrinsicExpression::Intrinsic::IntBitComplement:
-          if (args.size() != 1) {
-            return ProgramError(e->source_loc())
-                   << intrinsic_exp.name() << " takes 1 argument";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "complement argument", arena_->New<IntType>(),
-              &args[0]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<IntType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-        case IntrinsicExpression::Intrinsic::IntBitAnd:
-        case IntrinsicExpression::Intrinsic::IntBitOr:
-        case IntrinsicExpression::Intrinsic::IntBitXor:
-        case IntrinsicExpression::Intrinsic::IntLeftShift:
-        case IntrinsicExpression::Intrinsic::IntRightShift:
-          if (args.size() != 2) {
-            return ProgramError(e->source_loc())
-                   << intrinsic_exp.name() << " takes 2 arguments";
-          }
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "argument 1", arena_->New<IntType>(),
-              &args[0]->static_type(), impl_scope));
-          CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "argument 2", arena_->New<IntType>(),
-              &args[1]->static_type(), impl_scope));
-          e->set_static_type(arena_->New<IntType>());
-          e->set_expression_category(ExpressionCategory::Value);
-          return Success();
-      }
-    }
-    case ExpressionKind::IntTypeLiteral:
-    case ExpressionKind::BoolTypeLiteral:
-    case ExpressionKind::StringTypeLiteral:
-    case ExpressionKind::TypeTypeLiteral:
-      e->set_expression_category(ExpressionCategory::Value);
-      e->set_static_type(arena_->New<TypeType>());
-      return Success();
-    case ExpressionKind::IfExpression: {
-      auto& if_expr = cast<IfExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&if_expr.condition(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<Expression*> converted_condition,
-          ImplicitlyConvert("condition of `if`", impl_scope,
-                            &if_expr.condition(), arena_->New<BoolType>()));
-      if_expr.set_condition(converted_condition);
-
-      // TODO: Compute the common type and convert both operands to it.
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&if_expr.then_expression(), impl_scope));
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&if_expr.else_expression(), impl_scope));
-      CARBON_RETURN_IF_ERROR(ExpectExactType(
-          e->source_loc(), "expression of `if` expression",
-          &if_expr.then_expression().static_type(),
-          &if_expr.else_expression().static_type(), impl_scope));
-      e->set_static_type(&if_expr.then_expression().static_type());
-      e->set_expression_category(ExpressionCategory::Value);
-      return Success();
-    }
-    case ExpressionKind::WhereExpression: {
-      auto& where = cast<WhereExpression>(*e);
-      ImplScope inner_impl_scope(&impl_scope);
-
-      auto& self = where.self_binding();
-
-      // If there's some enclosing `.Self` value, our self is symbolically
-      // equal to that. Otherwise it's a new type variable.
-      if (auto enclosing_dot_self = where.enclosing_dot_self()) {
-        // TODO: We need to also enforce that our `.Self` does end up being the
-        // same as the enclosing type.
-        self.set_symbolic_identity(*(*enclosing_dot_self)->symbolic_identity());
-        self.set_value(&(*enclosing_dot_self)->value());
-      } else {
-        ConstraintTypeBuilder::PrepareSelfBinding(arena_, &self);
-      }
-
-      ConstraintTypeBuilder builder(arena_, &self);
-      ConstraintTypeBuilder::ConstraintsInScopeTracker constraint_tracker;
-
-      // Keep track of the builder so that we can look up its rewrites while
-      // processing later constraints.
-      partial_constraint_types_.push_back(&builder);
-      auto pop_partial_constraint_type =
-          llvm::make_scope_exit([&] { partial_constraint_types_.pop_back(); });
-
-      // Note, we don't want to call `TypeCheckPattern` here. Most of the setup
-      // for the self binding is instead done by the `ConstraintTypeBuilder`.
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base_type,
-                              TypeCheckTypeExp(&self.type(), impl_scope));
-      self.set_static_type(base_type);
-
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const ConstraintType*> base,
-          ConvertToConstraintType(where.source_loc(),
-                                  "first operand of `where` expression",
-                                  base_type));
-
-      // Start with the given constraint.
-      CARBON_RETURN_IF_ERROR(
-          builder.AddAndSubstitute(*this, base, builder.GetSelfType(),
-                                   builder.GetSelfWitness(), Bindings(),
-                                   /*add_lookup_contexts=*/true));
-
-      // Type-check and apply the `where` clauses.
-      for (Nonnull<WhereClause*> clause : where.clauses()) {
-        // Constraints from the LHS of `where` are in scope in the RHS, and
-        // constraints from earlier `where` clauses are in scope in later
-        // clauses.
-        builder.BringConstraintsIntoScope(*this, &inner_impl_scope,
-                                          &constraint_tracker);
-
-        CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, inner_impl_scope));
-
-        switch (clause->kind()) {
-          case WhereClauseKind::ImplsWhereClause: {
-            auto& impls_clause = cast<ImplsWhereClause>(*clause);
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> type,
-                TypeCheckTypeExp(&impls_clause.type(), inner_impl_scope));
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> constraint,
-                                    InterpExp(&impls_clause.constraint()));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const ConstraintType*> constraint_type,
-                ConvertToConstraintType(impls_clause.source_loc(),
-                                        "expression after `impls`",
-                                        constraint));
-            // Transform `where .B impls (C where .D impls E)` into
-            // `where .B impls C and .B.D impls E` then add all the resulting
-            // constraints.
-            CARBON_RETURN_IF_ERROR(
-                builder.AddAndSubstitute(*this, constraint_type, type,
-                                         builder.GetSelfWitness(), Bindings(),
-                                         /*add_lookup_contexts=*/false));
-            break;
-          }
-          case WhereClauseKind::EqualsWhereClause: {
-            const auto& equals_clause = cast<EqualsWhereClause>(*clause);
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> lhs,
-                                    InterpExp(&equals_clause.lhs()));
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> rhs,
-                                    InterpExp(&equals_clause.rhs()));
-            if (!ValueEqual(lhs, rhs, std::nullopt)) {
-              builder.AddEqualityConstraint({.values = {lhs, rhs}});
-            }
-            break;
-          }
-          case WhereClauseKind::RewriteWhereClause: {
-            const auto& rewrite_clause = cast<RewriteWhereClause>(*clause);
-            CARBON_ASSIGN_OR_RETURN(
-                ConstraintLookupResult result,
-                LookupInConstraint(clause->source_loc(),
-                                   "rewrite constraint lookup", base_type,
-                                   rewrite_clause.member_name()));
-            const auto* constant =
-                dyn_cast<AssociatedConstantDeclaration>(result.member);
-            if (!constant) {
-              return ProgramError(clause->source_loc())
-                     << "in rewrite constraint lookup, `"
-                     << rewrite_clause.member_name()
-                     << "` does not name an associated constant";
-            }
-
-            // Find (or add) `.Self impls I`, and form a symbolic value naming
-            // the associated constant.
-            // TODO: Reject if the impls constraint didn't already exist.
-            int index = builder.AddImplsConstraint(
-                {.type = builder.GetSelfType(), .interface = result.interface});
-            const auto* witness =
-                MakeConstraintWitnessAccess(builder.GetSelfWitness(), index);
-            auto* constant_value = arena_->New<AssociatedConstant>(
-                builder.GetSelfType(), result.interface, constant, witness);
-
-            // Find the replacement value prior to conversion to the constant's
-            // type. This is the value we'll rewrite to when type-checking a
-            // member access.
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> replacement_value,
-                                    InterpExp(&rewrite_clause.replacement()));
-            Nonnull<const Value*> replacement_type =
-                &rewrite_clause.replacement().static_type();
-
-            auto* replacement_literal = arena_->New<ValueLiteral>(
-                rewrite_clause.source_loc(), replacement_value,
-                replacement_type, ExpressionCategory::Value);
-
-            // Convert the replacement value to the type of the associated
-            // constant and find the converted value. This is the value that
-            // we'll produce during evaluation and substitution.
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> constraint_type,
-                GetTypeForAssociatedConstant(constant_value));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<Expression*> converted_expression,
-                ImplicitlyConvert("rewrite constraint", inner_impl_scope,
-                                  replacement_literal, constraint_type));
-            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
-                                    InterpExp(converted_expression));
-
-            // Add the rewrite constraint.
-            builder.AddRewriteConstraint(
-                {.constant = constant_value,
-                 .unconverted_replacement = replacement_value,
-                 .unconverted_replacement_type = replacement_type,
-                 .converted_replacement = converted_value});
-            break;
-          }
-        }
-      }
-
-      where.set_rewritten_form(arena_->New<ValueLiteral>(
-          where.source_loc(), std::move(builder).Build(),
-          arena_->New<TypeType>(), ExpressionCategory::Value));
-      return Success();
-    }
-    case ExpressionKind::UnimplementedExpression:
-      CARBON_FATAL("Unimplemented: {0}", *e);
-    case ExpressionKind::ArrayTypeLiteral: {
-      auto& array_literal = cast<ArrayTypeLiteral>(*e);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> element_type,
-          TypeCheckTypeExp(&array_literal.element_type_expression(), impl_scope,
-                           false));
-      std::optional<size_t> array_size;
-      if (array_literal.has_size_expression()) {
-        CARBON_RETURN_IF_ERROR(
-            TypeCheckExp(&array_literal.size_expression(), impl_scope));
-        CARBON_RETURN_IF_ERROR(ExpectExactType(
-            array_literal.size_expression().source_loc(), "array size",
-            arena_->New<IntType>(),
-            &array_literal.size_expression().static_type(), impl_scope));
-        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> size_value,
-                                InterpExp(&array_literal.size_expression()));
-        if (cast<IntValue>(size_value)->value() < 0) {
-          return ProgramError(array_literal.size_expression().source_loc())
-                 << "Array size cannot be negative";
-        }
-        array_size = cast<IntValue>(size_value)->value();
-      }
-      array_literal.set_static_type(arena_->New<TypeType>());
-      array_literal.set_expression_category(ExpressionCategory::Value);
-      array_literal.set_constant_value(
-          arena_->New<StaticArrayType>(element_type, array_size));
-      return Success();
-    }
-  }
-}
-
-void TypeChecker::CollectAndNumberGenericBindingsInPattern(
-    Nonnull<Pattern*> p,
-    std::vector<Nonnull<const GenericBinding*>>& generic_bindings) {
-  VisitNestedPatterns(*p, [&](Pattern& pattern) {
-    if (auto* binding = dyn_cast<GenericBinding>(&pattern)) {
-      binding->set_index(generic_bindings.size());
-      generic_bindings.push_back(binding);
-    }
-    return true;
-  });
-}
-
-void TypeChecker::CollectImplBindingsInPattern(
-    Nonnull<const Pattern*> p,
-    std::vector<Nonnull<const ImplBinding*>>& impl_bindings) {
-  VisitNestedPatterns(*p, [&](const Pattern& pattern) {
-    if (const auto* binding = dyn_cast<GenericBinding>(&pattern)) {
-      if (binding->impl_binding().has_value()) {
-        impl_bindings.push_back(binding->impl_binding().value());
-      }
-    }
-    return true;
-  });
-}
-
-void TypeChecker::BringPatternImplBindingsIntoScope(Nonnull<const Pattern*> p,
-                                                    ImplScope& impl_scope) {
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings;
-  CollectImplBindingsInPattern(p, impl_bindings);
-  BringImplBindingsIntoScope(impl_bindings, impl_scope);
-}
-
-void TypeChecker::BringImplBindingsIntoScope(
-    llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-    ImplScope& impl_scope) {
-  for (Nonnull<const ImplBinding*> impl_binding : impl_bindings) {
-    BringImplBindingIntoScope(impl_binding, impl_scope);
-  }
-}
-
-void TypeChecker::BringImplBindingIntoScope(
-    Nonnull<const ImplBinding*> impl_binding, ImplScope& impl_scope) {
-  CARBON_CHECK(impl_binding->type_var()->symbolic_identity().has_value() &&
-               impl_binding->symbolic_identity().has_value());
-  impl_scope.Add(impl_binding->interface(),
-                 *impl_binding->type_var()->symbolic_identity(),
-                 cast<Witness>(*impl_binding->symbolic_identity()), *this);
-}
-
-auto TypeChecker::TypeCheckTypeExp(Nonnull<Expression*> type_expression,
-                                   const ImplScope& impl_scope, bool concrete)
-    -> ErrorOr<Nonnull<const Value*>> {
-  CARBON_RETURN_IF_ERROR(TypeCheckExp(type_expression, impl_scope));
-  CARBON_ASSIGN_OR_RETURN(
-      type_expression,
-      ImplicitlyConvert("type expression", impl_scope, type_expression,
-                        arena_->New<TypeType>()));
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                          InterpExp(type_expression));
-  CARBON_CHECK(IsType(type), "type expression did not produce a type, got {0}",
-               *type);
-  if (concrete) {
-    if (TypeIsDeduceable(type)) {
-      return ProgramError(type_expression->source_loc())
-             << "`auto` is not permitted in this context";
-    }
-    CARBON_CHECK(IsNonDeduceableType(type),
-                 "unknown kind of non-concrete type {0}", *type);
-  }
-  CARBON_CHECK(!IsPlaceholderType(type),
-               "should be no way to write a placeholder type");
-  return type;
-}
-
-auto TypeChecker::TypeCheckWhereClause(Nonnull<WhereClause*> clause,
-                                       const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  switch (clause->kind()) {
-    case WhereClauseKind::ImplsWhereClause: {
-      auto& impls_clause = cast<ImplsWhereClause>(*clause);
-      // TODO: `type` is checked in the caller, because its converted value is
-      // needed. Find a way to move that checking back here.
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&impls_clause.constraint(), impl_scope));
-      if (!isa<TypeType>(impls_clause.constraint().static_type())) {
-        return ProgramError(impls_clause.constraint().source_loc())
-               << "expression after `impls` does not resolve to a constraint, "
-               << "found " << impls_clause.constraint().static_type();
-      }
-      return Success();
-    }
-    case WhereClauseKind::EqualsWhereClause: {
-      auto& equals_clause = cast<EqualsWhereClause>(*clause);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&equals_clause.lhs(), impl_scope));
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&equals_clause.rhs(), impl_scope));
-
-      // TODO: It's not clear what level of type compatibility is required
-      // between the operands. For now we require a builtin no-op implicit
-      // conversion.
-      Nonnull<const Value*> lhs_type = &equals_clause.lhs().static_type();
-      Nonnull<const Value*> rhs_type = &equals_clause.rhs().static_type();
-      CARBON_ASSIGN_OR_RETURN(
-          bool lhs_converts_to_rhs,
-          IsImplicitlyConvertible(equals_clause.lhs().source_loc(), lhs_type,
-                                  rhs_type, impl_scope,
-                                  /*allow_user_defined_conversions=*/false));
-      CARBON_ASSIGN_OR_RETURN(
-          bool rhs_converts_to_lhs,
-          IsImplicitlyConvertible(equals_clause.rhs().source_loc(), rhs_type,
-                                  lhs_type, impl_scope,
-                                  /*allow_user_defined_conversions=*/false));
-      if (!lhs_converts_to_rhs && !rhs_converts_to_lhs) {
-        return ProgramError(clause->source_loc())
-               << "type mismatch between values in `where LHS == RHS`\n"
-               << "  LHS type: " << *lhs_type << "\n"
-               << "  RHS type: " << *rhs_type;
-      }
-      return Success();
-    }
-    case WhereClauseKind::RewriteWhereClause: {
-      auto& rewrite_clause = cast<RewriteWhereClause>(*clause);
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&rewrite_clause.replacement(), impl_scope));
-      return Success();
-    }
-  }
-}
-
-auto TypeChecker::TypeCheckPattern(
-    Nonnull<Pattern*> p, PatternRequirements requirements,
-    std::optional<Nonnull<const Value*>> expected, ImplScope& impl_scope,
-    ExpressionCategory enclosing_expression_category) -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking " << p->kind() << " `" << *p << "`";
-    if (expected) {
-      *trace_stream_ << ", expecting `" << **expected << "`";
-    }
-    *trace_stream_ << " (" << p->source_loc() << ")\n";
-  }
-  switch (p->kind()) {
-    case PatternKind::AutoPattern:
-    case PatternKind::ExpressionPattern:
-      if (requirements == PatternRequirements::Irrefutable) {
-        return ProgramError(p->source_loc())
-               << "An irrefutable pattern is required, but `" << *p
-               << "` is refutable.";
-      }
-      break;
-    case PatternKind::BindingPattern:
-    case PatternKind::GenericBinding:
-      if (requirements == PatternRequirements::BindingType) {
-        return ProgramError(p->source_loc())
-               << "Binding types cannot contain bindings, but `" << *p
-               << "` is a binding.";
-      }
-      break;
-    default:
-      break;
-  }
-  switch (p->kind()) {
-    case PatternKind::AutoPattern: {
-      p->set_static_type(arena_->New<TypeType>());
-      p->set_value(arena_->New<AutoType>());
-      return Success();
-    }
-    case PatternKind::BindingPattern: {
-      auto& binding = cast<BindingPattern>(*p);
-      CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-          &binding.type(), PatternRequirements::BindingType, expected,
-          impl_scope, enclosing_expression_category));
-      Nonnull<const Value*> type = &binding.type().value();
-      // Convert to a type.
-      // TODO: Convert the pattern before interpreting it rather than doing
-      // this as a separate step.
-      if (!isa<TypeType>(binding.type().static_type())) {
-        auto* literal = arena_->New<ValueLiteral>(binding.source_loc(), type,
-                                                  &binding.type().static_type(),
-                                                  ExpressionCategory::Value);
-        CARBON_ASSIGN_OR_RETURN(
-            auto* converted,
-            ImplicitlyConvert("type of name binding", impl_scope, literal,
-                              arena_->New<TypeType>()));
-        CARBON_ASSIGN_OR_RETURN(type, InterpExp(converted));
-      }
-      CARBON_CHECK(
-          IsType(type),
-          "conversion to type succeeded but didn't produce a type, got {0}",
-          *type);
-      if (expected) {
-        // TODO: Per proposal #2188, we should be performing conversions at
-        // this level rather than on the overall initializer.
-        if (TypeIsDeduceable(type)) {
-          BindingMap generic_args;
-          if (!PatternMatch(type, ExpressionResult::Value(*expected),
-                            binding.type().source_loc(), std::nullopt,
-                            generic_args, trace_stream_, this->arena_)) {
-            return ProgramError(binding.type().source_loc())
-                   << "type pattern '" << *type
-                   << "' does not match actual type '" << **expected << "'";
-          }
-
-          type = DeducePatternType(type, *expected, arena_);
-        }
-
-      } else {
-        CARBON_RETURN_IF_ERROR(ExpectResolvedBindingType(binding, type));
-      }
-
-      CARBON_CHECK(IsNonDeduceableType(type),
-                   "did not resolve {0} to concrete type, got {1}", binding,
-                   *type);
-      CARBON_CHECK(!IsPlaceholderType(type),
-                   "should be no way to write a placeholder type");
-      binding.set_static_type(type);
-      binding.set_value(binding.name() != AnonymousName
-                            ? arena_->New<BindingPlaceholderValue>(&binding)
-                            : arena_->New<BindingPlaceholderValue>());
-
-      if (!binding.has_expression_category()) {
-        binding.set_expression_category(enclosing_expression_category);
-      }
-      return Success();
-    }
-    case PatternKind::GenericBinding: {
-      auto& binding = cast<GenericBinding>(*p);
-      if (expected) {
-        return ProgramError(binding.type().source_loc())
-               << "generic binding may not occur in pattern with expected "
-                  "type "
-               << binding;
-      }
-
-      return TypeCheckGenericBinding(binding, "generic binding", impl_scope);
-    }
-    case PatternKind::TuplePattern: {
-      auto& tuple = cast<TuplePattern>(*p);
-      std::vector<Nonnull<const Value*>> field_types;
-      std::vector<Nonnull<const Value*>> field_patterns;
-      if (expected && (*expected)->kind() != Value::Kind::TupleType) {
-        return ProgramError(p->source_loc()) << "didn't expect a tuple";
-      }
-      if (expected && tuple.fields().size() !=
-                          cast<TupleType>(**expected).elements().size()) {
-        return ProgramError(tuple.source_loc()) << "tuples of different length";
-      }
-      for (const auto [i, field] : llvm::enumerate(tuple.fields())) {
-        std::optional<Nonnull<const Value*>> expected_field_type;
-        if (expected) {
-          expected_field_type = cast<TupleType>(**expected).elements()[i];
-        }
-        CARBON_RETURN_IF_ERROR(TypeCheckPattern(field, requirements,
-                                                expected_field_type, impl_scope,
-                                                enclosing_expression_category));
-        if (trace_stream_->is_enabled()) {
-          trace_stream_->Start()
-              << "finished checking tuple pattern field `" << *field << "` ("
-              << field->source_loc() << ")\n";
-        }
-        field_types.push_back(&field->static_type());
-        field_patterns.push_back(&field->value());
-      }
-      tuple.set_static_type(arena_->New<TupleType>(std::move(field_types)));
-      tuple.set_value(arena_->New<TupleValue>(std::move(field_patterns)));
-      return Success();
-    }
-    case PatternKind::AlternativePattern: {
-      auto& alternative = cast<AlternativePattern>(*p);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> type,
-          TypeCheckTypeExp(&alternative.choice_type(), impl_scope));
-      if (!isa<ChoiceType>(type)) {
-        return ProgramError(alternative.source_loc())
-               << "alternative pattern does not name a choice type.";
-      }
-      const auto& choice_type = cast<ChoiceType>(*type);
-      // TODO: Per proposal #2188, we should perform an implicit conversion on
-      // the scrutinee if a choice type is provided.
-      std::optional<Nonnull<const AlternativeSignature*>> signature =
-          choice_type.declaration().FindAlternative(
-              alternative.alternative_name());
-      if (!signature) {
-        return ProgramError(alternative.source_loc())
-               << "`" << alternative.alternative_name()
-               << "` is not an alternative of " << choice_type;
-      }
-      if (!(*signature)->parameters_static_type()) {
-        return ProgramError(alternative.source_loc())
-               << "alternative `" << choice_type << "."
-               << alternative.alternative_name()
-               << "` does not expect an argument list";
-      }
-
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> parameter_type,
-          Substitute(choice_type.bindings(),
-                     *(*signature)->parameters_static_type()));
-      CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-          &alternative.arguments(), requirements, parameter_type, impl_scope,
-          enclosing_expression_category));
-      alternative.set_static_type(&choice_type);
-      alternative.set_value(arena_->New<AlternativeValue>(
-          &choice_type, *signature,
-          cast<TupleValue>(&alternative.arguments().value())));
-      return Success();
-    }
-    case PatternKind::ExpressionPattern: {
-      auto& expression = cast<ExpressionPattern>(*p).expression();
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&expression, impl_scope));
-      p->set_static_type(&expression.static_type());
-      // TODO: Per proposal #2188, we should form an `==` comparison here.
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> expr_value,
-                              InterpExp(&expression));
-      p->set_value(expr_value);
-      return Success();
-    }
-    case PatternKind::VarPattern: {
-      auto& var_pattern = cast<VarPattern>(*p);
-
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckPattern(&var_pattern.pattern(), requirements, expected,
-                           impl_scope, var_pattern.expression_category()));
-      var_pattern.set_static_type(&var_pattern.pattern().static_type());
-      var_pattern.set_value(&var_pattern.pattern().value());
-      return Success();
-    }
-    case PatternKind::AddrPattern: {
-      std::optional<Nonnull<const Value*>> expected_ptr;
-      auto& addr_pattern = cast<AddrPattern>(*p);
-      if (expected) {
-        expected_ptr = arena_->New<PointerType>(expected.value());
-      }
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckPattern(&addr_pattern.binding(), requirements, expected_ptr,
-                           impl_scope, enclosing_expression_category));
-
-      if (const auto* inner_binding_type =
-              dyn_cast<PointerType>(&addr_pattern.binding().static_type())) {
-        addr_pattern.set_static_type(&inner_binding_type->pointee_type());
-      } else {
-        return ProgramError(addr_pattern.source_loc())
-               << "Type associated with addr must be a pointer type.";
-      }
-      addr_pattern.set_value(
-          arena_->New<AddrValue>(&addr_pattern.binding().value()));
-      return Success();
-    }
-  }
-}
-
-auto TypeChecker::TypeCheckGenericBinding(GenericBinding& binding,
-                                          std::string_view context,
-                                          ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  // The binding can be referred to in its own type via `.Self`, so set up
-  // its symbolic identity before we type-check and interpret the type.
-  auto* symbolic_value = arena_->New<VariableType>(&binding);
-  binding.set_symbolic_identity(symbolic_value);
-  binding.set_value(symbolic_value);
-
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                          TypeCheckTypeExp(&binding.type(), impl_scope));
-  if (binding.named_as_type_via_dot_self() && !IsTypeOfType(type)) {
-    return ProgramError(binding.type().source_loc())
-           << "`.Self` used in type of non-type " << context << " `"
-           << binding.name() << "`";
-  }
-
-  // Create an impl binding if we have a constraint.
-  if (IsTypeOfType(type) && !isa<TypeType>(type)) {
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const ConstraintType*> constraint,
-        ConvertToConstraintType(binding.source_loc(), context, type));
-    Nonnull<ImplBinding*> impl_binding =
-        arena_->New<ImplBinding>(binding.source_loc(), &binding, std::nullopt);
-    auto* witness = arena_->New<BindingWitness>(impl_binding);
-    impl_binding->set_symbolic_identity(witness);
-    binding.set_impl_binding(impl_binding);
-
-    // Substitute the VariableType as `.Self` of the constraint to form the
-    // resolved type of the binding. Eg, `T:! X where .Self impls Y` resolves
-    // to `T:! <constraint T impls X and T impls Y>`.
-    ConstraintTypeBuilder builder(arena_, &binding, impl_binding);
-    CARBON_RETURN_IF_ERROR(
-        builder.AddAndSubstitute(*this, constraint, symbolic_value, witness,
-                                 Bindings(), /*add_lookup_contexts=*/true));
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start() << "resolving constraint type for `" << binding
-                             << "` from `" << *constraint << "`\n";
-    }
-    CARBON_RETURN_IF_ERROR(
-        builder.Resolve(*this, binding.type().source_loc(), impl_scope));
-    type = std::move(builder).Build();
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->End() << "resolved constraint type is `" << *type << "`\n";
-    }
-
-    BringImplBindingIntoScope(impl_binding, impl_scope);
-  }
-
-  binding.set_static_type(type);
-  return Success();
-}
-
-// Get the builtin interface that should be used for the given kind of
-// assignment operator.
-static auto GetBuiltinInterfaceForAssignOperator(AssignOperator op) -> Builtin {
-  switch (op) {
-    case AssignOperator::Plain:
-      return Builtin::AssignWith;
-    case AssignOperator::Add:
-      return Builtin::AddAssignWith;
-    case AssignOperator::Sub:
-      return Builtin::SubAssignWith;
-    case AssignOperator::Mul:
-      return Builtin::MulAssignWith;
-    case AssignOperator::Div:
-      return Builtin::DivAssignWith;
-    case AssignOperator::Mod:
-      return Builtin::ModAssignWith;
-    case AssignOperator::And:
-      return Builtin::BitAndAssignWith;
-    case AssignOperator::Or:
-      return Builtin::BitOrAssignWith;
-    case AssignOperator::Xor:
-      return Builtin::BitXorAssignWith;
-    case AssignOperator::ShiftLeft:
-      return Builtin::LeftShiftAssignWith;
-    case AssignOperator::ShiftRight:
-      return Builtin::RightShiftAssignWith;
-  }
-}
-
-auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
-                                const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Source()
-        << "type checking stmt at (" << s->source_loc() << ")\n";
-    *trace_stream_ << "```\n" << *s << "\n```\n";
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking " << s->kind() << " `" << PrintAsID(*s)
-                           << "` (" << s->source_loc() << ")\n";
-  }
-  switch (s->kind()) {
-    case StatementKind::Match: {
-      auto& match = cast<Match>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&match.expression(), impl_scope));
-      CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
-          match.expression().source_loc(), &match.expression().static_type()));
-      std::vector<Match::Clause> new_clauses;
-      std::optional<Nonnull<const Value*>> expected_type;
-      PatternMatrix patterns;
-      for (auto& clause : match.clauses()) {
-        ImplScope clause_scope(&impl_scope);
-        // TODO: Should user-defined conversions be permitted in `match`
-        // statements? When would we run them? See #1283.
-        CARBON_RETURN_IF_ERROR(
-            TypeCheckPattern(&clause.pattern(), PatternRequirements::None,
-                             &match.expression().static_type(), clause_scope,
-                             ExpressionCategory::Value));
-        if (expected_type.has_value()) {
-          // TODO: For now, we require all patterns to have the same type. If
-          // that's not the same type as the scrutinee, we will convert the
-          // scrutinee. We might want to instead allow a different conversion
-          // to be performed for each pattern.
-          CARBON_RETURN_IF_ERROR(
-              ExpectExactType(clause.pattern().source_loc(),
-                              "`match` pattern type", expected_type.value(),
-                              &clause.pattern().static_type(), impl_scope));
-        } else {
-          expected_type = &clause.pattern().static_type();
-        }
-        if (patterns.IsRedundant({&clause.pattern()})) {
-          return ProgramError(clause.pattern().source_loc())
-                 << "unreachable case: all values matched by this case "
-                 << "are matched by earlier cases";
-        }
-        patterns.Add({&clause.pattern()});
-        CARBON_RETURN_IF_ERROR(
-            TypeCheckStmt(&clause.statement(), clause_scope));
-      }
-      if (expected_type.has_value()) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_expression,
-            ImplicitlyConvert("`match` expression", impl_scope,
-                              &match.expression(), expected_type.value()));
-        match.set_expression(converted_expression);
-      }
-      return Success();
-    }
-    case StatementKind::While: {
-      auto& while_stmt = cast<While>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&while_stmt.condition(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<Expression*> converted_condition,
-          ImplicitlyConvert("condition of `while`", impl_scope,
-                            &while_stmt.condition(), arena_->New<BoolType>()));
-      while_stmt.set_condition(converted_condition);
-      CARBON_RETURN_IF_ERROR(TypeCheckStmt(&while_stmt.body(), impl_scope));
-      return Success();
-    }
-    case StatementKind::For: {
-      auto& for_stmt = cast<For>(*s);
-      ImplScope inner_impl_scope(&impl_scope);
-
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&for_stmt.loop_target(), inner_impl_scope));
-
-      const Value& rhs = for_stmt.loop_target().static_type();
-      if (rhs.kind() == Value::Kind::StaticArrayType) {
-        CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-            &for_stmt.variable_declaration(), PatternRequirements::Irrefutable,
-            &cast<StaticArrayType>(rhs).element_type(), inner_impl_scope,
-            ExpressionCategory::Reference));
-        CARBON_RETURN_IF_ERROR(ExpectExactType(
-            for_stmt.source_loc(), "`for` pattern",
-            &cast<StaticArrayType>(rhs).element_type(),
-            &for_stmt.variable_declaration().static_type(), impl_scope));
-      } else {
-        return ProgramError(for_stmt.source_loc())
-               << "expected array type after in, found value of type " << rhs;
-      }
-
-      CARBON_RETURN_IF_ERROR(TypeCheckStmt(&for_stmt.body(), inner_impl_scope));
-      return Success();
-    }
-    case StatementKind::Break:
-    case StatementKind::Continue:
-      return Success();
-    case StatementKind::Block: {
-      auto& block = cast<Block>(*s);
-      for (auto* block_statement : block.statements()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckStmt(block_statement, impl_scope));
-      }
-      return Success();
-    }
-    case StatementKind::VariableDefinition: {
-      auto& var = cast<VariableDefinition>(*s);
-
-      // TODO: If the pattern contains a binding that implies a new impl is
-      // available, should that remain in scope for as long as its binding?
-      // ```
-      // var a: (T:! Widget) = ...;
-      // // Is the `impl T as Widget` in scope here?
-      // a.(Widget.F)();
-      // ```
-      ImplScope var_scope(&impl_scope);
-      std::optional<Nonnull<const Value*>> init_type;
-
-      // Type-check the initializer before we inspect the type of the variable
-      // so we can use its type to deduce parts of the type of the binding.
-      if (var.has_init()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(&var.init(), impl_scope));
-        CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
-            var.init().source_loc(), &var.init().static_type()));
-        init_type = &var.init().static_type();
-      }
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckPattern(&var.pattern(), PatternRequirements::Irrefutable,
-                           init_type, var_scope, var.expression_category()));
-      CARBON_RETURN_IF_ERROR(ExpectCompleteType(
-          var.source_loc(), "type of variable", &var.pattern().static_type()));
-      CARBON_RETURN_IF_ERROR(
-          ExpectConcreteType(var.source_loc(), &var.pattern().static_type()));
-      if (var.has_init()) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_init,
-            ImplicitlyConvert("initializer of variable", impl_scope,
-                              &var.init(), &var.pattern().static_type()));
-        var.set_init(converted_init);
-      }
-      return Success();
-    }
-    case StatementKind::Assign: {
-      auto& assign = cast<Assign>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&assign.rhs(), impl_scope));
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&assign.lhs(), impl_scope));
-      if (assign.lhs().expression_category() != ExpressionCategory::Reference) {
-        return ProgramError(assign.source_loc())
-               << "Only a reference expression can be assigned to, but got `"
-               << assign.lhs() << "`";
-      }
-      if (assign.op() == AssignOperator::Plain &&
-          IsSameType(&assign.lhs().static_type(), &assign.rhs().static_type(),
-                     impl_scope)) {
-        // TODO: Interface lookup.
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_rhs,
-            ImplicitlyConvert("assignment", impl_scope, &assign.rhs(),
-                              &assign.lhs().static_type()));
-        assign.set_rhs(converted_rhs);
-      } else {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> rewritten,
-            BuildBuiltinMethodCall(
-                impl_scope, &assign.lhs(),
-                BuiltinInterfaceName{
-                    GetBuiltinInterfaceForAssignOperator(assign.op()),
-                    {&assign.rhs().static_type()}},
-                BuiltinMethodCall{"Op", {&assign.rhs()}}));
-        assign.set_rewritten_form(rewritten);
-      }
-      return Success();
-    }
-    case StatementKind::IncrementDecrement: {
-      auto& inc_dec = cast<IncrementDecrement>(*s);
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<Expression*> rewritten,
-          BuildBuiltinMethodCall(
-              impl_scope, &inc_dec.argument(),
-              BuiltinInterfaceName{
-                  inc_dec.is_increment() ? Builtin::Inc : Builtin::Dec, {}},
-              BuiltinMethodCall{"Op"}));
-      inc_dec.set_rewritten_form(rewritten);
-      return Success();
-    }
-    case StatementKind::ExpressionStatement: {
-      auto& expr_stmt = cast<ExpressionStatement>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&expr_stmt.expression(), impl_scope));
-      CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
-          expr_stmt.source_loc(), &expr_stmt.expression().static_type()));
-      return Success();
-    }
-    case StatementKind::If: {
-      auto& if_stmt = cast<If>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&if_stmt.condition(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<Expression*> converted_condition,
-          ImplicitlyConvert("condition of `if`", impl_scope,
-                            &if_stmt.condition(), arena_->New<BoolType>()));
-      if_stmt.set_condition(converted_condition);
-      CARBON_RETURN_IF_ERROR(TypeCheckStmt(&if_stmt.then_block(), impl_scope));
-      if (if_stmt.else_block()) {
-        CARBON_RETURN_IF_ERROR(
-            TypeCheckStmt(*if_stmt.else_block(), impl_scope));
-      }
-      return Success();
-    }
-    case StatementKind::ReturnVar: {
-      auto& ret = cast<ReturnVar>(*s);
-      ReturnTerm& return_term = ret.function().return_term();
-      if (return_term.is_auto()) {
-        return_term.set_static_type(&ret.value_node().static_type());
-      } else {
-        // TODO: Consider using `ExpectExactType` here.
-        CARBON_CHECK(IsNonDeduceableType(&return_term.static_type()));
-        CARBON_CHECK(IsNonDeduceableType(&ret.value_node().static_type()));
-        if (!IsSameType(&return_term.static_type(),
-                        &ret.value_node().static_type(), impl_scope)) {
-          return ProgramError(ret.value_node().base().source_loc())
-                 << "type of returned var `" << ret.value_node().static_type()
-                 << "` does not match return type `"
-                 << return_term.static_type() << "`";
-        }
-      }
-      return Success();
-    }
-    case StatementKind::ReturnExpression: {
-      auto& ret = cast<ReturnExpression>(*s);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&ret.expression(), impl_scope));
-      ReturnTerm& return_term = ret.function().return_term();
-      if (return_term.is_auto()) {
-        CARBON_RETURN_IF_ERROR(ExpectNonPlaceholderType(
-            ret.source_loc(), &ret.expression().static_type()));
-        return_term.set_static_type(&ret.expression().static_type());
-      } else {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_ret_val,
-            ImplicitlyConvert("return value", impl_scope, &ret.expression(),
-                              &return_term.static_type()));
-        ret.set_expression(converted_ret_val);
-      }
-      return Success();
-    }
-  }
-}
-
-// Returns true if we can statically verify that `match` is exhaustive, meaning
-// that one of its clauses will be executed for any possible operand value.
-static auto IsExhaustive(const Match& match) -> bool {
-  PatternMatrix matrix;
-  for (const Match::Clause& clause : match.clauses()) {
-    matrix.Add({&clause.pattern()});
-  }
-  return matrix.IsRedundant({AbstractPattern::MakeWildcard()});
-}
-
-auto TypeChecker::ExpectReturnOnAllPaths(
-    std::optional<Nonnull<Statement*>> opt_stmt, SourceLocation source_loc)
-    -> ErrorOr<Success> {
-  if (!opt_stmt) {
-    return ProgramError(source_loc)
-           << "control-flow reaches end of function that provides a `->` "
-              "return type without reaching a return statement";
-  }
-  Nonnull<Statement*> stmt = *opt_stmt;
-  switch (stmt->kind()) {
-    case StatementKind::Match: {
-      auto& match = cast<Match>(*stmt);
-      if (!IsExhaustive(match)) {
-        return ProgramError(source_loc)
-               << "non-exhaustive match may allow control-flow to reach the "
-                  "end "
-                  "of a function that provides a `->` return type";
-      }
-      std::vector<Match::Clause> new_clauses;
-      for (auto& clause : match.clauses()) {
-        CARBON_RETURN_IF_ERROR(
-            ExpectReturnOnAllPaths(&clause.statement(), stmt->source_loc()));
-      }
-      return Success();
-    }
-    case StatementKind::Block: {
-      auto& block = cast<Block>(*stmt);
-      if (block.statements().empty()) {
-        return ProgramError(stmt->source_loc())
-               << "control-flow reaches end of function that provides a `->` "
-                  "return type without reaching a return statement";
-      }
-      CARBON_RETURN_IF_ERROR(ExpectReturnOnAllPaths(
-          block.statements()[block.statements().size() - 1],
-          block.source_loc()));
-      return Success();
-    }
-    case StatementKind::If: {
-      auto& if_stmt = cast<If>(*stmt);
-      CARBON_RETURN_IF_ERROR(
-          ExpectReturnOnAllPaths(&if_stmt.then_block(), stmt->source_loc()));
-      CARBON_RETURN_IF_ERROR(
-          ExpectReturnOnAllPaths(if_stmt.else_block(), stmt->source_loc()));
-      return Success();
-    }
-    case StatementKind::ReturnVar:
-    case StatementKind::ReturnExpression:
-      return Success();
-    case StatementKind::Assign:
-    case StatementKind::IncrementDecrement:
-    case StatementKind::ExpressionStatement:
-    case StatementKind::While:
-    case StatementKind::For:
-    case StatementKind::Break:
-    case StatementKind::Continue:
-    case StatementKind::VariableDefinition:
-      return ProgramError(stmt->source_loc())
-             << "control-flow reaches end of function that provides a `->` "
-                "return type without reaching a return statement";
-  }
-}
-
-// TODO: Add checking to function definitions to ensure that
-//   all deduced type parameters will be deduced.
-auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
-                                             const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  const auto name = GetName(*f);
-  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *f);
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "declaring function `" << *name << "` ("
-                           << f->source_loc() << ")\n";
-  }
-  ImplScope function_scope(scope_info.innermost_scope);
-  std::vector<Nonnull<const GenericBinding*>> all_bindings =
-      scope_info.bindings;
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings;
-  // Bring the deduced parameters into scope.
-  for (Nonnull<GenericBinding*> deduced : f->deduced_parameters()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-        deduced, PatternRequirements::Irrefutable, std::nullopt, function_scope,
-        ExpressionCategory::Value));
-    CollectAndNumberGenericBindingsInPattern(deduced, all_bindings);
-    CollectImplBindingsInPattern(deduced, impl_bindings);
-  }
-  // Type check the receiver pattern.
-  std::optional<FunctionType::MethodSelf> method_self;
-  if (f->is_method()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-        &f->self_pattern(), PatternRequirements::Irrefutable, std::nullopt,
-        function_scope, ExpressionCategory::Value));
-    CollectAndNumberGenericBindingsInPattern(&f->self_pattern(), all_bindings);
-    CollectImplBindingsInPattern(&f->self_pattern(), impl_bindings);
-    FunctionType::MethodSelf method_self_present = {
-        .addr_self = (f->self_pattern().kind() == PatternKind::AddrPattern),
-        .self_type = &f->self_pattern().static_type()};
-    method_self = method_self_present;
-  }
-  // Type check the parameter pattern.
-  CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-      &f->param_pattern(), PatternRequirements::Irrefutable, std::nullopt,
-      function_scope, ExpressionCategory::Value));
-  CollectImplBindingsInPattern(&f->param_pattern(), impl_bindings);
-
-  // All bindings we've seen so far in this scope are our deduced bindings.
-  std::vector<Nonnull<const GenericBinding*>> deduced_bindings(
-      all_bindings.begin() + scope_info.bindings.size(), all_bindings.end());
-
-  // Keep track of any generic parameters and nested generic bindings in the
-  // parameter pattern.
-  std::vector<FunctionType::GenericParameter> generic_parameters;
-  for (const auto [i, param_pattern] :
-       llvm::enumerate(f->param_pattern().fields())) {
-    size_t old_size = all_bindings.size();
-    CollectAndNumberGenericBindingsInPattern(param_pattern, all_bindings);
-
-    if (const auto* binding = dyn_cast<GenericBinding>(param_pattern)) {
-      generic_parameters.push_back({.index = i, .binding = binding});
-    } else {
-      deduced_bindings.insert(deduced_bindings.end(),
-                              all_bindings.begin() + old_size,
-                              all_bindings.end());
-    }
-  }
-
-  // Evaluate the return type, if we can do so without examining the body.
-  if (std::optional<Nonnull<Expression*>> return_expression =
-          f->return_term().type_expression();
-      return_expression.has_value()) {
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const Value*> ret_type,
-        TypeCheckTypeExp(*return_expression, function_scope));
-    // TODO: This is setting the constant value of the return type. It would
-    // make more sense if this were called `set_constant_value` rather than
-    // `set_static_type`.
-    f->return_term().set_static_type(ret_type);
-  } else if (f->return_term().is_omitted()) {
-    f->return_term().set_static_type(TupleType::Empty());
-  } else {
-    // We have to type-check the body in order to determine the return type.
-    if (!f->body().has_value()) {
-      return ProgramError(f->return_term().source_loc())
-             << "Function declaration has deduced return type but no body";
-    }
-    CARBON_RETURN_IF_ERROR(TypeCheckStmt(*f->body(), function_scope));
-    if (!f->return_term().is_omitted()) {
-      CARBON_RETURN_IF_ERROR(
-          ExpectReturnOnAllPaths(f->body(), f->source_loc()));
-    }
-  }
-  CARBON_CHECK(IsNonDeduceableType(&f->return_term().static_type()));
-
-  f->set_static_type(arena_->New<FunctionType>(
-      method_self, &f->param_pattern().static_type(),
-      std::move(generic_parameters), &f->return_term().static_type(),
-      std::move(deduced_bindings), std::move(impl_bindings),
-      /*is_initializing*/ true));
-  switch (f->kind()) {
-    case DeclarationKind::FunctionDeclaration:
-      // TODO: Should we pass in the bindings from the enclosing scope?
-      f->set_constant_value(arena_->New<FunctionValue>(
-          cast<FunctionDeclaration>(f), Bindings::None()));
-      break;
-    case DeclarationKind::DestructorDeclaration:
-      f->set_constant_value(
-          arena_->New<DestructorValue>(cast<DestructorDeclaration>(f)));
-      break;
-    default:
-      CARBON_FATAL("f is not a callable declaration");
-  }
-
-  if (name == "Main") {
-    if (!f->return_term().type_expression().has_value()) {
-      return ProgramError(f->return_term().source_loc())
-             << "`Main` must have an explicit return type";
-    }
-    CARBON_RETURN_IF_ERROR(
-        ExpectExactType(f->return_term().source_loc(), "return type of `Main`",
-                        arena_->New<IntType>(), &f->return_term().static_type(),
-                        function_scope));
-    if (!f->param_pattern().fields().empty()) {
-      return ProgramError(f->source_loc())
-             << "`Main` must not take any parameters";
-    }
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "finished declaring function `" << *name
-                           << "` of type `" << f->static_type() << "` ("
-                           << f->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::TypeCheckCallableDeclaration(Nonnull<CallableDeclaration*> f,
-                                               const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  auto name = GetName(*f);
-  CARBON_CHECK(name, "Unexpected missing name for `{0}`.", *f);
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking function `" << *name << "` ("
-                           << f->source_loc() << ")\n";
-  }
-  // If f->return_term().is_auto(), the function body was already
-  // type checked in DeclareFunctionDeclaration.
-  if (f->body().has_value() && !f->return_term().is_auto()) {
-    // Bring the impl bindings into scope.
-    ImplScope function_scope(&impl_scope);
-    BringImplBindingsIntoScope(
-        cast<FunctionType>(f->static_type()).impl_bindings(), function_scope);
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "impl declarations for `" << PrintAsID(*f)
-                              << "` (" << f->source_loc() << ")\n";
-      *trace_stream_ << function_scope;
-    }
-    CARBON_RETURN_IF_ERROR(TypeCheckStmt(*f->body(), function_scope));
-    if (!f->return_term().is_omitted()) {
-      CARBON_RETURN_IF_ERROR(
-          ExpectReturnOnAllPaths(f->body(), f->source_loc()));
-    }
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "finished checking function `" << *name << "` ("
-                           << f->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
-                                          const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "declaring class `" << class_decl->name() << "` ("
-                           << class_decl->source_loc() << ")\n";
-  }
-  Nonnull<SelfDeclaration*> self = class_decl->self();
-  ImplScope class_scope(scope_info.innermost_scope);
-
-  // The base class and member declarations may refer to the class, so we must
-  // set the static type before we start processing them. We can't set the
-  // constant value until later, but the base class declaration doesn't need it.
-  self->set_static_type(arena_->New<TypeType>());
-  std::optional<Nonnull<const ParameterizedEntityName*>> param_name;
-  if (class_decl->type_params().has_value()) {
-    // TODO: The `enclosing_bindings` should be tracked in the parameterized
-    // entity name so that they can be included in the eventual type.
-    param_name = arena_->New<ParameterizedEntityName>(
-        class_decl, *class_decl->type_params());
-    class_decl->set_static_type(
-        arena_->New<TypeOfParameterizedEntityName>(*param_name));
-  } else {
-    class_decl->set_static_type(&self->static_type());
-  }
-
-  // Find base class declaration, if any. Right now, verify that it is first in
-  // the class. This avoids the problem identified in
-  // https://github.com/carbon-language/carbon-lang/issues/2994 where the base
-  // class expression could reference an earlier declaration in the class that
-  // hasn't been typechecked yet and therefore doesn't have its `static_type`
-  // set.
-
-  // TODO: Verify just that is before any data member declarations, and there is
-  // at most one, and delay remaining work (type checking, base class
-  // evaluation, etc.) until the `extend base` declaration is processed in
-  // order.
-  std::optional<Nonnull<const NominalClassType*>> base_class;
-  if (!class_decl->members().empty()) {
-    Nonnull<Declaration*> m = class_decl->members()[0];
-    if (m->kind() == DeclarationKind::ExtendBaseDeclaration) {
-      Nonnull<Expression*> base_class_expr =
-          cast<ExtendBaseDeclaration>(*m).base_class();
-      CARBON_ASSIGN_OR_RETURN(const auto base_type,
-                              TypeCheckTypeExp(base_class_expr, class_scope));
-      if (base_type->kind() != Value::Kind::NominalClassType) {
-        return ProgramError(m->source_loc())
-               << "Unsupported base class type for class `"
-               << class_decl->name()
-               << "`. Only simple classes are currently supported as base "
-                  "class.";
-      }
-      CARBON_RETURN_IF_ERROR(ExpectCompleteType(
-          base_class_expr->source_loc(), "base class declaration", base_type));
-
-      base_class = cast<NominalClassType>(base_type);
-      if (base_class.value()->declaration().extensibility() ==
-          ClassExtensibility::None) {
-        return ProgramError(m->source_loc())
-               << "Base class `" << base_class.value()->declaration().name()
-               << "` is `final` and cannot be inherited. Add the `base` or "
-                  "`abstract` class prefix to `"
-               << base_class.value()->declaration().name()
-               << "` to allow it to be inherited";
-      }
-      class_decl->set_base_type(base_class);
-    }
-    for (Nonnull<Declaration*> m : class_decl->members().drop_front()) {
-      if (m->kind() == DeclarationKind::ExtendBaseDeclaration) {
-        if (base_class.has_value()) {
-          return ProgramError(m->source_loc())
-                 << "At most one `extend base:` declaration in a class.";
-        } else {
-          return ProgramError(m->source_loc())
-                 << "`extend base:` declarations after the first declaration "
-                    "in the class are not yet supported";
-        }
-      }
-    }
-  }
-
-  std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
-  if (class_decl->type_params().has_value()) {
-    Nonnull<TuplePattern*> type_params = *class_decl->type_params();
-    CARBON_RETURN_IF_ERROR(
-        TypeCheckPattern(type_params, PatternRequirements::Irrefutable,
-                         std::nullopt, class_scope, ExpressionCategory::Value));
-    CollectAndNumberGenericBindingsInPattern(type_params, bindings);
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "impl declarations for `" << class_decl->name()
-                              << "` (" << class_decl->source_loc() << ")\n";
-      *trace_stream_ << class_scope;
-    }
-  }
-
-  // Generate a vtable for the type if necessary.
-  VTable class_vtable = base_class ? (*base_class)->vtable() : VTable();
-  const int class_level = base_class ? (*base_class)->hierarchy_level() + 1 : 0;
-  for (const auto* m : class_decl->members()) {
-    const auto* fun = dyn_cast<FunctionDeclaration>(m);
-    if (!fun) {
-      continue;
-    }
-    if (fun->virt_override() != VirtualOverride::None && !fun->is_method()) {
-      return ProgramError(fun->source_loc())
-             << "Error declaring `" << fun->name() << "`"
-             << ": class functions cannot be virtual.";
-    }
-    CARBON_CHECK(!fun->name().is_qualified(),
-                 "qualified function name not permitted in class scope");
-
-    if (fun->virt_override() == VirtualOverride::Abstract &&
-        fun->body().has_value()) {
-      return ProgramError(fun->source_loc())
-             << "Error declaring `" << fun->name() << "`"
-             << ": abstract method cannot have a body.";
-    }
-
-    bool has_vtable_entry =
-        class_vtable.find(fun->name().inner_name()) != class_vtable.end();
-    // TODO: Implement complete declaration logic from
-    // `/docs/design/classes.md#virtual-methods`.
-    switch (fun->virt_override()) {
-      case VirtualOverride::Abstract:
-        if (class_decl->extensibility() != ClassExtensibility::Abstract) {
-          return ProgramError(fun->source_loc())
-                 << "Error declaring `" << fun->name() << "`"
-                 << ": `abstract` methods are allowed only in abstract "
-                    "classes.";
-        }
-        break;
-      case VirtualOverride::None:
-      case VirtualOverride::Virtual:
-        if (has_vtable_entry) {
-          return ProgramError(fun->source_loc())
-                 << "Error declaring `" << fun->name() << "`"
-                 << ": method is declared virtual in base class, use `impl` "
-                    "to override it.";
-        }
-        // TODO: Error if declaring virtual method shadowing non-virtual method.
-        // See https://github.com/carbon-language/carbon-lang/issues/2355.
-        if (fun->virt_override() == VirtualOverride::None) {
-          // Not added to the vtable.
-          continue;
-        }
-        break;
-      case VirtualOverride::Impl:
-        if (!has_vtable_entry) {
-          return ProgramError(fun->source_loc())
-                 << "Error declaring `" << fun->name() << "`"
-                 << ": cannot override a method that is not declared "
-                    "`abstract` or `virtual` in base class.";
-        }
-        break;
-    }
-    class_vtable[fun->name().inner_name()] = {fun, class_level};
-  }
-
-  // Check destructor's virtual override, add to vtable if necessary.
-  if (const auto destructor = class_decl->destructor()) {
-    const auto* fun = (*destructor);
-    static constexpr llvm::StringRef DestructorName = "destructor";
-    bool has_vtable_entry =
-        class_vtable.find(DestructorName) != class_vtable.end();
-    switch (fun->virt_override()) {
-      case VirtualOverride::None:
-        break;
-      case VirtualOverride::Abstract:
-        return ProgramError(fun->source_loc())
-               << "Cannot declare abstract destructor.";
-      case VirtualOverride::Virtual:
-        if (has_vtable_entry) {
-          return ProgramError(fun->source_loc())
-                 << "Error declaring destructor for `" << class_decl->name()
-                 << "`: use `impl` to implement virtual destructor in child "
-                    "class.";
-        }
-        class_vtable[DestructorName] = {fun, class_level};
-        break;
-      case VirtualOverride::Impl:
-        if (!has_vtable_entry) {
-          return ProgramError(fun->source_loc())
-                 << "Error declaring destructor for `" << class_decl->name()
-                 << "`: cannot override a destructor that is not declared "
-                    "`virtual` in base class.";
-        }
-        class_vtable[DestructorName] = {fun, class_level};
-        break;
-    }
-  }
-
-  if (class_decl->extensibility() != ClassExtensibility::Abstract) {
-    auto abstract_method_it = std::find_if(
-        class_vtable.begin(), class_vtable.end(), [](const auto& vt) {
-          const auto* const fun = vt.getValue().first;
-          return fun->is_method() &&
-                 fun->virt_override() == VirtualOverride::Abstract;
-        });
-
-    if (abstract_method_it != class_vtable.end()) {
-      auto fun_name = GetName(*abstract_method_it->getValue().first);
-      CARBON_CHECK(fun_name.has_value());
-      return ProgramError(class_decl->source_loc())
-             << "Error declaring `" << class_decl->name() << "`"
-             << ": non abstract class should implement abstract method `"
-             << *fun_name << "`.";
-    }
-  }
-
-  // For class declaration `class MyType(T:! type, U:! AnInterface)`, `Self`
-  // should have the value `MyType(T, U)`.
-  const auto* self_type = arena_->New<NominalClassType>(
-      class_decl, Bindings::SymbolicIdentity(arena_, bindings), base_class,
-      arena_->New<VTable>(std::move(class_vtable)));
-  self->set_constant_value(self_type);
-
-  // The declarations of the members may refer to the class, so we must set the
-  // constant value of the class before we start processing the members.
-  if (param_name.has_value()) {
-    class_decl->set_constant_value(*param_name);
-  } else {
-    class_decl->set_constant_value(self_type);
-  }
-
-  ScopeInfo class_scope_info =
-      ScopeInfo::ForClassScope(scope_info, &class_scope, std::move(bindings));
-  for (Nonnull<Declaration*> m : class_decl->members()) {
-    CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, class_scope_info));
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished declaring class `" << class_decl->name()
-                         << "` (" << class_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::TypeCheckClassDeclaration(
-    Nonnull<ClassDeclaration*> class_decl, const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking class `" << class_decl->name() << "` ("
-                           << class_decl->source_loc() << ")\n";
-  }
-  ImplScope class_scope(&impl_scope);
-  if (class_decl->type_params().has_value()) {
-    BringPatternImplBindingsIntoScope(*class_decl->type_params(), class_scope);
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result() << "impl declarations for `" << class_decl->name()
-                            << "` (" << class_decl->source_loc() << ")\n";
-    *trace_stream_ << class_scope;
-  }
-  auto [it, inserted] =
-      collected_members_.insert({class_decl, CollectedMembersMap()});
-  CARBON_CHECK(inserted, "Adding class {0} to collected_members_ must not fail",
-               class_decl->name());
-  for (Nonnull<Declaration*> m : class_decl->members()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, class_scope, class_decl));
-    CARBON_RETURN_IF_ERROR(CollectMember(class_decl, m));
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished checking class `" << class_decl->name()
-                         << "` (" << class_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-// EXPERIMENTAL MIXIN FEATURE
-auto TypeChecker::DeclareMixinDeclaration(Nonnull<MixinDeclaration*> mixin_decl,
-                                          const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "declaring mixin `" << mixin_decl->name() << "` ("
-                           << mixin_decl->source_loc() << ")\n";
-  }
-  ImplScope mixin_scope(scope_info.innermost_scope);
-
-  if (mixin_decl->params().has_value()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-        *mixin_decl->params(), PatternRequirements::Irrefutable, std::nullopt,
-        mixin_scope, ExpressionCategory::Value));
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "impl declarations for `" << mixin_decl->name()
-                              << "` (" << mixin_decl->source_loc() << ")\n";
-      *trace_stream_ << mixin_scope;
-    }
-
-    const auto* param_name =
-        arena_->New<ParameterizedEntityName>(mixin_decl, *mixin_decl->params());
-    mixin_decl->set_static_type(
-        arena_->New<TypeOfParameterizedEntityName>(param_name));
-    mixin_decl->set_constant_value(param_name);
-  } else {
-    const auto* mixin_type = arena_->New<MixinPseudoType>(mixin_decl);
-    mixin_decl->set_static_type(arena_->New<TypeOfMixinPseudoType>(mixin_type));
-    mixin_decl->set_constant_value(mixin_type);
-  }
-
-  // Process the Self parameter.
-  CARBON_RETURN_IF_ERROR(
-      TypeCheckPattern(mixin_decl->self(), PatternRequirements::Irrefutable,
-                       std::nullopt, mixin_scope, ExpressionCategory::Value));
-
-  ScopeInfo mixin_scope_info = ScopeInfo::ForNonClassScope(&mixin_scope);
-  for (Nonnull<Declaration*> m : mixin_decl->members()) {
-    CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, mixin_scope_info));
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished declaring mixin `" << mixin_decl->name()
-                         << "` (" << mixin_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-// EXPERIMENTAL MIXIN FEATURE
-// Checks to see if mixin_decl is already within collected_members_. If it is,
-// then the mixin has already been type checked before either while type
-// checking a previous mix declaration or while type checking the original mixin
-// declaration. If not, then every member declaration is type checked and then
-// added to collected_members_ under the mixin_decl key.
-auto TypeChecker::TypeCheckMixinDeclaration(
-    Nonnull<const MixinDeclaration*> mixin_decl, const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  auto [it, inserted] =
-      collected_members_.insert({mixin_decl, CollectedMembersMap()});
-  if (!inserted) {
-    // This declaration has already been type checked before
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Skip() << "skipped checking mixin `" << mixin_decl->name()
-                            << "` (" << mixin_decl->source_loc() << ")\n";
-    }
-    return Success();
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking mixin `" << mixin_decl->name() << "` ("
-                           << mixin_decl->source_loc() << ")\n";
-  }
-  ImplScope mixin_scope(&impl_scope);
-  if (mixin_decl->params().has_value()) {
-    BringPatternImplBindingsIntoScope(*mixin_decl->params(), mixin_scope);
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result() << "impl declarations for `" << mixin_decl->name()
-                            << "` (" << mixin_decl->source_loc() << ")\n";
-    *trace_stream_ << mixin_scope;
-  }
-  for (Nonnull<Declaration*> m : mixin_decl->members()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, mixin_scope, mixin_decl));
-    CARBON_RETURN_IF_ERROR(CollectMember(mixin_decl, m));
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished checking mixin `" << mixin_decl->name()
-                         << "` (" << mixin_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-// EXPERIMENTAL MIXIN FEATURE
-// Type checks the mixin mentioned in the mix declaration.
-// TypeCheckMixinDeclaration ensures that the members of that mixin are
-// available in collected_members_. The mixin members are then collected as
-// members of the enclosing class or mixin declaration.
-auto TypeChecker::TypeCheckMixDeclaration(
-    Nonnull<MixDeclaration*> mix_decl, const ImplScope& impl_scope,
-    std::optional<Nonnull<const Declaration*>> enclosing_decl)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking MixDeclaration `"
-                           << PrintAsID(*mix_decl) << "` ("
-                           << mix_decl->source_loc() << ")\n";
-  }
-  // TODO(darshal): Check if the imports (interface mentioned in the 'for'
-  // clause) of the mixin being mixed are being impl'd in the enclosed
-  // class/mixin declaration This raises the question of how to handle impl
-  // declarations in mixin declarations
-
-  CARBON_CHECK(enclosing_decl.has_value());
-  Nonnull<const Declaration*> encl_decl = enclosing_decl.value();
-  const auto& mixin_decl = mix_decl->mixin_value().declaration();
-  CARBON_RETURN_IF_ERROR(TypeCheckMixinDeclaration(&mixin_decl, impl_scope));
-  CollectedMembersMap& mix_members = FindCollectedMembers(&mixin_decl);
-
-  // Merge members collected in the enclosing declaration with the members
-  // collected for the mixin declaration associated with the mix declaration
-  for (auto [mix_member_name, mix_member] : mix_members) {
-    CARBON_RETURN_IF_ERROR(CollectMember(encl_decl, mix_member));
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished checking `" << PrintAsID(*mix_decl)
-                         << "` (" << mix_decl->source_loc() << ")\n";
-  }
-
-  return Success();
-}
-
-auto TypeChecker::DeclareConstraintTypeDeclaration(
-    Nonnull<ConstraintTypeDeclaration*> constraint_decl,
-    const ScopeInfo& scope_info) -> ErrorOr<Success> {
-  CARBON_CHECK(
-      (isa<InterfaceDeclaration, ConstraintDeclaration>(constraint_decl)),
-      "unexpected kind of constraint type declaration");
-  bool is_interface = isa<InterfaceDeclaration>(constraint_decl);
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "declaring `" << PrintAsID(*constraint_decl)
-                           << "` (" << constraint_decl->source_loc() << ")\n";
-  }
-  ImplScope constraint_scope(scope_info.innermost_scope);
-
-  // Type-check the parameters and find the set of bindings that are in scope.
-  std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
-  if (constraint_decl->params().has_value()) {
-    CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-        *constraint_decl->params(), PatternRequirements::Irrefutable,
-        std::nullopt, constraint_scope, ExpressionCategory::Value));
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result()
-          << "impl declarations for `" << PrintAsID(*constraint_decl) << "` ("
-          << constraint_decl->source_loc() << ")\n";
-      *trace_stream_ << constraint_scope;
-    }
-    CollectAndNumberGenericBindingsInPattern(*constraint_decl->params(),
-                                             bindings);
-  }
-
-  // Form the full symbolic type of the interface or named constraint. This is
-  // used as part of the value of associated constants, if they're referenced
-  // within their interface, and as the symbolic value of the declaration.
-  Nonnull<const Value*> constraint_type;
-  if (is_interface) {
-    constraint_type = arena_->New<InterfaceType>(
-        cast<InterfaceDeclaration>(constraint_decl),
-        Bindings::SymbolicIdentity(arena_, bindings));
-  } else {
-    constraint_type = arena_->New<NamedConstraintType>(
-        cast<ConstraintDeclaration>(constraint_decl),
-        Bindings::SymbolicIdentity(arena_, bindings));
-  }
-
-  // Set up the meaning of the declaration when used as an identifier.
-  if (constraint_decl->params().has_value()) {
-    const auto* param_name = arena_->New<ParameterizedEntityName>(
-        constraint_decl, *constraint_decl->params());
-    constraint_decl->set_static_type(
-        arena_->New<TypeOfParameterizedEntityName>(param_name));
-    constraint_decl->set_constant_value(param_name);
-  } else {
-    constraint_decl->set_static_type(arena_->New<TypeType>());
-    constraint_decl->set_constant_value(constraint_type);
-  }
-
-  // Set the type of Self to be the instantiated constraint type.
-  Nonnull<SelfDeclaration*> self_type = constraint_decl->self_type();
-  self_type->set_static_type(arena_->New<TypeType>());
-  self_type->set_constant_value(constraint_type);
-
-  // Build a constraint corresponding to this constraint type.
-  ConstraintTypeBuilder::PrepareSelfBinding(arena_, constraint_decl->self());
-  ConstraintTypeBuilder builder(arena_, constraint_decl->self());
-  ConstraintTypeBuilder::ConstraintsInScopeTracker constraint_tracker;
-  constraint_decl->self()->set_static_type(constraint_type);
-
-  // Lookups into this constraint type look in this declaration.
-  builder.AddLookupContext({.context = constraint_type});
-
-  // If this is an interface, this is a symbolic witness that Self implements
-  // this interface.
-  std::optional<Nonnull<const Witness*>> iface_impl_witness;
-  if (is_interface) {
-    // The impls constraint says only that the direct members of the interface
-    // are available. For any indirect constraints, we need to add separate
-    // entries to the constraint type. This ensures that all indirect
-    // constraints are lifted to the top level so they can be accessed directly
-    // and resolved independently if necessary.
-    int index = builder.AddImplsConstraint(
-        {.type = builder.GetSelfType(),
-         .interface = cast<InterfaceType>(constraint_type)});
-    iface_impl_witness =
-        MakeConstraintWitnessAccess(builder.GetSelfWitness(), index);
-  }
-
-  ScopeInfo constraint_scope_info =
-      ScopeInfo::ForNonClassScope(&constraint_scope);
-  for (Nonnull<Declaration*> m : constraint_decl->members()) {
-    CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, constraint_scope_info));
-
-    // TODO: This should probably live in `DeclareDeclaration`, but it needs
-    // to update state that's not available from there.
-    switch (m->kind()) {
-      case DeclarationKind::InterfaceExtendDeclaration: {
-        // For an `extend C;` declaration, add `Self impls C` to our
-        // constraint.
-        auto* extend = cast<InterfaceExtendDeclaration>(m);
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> base,
-            TypeCheckTypeExp(extend->base(), constraint_scope));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const ConstraintType*> constraint_type,
-            ConvertToConstraintType(m->source_loc(), "extend declaration",
-                                    base));
-        CARBON_RETURN_IF_ERROR(builder.AddAndSubstitute(
-            *this, constraint_type, builder.GetSelfType(),
-            builder.GetSelfWitness(), Bindings(),
-            /*add_lookup_contexts=*/true));
-        break;
-      }
-
-      case DeclarationKind::InterfaceRequireDeclaration: {
-        // For an `require X impls Y;` declaration, add `X impls Y` to our
-        // constraint.
-        auto* require = cast<InterfaceRequireDeclaration>(m);
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> impl_type,
-            TypeCheckTypeExp(require->impl_type(), constraint_scope));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const Value*> constraint,
-            TypeCheckTypeExp(require->constraint(), constraint_scope));
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const ConstraintType*> constraint_type,
-            ConvertToConstraintType(m->source_loc(), "require declaration",
-                                    constraint));
-        CARBON_RETURN_IF_ERROR(
-            builder.AddAndSubstitute(*this, constraint_type, impl_type,
-                                     builder.GetSelfWitness(), Bindings(),
-                                     /*add_lookup_contexts=*/false));
-        break;
-      }
-
-      case DeclarationKind::AssociatedConstantDeclaration: {
-        auto* assoc = cast<AssociatedConstantDeclaration>(m);
-        if (!is_interface) {
-          // TODO: Template constraints can have associated constants.
-          return ProgramError(assoc->source_loc())
-                 << "associated constant not permitted in named constraint";
-        }
-
-        CARBON_RETURN_IF_ERROR(TypeCheckGenericBinding(
-            assoc->binding(), "associated constant", constraint_scope));
-        Nonnull<const Value*> constraint = &assoc->binding().static_type();
-        assoc->set_static_type(constraint);
-
-        // The constant value is used if the constant is named later in the
-        // same constraint type. Note that this differs from the symbolic
-        // identity of the binding, which was set in TypeCheckGenericBinding to
-        // a VariableType naming the binding so that .Self resolves to the
-        // binding itself.
-        auto* assoc_value = arena_->New<AssociatedConstant>(
-            &constraint_decl->self()->value(),
-            cast<InterfaceType>(constraint_type), assoc, *iface_impl_witness);
-        assoc->set_constant_value(assoc_value);
-
-        // The type specified for the associated constant becomes a
-        // constraint for the constraint type: `let X:! Interface` adds a
-        // `Self.X impls Interface` constraint that `impl` declarations must
-        // satisfy and users of the constraint type can rely on.
-        if (const auto* constraint_type =
-                dyn_cast<ConstraintType>(constraint)) {
-          CARBON_RETURN_IF_ERROR(
-              builder.AddAndSubstitute(*this, constraint_type, assoc_value,
-                                       builder.GetSelfWitness(), Bindings(),
-                                       /*add_lookup_contexts=*/false));
-        }
-        break;
-      }
-
-      case DeclarationKind::FunctionDeclaration: {
-        if (!is_interface) {
-          // TODO: Template constraints can have associated functions.
-          return ProgramError(m->source_loc())
-                 << "associated function not permitted in named constraint";
-        }
-        break;
-      }
-
-      default: {
-        CARBON_FATAL(
-            "unexpected declaration in constraint type declaration:\n{0}", *m);
-        break;
-      }
-    }
-
-    // Add any new impls constraints to the scope.
-    builder.BringConstraintsIntoScope(*this, &constraint_scope,
-                                      &constraint_tracker);
-  }
-
-  constraint_decl->set_constraint_type(std::move(builder).Build());
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished declaring `"
-                         << PrintAsID(*constraint_decl) << "` ("
-                         << constraint_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::TypeCheckConstraintTypeDeclaration(
-    Nonnull<ConstraintTypeDeclaration*> constraint_decl,
-    const ImplScope& impl_scope) -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking `" << PrintAsID(*constraint_decl)
-                           << "` (" << constraint_decl->source_loc() << ")\n";
-  }
-  ImplScope constraint_scope(&impl_scope);
-  if (constraint_decl->params().has_value()) {
-    BringPatternImplBindingsIntoScope(*constraint_decl->params(),
-                                      constraint_scope);
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Result()
-        << "impl declarations for `" << PrintAsID(*constraint_decl) << "` ("
-        << constraint_decl->source_loc() << ")\n";
-    *trace_stream_ << constraint_scope;
-  }
-  for (Nonnull<Declaration*> m : constraint_decl->members()) {
-    CARBON_RETURN_IF_ERROR(
-        TypeCheckDeclaration(m, constraint_scope, constraint_decl));
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished checking `" << PrintAsID(*constraint_decl)
-                         << "` (" << constraint_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::CheckImplIsDeducible(
-    SourceLocation source_loc, Nonnull<const Value*> impl_type,
-    Nonnull<const InterfaceType*> impl_iface,
-    llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-    const ImplScope& /*impl_scope*/) -> ErrorOr<Success> {
-  ArgumentDeduction deduction(source_loc, "impl", deduced_bindings,
-                              trace_stream_);
-  CARBON_RETURN_IF_ERROR(deduction.Deduce(impl_type, impl_type,
-                                          /*allow_implicit_conversion=*/false));
-  CARBON_RETURN_IF_ERROR(deduction.Deduce(impl_iface, impl_iface,
-                                          /*allow_implicit_conversion=*/false));
-  if (auto not_deduced = deduction.FindUndeducedBinding()) {
-    return ProgramError(source_loc)
-           << "parameter `" << **not_deduced << "` is not deducible from `impl "
-           << *impl_type << " as " << *impl_iface << "`";
-  }
-  return Success();
-}
-
-auto TypeChecker::CheckImplIsComplete(Nonnull<const InterfaceType*> iface_type,
-                                      Nonnull<const ImplDeclaration*> impl_decl,
-                                      Nonnull<const Value*> self_type,
-                                      Nonnull<const Witness*> /*self_witness*/,
-                                      Nonnull<const Witness*> iface_witness,
-                                      const ImplScope& impl_scope)
-    -> ErrorOr<Success> {
-  const auto& iface_decl = iface_type->declaration();
-  for (Nonnull<Declaration*> m : iface_decl.members()) {
-    if (auto* assoc = dyn_cast<AssociatedConstantDeclaration>(m)) {
-      // An associated constant must be given a value.
-      if (!LookupRewrite(impl_decl->constraint_type(), iface_type, assoc)) {
-        return ProgramError(impl_decl->source_loc())
-               << "implementation doesn't provide a concrete value for "
-               << *iface_type << "." << assoc->binding().name();
-      }
-    } else if (isa<InterfaceRequireDeclaration, InterfaceExtendDeclaration>(
-                   m)) {
-      // These get translated into constraints so there's nothing we need to
-      // check here.
-    } else {
-      // Every member function must be declared.
-      std::optional<std::string_view> mem_name = GetName(*m);
-      CARBON_CHECK(mem_name.has_value(), "unnamed interface member {0}", *m);
-
-      std::optional<Nonnull<const Declaration*>> mem =
-          FindMember(*mem_name, impl_decl->members());
-      if (!mem.has_value()) {
-        return ProgramError(impl_decl->source_loc())
-               << "implementation missing " << *mem_name;
-      }
-
-      Bindings bindings = iface_type->bindings();
-      bindings.Add(iface_decl.self(), self_type, iface_witness);
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> iface_mem_type,
-                              Substitute(bindings, &m->static_type()));
-      // TODO: How should the signature in the implementation be permitted
-      // to differ from the signature in the interface?
-      CARBON_RETURN_IF_ERROR(
-          ExpectExactType((*mem)->source_loc(), "member of implementation",
-                          iface_mem_type, &(*mem)->static_type(), impl_scope));
-    }
-  }
-  return Success();
-}
-
-auto TypeChecker::CheckAndAddImplBindings(
-    Nonnull<const ImplDeclaration*> impl_decl, Nonnull<const Value*> impl_type,
-    Nonnull<const Witness*> self_witness, Nonnull<const Witness*> impl_witness,
-    llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-    const ScopeInfo& scope_info) -> ErrorOr<Success> {
-  // Each interface that is a lookup context is required to be implemented by
-  // the impl members. Other constraints are required to be satisfied by
-  // either those implementations or implementations available elsewhere.
-  Nonnull<const ConstraintType*> constraint = impl_decl->constraint_type();
-  for (auto lookup : constraint->lookup_contexts()) {
-    if (const auto* iface_type = dyn_cast<InterfaceType>(lookup.context)) {
-      CARBON_RETURN_IF_ERROR(ExpectCompleteType(
-          impl_decl->source_loc(), "impl declaration", iface_type));
-      CARBON_RETURN_IF_ERROR(
-          CheckImplIsDeducible(impl_decl->source_loc(), impl_type, iface_type,
-                               deduced_bindings, *scope_info.innermost_scope));
-
-      // Bring the associated constant values for this interface into scope. We
-      // know that if the methods of this interface are used, they will use
-      // these values.
-      ImplScope iface_scope(scope_info.innermost_scope);
-      BringAssociatedConstantsIntoScope(constraint, impl_type, iface_type,
-                                        iface_scope);
-
-      // Compute a witness that the implementing type implements this interface
-      // by resolving the interface constraint in a context where this `impl`
-      // is used for it. We don't actually want the whole `impl` to be in
-      // scope, though, because it could be partially specialized.
-      Nonnull<const Witness*> iface_witness;
-      {
-        ImplScope impl_scope(&iface_scope);
-        impl_scope.Add(impl_decl->constraint_type(), impl_type, impl_witness,
-                       *this);
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<const ConstraintType*> iface_constraint,
-            ConvertToConstraintType(impl_decl->source_loc(), "impl declaration",
-                                    iface_type));
-        CARBON_ASSIGN_OR_RETURN(
-            iface_witness, impl_scope.Resolve(iface_constraint, impl_type,
-                                              impl_decl->source_loc(), *this));
-      }
-
-      CARBON_RETURN_IF_ERROR(CheckImplIsComplete(iface_type, impl_decl,
-                                                 impl_type, self_witness,
-                                                 iface_witness, iface_scope));
-
-      std::optional<TypeStructureSortKey> sort_key;
-      if (!deduced_bindings.empty()) {
-        sort_key = TypeStructureSortKey::ForImpl(impl_type, iface_type);
-        if (trace_stream_->is_enabled()) {
-          trace_stream_->End()
-              << "type structure sort key for `impl " << *impl_type << " as "
-              << *iface_type << "` is " << sort_key << "\n";
-        }
-      }
-
-      // TODO: We should do this either before checking any interface or after
-      // checking all of them, so that the order of lookup contexts doesn't
-      // matter.
-      scope_info.innermost_non_class_scope->Add(
-          iface_type, deduced_bindings, impl_type, impl_decl->impl_bindings(),
-          self_witness, *this, sort_key);
-    } else if (isa<NamedConstraintType>(lookup.context)) {
-      // Nothing to check here, since a named constraint can't introduce any
-      // associated entities.
-    } else {
-      // TODO: Add support for implementing `adapter`s.
-      return ProgramError(impl_decl->source_loc())
-             << "cannot implement a constraint whose lookup context includes "
-             << *lookup.context;
-    }
-  }
-  return Success();
-}
-
-auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
-                                         const ScopeInfo& scope_info,
-                                         bool is_template_instantiation)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "declaring `" << PrintAsID(*impl_decl) << "` ("
-                           << impl_decl->source_loc() << ")\n";
-  }
-
-  // We need to eagerly typecheck portions of the impl in terms of the generic
-  // parameters, and then typecheck it again at instantiation time in terms of
-  // the actual arguments. The AST doesn't allow type information to be mutated,
-  // So in order to do that, we need to preserve a clone that doesn't have type
-  // information attached.
-  if (!IsTemplateSaturated(impl_decl->deduced_parameters())) {
-    CloneContext context(arena_);
-    TemplateInfo template_info = {.pattern = context.Clone(impl_decl)};
-    for (const auto* deduced : impl_decl->deduced_parameters()) {
-      template_info.param_map.insert(
-          {deduced, context.GetExistingClone(deduced)});
-    }
-    templates_.insert({impl_decl, std::move(template_info)});
-  }
-
-  ImplScope impl_scope(scope_info.innermost_scope);
-  std::vector<Nonnull<const GenericBinding*>> generic_bindings =
-      scope_info.bindings;
-  std::vector<Nonnull<const ImplBinding*>> impl_bindings;
-
-  // Bring the deduced parameters into scope.
-  for (Nonnull<GenericBinding*> deduced : impl_decl->deduced_parameters()) {
-    CARBON_RETURN_IF_ERROR(
-        TypeCheckPattern(deduced, PatternRequirements::Irrefutable,
-                         std::nullopt, impl_scope, ExpressionCategory::Value));
-    CollectAndNumberGenericBindingsInPattern(deduced, generic_bindings);
-    CollectImplBindingsInPattern(deduced, impl_bindings);
-  }
-  impl_decl->set_impl_bindings(impl_bindings);
-
-  // Check and interpret the impl_type
-  CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> impl_type_value,
-                          TypeCheckTypeExp(impl_decl->impl_type(), impl_scope));
-
-  // Set `Self` to `impl_type`. We do this whether `Self` resolves to it or to
-  // the `Self` from an enclosing scope. This needs to be done before
-  // processing the interface, in case the interface expression uses `Self`.
-  Nonnull<SelfDeclaration*> self = impl_decl->self();
-  self->set_constant_value(impl_type_value);
-  self->set_static_type(arena_->New<TypeType>());
-
-  // Check and interpret the interface.
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const Value*> implemented_type,
-      TypeCheckTypeExp(&impl_decl->interface(), impl_scope));
-  CARBON_ASSIGN_OR_RETURN(
-      Nonnull<const ConstraintType*> implemented_constraint,
-      ConvertToConstraintType(impl_decl->interface().source_loc(),
-                              "impl declaration", implemented_type));
-
-  // Substitute the given type for `.Self` to form the resolved constraint that
-  // this `impl` implements.
-  Nonnull<const ConstraintType*> constraint_type;
-  {
-    // TODO: Combine this with the SelfDeclaration.
-    auto* self_binding = arena_->New<GenericBinding>(
-        self->source_loc(), "Self", &impl_decl->interface(),
-        GenericBinding::BindingKind::Checked);
-    self_binding->set_symbolic_identity(impl_type_value);
-    self_binding->set_value(impl_type_value);
-    auto* impl_binding = arena_->New<ImplBinding>(self_binding->source_loc(),
-                                                  self_binding, std::nullopt);
-    impl_binding->set_symbolic_identity(
-        arena_->New<BindingWitness>(impl_binding));
-    self_binding->set_impl_binding(impl_binding);
-
-    ConstraintTypeBuilder builder(arena_, self_binding, impl_binding);
-    CARBON_RETURN_IF_ERROR(
-        builder.AddAndSubstitute(*this, implemented_constraint, impl_type_value,
-                                 builder.GetSelfWitness(), Bindings(),
-                                 /*add_lookup_contexts=*/true));
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start()
-          << "resolving impls constraint type for `" << PrintAsID(*impl_decl)
-          << "` from `" << *implemented_constraint << "` ("
-          << impl_decl->source_loc() << ")\n";
-    }
-    CARBON_RETURN_IF_ERROR(builder.Resolve(
-        *this, impl_decl->interface().source_loc(), impl_scope));
-    constraint_type = std::move(builder).Build();
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start() << "resolving impls constraint type as `"
-                             << *constraint_type << "`\n";
-    }
-    impl_decl->set_constraint_type(constraint_type);
-  }
-
-  // Declare the impl members. An `impl` behaves like a class scope.
-  ScopeInfo impl_scope_info =
-      ScopeInfo::ForClassScope(scope_info, &impl_scope, generic_bindings);
-  for (Nonnull<Declaration*> m : impl_decl->members()) {
-    CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, impl_scope_info));
-  }
-
-  // Build the self witness. This is the witness used to demonstrate that
-  // this impl implements its lookup contexts.
-  auto* self_witness = arena_->New<ImplWitness>(
-      impl_decl, Bindings::SymbolicIdentity(arena_, generic_bindings));
-
-  // Check that this impl satisfies its constraints and push it into the
-  // ImplScope. For a templated impl, only the template is pushed into scope.
-  // Instantiations are found by substituting arguments into the parameterized
-  // ImplWitness.
-  if (!is_template_instantiation) {
-    // Compute a witness that the impl implements its constraint.
-    std::vector<EqualityConstraint> rewrite_constraints_as_equality_constraints;
-    ImplScope self_impl_scope(&impl_scope);
-
-    // For each interface we're going to implement, this impl is the witness
-    // that that interface is implemented.
-    for (auto lookup : constraint_type->lookup_contexts()) {
-      if (const auto* iface_type = dyn_cast<InterfaceType>(lookup.context)) {
-        self_impl_scope.Add(iface_type, impl_type_value, self_witness, *this);
-      }
-    }
-
-    // This impl also provides all of the equalities from its rewrite
-    // constraints.
-    for (const auto& rewrite : constraint_type->rewrite_constraints()) {
-      rewrite_constraints_as_equality_constraints.push_back(
-          {.values = {rewrite.constant, rewrite.converted_replacement}});
-    }
-    for (const auto& eq : rewrite_constraints_as_equality_constraints) {
-      self_impl_scope.AddEqualityConstraint(&eq);
-    }
-
-    // Ensure that's enough for our interface to be satisfied.
-    CARBON_ASSIGN_OR_RETURN(
-        Nonnull<const Witness*> impl_witness,
-        self_impl_scope.Resolve(constraint_type, impl_type_value,
-                                impl_decl->source_loc(), *this));
-
-    // Create the implied impl bindings.
-    CARBON_RETURN_IF_ERROR(CheckAndAddImplBindings(
-        impl_decl, impl_type_value, self_witness, impl_witness,
-        generic_bindings, impl_scope_info));
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished declaring impl `"
-                         << *impl_decl->impl_type() << "` as `"
-                         << impl_decl->interface() << "`\n";
-  }
-  return Success();
-}
-
-void TypeChecker::BringAssociatedConstantsIntoScope(
-    Nonnull<const ConstraintType*> constraint, Nonnull<const Value*> self,
-    Nonnull<const InterfaceType*> interface, ImplScope& scope) {
-  std::set<Nonnull<const AssociatedConstantDeclaration*>> assocs_in_interface;
-  for (Nonnull<Declaration*> m : interface->declaration().members()) {
-    if (auto* assoc = dyn_cast<AssociatedConstantDeclaration>(m)) {
-      assocs_in_interface.insert(assoc);
-    }
-  }
-
-  for (const auto& eq : constraint->equality_constraints()) {
-    for (Nonnull<const Value*> value : eq.values) {
-      if (const auto* assoc = dyn_cast<AssociatedConstant>(value)) {
-        if (assocs_in_interface.count(&assoc->constant()) &&
-            ValueEqual(&assoc->base(), self, std::nullopt) &&
-            ValueEqual(&assoc->interface(), interface, std::nullopt)) {
-          // This equality constraint mentions an associated constant that is
-          // part of interface. Bring it into scope.
-          scope.AddEqualityConstraint(&eq);
-          break;
-        }
-      }
-    }
-  }
-
-  // TODO: Find a way to bring rewrite constraints into scope.
-}
-
-auto TypeChecker::TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
-                                           const ImplScope& enclosing_scope)
-    -> ErrorOr<Success> {
-  if (!IsTemplateSaturated(impl_decl->deduced_parameters())) {
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Start() << "deferring checking templated `" << *impl_decl
-                             << "` (" << impl_decl->source_loc() << ")\n";
-    }
-    return Success();
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking ImplDeclaration `"
-                           << PrintAsID(*impl_decl) << "` ("
-                           << impl_decl->source_loc() << ")\n";
-  }
-
-  Nonnull<const Value*> self = *impl_decl->self()->constant_value();
-  Nonnull<const ConstraintType*> constraint = impl_decl->constraint_type();
-
-  // Bring the impl bindings from the parameters into scope.
-  ImplScope impl_scope(&enclosing_scope);
-  BringImplBindingsIntoScope(impl_decl->impl_bindings(), impl_scope);
-  for (Nonnull<Declaration*> m : impl_decl->members()) {
-    CARBON_ASSIGN_OR_RETURN(
-        ConstraintLookupResult result,
-        LookupInConstraint(m->source_loc(), "member impl declaration",
-                           constraint, GetName(*m).value()));
-
-    // Bring the associated constant values for the interface that this method
-    // implements part of into scope.
-    ImplScope member_scope(&impl_scope);
-    BringAssociatedConstantsIntoScope(constraint, self, result.interface,
-                                      member_scope);
-
-    CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, member_scope, impl_decl));
-  }
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->End() << "finished checking impl `" << PrintAsID(*impl_decl)
-                         << "` (" << impl_decl->source_loc() << ")\n";
-  }
-  return Success();
-}
-
-auto TypeChecker::DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
-                                           const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  ImplScope choice_scope(scope_info.innermost_scope);
-  std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
-  if (choice->type_params().has_value()) {
-    Nonnull<TuplePattern*> type_params = *choice->type_params();
-    CARBON_RETURN_IF_ERROR(
-        TypeCheckPattern(type_params, PatternRequirements::None, std::nullopt,
-                         choice_scope, ExpressionCategory::Value));
-    CollectAndNumberGenericBindingsInPattern(type_params, bindings);
-    if (trace_stream_->is_enabled()) {
-      trace_stream_->Result() << "impl declarations for `" << PrintAsID(*choice)
-                              << "` (" << choice->source_loc() << ")\n";
-      *trace_stream_ << choice_scope;
-    }
-  }
-
-  for (Nonnull<AlternativeSignature*> alternative : choice->alternatives()) {
-    if (auto params = alternative->parameters()) {
-      CARBON_ASSIGN_OR_RETURN(
-          auto type, TypeCheckTypeExp(*params, *scope_info.innermost_scope));
-      alternative->set_parameters_static_type(type);
-    }
-  }
-
-  if (choice->type_params().has_value()) {
-    const auto* param_name =
-        arena_->New<ParameterizedEntityName>(choice, *choice->type_params());
-    choice->set_static_type(
-        arena_->New<TypeOfParameterizedEntityName>(param_name));
-    choice->set_constant_value(param_name);
-    return Success();
-  }
-
-  auto* ct = arena_->New<ChoiceType>(
-      choice, Bindings::SymbolicIdentity(arena_, bindings));
-
-  choice->set_static_type(arena_->New<TypeType>());
-  choice->set_constant_value(ct);
-  return Success();
-}
-
-auto TypeChecker::TypeCheckChoiceDeclaration(
-    Nonnull<ChoiceDeclaration*> /*choice*/, const ImplScope& /*impl_scope*/)
-    -> ErrorOr<Success> {
-  // Nothing to do here, but perhaps that will change in the future?
-  return Success();
-}
-
-static auto IsValidTypeForAliasTarget(Nonnull<const Value*> type) -> bool {
-  switch (type->kind()) {
-    case Value::Kind::IntValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::StructValue:
-    case Value::Kind::NominalClassValue:
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::AlternativeValue:
-    case Value::Kind::TupleValue:
-    case Value::Kind::ImplWitness:
-    case Value::Kind::BindingWitness:
-    case Value::Kind::ConstraintWitness:
-    case Value::Kind::ConstraintImplWitness:
-    case Value::Kind::ParameterizedEntityName:
-    case Value::Kind::MemberName:
-    case Value::Kind::BindingPlaceholderValue:
-    case Value::Kind::AddrValue:
-    case Value::Kind::AlternativeConstructorValue:
-    case Value::Kind::StringValue:
-    case Value::Kind::UninitializedValue:
-      CARBON_FATAL("type of alias target is not a type: {0}", *type);
-
-    case Value::Kind::AutoType:
-    case Value::Kind::VariableType:
-      CARBON_FATAL("pattern type in alias target: {0}", *type);
-
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::PointerType:
-    case Value::Kind::StaticArrayType:
-    case Value::Kind::StructType:
-    case Value::Kind::TupleType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::StringType:
-    case Value::Kind::AssociatedConstant:
-    case Value::Kind::TypeOfMixinPseudoType:
-      return false;
-
-    case Value::Kind::FunctionType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::TypeType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName:
-      return true;
-  }
-}
-
-auto TypeChecker::DeclareAliasDeclaration(Nonnull<AliasDeclaration*> alias,
-                                          const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  CARBON_RETURN_IF_ERROR(
-      TypeCheckExp(&alias->target(), *scope_info.innermost_scope));
-
-  if (!IsValidTypeForAliasTarget(&alias->target().static_type())) {
-    return ProgramError(alias->source_loc())
-           << "invalid target for alias declaration";
-  }
-
-  alias->set_static_type(&alias->target().static_type());
-  // constant_value not needed for namespace alias because these are resolved by
-  // NameResolver
-  if (alias->target().static_type().kind() !=
-      Value::Kind::TypeOfNamespaceName) {
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> target,
-                            InterpExp(&alias->target()));
-    alias->set_constant_value(target);
-  }
-  return Success();
-}
-
-auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
-  ImplScope impl_scope;
-  ScopeInfo top_level_scope_info = ScopeInfo::ForNonClassScope(&impl_scope);
-  SetFileContext set_file_ctx(*trace_stream_, std::nullopt);
-
-  // Track that `impl_scope` is the top-level `ImplScope`.
-  llvm::SaveAndRestore<decltype(top_level_impl_scope_)>
-      set_top_level_impl_scope(top_level_impl_scope_, &impl_scope);
-
-  for (auto declaration : ast.declarations) {
-    set_file_ctx.update_source_loc(declaration->source_loc());
-    CARBON_RETURN_IF_ERROR(
-        DeclareDeclaration(declaration, top_level_scope_info));
-    CARBON_RETURN_IF_ERROR(
-        TypeCheckDeclaration(declaration, impl_scope, std::nullopt));
-    // Check to see if this declaration is a builtin.
-    // TODO: Only do this when type-checking the prelude.
-    builtins_.Register(declaration);
-  }
-  CARBON_RETURN_IF_ERROR(TypeCheckExp(*ast.main_call, impl_scope));
-  return Success();
-}
-
-auto TypeChecker::TypeCheckDeclaration(
-    Nonnull<Declaration*> d, const ImplScope& impl_scope,
-    std::optional<Nonnull<const Declaration*>> enclosing_decl)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Source()
-        << "type checking declaration at (" << d->source_loc() << ")\n";
-    *trace_stream_ << "```\n" << *d << "\n```\n";
-  }
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "checking " << d->kind() << " `" << PrintAsID(*d)
-                           << "` (" << d->source_loc() << ")\n";
-  }
-  switch (d->kind()) {
-    case DeclarationKind::NamespaceDeclaration:
-      break;
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      CARBON_RETURN_IF_ERROR(TypeCheckConstraintTypeDeclaration(
-          &cast<ConstraintTypeDeclaration>(*d), impl_scope));
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckImplDeclaration(&cast<ImplDeclaration>(*d), impl_scope));
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration: {
-      auto* match_first = cast<MatchFirstDeclaration>(d);
-      for (auto* impl : match_first->impl_declarations()) {
-        impl->set_match_first(match_first);
-        CARBON_RETURN_IF_ERROR(TypeCheckImplDeclaration(impl, impl_scope));
-      }
-      break;
-    }
-    case DeclarationKind::DestructorDeclaration:
-    case DeclarationKind::FunctionDeclaration:
-      CARBON_RETURN_IF_ERROR(TypeCheckCallableDeclaration(
-          &cast<CallableDeclaration>(*d), impl_scope));
-      break;
-    case DeclarationKind::ClassDeclaration:
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), impl_scope));
-      break;
-    case DeclarationKind::MixinDeclaration: {
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckMixinDeclaration(&cast<MixinDeclaration>(*d), impl_scope));
-      break;
-    }
-    case DeclarationKind::MixDeclaration: {
-      CARBON_RETURN_IF_ERROR(TypeCheckMixDeclaration(
-          &cast<MixDeclaration>(*d), impl_scope, enclosing_decl));
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration:
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d), impl_scope));
-      break;
-    case DeclarationKind::VariableDeclaration: {
-      auto& var = cast<VariableDeclaration>(*d);
-      if (var.has_initializer()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckExp(&var.initializer(), impl_scope));
-      }
-      if (!isa<ExpressionPattern>(&var.binding().type())) {
-        // TODO: consider adding support for `auto`
-        return ProgramError(var.source_loc())
-               << "Type of a top-level variable must be an expression.";
-      }
-      if (var.has_initializer()) {
-        CARBON_ASSIGN_OR_RETURN(
-            Nonnull<Expression*> converted_initializer,
-            ImplicitlyConvert("initializer of variable", impl_scope,
-                              &var.initializer(), &var.static_type()));
-        var.set_initializer(converted_initializer);
-      }
-      break;
-    }
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      // Checked in DeclareConstraintTypeDeclaration.
-      break;
-    }
-    case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL("Unreachable TypeChecker `Self` declaration");
-    }
-    case DeclarationKind::AliasDeclaration: {
-      break;
-    }
-    case DeclarationKind::ExtendBaseDeclaration: {
-      // Checked in TypeCheckClassDeclaration.
-      break;
-    }
-  }
-  d->set_is_type_checked();
-  return Success();
-}
-
-auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
-                                     const ScopeInfo& scope_info)
-    -> ErrorOr<Success> {
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Source() << "declaration at (" << d->source_loc() << ")\n";
-    *trace_stream_ << "```\n" << *d << "\n```\n";
-  }
-
-  switch (d->kind()) {
-    case DeclarationKind::NamespaceDeclaration: {
-      auto& namespace_decl = cast<NamespaceDeclaration>(*d);
-      namespace_decl.set_static_type(
-          arena_->New<TypeOfNamespaceName>(&namespace_decl));
-      break;
-    }
-    case DeclarationKind::InterfaceDeclaration:
-    case DeclarationKind::ConstraintDeclaration: {
-      auto& iface_decl = cast<ConstraintTypeDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(
-          DeclareConstraintTypeDeclaration(&iface_decl, scope_info));
-      break;
-    }
-    case DeclarationKind::ImplDeclaration: {
-      auto& impl_decl = cast<ImplDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareImplDeclaration(
-          &impl_decl, scope_info, /*is_template_instantiation=*/false));
-      break;
-    }
-    case DeclarationKind::MatchFirstDeclaration: {
-      for (auto* impl : cast<MatchFirstDeclaration>(d)->impl_declarations()) {
-        CARBON_RETURN_IF_ERROR(DeclareImplDeclaration(
-            impl, scope_info, /*is_template_instantiation=*/false));
-      }
-      break;
-    }
-    case DeclarationKind::FunctionDeclaration: {
-      auto& func_def = cast<CallableDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareCallableDeclaration(&func_def, scope_info));
-      break;
-    }
-    case DeclarationKind::DestructorDeclaration: {
-      auto& destructor_def = cast<CallableDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(
-          DeclareCallableDeclaration(&destructor_def, scope_info));
-      break;
-    }
-    case DeclarationKind::ClassDeclaration: {
-      auto& class_decl = cast<ClassDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareClassDeclaration(&class_decl, scope_info));
-      break;
-    }
-    case DeclarationKind::ExtendBaseDeclaration: {
-      // Handled in DeclareClassDeclaration.
-      break;
-    }
-    case DeclarationKind::MixinDeclaration: {
-      auto& mixin_decl = cast<MixinDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareMixinDeclaration(&mixin_decl, scope_info));
-      break;
-    }
-    case DeclarationKind::MixDeclaration: {
-      auto& mix_decl = cast<MixDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&mix_decl.mixin(), *scope_info.innermost_scope));
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> mixin,
-                              InterpExp(&mix_decl.mixin()));
-      if (const auto* mixin_value = dyn_cast<MixinPseudoType>(mixin)) {
-        mix_decl.set_mixin_value(mixin_value);
-      } else {
-        return ProgramError(mix_decl.source_loc())
-               << "Not a valid mixin: `" << mix_decl.mixin() << "`";
-      }
-      const auto& mixin_decl = mix_decl.mixin_value().declaration();
-      if (!mixin_decl.is_declared()) {
-        return ProgramError(mix_decl.source_loc())
-               << "incomplete mixin `" << mixin_decl.name()
-               << "` used in mix declaration";
-      }
-      break;
-    }
-    case DeclarationKind::ChoiceDeclaration: {
-      auto& choice = cast<ChoiceDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareChoiceDeclaration(&choice, scope_info));
-      break;
-    }
-
-    case DeclarationKind::VariableDeclaration: {
-      auto& var = cast<VariableDeclaration>(*d);
-      // Associate the variable name with it's declared type in the
-      // compile-time symbol table.
-      if (!isa<ExpressionPattern>(var.binding().type())) {
-        return ProgramError(var.binding().type().source_loc())
-               << "Expected expression for variable type";
-      }
-      CARBON_RETURN_IF_ERROR(TypeCheckPattern(
-          &var.binding(), PatternRequirements::Irrefutable, std::nullopt,
-          *scope_info.innermost_scope, var.expression_category()));
-      CARBON_RETURN_IF_ERROR(ExpectCompleteType(
-          var.source_loc(), "type of variable", &var.binding().static_type()));
-      CARBON_RETURN_IF_ERROR(
-          ExpectConcreteType(var.source_loc(), &var.binding().static_type()));
-      var.set_static_type(&var.binding().static_type());
-      break;
-    }
-
-    case DeclarationKind::InterfaceExtendDeclaration:
-    case DeclarationKind::InterfaceRequireDeclaration:
-    case DeclarationKind::AssociatedConstantDeclaration: {
-      // The semantic effects are handled by DeclareConstraintTypeDeclaration.
-      break;
-    }
-
-    case DeclarationKind::SelfDeclaration: {
-      CARBON_FATAL("Unreachable TypeChecker declare `Self` declaration");
-    }
-
-    case DeclarationKind::AliasDeclaration: {
-      auto& alias = cast<AliasDeclaration>(*d);
-      CARBON_RETURN_IF_ERROR(DeclareAliasDeclaration(&alias, scope_info));
-      break;
-    }
-  }
-  d->set_is_declared();
-  return Success();
-}
-
-auto TypeChecker::FindMemberWithParents(
-    std::string_view name, Nonnull<const NominalClassType*> enclosing_class)
-    -> ErrorOr<std::optional<
-        std::tuple<Nonnull<const Value*>, Nonnull<const Declaration*>,
-                   Nonnull<const NominalClassType*>>>> {
-  CARBON_ASSIGN_OR_RETURN(
-      const auto res,
-      FindMixedMemberAndType(name, enclosing_class->declaration().members(),
-                             enclosing_class));
-  if (res.has_value()) {
-    return {std::make_tuple(res->first, res->second, enclosing_class)};
-  }
-  if (const auto base = enclosing_class->base(); base.has_value()) {
-    return FindMemberWithParents(name, base.value());
-  }
-  return {std::nullopt};
-}
-
-auto TypeChecker::FindMixedMemberAndType(
-    const std::string_view& name, llvm::ArrayRef<Nonnull<Declaration*>> members,
-    const Nonnull<const Value*> enclosing_type)
-    -> ErrorOr<std::optional<
-        std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
-  for (Nonnull<const Declaration*> member : members) {
-    if (isa<MixDeclaration>(member)) {
-      const auto& mix_decl = cast<MixDeclaration>(*member);
-      Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
-      CARBON_ASSIGN_OR_RETURN(
-          const auto res,
-          FindMixedMemberAndType(name, mixin->declaration().members(), mixin));
-      if (res.has_value()) {
-        if (isa<NominalClassType>(enclosing_type)) {
-          Bindings temp_map;
-          // TODO: What is the type of Self? Do we ever need a witness?
-          temp_map.Add(mixin->declaration().self(), enclosing_type,
-                       std::nullopt);
-          CARBON_ASSIGN_OR_RETURN(const auto* const mix_member_type,
-                                  Substitute(temp_map, res.value().first));
-          return {std::make_pair(mix_member_type, res.value().second)};
-        } else {
-          return res;
-        }
-      }
-
-    } else if (std::optional<std::string_view> mem_name = GetName(*member);
-               mem_name.has_value()) {
-      if (*mem_name == name) {
-        return {std::make_pair(&member->static_type(), member)};
-      }
-    }
-  }
-
-  return {std::nullopt};
-}
-
-auto TypeChecker::CollectMember(Nonnull<const Declaration*> enclosing_decl,
-                                Nonnull<const Declaration*> member_decl)
-    -> ErrorOr<Success> {
-  CARBON_CHECK(isa<MixinDeclaration>(enclosing_decl) ||
-                   isa<ClassDeclaration>(enclosing_decl),
-               "Can't collect members for {0}", *enclosing_decl);
-  auto member_name = GetName(*member_decl);
-  if (!member_name.has_value()) {
-    // No need to collect members without a name
-    return Success();
-  }
-  auto encl_decl_name = GetName(*enclosing_decl);
-  CARBON_CHECK(encl_decl_name.has_value());
-  auto enclosing_decl_name = encl_decl_name.value();
-  auto enclosing_decl_loc = enclosing_decl->source_loc();
-  CollectedMembersMap& encl_members = FindCollectedMembers(enclosing_decl);
-  auto [it, inserted] = encl_members.insert({member_name.value(), member_decl});
-  if (!inserted) {
-    if (member_decl == it->second) {
-      return ProgramError(enclosing_decl_loc)
-             << "Member named " << member_name.value() << " (declared at "
-             << member_decl->source_loc() << ")"
-             << " is being mixed multiple times into " << enclosing_decl_name;
-    } else {
-      return ProgramError(enclosing_decl_loc)
-             << "Member named " << member_name.value() << " (declared at "
-             << member_decl->source_loc() << ") cannot be mixed into "
-             << enclosing_decl_name
-             << " because it clashes with an existing member"
-             << " with the same name (declared at " << it->second->source_loc()
-             << ")";
-    }
-  }
-  return Success();
-}
-
-auto TypeChecker::FindCollectedMembers(Nonnull<const Declaration*> decl)
-    -> CollectedMembersMap& {
-  switch (decl->kind()) {
-    case DeclarationKind::MixinDeclaration:
-    case DeclarationKind::ClassDeclaration: {
-      auto it = collected_members_.find(decl);
-      CARBON_CHECK(it != collected_members_.end());
-      return it->second;
-    }
-    default:
-      CARBON_FATAL("Can't collect members for {0}", *decl);
-  }
-}
-
-auto TypeChecker::InstantiateImplDeclaration(
-    Nonnull<const ImplDeclaration*> pattern,
-    Nonnull<const Bindings*> bindings) const
-    -> ErrorOr<Nonnull<const ImplWitness*>> {
-  CARBON_CHECK(IsTemplateSaturated(*bindings));
-
-  if (trace_stream_->is_enabled()) {
-    trace_stream_->Start() << "instantiating `" << PrintAsID(*pattern) << "` ("
-                           << pattern->source_loc() << ")\n";
-    *trace_stream_ << *bindings << "\n";
-  }
-
-  SetFileContext set_file_context(*trace_stream_, pattern->source_loc());
-
-  auto it = templates_.find(pattern);
-  CARBON_CHECK(it != templates_.end());
-  const TemplateInfo& info = it->second;
-
-  if (auto instantiation = info.instantiations.find(bindings);
-      instantiation != info.instantiations.end()) {
-    if (trace_stream_->is_enabled()) {
-      *trace_stream_ << "reusing cached instantiation\n";
-    }
-    return instantiation->second;
-  }
-
-  CloneContext context(arena_);
-  Nonnull<ImplDeclaration*> impl =
-      context.Clone(cast<ImplDeclaration>(info.pattern));
-
-  // Update the binding to store its instantiated value or a link back to the
-  // original generic parameter.
-  Bindings new_bindings;
-  for (auto [param, value] : bindings->args()) {
-    auto param_it = info.param_map.find(param);
-    CARBON_CHECK(param_it != info.param_map.end());
-
-    auto* clone = context.GetExistingClone(param_it->second);
-    switch (param->binding_kind()) {
-      case GenericBinding::BindingKind::Template: {
-        clone->set_template_value(value);
-        // TODO: Set a constant value on the impl binding too, if there is one.
-        break;
-      }
-
-      case GenericBinding::BindingKind::Checked: {
-        std::optional<Nonnull<const Value*>> witness;
-        if (auto impl = param->impl_binding()) {
-          auto it = bindings->witnesses().find(*impl);
-          CARBON_CHECK(it != bindings->witnesses().end(),
-                       "no witness for generic binding");
-          witness = it->second;
-        }
-        new_bindings.Add(clone, value, witness);
-        break;
-      }
-    }
-  }
-
-  // TODO: Make this method non-const and remove the const-cast here. The
-  // requirement to perform template instantiation unfortunately means that a
-  // lot of type-checking stops being free of side-effects, so this means
-  // removing `const` throughout most of the type-checker.
-  auto* type_checker = const_cast<TypeChecker*>(this);
-
-  // TODO: It's probably not correct to use the top-level impl scope here. It's
-  // not obvious what we should use, though -- which impls are in scope in
-  // template instantiation?
-  CARBON_CHECK(top_level_impl_scope_,
-               "can't perform template instantiation with no top-level scope");
-  ImplScope scope(*top_level_impl_scope_);
-
-  // Bring all impls from any checked generic bindings in the template
-  // arguments into scope.
-  //
-  // TODO: There shouldn't be any checked generic bindings in the template
-  // arguments by the time we come to perform an instantiation, but in order
-  // for that to work, we need to defer instantiating templates until we know
-  // the values of checked generic parameters, such as by performing
-  // monomorphization for checked generics (see #2153 for details). However,
-  // explorer doesn't yet support that.
-  //
-  // As a workaround for the lack of support for #2153, we can instantiate
-  // templates with the argument equal to a generic parameter. When we do so,
-  // the constraints on that generic parameter need to be in scope in the
-  // instantiation. This is imperfect: it misses constraints on the binding
-  // that come from anywhere other than its type.
-  for (auto [param, value] : bindings->args()) {
-    if (param->binding_kind() != GenericBinding::BindingKind::Template) {
-      continue;
-    }
-    VisitNestedValues(value, [&](Nonnull<const Value*> nested) -> bool {
-      if (auto* var_type = dyn_cast<VariableType>(nested)) {
-        if (auto impl_binding = var_type->binding().impl_binding()) {
-          type_checker->BringImplBindingIntoScope(*impl_binding, scope);
-        }
-      }
-      return true;
-    });
-  }
-
-  // Type-check the new impl.
-  //
-  // TODO: Augment any error we see here with an "instantiation failed" note
-  // pointing to the location where the instantiation was required.
-  CARBON_RETURN_IF_ERROR(type_checker->DeclareImplDeclaration(
-      impl, ScopeInfo::ForNonClassScope(&scope),
-      /*is_template_instantiation=*/true));
-  CARBON_RETURN_IF_ERROR(type_checker->TypeCheckImplDeclaration(impl, scope));
-
-  auto* result = arena_->New<ImplWitness>(
-      impl, arena_->New<Bindings>(std::move(new_bindings)));
-  CARBON_CHECK(info.instantiations.insert({bindings, result}).second);
-  return result;
-}
-
-auto TypeChecker::InterpExp(Nonnull<const Expression*> e)
-    -> ErrorOr<Nonnull<const Value*>> {
-  return Carbon::InterpExp(e, arena_, trace_stream_, print_stream_);
-}
-
-}  // namespace Carbon

+ 0 - 624
explorer/interpreter/type_checker.h

@@ -1,624 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_TYPE_CHECKER_H_
-#define CARBON_EXPLORER_INTERPRETER_TYPE_CHECKER_H_
-
-#include <optional>
-#include <set>
-#include <string_view>
-#include <tuple>
-#include <unordered_map>
-#include <vector>
-
-#include "common/error.h"
-#include "common/ostream.h"
-#include "explorer/ast/ast.h"
-#include "explorer/ast/expression.h"
-#include "explorer/ast/statement.h"
-#include "explorer/ast/value.h"
-#include "explorer/base/nonnull.h"
-#include "explorer/base/trace_stream.h"
-#include "explorer/interpreter/builtins.h"
-#include "explorer/interpreter/dictionary.h"
-#include "explorer/interpreter/impl_scope.h"
-#include "explorer/interpreter/interpreter.h"
-#include "explorer/interpreter/matching_impl_set.h"
-#include "explorer/interpreter/stack_space.h"
-#include "llvm/ADT/identity.h"
-
-namespace Carbon {
-
-using CollectedMembersMap =
-    std::unordered_map<std::string_view, Nonnull<const Declaration*>>;
-
-using GlobalMembersMap =
-    std::unordered_map<Nonnull<const Declaration*>, CollectedMembersMap>;
-
-class TypeChecker {
- public:
-  // Requirements for TypeCheckPattern.
-  enum class PatternRequirements {
-    None,
-
-    // The pattern must be fully irrefutable.
-    Irrefutable,
-
-    // A special case for types of bindings, which must be either `auto` or a
-    // valid expression.
-    // TODO: `auto` should be refactored from a pattern to an expression.
-    // Once that's done, this may be removable.
-    // https://github.com/carbon-language/carbon-lang/issues/2788
-    BindingType,
-  };
-
-  explicit TypeChecker(Nonnull<Arena*> arena,
-                       Nonnull<TraceStream*> trace_stream,
-                       Nonnull<llvm::raw_ostream*> print_stream)
-      : arena_(arena),
-        trace_stream_(trace_stream),
-        print_stream_(print_stream) {}
-
-  // Type-checks `ast` and sets properties such as `static_type`, as documented
-  // on the individual nodes.
-  // On failure, `ast` is left in a partial state and should not be further
-  // processed.
-  auto TypeCheck(AST& ast) -> ErrorOr<Success>;
-
-  // Construct a value that is the same as `value` except that occurrences
-  // of generic parameters (aka. `GenericBinding` and references to
-  // `ImplBinding`) are replaced by their corresponding value or witness in
-  // `bindings`.
-  auto Substitute(const Bindings& bindings, Nonnull<const Value*> value) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Same as `Substitute`, but cast the result to the type given as a template
-  // argument, which must be explicitly specified. The `remove_cv_t` here
-  // blocks template argument deduction.
-  template <typename T>
-  auto SubstituteCast(const Bindings& bindings,
-                      Nonnull<const std::remove_cv_t<T>*> value) const
-      -> ErrorOr<Nonnull<const T*>> {
-    CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> subst_value,
-                            Substitute(bindings, value));
-    return llvm::cast<T>(subst_value);
-  }
-
-  // Attempts to refine a witness that might be symbolic into an impl witness,
-  // using `impl` declarations that have been declared and type-checked so far.
-  // If a more precise witness cannot be found, returns `witness`.
-  auto RefineWitness(Nonnull<const Witness*> witness,
-                     Nonnull<const Value*> type,
-                     Nonnull<const Value*> constraint) const
-      -> ErrorOr<Nonnull<const Witness*>>;
-
-  // If `impl` can be an implementation of interface `iface` for the given
-  // `type`, then return the witness for this `impl`. Otherwise return
-  // std::nullopt.
-  auto MatchImpl(const InterfaceType& iface, Nonnull<const Value*> type,
-                 const ImplScope::ImplFact& impl, const ImplScope& impl_scope,
-                 SourceLocation source_loc) const
-      -> ErrorOr<std::optional<Nonnull<const Witness*>>>;
-
-  // Return the declaration of the member with the given name and the class type
-  // that owns it, from the class and its parents
-  auto FindMemberWithParents(std::string_view name,
-                             Nonnull<const NominalClassType*> enclosing_class)
-      -> ErrorOr<std::optional<
-          std::tuple<Nonnull<const Value*>, Nonnull<const Declaration*>,
-                     Nonnull<const NominalClassType*>>>>;
-
-  // Finds the direct or indirect member of a class or mixin by its name and
-  // returns the member's declaration and type. Indirect members are members of
-  // mixins that are mixed by member mix declarations. If the member is an
-  // indirect member from a mix declaration, then the Self type variable within
-  // the member's type is substituted with the type of the enclosing declaration
-  // containing the mix declaration.
-  auto FindMixedMemberAndType(const std::string_view& name,
-                              llvm::ArrayRef<Nonnull<Declaration*>> members,
-                              Nonnull<const Value*> enclosing_type)
-      -> ErrorOr<std::optional<
-          std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>>;
-
-  // Given the witnesses for the components of a constraint, form a witness for
-  // the constraint.
-  auto MakeConstraintWitness(
-      std::vector<Nonnull<const Witness*>> impls_constraint_witnesses) const
-      -> Nonnull<const Witness*>;
-
-  // Given the witnesses for the components of a constraint, form a witness for
-  // the constraint.
-  auto MakeConstraintWitnessAccess(Nonnull<const Witness*> witness,
-                                   int impl_offset) const
-      -> Nonnull<const Witness*>;
-
-  // Determine whether the given intrinsic constraint is known to be satisfied
-  // in the given scope.
-  auto IsIntrinsicConstraintSatisfied(SourceLocation source_loc,
-                                      const IntrinsicConstraint& constraint,
-                                      const ImplScope& impl_scope) const
-      -> ErrorOr<bool>;
-
- private:
-  class ConstraintTypeBuilder;
-  class SubstitutedGenericBindings;
-  class SubstituteTransform;
-  class ArgumentDeduction;
-
-  // Information about the currently enclosing scopes.
-  struct ScopeInfo {
-    static auto ForNonClassScope(Nonnull<ImplScope*> impl_scope) -> ScopeInfo {
-      return {.innermost_scope = impl_scope,
-              .innermost_non_class_scope = impl_scope,
-              .bindings = {}};
-    }
-
-    static auto ForClassScope(
-        const ScopeInfo& outer, Nonnull<ImplScope*> class_impl_scope,
-        std::vector<Nonnull<const GenericBinding*>> class_bindings)
-        -> ScopeInfo {
-      return {.innermost_scope = class_impl_scope,
-              .innermost_non_class_scope = outer.innermost_non_class_scope,
-              .bindings = std::move(class_bindings)};
-    }
-
-    // The innermost enclosing impl scope, within which impl declarations should
-    // be looked up.
-    Nonnull<ImplScope*> innermost_scope;
-    // The innermost enclosing non-class impl scope, where impl declarations
-    // should introduce new implementations.
-    Nonnull<ImplScope*> innermost_non_class_scope;
-    // The enclosing generic bindings, if any.
-    std::vector<Nonnull<const GenericBinding*>> bindings;
-  };
-
-  // Result from a lookup in a constraint.
-  struct ConstraintLookupResult {
-    Nonnull<const InterfaceType*> interface;
-    Nonnull<const Declaration*> member;
-  };
-
-  // Checks a member access that might be accessing a function taking `addr
-  // self: Self*`. If it does, this function marks the member access accordingly
-  // and ensures the object argument is a reference expression.
-  auto CheckAddrSelfAccess(Nonnull<MemberAccessExpression*> access,
-                           Nonnull<const FunctionDeclaration*> func_decl,
-                           const Bindings& bindings,
-                           const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Traverses the AST rooted at `e`, populating the static_type() of all nodes
-  // and ensuring they follow Carbon's typing rules.
-  //
-  // `values` maps variable names to their compile-time values. It is not
-  //    directly used in this function but is passed to InterpExp.
-  auto TypeCheckExp(Nonnull<Expression*> e, const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-  // For RunWithExtraStack.
-  auto TypeCheckExpImpl(Nonnull<Expression*> e, const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Type checks and interprets `type_expression`, and validates it represents a
-  // [concrete] type.
-  auto TypeCheckTypeExp(Nonnull<Expression*> type_expression,
-                        const ImplScope& impl_scope, bool concrete = true)
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Type checks and interprets `clause`, and validates it represents a valid
-  // `where` clause.
-  auto TypeCheckWhereClause(Nonnull<WhereClause*> clause,
-                            const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at `p`.
-  //
-  // `expected` is the type that this pattern is expected to have, if the
-  // surrounding context gives us that information. Otherwise, it is nullopt.
-  // Implicit conversions from `expected` to the pattern's type are permitted.
-  //
-  // `impl_scope` is extended with all implementations implied by the pattern.
-  auto TypeCheckPattern(Nonnull<Pattern*> p, PatternRequirements requirements,
-                        std::optional<Nonnull<const Value*>> expected,
-                        ImplScope& impl_scope,
-                        ExpressionCategory enclosing_expression_category)
-      -> ErrorOr<Success>;
-
-  // Type checks a generic binding. `symbolic_value` is the symbolic name by
-  // which this generic binding is known in its scope. `impl_scope` is updated
-  // with the impl implied by the binding, if any.
-  auto TypeCheckGenericBinding(GenericBinding& binding,
-                               std::string_view context, ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at `s`.
-  //
-  // REQUIRES: f.return_term().has_static_type() || f.return_term().is_auto(),
-  // where `f` is nearest enclosing FunctionDeclaration of `s`.
-  auto TypeCheckStmt(Nonnull<Statement*> s, const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Perform deduction for the deduced bindings in a function call, and set its
-  // lists of generic bindings and implementations.
-  //
-  // -   `params` is the list of parameters.
-  // -   `generic_params` indicates which parameters are generic parameters,
-  //     which require a constant argument.
-  // -   `deduced_bindings` is a list of the bindings that are expected to be
-  //     deduced by the call.
-  // -   `impl_bindings` is a list of the impl bindings that are expected to be
-  //     satisfied by the call.
-  auto DeduceCallBindings(
-      CallExpression& call, Nonnull<const Value*> params,
-      llvm::ArrayRef<FunctionType::GenericParameter> generic_params,
-      llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-      const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Establish the `static_type` and `constant_value` of the
-  // declaration and all of its nested declarations. This involves the
-  // compile-time interpretation of any type expressions in the
-  // declaration. It does not involve type checking statements and
-  // (runtime) expressions, as in the body of a function or a method.
-  // Dispatches to one of the following functions.
-  auto DeclareDeclaration(Nonnull<Declaration*> d, const ScopeInfo& scope_info)
-      -> ErrorOr<Success>;
-
-  auto DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
-                                  const ScopeInfo& scope_info)
-      -> ErrorOr<Success>;
-
-  auto DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
-                               const ScopeInfo& scope_info) -> ErrorOr<Success>;
-
-  auto DeclareMixinDeclaration(Nonnull<MixinDeclaration*> mixin_decl,
-                               const ScopeInfo& scope_info) -> ErrorOr<Success>;
-
-  auto DeclareConstraintTypeDeclaration(
-      Nonnull<ConstraintTypeDeclaration*> constraint_decl,
-      const ScopeInfo& scope_info) -> ErrorOr<Success>;
-
-  // Check that the deduced parameters of an impl are actually deducible from
-  // the form of the interface, for a declaration of the form
-  // `impl forall [deduced_bindings] impl_type as impl_iface`.
-  auto CheckImplIsDeducible(
-      SourceLocation source_loc, Nonnull<const Value*> impl_type,
-      Nonnull<const InterfaceType*> impl_iface,
-      llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-      const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Check that each required declaration in an implementation of the given
-  // interface is present in the given `impl`.
-  auto CheckImplIsComplete(Nonnull<const InterfaceType*> iface_type,
-                           Nonnull<const ImplDeclaration*> impl_decl,
-                           Nonnull<const Value*> self_type,
-                           Nonnull<const Witness*> self_witness,
-                           Nonnull<const Witness*> iface_witness,
-                           const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Check that an `impl` declaration satisfies its constraints and add the
-  // corresponding `ImplBinding`s to the impl scope.
-  auto CheckAndAddImplBindings(
-      Nonnull<const ImplDeclaration*> impl_decl,
-      Nonnull<const Value*> impl_type, Nonnull<const Witness*> self_witness,
-      Nonnull<const Witness*> impl_witness,
-      llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced_bindings,
-      const ScopeInfo& scope_info) -> ErrorOr<Success>;
-
-  auto DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
-                              const ScopeInfo& scope_info,
-                              bool is_template_instantiation)
-      -> ErrorOr<Success>;
-
-  auto DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
-                                const ScopeInfo& scope_info)
-      -> ErrorOr<Success>;
-  auto DeclareAliasDeclaration(Nonnull<AliasDeclaration*> alias,
-                               const ScopeInfo& scope_info) -> ErrorOr<Success>;
-
-  // Find all of the GenericBindings in the given pattern, and assign them
-  // index numbers based on their position in the resulting vector of bindings.
-  void CollectAndNumberGenericBindingsInPattern(
-      Nonnull<Pattern*> p,
-      std::vector<Nonnull<const GenericBinding*>>& generic_bindings);
-
-  // Find all of the ImplBindings in the given pattern. The pattern is required
-  // to have already been type-checked.
-  void CollectImplBindingsInPattern(
-      Nonnull<const Pattern*> p,
-      std::vector<Nonnull<const ImplBinding*>>& impl_bindings);
-
-  // Add the impl bindings from the pattern into the given `impl_scope`.
-  void BringPatternImplBindingsIntoScope(Nonnull<const Pattern*> p,
-                                         ImplScope& impl_scope);
-
-  // Create a witness for the given `impl` binding.
-  auto CreateImplBindingWitness(Nonnull<const ImplBinding*> impl_binding)
-      -> Nonnull<const Witness*>;
-
-  // Add the given ImplBinding to the given `impl_scope`.
-  void BringImplBindingIntoScope(Nonnull<const ImplBinding*> impl_binding,
-                                 ImplScope& impl_scope);
-
-  // Add all of the `impl_bindings` into the `scope`.
-  void BringImplBindingsIntoScope(
-      llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
-      ImplScope& scope);
-
-  // Checks the statements and (runtime) expressions within the
-  // declaration, such as the body of a function.
-  // Dispatches to one of the following functions.
-  // Assumes that DeclareDeclaration has already been invoked on `d`.
-  auto TypeCheckDeclaration(
-      Nonnull<Declaration*> d, const ImplScope& impl_scope,
-      std::optional<Nonnull<const Declaration*>> enclosing_decl)
-      -> ErrorOr<Success>;
-
-  // Type check the body of the function.
-  auto TypeCheckCallableDeclaration(Nonnull<CallableDeclaration*> f,
-                                    const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Type check all the members of the class.
-  auto TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
-                                 const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Type check all the members of the mixin.
-  auto TypeCheckMixinDeclaration(Nonnull<const MixinDeclaration*> mixin_decl,
-                                 const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  auto TypeCheckMixDeclaration(
-      Nonnull<MixDeclaration*> mix_decl, const ImplScope& impl_scope,
-      std::optional<Nonnull<const Declaration*>> enclosing_decl)
-      -> ErrorOr<Success>;
-
-  // Type check all the members of the interface or named constraint.
-  auto TypeCheckConstraintTypeDeclaration(
-      Nonnull<ConstraintTypeDeclaration*> constraint_decl,
-      const ImplScope& impl_scope) -> ErrorOr<Success>;
-
-  // Bring the associated constants in `constraint` that constrain the
-  // implementation of `interface` for `self` into `scope`.
-  void BringAssociatedConstantsIntoScope(
-      Nonnull<const ConstraintType*> constraint, Nonnull<const Value*> self,
-      Nonnull<const InterfaceType*> interface, ImplScope& scope);
-
-  // Type check all the members of the implementation.
-  auto TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
-                                const ImplScope& enclosing_scope)
-      -> ErrorOr<Success>;
-
-  // This currently does nothing, but perhaps that will change in the future.
-  auto TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
-                                  const ImplScope& impl_scope)
-      -> ErrorOr<Success>;
-
-  // Verifies that opt_stmt holds a statement, and it is structurally impossible
-  // for control flow to leave that statement except via a `return`.
-  auto ExpectReturnOnAllPaths(std::optional<Nonnull<Statement*>> opt_stmt,
-                              SourceLocation source_loc) -> ErrorOr<Success>;
-
-  // Returns the field names of the class together with their types.
-  auto FieldTypes(SourceLocation source_loc, std::string_view context,
-                  const NominalClassType& class_type) const
-      -> ErrorOr<std::vector<NamedValue>>;
-
-  // Returns the field names and types of the class and its parents.
-  auto FieldTypesWithBase(SourceLocation source_loc, std::string_view context,
-                          const NominalClassType& class_type) const
-      -> ErrorOr<std::vector<NamedValue>>;
-
-  // Returns true if *source is implicitly convertible to *destination. *source
-  // and *destination must be concrete types.
-  //
-  // If allow_user_defined_conversions, conversions requiring a user-defined
-  // `ImplicitAs` implementation are not considered, and only builtin
-  // conversions will be allowed.
-  auto IsImplicitlyConvertible(SourceLocation source_loc,
-                               Nonnull<const Value*> source,
-                               Nonnull<const Value*> destination,
-                               const ImplScope& impl_scope,
-                               bool allow_user_defined_conversions) const
-      -> ErrorOr<bool>;
-
-  // Returns true if the conversion from `source` to `destination` is a builtin
-  // conversion that `BuildBuiltinConversion` can perform.
-  auto IsBuiltinConversion(SourceLocation source_loc,
-                           Nonnull<const Value*> source,
-                           Nonnull<const Value*> destination,
-                           const ImplScope& impl_scope,
-                           bool allow_user_defined_conversions) const
-      -> ErrorOr<bool>;
-
-  // Attempt to implicitly convert type-checked expression `source` to the type
-  // `destination`.
-  auto ImplicitlyConvert(std::string_view context, const ImplScope& impl_scope,
-                         Nonnull<Expression*> source,
-                         Nonnull<const Value*> destination)
-      -> ErrorOr<Nonnull<Expression*>>;
-
-  // Checks that the given type is not a placeholder type. Diagnoses otherwise.
-  auto ExpectNonPlaceholderType(SourceLocation source_loc,
-                                Nonnull<const Value*> type) -> ErrorOr<Success>;
-
-  // Build and return class subtyping conversion expression, converting from
-  // `src_ptr` to `dest_ptr`.
-  auto BuildSubtypeConversion(Nonnull<Expression*> source,
-                              Nonnull<const PointerType*> src_ptr,
-                              Nonnull<const PointerType*> dest_ptr)
-      -> ErrorOr<Nonnull<Expression*>>;
-
-  // Build a builtin conversion of `source` to the type `destination`, if
-  // possible.
-  auto BuildBuiltinConversion(Nonnull<Expression*> source,
-                              Nonnull<const Value*> destination,
-                              const ImplScope& impl_scope)
-      -> ErrorOr<Nonnull<Expression*>>;
-
-  // Determine whether `type1` and `type2` are considered to be the same type
-  // in the given scope. This is true if they're structurally identical or if
-  // there is an equality relation in scope that specifies that they are the
-  // same.
-  auto IsSameType(Nonnull<const Value*> type1, Nonnull<const Value*> type2,
-                  const ImplScope& impl_scope) const -> bool;
-
-  // Check whether `actual` is the same type as `expected` and halt with a
-  // fatal compilation error if it is not.
-  auto ExpectExactType(SourceLocation source_loc, std::string_view context,
-                       Nonnull<const Value*> expected,
-                       Nonnull<const Value*> actual,
-                       const ImplScope& impl_scope) const -> ErrorOr<Success>;
-
-  // Rebuild a value in the current type-checking context. Applies any rewrites
-  // that are in scope and attempts to resolve associated constants using
-  // implementations that have been declared since the value was formed.
-  auto RebuildValue(Nonnull<const Value*> value) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // Implementation of Substitute and RebuildValue. Does not check that
-  // bindings are nonempty, nor does it trace its progress.
-  auto SubstituteImpl(const Bindings& bindings,
-                      Nonnull<const Value*> type) const
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  // The name of a builtin interface, with any arguments.
-  struct BuiltinInterfaceName {
-    Builtin builtin;
-    llvm::ArrayRef<Nonnull<const Value*>> arguments = {};
-  };
-  // The name of a method on a builtin interface, with any arguments.
-  struct BuiltinMethodCall {
-    const std::string& name;
-    llvm::ArrayRef<Nonnull<Expression*>> arguments = {};
-  };
-
-  // Form a builtin method call. Ensures that the type of `source` implements
-  // the interface `interface`, which should be defined in the prelude, and
-  // forms a call to the method `method` on that interface.
-  auto BuildBuiltinMethodCall(const ImplScope& impl_scope,
-                              Nonnull<Expression*> source,
-                              BuiltinInterfaceName interface,
-                              BuiltinMethodCall method)
-      -> ErrorOr<Nonnull<Expression*>>;
-
-  // Get a type for a builtin interface.
-  auto GetBuiltinInterfaceType(SourceLocation source_loc,
-                               BuiltinInterfaceName interface) const
-      -> ErrorOr<Nonnull<const InterfaceType*>>;
-
-  // Convert a value that is expected to represent a constraint into a
-  // `ConstraintType`.
-  auto ConvertToConstraintType(SourceLocation source_loc,
-                               std::string_view context,
-                               Nonnull<const Value*> constraint) const
-      -> ErrorOr<Nonnull<const ConstraintType*>>;
-
-  // Given a list of constraint types, form the combined constraint.
-  auto CombineConstraints(
-      SourceLocation source_loc,
-      llvm::ArrayRef<Nonnull<const ConstraintType*>> constraints)
-      -> ErrorOr<Nonnull<const ConstraintType*>>;
-
-  // Gets the type for the given associated constant.
-  auto GetTypeForAssociatedConstant(Nonnull<const AssociatedConstant*> assoc)
-      const -> ErrorOr<Nonnull<const Value*>>;
-
-  // Look up a member name in a constraint, which might be a single interface or
-  // a compound constraint.
-  auto LookupInConstraint(SourceLocation source_loc,
-                          std::string_view lookup_kind,
-                          Nonnull<const Value*> type,
-                          std::string_view member_name)
-      -> ErrorOr<ConstraintLookupResult>;
-
-  // Given `type.(interface.member)`, look for a rewrite in the declared type
-  // of `type`.
-  auto LookupRewriteInTypeOf(Nonnull<const Value*> type,
-                             Nonnull<const InterfaceType*> interface,
-                             Nonnull<const Declaration*> member) const
-      -> ErrorOr<std::optional<const RewriteConstraint*>>;
-
-  // Given a witness value, look for a rewrite for the given associated
-  // constant.
-  auto LookupRewriteInWitness(Nonnull<const Witness*> witness,
-                              Nonnull<const InterfaceType*> interface,
-                              Nonnull<const Declaration*> member) const
-      -> ErrorOr<std::optional<const RewriteConstraint*>>;
-
-  // Adds a member of a declaration to collected_members_
-  auto CollectMember(Nonnull<const Declaration*> enclosing_decl,
-                     Nonnull<const Declaration*> member_decl)
-      -> ErrorOr<Success>;
-
-  // Fetches all direct and indirect members of a class or mixin declaration
-  // stored within collected_members_
-  auto FindCollectedMembers(Nonnull<const Declaration*> decl)
-      -> CollectedMembersMap&;
-
-  // Instantiate an impl with the given set of bindings, including one or more
-  // template bindings.
-  auto InstantiateImplDeclaration(Nonnull<const ImplDeclaration*> pattern,
-                                  Nonnull<const Bindings*> bindings) const
-      -> ErrorOr<Nonnull<const ImplWitness*>>;
-
-  // Wraps the interpreter's InterpExp, forwarding TypeChecker members as
-  // arguments.
-  auto InterpExp(Nonnull<const Expression*> e)
-      -> ErrorOr<Nonnull<const Value*>>;
-
-  Nonnull<Arena*> arena_;
-  Builtins builtins_;
-
-  // Maps a mixin/class declaration to all of its direct and indirect members.
-  GlobalMembersMap collected_members_;
-
-  Nonnull<TraceStream*> trace_stream_;
-
-  // The stream for the Print intrinsic.
-  Nonnull<llvm::raw_ostream*> print_stream_;
-
-  // The top-level ImplScope, containing `impl` declarations that should be
-  // usable from any context. This is used when we want to try to refine a
-  // symbolic witness into an impl witness during substitution.
-  std::optional<const ImplScope*> top_level_impl_scope_;
-
-  // Constraint types that are currently being resolved. These may have
-  // rewrites that are not yet visible in any type.
-  std::vector<ConstraintTypeBuilder*> partial_constraint_types_;
-
-  // A set of implementations we're currently matching.
-  // TODO: This is `mutable` because `MatchImpl` is `const`. We need to remove
-  // the `const`s from everywhere that transitively does `impl` matching to get
-  // rid of this `mutable`.
-  mutable MatchingImplSet matching_impl_set_;
-
-  // Information about a generic that has one or more template parameters.
-  struct TemplateInfo {
-    // The original pattern, prior to any type-checking.
-    Nonnull<const Declaration*> pattern;
-    // A mapping from the bindings of the type-checked pattern to the bindings
-    // of the original.
-    std::map<const GenericBinding*, const GenericBinding*> param_map;
-
-    // Comparator for pointers to Bindings.
-    struct BindingPtrCompare {
-      auto operator()(Nonnull<const Bindings*> lhs,
-                      Nonnull<const Bindings*> rhs) const {
-        return std::tie(lhs->args(), lhs->witnesses()) <
-               std::tie(rhs->args(), rhs->witnesses());
-      }
-    };
-
-    // Cache of instantiations of this template.
-    mutable std::map<Nonnull<const Bindings*>, Nonnull<const ImplWitness*>,
-                     BindingPtrCompare>
-        instantiations;
-  };
-
-  // Map from template declarations to extra information we use to type-check
-  // and instantiate the template.
-  std::map<const Declaration*, TemplateInfo> templates_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_TYPE_CHECKER_H_

+ 0 - 143
explorer/interpreter/type_structure.cpp

@@ -1,143 +0,0 @@
-// 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 "explorer/interpreter/type_structure.h"
-
-#include <limits>
-
-#include "explorer/ast/declaration.h"
-#include "explorer/ast/expression_category.h"
-#include "explorer/ast/value.h"
-#include "llvm/ADT/StringExtras.h"
-
-namespace Carbon {
-
-namespace {
-struct TypeStructureBuilder {
-  // Visit a child of the current value at the given index.
-  template <typename T>
-  void VisitChild(int index, const T& child) {
-    path.push_back(index);
-    Visit(child);
-    path.pop_back();
-  }
-
-  // Visit an instance of a type derived from Value. By default we do this by
-  // decomposing the value and walking each piece in turn.
-  template <typename T>
-  void VisitValue(Nonnull<const T*> value) {
-    value->Decompose([&](const auto&... parts) {
-      int inner_index = 0;
-      (VisitChild(inner_index++, parts), ...);
-    });
-  }
-
-  // A variable type is a hole.
-  void VisitValue(Nonnull<const VariableType*>) { AddHole(); }
-
-  // Visit a value by visiting its derived type.
-  void Visit(Nonnull<const Value*> value) {
-    value->Visit<void>([&](auto* derived_value) { VisitValue(derived_value); });
-  }
-
-  // Visit all of the arguments in a list of bindings.
-  void Visit(Nonnull<const Bindings*> bindings) {
-    // Reconstruct the lexical ordering of the parameters.
-    // TODO: Store bindings as an array indexed by binding index, not as a map.
-    std::vector<Nonnull<const GenericBinding*>> params;
-    for (auto [param, value] : bindings->args()) {
-      params.push_back(param);
-    }
-    std::sort(params.begin(), params.end(), [](auto* param_1, auto* param_2) {
-      return param_1->index() < param_2->index();
-    });
-
-    for (int i = 0; i != static_cast<int>(params.size()); ++i) {
-      VisitChild(i, bindings->args().find(params[i])->second);
-    }
-  }
-
-  template <typename T>
-  void Visit(const std::optional<T>& opt) {
-    if (opt) {
-      Visit(*opt);
-    }
-  }
-
-  template <typename T>
-  void Visit(const std::vector<T>& seq) {
-    for (int i = 0; i != static_cast<int>(seq.size()); ++i) {
-      VisitChild(i, seq[i]);
-    }
-  }
-
-  void Visit(const NamedValue& value) { Visit(value.value); }
-
-  // Ignore values that can't contain holes.
-  void Visit(int) {}
-  void Visit(std::string_view) {}
-  void Visit(ExpressionCategory) {}
-  void Visit(Nonnull<const AstNode*>) {}
-  void Visit(const ValueNodeView&) {}
-  void Visit(const Address&) {}
-  void Visit(const VTable*) {}
-  void Visit(const FunctionType::GenericParameter&) {}
-  void Visit(const FunctionType::MethodSelf&) {}
-  void Visit(const NamedElement*) {}
-
-  // Constraint types can contain mentions of VariableTypes, but they aren't
-  // deducible so it's not important to look for them.
-  void Visit(const ImplsConstraint&) {}
-  void Visit(const IntrinsicConstraint&) {}
-  void Visit(const EqualityConstraint&) {}
-  void Visit(const RewriteConstraint&) {}
-  void Visit(const LookupContext&) {}
-
-  // TODO: Find a way to remove the derived-most pointer from NominalClassValue.
-  void Visit(Nonnull<const NominalClassValue**>) {}
-
-  void AddHole() {
-    if (!result.empty()) {
-      result.push_back(-1);
-    }
-    result.insert(result.end(), path.begin(), path.end());
-  }
-
-  std::vector<int> path;
-  std::vector<int> result;
-};
-}  // namespace
-
-auto TypeStructureSortKey::ForImpl(Nonnull<const Value*> type,
-                                   Nonnull<const Value*> interface)
-    -> TypeStructureSortKey {
-  TypeStructureBuilder builder;
-  builder.VisitChild(0, type);
-  builder.VisitChild(1, interface);
-
-  TypeStructureSortKey result;
-  result.holes_ = std::move(builder.result);
-  result.holes_.push_back(std::numeric_limits<int>::max());
-  return result;
-}
-
-void TypeStructureSortKey::Print(llvm::raw_ostream& out) const {
-  out << "[";
-  llvm::ListSeparator sep;
-  for (int i : holes_) {
-    if (i == -1) {
-      out << "; ";
-      // Reinitialize `sep` to suppress the next separator.
-      sep = llvm::ListSeparator();
-    } else if (i == std::numeric_limits<int>::max()) {
-      out << "]";
-    } else {
-      out << sep << i;
-    }
-  }
-}
-
-void TypeStructureSortKey::Dump() const { Print(llvm::errs()); }
-
-}  // namespace Carbon

+ 0 - 85
explorer/interpreter/type_structure.h

@@ -1,85 +0,0 @@
-// 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
-
-#ifndef CARBON_EXPLORER_INTERPRETER_TYPE_STRUCTURE_H_
-#define CARBON_EXPLORER_INTERPRETER_TYPE_STRUCTURE_H_
-
-#include <vector>
-
-#include "common/ostream.h"
-#include "explorer/base/nonnull.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-class Value;
-
-// A type structure sort key represents the information needed to order `impl`
-// declarations by their type structures.
-//
-// The type structure for an `impl` declaration is the `type as interface`
-// portion, with all references to the enclosing generic parameters replaced by
-// `?`s. Two type structures that match the same type and interface are ordered
-// based on the lexical position of the first `?` that is in one but not the
-// other. The type structure with the earlier `?` has the worse match.
-//
-// This class extracts the relevant information needed to order type
-// structures, and provides a weak ordering over type structures in which type
-// structures that provide a better match compare earlier.
-class TypeStructureSortKey : public Printable<TypeStructureSortKey> {
- public:
-  // Compute the sort key for `impl type as interface`.
-  static auto ForImpl(Nonnull<const Value*> type,
-                      Nonnull<const Value*> interface) -> TypeStructureSortKey;
-
-  // Order by sort key. Smaller keys represent better matches.
-  friend bool operator<(const TypeStructureSortKey& a,
-                        const TypeStructureSortKey& b) {
-    return a.holes_ > b.holes_;
-  }
-  friend bool operator<=(const TypeStructureSortKey& a,
-                         const TypeStructureSortKey& b) {
-    return a.holes_ >= b.holes_;
-  }
-  friend bool operator>(const TypeStructureSortKey& a,
-                        const TypeStructureSortKey& b) {
-    return a.holes_ < b.holes_;
-  }
-  friend bool operator>=(const TypeStructureSortKey& a,
-                         const TypeStructureSortKey& b) {
-    return a.holes_ <= b.holes_;
-  }
-
-  // Determine whether two sort keys are in the same equivalence class. If so,
-  // the sort keys represent type structures with `?`s in the same positions.
-  // This does not imply that the remainder of the type structure is the same.
-  // For example, the sort key for `Optional(?) as Hash` is the same as the
-  // sort key for `Vector(?) as Ordered`.
-  friend bool operator==(const TypeStructureSortKey& a,
-                         const TypeStructureSortKey& b) {
-    return a.holes_ == b.holes_;
-  }
-  friend bool operator!=(const TypeStructureSortKey& a,
-                         const TypeStructureSortKey& b) {
-    return a.holes_ != b.holes_;
-  }
-
-  void Print(llvm::raw_ostream& out) const;
-
-  LLVM_DUMP_METHOD void Dump() const;
-
- private:
-  // Positions of holes (`?`s) in the structure. Each hole is described as a
-  // path of indexes from the root of the type tree to the position of the `?`.
-  // Holes are listed in appearance order, separated by -1s, and the vector is
-  // terminated by std::numeric_limits<int>::max().
-  //
-  // This representation is chosen so that better matches are lexicographically
-  // larger than worse matches.
-  std::vector<int> holes_;
-};
-
-}  // namespace Carbon
-
-#endif  // CARBON_EXPLORER_INTERPRETER_TYPE_STRUCTURE_H_

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików