Update WGSL syntax for end2end tests

Changes I/O to use function parameters and return values, removes
unnecessary "-> void" return types, and changes "const" to "let".

BUG: dawn:755
Change-Id: Iabbfcc280fae37d73cba6a2f7e2215ed579a04e0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/47700
Reviewed-by: Brandon Jones <bajones@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
diff --git a/src/tests/end2end/BindGroupTests.cpp b/src/tests/end2end/BindGroupTests.cpp
index 8566678..6406014 100644
--- a/src/tests/end2end/BindGroupTests.cpp
+++ b/src/tests/end2end/BindGroupTests.cpp
@@ -46,16 +46,14 @@
 
     wgpu::ShaderModule MakeSimpleVSModule() const {
         return utils::CreateShaderModule(device, R"(
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-             const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+             let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
-            Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+            return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
         })");
     }
 
@@ -63,8 +61,6 @@
         ASSERT(bindingTypes.size() <= kMaxBindGroups);
 
         std::ostringstream fs;
-        fs << "[[location(0)]] var<out> fragColor : vec4<f32>;\n";
-
         for (size_t i = 0; i < bindingTypes.size(); ++i) {
             fs << "[[block]] struct Buffer" << i << R"( {
                 color : vec4<f32>;
@@ -84,10 +80,12 @@
             }
         }
 
-        fs << "\n[[stage(fragment)]] fn main() {\n";
+        fs << "\n[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32>{\n";
+        fs << "var fragColor : vec4<f32> = vec4<f32>();\n";
         for (size_t i = 0; i < bindingTypes.size(); ++i) {
             fs << "fragColor = fragColor + buffer" << i << ".color;\n";
         }
+        fs << "return fragColor;\n";
         fs << "}\n";
         return utils::CreateShaderModule(device, fs.str().c_str());
     }
@@ -165,17 +163,15 @@
 
         [[group(0), binding(0)]] var <uniform> vertexUbo : VertexUniformBuffer;
 
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+            let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
             var transform : mat2x2<f32> = mat2x2<f32>(vertexUbo.transform.xy, vertexUbo.transform.zw);
-            Position = vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
+            return vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -184,10 +180,8 @@
         };
         [[group(0), binding(1)]] var <uniform> fragmentUbo : FragmentUniformBuffer;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = fragmentUbo.color;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return fragmentUbo.color;
         })");
 
     utils::ComboRenderPipelineDescriptor2 textureDescriptor;
@@ -246,28 +240,24 @@
         };
         [[group(0), binding(0)]] var <uniform> vertexUbo : VertexUniformBuffer;
 
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+            let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
             var transform : mat2x2<f32> = mat2x2<f32>(vertexUbo.transform.xy, vertexUbo.transform.zw);
-            Position = vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
+            return vec4<f32>(transform * pos[VertexIndex], 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
         [[group(0), binding(1)]] var samp : sampler;
         [[group(0), binding(2)]] var tex : texture_2d<f32>;
-        [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = textureSample(tex, samp, FragCoord.xy);
+        [[stage(fragment)]]
+        fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
+            return textureSample(tex, samp, FragCoord.xy);
         })");
 
     utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
@@ -361,16 +351,14 @@
         [[group(0), binding(0)]] var <uniform> vertexUbo1 : VertexUniformBuffer1;
         [[group(1), binding(0)]] var <uniform> vertexUbo2 : VertexUniformBuffer2;
 
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+            let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
-            Position = vec4<f32>(mat2x2<f32>(
+            return vec4<f32>(mat2x2<f32>(
                 vertexUbo1.transform.xy + vertexUbo2.transform.xy,
                 vertexUbo1.transform.zw + vertexUbo2.transform.zw
             ) * pos[VertexIndex], 0.0, 1.0);
@@ -390,10 +378,8 @@
         [[group(0), binding(1)]] var <uniform> fragmentUbo1 : FragmentUniformBuffer1;
         [[group(1), binding(1)]] var <uniform> fragmentUbo2 : FragmentUniformBuffer2;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = fragmentUbo1.color + fragmentUbo2.color;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return fragmentUbo1.color + fragmentUbo2.color;
         })");
 
     utils::ComboRenderPipelineDescriptor2 textureDescriptor;
@@ -951,16 +937,14 @@
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+            let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
-            Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+            return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -982,10 +966,8 @@
         [[group(0), binding(47)]] var <uniform> ubo2 : Ubo2;
         [[group(0), binding(111)]] var <uniform> ubo3 : Ubo3;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = ubo1.color + 2.0 * ubo2.color + 4.0 * ubo3.color;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return ubo1.color + 2.0 * ubo2.color + 4.0 * ubo3.color;
         })");
 
     utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
@@ -1107,16 +1089,14 @@
     utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
 
     pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
-        [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+        [[stage(vertex)]]
+        fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+            let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                 vec2<f32>(-1.0, 1.0),
                 vec2<f32>( 1.0, 1.0),
                 vec2<f32>(-1.0, -1.0));
 
-            Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+            return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
         })");
 
     pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
@@ -1125,9 +1105,8 @@
         };
         [[group(0), binding(0)]] var<storage> buffer0 : [[access(read)]] Buffer0;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = buffer0.color;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return buffer0.color;
         })");
 
     constexpr uint32_t kRTSize = 4;
diff --git a/src/tests/end2end/BufferZeroInitTests.cpp b/src/tests/end2end/BufferZeroInitTests.cpp
index 22879f8..0be8244 100644
--- a/src/tests/end2end/BufferZeroInitTests.cpp
+++ b/src/tests/end2end/BufferZeroInitTests.cpp
@@ -207,11 +207,9 @@
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vertexShader);
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> i_color : vec4<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = i_color;
+            [[stage(fragment)]]
+            fn main([[location(0)]] i_color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                return i_color;
             })");
 
         ASSERT(vertexBufferCount <= 1u);
@@ -248,18 +246,20 @@
         constexpr wgpu::TextureFormat kColorAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
 
         wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[location(0)]] var<out> o_color : vec4<f32>;
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
+            [[stage(vertex)]] fn main([[location(0)]] pos : vec4<f32>) -> VertexOut {
+                var output : VertexOut;
                 if (all(pos == vec4<f32>(0.0, 0.0, 0.0, 0.0))) {
-                    o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+                    output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
                 } else {
-                    o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                    output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
                 }
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                return output;
             })");
 
         constexpr uint64_t kVertexAttributeSize = sizeof(float) * 4;
@@ -290,18 +290,21 @@
 
         wgpu::RenderPipeline renderPipeline =
             CreateRenderPipelineForTest(R"(
-            [[location(0)]] var<out> o_color : vec4<f32>;
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
+                var output : VertexOut;
                 if (VertexIndex == 0u) {
-                    o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+                    output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
                 } else {
-                    o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                    output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
                 }
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                return output;
             })",
                                         0 /* vertexBufferCount */);
 
@@ -337,13 +340,16 @@
         // As long as the vertex shader is executed once, the output color will be red.
         wgpu::RenderPipeline renderPipeline =
             CreateRenderPipelineForTest(R"(
-            [[location(0)]] var<out> o_color : vec4<f32>;
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> VertexOut {
+                var output : VertexOut;
+                output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                return output;
             })",
                                         0 /* vertexBufferCount */);
 
@@ -375,13 +381,16 @@
         // As long as the vertex shader is executed once, the output color will be red.
         wgpu::RenderPipeline renderPipeline =
             CreateRenderPipelineForTest(R"(
-            [[location(0)]] var<out> o_color : vec4<f32>;
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> VertexOut {
+                var output : VertexOut;
+                output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                return output;
             })",
                                         0 /* vertexBufferCount */);
         wgpu::Buffer indexBuffer =
diff --git a/src/tests/end2end/ClipSpaceTests.cpp b/src/tests/end2end/ClipSpaceTests.cpp
index 886641e..d126f22 100644
--- a/src/tests/end2end/ClipSpaceTests.cpp
+++ b/src/tests/end2end/ClipSpaceTests.cpp
@@ -26,7 +26,7 @@
         // 1. The depth value of the top-left one is >= 0.5
         // 2. The depth value of the bottom-right one is <= 0.5
         pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
-            const pos : array<vec3<f32>, 6> = array<vec3<f32>, 6>(
+            let pos : array<vec3<f32>, 6> = array<vec3<f32>, 6>(
                 vec3<f32>(-1.0,  1.0, 1.0),
                 vec3<f32>(-1.0, -1.0, 0.5),
                 vec3<f32>( 1.0,  1.0, 0.5),
@@ -34,19 +34,14 @@
                 vec3<f32>(-1.0, -1.0, 0.5),
                 vec3<f32>( 1.0, -1.0, 0.0));
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(pos[VertexIndex], 1.0);
-                return;
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(pos[VertexIndex], 1.0);
             })");
 
         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;;
-            [[stage(fragment)]] fn main() {
-               fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-               return;
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+               return vec4<f32>(1.0, 0.0, 0.0, 1.0);
             })");
 
         wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil();
diff --git a/src/tests/end2end/ColorStateTests.cpp b/src/tests/end2end/ColorStateTests.cpp
index 7e51cc1..4940d37 100644
--- a/src/tests/end2end/ColorStateTests.cpp
+++ b/src/tests/end2end/ColorStateTests.cpp
@@ -35,16 +35,13 @@
         DAWN_SKIP_TEST_IF(IsD3D12() && IsWARP());
 
         vsModule = utils::CreateShaderModule(device, R"(
-                [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-                [[builtin(position)]] var<out> Position : vec4<f32>;
-
-                [[stage(vertex)]] fn main() {
-                    const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+                [[stage(vertex)]]
+                fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                    let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                         vec2<f32>(-1.0, -1.0),
                         vec2<f32>(3.0, -1.0),
                         vec2<f32>(-1.0, 3.0));
-                    Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-                    return;
+                    return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
                 }
             )");
 
@@ -66,11 +63,8 @@
 
                 [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
 
-                [[location(0)]] var<out> fragColor : vec4<f32>;
-
-                [[stage(fragment)]] fn main() {
-                    fragColor = myUbo.color;
-                    return;
+                [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                    return myUbo.color;
                 }
             )");
 
@@ -799,17 +793,20 @@
 
         [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
 
-        [[location(0)]] var<out> fragColor0 : vec4<f32>;
-        [[location(1)]] var<out> fragColor1 : vec4<f32>;
-        [[location(2)]] var<out> fragColor2 : vec4<f32>;
-        [[location(3)]] var<out> fragColor3 : vec4<f32>;
+        struct FragmentOut {
+            [[location(0)]] fragColor0 : vec4<f32>;
+            [[location(1)]] fragColor1 : vec4<f32>;
+            [[location(2)]] fragColor2 : vec4<f32>;
+            [[location(3)]] fragColor3 : vec4<f32>;
+        };
 
-        [[stage(fragment)]] fn main() {
-            fragColor0 = myUbo.color0;
-            fragColor1 = myUbo.color1;
-            fragColor2 = myUbo.color2;
-            fragColor3 = myUbo.color3;
-            return;
+        [[stage(fragment)]] fn main() -> FragmentOut {
+            var output : FragmentOut;
+            output.fragColor0 = myUbo.color0;
+            output.fragColor1 = myUbo.color1;
+            output.fragColor2 = myUbo.color2;
+            output.fragColor3 = myUbo.color3;
+            return output;
         }
     )");
 
@@ -915,11 +912,8 @@
 
         [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = myUbo.color;
-            return;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return myUbo.color;
         }
     )");
 
@@ -1042,11 +1036,8 @@
 
         [[group(0), binding(0)]] var<uniform> myUbo : MyBlock;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-
-        [[stage(fragment)]] fn main() {
-            fragColor = myUbo.color;
-            return;
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return myUbo.color;
         }
     )");
 
diff --git a/src/tests/end2end/CompressedTextureFormatTests.cpp b/src/tests/end2end/CompressedTextureFormatTests.cpp
index e763899..efe4a24 100644
--- a/src/tests/end2end/CompressedTextureFormatTests.cpp
+++ b/src/tests/end2end/CompressedTextureFormatTests.cpp
@@ -140,31 +140,30 @@
 
         utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor;
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[location(0)]] var<out> texCoord : vec2 <f32>;
+            struct VertexOut {
+                [[location(0)]] texCoord : vec2 <f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-3.0,  1.0),
                     vec2<f32>( 3.0,  1.0),
                     vec2<f32>( 0.0, -2.0)
                 );
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-                texCoord = vec2<f32>(Position.x / 2.0, -Position.y / 2.0) + vec2<f32>(0.5, 0.5);
-                return;
+                var output : VertexOut;
+                output.position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                output.texCoord = vec2<f32>(output.position.x / 2.0, -output.position.y / 2.0) + vec2<f32>(0.5, 0.5);
+                return output;
             })");
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
 
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, texCoord);
-                return;
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, texCoord);
             })");
         renderPipelineDescriptor.vertex.module = vsModule;
         renderPipelineDescriptor.cFragment.module = fsModule;
