Parcourir la source

Eliminate run-time errors from PatternMatch (#1126)

Geoff Romer il y a 4 ans
Parent
commit
93842ad878

+ 30 - 0
executable_semantics/ast/pattern.cpp

@@ -51,6 +51,36 @@ void Pattern::Print(llvm::raw_ostream& out) const {
   }
 }
 
+// Equivalent to `GetBindings`, but stores its output in `bindings` instead of
+// returning it.
+static void GetBindingsImpl(
+    const Pattern& pattern,
+    std::vector<Nonnull<const BindingPattern*>>& bindings) {
+  switch (pattern.kind()) {
+    case PatternKind::BindingPattern:
+      bindings.push_back(&cast<BindingPattern>(pattern));
+      return;
+    case PatternKind::TuplePattern:
+      for (const Pattern* field : cast<TuplePattern>(pattern).fields()) {
+        GetBindingsImpl(*field, bindings);
+      }
+      return;
+    case PatternKind::AlternativePattern:
+      GetBindingsImpl(cast<AlternativePattern>(pattern).arguments(), bindings);
+      return;
+    case PatternKind::AutoPattern:
+    case PatternKind::ExpressionPattern:
+      return;
+  }
+}
+
+auto GetBindings(const Pattern& pattern)
+    -> std::vector<Nonnull<const BindingPattern*>> {
+  std::vector<Nonnull<const BindingPattern*>> result;
+  GetBindingsImpl(pattern, result);
+  return result;
+}
+
 auto PatternFromParenContents(Nonnull<Arena*> arena, SourceLocation source_loc,
                               const ParenContents<Pattern>& paren_contents)
     -> Nonnull<Pattern*> {

+ 6 - 0
executable_semantics/ast/pattern.h

@@ -84,6 +84,12 @@ class Pattern : public AstNode {
   std::optional<Nonnull<const Value*>> value_;
 };
 
+class BindingPattern;
+
+// Returns all `BindingPattern`s in the AST subtree rooted at `pattern`.
+auto GetBindings(const Pattern& pattern)
+    -> std::vector<Nonnull<const BindingPattern*>>;
+
 // A pattern consisting of the `auto` keyword.
 class AutoPattern : public Pattern {
  public:

+ 2 - 10
executable_semantics/interpreter/interpreter.cpp

@@ -178,11 +178,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
                   std::optional<Nonnull<RuntimeScope*>> bindings) -> bool {
   switch (p->kind()) {
     case Value::Kind::BindingPlaceholderValue: {
-      if (!bindings.has_value()) {
-        // TODO: move this to typechecker.
-        FATAL_COMPILATION_ERROR(source_loc)
-            << "Name bindings are not supported in this context";
-      }
+      CHECK(bindings.has_value());
       const auto& placeholder = cast<BindingPlaceholderValue>(*p);
       if (placeholder.value_node().has_value()) {
         (*bindings)->Initialize(*placeholder.value_node(), v);
@@ -194,11 +190,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
         case Value::Kind::TupleValue: {
           const auto& p_tup = cast<TupleValue>(*p);
           const auto& v_tup = cast<TupleValue>(*v);
-          if (p_tup.elements().size() != v_tup.elements().size()) {
-            FATAL_PROGRAM_ERROR(source_loc)
-                << "arity mismatch in tuple pattern match:\n  pattern: "
-                << p_tup << "\n  value: " << v_tup;
-          }
+          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], v_tup.elements()[i],
                               source_loc, bindings)) {

+ 4 - 0
executable_semantics/interpreter/type_checker.cpp

@@ -840,6 +840,10 @@ void TypeChecker::TypeCheckPattern(
     }
     case PatternKind::BindingPattern: {
       auto& binding = cast<BindingPattern>(*p);
+      if (!GetBindings(binding.type()).empty()) {
+        FATAL_COMPILATION_ERROR(binding.type().source_loc())
+            << "The type of a binding pattern cannot contain bindings.";
+      }
       TypeCheckPattern(&binding.type(), std::nullopt, impl_scope);
       Nonnull<const Value*> type =
           InterpPattern(&binding.type(), arena_, trace_);

+ 17 - 0
executable_semantics/testdata/basic_syntax/fail_nested_binding.carbon

@@ -0,0 +1,17 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{not} %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/basic_syntax/fail_nested_binding.carbon:15: The type of a binding pattern cannot contain bindings.
+
+package ExecutableSemanticsTest api;
+
+fn Main() -> i32 {
+  var x: (T: Type) = 1;
+  return 1;
+}