[glsl] Emit let's

Add emitting of let statements to the GLSL IR backend.

Bug: 42251044
Change-Id: I9a38f9d84383b7789f70d0348f8935b11696c559
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/200457
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/glsl/writer/BUILD.bazel b/src/tint/lang/glsl/writer/BUILD.bazel
index 9ea3151..8294a43 100644
--- a/src/tint/lang/glsl/writer/BUILD.bazel
+++ b/src/tint/lang/glsl/writer/BUILD.bazel
@@ -90,6 +90,7 @@
     "call_test.cc",
     "constant_test.cc",
     "function_test.cc",
+    "var_and_let_test.cc",
   ] + select({
     ":tint_build_glsl_validator": [
       "helper_test.h",
diff --git a/src/tint/lang/glsl/writer/BUILD.cmake b/src/tint/lang/glsl/writer/BUILD.cmake
index f105f30..e5a40a3 100644
--- a/src/tint/lang/glsl/writer/BUILD.cmake
+++ b/src/tint/lang/glsl/writer/BUILD.cmake
@@ -101,6 +101,7 @@
   lang/glsl/writer/call_test.cc
   lang/glsl/writer/constant_test.cc
   lang/glsl/writer/function_test.cc
+  lang/glsl/writer/var_and_let_test.cc
 )
 
 tint_target_add_dependencies(tint_lang_glsl_writer_test test
diff --git a/src/tint/lang/glsl/writer/BUILD.gn b/src/tint/lang/glsl/writer/BUILD.gn
index 1d2047c..8b3b547 100644
--- a/src/tint/lang/glsl/writer/BUILD.gn
+++ b/src/tint/lang/glsl/writer/BUILD.gn
@@ -93,6 +93,7 @@
         "call_test.cc",
         "constant_test.cc",
         "function_test.cc",
+        "var_and_let_test.cc",
       ]
       deps = [
         "${tint_src_dir}:gmock_and_gtest",
diff --git a/src/tint/lang/glsl/writer/call_test.cc b/src/tint/lang/glsl/writer/call_test.cc
index b7148f1..c4ead84 100644
--- a/src/tint/lang/glsl/writer/call_test.cc
+++ b/src/tint/lang/glsl/writer/call_test.cc
@@ -33,8 +33,7 @@
 namespace tint::glsl::writer {
 namespace {
 
-// TODO(dsinclair): Enable when `let` is supported
-TEST_F(GlslWriterTest, DISABLED_CallWithoutParams) {
+TEST_F(GlslWriterTest, CallWithoutParams) {
     auto* f = b.Function("a", ty.bool_());
     f->Block()->Append(b.Return(f, false));
 
@@ -50,12 +49,10 @@
 bool a() {
   return false;
 }
-
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void main() {
-    bool x = a();
+  bool x = a();
 }
-
 )");
 }
 
diff --git a/src/tint/lang/glsl/writer/printer/printer.cc b/src/tint/lang/glsl/writer/printer/printer.cc
index 8ce53bd..456c1ce 100644
--- a/src/tint/lang/glsl/writer/printer/printer.cc
+++ b/src/tint/lang/glsl/writer/printer/printer.cc
@@ -37,6 +37,7 @@
 #include "src/tint/lang/core/ir/core_unary.h"
 #include "src/tint/lang/core/ir/exit_if.h"
 #include "src/tint/lang/core/ir/function.h"
+#include "src/tint/lang/core/ir/let.h"
 #include "src/tint/lang/core/ir/load.h"
 #include "src/tint/lang/core/ir/load_vector_element.h"
 #include "src/tint/lang/core/ir/module.h"
@@ -176,6 +177,7 @@
             tint::Switch(
                 inst,                                                      //
                 [&](const core::ir::Call* i) { EmitCallStmt(i); },         //
+                [&](const core::ir::Let* i) { EmitLet(i); },               //
                 [&](const core::ir::Return* r) { EmitReturn(r); },         //
                 [&](const core::ir::Unreachable*) { EmitUnreachable(); },  //
 
@@ -194,6 +196,17 @@
         }
     }
 
+    void EmitLet(const core::ir::Let* l) {
+        auto out = Line();
+
+        // TODO(dsinclair): Investigate using `const` here as well, the AST printer doesn't emit
+        //                  const with a let, but we should be able to.
+        EmitTypeAndName(out, l->Result(0)->Type(), NameOf(l->Result(0)));
+        out << " = ";
+        EmitValue(out, l->Value());
+        out << ";";
+    }
+
     void EmitCallStmt(const core::ir::Call* c) {
         if (!c->Result(0)->IsUsed()) {
             auto out = Line();
@@ -213,10 +226,26 @@
         Line() << "#extension " << name << ": require";
     }
 
+    void EmitTypeAndName(StringStream& out, const core::type::Type* type, const std::string& name) {
+        bool name_printed = false;
+        EmitType(out, type, name, &name_printed);
+
+        if (!name.empty() && !name_printed) {
+            out << " " << name;
+        }
+    }
+
     /// Emit a type
     /// @param out the stream to emit too
     /// @param ty the type to emit
-    void EmitType(StringStream& out, const core::type::Type* ty) {
+    void EmitType(StringStream& out,
+                  const core::type::Type* ty,
+                  [[maybe_unused]] const std::string& name = "",
+                  bool* name_printed = nullptr) {
+        if (name_printed) {
+            *name_printed = false;
+        }
+
         tint::Switch(
             ty,  //
             [&](const core::type::Bool*) { out << "bool"; },
diff --git a/src/tint/lang/glsl/writer/var_and_let_test.cc b/src/tint/lang/glsl/writer/var_and_let_test.cc
new file mode 100644
index 0000000..aaee3f8
--- /dev/null
+++ b/src/tint/lang/glsl/writer/var_and_let_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/glsl/writer/helper_test.h"
+
+using namespace tint::core::fluent_types;     // NOLINT
+using namespace tint::core::number_suffixes;  // NOLINT
+
+namespace tint::glsl::writer {
+namespace {
+
+TEST_F(GlslWriterTest, Let) {
+    auto* func = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+    func->SetWorkgroupSize(1, 1, 1);
+    b.Append(func->Block(), [&] {
+        b.Let("a", 2_f);
+        b.Return(func);
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.glsl;
+    EXPECT_EQ(output_.glsl, GlslHeader() + R"(
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  float a = 2.0f;
+}
+)");
+}
+
+}  // namespace
+}  // namespace tint::glsl::writer