diff --git a/src/tests/end2end/ComputeCopyStorageBufferTests.cpp b/src/tests/end2end/ComputeCopyStorageBufferTests.cpp
index 1cdf2c7..f41da7b 100644
--- a/src/tests/end2end/ComputeCopyStorageBufferTests.cpp
+++ b/src/tests/end2end/ComputeCopyStorageBufferTests.cpp
@@ -99,10 +99,9 @@
         [[set(0), binding(0)]] var<storage> src : [[access(read_write)]] Buf1;
         [[set(0), binding(1)]] var<storage> dst : [[access(read_write)]] Buf2;
 
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
-        [[stage(compute)]] fn main() {
-            var index : u32 = GlobalInvocationID.x;
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
+            let index : u32 = GlobalInvocationID.x;
             if (index >= 4u) { return; }
             dst.s[index] = src.s[index];
         })");
@@ -127,10 +126,9 @@
         [[set(0), binding(0)]] var<storage> src : [[access(read_write)]] Buf1;
         [[set(0), binding(1)]] var<storage> dst : [[access(read_write)]] Buf2;
 
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
-        [[stage(compute)]] fn main() {
-            var index : u32 = GlobalInvocationID.x;
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
+            let index : u32 = GlobalInvocationID.x;
             if (index >= 4u) { return; }
             dst.s[index] = src.s[index];
         })");
@@ -150,10 +148,9 @@
         [[set(0), binding(0)]] var<storage> src : [[access(read_write)]] Buf1;
         [[set(0), binding(1)]] var<storage> dst : [[access(read_write)]] Buf2;
 
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
-        [[stage(compute)]] fn main() {
-            var index : u32 = GlobalInvocationID.x;
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
+            let index : u32 = GlobalInvocationID.x;
             if (index >= 4u) { return; }
             dst.s[index] = src.s[index];
         })");
diff --git a/src/tests/end2end/ComputeDispatchTests.cpp b/src/tests/end2end/ComputeDispatchTests.cpp
index e343091..93d844f 100644
--- a/src/tests/end2end/ComputeDispatchTests.cpp
+++ b/src/tests/end2end/ComputeDispatchTests.cpp
@@ -39,11 +39,9 @@
             [[group(0), binding(0)]] var<uniform> input : InputBuf;
             [[group(0), binding(1)]] var<storage> output : [[access(read_write)]] OutputBuf;
 
-            [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
             [[stage(compute), workgroup_size(1, 1, 1)]]
-            fn main() {
-                const dispatch : vec3<u32> = input.expectedDispatch;
+            fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
+                let dispatch : vec3<u32> = input.expectedDispatch;
 
                 if (dispatch.x == 0u || dispatch.y == 0u || dispatch.z == 0u) {
                     output.workGroups = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
diff --git a/src/tests/end2end/ComputeSharedMemoryTests.cpp b/src/tests/end2end/ComputeSharedMemoryTests.cpp
index c0a2e23..8ac3212 100644
--- a/src/tests/end2end/ComputeSharedMemoryTests.cpp
+++ b/src/tests/end2end/ComputeSharedMemoryTests.cpp
@@ -71,10 +71,8 @@
 // Basic shared memory test
 TEST_P(ComputeSharedMemoryTests, Basic) {
     BasicTest(R"(
-        const kTileSize : u32 = 4;
-        const kInstances : u32 = 11;
-
-        [[builtin(local_invocation_id)]] var<in> LocalInvocationID : vec3<u32>;
+        let kTileSize : u32 = 4u;
+        let kInstances : u32 = 11u;
 
         [[block]] struct Dst {
             x : u32;
@@ -84,8 +82,8 @@
         var<workgroup> tmp : u32;
 
         [[stage(compute), workgroup_size(4,4,1)]]
-        fn main() {
-            var index : u32 = LocalInvocationID.y * kTileSize + LocalInvocationID.x;
+        fn main([[builtin(local_invocation_id)]] LocalInvocationID : vec3<u32>) {
+            let index : u32 = LocalInvocationID.y * kTileSize + LocalInvocationID.x;
             if (index == 0u) {
                 tmp = 0u;
             }
diff --git a/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp b/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp
index 06c8a6e..3c85edf 100644
--- a/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp
+++ b/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp
@@ -38,9 +38,8 @@
 
         [[group(0), binding(0)]] var<storage> buf : [[access(read_write)]] Buf;
 
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
-        [[stage(compute)]] fn main() {
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
             buf.data[GlobalInvocationID.x] = buf.data[GlobalInvocationID.x] + 0x1234u;
         }
     )");
@@ -94,9 +93,9 @@
 
         [[group(0), binding(0)]] var<storage> src : [[access(read_write)]] Src;
         [[group(0), binding(1)]] var<storage> dst : [[access(read_write)]] Dst;
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
 
-        [[stage(compute)]] fn main() {
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
             dst.data[GlobalInvocationID.x] = src.data[GlobalInvocationID.x] + 0x1234u;
         }
     )");
@@ -166,9 +165,8 @@
         [[group(0), binding(0)]] var<storage> src : [[access(read)]] Src;
         [[group(0), binding(1)]] var<storage> dst : [[access(read_write)]] Dst;
 
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
-
-        [[stage(compute)]] fn main() {
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
             dst.data[GlobalInvocationID.x] = src.data[GlobalInvocationID.x] + 0x1234u;
         }
     )");
@@ -234,9 +232,9 @@
 
         [[group(0), binding(0)]] var<uniform> src : Buf;
         [[group(0), binding(1)]] var<storage> dst : [[access(read_write)]] Buf;
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
 
-        [[stage(compute)]] fn main() {
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
             dst.data[GlobalInvocationID.x] = src.data[GlobalInvocationID.x] +
                 vec4<u32>(0x1234u, 0x1234u, 0x1234u, 0x1234u);
         }
@@ -302,9 +300,9 @@
 
         [[group(0), binding(0)]] var<uniform> src : Buf;
         [[group(0), binding(1)]] var<storage> dst : [[access(read_write)]] Buf;
-        [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
 
-        [[stage(compute)]] fn main() {
+        [[stage(compute)]]
+        fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
             dst.data[GlobalInvocationID.x] = src.data[GlobalInvocationID.x] +
                 vec4<u32>(0x1234u, 0x1234u, 0x1234u, 0x1234u);
         }
diff --git a/src/tests/end2end/CopyTextureForBrowserTests.cpp b/src/tests/end2end/CopyTextureForBrowserTests.cpp
index fd03f62..5a71ea6 100644
--- a/src/tests/end2end/CopyTextureForBrowserTests.cpp
+++ b/src/tests/end2end/CopyTextureForBrowserTests.cpp
@@ -148,16 +148,16 @@
             [[group(0), binding(1)]] var dst : texture_2d<f32>;
             [[group(0), binding(2)]] var<storage> output : [[access(read_write)]] OutputBuf;
             [[group(0), binding(3)]] var<uniform> uniforms : Uniforms;
-            [[builtin(global_invocation_id)]] var<in> GlobalInvocationID : vec3<u32>;
             fn aboutEqual(value : f32, expect : f32) -> bool {
                 // The value diff should be smaller than the hard coded tolerance.
                 return abs(value - expect) < 0.001;
             }
-            [[stage(compute), workgroup_size(1, 1, 1)]] fn main() {
-                var srcSize : vec2<i32> = textureDimensions(src);
-                var dstSize : vec2<i32> = textureDimensions(dst);
-                var dstTexCoord : vec2<u32> = vec2<u32>(GlobalInvocationID.xy);
-                var nonCoveredColor : vec4<f32> =
+            [[stage(compute), workgroup_size(1, 1, 1)]]
+            fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
+                let srcSize : vec2<i32> = textureDimensions(src);
+                let dstSize : vec2<i32> = textureDimensions(dst);
+                let dstTexCoord : vec2<u32> = vec2<u32>(GlobalInvocationID.xy);
+                let nonCoveredColor : vec4<f32> =
                     vec4<f32>(0.0, 1.0, 0.0, 1.0); // should be green
 
                 var success : bool = true;
@@ -179,8 +179,8 @@
                         srcTexCoord.y = u32(srcSize.y) - srcTexCoord.y - 1u;
                     }
 
-                    var srcColor : vec4<f32> = textureLoad(src, vec2<i32>(srcTexCoord), 0);
-                    var dstColor : vec4<f32> = textureLoad(dst, vec2<i32>(dstTexCoord), 0);
+                    let srcColor : vec4<f32> = textureLoad(src, vec2<i32>(srcTexCoord), 0);
+                    let dstColor : vec4<f32> = textureLoad(dst, vec2<i32>(dstTexCoord), 0);
 
                     // Not use loop and variable index format to workaround
                     // crbug.com/tint/638.
@@ -196,7 +196,7 @@
                                   aboutEqual(dstColor.a, srcColor.a);
                     }
                 }
-                var outputIndex : u32 = GlobalInvocationID.y * u32(dstSize.x) +
+                let outputIndex : u32 = GlobalInvocationID.y * u32(dstSize.x) +
                                         GlobalInvocationID.x;
                 if (success) {
                     output.result[outputIndex] = 1u;
diff --git a/src/tests/end2end/CreatePipelineAsyncTests.cpp b/src/tests/end2end/CreatePipelineAsyncTests.cpp
index 362fc1b..f65f71b 100644
--- a/src/tests/end2end/CreatePipelineAsyncTests.cpp
+++ b/src/tests/end2end/CreatePipelineAsyncTests.cpp
@@ -147,14 +147,12 @@
 
     utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor;
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> o_color : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     renderPipelineDescriptor.vertex.module = vsModule;
     renderPipelineDescriptor.cFragment.module = fsModule;
@@ -217,14 +215,12 @@
 
     utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor;
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> o_color : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     renderPipelineDescriptor.vertex.module = vsModule;
     renderPipelineDescriptor.cFragment.module = fsModule;
@@ -281,14 +277,12 @@
 TEST_P(CreatePipelineAsyncTest, ReleaseDeviceBeforeCallbackOfCreateRenderPipelineAsync) {
     utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor;
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> o_color : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     renderPipelineDescriptor.vertex.module = vsModule;
     renderPipelineDescriptor.cFragment.module = fsModule;
diff --git a/src/tests/end2end/CullingTests.cpp b/src/tests/end2end/CullingTests.cpp
index ec05127..b23f885 100644
--- a/src/tests/end2end/CullingTests.cpp
+++ b/src/tests/end2end/CullingTests.cpp
@@ -26,7 +26,7 @@
         // 1. The top-left one is counterclockwise (CCW)
         // 2. The bottom-right one is clockwise (CW)
         pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
-            const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                 vec2<f32>(-1.0,  1.0),
                 vec2<f32>(-1.0,  0.0),
                 vec2<f32>( 0.0,  1.0),
@@ -34,26 +34,20 @@
                 vec2<f32>( 1.0,  0.0),
                 vec2<f32>( 1.0, -1.0));
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-                return;
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         // FragCoord of pixel(x, y) in framebuffer coordinate is (x + 0.5, y + 0.5). And we use
         // RGBA8 format for the back buffer. So (FragCoord.xy - vec2(0.5)) / 255 in shader code
         // will make the pixel's R and G channels exactly equal to the pixel's x and y coordinates.
         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;;
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(
                     (FragCoord.xy - vec2<f32>(0.5, 0.5)) / vec2<f32>(255.0, 255.0),
                     0.0, 1.0);
-                return;
             })");
 
         // Set culling mode and front face according to the parameters
diff --git a/src/tests/end2end/D3D12CachingTests.cpp b/src/tests/end2end/D3D12CachingTests.cpp
index 1295caa..d3328b1 100644
--- a/src/tests/end2end/D3D12CachingTests.cpp
+++ b/src/tests/end2end/D3D12CachingTests.cpp
@@ -101,18 +101,12 @@
     mPersistentCache.mIsDisabled = true;
 
     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn vertex_main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
-            return;
+        [[stage(vertex)]] fn vertex_main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         }
 
-        [[location(0)]] var<out> outColor : vec4<f32>;
-
-        [[stage(fragment)]] fn fragment_main() {
-          outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-          return;
+        [[stage(fragment)]] fn fragment_main() -> [[location(0)]] vec4<f32> {
+          return vec4<f32>(1.0, 0.0, 0.0, 1.0);
         }
     )");
 
@@ -148,18 +142,12 @@
 // entrypoints)
 TEST_P(D3D12CachingTests, ReuseShaderWithMultipleEntryPointsPerStage) {
     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn vertex_main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
-            return;
+        [[stage(vertex)]] fn vertex_main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         }
 
-        [[location(0)]] var<out> outColor : vec4<f32>;
-
-        [[stage(fragment)]] fn fragment_main() {
-          outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-          return;
+        [[stage(fragment)]] fn fragment_main() -> [[location(0)]] vec4<f32> {
+          return vec4<f32>(1.0, 0.0, 0.0, 1.0);
         }
     )");
 
@@ -193,18 +181,12 @@
 
     // Modify the WGSL shader functions and make sure it doesn't hit.
     wgpu::ShaderModule newModule = utils::CreateShaderModule(device, R"(
-      [[builtin(position)]] var<out> Position : vec4<f32>;
-
-      [[stage(vertex)]] fn vertex_main() {
-          Position = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-          return;
+      [[stage(vertex)]] fn vertex_main() -> [[builtin(position)]] vec4<f32> {
+          return vec4<f32>(1.0, 1.0, 1.0, 1.0);
       }
 
-      [[location(0)]] var<out> outColor : vec4<f32>;
-
-      [[stage(fragment)]] fn fragment_main() {
-        outColor = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-        return;
+      [[stage(fragment)]] fn fragment_main() -> [[location(0)]] vec4<f32> {
+        return vec4<f32>(1.0, 1.0, 1.0, 1.0);
       }
   )");
 
@@ -233,12 +215,10 @@
 
         [[stage(compute)]] fn write1() {
             data.data = 1u;
-            return;
         }
 
         [[stage(compute)]] fn write42() {
             data.data = 42u;
-            return;
         }
     )");
 
diff --git a/src/tests/end2end/D3D12VideoViewsTests.cpp b/src/tests/end2end/D3D12VideoViewsTests.cpp
index dfe5af6..d5250a1 100644
--- a/src/tests/end2end/D3D12VideoViewsTests.cpp
+++ b/src/tests/end2end/D3D12VideoViewsTests.cpp
@@ -231,13 +231,14 @@
         // Vertex shader used to render a sampled texture into a quad.
         wgpu::ShaderModule GetTestVertexShaderModule() const {
             return utils::CreateShaderModule(device, R"(
-                [[builtin(position)]] var<out> Position : vec4<f32>;
-                [[location(0)]] var<out> texCoord : vec2 <f32>;
+                struct VertexOut {
+                    [[location(0)]] texCoord : vec2 <f32>;
+                    [[builtin(position)]] position : vec4<f32>;
+                };
 
-                [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-
-                [[stage(vertex)]] fn main() {
-                    const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                [[stage(vertex)]]
+                fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
+                    let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                         vec2<f32>(-1.0, 1.0),
                         vec2<f32>(-1.0, -1.0),
                         vec2<f32>(1.0, -1.0),
@@ -245,8 +246,10 @@
                         vec2<f32>(1.0, -1.0),
                         vec2<f32>(1.0, 1.0)
                     );
-                    Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-                    texCoord = vec2<f32>(Position.xy * 0.5) + vec2<f32>(0.5, 0.5);
+                    var output : VertexOut;
+                    output.position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                    output.texCoord = vec2<f32>(output.position.xy * 0.5) + vec2<f32>(0.5, 0.5);
+                    return output;
             })");
         }
 
@@ -297,12 +300,10 @@
             [[set(0), binding(0)]] var sampler0 : sampler;
             [[set(0), binding(1)]] var texture : texture_2d<f32>;
 
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-               var y : f32 = textureSample(texture, sampler0, texCoord).r;
-               fragColor = vec4<f32>(y, 0.0, 0.0, 1.0);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+               let y : f32 = textureSample(texture, sampler0, texCoord).r;
+               return vec4<f32>(y, 0.0, 0.0, 1.0);
             })");
 
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
@@ -350,13 +351,11 @@
             [[set(0), binding(0)]] var sampler0 : sampler;
             [[set(0), binding(1)]] var texture : texture_2d<f32>;
 
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-               var u : f32 = textureSample(texture, sampler0, texCoord).r;
-               var v : f32 = textureSample(texture, sampler0, texCoord).g;
-               fragColor = vec4<f32>(u, v, 0.0, 1.0);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+               let u : f32 = textureSample(texture, sampler0, texCoord).r;
+               let v : f32 = textureSample(texture, sampler0, texCoord).g;
+               return vec4<f32>(u, v, 0.0, 1.0);
             })");
 
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
@@ -413,14 +412,12 @@
             [[set(0), binding(1)]] var lumaTexture : texture_2d<f32>;
             [[set(0), binding(2)]] var chromaTexture : texture_2d<f32>;
 
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-               var y : f32 = textureSample(lumaTexture, sampler0, texCoord).r;
-               var u : f32 = textureSample(chromaTexture, sampler0, texCoord).r;
-               var v : f32 = textureSample(chromaTexture, sampler0, texCoord).g;
-               fragColor = vec4<f32>(y, u, v, 1.0);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+               let y : f32 = textureSample(lumaTexture, sampler0, texCoord).r;
+               let u : f32 = textureSample(chromaTexture, sampler0, texCoord).r;
+               let v : f32 = textureSample(chromaTexture, sampler0, texCoord).g;
+               return vec4<f32>(y, u, v, 1.0);
             })");
 
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(
diff --git a/src/tests/end2end/DeprecatedAPITests.cpp b/src/tests/end2end/DeprecatedAPITests.cpp
index 9d95bad..a7c8c0b 100644
--- a/src/tests/end2end/DeprecatedAPITests.cpp
+++ b/src/tests/end2end/DeprecatedAPITests.cpp
@@ -497,20 +497,14 @@
                                        : "vec4<f32>(f32(a), 0.0, 0.0, 1.0)";
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, (attribute + R"(
-                [[builtin(position)]] var<out> Position : vec4<f32>;
-
-                [[stage(vertex)]] fn main() {
-                    Position = )" + attribAccess + R"(;
-                    return;
+                [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                    return )" + attribAccess + R"(;
                 }
             )")
                                                                             .c_str());
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-                [[location(0)]] var<out> outColor : vec4<f32>;
-
-                [[stage(fragment)]] fn main() {
-                    outColor = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-                    return;
+                [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                    return vec4<f32>(1.0, 1.0, 1.0, 1.0);
                 }
             )");
 
diff --git a/src/tests/end2end/DepthBiasTests.cpp b/src/tests/end2end/DepthBiasTests.cpp
index 5cc22d6..6ce037e 100644
--- a/src/tests/end2end/DepthBiasTests.cpp
+++ b/src/tests/end2end/DepthBiasTests.cpp
@@ -36,36 +36,32 @@
             case QuadAngle::Flat:
                 // Draw a square at z = 0.25
                 vertexSource = R"(
-    [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-    [[builtin(position)]] var<out> Position : vec4<f32>;
-    [[stage(vertex)]] fn main() {
-        const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+    [[stage(vertex)]]
+    fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+        let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
             vec2<f32>(-1.0, -1.0),
             vec2<f32>( 1.0, -1.0),
             vec2<f32>(-1.0,  1.0),
             vec2<f32>(-1.0,  1.0),
             vec2<f32>( 1.0, -1.0),
             vec2<f32>( 1.0,  1.0));
-        Position = vec4<f32>(pos[VertexIndex], 0.25, 1.0);
-        return;
+        return vec4<f32>(pos[VertexIndex], 0.25, 1.0);
     })";
                 break;
 
             case QuadAngle::TiltedX:
                 // Draw a square ranging from 0 to 0.5, bottom to top
                 vertexSource = R"(
