Use std::variant in ShaderBindingInfo

This patch uses std::variant to represent 4 possible member types in
ShaderBindingInfo (BufferBindingLayout, ShaderSamplerBindingInfo,
ShaderTextureBindingInfo, StorageTextureBindingLayout) as logically
at any time only one of them can be used.

Bug: dawn:527
Change-Id: Iad9eaef9706e797cd3db57761dba370047a92fc5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/171220
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn/native/PipelineLayout.cpp b/src/dawn/native/PipelineLayout.cpp
index 294d15a..f2bf900 100644
--- a/src/dawn/native/PipelineLayout.cpp
+++ b/src/dawn/native/PipelineLayout.cpp
@@ -232,57 +232,60 @@
            const ExternalTextureBindingLayout* externalTextureBindingEntry)
         -> BindGroupLayoutEntry {
         BindGroupLayoutEntry entry = {};
-        switch (shaderBinding.bindingType) {
-            case BindingInfoType::Buffer:
-                entry.buffer.type = shaderBinding.buffer.type;
-                entry.buffer.hasDynamicOffset = shaderBinding.buffer.hasDynamicOffset;
-                entry.buffer.minBindingSize = shaderBinding.buffer.minBindingSize;
-                break;
-            case BindingInfoType::Sampler:
-                if (shaderBinding.sampler.isComparison) {
-                    entry.sampler.type = wgpu::SamplerBindingType::Comparison;
-                } else {
-                    entry.sampler.type = wgpu::SamplerBindingType::Filtering;
-                }
-                break;
-            case BindingInfoType::Texture:
-                switch (shaderBinding.texture.compatibleSampleTypes) {
-                    case SampleTypeBit::Depth:
-                        entry.texture.sampleType = wgpu::TextureSampleType::Depth;
-                        break;
-                    case SampleTypeBit::Sint:
-                        entry.texture.sampleType = wgpu::TextureSampleType::Sint;
-                        break;
-                    case SampleTypeBit::Uint:
-                        entry.texture.sampleType = wgpu::TextureSampleType::Uint;
-                        break;
-                    case SampleTypeBit::Float:
-                    case SampleTypeBit::UnfilterableFloat:
-                    case SampleTypeBit::None:
-                        DAWN_UNREACHABLE();
-                        break;
-                    default:
-                        if (shaderBinding.texture.compatibleSampleTypes ==
-                            (SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat)) {
-                            // Default to UnfilterableFloat. It will be promoted to Float if it
-                            // is used with a sampler.
-                            entry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
-                        } else {
+
+        // TODO(dawn:2370): implement a helper in dawn/utils to simplify the call of std::visit.
+        std::visit(
+            [&](const auto& bindingInfo) {
+                using T = std::decay_t<decltype(bindingInfo)>;
+
+                if constexpr (std::is_same_v<T, BufferBindingInfo>) {
+                    entry.buffer.type = bindingInfo.type;
+                    entry.buffer.minBindingSize = bindingInfo.minBindingSize;
+                } else if constexpr (std::is_same_v<T, SamplerBindingInfo>) {
+                    if (bindingInfo.isComparison) {
+                        entry.sampler.type = wgpu::SamplerBindingType::Comparison;
+                    } else {
+                        entry.sampler.type = wgpu::SamplerBindingType::Filtering;
+                    }
+                } else if constexpr (std::is_same_v<T, SampledTextureBindingInfo>) {
+                    switch (bindingInfo.compatibleSampleTypes) {
+                        case SampleTypeBit::Depth:
+                            entry.texture.sampleType = wgpu::TextureSampleType::Depth;
+                            break;
+                        case SampleTypeBit::Sint:
+                            entry.texture.sampleType = wgpu::TextureSampleType::Sint;
+                            break;
+                        case SampleTypeBit::Uint:
+                            entry.texture.sampleType = wgpu::TextureSampleType::Uint;
+                            break;
+                        case SampleTypeBit::Float:
+                        case SampleTypeBit::UnfilterableFloat:
+                        case SampleTypeBit::None:
                             DAWN_UNREACHABLE();
-                        }
+                            break;
+                        default:
+                            if (bindingInfo.compatibleSampleTypes ==
+                                (SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat)) {
+                                // Default to UnfilterableFloat. It will be promoted to Float if it
+                                // is used with a sampler.
+                                entry.texture.sampleType =
+                                    wgpu::TextureSampleType::UnfilterableFloat;
+                            } else {
+                                DAWN_UNREACHABLE();
+                            }
+                    }
+                    entry.texture.viewDimension = bindingInfo.viewDimension;
+                    entry.texture.multisampled = bindingInfo.multisampled;
+                } else if constexpr (std::is_same_v<T, StorageTextureBindingInfo>) {
+                    entry.storageTexture.access = bindingInfo.access;
+                    entry.storageTexture.format = bindingInfo.format;
+                    entry.storageTexture.viewDimension = bindingInfo.viewDimension;
+                } else if constexpr (std::is_same_v<T, ExternalTextureBindingInfo>) {
+                    entry.nextInChain = externalTextureBindingEntry;
                 }
-                entry.texture.viewDimension = shaderBinding.texture.viewDimension;
-                entry.texture.multisampled = shaderBinding.texture.multisampled;
-                break;
-            case BindingInfoType::StorageTexture:
-                entry.storageTexture.access = shaderBinding.storageTexture.access;
-                entry.storageTexture.format = shaderBinding.storageTexture.format;
-                entry.storageTexture.viewDimension = shaderBinding.storageTexture.viewDimension;
-                break;
-            case BindingInfoType::ExternalTexture:
-                entry.nextInChain = externalTextureBindingEntry;
-                break;
-        }
+            },
+            shaderBinding.bindingInfo);
+
         return entry;
     };
 
diff --git a/src/dawn/native/ShaderModule.cpp b/src/dawn/native/ShaderModule.cpp
index 193c992..371d7f5 100644
--- a/src/dawn/native/ShaderModule.cpp
+++ b/src/dawn/native/ShaderModule.cpp
@@ -394,7 +394,13 @@
         DAWN_ASSERT(packedIdx < requiredBufferSizes.size());
         const auto& shaderInfo = shaderBindings.find(bindingInfo.binding);
         if (shaderInfo != shaderBindings.end()) {
-            requiredBufferSizes[packedIdx] = shaderInfo->second.buffer.minBindingSize;
+            auto* shaderBufferInfo =
+                std::get_if<BufferBindingInfo>(&shaderInfo->second.bindingInfo);
+            if (shaderBufferInfo != nullptr) {
+                requiredBufferSizes[packedIdx] = shaderBufferInfo->minBindingSize;
+            } else {
+                requiredBufferSizes[packedIdx] = 0;
+            }
         } else {
             // We have to include buffers if they are included in the bind group's
             // packed vector. We don't actually need to check these at draw time, so
@@ -409,10 +415,36 @@
 
 bool IsShaderCompatibleWithPipelineLayoutOnStorageTextureAccess(
     const BindingInfo& bindingInfo,
-    const ShaderBindingInfo& shaderBindingInfo) {
-    return bindingInfo.storageTexture.access == shaderBindingInfo.storageTexture.access ||
+    const StorageTextureBindingInfo& bindingLayout) {
+    return bindingInfo.storageTexture.access == bindingLayout.access ||
            (bindingInfo.storageTexture.access == wgpu::StorageTextureAccess::ReadWrite &&
-            shaderBindingInfo.storageTexture.access == wgpu::StorageTextureAccess::WriteOnly);
+            bindingLayout.access == wgpu::StorageTextureAccess::WriteOnly);
+}
+
+BindingInfoType GetShaderBindingType(const ShaderBindingInfo& shaderInfo) {
+    return std::visit(
+        [](const auto& bindingInfo) -> BindingInfoType {
+            using T = std::decay_t<decltype(bindingInfo)>;
+
+            if constexpr (std::is_same_v<T, BufferBindingInfo>) {
+                return BindingInfoType::Buffer;
+            }
+            if constexpr (std::is_same_v<T, StorageTextureBindingInfo>) {
+                return BindingInfoType::StorageTexture;
+            }
+            if constexpr (std::is_same_v<T, SampledTextureBindingInfo>) {
+                return BindingInfoType::Texture;
+            }
+            if constexpr (std::is_same_v<T, SamplerBindingInfo>) {
+                return BindingInfoType::Sampler;
+            }
+            if constexpr (std::is_same_v<T, ExternalTextureBindingInfo>) {
+                return BindingInfoType::ExternalTexture;
+            }
+            DAWN_UNREACHABLE();
+            return BindingInfoType::Buffer;
+        },
+        shaderInfo.bindingInfo);
 }
 
 MaybeError ValidateCompatibilityOfSingleBindingWithLayout(const DeviceBase* device,
@@ -427,7 +459,7 @@
     // the shader and bgl will always mismatch at this point. Expansion info is contained in
     // the bgl object, so we can still verify the bgl used to have an external texture in
     // the slot corresponding to the shader reflection.
-    if (shaderInfo.bindingType == BindingInfoType::ExternalTexture) {
+    if (std::holds_alternative<ExternalTextureBindingInfo>(shaderInfo.bindingInfo)) {
         // If an external texture binding used to exist in the bgl, it will be found as a
         // key in the ExternalTextureBindingExpansions map.
         ExternalTextureBindingExpansionMap expansions =
@@ -448,9 +480,10 @@
     BindingIndex bindingIndex(bindingIt->second);
     const BindingInfo& layoutInfo = layout->GetBindingInfo(bindingIndex);
 
-    DAWN_INVALID_IF(layoutInfo.bindingType != shaderInfo.bindingType,
+    BindingInfoType shaderBindingType = GetShaderBindingType(shaderInfo);
+    DAWN_INVALID_IF(layoutInfo.bindingType != shaderBindingType,
                     "Binding type in the shader (%s) doesn't match the type in the layout (%s).",
-                    shaderInfo.bindingType, layoutInfo.bindingType);
+                    shaderBindingType, layoutInfo.bindingType);
 
     ExternalTextureBindingExpansionMap expansions = layout->GetExternalTextureBindingExpansionMap();
     DAWN_INVALID_IF(expansions.find(bindingNumber) != expansions.end(),
@@ -461,100 +494,96 @@
                     "Entry point's stage (%s) is not in the binding visibility in the layout (%s).",
                     StageBit(entryPointStage), layoutInfo.visibility);
 
-    switch (layoutInfo.bindingType) {
-        case BindingInfoType::Texture: {
-            DAWN_INVALID_IF(
-                layoutInfo.texture.multisampled != shaderInfo.texture.multisampled,
-                "Binding multisampled flag (%u) doesn't match the layout's multisampled "
-                "flag (%u)",
-                layoutInfo.texture.multisampled, shaderInfo.texture.multisampled);
+    // TODO(dawn:2370): implement a helper in dawn/utils to simplify the call of std::visit.
+    return std::visit(
+        [&](const auto& bindingInfo) -> MaybeError {
+            using T = std::decay_t<decltype(bindingInfo)>;
 
-            // TODO(dawn:563): Provide info about the sample types.
-            SampleTypeBit requiredType;
-            if (layoutInfo.texture.sampleType == kInternalResolveAttachmentSampleType) {
-                // If the layout's texture's sample type is kInternalResolveAttachmentSampleType,
-                // then the shader's compatible sample types must contain float.
-                requiredType = SampleTypeBit::UnfilterableFloat;
-            } else {
-                requiredType = SampleTypeToSampleTypeBit(layoutInfo.texture.sampleType);
+            if constexpr (std::is_same_v<T, SampledTextureBindingInfo>) {
+                DAWN_INVALID_IF(
+                    layoutInfo.texture.multisampled != bindingInfo.multisampled,
+                    "Binding multisampled flag (%u) doesn't match the layout's multisampled "
+                    "flag (%u)",
+                    layoutInfo.texture.multisampled, bindingInfo.multisampled);
+
+                // TODO(dawn:563): Provide info about the sample types.
+                SampleTypeBit requiredType;
+                if (layoutInfo.texture.sampleType == kInternalResolveAttachmentSampleType) {
+                    // If the layout's texture's sample type is
+                    // kInternalResolveAttachmentSampleType, then the shader's compatible sample
+                    // types must contain float.
+                    requiredType = SampleTypeBit::UnfilterableFloat;
+                } else {
+                    requiredType = SampleTypeToSampleTypeBit(layoutInfo.texture.sampleType);
+                }
+
+                DAWN_INVALID_IF(!(bindingInfo.compatibleSampleTypes & requiredType),
+                                "The sample type in the shader is not compatible with the "
+                                "sample type of the layout.");
+
+                DAWN_INVALID_IF(
+                    layoutInfo.texture.viewDimension != bindingInfo.viewDimension,
+                    "The shader's binding dimension (%s) doesn't match the shader's binding "
+                    "dimension (%s).",
+                    layoutInfo.texture.viewDimension, bindingInfo.viewDimension);
+            } else if constexpr (std::is_same_v<T, StorageTextureBindingInfo>) {
+                DAWN_ASSERT(layoutInfo.storageTexture.format != wgpu::TextureFormat::Undefined);
+                DAWN_ASSERT(bindingInfo.format != wgpu::TextureFormat::Undefined);
+
+                DAWN_INVALID_IF(
+                    !IsShaderCompatibleWithPipelineLayoutOnStorageTextureAccess(layoutInfo,
+                                                                                bindingInfo),
+                    "The layout's binding access (%s) isn't compatible with the shader's "
+                    "binding access (%s).",
+                    layoutInfo.storageTexture.access, bindingInfo.access);
+
+                DAWN_INVALID_IF(
+                    layoutInfo.storageTexture.format != bindingInfo.format,
+                    "The layout's binding format (%s) doesn't match the shader's binding "
+                    "format (%s).",
+                    layoutInfo.storageTexture.format, bindingInfo.format);
+
+                DAWN_INVALID_IF(
+                    layoutInfo.storageTexture.viewDimension != bindingInfo.viewDimension,
+                    "The layout's binding dimension (%s) doesn't match the "
+                    "shader's binding dimension (%s).",
+                    layoutInfo.storageTexture.viewDimension, bindingInfo.viewDimension);
+            } else if constexpr (std::is_same_v<T, BufferBindingInfo>) {
+                // Binding mismatch between shader and bind group is invalid. For example, a
+                // writable binding in the shader with a readonly storage buffer in the bind
+                // group layout is invalid. For internal usage with internal shaders, a storage
+                // binding in the shader with an internal storage buffer in the bind group
+                // layout is also valid.
+                bool validBindingConversion =
+                    (layoutInfo.buffer.type == kInternalStorageBufferBinding &&
+                     bindingInfo.type == wgpu::BufferBindingType::Storage);
+
+                DAWN_INVALID_IF(
+                    layoutInfo.buffer.type != bindingInfo.type && !validBindingConversion,
+                    "The buffer type in the shader (%s) is not compatible with the type in the "
+                    "layout (%s).",
+                    bindingInfo.type, layoutInfo.buffer.type);
+
+                DAWN_INVALID_IF(layoutInfo.buffer.minBindingSize != 0 &&
+                                    bindingInfo.minBindingSize > layoutInfo.buffer.minBindingSize,
+                                "The shader uses more bytes of the buffer (%u) than the layout's "
+                                "minBindingSize (%u).",
+                                bindingInfo.minBindingSize, layoutInfo.buffer.minBindingSize);
+            } else if constexpr (std::is_same_v<T, SamplerBindingInfo>) {
+                DAWN_INVALID_IF(
+                    (layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison) !=
+                        bindingInfo.isComparison,
+                    "The sampler type in the shader (comparison: %u) doesn't match the type in "
+                    "the layout (comparison: %u).",
+                    bindingInfo.isComparison,
+                    layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison);
+            } else if constexpr (std::is_same_v<T, ExternalTextureBindingInfo>) {
+                DAWN_UNREACHABLE();
             }
 
-            DAWN_INVALID_IF(!(shaderInfo.texture.compatibleSampleTypes & requiredType),
-                            "The sample type in the shader is not compatible with the "
-                            "sample type of the layout.");
-
-            DAWN_INVALID_IF(
-                layoutInfo.texture.viewDimension != shaderInfo.texture.viewDimension,
-                "The shader's binding dimension (%s) doesn't match the shader's binding "
-                "dimension (%s).",
-                layoutInfo.texture.viewDimension, shaderInfo.texture.viewDimension);
-            break;
-        }
-
-        case BindingInfoType::StorageTexture: {
-            DAWN_ASSERT(layoutInfo.storageTexture.format != wgpu::TextureFormat::Undefined);
-            DAWN_ASSERT(shaderInfo.storageTexture.format != wgpu::TextureFormat::Undefined);
-
-            DAWN_INVALID_IF(
-                !IsShaderCompatibleWithPipelineLayoutOnStorageTextureAccess(layoutInfo, shaderInfo),
-                "The layout's binding access (%s) isn't compatible with the shader's "
-                "binding access (%s).",
-                layoutInfo.storageTexture.access, shaderInfo.storageTexture.access);
-
-            DAWN_INVALID_IF(layoutInfo.storageTexture.format != shaderInfo.storageTexture.format,
-                            "The layout's binding format (%s) doesn't match the shader's binding "
-                            "format (%s).",
-                            layoutInfo.storageTexture.format, shaderInfo.storageTexture.format);
-
-            DAWN_INVALID_IF(
-                layoutInfo.storageTexture.viewDimension != shaderInfo.storageTexture.viewDimension,
-                "The layout's binding dimension (%s) doesn't match the "
-                "shader's binding dimension (%s).",
-                layoutInfo.storageTexture.viewDimension, shaderInfo.storageTexture.viewDimension);
-            break;
-        }
-
-        case BindingInfoType::Buffer: {
-            // Binding mismatch between shader and bind group is invalid. For example, a
-            // writable binding in the shader with a readonly storage buffer in the bind
-            // group layout is invalid. For internal usage with internal shaders, a storage
-            // binding in the shader with an internal storage buffer in the bind group
-            // layout is also valid.
-            bool validBindingConversion =
-                (layoutInfo.buffer.type == kInternalStorageBufferBinding &&
-                 shaderInfo.buffer.type == wgpu::BufferBindingType::Storage);
-
-            DAWN_INVALID_IF(
-                layoutInfo.buffer.type != shaderInfo.buffer.type && !validBindingConversion,
-                "The buffer type in the shader (%s) is not compatible with the type in the "
-                "layout (%s).",
-                shaderInfo.buffer.type, layoutInfo.buffer.type);
-
-            DAWN_INVALID_IF(layoutInfo.buffer.minBindingSize != 0 &&
-                                shaderInfo.buffer.minBindingSize > layoutInfo.buffer.minBindingSize,
-                            "The shader uses more bytes of the buffer (%u) than the layout's "
-                            "minBindingSize (%u).",
-                            shaderInfo.buffer.minBindingSize, layoutInfo.buffer.minBindingSize);
-            break;
-        }
-
-        case BindingInfoType::Sampler:
-            DAWN_INVALID_IF(
-                (layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison) !=
-                    shaderInfo.sampler.isComparison,
-                "The sampler type in the shader (comparison: %u) doesn't match the type in "
-                "the layout (comparison: %u).",
-                shaderInfo.sampler.isComparison,
-                layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison);
-            break;
-
-        case BindingInfoType::ExternalTexture: {
-            DAWN_UNREACHABLE();
-            break;
-        }
-    }
-
-    return {};
+            return {};
+        },
+        shaderInfo.bindingInfo);
 }
 MaybeError ValidateCompatibilityWithBindGroupLayout(DeviceBase* device,
                                                     BindGroupIndex group,
@@ -813,60 +842,77 @@
          inspector->GetResourceBindings(entryPoint.name)) {
         ShaderBindingInfo info;
 
-        info.bindingType = TintResourceTypeToBindingInfoType(resource.resource_type);
         info.name = resource.variable_name;
 
-        switch (info.bindingType) {
-            case BindingInfoType::Buffer:
-                info.buffer.minBindingSize = resource.size;
-                DAWN_TRY_ASSIGN(info.buffer.type,
+        switch (TintResourceTypeToBindingInfoType(resource.resource_type)) {
+            case BindingInfoType::Buffer: {
+                BufferBindingInfo bindingInfo = {};
+                bindingInfo.minBindingSize = resource.size;
+                DAWN_TRY_ASSIGN(bindingInfo.type,
                                 TintResourceTypeToBufferBindingType(resource.resource_type));
+                info.bindingInfo = bindingInfo;
                 break;
-            case BindingInfoType::Sampler:
+            }
+
+            case BindingInfoType::Sampler: {
+                SamplerBindingInfo bindingInfo = {};
                 switch (resource.resource_type) {
                     case tint::inspector::ResourceBinding::ResourceType::kSampler:
-                        info.sampler.isComparison = false;
+                        bindingInfo.isComparison = false;
                         break;
                     case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler:
-                        info.sampler.isComparison = true;
+                        bindingInfo.isComparison = true;
                         break;
                     default:
                         DAWN_UNREACHABLE();
                 }
+                info.bindingInfo = bindingInfo;
                 break;
-            case BindingInfoType::Texture:
-                info.texture.viewDimension =
+            }
+
+            case BindingInfoType::Texture: {
+                SampledTextureBindingInfo bindingInfo = {};
+                bindingInfo.viewDimension =
                     TintTextureDimensionToTextureViewDimension(resource.dim);
                 if (resource.resource_type ==
                         tint::inspector::ResourceBinding::ResourceType::kDepthTexture ||
                     resource.resource_type ==
                         tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture) {
-                    info.texture.compatibleSampleTypes = SampleTypeBit::Depth;
+                    bindingInfo.compatibleSampleTypes = SampleTypeBit::Depth;
                 } else {
-                    info.texture.compatibleSampleTypes =
+                    bindingInfo.compatibleSampleTypes =
                         TintSampledKindToSampleTypeBit(resource.sampled_kind);
                 }
-                info.texture.multisampled =
+                bindingInfo.multisampled =
                     resource.resource_type ==
                         tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture ||
                     resource.resource_type ==
                         tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture;
-
+                info.bindingInfo = bindingInfo;
                 break;
-            case BindingInfoType::StorageTexture:
-                DAWN_TRY_ASSIGN(info.storageTexture.access,
+            }
+
+            case BindingInfoType::StorageTexture: {
+                StorageTextureBindingInfo bindingInfo = {};
+                DAWN_TRY_ASSIGN(bindingInfo.access,
                                 TintResourceTypeToStorageTextureAccess(resource.resource_type));
-                info.storageTexture.format = TintImageFormatToTextureFormat(resource.image_format);
-                info.storageTexture.viewDimension =
+                bindingInfo.format = TintImageFormatToTextureFormat(resource.image_format);
+                bindingInfo.viewDimension =
                     TintTextureDimensionToTextureViewDimension(resource.dim);
 
-                DAWN_INVALID_IF(info.storageTexture.format == wgpu::TextureFormat::BGRA8Unorm &&
+                DAWN_INVALID_IF(bindingInfo.format == wgpu::TextureFormat::BGRA8Unorm &&
                                     !device->HasFeature(Feature::BGRA8UnormStorage),
                                 "BGRA8Unorm storage textures are not supported if optional feature "
                                 "bgra8unorm-storage is not supported.");
+
+                info.bindingInfo = bindingInfo;
                 break;
-            case BindingInfoType::ExternalTexture:
+            }
+
+            case BindingInfoType::ExternalTexture: {
+                info.bindingInfo.emplace<ExternalTextureBindingInfo>();
                 break;
+            }
             default:
                 return DAWN_VALIDATION_ERROR("Unknown binding type in Shader");
         }
diff --git a/src/dawn/native/ShaderModule.h b/src/dawn/native/ShaderModule.h
index 5847e98..c31a16c 100644
--- a/src/dawn/native/ShaderModule.h
+++ b/src/dawn/native/ShaderModule.h
@@ -33,6 +33,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include "absl/container/flat_hash_map.h"
@@ -156,18 +157,34 @@
 
 // Mirrors wgpu::SamplerBindingLayout but instead stores a single boolean
 // for isComparison instead of a wgpu::SamplerBindingType enum.
-struct ShaderSamplerBindingInfo {
+struct SamplerBindingInfo {
     bool isComparison;
 };
 
 // Mirrors wgpu::TextureBindingLayout but instead has a set of compatible sampleTypes
 // instead of a single enum.
-struct ShaderTextureBindingInfo {
+struct SampledTextureBindingInfo {
     SampleTypeBit compatibleSampleTypes;
     wgpu::TextureViewDimension viewDimension;
     bool multisampled;
 };
 
+// Mirrors wgpu::ExternalTextureBindingLayout
+struct ExternalTextureBindingInfo {};
+
+// Mirrors wgpu::BufferBindingLayout
+struct BufferBindingInfo {
+    wgpu::BufferBindingType type;
+    uint64_t minBindingSize;
+};
+
+// Mirrors wgpu::StorageTextureBindingLayout
+struct StorageTextureBindingInfo {
+    wgpu::TextureFormat format;
+    wgpu::TextureViewDimension viewDimension;
+    wgpu::StorageTextureAccess access;
+};
+
 // Per-binding shader metadata contains some SPIRV specific information in addition to
 // most of the frontend per-binding information.
 struct ShaderBindingInfo {
@@ -176,15 +193,16 @@
     uint32_t base_type_id;
 
     BindingNumber binding;
-    BindingInfoType bindingType;
 
     // The variable name of the binding resource.
     std::string name;
 
-    BufferBindingLayout buffer;
-    ShaderSamplerBindingInfo sampler;
-    ShaderTextureBindingInfo texture;
-    StorageTextureBindingLayout storageTexture;
+    std::variant<BufferBindingInfo,
+                 SamplerBindingInfo,
+                 SampledTextureBindingInfo,
+                 StorageTextureBindingInfo,
+                 ExternalTextureBindingInfo>
+        bindingInfo;
 };
 
 using BindingGroupInfoMap = std::map<BindingNumber, ShaderBindingInfo>;
diff --git a/src/dawn/native/d3d12/ShaderModuleD3D12.cpp b/src/dawn/native/d3d12/ShaderModuleD3D12.cpp
index 408a45b..6bf7ba7 100644
--- a/src/dawn/native/d3d12/ShaderModuleD3D12.cpp
+++ b/src/dawn/native/d3d12/ShaderModuleD3D12.cpp
@@ -195,7 +195,7 @@
         // the Tint AST to make the "bindings" decoration match the offset chosen by
         // d3d12::BindGroupLayout so that Tint produces HLSL with the correct registers
         // assigned to each interface variable.
-        for (const auto& [binding, bindingInfo] : moduleGroupBindingInfo) {
+        for (const auto& [binding, shaderBindingInfo] : moduleGroupBindingInfo) {
             BindingIndex bindingIndex = bgl->GetBindingIndex(binding);
             BindingPoint srcBindingPoint{static_cast<uint32_t>(group),
                                          static_cast<uint32_t>(binding)};
@@ -205,12 +205,18 @@
                 bindingRemapper.binding_points.emplace(srcBindingPoint, dstBindingPoint);
             }
 
+            const auto* bufferBindingInfo =
+                std::get_if<BufferBindingInfo>(&shaderBindingInfo.bindingInfo);
+            if (bufferBindingInfo == nullptr) {
+                continue;
+            }
+
             // Declaring a read-only storage buffer in HLSL but specifying a storage
             // buffer in the BGL produces the wrong output. Force read-only storage
             // buffer bindings to be treated as UAV instead of SRV. Internal storage
             // buffer is a storage buffer used in the internal pipeline.
             const bool forceStorageBufferAsUAV =
-                (bindingInfo.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage &&
+                (bufferBindingInfo->type == wgpu::BufferBindingType::ReadOnlyStorage &&
                  (bgl->GetBindingInfo(bindingIndex).buffer.type ==
                       wgpu::BufferBindingType::Storage ||
                   bgl->GetBindingInfo(bindingIndex).buffer.type == kInternalStorageBufferBinding));
@@ -242,8 +248,8 @@
             //     }
             //     return 0u;
             // }
-            if ((bindingInfo.buffer.type == wgpu::BufferBindingType::Storage ||
-                 bindingInfo.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage) &&
+            if ((bufferBindingInfo->type == wgpu::BufferBindingType::Storage ||
+                 bufferBindingInfo->type == wgpu::BufferBindingType::ReadOnlyStorage) &&
                 !bgl->GetBindingInfo(bindingIndex).buffer.hasDynamicOffset) {
                 req.hlsl.tintOptions.binding_points_ignored_in_robustness_transform.emplace_back(
                     srcBindingPoint);
@@ -351,7 +357,7 @@
     // outside of the compilation.
     d3d::CompiledShader result = compiledShader.Acquire();
     result.hlslSource = "";
-    return result;
+    return std::move(result);
 }
 
 }  // namespace dawn::native::d3d12
diff --git a/src/dawn/native/metal/ShaderModuleMTL.mm b/src/dawn/native/metal/ShaderModuleMTL.mm
index 7eea7d4..82b9312 100644
--- a/src/dawn/native/metal/ShaderModuleMTL.mm
+++ b/src/dawn/native/metal/ShaderModuleMTL.mm
@@ -132,7 +132,7 @@
     for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
         const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group));
 
-        for (const auto& [binding, bindingInfo] : moduleBindingInfo[group]) {
+        for (const auto& [binding, shaderBindingInfo] : moduleBindingInfo[group]) {
             tint::BindingPoint srcBindingPoint{static_cast<uint32_t>(group),
                                                static_cast<uint32_t>(binding)};
 
@@ -142,68 +142,65 @@
 
             tint::BindingPoint dstBindingPoint{0, shaderIndex};
 
-            // Use the ShaderIndex as the indices for the buffer size lookups in the array length
-            // uniform transform. This is used to compute the size of variable length arrays in
-            // storage buffers.
-            if (bindingInfo.buffer.type == wgpu::BufferBindingType::Storage ||
-                bindingInfo.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage ||
-                bindingInfo.buffer.type == kInternalStorageBufferBinding) {
-                arrayLengthFromUniform.bindpoint_to_size_index.emplace(dstBindingPoint,
-                                                                       dstBindingPoint.binding);
-            }
+            // TODO(dawn:2370): implement a helper in dawn/utils to simplify the call of std::visit.
+            std::visit(
+                [&](const auto& bindingInfo) {
+                    using T = std::decay_t<decltype(bindingInfo)>;
 
-            switch (bindingInfo.bindingType) {
-                case BindingInfoType::Buffer:
-                    switch (bindingInfo.buffer.type) {
-                        case wgpu::BufferBindingType::Uniform:
-                            bindings.uniform.emplace(
-                                srcBindingPoint,
-                                tint::msl::writer::binding::Uniform{dstBindingPoint.binding});
-                            break;
-                        case kInternalStorageBufferBinding:
-                        case wgpu::BufferBindingType::Storage:
-                        case wgpu::BufferBindingType::ReadOnlyStorage:
-                            bindings.storage.emplace(
-                                srcBindingPoint,
-                                tint::msl::writer::binding::Storage{dstBindingPoint.binding});
-                            break;
-                        case wgpu::BufferBindingType::Undefined:
-                            DAWN_UNREACHABLE();
-                            break;
+                    if constexpr (std::is_same_v<T, BufferBindingInfo>) {
+                        switch (bindingInfo.type) {
+                            case wgpu::BufferBindingType::Uniform:
+                                bindings.uniform.emplace(
+                                    srcBindingPoint,
+                                    tint::msl::writer::binding::Uniform{dstBindingPoint.binding});
+                                break;
+                            case kInternalStorageBufferBinding:
+                            case wgpu::BufferBindingType::Storage:
+                            case wgpu::BufferBindingType::ReadOnlyStorage:
+                                bindings.storage.emplace(
+                                    srcBindingPoint,
+                                    tint::msl::writer::binding::Storage{dstBindingPoint.binding});
+                                // Use the ShaderIndex as the indices for the buffer size lookups in
+                                // the array length uniform transform. This is used to compute the
+                                // size of variable length arrays in storage buffers.
+                                arrayLengthFromUniform.bindpoint_to_size_index.emplace(
+                                    dstBindingPoint, dstBindingPoint.binding);
+                                break;
+                            case wgpu::BufferBindingType::Undefined:
+                                DAWN_UNREACHABLE();
+                                break;
+                        }
+                    } else if constexpr (std::is_same_v<T, SamplerBindingInfo>) {
+                        bindings.sampler.emplace(
+                            srcBindingPoint,
+                            tint::msl::writer::binding::Sampler{dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, SampledTextureBindingInfo>) {
+                        bindings.texture.emplace(
+                            srcBindingPoint,
+                            tint::msl::writer::binding::Texture{dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, StorageTextureBindingInfo>) {
+                        bindings.storage_texture.emplace(
+                            srcBindingPoint,
+                            tint::msl::writer::binding::StorageTexture{dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, ExternalTextureBindingInfo>) {
+                        const auto& etBindingMap = bgl->GetExternalTextureBindingExpansionMap();
+                        const auto& expansion = etBindingMap.find(binding);
+                        DAWN_ASSERT(expansion != etBindingMap.end());
+
+                        const auto& bindingExpansion = expansion->second;
+                        tint::msl::writer::binding::BindingInfo plane0{
+                            static_cast<uint32_t>(shaderIndex)};
+                        tint::msl::writer::binding::BindingInfo plane1{
+                            bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.plane1)]};
+                        tint::msl::writer::binding::BindingInfo metadata{
+                            bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.params)]};
+
+                        bindings.external_texture.emplace(
+                            srcBindingPoint,
+                            tint::msl::writer::binding::ExternalTexture{metadata, plane0, plane1});
                     }
-                    break;
-                case BindingInfoType::Sampler:
-                    bindings.sampler.emplace(srcBindingPoint, tint::msl::writer::binding::Sampler{
-                                                                  dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::Texture:
-                    bindings.texture.emplace(srcBindingPoint, tint::msl::writer::binding::Texture{
-                                                                  dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::StorageTexture:
-                    bindings.storage_texture.emplace(
-                        srcBindingPoint,
-                        tint::msl::writer::binding::StorageTexture{dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::ExternalTexture: {
-                    const auto& etBindingMap = bgl->GetExternalTextureBindingExpansionMap();
-                    const auto& expansion = etBindingMap.find(binding);
-                    DAWN_ASSERT(expansion != etBindingMap.end());
-
-                    const auto& bindingExpansion = expansion->second;
-                    tint::msl::writer::binding::BindingInfo plane0{
-                        static_cast<uint32_t>(shaderIndex)};
-                    tint::msl::writer::binding::BindingInfo plane1{
-                        bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.plane1)]};
-                    tint::msl::writer::binding::BindingInfo metadata{
-                        bindingIndexInfo[bgl->GetBindingIndex(bindingExpansion.params)]};
-
-                    bindings.external_texture.emplace(
-                        srcBindingPoint,
-                        tint::msl::writer::binding::ExternalTexture{metadata, plane0, plane1});
-                    break;
-                }
-            }
+                },
+                shaderBindingInfo.bindingInfo);
         }
     }
 
diff --git a/src/dawn/native/opengl/ShaderModuleGL.cpp b/src/dawn/native/opengl/ShaderModuleGL.cpp
index 821a350..9c39077 100644
--- a/src/dawn/native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn/native/opengl/ShaderModuleGL.cpp
@@ -206,7 +206,7 @@
 
             // For buffer bindings that can be sharable across stages, we need to rename them to
             // avoid GL program link failures due to block naming issues.
-            if (bindingInfo.bindingType == BindingInfoType::Buffer &&
+            if (std::holds_alternative<BufferBindingInfo>(bindingInfo.bindingInfo) &&
                 stage != SingleShaderStage::Compute) {
                 req.bufferBindingVariables.emplace_back(bindingInfo.name);
             }
diff --git a/src/dawn/native/vulkan/ShaderModuleVk.cpp b/src/dawn/native/vulkan/ShaderModuleVk.cpp
index ba70051..02530fa 100644
--- a/src/dawn/native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn/native/vulkan/ShaderModuleVk.cpp
@@ -238,72 +238,76 @@
     for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
         const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group));
 
-        for (const auto& [binding, bindingInfo] : moduleBindingInfo[group]) {
+        for (const auto& currentModuleBindingInfo : moduleBindingInfo[group]) {
+            // We cannot use structured binding here because lambda expressions can only capture
+            // variables, while structured binding doesn't introduce variables.
+            const auto& binding = currentModuleBindingInfo.first;
+            const auto& shaderBindingInfo = currentModuleBindingInfo.second;
             tint::BindingPoint srcBindingPoint{static_cast<uint32_t>(group),
                                                static_cast<uint32_t>(binding)};
 
             tint::BindingPoint dstBindingPoint{
                 static_cast<uint32_t>(group), static_cast<uint32_t>(bgl->GetBindingIndex(binding))};
 
-            switch (bindingInfo.bindingType) {
-                case BindingInfoType::Buffer:
-                    switch (bindingInfo.buffer.type) {
-                        case wgpu::BufferBindingType::Uniform:
-                            bindings.uniform.emplace(
-                                srcBindingPoint,
-                                tint::spirv::writer::binding::Uniform{dstBindingPoint.group,
-                                                                      dstBindingPoint.binding});
-                            break;
-                        case kInternalStorageBufferBinding:
-                        case wgpu::BufferBindingType::Storage:
-                        case wgpu::BufferBindingType::ReadOnlyStorage:
-                            bindings.storage.emplace(
-                                srcBindingPoint,
-                                tint::spirv::writer::binding::Storage{dstBindingPoint.group,
-                                                                      dstBindingPoint.binding});
-                            break;
-                        case wgpu::BufferBindingType::Undefined:
-                            DAWN_UNREACHABLE();
-                            break;
+            // TODO(dawn:2370): implement a helper in dawn/utils to simplify the call of std::visit.
+            std::visit(
+                [&](const auto& bindingInfo) {
+                    using T = std::decay_t<decltype(bindingInfo)>;
+
+                    if constexpr (std::is_same_v<T, BufferBindingInfo>) {
+                        switch (bindingInfo.type) {
+                            case wgpu::BufferBindingType::Uniform:
+                                bindings.uniform.emplace(
+                                    srcBindingPoint,
+                                    tint::spirv::writer::binding::Uniform{dstBindingPoint.group,
+                                                                          dstBindingPoint.binding});
+                                break;
+                            case kInternalStorageBufferBinding:
+                            case wgpu::BufferBindingType::Storage:
+                            case wgpu::BufferBindingType::ReadOnlyStorage:
+                                bindings.storage.emplace(
+                                    srcBindingPoint,
+                                    tint::spirv::writer::binding::Storage{dstBindingPoint.group,
+                                                                          dstBindingPoint.binding});
+                                break;
+                            case wgpu::BufferBindingType::Undefined:
+                                DAWN_UNREACHABLE();
+                                break;
+                        }
+                    } else if constexpr (std::is_same_v<T, SamplerBindingInfo>) {
+                        bindings.sampler.emplace(
+                            srcBindingPoint, tint::spirv::writer::binding::Sampler{
+                                                 dstBindingPoint.group, dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, SampledTextureBindingInfo>) {
+                        bindings.texture.emplace(
+                            srcBindingPoint, tint::spirv::writer::binding::Texture{
+                                                 dstBindingPoint.group, dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, StorageTextureBindingInfo>) {
+                        bindings.storage_texture.emplace(
+                            srcBindingPoint, tint::spirv::writer::binding::StorageTexture{
+                                                 dstBindingPoint.group, dstBindingPoint.binding});
+                    } else if constexpr (std::is_same_v<T, ExternalTextureBindingInfo>) {
+                        const auto& bindingMap = bgl->GetExternalTextureBindingExpansionMap();
+                        const auto& expansion = bindingMap.find(binding);
+                        DAWN_ASSERT(expansion != bindingMap.end());
+
+                        const auto& bindingExpansion = expansion->second;
+                        tint::spirv::writer::binding::BindingInfo plane0{
+                            static_cast<uint32_t>(group),
+                            static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.plane0))};
+                        tint::spirv::writer::binding::BindingInfo plane1{
+                            static_cast<uint32_t>(group),
+                            static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.plane1))};
+                        tint::spirv::writer::binding::BindingInfo metadata{
+                            static_cast<uint32_t>(group),
+                            static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.params))};
+
+                        bindings.external_texture.emplace(
+                            srcBindingPoint, tint::spirv::writer::binding::ExternalTexture{
+                                                 metadata, plane0, plane1});
                     }
-                    break;
-                case BindingInfoType::Sampler:
-                    bindings.sampler.emplace(srcBindingPoint,
-                                             tint::spirv::writer::binding::Sampler{
-                                                 dstBindingPoint.group, dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::Texture:
-                    bindings.texture.emplace(srcBindingPoint,
-                                             tint::spirv::writer::binding::Texture{
-                                                 dstBindingPoint.group, dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::StorageTexture:
-                    bindings.storage_texture.emplace(
-                        srcBindingPoint, tint::spirv::writer::binding::StorageTexture{
-                                             dstBindingPoint.group, dstBindingPoint.binding});
-                    break;
-                case BindingInfoType::ExternalTexture: {
-                    const auto& bindingMap = bgl->GetExternalTextureBindingExpansionMap();
-                    const auto& expansion = bindingMap.find(binding);
-                    DAWN_ASSERT(expansion != bindingMap.end());
-
-                    const auto& bindingExpansion = expansion->second;
-                    tint::spirv::writer::binding::BindingInfo plane0{
-                        static_cast<uint32_t>(group),
-                        static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.plane0))};
-                    tint::spirv::writer::binding::BindingInfo plane1{
-                        static_cast<uint32_t>(group),
-                        static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.plane1))};
-                    tint::spirv::writer::binding::BindingInfo metadata{
-                        static_cast<uint32_t>(group),
-                        static_cast<uint32_t>(bgl->GetBindingIndex(bindingExpansion.params))};
-
-                    bindings.external_texture.emplace(
-                        srcBindingPoint,
-                        tint::spirv::writer::binding::ExternalTexture{metadata, plane0, plane1});
-                    break;
-                }
-            }
+                },
+                shaderBindingInfo.bindingInfo);
         }
     }