Просмотр исходного кода

Add type_utils file (#2926)

Prerequisite for #2909. Moves some type-related functions to the
separate file.
kshokhin 2 лет назад
Родитель
Сommit
d34b3e02c2

+ 38 - 0
explorer/interpreter/BUILD

@@ -117,7 +117,9 @@ cc_library(
         ":action",
         ":action_stack",
         ":heap",
+        ":pattern_match",
         ":stack",
+        ":type_utils",
         "//common:check",
         "//common:error",
         "//common:ostream",
@@ -205,8 +207,10 @@ cc_library(
         ":dictionary",
         ":interpreter",
         ":pattern_analysis",
+        ":pattern_match",
         ":stack_space",
         ":type_structure",
+        ":type_utils",
         "//common:check",
         "//common:enum_base",
         "//common:error",
@@ -268,3 +272,37 @@ cc_library(
         "@llvm-project//llvm:Support",
     ],
 )
+
+cc_library(
+    name = "type_utils",
+    srcs = [
+        "type_utils.cpp",
+    ],
+    hdrs = [
+        "type_utils.h",
+    ],
+    deps = [
+        "//explorer/ast",
+        "//explorer/common:nonnull",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_library(
+    name = "pattern_match",
+    srcs = [
+        "pattern_match.cpp",
+    ],
+    hdrs = [
+        "pattern_match.h",
+    ],
+    deps = [
+        ":action",
+        "//explorer/ast",
+        "//explorer/common:arena",
+        "//explorer/common:nonnull",
+        "//explorer/common:source_location",
+        "//explorer/common:trace_stream",
+        "@llvm-project//llvm:Support",
+    ],
+)

+ 2 - 203
explorer/interpreter/interpreter.cpp

@@ -28,7 +28,9 @@
 #include "explorer/common/trace_stream.h"
 #include "explorer/interpreter/action.h"
 #include "explorer/interpreter/action_stack.h"
+#include "explorer/interpreter/pattern_match.h"
 #include "explorer/interpreter/stack.h"
+#include "explorer/interpreter/type_utils.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
@@ -297,209 +299,6 @@ auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
   return arena_->New<StructValue>(std::move(elements));
 }
 
-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 pattern " << *p << "\nfrom "
-                  << 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 "
-                         << *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 "
-                         << *v.value();
-      }
-    case Value::Kind::UninitializedValue:
-      CARBON_FATAL() << "uninitialized value is not allowed in pattern "
-                     << *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: {
-      switch (v.value()->kind()) {
-        case Value::Kind::TupleType:
-        case Value::Kind::TupleValue: {
-          return true;
-        }
-        case Value::Kind::StaticArrayType: {
-          const auto& v_arr = cast<StaticArrayType>(*v.value());
-          return v_arr.has_size();
-        }
-        default:
-          return false;
-      }
-    }
-    default:
-      return ValueEqual(p, v.value(), std::nullopt);
-  }
-}
-
 auto Interpreter::StepLocation() -> ErrorOr<Success> {
   Action& act = todo_.CurrentAction();
   const Expression& exp = cast<LocationAction>(act).expression();

+ 0 - 16
explorer/interpreter/interpreter.h

@@ -36,22 +36,6 @@ auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
                Nonnull<llvm::raw_ostream*> print_stream)
     -> ErrorOr<Nonnull<const Value*>>;
 
-// 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`.
-// TODO: consider moving this to a separate header.
-[[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_INTERPRETER_H_

+ 220 - 0
explorer/interpreter/pattern_match.cpp

@@ -0,0 +1,220 @@
+// 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 "explorer/ast/value.h"
+#include "explorer/common/arena.h"
+#include "explorer/common/trace_stream.h"
+#include "explorer/interpreter/action.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 pattern " << *p << "\nfrom "
+                  << 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 "
+                         << *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 "
+                         << *v.value();
+      }
+    case Value::Kind::UninitializedValue:
+      CARBON_FATAL() << "uninitialized value is not allowed in pattern "
+                     << *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: {
+      switch (v.value()->kind()) {
+        case Value::Kind::TupleType:
+        case Value::Kind::TupleValue: {
+          return true;
+        }
+        case Value::Kind::StaticArrayType: {
+          const auto& v_arr = cast<StaticArrayType>(*v.value());
+          return v_arr.has_size();
+        }
+        default:
+          return false;
+      }
+    }
+    default:
+      return ValueEqual(p, v.value(), std::nullopt);
+  }
+}
+}  // namespace Carbon

+ 37 - 0
explorer/interpreter/pattern_match.h

@@ -0,0 +1,37 @@
+// 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/common/nonnull.h"
+#include "explorer/common/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_