-    [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-    [[builtin(position)]] var<out> Position : vec4<f32>;
-    [[stage(vertex)]] fn main() {
-        const pos : array<vec3<f32>, 6> = array<vec3<f32>, 6>(
+    [[stage(vertex)]]
+    fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+        let pos : array<vec3<f32>, 6> = array<vec3<f32>, 6>(
             vec3<f32>(-1.0, -1.0, 0.0),
             vec3<f32>( 1.0, -1.0, 0.0),
             vec3<f32>(-1.0,  1.0, 0.5),
             vec3<f32>(-1.0,  1.0, 0.5),
             vec3<f32>( 1.0, -1.0, 0.0),
             vec3<f32>( 1.0,  1.0, 0.5));
-        Position = vec4<f32>(pos[VertexIndex], 1.0);
-        return;
+        return vec4<f32>(pos[VertexIndex], 1.0);
     })";
                 break;
         }
@@ -73,10 +69,8 @@
         wgpu::ShaderModule vertexModule = utils::CreateShaderModule(device, vertexSource);
 
         wgpu::ShaderModule fragmentModule = utils::CreateShaderModule(device, R"(
-    [[location(0)]] var<out> fragColor : vec4<f32>;;
-    [[stage(fragment)]] fn main() {
-        fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-        return;
+    [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+        return vec4<f32>(1.0, 0.0, 0.0, 1.0);
     })");
 
         {
diff --git a/src/tests/end2end/DepthStencilCopyTests.cpp b/src/tests/end2end/DepthStencilCopyTests.cpp
index 166a926..b585133 100644
--- a/src/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/tests/end2end/DepthStencilCopyTests.cpp
@@ -29,18 +29,16 @@
 
         // Draw a square in the bottom left quarter of the screen.
         mVertexModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 0.0, -1.0),
                     vec2<f32>(-1.0,  0.0),
                     vec2<f32>(-1.0,  0.0),
                     vec2<f32>( 0.0, -1.0),
                     vec2<f32>( 0.0,  0.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
     }
 
@@ -74,9 +72,8 @@
         desc->vertex.module = mVertexModule;
 
         std::string fsSource = R"(
-        [[builtin(frag_depth)]] var<out> FragDepth : f32;
-        [[stage(fragment)]] fn main() {
-            FragDepth = )" + std::to_string(regionDepth) +
+        [[stage(fragment)]] fn main() -> [[builtin(frag_depth)]] f32 {
+            return )" + std::to_string(regionDepth) +
                                ";\n}";
 
         desc->cFragment.module = utils::CreateShaderModule(device, fsSource.c_str());
@@ -239,29 +236,31 @@
         utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
 
         pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 3.0, -1.0),
                     vec2<f32>(-1.0,  3.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         // Sample the input texture and write out depth. |result| will only be set to 1 if we
         // pass the depth test.
         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var texture0 : texture_2d<f32>;
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
 
-            [[location(0)]] var<out> result : u32;
-            [[builtin(frag_depth)]] var<out> FragDepth : f32;
+            struct FragmentOut {
+                [[location(0)]] result : u32;
+                [[builtin(frag_depth)]] fragDepth : f32;
+            };
 
-            [[stage(fragment)]] fn main() {
-                result = 1u;
-                FragDepth = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0)[0];
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> FragmentOut {
+                var output : FragmentOut;
+                output.result = 1u;
+                output.fragDepth = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0)[0];
+                return output;
             })");
 
         // Pass the depth test only if the depth is equal.
diff --git a/src/tests/end2end/DepthStencilSamplingTests.cpp b/src/tests/end2end/DepthStencilSamplingTests.cpp
index 28274d5..c03349b 100644
--- a/src/tests/end2end/DepthStencilSamplingTests.cpp
+++ b/src/tests/end2end/DepthStencilSamplingTests.cpp
@@ -66,9 +66,8 @@
     wgpu::RenderPipeline CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,
                                                       uint32_t componentIndex) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
@@ -173,9 +172,8 @@
 
     wgpu::RenderPipeline CreateComparisonRenderPipeline() {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -186,10 +184,8 @@
             };
             [[group(0), binding(2)]] var<uniform> uniforms : Uniforms;
 
-            [[location(0)]] var<out> samplerResult : f32;
-
-            [[stage(fragment)]] fn main() {
-                samplerResult = textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
+            [[stage(fragment)]] fn main() -> [[location(0)]] f32 {
+                return textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
             })");
 
         // TODO(dawn:367): Cannot use GetBindGroupLayout for comparison samplers without shader
