Implement C INIT macros

Add a unittest that the C defaults match the C++ defaults
Use the init macros in some existing tests that use the C api

Fixed: dawn:2243
Change-Id: I4bf7492a19babac082872cad9bc151dbff3d5a1f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185968
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/generator/templates/api.h b/generator/templates/api.h
index b541b9c..c4c1a3d 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -75,6 +75,18 @@
 
 #define WGPU_BREAKING_REFERENCE_ADDREF
 
+#if defined(__cplusplus)
+#  if __cplusplus >= 201103L
+#    define {{API}}_MAKE_INIT_STRUCT(type, value) (type value)
+#  else
+#    define {{API}}_MAKE_INIT_STRUCT(type, value) value
+#  endif
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#  define {{API}}_MAKE_INIT_STRUCT(type, value) ((type) value)
+#else
+#  define {{API}}_MAKE_INIT_STRUCT(type, value) value
+#endif
+
 {% for constant in by_category["constant"] %}
     #define {{API}}_{{constant.name.SNAKE_CASE()}} {{constant.value}}
 {% endfor %}
@@ -126,6 +138,26 @@
     {{API}}SType sType;
 } {{API}}ChainedStructOut {{API}}_STRUCTURE_ATTRIBUTE;
 
+{% macro render_c_default_value(member) -%}
+    {%- if member.annotation in ["*", "const*"] and member.optional or member.default_value == "nullptr" -%}
+        nullptr
+    {%- elif member.type.category == "object" and member.optional -%}
+        nullptr
+    {%- elif member.type.category in ["enum", "bitmask"] and member.default_value != None -%}
+        {{as_cEnum(member.type.name, Name(member.default_value))}}
+    {%- elif member.default_value != None -%}
+        {{member.default_value}}
+    {%- elif member.type.category == "structure" and member.annotation == "value" -%}
+        {{API}}_{{member.type.name.SNAKE_CASE()}}_INIT
+    {%- else -%}
+        {{- assert(member.json_data.get("no_default", false) == false) -}}
+        {{- assert(member.default_value == None) -}}
+        {}
+    {%- endif -%}
+{% endmacro %}
+
+#define {{API}}_COMMA ,
+
 {% for type in by_category["structure"] %}
     {% for root in type.chain_roots %}
         // Can be chained in {{as_cType(root.name)}}
@@ -148,6 +180,18 @@
         {% endfor %}
     } {{as_cType(type.name)}} {{API}}_STRUCTURE_ATTRIBUTE;
 
+    #define {{API}}_{{type.name.SNAKE_CASE()}}_INIT {{API}}_MAKE_INIT_STRUCT({{as_cType(type.name)}}, { \
+        {% if type.extensible %}
+            /*.nextInChain=*/nullptr {{API}}_COMMA \
+        {% endif %}
+        {% if type.chained %}
+            /*.chain=*/{} {{API}}_COMMA \
+        {% endif %}
+        {% for member in type.members %}
+            /*.{{as_varName(member.name)}}=*/{{render_c_default_value(member)}} {{API}}_COMMA \
+        {% endfor %}
+    })
+
 {% endfor %}
 {% for typeDef in by_category["typedef"] %}
     // {{as_cType(typeDef.name)}} is deprecated.
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index fd3aa8e..e858d83 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -327,6 +327,7 @@
     "unittests/CommandAllocatorTests.cpp",
     "unittests/ConcurrentCacheTests.cpp",
     "unittests/ContentLessObjectCacheTests.cpp",
+    "unittests/DefaultTests.cpp",
     "unittests/EnumClassBitmasksTests.cpp",
     "unittests/EnumMaskIteratorTests.cpp",
     "unittests/EnumeratorTests.cpp",
