tint/writer: Handle and emit 'const' variables

Bug: tint:1580
Change-Id: Ib3a5ff5c567e19eca1ba8fb3c2f7e83dee68e2a0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94686
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index d2cb602..4a9f5c6 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -1772,12 +1772,7 @@
             // TODO(crbug.com/tint/1580): Once 'const' is implemented, 'let' will no longer resolve
             // to a shader-creation time constant value, and this can be removed.
             if (auto constant = sem->ConstantValue()) {
-                // We do not want to inline array constants, as this will undo the work of
-                // PromoteInitializersToLet, which ensures that arrays are declarated in 'let's
-                // before their usage.
-                if (!constant.Type()->Is<sem::Array>()) {
-                    return EmitConstant(out, constant);
-                }
+                return EmitConstant(out, constant);
             }
         }
     }
@@ -1938,6 +1933,9 @@
         },
         [&](const ast::Let* let) { return EmitProgramConstVariable(let); },
         [&](const ast::Override* override) { return EmitOverride(override); },
+        [&](const ast::Const*) {
+            return true;  // Constants are embedded at their use
+        },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_)
                 << "unhandled global variable type " << global->TypeInfo().name;
@@ -2252,10 +2250,7 @@
             ScopedParen sp(out);
 
             if (constant.AllEqual(start, end)) {
-                if (!EmitConstantRange(out, constant, v->type(), start, start + 1)) {
-                    return false;
-                }
-                return true;
+                return EmitConstantRange(out, constant, v->type(), start, start + 1);
             }
 
             for (size_t i = start; i < end; i++) {
@@ -2287,6 +2282,28 @@
             }
             return true;
         },