diff --git a/src/tests/end2end/DepthStencilStateTests.cpp b/src/tests/end2end/DepthStencilStateTests.cpp
index 0c01143..dbd118d 100644
--- a/src/tests/end2end/DepthStencilStateTests.cpp
+++ b/src/tests/end2end/DepthStencilStateTests.cpp
@@ -61,18 +61,17 @@
                 depth : f32;
             };
             [[group(0), binding(0)]] var<uniform> ubo : UBO;
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
 
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                         vec2<f32>(-1.0,  1.0),
                         vec2<f32>(-1.0, -1.0),
                         vec2<f32>( 1.0, -1.0), // front-facing
                         vec2<f32>(-1.0,  1.0),
                         vec2<f32>( 1.0,  1.0),
                         vec2<f32>( 1.0, -1.0)); // back-facing
-                Position = vec4<f32>(pos[VertexIndex], ubo.depth, 1.0);
+                return vec4<f32>(pos[VertexIndex], ubo.depth, 1.0);
             })");
 
         fsModule = utils::CreateShaderModule(device, R"(
@@ -82,10 +81,8 @@
             };
             [[group(0), binding(0)]] var<uniform> ubo : UBO;
 
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(ubo.color, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(ubo.color, 1.0);
             })");
     }
 
diff --git a/src/tests/end2end/DestroyTests.cpp b/src/tests/end2end/DestroyTests.cpp
index eaa7b7f..88bf140 100644
--- a/src/tests/end2end/DestroyTests.cpp
+++ b/src/tests/end2end/DestroyTests.cpp
@@ -28,16 +28,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-              [[location(0)]] var<in> pos : vec4<f32>;
-              [[builtin(position)]] var<out> Position : vec4<f32>;
-              [[stage(vertex)]] fn main() {
-                  Position = pos;
+              [[stage(vertex)]]
+              fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                  return pos;
               })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-              [[location(0)]] var<out> fragColor : vec4<f32>;
-              [[stage(fragment)]] fn main() {
-                  fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+              [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                  return vec4<f32>(0.0, 1.0, 0.0, 1.0);
               })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/DeviceLostTests.cpp b/src/tests/end2end/DeviceLostTests.cpp
index 3055e00..d420a0a 100644
--- a/src/tests/end2end/DeviceLostTests.cpp
+++ b/src/tests/end2end/DeviceLostTests.cpp
@@ -212,10 +212,9 @@
     SetCallbackAndLoseForTesting();
 
     ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<in> color : vec4<f32>;
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = color;
+        [[stage(fragment)]]
+        fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+            return color;
         })"));
 }
 
diff --git a/src/tests/end2end/DrawIndexedIndirectTests.cpp b/src/tests/end2end/DrawIndexedIndirectTests.cpp
index 0da508f..55702ab 100644
--- a/src/tests/end2end/DrawIndexedIndirectTests.cpp
+++ b/src/tests/end2end/DrawIndexedIndirectTests.cpp
@@ -27,16 +27,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/DrawIndexedTests.cpp b/src/tests/end2end/DrawIndexedTests.cpp
index 3c696ac..3d3cf24 100644
--- a/src/tests/end2end/DrawIndexedTests.cpp
+++ b/src/tests/end2end/DrawIndexedTests.cpp
@@ -27,16 +27,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/DrawIndirectTests.cpp b/src/tests/end2end/DrawIndirectTests.cpp
index 4887f7a..8cb9e57 100644
--- a/src/tests/end2end/DrawIndirectTests.cpp
+++ b/src/tests/end2end/DrawIndirectTests.cpp
@@ -27,16 +27,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/DrawTests.cpp b/src/tests/end2end/DrawTests.cpp
index dc30a55..eefd43e 100644
--- a/src/tests/end2end/DrawTests.cpp
+++ b/src/tests/end2end/DrawTests.cpp
@@ -27,16 +27,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/DynamicBufferOffsetTests.cpp b/src/tests/end2end/DynamicBufferOffsetTests.cpp
index afabe47..6f557ff 100644
--- a/src/tests/end2end/DynamicBufferOffsetTests.cpp
+++ b/src/tests/end2end/DynamicBufferOffsetTests.cpp
@@ -94,14 +94,13 @@
 
     wgpu::RenderPipeline CreateRenderPipeline(bool isInheritedPipeline = false) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0, 0.0),
                     vec2<f32>(-1.0, 1.0),
                     vec2<f32>( 0.0, 1.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         // Construct fragment shader source
@@ -141,14 +140,12 @@
             )";
         }
 
-        fs << "[[location(0)]] var<out> fragColor : vec4<f32>;\n";
-
-        fs << "const multipleNumber : u32 = " << multipleNumber << "u;\n";
+        fs << "let multipleNumber : u32 = " << multipleNumber << "u;\n";
         fs << R"(
-            [[stage(fragment)]] fn main() {
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
                 sBufferNotDynamic.value = uBufferNotDynamic.value.xy;
                 sBuffer.value = vec2<u32>(multipleNumber, multipleNumber) * (uBuffer.value.xy + sBufferNotDynamic.value.xy);
-                fragColor = vec4<f32>(f32(uBuffer.value.x) / 255.0, f32(uBuffer.value.y) / 255.0,
+                return vec4<f32>(f32(uBuffer.value.x) / 255.0, f32(uBuffer.value.y) / 255.0,
                                       1.0, 1.0);
             }
         )";
@@ -210,7 +207,7 @@
             )";
         }
 
-        cs << "const multipleNumber : u32 = " << multipleNumber << "u;\n";
+        cs << "let multipleNumber : u32 = " << multipleNumber << "u;\n";
         cs << R"(
             [[stage(compute)]] fn main() {
                 sBufferNotDynamic.value = uBufferNotDynamic.value.xy;
diff --git a/src/tests/end2end/EntryPointTests.cpp b/src/tests/end2end/EntryPointTests.cpp
index f0bd062..e27390d 100644
--- a/src/tests/end2end/EntryPointTests.cpp
+++ b/src/tests/end2end/EntryPointTests.cpp
@@ -24,18 +24,12 @@
     // TODO(crbug.com/dawn/658): Crashes on bots
     DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn vertex_main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
-            return;
+        [[stage(vertex)]] fn vertex_main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         }
 
-        [[location(0)]] var<out> outColor : vec4<f32>;
-
-        [[stage(fragment)]] fn fragment_main() {
-          outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
-          return;
+        [[stage(fragment)]] fn fragment_main() -> [[location(0)]] vec4<f32> {
+          return vec4<f32>(1.0, 0.0, 0.0, 1.0);
         }
     )");
 
diff --git a/src/tests/end2end/FirstIndexOffsetTests.cpp b/src/tests/end2end/FirstIndexOffsetTests.cpp
index d64b926..46d2d6b 100644
--- a/src/tests/end2end/FirstIndexOffsetTests.cpp
+++ b/src/tests/end2end/FirstIndexOffsetTests.cpp
@@ -121,7 +121,7 @@
 
         [[group(0), binding(0)]] var<storage> idx_vals : [[access(read_write)]] IndexVals;
 
-        [[stage(fragment)]] fn main()  {
+        [[stage(fragment)]] fn main() {
         )";
 
     if ((checkIndex & CheckIndex::Vertex) != 0) {
diff --git a/src/tests/end2end/GpuMemorySynchronizationTests.cpp b/src/tests/end2end/GpuMemorySynchronizationTests.cpp
index fe768b5..674eb6f 100644
--- a/src/tests/end2end/GpuMemorySynchronizationTests.cpp
+++ b/src/tests/end2end/GpuMemorySynchronizationTests.cpp
@@ -58,9 +58,8 @@
         const wgpu::Buffer& buffer,
         wgpu::TextureFormat colorFormat) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -68,10 +67,9 @@
                 i : i32;
             };
             [[group(0), binding(0)]] var<storage> data : [[access(read_write)]] Data;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
                 data.i = data.i + 1;
-                fragColor = vec4<f32>(f32(data.i) / 255.0, 0.0, 0.0, 1.0);
+                return vec4<f32>(f32(data.i) / 255.0, 0.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 rpDesc;
@@ -335,9 +333,8 @@
     std::tuple<wgpu::RenderPipeline, wgpu::BindGroup> CreatePipelineAndBindGroupForRender(
         wgpu::TextureFormat colorFormat) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -346,9 +343,8 @@
             };
             [[group(0), binding(0)]] var<uniform> contents : Contents;
 
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(contents.color, 0.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(contents.color, 0.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 rpDesc;
@@ -538,7 +534,7 @@
             vbContents.pos[1] = vec4<f32>(1.0, 1.0, 0.0, 1.0);
             vbContents.pos[2] = vec4<f32>(1.0, -1.0, 0.0, 1.0);
             vbContents.pos[3] = vec4<f32>(-1.0, -1.0, 0.0, 1.0);
-            const dummy : i32 = 0;
+            let dummy : i32 = 0;
             ibContents.indices[0] = vec4<i32>(0, 1, 2, 0);
             ibContents.indices[1] = vec4<i32>(2, 3, dummy, dummy);
             uniformContents.color = 1.0;
@@ -574,10 +570,9 @@
 
     // Create pipeline, bind group, and reuse buffers in render pass.
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<in> pos : vec4<f32>;
-        [[builtin(position)]] var<out> Position: vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = pos;
+        [[stage(vertex)]]
+        fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+            return pos;
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -588,9 +583,8 @@
         [[group(0), binding(0)]] var<uniform> uniformBuffer : Buf;
         [[group(0), binding(1)]] var<storage> storageBuffer : [[access(read)]] Buf;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(uniformBuffer.color, storageBuffer.color, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(uniformBuffer.color, storageBuffer.color, 0.0, 1.0);
         })");
 
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
@@ -655,7 +649,7 @@
             contents.pos[1] = vec4<f32>(1.0, 1.0, 0.0, 1.0);
             contents.pos[2] = vec4<f32>(1.0, -1.0, 0.0, 1.0);
             contents.pos[3] = vec4<f32>(-1.0, -1.0, 0.0, 1.0);
-            const dummy : i32 = 0;
+            let dummy : i32 = 0;
             contents.indices[0] = vec4<i32>(0, 1, 2, 0);
             contents.indices[1] = vec4<i32>(2, 3, dummy, dummy);
             contents.color0 = 1.0;
@@ -692,10 +686,9 @@
 
     // Create pipeline, bind group, and reuse the buffer in render pass.
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<in> pos : vec4<f32>;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = pos;
+        [[stage(vertex)]]
+        fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+            return pos;
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
@@ -705,9 +698,8 @@
         [[group(0), binding(0)]] var<uniform> uniformBuffer : Buf;
         [[group(0), binding(1)]] var<storage> storageBuffer : [[access(read)]] Buf;
 
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(uniformBuffer.color, storageBuffer.color, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(uniformBuffer.color, storageBuffer.color, 0.0, 1.0);
         })");
 
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
diff --git a/src/tests/end2end/IOSurfaceWrappingTests.cpp b/src/tests/end2end/IOSurfaceWrappingTests.cpp
index 74a060a..df9417c 100644
--- a/src/tests/end2end/IOSurfaceWrappingTests.cpp
+++ b/src/tests/end2end/IOSurfaceWrappingTests.cpp
@@ -248,12 +248,14 @@
         wgpu::RenderPipeline pipeline;
         {
             wgpu::ShaderModule vs = utils::CreateShaderModule(device, R"(
-                [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-                [[location(0)]] var<out> o_texCoord : vec2<f32>;
-                [[builtin(position)]] var<out> Position : vec4<f32>;
+                struct VertexOut {
+                    [[location(0)]] texCoord : vec2<f32>;
+                    [[builtin(position)]] position : vec4<f32>;
+                };
 
-                [[stage(vertex)]] fn main() {
-                    const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                [[stage(vertex)]]
+                fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
+                    let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                         vec2<f32>(-2.0, -2.0),
                         vec2<f32>(-2.0,  2.0),
                         vec2<f32>( 2.0, -2.0),
@@ -261,7 +263,7 @@
                         vec2<f32>( 2.0, -2.0),
                         vec2<f32>( 2.0,  2.0));
 
-                    const texCoord : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                    let texCoord : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                         vec2<f32>(0.0, 0.0),
                         vec2<f32>(0.0, 1.0),
                         vec2<f32>(1.0, 0.0),
@@ -269,19 +271,19 @@
                         vec2<f32>(1.0, 0.0),
                         vec2<f32>(1.0, 1.0));
 
-                    Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-                    o_texCoord = texCoord[VertexIndex];
+                    var output : VertexOut;
+                    output.position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                    output.texCoord = texCoord[VertexIndex];
+                    return output;
                 }
             )");
             wgpu::ShaderModule fs = utils::CreateShaderModule(device, R"(
                 [[group(0), binding(0)]] var sampler0 : sampler;
                 [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
 
-                [[location(0)]] var<in> texCoord : vec2<f32>;
-                [[location(0)]] var<out> fragColor : vec4<f32>;
-
-                [[stage(fragment)]] fn main() {
-                    fragColor = textureSample(texture0, sampler0, texCoord);
+                [[stage(fragment)]]
+                fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+                    return textureSample(texture0, sampler0, texCoord);
                 }
             )");
 
diff --git a/src/tests/end2end/IndexFormatTests.cpp b/src/tests/end2end/IndexFormatTests.cpp
index 9c9ff19..ae4a12e 100644
--- a/src/tests/end2end/IndexFormatTests.cpp
+++ b/src/tests/end2end/IndexFormatTests.cpp
@@ -33,22 +33,22 @@
     wgpu::RenderPipeline MakeTestPipeline(wgpu::IndexFormat format,
         wgpu::PrimitiveTopology primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(vertex_index)]] var<in> idx : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
