[wgsl][writer] Emit requires directives

Bug: tint:2088
Change-Id: Iff78857539e1e2bab68aa2db1df6463196cb8f64
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/158629
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/wgsl/writer/ast_printer/BUILD.bazel b/src/tint/lang/wgsl/writer/ast_printer/BUILD.bazel
index 16f6447..320ea14 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/BUILD.bazel
+++ b/src/tint/lang/wgsl/writer/ast_printer/BUILD.bazel
@@ -99,6 +99,7 @@
     "literal_test.cc",
     "loop_test.cc",
     "member_accessor_test.cc",
+    "requires_test.cc",
     "return_test.cc",
     "switch_test.cc",
     "type_test.cc",
diff --git a/src/tint/lang/wgsl/writer/ast_printer/BUILD.cmake b/src/tint/lang/wgsl/writer/ast_printer/BUILD.cmake
index b65e2a0..50487ec 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/BUILD.cmake
+++ b/src/tint/lang/wgsl/writer/ast_printer/BUILD.cmake
@@ -102,6 +102,7 @@
   lang/wgsl/writer/ast_printer/literal_test.cc
   lang/wgsl/writer/ast_printer/loop_test.cc
   lang/wgsl/writer/ast_printer/member_accessor_test.cc
+  lang/wgsl/writer/ast_printer/requires_test.cc
   lang/wgsl/writer/ast_printer/return_test.cc
   lang/wgsl/writer/ast_printer/switch_test.cc
   lang/wgsl/writer/ast_printer/type_test.cc
diff --git a/src/tint/lang/wgsl/writer/ast_printer/BUILD.gn b/src/tint/lang/wgsl/writer/ast_printer/BUILD.gn
index 0f53128..940f468 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/BUILD.gn
+++ b/src/tint/lang/wgsl/writer/ast_printer/BUILD.gn
@@ -101,6 +101,7 @@
         "literal_test.cc",
         "loop_test.cc",
         "member_accessor_test.cc",
+        "requires_test.cc",
         "return_test.cc",
         "switch_test.cc",
         "type_test.cc",
diff --git a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
index 7ff0c0d..ec38e3d 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
@@ -102,6 +102,10 @@
         EmitEnable(enable);
         has_directives = true;
     }
+    for (auto req : program_.AST().Requires()) {
+        EmitRequires(req);
+        has_directives = true;
+    }
     for (auto diagnostic : program_.AST().DiagnosticDirectives()) {
         auto out = Line();
         EmitDiagnosticControl(out, diagnostic->control);
@@ -113,7 +117,7 @@
     }
     // Generate global declarations in the order they appear in the module.
     for (auto* decl : program_.AST().GlobalDeclarations()) {
-        if (decl->IsAnyOf<ast::DiagnosticDirective, ast::Enable>()) {
+        if (decl->IsAnyOf<ast::DiagnosticDirective, ast::Enable, ast::Requires>()) {
             continue;
         }
         Switch(
@@ -148,6 +152,20 @@
     out << ";";
 }
 
+void ASTPrinter::EmitRequires(const ast::Requires* req) {
+    auto out = Line();
+    out << "requires ";
+    bool first = true;
+    for (auto feature : req->features) {
+        if (!first) {
+            out << ", ";
+        }
+        out << feature;
+        first = false;
+    }
+    out << ";";
+}
+
 void ASTPrinter::EmitTypeDecl(const ast::TypeDecl* ty) {
     Switch(
         ty,  //
diff --git a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
index 9fe6ca3..71bb09a 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
@@ -67,6 +67,7 @@
 class LiteralExpression;
 class LoopStatement;
 class MemberAccessorExpression;
+class Requires;
 class ReturnStatement;
 class Statement;
 class Statement;
@@ -104,6 +105,9 @@
     /// Handles generating an enable directive
     /// @param enable the enable node
     void EmitEnable(const ast::Enable* enable);
+    /// Handles generating a requires directive
+    /// @param req the requires node
+    void EmitRequires(const ast::Requires* req);
     /// Handles generating a declared type
     /// @param ty the declared type to generate
     void EmitTypeDecl(const ast::TypeDecl* ty);
diff --git a/src/tint/lang/wgsl/writer/ast_printer/requires_test.cc b/src/tint/lang/wgsl/writer/ast_printer/requires_test.cc
new file mode 100644
index 0000000..1526fbb
--- /dev/null
+++ b/src/tint/lang/wgsl/writer/ast_printer/requires_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 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/wgsl/writer/ast_printer/helper_test.h"
+
+#include "gmock/gmock.h"
+
+namespace tint::wgsl::writer {
+namespace {
+
+using WgslASTPrinterTest = TestHelper;
+
+TEST_F(WgslASTPrinterTest, Emit_Requires) {
+    auto* req = Require(wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures);
+
+    ASTPrinter& gen = Build();
+
+    gen.EmitRequires(req);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
+    EXPECT_EQ(gen.Result(), R"(requires readonly_and_readwrite_storage_textures;
+)");
+}
+
+// TODO(jrprice): Enable this once we have multiple language features.
+TEST_F(WgslASTPrinterTest, DISABLED_Emit_Requires_Multiple) {
+    auto* req = create<ast::Requires>(
+        wgsl::LanguageFeatures({wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures,
+                                wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures}));
+    AST().AddRequires(req);
+
+    ASTPrinter& gen = Build();
+
+    gen.EmitRequires(req);
+    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
+    EXPECT_EQ(
+        gen.Result(),
+        R"(requires readonly_and_readwrite_storage_textures, readonly_and_readwrite_storage_textures;
+)");
+}
+
+}  // namespace
+}  // namespace tint::wgsl::writer