+        [&](const sem::Array* a) {
+            if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                return false;
+            }
+
+            ScopedParen sp(out);
+
+            auto* el_ty = a->ElemType();
+
+            uint32_t step = 0;
+            sem::Type::DeepestElementOf(el_ty, &step);
+            for (size_t i = start; i < end; i += step) {
+                if (i > start) {
+                    out << ", ";
+                }
+                if (!EmitConstantRange(out, constant, el_ty, i, i + step)) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
@@ -2655,6 +2672,9 @@
             v->variable,  //
             [&](const ast::Var* var) { return EmitVar(var); },
             [&](const ast::Let* let) { return EmitLet(let); },
+            [&](const ast::Const*) {
+                return true;  // Constants are embedded at their use
+            },
             [&](Default) {  //
                 TINT_ICE(Writer, diagnostics_)
                     << "unknown variable type: " << v->variable->TypeInfo().name;
diff --git a/src/tint/writer/glsl/generator_impl_constructor_test.cc b/src/tint/writer/glsl/generator_impl_constructor_test.cc
index e70ecaf..9a937e8 100644
--- a/src/tint/writer/glsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_constructor_test.cc
@@ -189,8 +189,7 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f),"
-                                        " vec3(0.0f, 0.0f, 0.0f))"));
+    EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(0.0f), vec3(0.0f), vec3(0.0f))"));
 }
 
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Struct) {
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index e43ad53..b283302 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -805,7 +805,7 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Const) {
+TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Let) {
     GlobalLet("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalLet("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalLet("depth", ty.i32(), Construct(ty.i32(), 4_i));
@@ -830,6 +830,28 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Const) {
+    GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
+    GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
+    GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
+    Func("main", {}, ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize("width", "height", "depth"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+layout(local_size_x = 2, local_size_y = 3, local_size_z = 4) in;
+void main() {
+  return;
+}
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_Compute_WithWorkgroup_OverridableConst) {
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
diff --git a/src/tint/writer/glsl/generator_impl_module_constant_test.cc b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
index a97d4cd..b483511 100644
--- a/src/tint/writer/glsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
@@ -22,7 +22,7 @@
 
 using GlslGeneratorImplTest_ModuleConstant = TestHelper;
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_ModuleConstant) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalLet) {
     auto* var = Let("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
     WrapInFunction(Decl(var));
 
@@ -32,7 +32,216 @@
     EXPECT_EQ(gen.result(), "const float pos[3] = float[3](1.0f, 2.0f, 3.0f);\n");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  uint l = 1u;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  ivec3 l = ivec3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[3] = float[3](1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  bvec2 l[3] = bvec2[3](bvec2(true, false), bvec2(false, true), bvec2(true));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -48,7 +257,7 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override_NoConstructor) {
     auto* var = Override("pos", ty.f32(), nullptr,
                          ast::AttributeList{
                              Id(23),
@@ -64,7 +273,7 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoId) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override_NoId) {
     auto* a = Override("a", ty.f32(), Expr(3_f),
                        ast::AttributeList{
                            Id(0),
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index c3a02b4..daa664a 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -16,6 +16,8 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/glsl/test_helper.h"
 
+using namespace tint::number_suffixes;  // NOLINT
+
 namespace tint::writer::glsl {
 namespace {
 
@@ -36,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -49,6 +51,228 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  uint l = 1u;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  ivec3 l = ivec3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[3] = float[3](1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  bvec2 l[3] = bvec2[3](bvec2(true, false), bvec2(false, true), bvec2(true));
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>());
 
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 8139e6b..bd58e30 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -2617,12 +2617,7 @@
             // TODO(crbug.com/tint/1580): Once 'const' is implemented, 'let' will no longer resolve
             // to a shader-creation time constant value, and this can be removed.
             if (auto constant = sem->ConstantValue()) {
-                // We do not want to inline array constants, as this will undo the work of
-                // PromoteInitializersToLet, which ensures that arrays are declarated in 'let's
-                // before their usage.
-                if (!constant.Type()->Is<sem::Array>()) {
-                    return EmitConstant(out, constant);
-                }
+                return EmitConstant(out, constant);
             }
         }
     }
@@ -2856,6 +2851,9 @@
         },
         [&](const ast::Let* let) { return EmitProgramConstVariable(let); },
         [&](const ast::Override* override) { return EmitOverride(override); },
+        [&](const ast::Const*) {
+            return true;  // Constants are embedded at their use
+        },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_)
                 << "unhandled global variable type " << global->TypeInfo().name;
@@ -3174,8 +3172,7 @@
             return true;
         },
         [&](const sem::Matrix* m) {
-            if (!EmitType(out, constant.Type(), ast::StorageClass::kNone, ast::Access::kUndefined,
-                          "")) {
+            if (!EmitType(out, m, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -3193,6 +3190,34 @@
             }
             return true;
         },
+        [&](const sem::Array* a) {
+            if (constant.AllZero(start, end)) {
+                out << "(";
+                if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                    return false;
+                }
+                out << ")0";
+                return true;
+            }
+
+            out << "{";
+            TINT_DEFER(out << "}");
+
+            auto* el_ty = a->ElemType();
+
+            uint32_t step = 0;
+            sem::Type::DeepestElementOf(el_ty, &step);
+            for (size_t i = start; i < end; i += step) {
+                if (i > start) {
+                    out << ", ";
+                }
+                if (!EmitConstantRange(out, constant, el_ty, i, i + step)) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
@@ -3571,6 +3596,9 @@
                 v->variable,  //
                 [&](const ast::Var* var) { return EmitVar(var); },
                 [&](const ast::Let* let) { return EmitLet(let); },
+                [&](const ast::Const*) {
+                    return true;  // Constants are embedded at their use
+                },
                 [&](Default) {  //
                     TINT_ICE(Writer, diagnostics_)
                         << "unknown variable type: " << v->variable->TypeInfo().name;
diff --git a/src/tint/writer/hlsl/generator_impl_assign_test.cc b/src/tint/writer/hlsl/generator_impl_assign_test.cc
index 5e5f031..7982e6b 100644
--- a/src/tint/writer/hlsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_assign_test.cc
@@ -41,7 +41,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.vec3<f32>())),
@@ -63,6 +63,27 @@
 )");
 }
 
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.vec3<f32>())),
+             Decl(Var("rhs", ty.f32())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor("lhs", "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float3 lhs = float3(0.0f, 0.0f, 0.0f);
+  float rhs = 0.0f;
+  lhs[0u] = rhs;
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_DynamicIndex) {
     Func("fn", {}, ty.void_(),
          {
@@ -89,7 +110,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.mat4x2<f32>())),
@@ -111,6 +132,27 @@
 )");
 }
 
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.mat4x2<f32>())),
+             Decl(Var("rhs", ty.vec2<f32>())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor("lhs", "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+  float2 rhs = float2(0.0f, 0.0f);
+  lhs[0u] = rhs;
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_DynamicIndex) {
     Func("fn", {}, ty.void_(),
          {
@@ -142,7 +184,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.mat4x2<f32>())),
@@ -164,6 +206,27 @@
 )");
 }
 
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.mat4x2<f32>())),
+             Decl(Var("rhs", ty.f32())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor(IndexAccessor("lhs", "index"), "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+  float rhs = 0.0f;
+  lhs[0u][0u] = rhs;
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_DynamicIndex) {
     Func("fn", {}, ty.void_(),
          {
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index 17b4ed4..4e8e721 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -705,7 +705,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Const) {
+TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Let) {
     GlobalLet("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalLet("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalLet("depth", ty.i32(), Construct(ty.i32(), 4_i));
@@ -729,6 +729,26 @@
 )");
 }
 
+TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Const) {
+    GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
+    GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
+    GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
+    Func("main", {}, ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize("width", "height", "depth"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"([numthreads(2, 3, 4)]
+void main() {
+  return;
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_Compute_WithWorkgroup_OverridableConst) {
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
diff --git a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
index 19be5e6..f858499 100644
--- a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
@@ -22,7 +22,7 @@
 
 using HlslGeneratorImplTest_ModuleConstant = TestHelper;
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_ModuleConstant) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalLet) {
     auto* var = Let("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
     WrapInFunction(Decl(var));
 
@@ -32,7 +32,180 @@
     EXPECT_EQ(gen.result(), "static const float pos[3] = {1.0f, 2.0f, 3.0f};\n");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const uint l = 1u;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int3 l = int3(1, 2, 3);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l[3] = {1.0f, 2.0f, 3.0f};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -48,7 +221,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override_NoConstructor) {
     auto* var = Override("pos", ty.f32(), nullptr,
                          ast::AttributeList{
                              Id(23),
@@ -64,7 +237,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoId) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override_NoId) {
     auto* a = Override("a", ty.f32(), Expr(3_f),
                        ast::AttributeList{
                            Id(0),
diff --git a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 1109014..1e111cd 100644
--- a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -16,6 +16,8 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/hlsl/test_helper.h"
 
+using namespace tint::number_suffixes;  // NOLINT
+
 namespace tint::writer::hlsl {
 namespace {
 
@@ -36,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -49,6 +51,192 @@
     EXPECT_EQ(gen.result(), "  const float a = 0.0f;\n");
 }
 
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const uint l = 1u;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int3 l = int3(1, 2, 3);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l[3] = {1.0f, 2.0f, 3.0f};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>());
 
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index dd25dcb..a2e333d 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -255,6 +255,9 @@
                 TINT_DEFER(line());
                 return EmitProgramConstVariable(let);
             },
+            [&](const ast::Const*) {
+                return true;  // Constants are embedded at their use
+            },
             [&](const ast::Override* override) {
                 TINT_DEFER(line());
                 return EmitOverride(override);
@@ -1660,6 +1663,34 @@
             }
             return true;
         },
+        [&](const sem::Array* a) {
+            if (!EmitType(out, a, "")) {
+                return false;
+            }
+
+            if (constant.AllZero(start, end)) {
+                out << "{}";
+                return true;
+            }
+
+            out << "{";
+            TINT_DEFER(out << "}");
+
+            auto* el_ty = a->ElemType();
+
+            uint32_t step = 0;
+            sem::Type::DeepestElementOf(el_ty, &step);
+            for (size_t i = start; i < end; i += step) {
+                if (i > start) {
+                    out << ", ";
+                }
+                if (!EmitConstantRange(out, constant, el_ty, i, i + step)) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
@@ -1708,12 +1739,7 @@
             // TODO(crbug.com/tint/1580): Once 'const' is implemented, 'let' will no longer resolve
             // to a shader-creation time constant value, and this can be removed.
             if (auto constant = sem->ConstantValue()) {
-                // We do not want to inline array constants, as this will undo the work of
-                // PromoteInitializersToLet, which ensures that arrays are declarated in 'let's
-                // before their usage.
-                if (!constant.Type()->Is<sem::Array>()) {
-                    return EmitConstant(out, constant);
-                }
+                return EmitConstant(out, constant);
             }
         }
     }
@@ -2356,6 +2382,9 @@
                 v->variable,  //
                 [&](const ast::Var* var) { return EmitVar(var); },
                 [&](const ast::Let* let) { return EmitLet(let); },
+                [&](const ast::Const*) {
+                    return true;  // Constants are embedded at their use
+                },
                 [&](Default) {  //
                     TINT_ICE(Writer, diagnostics_)
                         << "unknown statement type: " << stmt->TypeInfo().name;
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index 3685f40..155f703 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -22,16 +22,264 @@
 
 using MslGeneratorImplTest = TestHelper;
 
-TEST_F(MslGeneratorImplTest, Emit_ModuleConstant) {
+TEST_F(MslGeneratorImplTest, Emit_GlobalLet) {
     auto* var = GlobalLet("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
 
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
-    EXPECT_EQ(gen.result(), "constant tint_array<float, 3> pos = tint_array<float, 3>{1.0f, 2.0f, 3.0f};\n");
+    EXPECT_EQ(gen.result(),
+              "constant tint_array<float, 3> pos = tint_array<float, 3>{1.0f, 2.0f, 3.0f};\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_SpecConstant) {
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  uint const l = 1u;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int3 const l = int3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -43,7 +291,7 @@
     EXPECT_EQ(gen.result(), "constant float pos [[function_constant(23)]];\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_SpecConstant_NoId) {
+TEST_F(MslGeneratorImplTest, Emit_Override_NoId) {
     auto* var_a = Override("a", ty.f32(), nullptr,
                            ast::AttributeList{
                                Id(0),
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index 7a0a379..46becb0 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -38,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -51,6 +51,266 @@
     EXPECT_EQ(gen.result(), "  float const a = 0.0f;\n");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  uint const l = 1u;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int3 const l = int3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>(), ast::StorageClass::kNone);
     auto* stmt = Decl(var);
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index ab8952e..28f8727 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -693,6 +693,12 @@
 }
 
 bool Builder::GenerateFunctionVariable(const ast::Variable* v) {
+    if (v->Is<ast::Const>()) {
+        // Constants are generated at their use. This is required as the 'const' declaration may be
+        // abstract-numeric, which has no SPIR-V type.
+        return true;
+    }
+
     uint32_t init_id = 0;
     if (v->constructor) {
         init_id = GenerateExpressionWithLoadIfNeeded(v->constructor);
@@ -703,9 +709,9 @@
 
     auto* sem = builder_.Sem().Get(v);
 
-    if (auto* let = v->As<ast::Let>()) {
-        if (!let->constructor) {
-            error_ = "missing constructor for constant";
+    if (v->Is<ast::Let>()) {
+        if (!v->constructor) {
+            error_ = "missing constructor for let";
             return false;
         }
         RegisterVariable(sem, init_id);
@@ -748,6 +754,12 @@
 }
 
 bool Builder::GenerateGlobalVariable(const ast::Variable* v) {
+    if (v->Is<ast::Const>()) {
+        // Constants are generated at their use. This is required as the 'const' declaration may be
+        // abstract-numeric, which has no SPIR-V type.
+        return true;
+    }
+
     auto* sem = builder_.Sem().Get(v);
     auto* type = sem->Type()->UnwrapRef();
 
@@ -1280,8 +1292,16 @@
 
 uint32_t Builder::GenerateConstructorExpression(const ast::Variable* var,
                                                 const ast::Expression* expr) {
-    if (auto* literal = expr->As<ast::LiteralExpression>()) {
-        return GenerateLiteralIfNeeded(var, literal);
+    if (Is<ast::Override>(var)) {
+        if (auto* literal = expr->As<ast::LiteralExpression>()) {
+            return GenerateLiteralIfNeeded(var, literal);
+        }
+    } else {
+        if (auto* sem = builder_.Sem().Get(expr)) {
+            if (auto constant = sem->ConstantValue()) {
+                return GenerateConstantIfNeeded(constant);
+            }
+        }
     }
     if (auto* call = builder_.Sem().Get<sem::Call>(expr)) {
         if (call->Target()->IsAnyOf<sem::TypeConstructor, sem::TypeConversion>()) {
diff --git a/src/tint/writer/spirv/builder_accessor_expression_test.cc b/src/tint/writer/spirv/builder_accessor_expression_test.cc
index ebb3f96..7c8236b 100644
--- a/src/tint/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_accessor_expression_test.cc
@@ -22,7 +22,7 @@
 
 using BuilderTest = TestHelper;
 
-TEST_F(BuilderTest, Const_IndexAccessor_Vector) {
+TEST_F(BuilderTest, Let_IndexAccessor_Vector) {
     // let ary = vec3<i32>(1, 2, 3);
     // var x = ary[1i];
 
@@ -54,6 +54,35 @@
     Validate(b);
 }
 
+TEST_F(BuilderTest, Const_IndexAccessor_Vector) {
+    // const ary = vec3<i32>(1, 2, 3);
+    // var x = ary[1i];
+
+    auto* ary = Const("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
+    auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeInt 32 1
+%6 = OpConstant %5 2
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, Runtime_IndexAccessor_Vector) {
     // var ary : vec3<u32>;
     // var x = ary[1i];
@@ -244,7 +273,7 @@
     Validate(b);
 }
 
-TEST_F(BuilderTest, Const_IndexAccessor_Array_MultiLevel) {
+TEST_F(BuilderTest, Let_IndexAccessor_Array_MultiLevel) {
     // let ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
     // var x = ary[1i][2i];
 
@@ -287,6 +316,37 @@
     Validate(b);
 }
 
+TEST_F(BuilderTest, Const_IndexAccessor_Array_MultiLevel) {
+    // const ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
+    // var x = ary[1i][2i];
+
+    auto* ary =
+        Const("ary", nullptr,
+              array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 6
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, Runtime_IndexAccessor_Array_MultiLevel) {
     // var ary : array<vec3<f32>, 4u>;
     // var x = ary[1i][2i];
@@ -510,7 +570,7 @@
     Validate(b);
 }
 
-TEST_F(BuilderTest, Const_IndexAccessor_Nested_Array_f32) {
+TEST_F(BuilderTest, Let_IndexAccessor_Nested_Array_f32) {
     // let pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
     //   array<f32, 2>(0.0, 0.5),
     //   array<f32, 2>(-0.5, -0.5),
@@ -553,6 +613,40 @@
     Validate(b);
 }
 
+TEST_F(BuilderTest, Const_IndexAccessor_Nested_Array_f32) {
+    // const pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
+    //   array<f32, 2>(0.0, 0.5),
+    //   array<f32, 2>(-0.5, -0.5),
+    //   array<f32, 2>(0.5, -0.5));
+    // var x = pos[1u][0u];
+
+    auto* pos = Const("pos", ty.array(ty.vec2<f32>(), 3_u),
+                      Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
+                                vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 0_u));
+    WrapInFunction(pos, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 -0.5
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, Runtime_IndexAccessor_Nested_Array_f32) {
     // var pos : array<array<f32, 2>, 3u>;
     // var x = pos[1u][2u];
@@ -636,7 +730,7 @@
     Validate(b);
 }
 
-TEST_F(BuilderTest, Const_IndexAccessor_Matrix) {
+TEST_F(BuilderTest, Let_IndexAccessor_Matrix) {
     // let a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
     // var x = a[1i]
 
@@ -674,6 +768,40 @@
     Validate(b);
 }
 
+TEST_F(BuilderTest, Const_IndexAccessor_Matrix) {
+    // const a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
+    // var x = a[1i]
+
+    auto* a = Const("a", ty.mat2x2<f32>(),
+                    Construct(ty.mat2x2<f32>(), Construct(ty.vec2<f32>(), 1_f, 2_f),
+                              Construct(ty.vec2<f32>(), 3_f, 4_f)));
+    auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
+    WrapInFunction(a, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 3
+%8 = OpConstant %6 4
+%9 = OpConstantComposite %5 %7 %8
+%11 = OpTypePointer Function %5
+%12 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%10 = OpVariable %11 Function %12
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %10 %9
+OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, Runtime_IndexAccessor_Matrix) {
     // var a : mat2x2<f32>;
     // var x = a[1i]
diff --git a/src/tint/writer/spirv/builder_constructor_expression_test.cc b/src/tint/writer/spirv/builder_constructor_expression_test.cc
index 9f08347..c1b95df 100644
--- a/src/tint/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_constructor_expression_test.cc
@@ -1073,7 +1073,7 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_F32_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_F32_With_F32) {
     auto* ctor = Construct<f32>(2_f);
     GlobalLet("g", ty.f32(), ctor);
 
@@ -1088,7 +1088,45 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_U32_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_F32_With_F32) {
+    auto* ctor = Construct<f32>(2_f);
+    GlobalConst("g", ty.f32(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 2
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_F32_With_F32) {
+    auto* ctor = Construct<f32>(2_f);
+    GlobalVar("g", ty.f32(), ast::StorageClass::kPrivate, ctor);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 2
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_U32_With_F32) {
     auto* ctor = Construct<u32>(1.5_f);
     GlobalLet("g", ty.u32(), ctor);
 
@@ -1103,9 +1141,47 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec2_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_U32_With_F32) {
+    auto* ctor = Construct<u32>(1.5_f);
+    GlobalConst("g", ty.u32(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 1
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_U32_With_F32) {
+    auto* ctor = Construct<u32>(1.5_f);
+    GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstant %1 1
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec2_With_F32) {
     auto* cast = vec2<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec2<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec2<f32>(), cast);
 
     spirv::Builder& b = Build();
 
@@ -1119,7 +1195,46 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec2_With_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_F32) {
+    auto* cast = vec2<f32>(2_f);
+    GlobalConst("g", ty.vec2<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_F32) {
+    auto* cast = vec2<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec2_With_Vec2) {
     auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
     GlobalLet("a", ty.vec2<f32>(), cast);
 
@@ -1137,7 +1252,50 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_Vec2) {
+    auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
+    GlobalConst("g", ty.vec2<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_Vec2) {
+    auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
+    GlobalVar("a", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 2
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec3_With_Vec3) {
     auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
     GlobalLet("a", ty.vec3<f32>(), cast);
 
@@ -1155,7 +1313,50 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec3) {
+    auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec3) {
+    auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
+    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_Vec4) {
     auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
     GlobalLet("a", ty.vec4<f32>(), cast);
 
@@ -1173,9 +1374,51 @@
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec4) {
+    auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec4) {
+    auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
+    GlobalVar("a", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+)");
+
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec3_With_F32) {
     auto* cast = vec3<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec3<f32>(), cast);
 
     spirv::Builder& b = Build();
 
@@ -1189,55 +1432,158 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_F32_Vec2) {
-    auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32) {
+    auto* cast = vec3<f32>(2_f);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32) {
+    auto* cast = vec3<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %6 %9
+%4 = OpConstantComposite %1 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_Vec2_F32) {
-    auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec3_With_F32_Vec2) {
+    auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
+    auto* g = GlobalLet("g", ty.vec3<f32>(), cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %6 %9 %4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32_Vec2) {
+    auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32_Vec2) {
+    auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec3_With_Vec2_F32) {
+    auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
+    auto* g = GlobalLet("g", ty.vec3<f32>(), cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec2_F32) {
+    auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec2_F32) {
+    auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_F32) {
     auto* cast = vec4<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
 
     spirv::Builder& b = Build();
 
@@ -1251,147 +1597,372 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_F32_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32) {
+    auto* cast = vec4<f32>(2_f);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32) {
+    auto* cast = vec4<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_F32_F32_Vec2) {
     auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %3 %6 %9
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_Vec2_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_F32_Vec2) {
+    auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_F32_Vec2) {
+    auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_F32_Vec2_F32) {
     auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %6 %9 %3
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec2_F32_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec2_F32) {
+    auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec2_F32) {
+    auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_Vec2_F32_F32) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %6 %9 %4 %4
-)");
-}
-
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec2_Vec2) {
-    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantOp %2 CompositeExtract %5 8
-%12 = OpSpecConstantOp %2 CompositeExtract %5 10
-%13 = OpSpecConstantComposite %1 %6 %9 %11 %12
-)");
-}
-
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_Vec3) {
-    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 3
-%5 = OpConstantComposite %4 %3 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%12 = OpConstant %7 2
-%11 = OpSpecConstantOp %2 CompositeExtract %5 12
-%13 = OpSpecConstantComposite %1 %3 %6 %9 %11
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec3_F32) {
-    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_F32_F32) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_F32_F32) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 3
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%12 = OpConstant %7 2
-%11 = OpSpecConstantOp %2 CompositeExtract %5 12
-%13 = OpSpecConstantComposite %1 %6 %9 %11 %4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_Vec2_Vec2) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_Vec2) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_Vec2) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_F32_Vec3) {
+    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec3) {
+    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec3) {
+    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalLet_Vec4_With_Vec3_F32) {
+    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
+    auto* g = GlobalLet("g", ty.vec4<f32>(), cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec3_F32) {
+    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec3_F32) {
+    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
diff --git a/src/tint/writer/spirv/builder_function_attribute_test.cc b/src/tint/writer/spirv/builder_function_attribute_test.cc
index 0bfca02..7bec69e 100644
--- a/src/tint/writer/spirv/builder_function_attribute_test.cc
+++ b/src/tint/writer/spirv/builder_function_attribute_test.cc
@@ -131,7 +131,7 @@
 )");
 }
 
-TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Const) {
+TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Let) {
     GlobalLet("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalLet("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalLet("depth", ty.i32(), Construct(ty.i32(), 4_i));
@@ -149,6 +149,24 @@
 )");
 }
 
+TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Const) {
+    GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
+    GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
+    GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          WorkgroupSize("width", "height", "depth"),
+                          Stage(ast::PipelineStage::kCompute),
+                      });
+
+    spirv::Builder& b = Build();
+
+    ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
+    EXPECT_EQ(DumpInstructions(b.execution_modes()),
+              R"(OpExecutionMode %3 LocalSize 2 3 4
+)");
+}
+
 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_OverridableConst) {
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     Override("height", ty.i32(), Construct(ty.i32(), 3_i), {Id(8u)});
@@ -179,7 +197,7 @@
 )");
 }
 
-TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
+TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndLet) {
     Override("height", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     GlobalLet("depth", ty.i32(), Construct(ty.i32(), 3_i));
     auto* func = Func("main", {}, ty.void_(), {},
@@ -206,6 +224,33 @@
 )");
 }
 
+TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
+    Override("height", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
+    GlobalConst("depth", ty.i32(), Construct(ty.i32(), 3_i));
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          WorkgroupSize(4_i, "height", "depth"),
+                          Stage(ast::PipelineStage::kCompute),
+                      });
+
+    spirv::Builder& b = Build();
+
+    ASSERT_TRUE(b.GenerateExecutionModes(func, 3)) << b.error();
+    EXPECT_EQ(DumpInstructions(b.execution_modes()), "");
+    EXPECT_EQ(DumpInstructions(b.types()),
+              R"(%2 = OpTypeInt 32 0
+%1 = OpTypeVector %2 3
+%4 = OpConstant %2 4
+%5 = OpSpecConstant %2 2
+%6 = OpConstant %2 3
+%3 = OpSpecConstantComposite %1 %4 %5 %6
+)");
+    EXPECT_EQ(DumpInstructions(b.annots()),
+              R"(OpDecorate %5 SpecId 7
+OpDecorate %3 BuiltIn WorkgroupSize
+)");
+}
+
 TEST_F(BuilderTest, Decoration_ExecutionMode_MultipleFragment) {
     auto* func1 = Func("main1", {}, ty.void_(), {},
                        {
diff --git a/src/tint/writer/spirv/builder_function_variable_test.cc b/src/tint/writer/spirv/builder_function_variable_test.cc
index da72233..4a7026a 100644
--- a/src/tint/writer/spirv/builder_function_variable_test.cc
+++ b/src/tint/writer/spirv/builder_function_variable_test.cc
@@ -138,7 +138,7 @@
 )");
 }
 
-TEST_F(BuilderTest, FunctionVar_ConstWithVarInitializer) {
+TEST_F(BuilderTest, FunctionVar_LetWithVarInitializer) {
     // var v : f32 = 1.0;
     // let v2 : f32 = v; // Should generate the load
 
@@ -173,7 +173,38 @@
 )");
 }
 
-TEST_F(BuilderTest, FunctionVar_Const) {
+TEST_F(BuilderTest, FunctionVar_ConstWithVarInitializer) {
+    // const v : f32 = 1.0;
+    // let v2 : f32 = v;
+
+    auto* v = Const("v", ty.f32(), Expr(1_f));
+
+    auto* v2 = Var("v2", ty.f32(), ast::StorageClass::kNone, Expr("v"));
+    WrapInFunction(v, v2);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateFunctionVariable(v)) << b.error();
+    EXPECT_TRUE(b.GenerateFunctionVariable(v2)) << b.error();
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "v2"
+)");
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
+              R"(%3 = OpVariable %4 Function %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+)");
+}
+
+TEST_F(BuilderTest, FunctionVar_Let) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
     auto* v = Let("var", ty.vec3<f32>(), init);
@@ -193,5 +224,20 @@
 )");
 }
 
+TEST_F(BuilderTest, FunctionVar_Const) {
+    auto* init = vec3<f32>(1_f, 1_f, 3_f);
+
+    auto* v = Const("var", ty.vec3<f32>(), init);
+
+    WrapInFunction(v);
+
+    spirv::Builder& b = Build();
+
+    EXPECT_TRUE(b.GenerateFunctionVariable(v)) << b.error();
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), "");  // Not a mistake - 'const' is inlined
+}
+
 }  // namespace
 }  // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index ca37315..8f0b481 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -61,7 +61,7 @@
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_Const) {
+TEST_F(BuilderTest, GlobalLet) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
     auto* v = GlobalLet("l", ty.vec3<f32>(), init);
@@ -81,7 +81,32 @@
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_Complex_Constructor) {
+TEST_F(BuilderTest, GlobalConst) {
+    // const c = 42;
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, Expr(42_a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 42
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalLet_Vec_Constructor) {
     auto* init = vec3<f32>(1_f, 2_f, 3_f);
 
     auto* v = GlobalLet("l", ty.vec3<f32>(), init);
@@ -100,7 +125,94 @@
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_Complex_ConstructorNestedVector) {
+TEST_F(BuilderTest, GlobalConst_Vec_Constructor) {
+    // const c = vec3<f32>(1f, 2f, 3f);
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalConst_Vec_AInt_Constructor) {
+    // const c = vec3(1, 2, 3);
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalConst_Vec_AFloat_Constructor) {
+    // const c = vec3(1.0, 2.0, 3.0);
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalLet_Nested_Vec_Constructor) {
     auto* init = vec3<f32>(vec2<f32>(1_f, 2_f), 3_f);
 
     auto* v = GlobalLet("l", ty.vec3<f32>(), init);
@@ -119,6 +231,35 @@
 )");
 }
 
+TEST_F(BuilderTest, GlobalConst_Nested_Vec_Constructor) {
+    // const c = vec3<f32>(vec2<f32>(1f, 2f), 3f));
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, vec3<f32>(vec2<f32>(1_f, 2_f), 3_f));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
 TEST_F(BuilderTest, GlobalVar_WithBindingAndGroup) {
     auto* v =
         GlobalVar("var", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
diff --git a/src/tint/writer/spirv/builder_ident_expression_test.cc b/src/tint/writer/spirv/builder_ident_expression_test.cc
index 37d0fd1..0f24146 100644
--- a/src/tint/writer/spirv/builder_ident_expression_test.cc
+++ b/src/tint/writer/spirv/builder_ident_expression_test.cc
@@ -25,9 +25,9 @@
 TEST_F(BuilderTest, IdentifierExpression_GlobalConst) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
-    auto* v = GlobalLet("var", ty.vec3<f32>(), init);
+    auto* v = GlobalConst("c", ty.vec3<f32>(), init);
 
-    auto* expr = Expr("var");
+    auto* expr = Expr("c");
     WrapInFunction(expr);
 
     spirv::Builder& b = Build();
@@ -35,14 +35,9 @@
     EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
     ASSERT_FALSE(b.has_error()) << b.error();
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 3
-%3 = OpConstant %2 1
-%4 = OpConstant %2 3
-%5 = OpConstantComposite %1 %3 %3 %4
-)");
+    EXPECT_EQ(DumpInstructions(b.types()), R"()");
 
-    EXPECT_EQ(b.GenerateIdentifierExpression(expr), 5u);
+    EXPECT_EQ(b.GenerateIdentifierExpression(expr), 0u);
 }
 
 TEST_F(BuilderTest, IdentifierExpression_GlobalVar) {
@@ -115,7 +110,6 @@
 
 TEST_F(BuilderTest, IdentifierExpression_Load) {
     auto* var = GlobalVar("var", ty.i32(), ast::StorageClass::kPrivate);
-
     auto* expr = Add("var", "var");
     WrapInFunction(expr);
 
@@ -138,15 +132,14 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_NoLoadConst) {
-    auto* var = GlobalLet("var", ty.i32(), Expr(2_i));
-
-    auto* expr = Add("var", "var");
-    WrapInFunction(expr);
+    auto* let = Let("let", ty.i32(), Expr(2_i));
+    auto* expr = Add("let", "let");
+    WrapInFunction(let, expr);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    ASSERT_TRUE(b.GenerateFunctionVariable(let)) << b.error();
 
     EXPECT_EQ(b.GenerateBinaryExpression(expr->As<ast::BinaryExpression>()), 3u) << b.error();
     EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
diff --git a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index a68932c..aeacd30 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -50,5 +50,189 @@
     EXPECT_EQ(gen.result(), "  var a = 123i;\n");
 }
 
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1.0;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1i;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1u;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1.0f;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3(1, 2, 3);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3(1.0, 2.0, 3.0);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3<f32>(1.0f, 2.0f, 3.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = mat2x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = mat2x3<f32>(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = array<f32, 3u>(1.0f, 2.0f, 3.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = array<vec2<bool>, 3u>(vec2<bool>(true, false), vec2<bool>(false, true), vec2<bool>(true, true));
+  let l = C;
+}
+)");
+}
 }  // namespace
 }  // namespace tint::writer::wgsl