+            struct VertexIn {
+                [[location(0)]] pos : vec4<f32>;
+                [[builtin(vertex_index)]] idx : u32;
+            };
+
+            [[stage(vertex)]] fn main(input : VertexIn) -> [[builtin(position)]] vec4<f32> {
                 // 0xFFFFFFFE is a designated invalid index used by some tests.
-                if (idx == 0xFFFFFFFEu) {
-                    Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
-                } else {
-                    Position = pos;
+                if (input.idx == 0xFFFFFFFEu) {
+                    return vec4<f32>(0.0, 0.0, 0.0, 1.0);
                 }
+                return input.pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/MultisampledRenderingTests.cpp b/src/tests/end2end/MultisampledRenderingTests.cpp
index 5d1968d..3fae962 100644
--- a/src/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/tests/end2end/MultisampledRenderingTests.cpp
@@ -50,12 +50,17 @@
                 depth : f32;
             };
             [[group(0), binding(0)]] var<uniform> uBuffer : U;
-            [[location(0)]] var<out> FragColor : vec4<f32>;
-            [[builtin(frag_depth)]] var<out> FragDepth : f32;
 
-            [[stage(fragment)]] fn main() {
-                FragColor = uBuffer.color;
-                FragDepth = uBuffer.depth;
+            struct FragmentOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(frag_depth)]] depth : f32;
+            };
+
+            [[stage(fragment)]] fn main() -> FragmentOut {
+                var output : FragmentOut;
+                output.color = uBuffer.color;
+                output.depth = uBuffer.depth;
+                return output;
             })";
 
         const char* kFsOneOutputWithoutDepth = R"(
@@ -63,10 +68,9 @@
                 color : vec4<f32>;
             };
             [[group(0), binding(0)]] var<uniform> uBuffer : U;
-            [[location(0)]] var<out> FragColor : vec4<f32>;
 
-            [[stage(fragment)]] fn main() {
-                FragColor = uBuffer.color;
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return uBuffer.color;
             })";
 
         const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
@@ -84,12 +88,17 @@
                 color1 : vec4<f32>;
             };
             [[group(0), binding(0)]] var<uniform> uBuffer : U;
-            [[location(0)]] var<out> FragColor0 : vec4<f32>;
-            [[location(1)]] var<out> FragColor1 : vec4<f32>;
 
-            [[stage(fragment)]] fn main() {
-                FragColor0 = uBuffer.color0;
-                FragColor1 = uBuffer.color1;
+            struct FragmentOut {
+                [[location(0)]] color0 : vec4<f32>;
+                [[location(1)]] color1 : vec4<f32>;
+            };
+
+            [[stage(fragment)]] fn main() -> FragmentOut {
+                var output : FragmentOut;
+                output.color0 = uBuffer.color0;
+                output.color1 = uBuffer.color1;
+                return output;
             })";
 
         return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask,
@@ -214,30 +223,26 @@
         // Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
         // only two of the samples will be touched.
         const char* vs = R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0,  1.0),
                     vec2<f32>( 1.0,  1.0),
                     vec2<f32>( 1.0, -1.0)
                 );
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })";
 
         // Draw a bottom-left triangle.
         const char* vsFlipped = R"(
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0,  1.0),
                     vec2<f32>( 1.0,  1.0),
                     vec2<f32>(-1.0, -1.0)
                 );
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })";
 
         if (flipTriangle) {
@@ -776,12 +781,17 @@
             color : vec4<f32>;
         };
         [[group(0), binding(0)]] var<uniform> uBuffer : U;
-        [[location(0)]] var<out> FragColor : vec4<f32>;
-        [[builtin(sample_mask_out)]] var<out> SampleMask : u32;
 
-        [[stage(fragment)]] fn main() {
-            FragColor = uBuffer.color;
-            SampleMask = 6u;
+        struct FragmentOut {
+            [[location(0)]] color : vec4<f32>;
+            [[builtin(sample_mask_out)]] sampleMask : u32;
+        };
+
+        [[stage(fragment)]] fn main() -> FragmentOut {
+            var output : FragmentOut;
+            output.color = uBuffer.color;
+            output.sampleMask = 6u;
+            return output;
         })";
 
     wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 1, false, kSampleMask);
@@ -833,14 +843,19 @@
             color1 : vec4<f32>;
         };
         [[group(0), binding(0)]] var<uniform> uBuffer : U;
-        [[location(0)]] var<out> FragColor0 : vec4<f32>;
-        [[location(1)]] var<out> FragColor1 : vec4<f32>;
-        [[builtin(sample_mask_out)]] var<out> SampleMask : u32;
 
-        [[stage(fragment)]] fn main() {
-            FragColor0 = uBuffer.color0;
-            FragColor1 = uBuffer.color1;
-            SampleMask = 6u;
+        struct FragmentOut {
+            [[location(0)]] color0 : vec4<f32>;
+            [[location(1)]] color1 : vec4<f32>;
+            [[builtin(sample_mask_out)]] sampleMask : u32;
+        };
+
+        [[stage(fragment)]] fn main() -> FragmentOut {
+            var output : FragmentOut;
+            output.color0 = uBuffer.color0;
+            output.color1 = uBuffer.color1;
+            output.sampleMask = 6u;
+            return output;
         })";
 
     wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 2, false);
diff --git a/src/tests/end2end/MultisampledSamplingTests.cpp b/src/tests/end2end/MultisampledSamplingTests.cpp
index cfa5307..5fec69f 100644
--- a/src/tests/end2end/MultisampledSamplingTests.cpp
+++ b/src/tests/end2end/MultisampledSamplingTests.cpp
@@ -54,18 +54,22 @@
             utils::ComboRenderPipelineDescriptor2 desc;
 
             desc.vertex.module = utils::CreateShaderModule(device, R"(
-                [[location(0)]] var<in> pos : vec2<f32>;
-                [[builtin(position)]] var<out> Position : vec4<f32>;
-                [[stage(vertex)]] fn main() {
-                    Position = vec4<f32>(pos, 0.0, 1.0);
+                [[stage(vertex)]]
+                fn main([[location(0)]] pos : vec2<f32>) -> [[builtin(position)]] vec4<f32> {
+                    return vec4<f32>(pos, 0.0, 1.0);
                 })");
 
             desc.cFragment.module = utils::CreateShaderModule(device, R"(
-                [[location(0)]] var<out> fragColor : f32;
-                [[builtin(frag_depth)]] var<out> FragDepth : f32;
-                [[stage(fragment)]] fn main() {
-                    fragColor = 1.0;
-                    FragDepth = 0.7;
+                struct FragmentOut {
+                    [[location(0)]] color : f32;
+                    [[builtin(frag_depth)]] depth : f32;
+                };
+
+                [[stage(fragment)]] fn main() -> FragmentOut {
+                    var output : FragmentOut;
+                    output.color = 1.0;
+                    output.depth = 0.7;
+                    return output;
                 })");
 
             desc.primitive.stripIndexFormat = wgpu::IndexFormat::Uint32;
diff --git a/src/tests/end2end/ObjectCachingTests.cpp b/src/tests/end2end/ObjectCachingTests.cpp
index 3be9e99..630437c 100644
--- a/src/tests/end2end/ObjectCachingTests.cpp
+++ b/src/tests/end2end/ObjectCachingTests.cpp
@@ -104,19 +104,16 @@
 // Test that ShaderModules are correctly deduplicated.
 TEST_P(ObjectCachingTest, ShaderModuleDeduplication) {
     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     wgpu::ShaderModule sameModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     wgpu::ShaderModule otherModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
 
     EXPECT_NE(module.Get(), otherModule.Get());
@@ -212,9 +209,8 @@
 
     utils::ComboRenderPipelineDescriptor2 desc;
     desc.vertex.module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
     desc.cFragment.module = utils::CreateShaderModule(device, R"(
         [[stage(fragment)]] fn main() {
@@ -236,19 +232,16 @@
 // Test that RenderPipelines are correctly deduplicated wrt. their vertex module
 TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnVertexModule) {
     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
     wgpu::ShaderModule sameModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
     wgpu::ShaderModule otherModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(1.0, 1.0, 1.0, 1.0);
         })");
 
     EXPECT_NE(module.Get(), otherModule.Get());
@@ -281,9 +274,8 @@
         [[stage(fragment)]] fn main() {
         })");
     wgpu::ShaderModule otherModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
 
     EXPECT_NE(module.Get(), otherModule.Get());
@@ -291,9 +283,8 @@
 
     utils::ComboRenderPipelineDescriptor2 desc;
     desc.vertex.module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
         })");
 
     desc.cFragment.module = module;
diff --git a/src/tests/end2end/OpArrayLengthTests.cpp b/src/tests/end2end/OpArrayLengthTests.cpp
index efc02fe..f8f13e0 100644
--- a/src/tests/end2end/OpArrayLengthTests.cpp
+++ b/src/tests/end2end/OpArrayLengthTests.cpp
@@ -165,18 +165,18 @@
     // Create the pipeline that computes the length of the buffers and writes it to the only render
     // pass pixel.
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, (mShaderInterface + R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            var fragColor : vec4<f32>;
             fragColor.r = f32(arrayLength(buffer1.data)) / 255.0;
             fragColor.g = f32(arrayLength(buffer2.data)) / 255.0;
             fragColor.b = f32(arrayLength(buffer3.data)) / 255.0;
             fragColor.a = 0.0;
+            return fragColor;
         })")
                                                                         .c_str());
 
@@ -218,23 +218,27 @@
     // Create the pipeline that computes the length of the buffers and writes it to the only render
     // pass pixel.
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, (mShaderInterface + R"(
-        [[location(0)]] var<out> pointColor : vec4<f32>;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            pointColor.r = f32(arrayLength(buffer1.data)) / 255.0;
-            pointColor.g = f32(arrayLength(buffer2.data)) / 255.0;
-            pointColor.b = f32(arrayLength(buffer3.data)) / 255.0;
-            pointColor.a = 0.0;
+        struct VertexOut {
+            [[location(0)]] color : vec4<f32>;
+            [[builtin(position)]] position : vec4<f32>;
+        };
 
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> VertexOut {
+            var output : VertexOut;
+            output.color.r = f32(arrayLength(buffer1.data)) / 255.0;
+            output.color.g = f32(arrayLength(buffer2.data)) / 255.0;
+            output.color.b = f32(arrayLength(buffer3.data)) / 255.0;
+            output.color.a = 0.0;
+
+            output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+            return output;
         })")
                                                                         .c_str());
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[location(0)]] var<in> pointColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = pointColor;
+        [[stage(fragment)]]
+        fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+            return color;
         })");
 
     utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/PrimitiveStateTests.cpp b/src/tests/end2end/PrimitiveStateTests.cpp
index 470f5ae..6b67390 100644
--- a/src/tests/end2end/PrimitiveStateTests.cpp
+++ b/src/tests/end2end/PrimitiveStateTests.cpp
@@ -50,10 +50,9 @@
                 depth : f32;
             };
             [[group(0), binding(0)]] var<uniform> ubo : UBO;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
 
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(0.0, 0.0, ubo.depth, 1.0);
+            [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, ubo.depth, 1.0);
             })");
 
         fsModule = utils::CreateShaderModule(device, R"(
@@ -63,10 +62,8 @@
             };
             [[group(0), binding(0)]] var<uniform> ubo : UBO;
 
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(ubo.color, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(ubo.color, 1.0);
             })");
     }
 