diff --git a/src/dawn/tests/unittests/DefaultTests.cpp b/src/dawn/tests/unittests/DefaultTests.cpp
new file mode 100644
index 0000000..422dd24
--- /dev/null
+++ b/src/dawn/tests/unittests/DefaultTests.cpp
@@ -0,0 +1,48 @@
+// 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 "dawn/webgpu.h"
+#include "dawn/webgpu_cpp.h"
+#include "gtest/gtest.h"
+
+// Test that the C init structs match the C++ default values
+TEST(DefaultTests, CMatchesCpp) {
+    // Test render pipeline descriptor.
+    // It's non-trivial and has nested structs too.
+    wgpu::RenderPipelineDescriptor cppDesc = {};
+    WGPURenderPipelineDescriptor cDesc = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT;
+
+    EXPECT_EQ(cppDesc.primitive.topology,
+              static_cast<wgpu::PrimitiveTopology>(cDesc.primitive.topology));
+    EXPECT_EQ(cppDesc.primitive.stripIndexFormat,
+              static_cast<wgpu::IndexFormat>(cDesc.primitive.stripIndexFormat));
+    EXPECT_EQ(cppDesc.primitive.frontFace, static_cast<wgpu::FrontFace>(cDesc.primitive.frontFace));
+    EXPECT_EQ(cppDesc.primitive.cullMode, static_cast<wgpu::CullMode>(cDesc.primitive.cullMode));
+    EXPECT_EQ(cppDesc.multisample.count, cDesc.multisample.count);
+    EXPECT_EQ(cppDesc.multisample.mask, cDesc.multisample.mask);
+    EXPECT_EQ(cppDesc.multisample.alphaToCoverageEnabled, cDesc.multisample.alphaToCoverageEnabled);
+}
diff --git a/src/dawn/tests/unittests/wire/WireArgumentTests.cpp b/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
index c2f229f..b2e3b95 100644
--- a/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
@@ -64,18 +64,14 @@
 // Test that the wire is able to send arrays of numerical values
 TEST_F(WireArgumentTests, ValueArrayArgument) {
     // Create a bindgroup.
-    WGPUBindGroupLayoutDescriptor bglDescriptor = {};
-    bglDescriptor.entryCount = 0;
-    bglDescriptor.entries = nullptr;
+    WGPUBindGroupLayoutDescriptor bglDescriptor = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT;
 
     WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
     WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
     EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
 
-    WGPUBindGroupDescriptor bindGroupDescriptor = {};
+    WGPUBindGroupDescriptor bindGroupDescriptor = WGPU_BIND_GROUP_DESCRIPTOR_INIT;
     bindGroupDescriptor.layout = bgl;
-    bindGroupDescriptor.entryCount = 0;
-    bindGroupDescriptor.entries = nullptr;
 
     WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescriptor);
     WGPUBindGroup apiBindGroup = api.GetNewBindGroup();
@@ -111,73 +107,49 @@
 // Test that the wire is able to send C strings
 TEST_F(WireArgumentTests, CStringArgument) {
     // Create shader module
-    WGPUShaderModuleDescriptor vertexDescriptor = {};
+    WGPUShaderModuleDescriptor vertexDescriptor = WGPU_SHADER_MODULE_DESCRIPTOR_INIT;
     WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor);
     WGPUShaderModule apiVsModule = api.GetNewShaderModule();
     EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
 
     // Create the color state descriptor
-    WGPUBlendComponent blendComponent = {};
-    blendComponent.operation = WGPUBlendOperation_Add;
-    blendComponent.srcFactor = WGPUBlendFactor_One;
-    blendComponent.dstFactor = WGPUBlendFactor_One;
-    WGPUBlendState blendState = {};
+    WGPUBlendComponent blendComponent = WGPU_BLEND_COMPONENT_INIT;
+    WGPUBlendState blendState = WGPU_BLEND_STATE_INIT;
     blendState.alpha = blendComponent;
     blendState.color = blendComponent;
-    WGPUColorTargetState colorTargetState = {};
+    WGPUColorTargetState colorTargetState = WGPU_COLOR_TARGET_STATE_INIT;
     colorTargetState.format = WGPUTextureFormat_RGBA8Unorm;
     colorTargetState.blend = &blendState;
-    colorTargetState.writeMask = WGPUColorWriteMask_All;
 
     // Create the depth-stencil state
-    WGPUStencilFaceState stencilFace = {};
-    stencilFace.compare = WGPUCompareFunction_Always;
-    stencilFace.failOp = WGPUStencilOperation_Keep;
-    stencilFace.depthFailOp = WGPUStencilOperation_Keep;
-    stencilFace.passOp = WGPUStencilOperation_Keep;
+    WGPUStencilFaceState stencilFace = WGPU_STENCIL_FACE_STATE_INIT;
 
-    WGPUDepthStencilState depthStencilState = {};
+    WGPUDepthStencilState depthStencilState = WGPU_DEPTH_STENCIL_STATE_INIT;
     depthStencilState.format = WGPUTextureFormat_Depth24PlusStencil8;
-    depthStencilState.depthWriteEnabled = false;
     depthStencilState.depthCompare = WGPUCompareFunction_Always;
     depthStencilState.stencilBack = stencilFace;
     depthStencilState.stencilFront = stencilFace;
-    depthStencilState.stencilReadMask = 0xff;
-    depthStencilState.stencilWriteMask = 0xff;
-    depthStencilState.depthBias = 0;
-    depthStencilState.depthBiasSlopeScale = 0.0;
-    depthStencilState.depthBiasClamp = 0.0;
 
     // Create the pipeline layout
