writer/msl: Fix array type emission

Add a special-case for pointer-to-array types, where the * and the
variable name need to be enclosed in parentheses in between the array
element type and the size.

Move the `const` qualifier to before the array size.

Add E2E tests to cover all non-handle types used in various places.

Fixed: tint:822
Change-Id: I93b7d6867f92397aa47838ab2c94530b6e634617
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51823
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 910850f..193e4e7 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -1314,8 +1314,8 @@
     if (!EmitType(type, program_->Symbols().NameFor(v->symbol()))) {
       return false;
     }
-    // Array name is output as part of the type
-    if (!type->Is<sem::Array>()) {
+    // Parameter name is output as part of the type for arrays and pointers.
+    if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
       out_ << " " << program_->Symbols().NameFor(v->symbol());
     }
   }
@@ -1926,10 +1926,17 @@
       default:
         TINT_ICE(diagnostics_) << "unhandled storage class for pointer";
     }
-    if (!EmitType(ptr->StoreType(), "")) {
-      return false;
+    if (ptr->StoreType()->Is<sem::Array>()) {
+      std::string inner = "(*" + name + ")";
+      if (!EmitType(ptr->StoreType(), inner)) {
+        return false;
+      }
+    } else {
+      if (!EmitType(ptr->StoreType(), "")) {
+        return false;
+      }
+      out_ << "* " << name;
     }
-    out_ << "*";
   } else if (type->Is<sem::Sampler>()) {
     out_ << "sampler";
   } else if (auto* str = type->As<sem::Struct>()) {
@@ -2201,14 +2208,17 @@
     return false;
   }
   auto* type = var->Type()->UnwrapRef();