diff --git a/src/tests/end2end/PrimitiveTopologyTests.cpp b/src/tests/end2end/PrimitiveTopologyTests.cpp
index 155987c..adb7dc6 100644
--- a/src/tests/end2end/PrimitiveTopologyTests.cpp
+++ b/src/tests/end2end/PrimitiveTopologyTests.cpp
@@ -154,16 +154,14 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices),
diff --git a/src/tests/end2end/QueryTests.cpp b/src/tests/end2end/QueryTests.cpp
index 746ba59..c09a921 100644
--- a/src/tests/end2end/QueryTests.cpp
+++ b/src/tests/end2end/QueryTests.cpp
@@ -81,20 +81,18 @@
 
         // Create basic render pipeline
         vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>( 1.0,  1.0),
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 1.0, -1.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/RenderBundleTests.cpp b/src/tests/end2end/RenderBundleTests.cpp
index 74a489d..d1ddb16 100644
--- a/src/tests/end2end/RenderBundleTests.cpp
+++ b/src/tests/end2end/RenderBundleTests.cpp
@@ -32,21 +32,19 @@
         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> pos : vec4<f32>;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                Position = pos;
+            [[stage(vertex)]]
+            fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
+                return pos;
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
             [[block]] struct Ubo {
                 color : vec4<f32>;
             };
             [[group(0), binding(0)]] var<uniform> fragmentUniformBuffer : Ubo;
 
-            [[stage(fragment)]] fn main() {
-                fragColor = fragmentUniformBuffer.color;
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return fragmentUniformBuffer.color;
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/RenderPassLoadOpTests.cpp b/src/tests/end2end/RenderPassLoadOpTests.cpp
index 6193b52..f6dc853 100644
--- a/src/tests/end2end/RenderPassLoadOpTests.cpp
+++ b/src/tests/end2end/RenderPassLoadOpTests.cpp
@@ -77,11 +77,9 @@
 
         // draws a blue quad on the right half of the screen
         const char* vsSource = R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                     vec2<f32>( 0.0, -1.0),
                     vec2<f32>( 1.0, -1.0),
                     vec2<f32>( 0.0,  1.0),
@@ -89,13 +87,12 @@
                     vec2<f32>( 1.0, -1.0),
                     vec2<f32>( 1.0,  1.0));
 
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })";
 
         const char* fsSource = R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 0.0, 1.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 1.0, 1.0);
             })";
         blueQuad = DrawQuad(device, vsSource, fsSource);
     }
diff --git a/src/tests/end2end/RenderPassTests.cpp b/src/tests/end2end/RenderPassTests.cpp
index e262403..896f3d5 100644
--- a/src/tests/end2end/RenderPassTests.cpp
+++ b/src/tests/end2end/RenderPassTests.cpp
@@ -27,22 +27,19 @@
 
         // Shaders to draw a bottom-left triangle in blue.
         mVSModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0,  1.0),
                     vec2<f32>( 1.0, -1.0),
                     vec2<f32>(-1.0, -1.0));
 
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 0.0, 1.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 0.0, 1.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/SamplerFilterAnisotropicTests.cpp b/src/tests/end2end/SamplerFilterAnisotropicTests.cpp
index 4487396..3cc3f11 100644
--- a/src/tests/end2end/SamplerFilterAnisotropicTests.cpp
+++ b/src/tests/end2end/SamplerFilterAnisotropicTests.cpp
@@ -42,31 +42,38 @@
                 matrix : mat4x4<f32>;
             };
 
-            [[location(0)]] var<in> position : vec4<f32>;
-            [[location(1)]] var<in> uv : vec2<f32>;
+            struct VertexIn {
+                [[location(0)]] position : vec4<f32>;
+                [[location(1)]] uv : vec2<f32>;
+            };
 
             [[group(0), binding(2)]] var<uniform> uniforms : Uniforms;
 
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[location(0)]] var<out> fragUV : vec2<f32>;
+            struct VertexOut {
+                [[location(0)]] uv : vec2<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
 
-            [[stage(vertex)]] fn main() {
-                fragUV = uv;
-                Position = uniforms.matrix * position;
+            [[stage(vertex)]]
+            fn main(input : VertexIn) -> VertexOut {
+                var output : VertexOut;
+                output.uv = input.uv;
+                output.position = uniforms.matrix * input.position;
+                return output;
             }
         )");
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
 
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
+            struct FragmentIn {
+                [[location(0)]] uv: vec2<f32>;
+                [[builtin(frag_coord)]] fragCoord : vec4<f32>;
+            };
 
-            [[location(0)]] var<in> fragUV: vec2<f32>;
-
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, fragUV);
+            [[stage(fragment)]]
+            fn main(input : FragmentIn) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, input.uv);
             })");
 
         utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
diff --git a/src/tests/end2end/SamplerTests.cpp b/src/tests/end2end/SamplerTests.cpp
index fe10de2..ca031fb 100644
--- a/src/tests/end2end/SamplerTests.cpp
+++ b/src/tests/end2end/SamplerTests.cpp
@@ -55,30 +55,25 @@
         mRenderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
         auto vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                     vec2<f32>(-2.0, -2.0),
                     vec2<f32>(-2.0,  2.0),
                     vec2<f32>( 2.0, -2.0),
                     vec2<f32>(-2.0,  2.0),
                     vec2<f32>( 2.0, -2.0),
                     vec2<f32>( 2.0,  2.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             }
         )");
         auto fsModule = utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
 
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
-
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, FragCoord.xy / vec2<f32>(2.0, 2.0));
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, FragCoord.xy / vec2<f32>(2.0, 2.0));
             })");
 
         utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
diff --git a/src/tests/end2end/ScissorTests.cpp b/src/tests/end2end/ScissorTests.cpp
index 993ae41..b3bcf39 100644
--- a/src/tests/end2end/ScissorTests.cpp
+++ b/src/tests/end2end/ScissorTests.cpp
@@ -21,10 +21,7 @@
   protected:
     wgpu::RenderPipeline CreateQuadPipeline(wgpu::TextureFormat format) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                 vec2<f32>(-1.0, -1.0),
                 vec2<f32>(-1.0,  1.0),
                 vec2<f32>( 1.0, -1.0),
@@ -32,14 +29,14 @@
                 vec2<f32>(-1.0,  1.0),
                 vec2<f32>( 1.0, -1.0));
 
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(pos[VertexIndex], 0.5, 1.0);
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(pos[VertexIndex], 0.5, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/ShaderTests.cpp b/src/tests/end2end/ShaderTests.cpp
index 08e6efd..cdc9abe 100644
--- a/src/tests/end2end/ShaderTests.cpp
+++ b/src/tests/end2end/ShaderTests.cpp
@@ -38,7 +38,7 @@
 [[group(0), binding(0)]] var<storage> buf : [[access(read_write)]] Buf;
 
 [[stage(compute)]] fn main() {
-    const factor : f32 = 1.0001;
+    let factor : f32 = 1.0001;
 
     buf.data[0] = u32(log2(1.0 * factor));
     buf.data[1] = u32(log2(2.0 * factor));
diff --git a/src/tests/end2end/StorageTextureTests.cpp b/src/tests/end2end/StorageTextureTests.cpp
index 9a2e86c..e6a1744a 100644
--- a/src/tests/end2end/StorageTextureTests.cpp
+++ b/src/tests/end2end/StorageTextureTests.cpp
@@ -273,7 +273,7 @@
                 // On Windows Intel drivers the tests will fail if tolerance <= 0.00000001f.
                 return R"(
 fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
-  const tolerance : f32 = 0.0000001;
+  let tolerance : f32 = 0.0000001;
   return all(abs(pixel - expected) < vec4<f32>(tolerance, tolerance, tolerance, tolerance));
 })";
 
@@ -297,7 +297,7 @@
                 << GetComparisonFunction(format) << "\n";
         ostream << "fn doTest() -> bool {\n";
         ostream << "  var size : vec2<i32> = textureDimensions(storageImage0);\n";
-        ostream << "  const layerCount : i32 = " << layerCount << ";\n";
+        ostream << "  let layerCount : i32 = " << layerCount << ";\n";
         ostream << "  for (var layer : i32 = 0; layer < layerCount; layer = layer + 1) {\n";
         ostream << "    for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
         ostream << "      for (var x : i32 = 0; x < size.x; x = x + 1) {\n";
@@ -331,8 +331,8 @@
         ostream << GetImageDeclaration(format, "write", is2DArray, 0) << "\n";
         ostream << "[[stage(" << stage << ")]]\n";
         ostream << "fn main() {\n";
-        ostream << "  var size : vec2<i32> = textureDimensions(storageImage0);\n";
-        ostream << "  const layerCount : i32 = " << layerCount << ";\n";
+        ostream << "  let size : vec2<i32> = textureDimensions(storageImage0);\n";
+        ostream << "  let layerCount : i32 = " << layerCount << ";\n";
         ostream << "  for (var layer : i32 = 0; layer < layerCount; layer = layer + 1) {\n";
         ostream << "    for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
         ostream << "      for (var x : i32 = 0; x < size.x; x = x + 1) {\n";
@@ -359,8 +359,8 @@
         ostream << GetImageDeclaration(format, "write", is2DArray, 0) << "\n";
         ostream << GetImageDeclaration(format, "read", is2DArray, 1) << "\n";
         ostream << "[[stage(compute)]] fn main() {\n";
-        ostream << "  var size : vec2<i32> = textureDimensions(storageImage0);\n";
-        ostream << "  const layerCount : i32 = " << layerCount << ";\n";
+        ostream << "  let size : vec2<i32> = textureDimensions(storageImage0);\n";
+        ostream << "  let layerCount : i32 = " << layerCount << ";\n";
         ostream << "  for (var layer : i32 = 0; layer < layerCount; layer = layer + 1) {\n";
         ostream << "    for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
         ostream << "      for (var x : i32 = 0; x < size.x; x = x + 1) {\n";
@@ -648,9 +648,9 @@
     static constexpr wgpu::TextureFormat kRenderAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
 
     const char* kSimpleVertexShader = R"(
-[[builtin(position)]] var<out> position : vec4<f32>;
-[[stage(vertex)]] fn main() {
-  position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+;
+[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+  return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 })";
 
     const char* kComputeExpectedValue = "1 + x + size.x * (y + size.y * layer)";
@@ -746,23 +746,26 @@
         // uses green as the output color, otherwise uses red instead.
         std::ostringstream vsStream;
         vsStream << R"(
-[[builtin(position)]] var<out> position : vec4<f32>;
-[[location(0)]] var<out> o_color : vec4<f32>;
+struct VertexOut {
+  [[location(0)]] color : vec4<f32>;
+  [[builtin(position)]] position : vec4<f32>;
+};
 )" << CommonReadOnlyTestCode(format)
                  << R"(
-[[stage(vertex)]] fn main() {
-  position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+[[stage(vertex)]] fn main() -> VertexOut {
+  var output : VertexOut;
+  output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
   if (doTest()) {
-    o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+    output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
   } else {
-    o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+    output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
   }
+  return output;
 })";
         const char* kFragmentShader = R"(
-[[location(0)]] var<in> o_color : vec4<f32>;
-[[location(0)]] var<out> fragColor : vec4<f32>;
-[[stage(fragment)]] fn main() {
-  fragColor = o_color;
+[[stage(fragment)]]
+fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+  return color;
 })";
         CheckDrawsGreen(vsStream.str().c_str(), kFragmentShader, readonlyStorageTexture);
     }
@@ -791,16 +794,12 @@
         // uses green as the output color if the pixel value is expected, otherwise uses red
         // instead.
         std::ostringstream fsStream;
-        fsStream << R"(
-[[location(0)]] var<out> o_color : vec4<f32>;
-)" << CommonReadOnlyTestCode(format)
-                 << R"(
-[[stage(fragment)]] fn main() {
+        fsStream << CommonReadOnlyTestCode(format) << R"(
+[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
   if (doTest()) {
-    o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
-  } else {
-    o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+    return vec4<f32>(0.0, 1.0, 0.0, 1.0);
   }
+  return vec4<f32>(1.0, 0.0, 0.0, 1.0);
 })";
         CheckDrawsGreen(kSimpleVertexShader, fsStream.str().c_str(), readonlyStorageTexture);
     }
@@ -1180,15 +1179,13 @@
     const char* kVertexShader = kSimpleVertexShader;
     const std::string kFragmentShader = std::string(R"(
 [[group(0), binding(0)]] var srcImage : [[access(read)]] texture_storage_2d<r32uint>;
-[[location(0)]] var<out> o_color : vec4<f32>;
 )") + kCommonReadOnlyZeroInitTestCode +
                                         R"(