-    WGPUPipelineLayoutDescriptor layoutDescriptor = {};
-    layoutDescriptor.bindGroupLayoutCount = 0;
-    layoutDescriptor.bindGroupLayouts = nullptr;
+    WGPUPipelineLayoutDescriptor layoutDescriptor = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT;
     WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device, &layoutDescriptor);
     WGPUPipelineLayout apiLayout = api.GetNewPipelineLayout();
     EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout));
 
     // Create pipeline
-    WGPURenderPipelineDescriptor pipelineDescriptor = {};
+    WGPURenderPipelineDescriptor pipelineDescriptor = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT;
 
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.vertex.entryPoint = "main";
-    pipelineDescriptor.vertex.bufferCount = 0;
-    pipelineDescriptor.vertex.buffers = nullptr;
 
-    WGPUFragmentState fragment = {};
+    WGPUFragmentState fragment = WGPU_FRAGMENT_STATE_INIT;
     fragment.module = vsModule;
     fragment.entryPoint = "main";
     fragment.targetCount = 1;
     fragment.targets = &colorTargetState;
     pipelineDescriptor.fragment = &fragment;
 
-    pipelineDescriptor.multisample.count = 1;
-    pipelineDescriptor.multisample.mask = 0xFFFFFFFF;
-    pipelineDescriptor.multisample.alphaToCoverageEnabled = false;
     pipelineDescriptor.layout = layout;
-    pipelineDescriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList;
-    pipelineDescriptor.primitive.frontFace = WGPUFrontFace_CCW;
-    pipelineDescriptor.primitive.cullMode = WGPUCullMode_None;
     pipelineDescriptor.depthStencil = &depthStencilState;
 
     wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor);
@@ -199,7 +171,7 @@
     WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
     EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
 
-    WGPUBufferDescriptor descriptor = {};
+    WGPUBufferDescriptor descriptor = WGPU_BUFFER_DESCRIPTOR_INIT;
     descriptor.size = 8;
     descriptor.usage =
         static_cast<WGPUBufferUsage>(WGPUBufferUsage_CopySrc | WGPUBufferUsage_CopyDst);
@@ -251,14 +223,11 @@
 
 // Test that the wire is able to send structures that contain pure values (non-objects)
 TEST_F(WireArgumentTests, StructureOfValuesArgument) {
-    WGPUSamplerDescriptor descriptor = {};
+    WGPUSamplerDescriptor descriptor = WGPU_SAMPLER_DESCRIPTOR_INIT;
     descriptor.magFilter = WGPUFilterMode_Linear;
-    descriptor.minFilter = WGPUFilterMode_Nearest;
     descriptor.mipmapFilter = WGPUMipmapFilterMode_Linear;
-    descriptor.addressModeU = WGPUAddressMode_ClampToEdge;
     descriptor.addressModeV = WGPUAddressMode_Repeat;
     descriptor.addressModeW = WGPUAddressMode_MirrorRepeat;
-    descriptor.lodMinClamp = kLodMin;
     descriptor.lodMaxClamp = kLodMax;
     descriptor.compare = WGPUCompareFunction_Never;
 
@@ -284,15 +253,13 @@
 
 // Test that the wire is able to send structures that contain objects
 TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) {
-    WGPUBindGroupLayoutDescriptor bglDescriptor = {};
-    bglDescriptor.entryCount = 0;
-    bglDescriptor.entries = nullptr;
+    WGPUBindGroupLayoutDescriptor bglDescriptor = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT;
 
     WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor);
     WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
     EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
 
-    WGPUPipelineLayoutDescriptor descriptor = {};
+    WGPUPipelineLayoutDescriptor descriptor = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT;
     descriptor.bindGroupLayoutCount = 1;
     descriptor.bindGroupLayouts = &bgl;
 
@@ -337,7 +304,7 @@
          {},
          {}},
     };
-    WGPUBindGroupLayoutDescriptor bglDescriptor = {};
+    WGPUBindGroupLayoutDescriptor bglDescriptor = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT;
     bglDescriptor.entryCount = NUM_BINDINGS;
     bglDescriptor.entries = entries;
 
@@ -367,7 +334,7 @@
 TEST_F(WireArgumentTests, DISABLED_NullptrInArray) {
     WGPUBindGroupLayout nullBGL = nullptr;
 
-    WGPUPipelineLayoutDescriptor descriptor = {};
+    WGPUPipelineLayoutDescriptor descriptor = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT;
     descriptor.bindGroupLayoutCount = 1;
     descriptor.bindGroupLayouts = &nullBGL;