[tint][hlsl] Don't ICE if the PixelLocal transform is missing data

Just use a error severity diagnostic, and recover.

Change-Id: I6d50213b8e1a678fc6e77e04b6c201affe57db5d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185582
Reviewed-by: dan sinclair <dsinclair@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc b/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc
index 04e7b14..fd59677 100644
--- a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc
+++ b/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc
@@ -39,7 +39,10 @@
 #include "src/tint/lang/wgsl/sem/statement.h"
 #include "src/tint/lang/wgsl/sem/struct.h"
 #include "src/tint/utils/containers/transform.h"
+#include "src/tint/utils/diagnostic/diagnostic.h"
+#include "src/tint/utils/result/result.h"
 #include "src/tint/utils/rtti/switch.h"
+#include "src/tint/utils/text/text_style.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::PixelLocal);
 TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::PixelLocal::RasterizerOrderedView);
@@ -120,7 +123,12 @@
                 // Obtain struct of the pixel local.
                 auto* pixel_local_str =
                     pixel_local_variable->Type()->UnwrapRef()->As<sem::Struct>();
-                TransformEntryPoint(entry_point, pixel_local_variable, pixel_local_str);
+                if (auto res =
+                        TransformEntryPoint(entry_point, pixel_local_variable, pixel_local_str);
+                    res != Success) {
+                    b.Diagnostics().Add(res.Failure().reason);
+                    made_changes = true;
+                }
 
                 break;  // Only a single `var<pixel_local>` can be used by an entry point.
             }
@@ -139,9 +147,9 @@
     /// @param entry_point the entry point
     /// @param pixel_local_var the `var<pixel_local>`
     /// @param pixel_local_str the struct type of the var