-[[stage(fragment)]] fn main() {
+[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
   if (doTest()) {
-    o_color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
-  } else {
-    o_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+    return vec4<f32>(0.0, 1.0, 0.0, 1.0);
   }
+  return vec4<f32>(1.0, 0.0, 0.0, 1.0);
 })";
     CheckDrawsGreen(kVertexShader, kFragmentShader.c_str(), readonlyStorageTexture);
 }
diff --git a/src/tests/end2end/SwapChainValidationTests.cpp b/src/tests/end2end/SwapChainValidationTests.cpp
index 1584e29..8bb488f 100644
--- a/src/tests/end2end/SwapChainValidationTests.cpp
+++ b/src/tests/end2end/SwapChainValidationTests.cpp
@@ -222,14 +222,12 @@
 TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
     utils::ComboRenderPipelineDescriptor2 pipelineDesc;
     pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
     pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
     // Validation will check that the sample count of the view matches this format.
     pipelineDesc.multisample.count = 1;
diff --git a/src/tests/end2end/TextureFormatTests.cpp b/src/tests/end2end/TextureFormatTests.cpp
index f5edb89..fc15b30 100644
--- a/src/tests/end2end/TextureFormatTests.cpp
+++ b/src/tests/end2end/TextureFormatTests.cpp
@@ -143,16 +143,14 @@
         utils::ComboRenderPipelineDescriptor2 desc;
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-3.0, -1.0),
                     vec2<f32>( 3.0, -1.0),
                     vec2<f32>( 0.0,  2.0));
 
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         // Compute the WGSL type of the texture's data.
@@ -160,11 +158,15 @@
 
         std::ostringstream fsSource;
         fsSource << "[[group(0), binding(0)]] var myTexture : texture_2d<" << type << ">;\n";
-        fsSource << "[[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;\n";
-        fsSource << "[[location(0)]] var<out> fragColor : vec4<" << type << ">;\n";
-        fsSource << "[[stage(fragment)]] fn main() {\n";
-        fsSource << "    fragColor = textureLoad(myTexture, vec2<i32>(FragCoord.xy), 0);\n";
-        fsSource << "}";
+        fsSource << "struct FragmentOut {\n";
+        fsSource << "   [[location(0)]] color : vec4<" << type << ">;\n";
+        fsSource << R"(};
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> FragmentOut {
+                var output : FragmentOut;
+                output.color = textureLoad(myTexture, vec2<i32>(FragCoord.xy), 0);
+                return output;
+            })";
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fsSource.str().c_str());
 
diff --git a/src/tests/end2end/TextureSubresourceTests.cpp b/src/tests/end2end/TextureSubresourceTests.cpp
index 5044805..a21d2d0 100644
--- a/src/tests/end2end/TextureSubresourceTests.cpp
+++ b/src/tests/end2end/TextureSubresourceTests.cpp
@@ -50,22 +50,19 @@
 
     void DrawTriangle(const wgpu::TextureView& view) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0,  1.0),
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 1.0, -1.0));
 
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(1.0, 0.0, 0.0, 1.0);
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
@@ -90,11 +87,9 @@
 
     void SampleAndDraw(const wgpu::TextureView& samplerView, const wgpu::TextureView& renderView) {
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 1.0,  1.0),
                     vec2<f32>(-1.0,  1.0),
@@ -102,19 +97,16 @@
                     vec2<f32>( 1.0, -1.0),
                     vec2<f32>( 1.0,  1.0));
 
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var samp : sampler;
             [[group(0), binding(1)]] var tex : texture_2d<f32>;
 
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
-
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(tex, samp, FragCoord.xy / vec2<f32>(4.0, 4.0));
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(tex, samp, FragCoord.xy / vec2<f32>(4.0, 4.0));
             })");
 
         utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/TextureViewTests.cpp b/src/tests/end2end/TextureViewTests.cpp
index f487b9f..1b137f7 100644
--- a/src/tests/end2end/TextureViewTests.cpp
+++ b/src/tests/end2end/TextureViewTests.cpp
@@ -61,26 +61,31 @@
 
     wgpu::ShaderModule CreateDefaultVertexShaderModule(wgpu::Device device) {
         return utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[location(0)]] var<out> TexCoord : vec2<f32>;
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            struct VertexOut {
+                [[location(0)]] texCoord : vec2<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
+
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
+                var output : VertexOut;
+                let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                                             vec2<f32>(-2., -2.),
                                             vec2<f32>(-2.,  2.),
                                             vec2<f32>( 2., -2.),
                                             vec2<f32>(-2.,  2.),
                                             vec2<f32>( 2., -2.),
                                             vec2<f32>( 2.,  2.));
-                const texCoord : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                let texCoord : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                                                  vec2<f32>(0., 0.),
                                                  vec2<f32>(0., 1.),
                                                  vec2<f32>(1., 0.),
                                                  vec2<f32>(0., 1.),
                                                  vec2<f32>(1., 0.),
                                                  vec2<f32>(1., 1.));
-                Position = vec4<f32>(pos[VertexIndex], 0., 1.);
-                TexCoord = texCoord[VertexIndex];
+                output.position = vec4<f32>(pos[VertexIndex], 0., 1.);
+                output.texCoord = texCoord[VertexIndex];
+                return output;
             }
         )");
     }
@@ -216,11 +221,10 @@
         const char* fragmentShader = R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d<f32>;
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
 
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, texCoord);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, texCoord);
             }
         )";
 
@@ -255,13 +259,12 @@
         const char* fragmentShader = R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d_array<f32>;
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
 
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, texCoord, 0) +
-                            textureSample(texture0, sampler0, texCoord, 1) +
-                            textureSample(texture0, sampler0, texCoord, 2);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, texCoord, 0) +
+                       textureSample(texture0, sampler0, texCoord, 1) +
+                       textureSample(texture0, sampler0, texCoord, 2);
             }
         )";
 
@@ -292,13 +295,11 @@
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : )"
                << textureType << R"(<f32>;
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
                 var sc : f32 = 2.0 * texCoord.x - 1.0;
                 var tc : f32 = 2.0 * texCoord.y - 1.0;
-                fragColor = textureSample(texture0, sampler0, vec3<f32>()"
+                return textureSample(texture0, sampler0, vec3<f32>()"
                << coordToCubeMapFace << ")";
 
         if (isCubeMapArray) {
@@ -365,13 +366,12 @@
     const char* fragmentShader = R"(
             [[group(0), binding(0)]] var sampler0 : sampler;
             [[group(0), binding(1)]] var texture0 : texture_2d_array<f32>;
-            [[location(0)]] var<in> texCoord : vec2<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
 
-            [[stage(fragment)]] fn main() {
-                fragColor = textureSample(texture0, sampler0, texCoord, 0) +
-                            textureSample(texture0, sampler0, texCoord, 1) +
-                            textureSample(texture0, sampler0, texCoord, 2);
+            [[stage(fragment)]]
+            fn main([[location(0)]] texCoord : vec2<f32>) -> [[location(0)]] vec4<f32> {
+                return textureSample(texture0, sampler0, texCoord, 0) +
+                       textureSample(texture0, sampler0, texCoord, 1) +
+                       textureSample(texture0, sampler0, texCoord, 2);
             }
         )";
 
@@ -496,10 +496,8 @@
         renderPassInfo.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f};
 
         const char* oneColorFragmentShader = R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(0.0, 1.0, 0.0, 1.0);
             }
         )";
         wgpu::ShaderModule oneColorFsModule =
diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp
index 665abe5..ce7fcaf 100644
--- a/src/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/tests/end2end/TextureZeroInitTests.cpp
@@ -69,9 +69,9 @@
         utils::ComboRenderPipelineDescriptor2 pipelineDescriptor;
         pipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(depth);
         const char* fs = R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-               fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+            ;
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+               return vec4<f32>(1.0, 0.0, 0.0, 1.0);
             }
         )";
         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, fs);
@@ -83,7 +83,7 @@
     }
     wgpu::ShaderModule CreateBasicVertexShaderForTest(float depth = 0.f) {
         std::string source = R"(
-            const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                 vec2<f32>(-1.0, -1.0),
                 vec2<f32>(-1.0,  1.0),
                 vec2<f32>( 1.0, -1.0),
@@ -92,11 +92,9 @@
                 vec2<f32>( 1.0, -1.0)
             );
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(pos[VertexIndex], )" +
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(pos[VertexIndex], )" +
                              std::to_string(depth) + R"(, 1.0);
             })";
         return utils::CreateShaderModule(device, source.c_str());
@@ -104,10 +102,14 @@
     wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() {
         return utils::CreateShaderModule(device, R"(
             [[group(0), binding(0)]] var texture0 : texture_2d<f32>;
-            [[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0);
+            struct FragmentOut {
+                [[location(0)]] color : vec4<f32>;
+            };
+            [[stage(fragment)]]
+            fn main([[builtin(frag_coord)]] FragCoord : vec4<f32>) -> FragmentOut {
+                var output : FragmentOut;
+                output.color = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0);
+                return output;
             }
         )");
     }
diff --git a/src/tests/end2end/VertexBufferRobustnessTests.cpp b/src/tests/end2end/VertexBufferRobustnessTests.cpp
index befcd7a..7253560 100644
--- a/src/tests/end2end/VertexBufferRobustnessTests.cpp
+++ b/src/tests/end2end/VertexBufferRobustnessTests.cpp
@@ -33,17 +33,13 @@
     wgpu::ShaderModule CreateVertexModule(const std::string& attributes,
                                           const std::string& successExpression) {
         return utils::CreateShaderModule(device, (attributes + R"(
-                [[builtin(position)]] var<out> Position : vec4<f32>;
-
-                [[stage(vertex)]] fn main() {
+                [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
                     if ()" + successExpression + R"() {
                         // Success case, move the vertex out of the viewport
-                        Position = vec4<f32>(-10.0, 0.0, 0.0, 1.0);
-                    } else {
-                        // Failure case, move the vertex inside the viewport
-                        Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+                        return vec4<f32>(-10.0, 0.0, 0.0, 1.0);
                     }
-                    return;
+                    // Failure case, move the vertex inside the viewport
+                    return vec4<f32>(0.0, 0.0, 0.0, 1.0);
                 }
             )")
                                                      .c_str());
@@ -58,11 +54,8 @@
                 bool expectation) {
         wgpu::ShaderModule vsModule = CreateVertexModule(attributes, successExpression);
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-                [[location(0)]] var<out> outColor : vec4<f32>;
-
-                [[stage(fragment)]] fn main() {
-                    outColor = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-                    return;
+                [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                    return vec4<f32>(1.0, 1.0, 1.0, 1.0);
                 }
             )");
 
diff --git a/src/tests/end2end/VertexFormatTests.cpp b/src/tests/end2end/VertexFormatTests.cpp
index d702e7d..2d2a6a5 100644
--- a/src/tests/end2end/VertexFormatTests.cpp
+++ b/src/tests/end2end/VertexFormatTests.cpp
@@ -232,19 +232,22 @@
         std::string expectedDataType = ShaderTypeGenerator(isFloat, isNormalized, isUnsigned, 1);
 
         std::ostringstream vs;
-        vs << "[[location(0)]] var<in> test : " << variableType << ";\n";
+        vs << "struct VertexIn {\n";
+        vs << "    [[location(0)]] test : " << variableType << ";\n";
+        vs << "    [[builtin(vertex_index)]] VertexIndex : u32;\n";
+        vs << "};\n";
+
         // Because x86 CPU using "extended
         // precision"(https://en.wikipedia.org/wiki/Extended_precision) during float
         // math(https://developer.nvidia.com/sites/default/files/akamai/cuda/files/NVIDIA-CUDA-Floating-Point.pdf),
         // move normalization and Float16ToFloat32 into shader to generate
         // expected value.
         vs << R"(
-            [[location(0)]] var<out> color : vec4<f32>;
             fn Float16ToFloat32(fp16 : u32) -> f32 {
-                const magic : u32 = (254u - 15u) << 23u;
-                const was_inf_nan : u32 = (127u + 16u) << 23u;
+                let magic : u32 = (254u - 15u) << 23u;
+                let was_inf_nan : u32 = (127u + 16u) << 23u;
                 var fp32u : u32 = (fp16 & 0x7FFFu) << 13u;
-                const fp32 : f32 = bitcast<f32>(fp32u) * bitcast<f32>(magic);
+                let fp32 : f32 = bitcast<f32>(fp32u) * bitcast<f32>(magic);
                 fp32u = bitcast<u32>(fp32);
                 if (fp32 >= bitcast<f32>(was_inf_nan)) {
                     fp32u = fp32u | (255u << 23u);
@@ -253,14 +256,19 @@
                 return bitcast<f32>(fp32u);
             }
 
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-            [[stage(vertex)]] fn main() {
-                const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
+
+            [[stage(vertex)]]
+            fn main(input : VertexIn) -> VertexOut {
+                let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
                     vec2<f32>(-1.0, -1.0),
                     vec2<f32>( 2.0,  0.0),
                     vec2<f32>( 0.0,  2.0));
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                var output : VertexOut;
+                output.position = vec4<f32>(pos[input.VertexIndex], 0.0, 1.0);
         )";
 
         // Declare expected values.
@@ -310,8 +318,8 @@
             std::string expectedVal = "expectedVal" + std::to_string(component);
             vs << "    var " << testVal << " : " << expectedDataType << ";\n";
             vs << "    var " << expectedVal << " : " << expectedDataType << ";\n";
-            vs << "    " << testVal << " = test" << suffix << ";\n";
-            vs << "    " << expectedVal << " = expected[VertexIndex]"
+            vs << "    " << testVal << " = input.test" << suffix << ";\n";
+            vs << "    " << expectedVal << " = expected[input.VertexIndex]"
                << "[" << component << "];\n";
             if (!isInputTypeFloat) {  // Integer / unsigned integer need to match exactly.
                 vs << "    success = success && (" << testVal << " == " << expectedVal << ");\n";
@@ -322,8 +330,8 @@
                 vs << "    if (isNan(" << expectedVal << ")) {\n";
                 vs << "       success = success && isNan(" << testVal << ");\n";
                 vs << "    } else {\n";
-                vs << "        const testValFloatToUint : u32 = bitcast<u32>(" << testVal << ");\n";
-                vs << "        const expectedValFloatToUint : u32 = bitcast<u32>(" << expectedVal
+                vs << "        let testValFloatToUint : u32 = bitcast<u32>(" << testVal << ");\n";
+                vs << "        let expectedValFloatToUint : u32 = bitcast<u32>(" << expectedVal
                    << ");\n";
                 vs << "        success = success && max(testValFloatToUint, "
                       "expectedValFloatToUint)";
@@ -333,18 +341,18 @@
         }
         vs << R"(
             if (success) {
-                color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+                output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
             } else {
-                color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
             }
+            return output;
         })";
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vs.str().c_str());
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-                [[location(0)]] var<in> color : vec4<f32>;
-                [[location(0)]] var<out> FragColor : vec4<f32>;
-                [[stage(fragment)]] fn main() {
-                    FragColor = color;
+                [[stage(fragment)]]
+                fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                    return color;
                 })");
 
         uint32_t bytesPerComponents = BytesPerComponents(format);
