[validator] Move exit functional checks.

Move the functional validation for `Exit*` to the functional
validator.

Bug: 516717234
Change-Id: Ic0ae86e64b38e5ac73649ff88cae9dfce7dff192
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/314518
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/functional_validator.cc b/src/tint/lang/core/ir/functional_validator.cc
index 1a36359..33bd858 100644
--- a/src/tint/lang/core/ir/functional_validator.cc
+++ b/src/tint/lang/core/ir/functional_validator.cc
@@ -29,6 +29,8 @@
 
 #include "src/tint/lang/core/intrinsic/dialect.h"
 #include "src/tint/lang/core/ir/discard.h"
+#include "src/tint/lang/core/ir/exit_if.h"
+#include "src/tint/lang/core/ir/exit_switch.h"
 #include "src/tint/lang/core/ir/multi_in_block.h"
 #include "src/tint/lang/core/ir/phony.h"
 #include "src/tint/lang/core/ir/type/array_count.h"
@@ -1002,10 +1004,13 @@
 
 void Functional::CheckTerminator(const Terminator* b) {
     tint::Switch(
-        b,                                                //
-        [&](const ir::BreakIf* i) { CheckBreakIf(i); },   //
-        [&](const ir::Continue* c) { CheckContinue(c); }  //
-        // TODO(516717234): Add TINT_ICE_ON_NO_MATCH
+        b,                                                 //
+        [&](const ir::BreakIf* i) { CheckBreakIf(i); },    //
+        [&](const ir::Continue* c) { CheckContinue(c); },  //
+        [&](const ir::Exit* e) { CheckExit(e); },          //
+
+        [&](Default) {}  //
+                         // TODO(516717234): Add TINT_ICE_ON_NO_MATCH
     );
 }
 
@@ -1389,4 +1394,36 @@
     }
 }
 
+void Functional::CheckExit(const Exit* e) {
+    tint::Switch(
+        e,                                                 //
+        [&](const ir::ExitIf*) {},                         //
+        [&](const ir::ExitLoop* l) { CheckExitLoop(l); },  //
+        [&](const ir::ExitSwitch*) {},                     //
+        TINT_ICE_ON_NO_MATCH);
+}
+
+void Functional::CheckExitLoop(const ExitLoop* l) {
+    const Instruction* inst = l;
+    const Loop* control = l->Loop();
+    while (inst) {
+        // Found parent loop
+        if (inst->Block()->Parent() == control) {
+            if (inst->Block() == control->Continuing()) {
+                AddError(l) << "loop exit jumps out of continuing block";
+                if (control->Continuing() != l->Block()) {
+                    AddNote(control->Continuing()) << "in continuing block";
+                }
+            } else if (inst->Block() == control->Initializer()) {
+                AddError(l) << "loop exit not permitted in loop initializer";
+                if (control->Initializer() != l->Block()) {
+                    AddNote(control->Initializer()) << "in initializer block";
+                }
+            }
+            break;
+        }
+        inst = inst->Block()->Parent();
+    }
+}
+
 }  // namespace tint::core::ir::validator
diff --git a/src/tint/lang/core/ir/functional_validator.h b/src/tint/lang/core/ir/functional_validator.h
index dfb45c7..7e45603 100644
--- a/src/tint/lang/core/ir/functional_validator.h
+++ b/src/tint/lang/core/ir/functional_validator.h
@@ -40,6 +40,8 @@
 #include "src/tint/lang/core/ir/convert.h"
 #include "src/tint/lang/core/ir/core_builtin_call.h"
 #include "src/tint/lang/core/ir/disassembler.h"
+#include "src/tint/lang/core/ir/exit.h"
+#include "src/tint/lang/core/ir/exit_loop.h"
 #include "src/tint/lang/core/ir/if.h"
 #include "src/tint/lang/core/ir/let.h"
 #include "src/tint/lang/core/ir/load.h"
@@ -130,6 +132,8 @@
     void CheckConvert(const Convert* convert);
     void CheckCoreBuiltinCall(const CoreBuiltinCall* call,
                               const core::intrinsic::Overload& overload);
