[ir_to_program] Cleanup IRToProgram.

This Cl simplifies the binding tracking to just store the `ir_expr`,
`ast_expr` or `name` we care about and drops the use of the `variant`
and visitor code. The `SCOPED_NESTING` is removed in favour of the more
commonly used `TINT_SCOPED_ASSIGNMENT`.

Bug: 422532802
Change-Id: I6b3aa199276c26069f229b4fa6f17352859ec280
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/245614
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
index b0a5a7a..333781c 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
@@ -97,12 +97,6 @@
 #include "src/tint/utils/math/math.h"
 #include "src/tint/utils/rtti/switch.h"
 
-// Helper for incrementing nesting_depth_ and then decrementing nesting_depth_ at the end
-// of the scope that holds the call.
-#define SCOPED_NESTING() \
-    nesting_depth_++;    \
-    TINT_DEFER(nesting_depth_--)
-
 using namespace tint::core::fluent_types;  // NOLINT
 
 namespace tint::wgsl::writer {
@@ -153,28 +147,13 @@
     /// The target ProgramBuilder
     ProgramBuilder b;
 
-    /// The structure for a value held by a 'let', 'var' or parameter.
-    struct VariableValue {
-        Symbol name;  // Name of the variable
-    };
-
     /// The structure for a reusable value
-    struct ReusableValue {
-        const core::ir::Value* expr = nullptr;
+    struct ValueBinding {
+        Symbol name{};
+        const core::ir::Value* ir_expr = nullptr;
+        const ast::Expression* ast_expr = nullptr;
     };
 
-    /// The structure for an inlined value
-    struct InlinedValue {
-        const ast::Expression* expr = nullptr;
-    };
-
-    /// Empty struct used as a sentinel value to indicate that an ast::Value has been consumed by
-    /// its single place of usage. Attempting to use this value a second time should result in an
-    /// ICE.
-    struct ConsumedValue {};
-
-    using ValueBinding = std::variant<VariableValue, InlinedValue, ConsumedValue, ReusableValue>;
-
     /// IR values to their representation
     Hashmap<const core::ir::Value*, ValueBinding, 32> bindings_;
 
@@ -214,7 +193,7 @@
         }
     }
     const ast::Function* Fn(const core::ir::Function* fn) {
-        SCOPED_NESTING();
+        TINT_SCOPED_ASSIGNMENT(nesting_depth_, nesting_depth_ + 1);
 
         // Emit parameters.
         static constexpr size_t N = decltype(ast::Function::params)::static_length;
@@ -395,7 +374,7 @@
     }
 
     void If(const core::ir::If* if_) {
-        SCOPED_NESTING();
+        TINT_SCOPED_ASSIGNMENT(nesting_depth_, nesting_depth_ + 1);
 
         auto true_stmts = Statements(if_->True());
         auto false_stmts = Statements(if_->False());
@@ -423,7 +402,7 @@
     }
 
     void Loop(const core::ir::Loop* l) {
-        SCOPED_NESTING();
+        TINT_SCOPED_ASSIGNMENT(nesting_depth_, nesting_depth_ + 1);
 
         // Build all the initializer statements
         auto init_stmts = Statements(l->Initializer());
@@ -484,9 +463,8 @@
 
         // Depending on 'init', 'cond' and 'cont', build a 'for', 'while' or 'loop'
         const ast::Statement* loop = nullptr;
-        if ((!cont && !cont_stmts.IsEmpty())  // Non-trivial continuing
-            || !cond                          // or non-trivial or no condition
-        ) {
+        bool non_trivial_continuing = !cont && !cont_stmts.IsEmpty();
+        if (non_trivial_continuing || !cond) {
             // Build a loop
             if (cond) {
                 body_stmts.Insert(0, b.If(b.Not(cond), b.Block(b.Break())));
@@ -519,14 +497,14 @@
     }
 
     void Switch(const core::ir::Switch* s) {
-        SCOPED_NESTING();
+        TINT_SCOPED_ASSIGNMENT(nesting_depth_, nesting_depth_ + 1);
 
         auto* cond = Expr(s->Condition());
 
         auto cases = tint::Transform<4>(
             s->Cases(),  //
             [&](const core::ir::Switch::Case& c) -> const tint::ast::CaseStatement* {
-                SCOPED_NESTING();
+                TINT_SCOPED_ASSIGNMENT(nesting_depth_, nesting_depth_ + 1);
 
                 const ast::BlockStatement* body = nullptr;
                 {
@@ -839,7 +817,7 @@
         auto* vec = Expr(s->Object());
         Vector<char, 4> components;
         for (uint32_t i : s->Indices()) {
-            if (DAWN_UNLIKELY(i >= 4)) {
+            if (i >= 4) {
                 TINT_ICE() << "invalid swizzle index: " << i;
             }
             components.Push(xyzw[i]);
@@ -921,57 +899,25 @@
         Bind(e->Result(), expr);
     }
 
-    TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
-
     const ast::Expression* Expr(const core::ir::Value* value) {
-        auto expr = tint::Switch(
-            value,  //
-            [&](const core::ir::Constant* c) { return Constant(c); },
-            [&](Default) -> const ast::Expression* {
-                auto lookup = bindings_.Get(value);
-                if (DAWN_UNLIKELY(!lookup)) {
-                    TINT_ICE() << "Expr(" << (value ? value->TypeInfo().name : "null")
-                               << ") value has no expression";
-                }
-                return std::visit(
-                    [&](auto&& got) -> const ast::Expression* {
-                        using T = std::decay_t<decltype(got)>;
-
-                        if constexpr (std::is_same_v<T, VariableValue>) {
-                            return b.Expr(got.name);
-                        }
-                        if constexpr (std::is_same_v<T, ReusableValue>) {
-                            return Expr(got.expr);
-                        }
-
-                        if constexpr (std::is_same_v<T, InlinedValue>) {
-                            auto result = got.expr;
-                            // Single use (inlined) expression.
-                            // Mark the bindings_ map entry as consumed.
-                            *lookup = ConsumedValue{};
-                            return result;
-                        }
-
-                        if constexpr (std::is_same_v<T, ConsumedValue>) {
-                            TINT_ICE() << "Expr(" << value->TypeInfo().name
-                                       << ") called twice on the same value";
-                        } else {
-                            TINT_ICE()
-                                << "Expr(" << value->TypeInfo().name << ") has unhandled value";
-                        }
-                        return nullptr;
-                    },
-                    *lookup);
-            });
-
-        if (!expr) {
-            return b.Expr("<error>");
+        if (auto* cnst = value->As<core::ir::Constant>()) {
+            return Constant(cnst);
         }
 
-        return expr;
-    }
+        auto lookup = bindings_.Get(value);
+        if (!lookup) {
+            TINT_ICE() << "Expr(" << (value ? value->TypeInfo().name : "null")
+                       << ") value has no expression";
+        }
 
-    TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
+        if (lookup->ast_expr != nullptr) {
+            return lookup->ast_expr;
+        }
+        if (lookup->ir_expr != nullptr) {
+            return Expr(lookup->ir_expr);
+        }
+        return b.Expr(lookup->name);
+    }
 
     const ast::Expression* Constant(const core::ir::Constant* c) { return Constant(c->Value()); }
 
@@ -1014,16 +960,6 @@
         }
     }
 
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Types
-    //
-    // The the case of an error:
-    // * The types generating methods must return a non-null ast type, which may not be semantically
-    //   legal, but is enough to populate the AST.
-    // * A diagnostic error must be added to the ast::ProgramBuilder.
-    // This prevents littering the ToProgram logic with expensive error checking code.
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-
     /// @param ty the type::Type
     /// @return an ast::Type from @p ty.
     /// @note May be a semantically-invalid placeholder type on error.
@@ -1061,7 +997,7 @@
                     return b.ty.array(el, std::move(attrs));
                 }
                 auto count = a->ConstantCount();
-                if (DAWN_UNLIKELY(!count)) {
+                if (!count) {
                     TINT_ICE() << core::type::Array::kErrExpectedConstantCount;
                 }
                 return b.ty.array(el, u32(count.value()), std::move(attrs));
@@ -1253,7 +1189,7 @@
     void Bind(const core::ir::Value* value, const core::ir::Value* expr) {
         TINT_ASSERT(value);
         if (value->IsUsed()) {
-            bindings_.Replace(value, ReusableValue{expr});
+            bindings_.Replace(value, ValueBinding{.ir_expr = expr});
         } else {
             Append(b.Assign(b.Phony(), Expr(expr)));
         }
@@ -1264,8 +1200,7 @@
     void Bind(const core::ir::Value* value, const ast::Expression* expr) {
         TINT_ASSERT(value);
         if (value->IsUsed()) {
-            // Value will be inlined at its place of usage.
-            if (DAWN_UNLIKELY(!bindings_.Add(value, InlinedValue{expr}))) {
+            if (!bindings_.Add(value, ValueBinding{.ast_expr = expr})) {
                 TINT_ICE() << "Bind(" << value->TypeInfo().name << ") called twice for same value";
             }
         } else {
@@ -1277,7 +1212,7 @@
     /// name.
     void Bind(const core::ir::Value* value, Symbol name) {
         TINT_ASSERT(value);
-        if (DAWN_UNLIKELY(!bindings_.Add(value, VariableValue{name}))) {
+        if (!bindings_.Add(value, ValueBinding{.name = name})) {
             TINT_ICE() << "Bind(" << value->TypeInfo().name << ") called twice for same value";
         }
     }