diff --git a/src/tests/end2end/VertexStateTests.cpp b/src/tests/end2end/VertexStateTests.cpp
index 61a3636..36b9fd8 100644
--- a/src/tests/end2end/VertexStateTests.cpp
+++ b/src/tests/end2end/VertexStateTests.cpp
@@ -70,45 +70,56 @@
                                           int multiplier,
                                           const std::vector<ShaderTestSpec>& testSpec) {
         std::ostringstream vs;
+        vs << "struct VertexIn {\n";
 
         // TODO(cwallez@chromium.org): this only handles float attributes, we should extend it to
         // other types Adds line of the form
-        //    [[location(1) var<in> input1 : vec4<f32>;
+        //    [[location(1) input1 : vec4<f32>;
         for (const auto& input : testSpec) {
-            vs << "[[location(" << input.location << ")]] var<in> input" << input.location
+            vs << "[[location(" << input.location << ")]] input" << input.location
                << " : vec4<f32>;\n";
         }
 
-        vs << "[[builtin(vertex_index)]] var<in> VertexIndex : u32;\n";
-        vs << "[[builtin(instance_index)]] var<in> InstanceIndex : u32;\n";
-        vs << "[[location(0)]] var<out> color : vec4<f32>;\n";
-        vs << "[[builtin(position)]] var<out> Position : vec4<f32>;\n";
-        vs << "[[stage(vertex)]] fn main() {\n";
+        vs << R"(
+                [[builtin(vertex_index)]] VertexIndex : u32;
+                [[builtin(instance_index)]] InstanceIndex : u32;
+            };
+            
+            struct VertexOut {
+                [[location(0)]] color : vec4<f32>;
+                [[builtin(position)]] position : vec4<f32>;
+            };
+
+            [[stage(vertex)]] fn main(input : VertexIn) -> VertexOut {
+                var output : VertexOut;
+        )";
 
         // Hard code the triangle in the shader so that we don't have to add a vertex input for it.
         // Also this places the triangle in the grid based on its VertexID and InstanceID
-        vs << "    const pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n"
+        vs << "    let pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n"
               "         vec2<f32>(0.5, 1.0), vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0));\n";
-        vs << "    var offset : vec2<f32> = vec2<f32>(f32(VertexIndex / 3u), "
-              "f32(InstanceIndex));\n";
-        vs << "    var worldPos : vec2<f32> = pos[VertexIndex % 3u] + offset;\n";
+        vs << "    var offset : vec2<f32> = vec2<f32>(f32(input.VertexIndex / 3u), "
+              "f32(input.InstanceIndex));\n";
+        vs << "    var worldPos : vec2<f32> = pos[input.VertexIndex % 3u] + offset;\n";
         vs << "    var position : vec4<f32> = vec4<f32>(0.5 * worldPos - vec2<f32>(1.0, 1.0), 0.0, "
               "1.0);\n";
-        vs << "    Position = vec4<f32>(position.x, -position.y, position.z, position.w);\n";
+        vs << "    output.position = vec4<f32>(position.x, -position.y, position.z, position.w);\n";
 
         // Perform the checks by successively ANDing a boolean
         vs << "    var success : bool = true;\n";
         for (const auto& input : testSpec) {
             for (int component = 0; component < 4; ++component) {
-                vs << "    success = success && (input" << input.location << "[" << component
+                vs << "    success = success && (input.input" << input.location << "[" << component
                    << "] == ";
                 if (ShouldComponentBeDefault(input.format, component)) {
                     vs << (component == 3 ? "1.0" : "0.0");
                 } else {
                     if (input.step == InputStepMode::Vertex) {
-                        vs << "f32(" << multiplier << "u * VertexIndex) + " << component << ".0";
+                        vs << "f32(" << multiplier << "u * input.VertexIndex) + " << component
+                           << ".0";
                     } else {
-                        vs << "f32(" << multiplier << "u * InstanceIndex) + " << component << ".0";
+                        vs << "f32(" << multiplier << "u * input.InstanceIndex) + " << component
+                           << ".0";
                     }
                 }
                 vs << ");\n";
@@ -116,19 +127,20 @@
         }
 
         // Choose the color
-        vs << "    if (success) {\n";
-        vs << "        color = vec4<f32>(0.0, 1.0, 0.0, 1.0);\n";
-        vs << "    } else {\n";
-        vs << "        color = vec4<f32>(1.0, 0.0, 0.0, 1.0);\n";
-        vs << "    }\n";
-        vs << "}\n";
+        vs << R"(
+            if (success) {
+                output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+            } else {
+                output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+            }
+            return output;
+        })";
 
         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, vs.str().c_str());
         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<in> color : vec4<f32>;
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = color;
+            [[stage(fragment)]]
+            fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+                return color;
             }
         )");
 
@@ -570,36 +582,41 @@
 
     utils::ComboRenderPipelineDescriptor2 pipelineDesc;
     pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<in> attr0 : vec4<f32>;
-        [[location(1)]] var<in> attr1 : vec2<u32>;
-        [[location(2)]] var<in> attr2 : vec4<f32>;
-        [[location(3)]] var<in> attr3 : f32;
+        struct VertexIn {
+            [[location(0)]] attr0 : vec4<f32>;
+            [[location(1)]] attr1 : vec2<u32>;
+            [[location(2)]] attr2 : vec4<f32>;
+            [[location(3)]] attr3 : f32;
+        };
 
-        [[location(0)]] var<out> color : vec4<f32>;
-        [[builtin(position)]] var<out> Position : vec4<f32>;
+        struct VertexOut {
+            [[location(0)]] color : vec4<f32>;
+            [[builtin(position)]] position : vec4<f32>;
+        };
 
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main(input : VertexIn) -> VertexOut {
+            var output : VertexOut;
+            output.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
 
             var success : bool = (
-                attr0.x == 1.0 &&
-                attr1.x == 2u &&
-                attr1.y == 3u &&
-                attr2.z == 4.0 &&
-                attr2.w == 5.0 &&
-                attr3 == 1.0
+                input.attr0.x == 1.0 &&
+                input.attr1.x == 2u &&
+                input.attr1.y == 3u &&
+                input.attr2.z == 4.0 &&
+                input.attr2.w == 5.0 &&
+                input.attr3 == 1.0
             );
             if (success) {
-                color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+                output.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
             } else {
-                color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+                output.color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
             }
+            return output;
         })");
     pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<in> color : vec4<f32>;
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = color;
+        [[stage(fragment)]]
+        fn main([[location(0)]] color : vec4<f32>) -> [[location(0)]] vec4<f32> {
+            return color;
         })");
     pipelineDesc.vertex.bufferCount = vertexState.vertexBufferCount;
     pipelineDesc.vertex.buffers = &vertexState.cVertexBuffers[0];
@@ -642,15 +659,13 @@
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 3, 3);
 
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
 
     utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/ViewportOrientationTests.cpp b/src/tests/end2end/ViewportOrientationTests.cpp
index 372bba3..2f62f42 100644
--- a/src/tests/end2end/ViewportOrientationTests.cpp
+++ b/src/tests/end2end/ViewportOrientationTests.cpp
@@ -24,16 +24,13 @@
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 2, 2);
 
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
-        [[builtin(position)]] var<out> Position : vec4<f32>;
-
-        [[stage(vertex)]] fn main() {
-            Position = vec4<f32>(-0.5, 0.5, 0.0, 1.0);
+        [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
+            return vec4<f32>(-0.5, 0.5, 0.0, 1.0);
         })");
 
     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
-        [[location(0)]] var<out> fragColor : vec4<f32>;
-        [[stage(fragment)]] fn main() {
-            fragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
+        [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+            return vec4<f32>(0.0, 1.0, 0.0, 1.0);
         })");
 
     utils::ComboRenderPipelineDescriptor2 descriptor;
diff --git a/src/tests/end2end/ViewportTests.cpp b/src/tests/end2end/ViewportTests.cpp
index 3f448f5..68bb1f4 100644
--- a/src/tests/end2end/ViewportTests.cpp
+++ b/src/tests/end2end/ViewportTests.cpp
@@ -23,10 +23,7 @@
         DawnTest::SetUp();
 
         mQuadVS = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+            let pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
                 vec2<f32>(-1.0,  1.0),
                 vec2<f32>(-1.0, -1.0),
                 vec2<f32>( 1.0,  1.0),
@@ -34,14 +31,14 @@
                 vec2<f32>(-1.0, -1.0),
                 vec2<f32>( 1.0, -1.0));
 
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
             })");
 
         mQuadFS = utils::CreateShaderModule(device, R"(
-            [[location(0)]] var<out> fragColor : vec4<f32>;
-            [[stage(fragment)]] fn main() {
-                fragColor = vec4<f32>(1.0, 1.0, 1.0, 1.0);
+            [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
+                return vec4<f32>(1.0, 1.0, 1.0, 1.0);
             })");
     }
 
@@ -96,16 +93,14 @@
         // Create a pipeline drawing 3 points at depth 1.0, 0.5 and 0.0.
         utils::ComboRenderPipelineDescriptor2 pipelineDesc;
         pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
-            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
-            [[builtin(position)]] var<out> Position : vec4<f32>;
-
-            const points : array<vec3<f32>, 3> = array<vec3<f32>, 3>(
+            let points : array<vec3<f32>, 3> = array<vec3<f32>, 3>(
                 vec3<f32>(-0.9, 0.0, 1.0),
                 vec3<f32>( 0.0, 0.0, 0.5),
                 vec3<f32>( 0.9, 0.0, 0.0));
 
-            [[stage(vertex)]] fn main() {
-                Position = vec4<f32>(points[VertexIndex], 1.0);
+            [[stage(vertex)]]
+            fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
+                return vec4<f32>(points[VertexIndex], 1.0);
             })");
         pipelineDesc.cFragment.module = mQuadFS;
         pipelineDesc.cFragment.targetCount = 0;