+    void CheckExit(const Exit* e);
+    void CheckExitLoop(const ExitLoop* l);
     void CheckIf(const If* if_);
     void CheckLet(const Let* l);
     void CheckLoad(const Load* l);
diff --git a/src/tint/lang/core/ir/structural_validator.cc b/src/tint/lang/core/ir/structural_validator.cc
index ac73f63..8785c51 100644
--- a/src/tint/lang/core/ir/structural_validator.cc
+++ b/src/tint/lang/core/ir/structural_validator.cc
@@ -2722,7 +2722,7 @@
         [&](const Unary* u) { CheckUnary(u); },                            //
         [&](const Override* o) { CheckOverride(o); },                      //
         [&](const Var* var) { CheckVar(var); },                            //
-        [&](const Default) { AddError(inst) << "missing validation"; });
+        TINT_ICE_ON_NO_MATCH);
 }
 
 void Structural::CheckOverride(const Override* o) {
@@ -3124,7 +3124,8 @@
                     [&](Default) {
                         auto* e = t->DeepestElement()->UnwrapPtrOrRef();
                         tint::Switch(
-                            e, [&](const core::type::Struct* s) { result = is_numeric(s); },
+                            e,  //
+                            [&](const core::type::Struct* s) { result = is_numeric(s); },
                             [&](Default) { result = e->IsNumericScalarOrVector(); });
                     });
                 return result;
@@ -3475,7 +3476,7 @@
         [&](const ir::Return* ret) { CheckReturn(ret); },            //
         [&](const ir::TerminateInvocation*) {},                      //
         [&](const ir::Unreachable* u) { CheckUnreachable(u); },      //
-        [&](Default) { AddError(b) << "missing validation"; });
+        TINT_ICE_ON_NO_MATCH);
 
     if (b->next) {
         AddError(b) << "must be the last instruction in the block";
@@ -3521,15 +3522,14 @@
 }
 
 void Structural::CheckExit(const Exit* e) {
-    if (e->ControlInstruction() == nullptr) {
-        AddError(e) << "has no parent control instruction";
-        return;
-    }
-
     if (control_stack_.IsEmpty()) {
         AddError(e) << "found outside all control instructions";
         return;
     }
+    if (e->ControlInstruction() == nullptr) {
+        AddError(e) << "has no parent control instruction";
+        return;
+    }
 
     auto args = e->Args();
     CheckOperandsMatchTarget(e, e->ArgsOperandOffset(), args.size(), e->ControlInstruction(),
@@ -3540,7 +3540,7 @@
         [&](const ir::ExitIf* i) { CheckExitIf(i); },          //
         [&](const ir::ExitLoop* l) { CheckExitLoop(l); },      //
         [&](const ir::ExitSwitch* s) { CheckExitSwitch(s); },  //
-        [&](Default) { AddError(e) << "missing validation"; });
+        TINT_ICE_ON_NO_MATCH);
 }
 
 void Structural::CheckNextIteration(const NextIteration* n) {
@@ -3632,27 +3632,6 @@
 
 void Structural::CheckExitLoop(const ExitLoop* l) {
     CheckControlsAllowingIf(l, l->ControlInstruction());
-
-    const Instruction* inst = l;
-    const Loop* control = l->Loop();
-    while (inst) {
-        // Found parent loop
-        if (inst->Block()->Parent() == control) {
-            if (inst->Block() == control->Continuing()) {
-                AddError(l) << "loop exit jumps out of continuing block";
-                if (control->Continuing() != l->Block()) {
-                    AddNote(control->Continuing()) << "in continuing block";
-                }
-            } else if (inst->Block() == control->Initializer()) {
-                AddError(l) << "loop exit not permitted in loop initializer";
-                if (control->Initializer() != l->Block()) {
-                    AddNote(control->Initializer()) << "in initializer block";
-                }
-            }
-            break;
-        }
-        inst = inst->Block()->Parent();
-    }
 }
 
 void Structural::CheckLoad(const Load* l) {