[ir] Fix validation order for control flow

Make sure we validate blocks nested in control flow before we validate
instructions that follow the control flow instruction.

This fixes an issue where values declared after a control flow
instruction were incorrectly seen as in scope inside that control flow
instruction.

Fixed: 467610939
Change-Id: I43c88601ff041e084475f17d814858af193a72d5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/280596
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 9406804..4f11f7a 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -3186,10 +3186,12 @@
 
 void Validator::QueueInstructions(const Instruction* inst) {
     tasks_.Push([this, inst] {
-        CheckInstruction(inst);
+        // Tasks are processed LIFO, so push the next instruction to the stack before checking the
+        // current instruction, which may need to add more blocks to the stack itself.
         if (inst->next) {
             QueueInstructions(inst->next);
         }
+        CheckInstruction(inst);
     });
 }
 
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 63f0a154..c9f7993 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -1650,6 +1650,34 @@
 )")) << res.Failure();
 }
 
+TEST_F(IR_ValidatorTest, Scoping_UseBeforeDecl_InControlFlow) {
+    auto* f = b.Function("my_func", ty.void_());
+    auto* decl = b.Let("decl", 42_u);
+    auto* if_ = b.If(true);
+    b.Append(if_->True(), [&] {
+        b.Let("use", decl);
+        b.ExitIf(if_);
+    });
+    f->Block()->Append(if_);
+    f->Block()->Append(decl);
+    f->Block()->Append(b.Return(f));
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason, testing::HasSubstr(R"(:5:24 error: let: %decl is not in scope
+        %use:u32 = let %decl
+                       ^^^^^
+
+:4:7 note: in block
+      $B2: {  # true
+      ^^^
+
+:9:5 note: %decl declared here
+    %decl:u32 = let 42u
+    ^^^^^^^^^
+)")) << res.Failure();
+}
+
 TEST_F(IR_ValidatorTest, OverrideWithoutCapability) {
     b.Append(mod.root_block, [&] { b.Override("a", 1_u); });