-  if (!EmitType(type, program_->Symbols().NameFor(decl->symbol()))) {
+
+  std::string name = program_->Symbols().NameFor(decl->symbol());
+  if (decl->is_const()) {
+    name = "const " + name;
+  }
+  if (!EmitType(type, name)) {
     return false;
   }
-  if (decl->is_const()) {
-    out_ << " const";
-  }
-  if (!type->Is<sem::Array>()) {
-    out_ << " " << program_->Symbols().NameFor(decl->symbol());
+  // Variable name is output as part of the type for arrays and pointers.
+  if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+    out_ << " " << name;
   }
 
   if (!skip_constructor) {
diff --git a/test/bug/tint/824.wgsl.expected.msl b/test/bug/tint/824.wgsl.expected.msl
index 9f1e787..fa3aabb 100644
--- a/test/bug/tint/824.wgsl.expected.msl
+++ b/test/bug/tint/824.wgsl.expected.msl
@@ -17,11 +17,11 @@
 vertex tint_symbol_3 tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
   uint const VertexIndex = tint_symbol_1.VertexIndex;
   uint const InstanceIndex = tint_symbol_1.InstanceIndex;
-  float2 zv[4] const = {float2(0.200000003f, 0.200000003f), float2(0.300000012f, 0.300000012f), float2(-0.100000001f, -0.100000001f), float2(1.100000024f, 1.100000024f)};
+  float2 const zv[4] = {float2(0.200000003f, 0.200000003f), float2(0.300000012f, 0.300000012f), float2(-0.100000001f, -0.100000001f), float2(1.100000024f, 1.100000024f)};
   float const z = zv[InstanceIndex].x;
   Output output = {};
   output.Position = float4(0.5f, 0.5f, z, 1.0f);
-  float4 colors[4] const = {float4(1.0f, 0.0f, 0.0f, 1.0f), float4(0.0f, 1.0f, 0.0f, 1.0f), float4(0.0f, 0.0f, 1.0f, 1.0f), float4(1.0f, 1.0f, 1.0f, 1.0f)};
+  float4 const colors[4] = {float4(1.0f, 0.0f, 0.0f, 1.0f), float4(0.0f, 1.0f, 0.0f, 1.0f), float4(0.0f, 0.0f, 1.0f, 1.0f), float4(1.0f, 1.0f, 1.0f, 1.0f)};
   output.color = colors[InstanceIndex];
   return {output.color, output.Position};
 }
diff --git a/test/types/function_scope_declarations.wgsl b/test/types/function_scope_declarations.wgsl
new file mode 100644
index 0000000..f5a04a0
--- /dev/null
+++ b/test/types/function_scope_declarations.wgsl
@@ -0,0 +1,29 @@
+struct S {
+};
+
+[[stage(compute)]]
+fn main() {
+  var bool_var : bool = bool();
+  let bool_let : bool = bool();
+  var i32_var : i32 = i32();
+  let i32_let : i32 = i32();
+  var u32_var : u32 = u32();
+  let u32_let : u32 = u32();
+  var f32_var : f32 = f32();
+  let f32_let : f32 = f32();
+  var v2i32_var : vec2<i32> = vec2<i32>();
+  let v2i32_let : vec2<i32> = vec2<i32>();
+  var v3u32_var : vec3<u32> = vec3<u32>();
+  let v3u32_let : vec3<u32> = vec3<u32>();
+  var v4f32_var : vec4<f32> = vec4<f32>();
+  let v4f32_let : vec4<f32> = vec4<f32>();
+  var m2x3_var : mat2x3<f32> = mat2x3<f32>();
+  let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+  var arr_var : array<f32, 4> = array<f32, 4>();
+  let arr_let : array<f32, 4> = array<f32, 4>();
+  var struct_var : S = S();
+  let struct_let : S = S();
+  let ptr_f32 : ptr<function, f32> = &f32_var;
+  let ptr_vec : ptr<function, vec4<f32>> = &v4f32_var;
+  let ptr_arr : ptr<function, array<f32, 4>> = &arr_var;
+}
diff --git a/test/types/function_scope_declarations.wgsl.expected.hlsl b/test/types/function_scope_declarations.wgsl.expected.hlsl
new file mode 100644
index 0000000..b3db42f
--- /dev/null
+++ b/test/types/function_scope_declarations.wgsl.expected.hlsl
@@ -0,0 +1 @@
+SKIP: Failed to generate: error: pointers not supported in HLSL
diff --git a/test/types/function_scope_declarations.wgsl.expected.msl b/test/types/function_scope_declarations.wgsl.expected.msl
new file mode 100644
index 0000000..8c0e766
--- /dev/null
+++ b/test/types/function_scope_declarations.wgsl.expected.msl
@@ -0,0 +1,33 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+};
+
+kernel void tint_symbol() {
+  bool bool_var = bool();
+  bool const bool_let = bool();
+  int i32_var = int();
+  int const i32_let = int();
+  uint u32_var = uint();
+  uint const u32_let = uint();
+  float f32_var = float();
+  float const f32_let = float();
+  int2 v2i32_var = int2();
+  int2 const v2i32_let = int2();
+  uint3 v3u32_var = uint3();
+  uint3 const v3u32_let = uint3();
+  float4 v4f32_var = float4();
+  float4 const v4f32_let = float4();
+  float2x3 m2x3_var = float2x3();
+  float3x4 const m3x4_let = float3x4();
+  float arr_var[4] = {};
+  float const arr_let[4] = {};
+  S struct_var = {};
+  S const struct_let = {};
+  thread float* const ptr_f32 = &(f32_var);
+  thread float4* const ptr_vec = &(v4f32_var);
+  thread float (*const ptr_arr)[4] = &(arr_var);
+  return;
+}
+
diff --git a/test/types/function_scope_declarations.wgsl.expected.spvasm b/test/types/function_scope_declarations.wgsl.expected.spvasm
new file mode 100644
index 0000000..d5fa434
--- /dev/null
+++ b/test/types/function_scope_declarations.wgsl.expected.spvasm
@@ -0,0 +1,82 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 52
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %main "main"
+               OpName %bool_var "bool_var"
+               OpName %i32_var "i32_var"
+               OpName %u32_var "u32_var"
+               OpName %f32_var "f32_var"
+               OpName %v2i32_var "v2i32_var"
+               OpName %v3u32_var "v3u32_var"
+               OpName %v4f32_var "v4f32_var"
+               OpName %m2x3_var "m2x3_var"
+               OpName %arr_var "arr_var"
+               OpName %S "S"
+               OpName %struct_var "struct_var"
+               OpDecorate %_arr_float_uint_4 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %bool = OpTypeBool
+          %6 = OpConstantNull %bool
+%_ptr_Function_bool = OpTypePointer Function %bool
+        %int = OpTypeInt 32 1
+         %10 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+         %14 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+      %float = OpTypeFloat 32
+         %18 = OpConstantNull %float
+%_ptr_Function_float = OpTypePointer Function %float
+      %v2int = OpTypeVector %int 2
+         %22 = OpConstantNull %v2int
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+     %v3uint = OpTypeVector %uint 3
+         %26 = OpConstantNull %v3uint
+%_ptr_Function_v3uint = OpTypePointer Function %v3uint
+    %v4float = OpTypeVector %float 4
+         %30 = OpConstantNull %v4float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+    %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+         %35 = OpConstantNull %mat2v3float
+%_ptr_Function_mat2v3float = OpTypePointer Function %mat2v3float
+%mat3v4float = OpTypeMatrix %v4float 3
+         %39 = OpConstantNull %mat3v4float
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+         %42 = OpConstantNull %_arr_float_uint_4
+%_ptr_Function__arr_float_uint_4 = OpTypePointer Function %_arr_float_uint_4
+          %S = OpTypeStruct
+         %46 = OpConstantNull %S
+%_ptr_Function_S = OpTypePointer Function %S
+       %main = OpFunction %void None %1
+          %4 = OpLabel
+   %bool_var = OpVariable %_ptr_Function_bool Function %6
+    %i32_var = OpVariable %_ptr_Function_int Function %10
+    %u32_var = OpVariable %_ptr_Function_uint Function %14
+    %f32_var = OpVariable %_ptr_Function_float Function %18
+  %v2i32_var = OpVariable %_ptr_Function_v2int Function %22
+  %v3u32_var = OpVariable %_ptr_Function_v3uint Function %26
+  %v4f32_var = OpVariable %_ptr_Function_v4float Function %30
+   %m2x3_var = OpVariable %_ptr_Function_mat2v3float Function %35
+    %arr_var = OpVariable %_ptr_Function__arr_float_uint_4 Function %42
+ %struct_var = OpVariable %_ptr_Function_S Function %46
+               OpStore %bool_var %6
+               OpStore %i32_var %10
+               OpStore %u32_var %14
+               OpStore %f32_var %18
+               OpStore %v2i32_var %22
+               OpStore %v3u32_var %26
+               OpStore %v4f32_var %30
+               OpStore %m2x3_var %35
+               OpStore %arr_var %42
+               OpStore %struct_var %46
+               OpReturn
+               OpFunctionEnd
diff --git a/test/types/function_scope_declarations.wgsl.expected.wgsl b/test/types/function_scope_declarations.wgsl.expected.wgsl
new file mode 100644
index 0000000..e806b19
--- /dev/null
+++ b/test/types/function_scope_declarations.wgsl.expected.wgsl
@@ -0,0 +1,29 @@
+struct S {
+};
+
+[[stage(compute)]]
+fn main() {
+  var bool_var : bool = bool();
+  let bool_let : bool = bool();
+  var i32_var : i32 = i32();
+  let i32_let : i32 = i32();
+  var u32_var : u32 = u32();
+  let u32_let : u32 = u32();
+  var f32_var : f32 = f32();
+  let f32_let : f32 = f32();
+  var v2i32_var : vec2<i32> = vec2<i32>();
+  let v2i32_let : vec2<i32> = vec2<i32>();
+  var v3u32_var : vec3<u32> = vec3<u32>();
+  let v3u32_let : vec3<u32> = vec3<u32>();
+  var v4f32_var : vec4<f32> = vec4<f32>();
+  let v4f32_let : vec4<f32> = vec4<f32>();
+  var m2x3_var : mat2x3<f32> = mat2x3<f32>();
+  let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+  var arr_var : array<f32, 4> = array<f32, 4>();
+  let arr_let : array<f32, 4> = array<f32, 4>();
+  var struct_var : S = S();
+  let struct_let : S = S();
+  let ptr_f32 : ptr<function, f32> = &(f32_var);
+  let ptr_vec : ptr<function, vec4<f32>> = &(v4f32_var);
+  let ptr_arr : ptr<function, array<f32, 4>> = &(arr_var);
+}
diff --git a/test/types/module_scope_declarations.wgsl b/test/types/module_scope_declarations.wgsl
new file mode 100644
index 0000000..6be0c4a
--- /dev/null
+++ b/test/types/module_scope_declarations.wgsl
@@ -0,0 +1,27 @@
+struct S {
+};
+
+var<private> bool_var : bool = bool();
+let bool_let : bool = bool();
+var<private> i32_var : i32 = i32();
+let i32_let : i32 = i32();
+var<private> u32_var : u32 = u32();
+let u32_let : u32 = u32();
+var<private> f32_var : f32 = f32();
+let f32_let : f32 = f32();
+var<private> v2i32_var : vec2<i32> = vec2<i32>();
+let v2i32_let : vec2<i32> = vec2<i32>();
+var<private> v3u32_var : vec3<u32> = vec3<u32>();
+let v3u32_let : vec3<u32> = vec3<u32>();
+var<private> v4f32_var : vec4<f32> = vec4<f32>();
+let v4f32_let : vec4<f32> = vec4<f32>();
+var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
+let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+var<private> arr_var : array<f32, 4> = array<f32, 4>();
+let arr_let : array<f32, 4> = array<f32, 4>();
+var<private> struct_var : S = S();
+let struct_let : S = S();
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/module_scope_declarations.wgsl.expected.hlsl b/test/types/module_scope_declarations.wgsl.expected.hlsl
new file mode 100644
index 0000000..a0d6816
--- /dev/null
+++ b/test/types/module_scope_declarations.wgsl.expected.hlsl
@@ -0,0 +1,18 @@
+struct S {
+};
+
+static const bool bool_let = false;
+static const int i32_let = 0;
+static const uint u32_let = 0u;
+static const float f32_let = 0.0f;
+static const int2 v2i32_let = int2(0, 0);
+static const uint3 v3u32_let = uint3(0u, 0u, 0u);
+static const float4 v4f32_let = float4(0.0f, 0.0f, 0.0f, 0.0f);
+static const float3x4 m3x4_let = float3x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+static const float arr_let[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+static const S struct_let = {};
+[numthreads(1, 1, 1)]
+void main() {
+  return;
+}
+
diff --git a/test/types/module_scope_declarations.wgsl.expected.msl b/test/types/module_scope_declarations.wgsl.expected.msl
new file mode 100644
index 0000000..1b2d3db
--- /dev/null
+++ b/test/types/module_scope_declarations.wgsl.expected.msl
@@ -0,0 +1 @@
+SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
diff --git a/test/types/module_scope_declarations.wgsl.expected.spvasm b/test/types/module_scope_declarations.wgsl.expected.spvasm
new file mode 100644
index 0000000..9d09d23
--- /dev/null
+++ b/test/types/module_scope_declarations.wgsl.expected.spvasm
@@ -0,0 +1,82 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 49
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %bool_var "bool_var"
+               OpName %bool_let "bool_let"
+               OpName %i32_var "i32_var"
+               OpName %i32_let "i32_let"
+               OpName %u32_var "u32_var"
+               OpName %u32_let "u32_let"
+               OpName %f32_var "f32_var"
+               OpName %f32_let "f32_let"
+               OpName %v2i32_var "v2i32_var"
+               OpName %v2i32_let "v2i32_let"
+               OpName %v3u32_var "v3u32_var"
+               OpName %v3u32_let "v3u32_let"
+               OpName %v4f32_var "v4f32_var"
+               OpName %v4f32_let "v4f32_let"
+               OpName %m2x3_var "m2x3_var"
+               OpName %m3x4_let "m3x4_let"
+               OpName %arr_var "arr_var"
+               OpName %arr_let "arr_let"
+               OpName %S "S"
+               OpName %struct_var "struct_var"
+               OpName %struct_let "struct_let"
+               OpName %main "main"
+               OpDecorate %_arr_float_uint_4 ArrayStride 4
+       %bool = OpTypeBool
+   %bool_let = OpConstantNull %bool
+%_ptr_Private_bool = OpTypePointer Private %bool
+   %bool_var = OpVariable %_ptr_Private_bool Private %bool_let
+        %int = OpTypeInt 32 1
+    %i32_let = OpConstantNull %int
+%_ptr_Private_int = OpTypePointer Private %int
+    %i32_var = OpVariable %_ptr_Private_int Private %i32_let
+       %uint = OpTypeInt 32 0
+    %u32_let = OpConstantNull %uint
+%_ptr_Private_uint = OpTypePointer Private %uint
+    %u32_var = OpVariable %_ptr_Private_uint Private %u32_let
+      %float = OpTypeFloat 32
+    %f32_let = OpConstantNull %float
+%_ptr_Private_float = OpTypePointer Private %float
+    %f32_var = OpVariable %_ptr_Private_float Private %f32_let
+      %v2int = OpTypeVector %int 2
+  %v2i32_let = OpConstantNull %v2int
+%_ptr_Private_v2int = OpTypePointer Private %v2int
+  %v2i32_var = OpVariable %_ptr_Private_v2int Private %v2i32_let
+     %v3uint = OpTypeVector %uint 3
+  %v3u32_let = OpConstantNull %v3uint
+%_ptr_Private_v3uint = OpTypePointer Private %v3uint
+  %v3u32_var = OpVariable %_ptr_Private_v3uint Private %v3u32_let
+    %v4float = OpTypeVector %float 4
+  %v4f32_let = OpConstantNull %v4float
+%_ptr_Private_v4float = OpTypePointer Private %v4float
+  %v4f32_var = OpVariable %_ptr_Private_v4float Private %v4f32_let
+    %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+         %31 = OpConstantNull %mat2v3float
+%_ptr_Private_mat2v3float = OpTypePointer Private %mat2v3float
+   %m2x3_var = OpVariable %_ptr_Private_mat2v3float Private %31
+%mat3v4float = OpTypeMatrix %v4float 3
+   %m3x4_let = OpConstantNull %mat3v4float
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+    %arr_let = OpConstantNull %_arr_float_uint_4
+%_ptr_Private__arr_float_uint_4 = OpTypePointer Private %_arr_float_uint_4
+    %arr_var = OpVariable %_ptr_Private__arr_float_uint_4 Private %arr_let
+          %S = OpTypeStruct
+ %struct_let = OpConstantNull %S
+%_ptr_Private_S = OpTypePointer Private %S
+ %struct_var = OpVariable %_ptr_Private_S Private %struct_let
+       %void = OpTypeVoid
+         %45 = OpTypeFunction %void
+       %main = OpFunction %void None %45
+         %48 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/types/module_scope_declarations.wgsl.expected.wgsl b/test/types/module_scope_declarations.wgsl.expected.wgsl
new file mode 100644
index 0000000..e6edc6e
--- /dev/null
+++ b/test/types/module_scope_declarations.wgsl.expected.wgsl
@@ -0,0 +1,46 @@
+struct S {
+};
+
+var<private> bool_var : bool = bool();
+
+let bool_let : bool = bool();
+
+var<private> i32_var : i32 = i32();
+
+let i32_let : i32 = i32();
+
+var<private> u32_var : u32 = u32();
+
+let u32_let : u32 = u32();
+
+var<private> f32_var : f32 = f32();
+
+let f32_let : f32 = f32();
+
+var<private> v2i32_var : vec2<i32> = vec2<i32>();
+
+let v2i32_let : vec2<i32> = vec2<i32>();
+
+var<private> v3u32_var : vec3<u32> = vec3<u32>();
+
+let v3u32_let : vec3<u32> = vec3<u32>();
+
+var<private> v4f32_var : vec4<f32> = vec4<f32>();
+
+let v4f32_let : vec4<f32> = vec4<f32>();
+
+var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
+
+let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+
+var<private> arr_var : array<f32, 4> = array<f32, 4>();
+
+let arr_let : array<f32, 4> = array<f32, 4>();
+
+var<private> struct_var : S = S();
+
+let struct_let : S = S();
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/parameters.wgsl b/test/types/parameters.wgsl
new file mode 100644
index 0000000..04082ff
--- /dev/null
+++ b/test/types/parameters.wgsl
@@ -0,0 +1,22 @@
+struct S {
+};
+
+fn foo(
+  param_bool : bool,
+  param_i32 : i32,
+  param_u32 : u32,
+  param_f32 : f32,
+  param_v2i32 : vec2<i32>,
+  param_v3u32 : vec3<u32>,
+  param_v4f32 : vec4<f32>,
+  param_m2x3 : mat2x3<f32>,
+  param_arr : array<f32, 4>,
+  param_struct : S,
+  param_ptr_f32 : ptr<function, f32>,
+  param_ptr_vec : ptr<function, vec4<f32>>,
+  param_ptr_arr : ptr<function, array<f32, 4>>,
+) {}
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/parameters.wgsl.expected.hlsl b/test/types/parameters.wgsl.expected.hlsl
new file mode 100644
index 0000000..b3db42f
--- /dev/null
+++ b/test/types/parameters.wgsl.expected.hlsl
@@ -0,0 +1 @@
+SKIP: Failed to generate: error: pointers not supported in HLSL
diff --git a/test/types/parameters.wgsl.expected.msl b/test/types/parameters.wgsl.expected.msl
new file mode 100644
index 0000000..9134d86
--- /dev/null
+++ b/test/types/parameters.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+};
+
+void foo(bool param_bool, int param_i32, uint param_u32, float param_f32, int2 param_v2i32, uint3 param_v3u32, float4 param_v4f32, float2x3 param_m2x3, float param_arr[4], S param_struct, thread float* param_ptr_f32, thread float4* param_ptr_vec, thread float (*param_ptr_arr)[4]) {
+}
+
+kernel void tint_symbol() {
+  return;
+}
+
diff --git a/test/types/parameters.wgsl.expected.spvasm b/test/types/parameters.wgsl.expected.spvasm
new file mode 100644
index 0000000..5edf8f0
--- /dev/null
+++ b/test/types/parameters.wgsl.expected.spvasm
@@ -0,0 +1,65 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 36
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %S "S"
+               OpName %foo "foo"
+               OpName %param_bool "param_bool"
+               OpName %param_i32 "param_i32"
+               OpName %param_u32 "param_u32"
+               OpName %param_f32 "param_f32"
+               OpName %param_v2i32 "param_v2i32"
+               OpName %param_v3u32 "param_v3u32"
+               OpName %param_v4f32 "param_v4f32"
+               OpName %param_m2x3 "param_m2x3"
+               OpName %param_arr "param_arr"
+               OpName %param_struct "param_struct"
+               OpName %param_ptr_f32 "param_ptr_f32"
+               OpName %param_ptr_vec "param_ptr_vec"
+               OpName %param_ptr_arr "param_ptr_arr"
+               OpName %main "main"
+               OpDecorate %_arr_float_uint_4 ArrayStride 4
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+      %v2int = OpTypeVector %int 2
+     %v3uint = OpTypeVector %uint 3
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+          %S = OpTypeStruct
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Function__arr_float_uint_4 = OpTypePointer Function %_arr_float_uint_4
+          %1 = OpTypeFunction %void %bool %int %uint %float %v2int %v3uint %v4float %mat2v3float %_arr_float_uint_4 %S %_ptr_Function_float %_ptr_Function_v4float %_ptr_Function__arr_float_uint_4
+         %33 = OpTypeFunction %void
+        %foo = OpFunction %void None %1
+ %param_bool = OpFunctionParameter %bool
+  %param_i32 = OpFunctionParameter %int
+  %param_u32 = OpFunctionParameter %uint
+  %param_f32 = OpFunctionParameter %float
+%param_v2i32 = OpFunctionParameter %v2int
+%param_v3u32 = OpFunctionParameter %v3uint
+%param_v4f32 = OpFunctionParameter %v4float
+ %param_m2x3 = OpFunctionParameter %mat2v3float
+  %param_arr = OpFunctionParameter %_arr_float_uint_4
+%param_struct = OpFunctionParameter %S
+%param_ptr_f32 = OpFunctionParameter %_ptr_Function_float
+%param_ptr_vec = OpFunctionParameter %_ptr_Function_v4float
+%param_ptr_arr = OpFunctionParameter %_ptr_Function__arr_float_uint_4
+         %32 = OpLabel
+               OpReturn
+               OpFunctionEnd
+       %main = OpFunction %void None %33
+         %35 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/types/parameters.wgsl.expected.wgsl b/test/types/parameters.wgsl.expected.wgsl
new file mode 100644
index 0000000..d06b2dd
--- /dev/null
+++ b/test/types/parameters.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+struct S {
+};
+
+fn foo(param_bool : bool, param_i32 : i32, param_u32 : u32, param_f32 : f32, param_v2i32 : vec2<i32>, param_v3u32 : vec3<u32>, param_v4f32 : vec4<f32>, param_m2x3 : mat2x3<f32>, param_arr : array<f32, 4>, param_struct : S, param_ptr_f32 : ptr<function, f32>, param_ptr_vec : ptr<function, vec4<f32>>, param_ptr_arr : ptr<function, array<f32, 4>>) {
+}
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/return_types.wgsl b/test/types/return_types.wgsl
new file mode 100644
index 0000000..2dc79af
--- /dev/null
+++ b/test/types/return_types.wgsl
@@ -0,0 +1,17 @@
+struct S {
+};
+
+fn ret_bool() -> bool { return bool(); }
+fn ret_i32() -> i32 { return i32(); }
+fn ret_u32() -> u32 { return u32(); }
+fn ret_f32() -> f32 { return f32(); }
+fn ret_v2i32() -> vec2<i32> { return vec2<i32>(); }
+fn ret_v3u32() -> vec3<u32> { return vec3<u32>(); }
+fn ret_v4f32() -> vec4<f32> { return vec4<f32>(); }
+fn ret_m2x3() -> mat2x3<f32> { return mat2x3<f32>(); }
+fn ret_arr() -> array<f32, 4> { return array<f32, 4>(); }
+fn ret_struct() -> S { return S(); }
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/return_types.wgsl.expected.hlsl b/test/types/return_types.wgsl.expected.hlsl
new file mode 100644
index 0000000..9c9d96e
--- /dev/null
+++ b/test/types/return_types.wgsl.expected.hlsl
@@ -0,0 +1 @@
+SKIP: crbug.com/tint/820: arrays as function return types
diff --git a/test/types/return_types.wgsl.expected.msl b/test/types/return_types.wgsl.expected.msl
new file mode 100644
index 0000000..9c9d96e
--- /dev/null
+++ b/test/types/return_types.wgsl.expected.msl
@@ -0,0 +1 @@
+SKIP: crbug.com/tint/820: arrays as function return types
diff --git a/test/types/return_types.wgsl.expected.spvasm b/test/types/return_types.wgsl.expected.spvasm
new file mode 100644
index 0000000..a13e80b
--- /dev/null
+++ b/test/types/return_types.wgsl.expected.spvasm
@@ -0,0 +1,100 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 57
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %ret_bool "ret_bool"
+               OpName %ret_i32 "ret_i32"
+               OpName %ret_u32 "ret_u32"
+               OpName %ret_f32 "ret_f32"
+               OpName %ret_v2i32 "ret_v2i32"
+               OpName %ret_v3u32 "ret_v3u32"
+               OpName %ret_v4f32 "ret_v4f32"
+               OpName %ret_m2x3 "ret_m2x3"
+               OpName %ret_arr "ret_arr"
+               OpName %S "S"
+               OpName %ret_struct "ret_struct"
+               OpName %main "main"
+               OpDecorate %_arr_float_uint_4 ArrayStride 4
+       %bool = OpTypeBool
+          %1 = OpTypeFunction %bool
+          %5 = OpConstantNull %bool
+        %int = OpTypeInt 32 1
+          %6 = OpTypeFunction %int
+         %10 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+         %11 = OpTypeFunction %uint
+         %15 = OpConstantNull %uint
+      %float = OpTypeFloat 32
+         %16 = OpTypeFunction %float
+         %20 = OpConstantNull %float
+      %v2int = OpTypeVector %int 2
+         %21 = OpTypeFunction %v2int
+         %25 = OpConstantNull %v2int
+     %v3uint = OpTypeVector %uint 3
+         %26 = OpTypeFunction %v3uint
+         %30 = OpConstantNull %v3uint
+    %v4float = OpTypeVector %float 4
+         %31 = OpTypeFunction %v4float
+         %35 = OpConstantNull %v4float
+    %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+         %36 = OpTypeFunction %mat2v3float
+         %41 = OpConstantNull %mat2v3float
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+         %42 = OpTypeFunction %_arr_float_uint_4
+         %47 = OpConstantNull %_arr_float_uint_4
+          %S = OpTypeStruct
+         %48 = OpTypeFunction %S
+         %52 = OpConstantNull %S
+       %void = OpTypeVoid
+         %53 = OpTypeFunction %void
+   %ret_bool = OpFunction %bool None %1
+          %4 = OpLabel
+               OpReturnValue %5
+               OpFunctionEnd
+    %ret_i32 = OpFunction %int None %6
+          %9 = OpLabel
+               OpReturnValue %10
+               OpFunctionEnd
+    %ret_u32 = OpFunction %uint None %11
+         %14 = OpLabel
+               OpReturnValue %15
+               OpFunctionEnd
+    %ret_f32 = OpFunction %float None %16
+         %19 = OpLabel
+               OpReturnValue %20
+               OpFunctionEnd
+  %ret_v2i32 = OpFunction %v2int None %21
+         %24 = OpLabel
+               OpReturnValue %25
+               OpFunctionEnd
+  %ret_v3u32 = OpFunction %v3uint None %26
+         %29 = OpLabel
+               OpReturnValue %30
+               OpFunctionEnd
+  %ret_v4f32 = OpFunction %v4float None %31
+         %34 = OpLabel
+               OpReturnValue %35
+               OpFunctionEnd
+   %ret_m2x3 = OpFunction %mat2v3float None %36
+         %40 = OpLabel
+               OpReturnValue %41
+               OpFunctionEnd
+    %ret_arr = OpFunction %_arr_float_uint_4 None %42
+         %46 = OpLabel
+               OpReturnValue %47
+               OpFunctionEnd
+ %ret_struct = OpFunction %S None %48
+         %51 = OpLabel
+               OpReturnValue %52
+               OpFunctionEnd
+       %main = OpFunction %void None %53
+         %56 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/types/return_types.wgsl.expected.wgsl b/test/types/return_types.wgsl.expected.wgsl
new file mode 100644
index 0000000..3507a99
--- /dev/null
+++ b/test/types/return_types.wgsl.expected.wgsl
@@ -0,0 +1,46 @@
+struct S {
+};
+
+fn ret_bool() -> bool {
+  return bool();
+}
+
+fn ret_i32() -> i32 {
+  return i32();
+}
+
+fn ret_u32() -> u32 {
+  return u32();
+}
+
+fn ret_f32() -> f32 {
+  return f32();
+}
+
+fn ret_v2i32() -> vec2<i32> {
+  return vec2<i32>();
+}
+
+fn ret_v3u32() -> vec3<u32> {
+  return vec3<u32>();
+}
+
+fn ret_v4f32() -> vec4<f32> {
+  return vec4<f32>();
+}
+
+fn ret_m2x3() -> mat2x3<f32> {
+  return mat2x3<f32>();
+}
+
+fn ret_arr() -> array<f32, 4> {
+  return array<f32, 4>();
+}
+
+fn ret_struct() -> S {
+  return S();
+}
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/struct_members.wgsl b/test/types/struct_members.wgsl
new file mode 100644
index 0000000..5850d30
--- /dev/null
+++ b/test/types/struct_members.wgsl
@@ -0,0 +1,20 @@
+struct S_inner {
+};
+
+struct S {
+  member_bool : bool;
+  member_i32 : i32;
+  member_u32 : u32;
+  member_f32 : f32;
+  member_v2i32 : vec2<i32>;
+  member_v3u32 : vec3<u32>;
+  member_v4f32 : vec4<f32>;
+  member_m2x3 : mat2x3<f32>;
+  member_arr : array<f32, 4>;
+  member_struct : S_inner;
+};
+
+[[stage(compute)]]
+fn main() {
+  let s : S = S();
+}
diff --git a/test/types/struct_members.wgsl.expected.hlsl b/test/types/struct_members.wgsl.expected.hlsl
new file mode 100644
index 0000000..f229075
--- /dev/null
+++ b/test/types/struct_members.wgsl.expected.hlsl
@@ -0,0 +1,21 @@
+struct S_inner {
+};
+struct S {
+  bool member_bool;
+  int member_i32;
+  uint member_u32;
+  float member_f32;
+  int2 member_v2i32;
+  uint3 member_v3u32;
+  float4 member_v4f32;
+  float2x3 member_m2x3;
+  float member_arr[4];
+  S_inner member_struct;
+};
+
+[numthreads(1, 1, 1)]
+void main() {
+  const S s = {false, 0, 0u, 0.0f, int2(0, 0), uint3(0u, 0u, 0u), float4(0.0f, 0.0f, 0.0f, 0.0f), float2x3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), {0.0f, 0.0f, 0.0f, 0.0f}, {}};
+  return;
+}
+
diff --git a/test/types/struct_members.wgsl.expected.msl b/test/types/struct_members.wgsl.expected.msl
new file mode 100644
index 0000000..8f7c579
--- /dev/null
+++ b/test/types/struct_members.wgsl.expected.msl
@@ -0,0 +1,23 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S_inner {
+};
+struct S {
+  bool member_bool;
+  int member_i32;
+  uint member_u32;
+  float member_f32;
+  int2 member_v2i32;
+  uint3 member_v3u32;
+  float4 member_v4f32;
+  float2x3 member_m2x3;
+  float member_arr[4];
+  S_inner member_struct;
+};
+
+kernel void tint_symbol() {
+  S const s = {};
+  return;
+}
+
diff --git a/test/types/struct_members.wgsl.expected.spvasm b/test/types/struct_members.wgsl.expected.spvasm
new file mode 100644
index 0000000..b8fcce5
--- /dev/null
+++ b/test/types/struct_members.wgsl.expected.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 19
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %main "main"
+               OpName %S "S"
+               OpMemberName %S 0 "member_bool"
+               OpMemberName %S 1 "member_i32"
+               OpMemberName %S 2 "member_u32"
+               OpMemberName %S 3 "member_f32"
+               OpMemberName %S 4 "member_v2i32"
+               OpMemberName %S 5 "member_v3u32"
+               OpMemberName %S 6 "member_v4f32"
+               OpMemberName %S 7 "member_m2x3"
+               OpMemberName %S 8 "member_arr"
+               OpMemberName %S 9 "member_struct"
+               OpName %S_inner "S_inner"
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 8
+               OpMemberDecorate %S 3 Offset 12
+               OpMemberDecorate %S 4 Offset 16
+               OpMemberDecorate %S 5 Offset 32
+               OpMemberDecorate %S 6 Offset 48
+               OpMemberDecorate %S 7 Offset 64
+               OpMemberDecorate %S 7 ColMajor
+               OpMemberDecorate %S 7 MatrixStride 16
+               OpMemberDecorate %S 8 Offset 96
+               OpDecorate %_arr_float_uint_4 ArrayStride 4
+               OpMemberDecorate %S 9 Offset 112
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+       %bool = OpTypeBool
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+      %v2int = OpTypeVector %int 2
+     %v3uint = OpTypeVector %uint 3
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+    %S_inner = OpTypeStruct
+          %S = OpTypeStruct %bool %int %uint %float %v2int %v3uint %v4float %mat2v3float %_arr_float_uint_4 %S_inner
+         %18 = OpConstantNull %S
+       %main = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/types/struct_members.wgsl.expected.wgsl b/test/types/struct_members.wgsl.expected.wgsl
new file mode 100644
index 0000000..5850d30
--- /dev/null
+++ b/test/types/struct_members.wgsl.expected.wgsl
@@ -0,0 +1,20 @@
+struct S_inner {
+};
+
+struct S {
+  member_bool : bool;
+  member_i32 : i32;
+  member_u32 : u32;
+  member_f32 : f32;
+  member_v2i32 : vec2<i32>;
+  member_v3u32 : vec3<u32>;
+  member_v4f32 : vec4<f32>;
+  member_m2x3 : mat2x3<f32>;
+  member_arr : array<f32, 4>;
+  member_struct : S_inner;
+};
+
+[[stage(compute)]]
+fn main() {
+  let s : S = S();
+}