+ 2 - 222
explorer/interpreter/type_checker.cpp

@@ -32,7 +32,9 @@
 #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/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -81,134 +83,6 @@ static auto ExpectPointerType(SourceLocation source_loc,
   return Success();
 }
 
-// Returns whether the value is a type whose values are themselves known to be
-// types.
-static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
-  switch (value->kind()) {
-    case Value::Kind::IntValue:
-    case Value::Kind::DestructorValue:
-    case Value::Kind::FunctionValue:
-    case Value::Kind::BoundMethodValue:
-    case Value::Kind::PointerValue:
-    case Value::Kind::LocationValue:
-    case Value::Kind::ReferenceExpressionValue:
-    case Value::Kind::BoolValue:
-    case Value::Kind::TupleValue:
-    case Value::Kind::StructValue:
-    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:
-      // These are values, not types.
-      return false;
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::FunctionType:
-    case Value::Kind::PointerType:
-    case Value::Kind::StructType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::MixinPseudoType:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::StringType:
-    case Value::Kind::StaticArrayType:
-    case Value::Kind::TupleType:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName:
-      // These are types whose values are not types.
-      return false;
-    case Value::Kind::AutoType:
-    case Value::Kind::VariableType:
-    case Value::Kind::AssociatedConstant:
-      // A value of one of these types could be a type, but isn't known to be.
-      return false;
-    case Value::Kind::TypeType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-      // A value of one of these types is itself always a type.
-      return true;
-  }
-}
-
-// Returns whether the value is a type value, such as might be a valid type for
-// a syntactic pattern. This includes types involving `auto`. Use
-// `TypeContainsAuto` to determine if a type involves `auto`.
-static auto IsType(Nonnull<const Value*> value) -> bool {
-  switch (value->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::TupleValue:
-    case Value::Kind::StructValue:
-    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:
-      return false;
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::PointerType:
-    case Value::Kind::FunctionType:
-    case Value::Kind::StructType:
-    case Value::Kind::TupleType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::VariableType:
-    case Value::Kind::StringType:
-    case Value::Kind::StaticArrayType:
-    case Value::Kind::AutoType:
-      return true;
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfNamespaceName:
-      // These aren't first-class types, but they are still types.
-      return true;
-    case Value::Kind::AssociatedConstant: {
-      // An associated type is an associated constant whose type is a
-      // type-of-type.
-      const auto& assoc = cast<AssociatedConstant>(*value);
-      // TODO: Should we substitute in the arguments? Given
-      //   interface I(T:! type) { let V:! T; }
-      // ... is T.(I(type).V) considered to be a type?
-      return IsTypeOfType(&assoc.constant().static_type());
-    }
-    case Value::Kind::MixinPseudoType:
-      // Mixin type is a second-class type that cannot be used
-      // within a type annotation expression.
-      return false;
-  }
-}
-
 // Expect that a type is complete. Issue a diagnostic if not.
 static auto ExpectCompleteType(SourceLocation source_loc,
                                std::string_view context,
@@ -323,76 +197,6 @@ static auto ExpectConcreteType(SourceLocation source_loc,
   return Success();
 }
 
-// Returns whether *value represents the type of a Carbon value, as
-// opposed to a type pattern or a non-type value.
-static auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
-  CARBON_CHECK(IsType(type)) << "expected a type, but found " << *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::TupleValue:
-    case Value::Kind::StructValue:
-    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() << "non-type value";
-    case Value::Kind::IntType:
-    case Value::Kind::BoolType:
-    case Value::Kind::TypeType:
-    case Value::Kind::VariableType:
-    case Value::Kind::StringType:
-    case Value::Kind::TypeOfMixinPseudoType:
-    case Value::Kind::TypeOfParameterizedEntityName:
-    case Value::Kind::TypeOfMemberName:
-    case Value::Kind::TypeOfNamespaceName:
-      // These types do not contain other types.
-      return false;
-    case Value::Kind::FunctionType:
-    case Value::Kind::NominalClassType:
-    case Value::Kind::InterfaceType:
-    case Value::Kind::NamedConstraintType:
-    case Value::Kind::ConstraintType:
-    case Value::Kind::ChoiceType:
-    case Value::Kind::AssociatedConstant:
-      // These types can contain other types, but those types can't involve
-      // `auto`.
-      return false;
-    case Value::Kind::AutoType:
-      return true;
-    case Value::Kind::StructType:
-      return llvm::any_of(
-          llvm::map_range(cast<StructType>(type)->fields(),
-                          [](const NamedValue& v) { return v.value; }),
-          TypeIsDeduceable);
-    case Value::Kind::TupleType:
-      return llvm::any_of(cast<TupleType>(type)->elements(), TypeIsDeduceable);
-    case Value::Kind::PointerType:
-      return TypeIsDeduceable(&cast<PointerType>(type)->pointee_type());
-    case Value::Kind::StaticArrayType:
-      const auto* array_type = cast<StaticArrayType>(type);
-      return !array_type->has_size() ||
-             TypeIsDeduceable(&array_type->element_type());
-  }
-}
-
 // 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 {