-    void TransformEntryPoint(const sem::Function* entry_point,
-                             const sem::GlobalVariable* pixel_local_var,
-                             const sem::Struct* pixel_local_str) {
+    Result<SuccessType> TransformEntryPoint(const sem::Function* entry_point,
+                                            const sem::GlobalVariable* pixel_local_var,
+                                            const sem::Struct* pixel_local_str) {
         // Wrap the old entry point "fn" into a new entry point where functions to load and store
         // ROV data are called.
         auto* original_entry_point_fn = entry_point->Declaration();
@@ -175,9 +183,12 @@
         // load data from and store data into the ROVs.
         auto load_rov_function_name = b.Symbols().New("load_from_pixel_local_storage");
         auto store_rov_function_name = b.Symbols().New("store_into_pixel_local_storage");
-        DeclareROVsAndLoadStoreFunctions(load_rov_function_name, store_rov_function_name,
-                                         pixel_local_var->Declaration()->name->symbol.Name(),
-                                         pixel_local_str);
+        if (auto res = DeclareROVsAndLoadStoreFunctions(
+                load_rov_function_name, store_rov_function_name,
+                pixel_local_var->Declaration()->name->symbol.Name(), pixel_local_str);
+            res != Success) {
+            return res.Failure();
+        }
 
         // Declare new entry point
         Vector<const ast::Statement*, 5> new_entry_point_function_body;
@@ -276,6 +287,7 @@
         // Declare the new entry point that calls the inner function
         b.Func(entry_point_name, std::move(new_entry_point_params), new_entry_point_return_type,
                new_entry_point_function_body, Vector{b.Stage(ast::PipelineStage::kFragment)});
+        return Success;
     }
 
     /// Add the declarations of all the ROVs as a special type of read-write storage texture that
@@ -285,10 +297,11 @@
     /// @param store_rov_function_name the name of the function that stores the data into the ROVs
     /// @param pixel_local_variable_name the name of the pixel local variable
     /// @param pixel_local_str the struct type of the pixel local variable
-    void DeclareROVsAndLoadStoreFunctions(const Symbol& load_rov_function_name,
-                                          const Symbol& store_rov_function_name,
-                                          const std::string& pixel_local_variable_name,
-                                          const sem::Struct* pixel_local_str) {
+    Result<SuccessType> DeclareROVsAndLoadStoreFunctions(
+        const Symbol& load_rov_function_name,
+        const Symbol& store_rov_function_name,
+        const std::string& pixel_local_variable_name,
+        const sem::Struct* pixel_local_str) {
         std::string_view load_store_input_name = "my_input";
         Vector load_parameters{b.Param(load_store_input_name, b.ty.vec4<f32>())};
         Vector store_parameters{b.Param(load_store_input_name, b.ty.vec4<f32>())};
@@ -314,8 +327,11 @@
                 [&](const core::type::F32*) { return core::TexelFormat::kR32Float; },
                 TINT_ICE_ON_NO_MATCH);
             auto rov_format = ROVTexelFormat(member->Index());
-            auto rov_type = b.ty.storage_texture(core::type::TextureDimension::k2d, rov_format,
-                                                 core::Access::kReadWrite);
+            if (TINT_UNLIKELY(rov_format != Success)) {
+                return rov_format.Failure();
+            }
+            auto rov_type = b.ty.storage_texture(core::type::TextureDimension::k2d,
+                                                 rov_format.Get(), core::Access::kReadWrite);
             auto rov_symbol_name = b.Symbols().New("pixel_local_" + member->Name().Name());
             b.GlobalVar(rov_symbol_name, rov_type,
                         tint::Vector{b.Binding(AInt(ROVRegisterIndex(member->Index()))),
@@ -352,7 +368,7 @@
             // textureStore(
             //     pixel_local_member, rov_texcoord, vec4u(bitcast(PLS_Private_Variable.member)));
             std::string rov_pixel_type;
-            switch (rov_format) {
+            switch (rov_format.Get()) {
                 case core::TexelFormat::kR32Uint:
                     rov_pixel_type = "vec4u";
                     break;
@@ -363,8 +379,7 @@
                     rov_pixel_type = "vec4f";
                     break;
                 default:
-                    TINT_ICE() << "Invalid ROV format (now only R32Uint, R32Sint and R32Float are "
-                                  "supported)";
+                    TINT_UNREACHABLE();
                     break;
             }
             auto pixel_local_var_member_access_in_store_call =
@@ -374,7 +389,7 @@
                 to_vec4_call = b.Call(rov_pixel_type, pixel_local_var_member_access_in_store_call);
             } else {
                 ast::Type rov_pixel_ast_type;
-                switch (rov_format) {
+                switch (rov_format.Get()) {
                     case core::TexelFormat::kR32Uint:
                         rov_pixel_ast_type = b.ty.u32();
                         break;
@@ -399,6 +414,7 @@
 
         b.Func(load_rov_function_name, std::move(load_parameters), b.ty.void_(), load_body);
         b.Func(store_rov_function_name, std::move(store_parameters), b.ty.void_(), store_body);
+        return Success;
     }
 
     /// Find and get `@builtin(position)` which is needed for loading and storing data with ROVs
@@ -462,12 +478,14 @@
 
     /// @returns the texel format for the pixel local field with the given index
     /// @param field_index the pixel local field index
-    core::TexelFormat ROVTexelFormat(uint32_t field_index) {
+    Result<core::TexelFormat> ROVTexelFormat(uint32_t field_index) {
         auto format = cfg.pls_member_to_rov_format.Get(field_index);
         if (TINT_UNLIKELY(!format)) {
-            b.Diagnostics().AddError(diag::System::Transform, Source{})
-                << "PixelLocal::Config::attachments missing entry for field " << field_index;
-            return core::TexelFormat::kUndefined;
+            diag::Diagnostic err;
+            err.severity = diag::Severity::Error;
+            err.message << "PixelLocal::Config::attachments missing entry for field "
+                        << field_index;
+            return Failure{std::move(err)};
         }
         return *format;
     }
diff --git a/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc b/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc
index 9f644a7..f34138a 100644
--- a/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc
+++ b/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc
@@ -61,6 +61,31 @@
     EXPECT_FALSE(ShouldRun<PixelLocal>(src, Bindings({})));
 }
 
+TEST_F(HLSLPixelLocalTest, MissingBindings) {
+    auto* src = R"(
+enable chromium_experimental_pixel_local;
+
+struct PixelLocal {
+  a : u32,
+}
+
+var<pixel_local> P : PixelLocal;
+
+@fragment
+fn F() -> @location(0) vec4f {
+  P.a += 42;
+  return vec4f(1, 0, 0, 1);
+}
+)";
+
+    auto* expect = R"(error: PixelLocal::Config::attachments missing entry for field 0)";
+    ast::transform::DataMap data;
+    data.Add<PixelLocal::Config>();
+    auto got = Run<PixelLocal>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 TEST_F(HLSLPixelLocalTest, UseInEntryPoint_NoPosition) {
     auto* src = R"(
 enable chromium_experimental_pixel_local;