@@ -401,12 +205,6 @@ static auto IsPlaceholderType(Nonnull<const Value*> type) -> bool {
              TypeOfMixinPseudoType, TypeOfNamespaceName>(type);
 }
 
-// Returns whether `value` is a concrete type, which would be valid as the
-// static type of an expression. This is currently any type other than `auto`.
-static auto IsNonDeduceableType(Nonnull<const Value*> value) -> bool {
-  return IsType(value) && !TypeIsDeduceable(value);
-}
-
 static auto ExpectResolvedBindingType(const BindingPattern& binding,
                                       Nonnull<const Value*> type)
     -> ErrorOr<Success> {
@@ -4459,24 +4257,6 @@ auto TypeChecker::TypeCheckWhereClause(Nonnull<WhereClause*> clause,
   }
 }
 
-// Returns the list size for type deduction.
-static auto GetSize(Nonnull<const Value*> from) -> size_t {
-  switch (from->kind()) {
-    case Value::Kind::TupleType:
-    case Value::Kind::TupleValue: {
-      const auto& from_tup = cast<TupleValueBase>(*from);
-      return from_tup.elements().size();
-    }
-    case Value::Kind::StaticArrayType: {
-      const auto& from_arr = cast<StaticArrayType>(*from);
-      CARBON_CHECK(from_arr.has_size());
-      return from_arr.size();
-    }
-    default:
-      return 0;
-  }
-}
-
 auto TypeChecker::TypeCheckPattern(
     Nonnull<Pattern*> p, PatternRequirements requirements,
     std::optional<Nonnull<const Value*>> expected, ImplScope& impl_scope,

+ 225 - 0
explorer/interpreter/type_utils.cpp

@@ -0,0 +1,225 @@
+// 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_utils.h"
+
+#include "explorer/ast/value.h"
+#include "llvm/Support/Casting.h"
+
+using llvm::cast;
+
+namespace Carbon {
+
+auto IsNonDeduceableType(Nonnull<const Value*> value) -> bool {
+  return IsType(value) && !TypeIsDeduceable(value);
+}
+
+auto IsType(Nonnull<const Value*> value) -> bool {
+  switch (value->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::TupleValue:
+    case Value::Kind::StructValue:
+    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:
+      return false;
+    case Value::Kind::IntType:
+    case Value::Kind::BoolType:
+    case Value::Kind::TypeType:
+    case Value::Kind::PointerType:
+    case Value::Kind::FunctionType:
+    case Value::Kind::StructType:
+    case Value::Kind::TupleType:
+    case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
+    case Value::Kind::NamedConstraintType:
+    case Value::Kind::ConstraintType:
+    case Value::Kind::ChoiceType:
+    case Value::Kind::VariableType:
+    case Value::Kind::StringType:
+    case Value::Kind::StaticArrayType:
+    case Value::Kind::AutoType:
+      return true;
+    case Value::Kind::TypeOfParameterizedEntityName:
+    case Value::Kind::TypeOfMemberName:
+    case Value::Kind::TypeOfMixinPseudoType:
+    case Value::Kind::TypeOfNamespaceName:
+      // These aren't first-class types, but they are still types.
+      return true;
+    case Value::Kind::AssociatedConstant: {
+      // An associated type is an associated constant whose type is a
+      // type-of-type.
+      const auto& assoc = cast<AssociatedConstant>(*value);
+      // TODO: Should we substitute in the arguments? Given
+      //   interface I(T:! type) { let V:! T; }
+      // ... is T.(I(type).V) considered to be a type?
+      return IsTypeOfType(&assoc.constant().static_type());
+    }
+    case Value::Kind::MixinPseudoType:
+      // Mixin type is a second-class type that cannot be used
+      // within a type annotation expression.
+      return false;
+  }
+}
+
+auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
+  CARBON_CHECK(IsType(type)) << "expected a type, but found " << *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::TupleValue:
+    case Value::Kind::StructValue:
+    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() << "non-type value";
+    case Value::Kind::IntType:
+    case Value::Kind::BoolType:
+    case Value::Kind::TypeType:
+    case Value::Kind::VariableType:
+    case Value::Kind::StringType:
+    case Value::Kind::TypeOfMixinPseudoType:
+    case Value::Kind::TypeOfParameterizedEntityName:
+    case Value::Kind::TypeOfMemberName:
+    case Value::Kind::TypeOfNamespaceName:
+      // These types do not contain other types.
+      return false;
+    case Value::Kind::FunctionType:
+    case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
+    case Value::Kind::NamedConstraintType:
+    case Value::Kind::ConstraintType:
+    case Value::Kind::ChoiceType:
+    case Value::Kind::AssociatedConstant:
+      // These types can contain other types, but those types can't involve
+      // `auto`.
+      return false;
+    case Value::Kind::AutoType:
+      return true;
+    case Value::Kind::StructType:
+      return llvm::any_of(
+          llvm::map_range(cast<StructType>(type)->fields(),
+                          [](const NamedValue& v) { return v.value; }),
+          TypeIsDeduceable);
+    case Value::Kind::TupleType:
+      return llvm::any_of(cast<TupleType>(type)->elements(), TypeIsDeduceable);
+    case Value::Kind::PointerType:
+      return TypeIsDeduceable(&cast<PointerType>(type)->pointee_type());
+    case Value::Kind::StaticArrayType:
+      const auto* array_type = cast<StaticArrayType>(type);
+      return !array_type->has_size() ||
+             TypeIsDeduceable(&array_type->element_type());
+  }
+}
+
+auto GetSize(Nonnull<const Value*> from) -> size_t {
+  switch (from->kind()) {
+    case Value::Kind::TupleType:
+    case Value::Kind::TupleValue: {
+      const auto& from_tup = cast<TupleValueBase>(*from);
+      return from_tup.elements().size();
+    }
+    case Value::Kind::StaticArrayType: {
+      const auto& from_arr = cast<StaticArrayType>(*from);
+      CARBON_CHECK(from_arr.has_size());
+      return from_arr.size();
+    }
+    default:
+      return 0;
+  }
+}
+
+auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
+  switch (value->kind()) {
+    case Value::Kind::IntValue:
+    case Value::Kind::DestructorValue:
+    case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
+    case Value::Kind::PointerValue:
+    case Value::Kind::LocationValue:
+    case Value::Kind::ReferenceExpressionValue:
+    case Value::Kind::BoolValue:
+    case Value::Kind::TupleValue:
+    case Value::Kind::StructValue:
+    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:
+      // These are values, not types.
+      return false;
+    case Value::Kind::IntType:
+    case Value::Kind::BoolType:
+    case Value::Kind::FunctionType:
+    case Value::Kind::PointerType:
+    case Value::Kind::StructType:
+    case Value::Kind::NominalClassType:
+    case Value::Kind::MixinPseudoType:
+    case Value::Kind::ChoiceType:
+    case Value::Kind::StringType:
+    case Value::Kind::StaticArrayType:
+    case Value::Kind::TupleType:
+    case Value::Kind::TypeOfMixinPseudoType:
+    case Value::Kind::TypeOfParameterizedEntityName:
+    case Value::Kind::TypeOfMemberName:
+    case Value::Kind::TypeOfNamespaceName:
+      // These are types whose values are not types.
+      return false;
+    case Value::Kind::AutoType:
+    case Value::Kind::VariableType:
+    case Value::Kind::AssociatedConstant:
+      // A value of one of these types could be a type, but isn't known to be.
+      return false;
+    case Value::Kind::TypeType:
+    case Value::Kind::InterfaceType:
+    case Value::Kind::NamedConstraintType:
+    case Value::Kind::ConstraintType:
+      // A value of one of these types is itself always a type.
+      return true;
+  }
+}
+}  // namespace Carbon

+ 35 - 0
explorer/interpreter/type_utils.h

@@ -0,0 +1,35 @@
+// 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_UTILS_H_
+#define CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_
+
+#include "explorer/common/nonnull.h"
+
+namespace Carbon {
+
+class Value;
+
+// Returns whether `value` is a concrete type, which would be valid as the
+// static type of an expression. This is currently any type other than `auto`.
+auto IsNonDeduceableType(Nonnull<const Value*> value) -> bool;
+
+// Returns whether the value is a type value, such as might be a valid type for
+// a syntactic pattern. This includes types involving `auto`. Use
+// `TypeContainsAuto` to determine if a type involves `auto`.
+auto IsType(Nonnull<const Value*> value) -> bool;
+
+// Returns whether *value represents the type of a Carbon value, as
+// opposed to a type pattern or a non-type value.
+auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool;
+
+// Returns the list size for type deduction.
+auto GetSize(Nonnull<const Value*> from) -> size_t;
+
+// Returns whether the value is a type whose values are themselves known to be
+// types.
+auto IsTypeOfType(Nonnull<const Value*> value) -> bool;
+}  // namespace Carbon
+
+#endif  // CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_