Remove cast operator.

This CL removes the cast operator and converts the tests over to using
type constructors.

Bug: tint:241
Change-Id: I2526acb61f5624b2e1c068612a2ddcc748c92aed
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28860
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index a1ed133..ea3b979 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -243,8 +243,6 @@
     "src/ast/call_statement.h",
     "src/ast/case_statement.cc",
     "src/ast/case_statement.h",
-    "src/ast/cast_expression.cc",
-    "src/ast/cast_expression.h",
     "src/ast/constructor_expression.cc",
     "src/ast/constructor_expression.h",
     "src/ast/continue_statement.cc",
@@ -697,7 +695,6 @@
     "src/ast/call_expression_test.cc",
     "src/ast/call_statement_test.cc",
     "src/ast/case_statement_test.cc",
-    "src/ast/cast_expression_test.cc",
     "src/ast/continue_statement_test.cc",
     "src/ast/decorated_variable_test.cc",
     "src/ast/discard_statement_test.cc",
@@ -831,7 +828,6 @@
     "src/writer/spirv/builder_bitcast_expression_test.cc",
     "src/writer/spirv/builder_block_test.cc",
     "src/writer/spirv/builder_call_test.cc",
-    "src/writer/spirv/builder_cast_expression_test.cc",
     "src/writer/spirv/builder_constructor_expression_test.cc",
     "src/writer/spirv/builder_discard_test.cc",
     "src/writer/spirv/builder_format_conversion_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7c16a03..b2d4885 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -64,8 +64,6 @@
   ast/call_statement.h
   ast/case_statement.cc
   ast/case_statement.h
-  ast/cast_expression.cc
-  ast/cast_expression.h
   ast/constructor_expression.cc
   ast/constructor_expression.h
   ast/continue_statement.cc
@@ -306,7 +304,6 @@
   ast/call_expression_test.cc
   ast/call_statement_test.cc
   ast/case_statement_test.cc
-  ast/cast_expression_test.cc
   ast/continue_statement_test.cc
   ast/discard_statement_test.cc
   ast/decorated_variable_test.cc
@@ -489,7 +486,6 @@
     writer/spirv/builder_bitcast_expression_test.cc
     writer/spirv/builder_block_test.cc
     writer/spirv/builder_call_test.cc
-    writer/spirv/builder_cast_expression_test.cc
     writer/spirv/builder_constructor_expression_test.cc
     writer/spirv/builder_discard_test.cc
     writer/spirv/builder_format_conversion_test.cc
diff --git a/src/ast/cast_expression.cc b/src/ast/cast_expression.cc
deleted file mode 100644
index 327c37d..0000000
--- a/src/ast/cast_expression.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/cast_expression.h"
-
-namespace tint {
-namespace ast {
-
-CastExpression::CastExpression() : Expression() {}
-
-CastExpression::CastExpression(type::Type* type,
-                               std::unique_ptr<Expression> expr)
-    : Expression(), type_(type), expr_(std::move(expr)) {}
-
-CastExpression::CastExpression(const Source& source,
-                               type::Type* type,
-                               std::unique_ptr<Expression> expr)
-    : Expression(source), type_(type), expr_(std::move(expr)) {}
-
-CastExpression::CastExpression(CastExpression&&) = default;
-
-CastExpression::~CastExpression() = default;
-
-bool CastExpression::IsCast() const {
-  return true;
-}
-
-bool CastExpression::IsValid() const {
-  if (expr_ == nullptr || !expr_->IsValid())
-    return false;
-  return type_ != nullptr;
-}
-
-void CastExpression::to_str(std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-  out << "Cast<" << type_->type_name() << ">(" << std::endl;
-  expr_->to_str(out, indent + 2);
-  make_indent(out, indent);
-  out << ")" << std::endl;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/cast_expression.h b/src/ast/cast_expression.h
deleted file mode 100644
index b1db7fe..0000000
--- a/src/ast/cast_expression.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_CAST_EXPRESSION_H_
-#define SRC_AST_CAST_EXPRESSION_H_
-
-#include <memory>
-#include <utility>
-
-#include "src/ast/expression.h"
-#include "src/ast/literal.h"
-#include "src/ast/type/type.h"
-
-namespace tint {
-namespace ast {
-
-/// A cast expression
-class CastExpression : public Expression {
- public:
-  /// Constructor
-  CastExpression();
-  /// Constructor
-  /// @param type the type
-  /// @param expr the expr
-  CastExpression(type::Type* type, std::unique_ptr<Expression> expr);
-  /// Constructor
-  /// @param source the cast expression source
-  /// @param type the type
-  /// @param expr the expr
-  CastExpression(const Source& source,
-                 type::Type* type,
-                 std::unique_ptr<Expression> expr);
-  /// Move constructor
-  CastExpression(CastExpression&&);
-  ~CastExpression() override;
-
-  /// Sets the type
-  /// @param type the type
-  void set_type(type::Type* type) { type_ = std::move(type); }
-  /// @returns the left side expression
-  type::Type* type() const { return type_; }
-
-  /// Sets the expr
-  /// @param expr the expression
-  void set_expr(std::unique_ptr<Expression> expr) { expr_ = std::move(expr); }
-  /// @returns the expression
-  Expression* expr() const { return expr_.get(); }
-
-  /// @returns true if this is a cast expression
-  bool IsCast() const override;
-
-  /// @returns true if the node is valid
-  bool IsValid() const override;
-
-  /// Writes a representation of the node to the output stream
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(std::ostream& out, size_t indent) const override;
-
- private:
-  CastExpression(const CastExpression&) = delete;
-
-  type::Type* type_ = nullptr;
-  std::unique_ptr<Expression> expr_;
-};
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_CAST_EXPRESSION_H_
diff --git a/src/ast/cast_expression_test.cc b/src/ast/cast_expression_test.cc
deleted file mode 100644
index 4fe7566..0000000
--- a/src/ast/cast_expression_test.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/cast_expression.h"
-
-#include "gtest/gtest.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/type/f32_type.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using CastExpressionTest = testing::Test;
-
-TEST_F(CastExpressionTest, Creation) {
-  type::F32Type f32;
-  auto expr = std::make_unique<IdentifierExpression>("expr");
-  auto* expr_ptr = expr.get();
-
-  CastExpression c(&f32, std::move(expr));
-  EXPECT_EQ(c.type(), &f32);
-  EXPECT_EQ(c.expr(), expr_ptr);
-}
-
-TEST_F(CastExpressionTest, Creation_withSource) {
-  type::F32Type f32;
-  auto expr = std::make_unique<IdentifierExpression>("expr");
-
-  CastExpression c(Source{20, 2}, &f32, std::move(expr));
-  auto src = c.source();
-  EXPECT_EQ(src.line, 20u);
-  EXPECT_EQ(src.column, 2u);
-}
-
-TEST_F(CastExpressionTest, IsCast) {
-  CastExpression c;
-  EXPECT_TRUE(c.IsCast());
-}
-
-TEST_F(CastExpressionTest, IsValid) {
-  type::F32Type f32;
-  auto expr = std::make_unique<IdentifierExpression>("expr");
-
-  CastExpression c(&f32, std::move(expr));
-  EXPECT_TRUE(c.IsValid());
-}
-
-TEST_F(CastExpressionTest, IsValid_MissingType) {
-  auto expr = std::make_unique<IdentifierExpression>("expr");
-
-  CastExpression c;
-  c.set_expr(std::move(expr));
-  EXPECT_FALSE(c.IsValid());
-}
-
-TEST_F(CastExpressionTest, IsValid_MissingExpression) {
-  type::F32Type f32;
-
-  CastExpression c;
-  c.set_type(&f32);
-  EXPECT_FALSE(c.IsValid());
-}
-
-TEST_F(CastExpressionTest, IsValid_InvalidExpression) {
-  type::F32Type f32;
-  auto expr = std::make_unique<IdentifierExpression>("");
-  CastExpression c(&f32, std::move(expr));
-  EXPECT_FALSE(c.IsValid());
-}
-
-TEST_F(CastExpressionTest, ToStr) {
-  type::F32Type f32;
-  auto expr = std::make_unique<IdentifierExpression>("expr");
-
-  CastExpression c(Source{20, 2}, &f32, std::move(expr));
-  std::ostringstream out;
-  c.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  Cast<__f32>(
-    Identifier{expr}
-  )
-)");
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/expression.cc b/src/ast/expression.cc
index 2e1ad97..f58c138 100644
--- a/src/ast/expression.cc
+++ b/src/ast/expression.cc
@@ -20,7 +20,6 @@
 #include "src/ast/binary_expression.h"
 #include "src/ast/bitcast_expression.h"
 #include "src/ast/call_expression.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/constructor_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/member_accessor_expression.h"
@@ -96,11 +95,6 @@
   return static_cast<const CallExpression*>(this);
 }
 
-const CastExpression* Expression::AsCast() const {
-  assert(IsCast());
-  return static_cast<const CastExpression*>(this);
-}
-
 const ConstructorExpression* Expression::AsConstructor() const {
   assert(IsConstructor());
   return static_cast<const ConstructorExpression*>(this);
@@ -141,11 +135,6 @@
   return static_cast<CallExpression*>(this);
 }
 
-CastExpression* Expression::AsCast() {
-  assert(IsCast());
-  return static_cast<CastExpression*>(this);
-}
-
 ConstructorExpression* Expression::AsConstructor() {
   assert(IsConstructor());
   return static_cast<ConstructorExpression*>(this);
diff --git a/src/ast/expression.h b/src/ast/expression.h
index b69e9da..dc8ce58 100644
--- a/src/ast/expression.h
+++ b/src/ast/expression.h
@@ -28,7 +28,6 @@
 class BinaryExpression;
 class BitcastExpression;
 class CallExpression;
-class CastExpression;
 class IdentifierExpression;
 class ConstructorExpression;
 class MemberAccessorExpression;
@@ -70,8 +69,6 @@
   const BitcastExpression* AsBitcast() const;
   /// @returns the expression as a call
   const CallExpression* AsCall() const;
-  /// @returns the expression as a cast
-  const CastExpression* AsCast() const;
   /// @returns the expression as an identifier
   const IdentifierExpression* AsIdentifier() const;
   /// @returns the expression as an constructor
@@ -89,8 +86,6 @@
   BitcastExpression* AsBitcast();
   /// @returns the expression as a call
   CallExpression* AsCall();
-  /// @returns the expression as a cast
-  CastExpression* AsCast();
   /// @returns the expression as an identifier
   IdentifierExpression* AsIdentifier();
   /// @returns the expression as an constructor
diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc
index a163014..7565d73 100644
--- a/src/ast/type/type.cc
+++ b/src/ast/type/type.cc
@@ -109,6 +109,10 @@
   return false;
 }
 
+bool Type::is_scalar() {
+  return is_float_scalar() || is_integer_scalar() || IsBool();
+}
+
 bool Type::is_float_scalar() {
   return IsF32();
 }
diff --git a/src/ast/type/type.h b/src/ast/type/type.h
index e506c4d..5d0dbe7 100644
--- a/src/ast/type/type.h
+++ b/src/ast/type/type.h
@@ -90,6 +90,8 @@
   /// @returns the unwrapped type
   Type* UnwrapAliasPtrAlias();
 
+  /// @returns true if this type is a scalar
+  bool is_scalar();
   /// @returns true if this type is a float scalar
   bool is_float_scalar();
   /// @returns true if this type is a float matrix
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index bc59df1..963a33d 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -36,7 +36,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/discard_statement.h"
 #include "src/ast/else_statement.h"
@@ -3463,8 +3462,11 @@
     return {};
   }
 
-  TypedExpression result(expr_type, std::make_unique<ast::CastExpression>(
-                                        expr_type, std::move(arg_expr.expr)));
+  ast::ExpressionList params;
+  params.push_back(std::move(arg_expr.expr));
+  TypedExpression result(expr_type,
+                         std::make_unique<ast::TypeConstructorExpression>(
+                             expr_type, std::move(params)));
 
   if (requested_type == expr_type) {
     return result;
diff --git a/src/reader/spirv/function_conversion_test.cc b/src/reader/spirv/function_conversion_test.cc
index 37525b9..24a6a7a 100644
--- a/src/reader/spirv/function_conversion_test.cc
+++ b/src/reader/spirv/function_conversion_test.cc
@@ -243,9 +243,10 @@
     none
     __f32
     {
-      Cast<__f32>(
+      TypeConstructor{
+        __f32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -269,11 +270,12 @@
     none
     __f32
     {
-      Cast<__f32>(
+      TypeConstructor{
+        __f32
         Bitcast<__i32>{
           Identifier{x_30}
         }
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -297,9 +299,10 @@
     none
     __vec_2__f32
     {
-      Cast<__vec_2__f32>(
+      TypeConstructor{
+        __vec_2__f32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -323,11 +326,12 @@
     none
     __vec_2__f32
     {
-      Cast<__vec_2__f32>(
+      TypeConstructor{
+        __vec_2__f32
         Bitcast<__vec_2__i32>{
           Identifier{x_30}
         }
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -384,11 +388,12 @@
     none
     __f32
     {
-      Cast<__f32>(
+      TypeConstructor{
+        __f32
         Bitcast<__u32>{
           Identifier{x_30}
         }
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -412,9 +417,10 @@
     none
     __f32
     {
-      Cast<__f32>(
+      TypeConstructor{
+        __f32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -438,11 +444,12 @@
     none
     __vec_2__f32
     {
-      Cast<__vec_2__f32>(
+      TypeConstructor{
+        __vec_2__f32
         Bitcast<__vec_2__u32>{
           Identifier{x_30}
         }
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -466,9 +473,10 @@
     none
     __vec_2__f32
     {
-      Cast<__vec_2__f32>(
+      TypeConstructor{
+        __vec_2__f32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -526,9 +534,10 @@
     none
     __i32
     {
-      Cast<__i32>(
+      TypeConstructor{
+        __i32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -553,9 +562,10 @@
     __u32
     {
       Bitcast<__u32>{
-        Cast<__i32>(
+        TypeConstructor{
+          __i32
           Identifier{x_30}
-        )
+        }
       }
     }
   })"))
@@ -580,9 +590,10 @@
     none
     __vec_2__i32
     {
-      Cast<__vec_2__i32>(
+      TypeConstructor{
+        __vec_2__i32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -607,9 +618,10 @@
     __vec_2__u32
     {
       Bitcast<__vec_2__u32>{
-        Cast<__vec_2__i32>(
+        TypeConstructor{
+          __vec_2__i32
           Identifier{x_30}
-        )
+        }
       }
     }
   })"))
@@ -669,9 +681,10 @@
     __i32
     {
       Bitcast<__i32>{
-        Cast<__u32>(
+        TypeConstructor{
+          __u32
           Identifier{x_30}
-        )
+        }
       }
     }
   })"))
@@ -696,9 +709,10 @@
     none
     __u32
     {
-      Cast<__u32>(
+      TypeConstructor{
+        __u32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
@@ -723,9 +737,10 @@
     __vec_2__i32
     {
       Bitcast<__vec_2__i32>{
-        Cast<__vec_2__u32>(
+        TypeConstructor{
+          __vec_2__u32
           Identifier{x_30}
-        )
+        }
       }
     }
   })"))
@@ -750,9 +765,10 @@
     none
     __vec_2__u32
     {
-      Cast<__vec_2__u32>(
+      TypeConstructor{
+        __vec_2__u32
         Identifier{x_30}
-      )
+      }
     }
   })"))
       << ToString(fe.ast_body());
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 87b678f..42019fa 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -485,8 +485,6 @@
     return {Token::Type::kBuiltin, source, "builtin"};
   if (str == "case")
     return {Token::Type::kCase, source, "case"};
-  if (str == "cast")
-    return {Token::Type::kCast, source, "cast"};
   if (str == "compute")
     return {Token::Type::kCompute, source, "compute"};
   if (str == "const")
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index c3b456e..81e27e4 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -420,7 +420,6 @@
         TokenData{"break", Token::Type::kBreak},
         TokenData{"builtin", Token::Type::kBuiltin},
         TokenData{"case", Token::Type::kCase},
-        TokenData{"cast", Token::Type::kCast},
         TokenData{"compute", Token::Type::kCompute},
         TokenData{"const", Token::Type::kConst},
         TokenData{"continue", Token::Type::kContinue},
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 1616ea8..26044d4 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -26,7 +26,6 @@
 #include "src/ast/builtin_decoration.h"
 #include "src/ast/call_expression.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/discard_statement.h"
@@ -2767,8 +2766,7 @@
 //   | type_decl PAREN_LEFT argument_expression_list* PAREN_RIGHT
 //   | const_literal
 //   | paren_rhs_stmt
-//   | CAST LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
-//   | AS LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+//   | BITCAST LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
 std::unique_ptr<ast::Expression> ParserImpl::primary_expression() {
   auto t = peek();
   auto source = t.source();
@@ -2790,14 +2788,14 @@
     return paren;
   }
 
-  if (t.IsCast() || t.IsBitcast()) {
+  if (t.IsBitcast()) {
     auto src = t;
 
     next();  // Consume the peek
 
     t = next();
     if (!t.IsLessThan()) {
-      set_error(t, "missing < for " + src.to_name() + " expression");
+      set_error(t, "missing < for bitcast expression");
       return nullptr;
     }
 
@@ -2805,13 +2803,13 @@
     if (has_error())
       return nullptr;
     if (type == nullptr) {
-      set_error(peek(), "missing type for " + src.to_name() + " expression");
+      set_error(peek(), "missing type for bitcast expression");
       return nullptr;
     }
 
     t = next();
     if (!t.IsGreaterThan()) {
-      set_error(t, "missing > for " + src.to_name() + " expression");
+      set_error(t, "missing > for bitcast expression");
       return nullptr;
     }
 
@@ -2823,14 +2821,8 @@
       return nullptr;
     }
 
-    if (src.IsCast()) {
-      return std::make_unique<ast::CastExpression>(source, type,
-                                                   std::move(params));
-    } else {
-      return std::make_unique<ast::BitcastExpression>(source, type,
-                                                      std::move(params));
-    }
-
+    return std::make_unique<ast::BitcastExpression>(source, type,
+                                                    std::move(params));
   } else if (t.IsIdentifier()) {
     next();  // Consume the peek
 
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
index bd459e1..49737e4 100644
--- a/src/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -16,7 +16,6 @@
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/bitcast_expression.h"
 #include "src/ast/bool_literal.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/sint_literal.h"
@@ -170,73 +169,19 @@
 TEST_F(ParserImplTest, PrimaryExpression_Cast) {
   auto* f32_type = tm()->Get(std::make_unique<ast::type::F32Type>());
 
-  auto* p = parser("cast<f32>(1)");
+  auto* p = parser("f32(1)");
   auto e = p->primary_expression();
   ASSERT_FALSE(p->has_error()) << p->error();
   ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsCast());
+  ASSERT_TRUE(e->IsConstructor());
+  ASSERT_TRUE(e->AsConstructor()->IsTypeConstructor());
 
-  auto* c = e->AsCast();
+  auto* c = e->AsConstructor()->AsTypeConstructor();
   ASSERT_EQ(c->type(), f32_type);
+  ASSERT_EQ(c->values().size(), 1u);
 
-  ASSERT_TRUE(c->expr()->IsConstructor());
-  ASSERT_TRUE(c->expr()->AsConstructor()->IsScalarConstructor());
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingGreaterThan) {
-  auto* p = parser("cast<f32(1)");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:9: missing > for cast expression");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingType) {
-  auto* p = parser("cast<>(1)");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing type for cast expression");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_InvalidType) {
-  auto* p = parser("cast<invalid>(1)");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: unknown type alias 'invalid'");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingLeftParen) {
-  auto* p = parser("cast<f32>1)");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:10: expected (");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingRightParen) {
-  auto* p = parser("cast<f32>(1");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:12: expected )");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingExpression) {
-  auto* p = parser("cast<f32>()");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: unable to parse expression");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Cast_InvalidExpression) {
-  auto* p = parser("cast<f32>(if (a) {})");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: unable to parse expression");
+  ASSERT_TRUE(c->values()[0]->IsConstructor());
+  ASSERT_TRUE(c->values()[0]->AsConstructor()->IsScalarConstructor());
 }
 
 TEST_F(ParserImplTest, PrimaryExpression_Bitcast) {
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index f836c0c..76d88e9 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -121,8 +121,6 @@
       return "builtin";
     case Token::Type::kCase:
       return "case";
-    case Token::Type::kCast:
-      return "cast";
     case Token::Type::kCompute:
       return "compute";
     case Token::Type::kConst:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index ebd39e6..184d1d9 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -132,8 +132,6 @@
     kBuiltin,
     /// A 'case'
     kCase,
-    /// A 'cast'
-    kCast,
     /// A 'compute'
     kCompute,
     /// A 'const'
@@ -509,8 +507,6 @@
   bool IsBuiltin() const { return type_ == Type::kBuiltin; }
   /// @returns true if token is a 'case'
   bool IsCase() const { return type_ == Type::kCase; }
-  /// @returns true if token is a 'cast'
-  bool IsCast() const { return type_ == Type::kCast; }
   /// @returns true if token is a 'sampler_comparison'
   bool IsComparisonSampler() const { return type_ == Type::kComparisonSampler; }
   /// @returns true if token is a 'compute'
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index dedf1f0..5a412a4 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -26,7 +26,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/identifier_expression.h"
@@ -305,9 +304,6 @@
   if (expr->IsCall()) {
     return DetermineCall(expr->AsCall());
   }
-  if (expr->IsCast()) {
-    return DetermineCast(expr->AsCast());
-  }
   if (expr->IsConstructor()) {
     return DetermineConstructor(expr->AsConstructor());
   }
@@ -737,15 +733,6 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineCast(ast::CastExpression* expr) {
-  if (!DetermineResultType(expr->expr())) {
-    return false;
-  }
-
-  expr->set_result_type(expr->type());
-  return true;
-}
-
 bool TypeDeterminer::DetermineConstructor(ast::ConstructorExpression* expr) {
   if (expr->IsTypeConstructor()) {
     auto* ty = expr->AsTypeConstructor();
diff --git a/src/type_determiner.h b/src/type_determiner.h
index 6ba3833..482a439b 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -30,7 +30,6 @@
 class BinaryExpression;
 class BitcastExpression;
 class CallExpression;
-class CastExpression;
 class ConstructorExpression;
 class Function;
 class IdentifierExpression;
@@ -120,7 +119,6 @@
   bool DetermineBinary(ast::BinaryExpression* expr);
   bool DetermineBitcast(ast::BitcastExpression* expr);
   bool DetermineCall(ast::CallExpression* expr);
-  bool DetermineCast(ast::CastExpression* expr);
   bool DetermineConstructor(ast::ConstructorExpression* expr);
   bool DetermineIdentifier(ast::IdentifierExpression* expr);
   bool DetermineIntrinsic(ast::IdentifierExpression* name,
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 9a6bd20..d60b376 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -29,7 +29,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/float_literal.h"
@@ -686,8 +685,11 @@
 
 TEST_F(TypeDeterminerTest, Expr_Cast) {
   ast::type::F32Type f32;
-  ast::CastExpression cast(&f32,
-                           std::make_unique<ast::IdentifierExpression>("name"));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("name"));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
 
   EXPECT_TRUE(td()->DetermineResultType(&cast));
   ASSERT_NE(cast.result_type(), nullptr);
diff --git a/src/validator_test.cc b/src/validator_test.cc
index b97b58a..62ada22 100644
--- a/src/validator_test.cc
+++ b/src/validator_test.cc
@@ -24,7 +24,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/float_literal.h"
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 68a33ca..6e91894 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -24,7 +24,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/float_literal.h"
@@ -724,21 +723,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitCast(std::ostream& pre,
-                             std::ostream& out,
-                             ast::CastExpression* expr) {
-  if (!EmitType(out, expr->type(), "")) {
-    return false;
-  }
-
-  out << "(";
-  if (!EmitExpression(pre, out, expr->expr())) {
-    return false;
-  }
-  out << ")";
-  return true;
-}
-
 bool GeneratorImpl::EmitCase(std::ostream& out, ast::CaseStatement* stmt) {
   make_indent(out);
 
@@ -868,9 +852,6 @@
   if (expr->IsCall()) {
     return EmitCall(pre, out, expr->AsCall());
   }
-  if (expr->IsCast()) {
-    return EmitCast(pre, out, expr->AsCast());
-  }
   if (expr->IsConstructor()) {
     return EmitConstructor(pre, out, expr->AsConstructor());
   }
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index e30c363..1e231a6 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -128,14 +128,6 @@
   /// @param stmt the statement
   /// @returns true if the statment was emitted successfully
   bool EmitCase(std::ostream& out, ast::CaseStatement* stmt);
-  /// Handles generating a cast expression
-  /// @param pre the preamble for the expression stream
-  /// @param out the output of the expression stream
-  /// @param expr the cast expression
-  /// @returns true if the cast was emitted
-  bool EmitCast(std::ostream& pre,
-                std::ostream& out,
-                ast::CastExpression* expr);
   /// Handles generating constructor expressions
   /// @param pre the preamble for the expression stream
   /// @param out the output of the expression stream
diff --git a/src/writer/hlsl/generator_impl_cast_test.cc b/src/writer/hlsl/generator_impl_cast_test.cc
index e646e07..b92b104 100644
--- a/src/writer/hlsl/generator_impl_cast_test.cc
+++ b/src/writer/hlsl/generator_impl_cast_test.cc
@@ -14,11 +14,11 @@
 
 #include <memory>
 
-#include "src/ast/cast_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/module.h"
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/vector_type.h"
+#include "src/ast/type_constructor_expression.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
@@ -30,8 +30,11 @@
 
 TEST_F(HlslGeneratorImplTest_Cast, EmitExpression_Cast_Scalar) {
   ast::type::F32Type f32;
-  auto id = std::make_unique<ast::IdentifierExpression>("id");
-  ast::CastExpression cast(&f32, std::move(id));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("id"));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
 
   ASSERT_TRUE(gen().EmitExpression(pre(), out(), &cast)) << gen().error();
   EXPECT_EQ(result(), "float(id)");
@@ -41,8 +44,10 @@
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
 
-  auto id = std::make_unique<ast::IdentifierExpression>("id");
-  ast::CastExpression cast(&vec3, std::move(id));
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("id"));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
 
   ASSERT_TRUE(gen().EmitExpression(pre(), out(), &cast)) << gen().error();
   EXPECT_EQ(result(), "vector<float, 3>(id)");
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 45773b9..6f26ffa 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -24,7 +24,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/else_statement.h"
@@ -744,19 +743,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitCast(ast::CastExpression* expr) {
-  if (!EmitType(expr->type(), "")) {
-    return false;
-  }
-
-  out_ << "(";
-  if (!EmitExpression(expr->expr())) {
-    return false;
-  }
-  out_ << ")";
-  return true;
-}
-
 bool GeneratorImpl::EmitConstructor(ast::ConstructorExpression* expr) {
   if (expr->IsScalarConstructor()) {
     return EmitScalarConstructor(expr->AsScalarConstructor());
@@ -992,9 +978,6 @@
   if (expr->IsCall()) {
     return EmitCall(expr->AsCall());
   }
-  if (expr->IsCast()) {
-    return EmitCast(expr->AsCast());
-  }
   if (expr->IsConstructor()) {
     return EmitConstructor(expr->AsConstructor());
   }
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h
index cc458d2..f33a143 100644
--- a/src/writer/msl/generator_impl.h
+++ b/src/writer/msl/generator_impl.h
@@ -98,10 +98,6 @@
   /// @param stmt the statement
   /// @returns true if the statement was emitted successfully
   bool EmitCase(ast::CaseStatement* stmt);
-  /// Handles generating a cast expression
-  /// @param expr the cast expression
-  /// @returns true if the cast was emitted
-  bool EmitCast(ast::CastExpression* expr);
   /// Handles generating constructor expressions
   /// @param expr the constructor expression
   /// @returns true if the expression was emitted
diff --git a/src/writer/msl/generator_impl_cast_test.cc b/src/writer/msl/generator_impl_cast_test.cc
index f760335..aea7840 100644
--- a/src/writer/msl/generator_impl_cast_test.cc
+++ b/src/writer/msl/generator_impl_cast_test.cc
@@ -15,11 +15,11 @@
 #include <memory>
 
 #include "gtest/gtest.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/module.h"
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/vector_type.h"
+#include "src/ast/type_constructor_expression.h"
 #include "src/writer/msl/generator_impl.h"
 
 namespace tint {
@@ -31,8 +31,11 @@
 
 TEST_F(MslGeneratorImplTest, EmitExpression_Cast_Scalar) {
   ast::type::F32Type f32;
-  auto id = std::make_unique<ast::IdentifierExpression>("id");
-  ast::CastExpression cast(&f32, std::move(id));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("id"));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
 
   ast::Module m;
   GeneratorImpl g(&m);
@@ -44,8 +47,10 @@
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
 
-  auto id = std::make_unique<ast::IdentifierExpression>("id");
-  ast::CastExpression cast(&vec3, std::move(id));
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("id"));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
 
   ast::Module m;
   GeneratorImpl g(&m);
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index b2fcdf1..f879bef 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -30,7 +30,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/constructor_expression.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/else_statement.h"
@@ -465,9 +464,6 @@
   if (expr->IsCall()) {
     return GenerateCallExpression(expr->AsCall());
   }
-  if (expr->IsCast()) {
-    return GenerateCastExpression(expr->AsCast());
-  }
   if (expr->IsConstructor()) {
     return GenerateConstructorExpression(expr->AsConstructor(), false);
   }
@@ -1054,32 +1050,20 @@
 uint32_t Builder::GenerateTypeConstructorExpression(
     ast::TypeConstructorExpression* init,
     bool is_global_init) {
-  auto type_id = GenerateTypeIfNeeded(init->type());
-  if (type_id == 0) {
-    return 0;
-  }
+  auto& values = init->values();
 
   // Generate the zero initializer if there are no values provided.
-  if (init->values().empty()) {
+  if (values.empty()) {
     ast::NullLiteral nl(init->type()->UnwrapPtrIfNeeded());
     return GenerateLiteralIfNeeded(&nl);
   }
 
-  auto* result_type = init->type()->UnwrapPtrIfNeeded();
-  if (result_type->IsVector()) {
-    result_type = result_type->AsVector()->type();
-  } else if (result_type->IsArray()) {
-    result_type = result_type->AsArray()->type();
-  } else if (result_type->IsMatrix()) {
-    result_type = result_type->AsMatrix()->type();
-  }
-
   std::ostringstream out;
   out << "__const";
 
   OperandList ops;
   bool constructor_is_const = true;
-  for (const auto& e : init->values()) {
+  for (const auto& e : values) {
     if (!e->IsConstructor()) {
       if (is_global_init) {
         error_ = "constructor must be a constant expression";
@@ -1089,9 +1073,37 @@
     }
   }
 
+  auto* result_type = init->type()->UnwrapAliasPtrAlias();
+
+  bool can_cast_or_copy = result_type->is_scalar();
+  if (result_type->IsVector() && result_type->AsVector()->type()->is_scalar()) {
+    auto* value_type = values[0]->result_type()->UnwrapAliasPtrAlias();
+    can_cast_or_copy =
+        (value_type->IsVector() &&
+         value_type->AsVector()->type()->is_scalar() &&
+         result_type->AsVector()->size() == value_type->AsVector()->size());
+  }
+  if (can_cast_or_copy) {
+    return GenerateCastOrCopy(result_type, values[0].get());
+  }
+
+  auto type_id = GenerateTypeIfNeeded(init->type());
+  if (type_id == 0) {
+    return 0;
+  }
+
   bool result_is_constant_composite = constructor_is_const;
   bool result_is_spec_composite = false;
-  for (const auto& e : init->values()) {
+
+  if (result_type->IsVector()) {
+    result_type = result_type->AsVector()->type();
+  } else if (result_type->IsArray()) {
+    result_type = result_type->AsArray()->type();
+  } else if (result_type->IsMatrix()) {
+    result_type = result_type->AsMatrix()->type();
+  }
+
+  for (const auto& e : values) {
     uint32_t id = 0;
     if (constructor_is_const) {
       id = GenerateConstructorExpression(e->AsConstructor(), is_global_init);
@@ -1104,54 +1116,54 @@
     }
 
     auto* value_type = e->result_type()->UnwrapPtrIfNeeded();
+    if (result_type == value_type) {
+      out << "_" << id;
+      ops.push_back(Operand::Int(id));
+      continue;
+    }
+
+    // Both scalars, but not the same type so we need to generate a conversion
+    // of the value.
+    if (value_type->is_scalar() && result_type->is_scalar()) {
+      id = GenerateCastOrCopy(result_type, values[0].get());
+      out << "_" << id;
+      ops.push_back(Operand::Int(id));
+      continue;
+    }
 
     // When handling vectors as the values there a few cases to take into
     // consideration:
     //  1. Module scoped vec3<f32>(vec2<f32>(1, 2), 3)  -> OpSpecConstantOp
-    //  2. Function scoped vec3<f32>(vec2<f32>(1, 2), 3) -> OpCompositeExtract
+    //  2. Function scoped vec3<f32>(vec2<f32>(1, 2), 3) ->  OpCompositeExtract
     //  3. Either array<vec3<f32>, 1>(vec3<f32>(1, 2, 3))  -> use the ID.
+    //       -> handled above
+    //
+    // For cases 1 and 2, if the type is different we also may need to insert
+    // a type cast.
     if (value_type->IsVector()) {
       auto* vec = value_type->AsVector();
       auto* vec_type = vec->type();
 
-      // If the value we want is the same as what we have, use it directly.
-      // This maps to case 3.
-      if (result_type == value_type) {
-        out << "_" << id;
-        ops.push_back(Operand::Int(id));
-      } else if (!is_global_init) {
-        // A non-global initializer. Case 2.
-        auto value_type_id = GenerateTypeIfNeeded(vec_type);
-        if (value_type_id == 0) {
-          return 0;
-        }
+      auto value_type_id = GenerateTypeIfNeeded(vec_type);
+      if (value_type_id == 0) {
+        return 0;
+      }
 
-        for (uint32_t i = 0; i < vec->size(); ++i) {
-          auto extract = result_op();
-          auto extract_id = extract.to_i();
+      for (uint32_t i = 0; i < vec->size(); ++i) {
+        auto extract = result_op();
+        auto extract_id = extract.to_i();
 
+        if (!is_global_init) {
+          // A non-global initializer. Case 2.
           push_function_inst(spv::Op::OpCompositeExtract,
                              {Operand::Int(value_type_id), extract,
                               Operand::Int(id), Operand::Int(i)});
 
-          out << "_" << extract_id;
-          ops.push_back(Operand::Int(extract_id));
-
           // We no longer have a constant composite, but have to do a
           // composite construction as these calls are inside a function.
           result_is_constant_composite = false;
-        }
-      } else {
-        // A global initializer, must use OpSpecConstantOp. Case 1.
-        auto value_type_id = GenerateTypeIfNeeded(vec_type);
-        if (value_type_id == 0) {
-          return 0;
-        }
-
-        for (uint32_t i = 0; i < vec->size(); ++i) {
-          auto extract = result_op();
-          auto extract_id = extract.to_i();
-
+        } else {
+          // A global initializer, must use OpSpecConstantOp. Case 1.
           auto idx_id = GenerateU32Literal(i);
           if (idx_id == 0) {
             return 0;
@@ -1161,15 +1173,15 @@
                      Operand::Int(SpvOpCompositeExtract), Operand::Int(id),
                      Operand::Int(idx_id)});
 
-          out << "_" << extract_id;
-          ops.push_back(Operand::Int(extract_id));
-
           result_is_spec_composite = true;
         }
+
+        out << "_" << extract_id;
+        ops.push_back(Operand::Int(extract_id));
       }
     } else {
-      out << "_" << id;
-      ops.push_back(Operand::Int(id));
+      error_ = "Unhandled type cast value type";
+      return 0;
     }
   }
 
@@ -1192,9 +1204,69 @@
   } else {
     push_function_inst(spv::Op::OpCompositeConstruct, ops);
   }
+
   return result.to_i();
 }
 
+uint32_t Builder::GenerateCastOrCopy(ast::type::Type* to_type,
+                                     ast::Expression* from_expr) {
+  auto result = result_op();
+  auto result_id = result.to_i();
+
+  auto result_type_id = GenerateTypeIfNeeded(to_type);
+  if (result_type_id == 0) {
+    return 0;
+  }
+
+  auto val_id = GenerateExpression(from_expr);
+  if (val_id == 0) {
+    return 0;
+  }
+  val_id = GenerateLoadIfNeeded(from_expr->result_type(), val_id);
+
+  auto* from_type = from_expr->result_type()->UnwrapPtrIfNeeded();
+
+  spv::Op op = spv::Op::OpNop;
+  if ((from_type->IsI32() && to_type->IsF32()) ||
+      (from_type->is_signed_integer_vector() && to_type->is_float_vector())) {
+    op = spv::Op::OpConvertSToF;
+  } else if ((from_type->IsU32() && to_type->IsF32()) ||
+             (from_type->is_unsigned_integer_vector() &&
+              to_type->is_float_vector())) {
+    op = spv::Op::OpConvertUToF;
+  } else if ((from_type->IsF32() && to_type->IsI32()) ||
+             (from_type->is_float_vector() &&
+              to_type->is_signed_integer_vector())) {
+    op = spv::Op::OpConvertFToS;
+  } else if ((from_type->IsF32() && to_type->IsU32()) ||
+             (from_type->is_float_vector() &&
+              to_type->is_unsigned_integer_vector())) {
+    op = spv::Op::OpConvertFToU;
+  } else if ((from_type->IsBool() && to_type->IsBool()) ||
+             (from_type->IsU32() && to_type->IsU32()) ||
+             (from_type->IsI32() && to_type->IsI32()) ||
+             (from_type->IsF32() && to_type->IsF32())) {
+    op = spv::Op::OpCopyObject;
+  } else if ((from_type->IsI32() && to_type->IsU32()) ||
+             (from_type->IsU32() && to_type->IsI32()) ||
+             (from_type->is_signed_integer_vector() &&
+              to_type->is_unsigned_integer_vector()) ||
+             (from_type->is_unsigned_integer_vector() &&
+              to_type->is_integer_scalar_or_vector())) {
+    op = spv::Op::OpBitcast;
+  }
+  if (op == spv::Op::OpNop) {
+    error_ = "unable to determine conversion type for cast, from: " +
+             from_type->type_name() + " to: " + to_type->type_name();
+    return 0;
+  }
+
+  push_function_inst(
+      op, {Operand::Int(result_type_id), result, Operand::Int(val_id)});
+
+  return result_id;
+}
+
 uint32_t Builder::GenerateLiteralIfNeeded(ast::Literal* lit) {
   auto type_id = GenerateTypeIfNeeded(lit->type());
   if (type_id == 0) {
@@ -1726,70 +1798,6 @@
   return result_id;
 }
 
-uint32_t Builder::GenerateCastExpression(ast::CastExpression* cast) {
-  auto result = result_op();
-  auto result_id = result.to_i();
-
-  auto result_type_id = GenerateTypeIfNeeded(cast->result_type());
-  if (result_type_id == 0) {
-    return 0;
-  }
-
-  auto val_id = GenerateExpression(cast->expr());
-  if (val_id == 0) {
-    return 0;
-  }
-  val_id = GenerateLoadIfNeeded(cast->expr()->result_type(), val_id);
-
-  auto* to_type = cast->result_type()->UnwrapPtrIfNeeded();
-  auto* from_type = cast->expr()->result_type()->UnwrapPtrIfNeeded();
-
-  spv::Op op = spv::Op::OpNop;
-  if ((from_type->IsI32() && to_type->IsF32()) ||
-      (from_type->is_signed_integer_vector() && to_type->is_float_vector())) {
-    op = spv::Op::OpConvertSToF;
-  } else if ((from_type->IsU32() && to_type->IsF32()) ||
-             (from_type->is_unsigned_integer_vector() &&
-              to_type->is_float_vector())) {
-    op = spv::Op::OpConvertUToF;
-  } else if ((from_type->IsF32() && to_type->IsI32()) ||
-             (from_type->is_float_vector() &&
-              to_type->is_signed_integer_vector())) {
-    op = spv::Op::OpConvertFToS;
-  } else if ((from_type->IsF32() && to_type->IsU32()) ||
-             (from_type->is_float_vector() &&
-              to_type->is_unsigned_integer_vector())) {
-    op = spv::Op::OpConvertFToU;
-  } else if ((from_type->IsU32() && to_type->IsU32()) ||
-             (from_type->IsI32() && to_type->IsI32()) ||
-             (from_type->IsF32() && to_type->IsF32()) ||
-             (from_type->is_unsigned_integer_vector() &&
-              to_type->is_unsigned_integer_vector()) ||
-             (from_type->is_signed_integer_vector() &&
-              to_type->is_signed_integer_vector()) ||
-             (from_type->is_float_vector() && to_type->is_float_vector())) {
-    op = spv::Op::OpCopyObject;
-  } else if ((from_type->IsI32() && to_type->IsU32()) ||
-             (from_type->IsU32() && to_type->IsI32()) ||
-             (from_type->is_signed_integer_vector() &&
-              to_type->is_unsigned_integer_vector()) ||
-             (from_type->is_unsigned_integer_vector() &&
-              to_type->is_integer_scalar_or_vector())) {
-    op = spv::Op::OpBitcast;
-  }
-
-  if (op == spv::Op::OpNop) {
-    error_ = "unable to determine conversion type for cast, from: " +
-             from_type->type_name() + " to: " + to_type->type_name();
-    return 0;
-  }
-
-  push_function_inst(
-      op, {Operand::Int(result_type_id), result, Operand::Int(val_id)});
-
-  return result_id;
-}
-
 bool Builder::GenerateConditionalBlock(
     ast::Expression* cond,
     const ast::BlockStatement* true_body,
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index a0c33d6..ccfce39 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -306,10 +306,12 @@
   uint32_t GenerateSampledImage(ast::type::Type* texture_type,
                                 Operand texture_operand,
                                 Operand sampler_operand);
-  /// Generates a cast expression
-  /// @param expr the expression to generate
+  /// Generates a cast or object copy for the expression result
+  /// @param to_type the type we're casting too
+  /// @param from_expr the expression to cast
   /// @returns the expression ID on success or 0 otherwise
-  uint32_t GenerateCastExpression(ast::CastExpression* expr);
+  uint32_t GenerateCastOrCopy(ast::type::Type* to_type,
+                              ast::Expression* from_expr);
   /// Generates a loop statement
   /// @param stmt the statement to generate
   /// @returns true on successful generation
diff --git a/src/writer/spirv/builder_cast_expression_test.cc b/src/writer/spirv/builder_cast_expression_test.cc
deleted file mode 100644
index 675bb20..0000000
--- a/src/writer/spirv/builder_cast_expression_test.cc
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "src/ast/cast_expression.h"
-#include "src/ast/float_literal.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/module.h"
-#include "src/ast/scalar_constructor_expression.h"
-#include "src/ast/sint_literal.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
-#include "src/ast/type/u32_type.h"
-#include "src/ast/type/vector_type.h"
-#include "src/ast/uint_literal.h"
-#include "src/context.h"
-#include "src/type_determiner.h"
-#include "src/writer/spirv/builder.h"
-#include "src/writer/spirv/spv_dump.h"
-
-namespace tint {
-namespace writer {
-namespace spirv {
-namespace {
-
-using BuilderTest = testing::Test;
-
-TEST_F(BuilderTest, Cast_FloatToU32) {
-  ast::type::U32Type u32;
-  ast::type::F32Type f32;
-
-  ast::CastExpression cast(&u32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::FloatLiteral>(&f32, 2.4)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
-%3 = OpTypeFloat 32
-%4 = OpConstant %3 2.4000001
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpConvertFToU %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_FloatToI32) {
-  ast::type::I32Type i32;
-  ast::type::F32Type f32;
-
-  ast::CastExpression cast(&i32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::FloatLiteral>(&f32, 2.4)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
-%3 = OpTypeFloat 32
-%4 = OpConstant %3 2.4000001
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpConvertFToS %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_I32ToFloat) {
-  ast::type::I32Type i32;
-  ast::type::F32Type f32;
-
-  ast::CastExpression cast(&f32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::SintLiteral>(&i32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%3 = OpTypeInt 32 1
-%4 = OpConstant %3 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpConvertSToF %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_U32ToFloat) {
-  ast::type::U32Type u32;
-  ast::type::F32Type f32;
-
-  ast::CastExpression cast(&f32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::UintLiteral>(&u32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%3 = OpTypeInt 32 0
-%4 = OpConstant %3 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpConvertUToF %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_WithLoad) {
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  // var i : i32 = 1;
-  // cast<f32>(i);
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &i32);
-
-  ast::CastExpression cast(&f32,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 5u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
-%2 = OpTypePointer Private %3
-%4 = OpConstantNull %3
-%1 = OpVariable %2 Private %4
-%6 = OpTypeFloat 32
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%5 = OpConvertSToF %6 %7
-)");
-}
-
-TEST_F(BuilderTest, Cast_WithAlias) {
-  ast::type::I32Type i32;
-  ast::type::F32Type f32;
-
-  // type Int = i32
-  // cast<Int>(1.f)
-
-  ast::type::AliasType alias("Int", &i32);
-
-  ast::CastExpression cast(&alias,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::FloatLiteral>(&f32, 2.3)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
-%3 = OpTypeFloat 32
-%4 = OpConstant %3 2.29999995
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpConvertFToS %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_I32ToU32) {
-  ast::type::U32Type u32;
-  ast::type::I32Type i32;
-
-  ast::CastExpression cast(&u32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::SintLiteral>(&i32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
-%3 = OpTypeInt 32 1
-%4 = OpConstant %3 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpBitcast %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_U32ToI32) {
-  ast::type::U32Type u32;
-  ast::type::I32Type i32;
-
-  ast::CastExpression cast(&i32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::UintLiteral>(&u32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
-%3 = OpTypeInt 32 0
-%4 = OpConstant %3 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpBitcast %2 %4
-)");
-}
-
-TEST_F(BuilderTest, Cast_I32ToI32) {
-  ast::type::I32Type i32;
-
-  ast::CastExpression cast(&i32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::SintLiteral>(&i32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
-%3 = OpConstant %2 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
-}
-
-TEST_F(BuilderTest, Cast_U32ToU32) {
-  ast::type::U32Type u32;
-
-  ast::CastExpression cast(&u32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::UintLiteral>(&u32, 2)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
-%3 = OpConstant %2 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
-}
-
-TEST_F(BuilderTest, Cast_F32ToF32) {
-  ast::type::F32Type f32;
-
-  ast::CastExpression cast(&f32,
-                           std::make_unique<ast::ScalarConstructorExpression>(
-                               std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 1u);
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%3 = OpConstant %2 2
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%1 = OpCopyObject %2 %3
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_I32_to_F32) {
-  ast::type::I32Type i32;
-  ast::type::VectorType ivec3(&i32, 3);
-  ast::type::F32Type f32;
-  ast::type::VectorType fvec3(&f32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &ivec3);
-
-  ast::CastExpression cast(&fvec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeFloat 32
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = OpConvertSToF %7 %9
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_U32_to_F32) {
-  ast::type::U32Type u32;
-  ast::type::VectorType uvec3(&u32, 3);
-  ast::type::F32Type f32;
-  ast::type::VectorType fvec3(&f32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &uvec3);
-
-  ast::CastExpression cast(&fvec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeFloat 32
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = OpConvertUToF %7 %9
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_F32_to_I32) {
-  ast::type::I32Type i32;
-  ast::type::VectorType ivec3(&i32, 3);
-  ast::type::F32Type f32;
-  ast::type::VectorType fvec3(&f32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &fvec3);
-
-  ast::CastExpression cast(&ivec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeInt 32 1
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = OpConvertFToS %7 %9
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_F32_to_U32) {
-  ast::type::U32Type u32;
-  ast::type::VectorType uvec3(&u32, 3);
-  ast::type::F32Type f32;
-  ast::type::VectorType fvec3(&f32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &fvec3);
-
-  ast::CastExpression cast(&uvec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeInt 32 0
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = OpConvertFToU %7 %9
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_U32_to_U32) {
-  ast::type::U32Type u32;
-  ast::type::VectorType uvec3(&u32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &uvec3);
-
-  ast::CastExpression cast(&uvec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%6 = OpCopyObject %3 %7
-)");
-}
-
-TEST_F(BuilderTest, Cast_Vectors_I32_to_U32) {
-  ast::type::U32Type u32;
-  ast::type::VectorType uvec3(&u32, 3);
-  ast::type::I32Type i32;
-  ast::type::VectorType ivec3(&i32, 3);
-
-  auto var =
-      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &ivec3);
-
-  ast::CastExpression cast(&uvec3,
-                           std::make_unique<ast::IdentifierExpression>("i"));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  EXPECT_EQ(b.GenerateCastExpression(&cast), 6u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeInt 32 0
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = OpBitcast %7 %9
-)");
-}
-
-}  // namespace
-}  // namespace spirv
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc
index 3a16281..f1468db 100644
--- a/src/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -18,12 +18,25 @@
 #include "spirv/unified1/spirv.h"
 #include "spirv/unified1/spirv.hpp11"
 #include "src/ast/binary_expression.h"
+#include "src/ast/bool_literal.h"
 #include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
+#include "src/ast/member_accessor_expression.h"
 #include "src/ast/scalar_constructor_expression.h"
+#include "src/ast/sint_literal.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_decoration.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/type/array_type.h"
+#include "src/ast/type/bool_type.h"
 #include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/ast/type/u32_type.h"
 #include "src/ast/type/vector_type.h"
 #include "src/ast/type_constructor_expression.h"
+#include "src/ast/uint_literal.h"
 #include "src/context.h"
 #include "src/type_determiner.h"
 #include "src/writer/spirv/builder.h"
@@ -82,31 +95,40 @@
 )");
 }
 
-TEST_F(BuilderTest, Constructor_Type_ZeroInit) {
+TEST_F(BuilderTest, Constructor_Type_WithAlias) {
+  ast::type::I32Type i32;
   ast::type::F32Type f32;
-  ast::type::VectorType vec(&f32, 2);
 
-  ast::ExpressionList vals;
-  ast::TypeConstructorExpression t(&vec, std::move(vals));
+  // type Int = i32
+  // cast<Int>(1.f)
+
+  ast::type::AliasType alias("Int", &i32);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.3)));
+
+  ast::TypeConstructorExpression cast(&alias, std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
 
   Builder b(&mod);
   b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
 
-  EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 3u);
-  ASSERT_FALSE(b.has_error()) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 2
-%3 = OpConstantNull %1
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpTypeFloat 32
+%4 = OpConstant %3 2.29999995
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpConvertFToS %2 %4
 )");
 }
 
-TEST_F(BuilderTest, Constructor_Type_NonConstructorParam) {
+TEST_F(BuilderTest, Constructor_Type_IdentifierExpression_Param) {
   ast::type::F32Type f32;
   ast::type::VectorType vec(&f32, 2);
 
@@ -130,7 +152,7 @@
   b.push_function(Function{});
   ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error();
 
-  EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 8u);
+  EXPECT_EQ(b.GenerateExpression(&t), 8u);
   ASSERT_FALSE(b.has_error()) << b.error();
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
@@ -149,81 +171,7 @@
 )");
 }
 
-TEST_F(BuilderTest, Constructor_Type_NonConstVector) {
-  ast::type::F32Type f32;
-  ast::type::VectorType vec2(&f32, 2);
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto var = std::make_unique<ast::Variable>(
-      "ident", ast::StorageClass::kFunction, &vec2);
-
-  ast::ExpressionList vals;
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-  vals.push_back(std::make_unique<ast::IdentifierExpression>("ident"));
-
-  ast::TypeConstructorExpression t(&vec4, std::move(vals));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
-
-  Builder b(&mod);
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error();
-
-  EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 11u);
-  ASSERT_FALSE(b.has_error()) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 2
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%6 = OpTypeVector %4 4
-%7 = OpConstant %4 1
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-            R"(%1 = OpVariable %2 Function %5
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%8 = OpLoad %3 %1
-%9 = OpCompositeExtract %4 %8 0
-%10 = OpCompositeExtract %4 %8 1
-%11 = OpCompositeConstruct %6 %7 %7 %9 %10
-)");
-}
-
-TEST_F(BuilderTest, Constructor_Type_Dedups) {
-  ast::type::F32Type f32;
-  ast::type::VectorType vec(&f32, 3);
-
-  ast::ExpressionList vals;
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
-
-  ast::TypeConstructorExpression t(&vec, std::move(vals));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
-
-  Builder b(&mod);
-  EXPECT_EQ(b.GenerateConstructorExpression(&t, true), 5u);
-  EXPECT_EQ(b.GenerateConstructorExpression(&t, true), 5u);
-  ASSERT_FALSE(b.has_error()) << b.error();
-}
-
-TEST_F(BuilderTest, Constructor_NonConst_Type_Fails) {
+TEST_F(BuilderTest, Constructor_Type_NonConst_Value_Fails) {
   ast::type::F32Type f32;
   ast::type::VectorType vec(&f32, 2);
   auto rel = std::make_unique<ast::BinaryExpression>(
@@ -247,6 +195,1607 @@
   EXPECT_EQ(b.error(), R"(constructor must be a constant expression)");
 }
 
+TEST_F(BuilderTest, Constructor_Type_Bool_With_Bool) {
+  ast::type::BoolType bool_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::BoolLiteral>(&bool_type, true)));
+
+  ast::TypeConstructorExpression t(&bool_type, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 1u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
+%3 = OpConstantTrue %2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpCopyObject %2 %3
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_I32_With_I32) {
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::TypeConstructorExpression cast(&i32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpConstant %2 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpCopyObject %2 %3
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_U32_With_U32) {
+  ast::type::U32Type u32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 2)));
+
+  ast::TypeConstructorExpression cast(&u32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
+%3 = OpConstant %2 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpCopyObject %2 %3
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_F32_With_F32) {
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%3 = OpConstant %2 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpCopyObject %2 %3
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec2_With_F32_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&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(BuilderTest, Constructor_Type_Vec3_With_F32_F32_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&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(BuilderTest, Constructor_Type_Vec3_With_F32_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 8u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeConstruct %1 %3 %6 %7
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec3_With_Vec2_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 8u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeConstruct %1 %6 %7 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_F32_F32_F32_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 4);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&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(BuilderTest, Constructor_Type_Vec4_With_F32_F32_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 8u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeConstruct %1 %3 %3 %6 %7
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_F32_Vec2_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 8u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeConstruct %1 %3 %6 %7 %3
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_Vec2_F32_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 8u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeConstruct %1 %6 %7 %4 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_Vec2_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList vec2_params;
+  vec2_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec2_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec2_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 10u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeExtract %2 %5 0
+%9 = OpCompositeExtract %2 %5 1
+%10 = OpCompositeConstruct %1 %6 %7 %8 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_F32_Vec3) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 9u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeExtract %2 %5 2
+%9 = OpCompositeConstruct %1 %3 %6 %7 %8
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Vec4_With_Vec3_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 9u);
+
+  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
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpCompositeExtract %2 %5 0
+%7 = OpCompositeExtract %2 %5 1
+%8 = OpCompositeExtract %2 %5 2
+%9 = OpCompositeConstruct %1 %6 %7 %8 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec3_With_F32_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 11u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec3_With_Vec2_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 11u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_F32_F32_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 11u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_F32_Vec2_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 11u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_Vec2_F32_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 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(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_Vec2_Vec2) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList vec2_params;
+  vec2_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec2_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vec2_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 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(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_F32_Vec3) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vec_params)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 13u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ModuleScope_Vec4_With_Vec3_F32) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec4(&f32, 4);
+
+  ast::ExpressionList vec_params;
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+  vec_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vec_params)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::TypeConstructorExpression cast(&vec4, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateConstructorExpression(&cast, true), 13u);
+
+  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
+)");
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat2x2_With_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat3x2_With_Vec2_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat4x2_With_Vec2_Vec2_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat2x3_With_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat3x3_With_Vec3_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat4x3_With_Vec3_Vec3_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat2x4_With_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat3x4_With_Vec4_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Mat4x4_With_Vec4_Vec4_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat2x2_With_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat3x2_With_Vec2_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat4x2_With_Vec2_Vec2_Vec2_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat2x3_With_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat3x3_With_Vec3_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat4x3_With_Vec3_Vec3_Vec3_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat2x4_With_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat3x4_With_Vec4_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest,
+       DISABLED_Constructor_Type_ModuleScope_Mat4x4_With_Vec4_Vec4_Vec4_Vec4) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Array_5_F32) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Array_5_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_ModuleScope_Array_5_Vec3) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_Struct) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, DISABLED_Constructor_Type_ModuleScope_Struct_With_Vec2) {
+  FAIL();
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_F32) {
+  ast::type::F32Type f32;
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&f32, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 2u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_I32) {
+  ast::type::I32Type i32;
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&i32, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 2u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_U32) {
+  ast::type::U32Type u32;
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&u32, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 2u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_Bool) {
+  ast::type::BoolType bool_type;
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&bool_type, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 2u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_Vector) {
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 2);
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&vec, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 3u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%1 = OpTypeVector %2 2
+%3 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_Matrix) {
+  ast::type::F32Type f32;
+  ast::type::MatrixType mat(&f32, 2, 4);
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&mat, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 4u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypeVector %3 2
+%1 = OpTypeMatrix %2 4
+%4 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_Array) {
+  ast::type::I32Type i32;
+  ast::type::ArrayType ary(&i32, 2);
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&ary, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 5u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpTypeInt 32 0
+%4 = OpConstant %3 2
+%1 = OpTypeArray %2 %4
+%5 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_ZeroInit_Struct) {
+  ast::type::F32Type f32;
+
+  ast::StructMemberDecorationList decos;
+  ast::StructMemberList members;
+  members.push_back(
+      std::make_unique<ast::StructMember>("a", &f32, std::move(decos)));
+
+  auto s = std::make_unique<ast::Struct>(ast::StructDecoration::kNone,
+                                         std::move(members));
+  ast::type::StructType s_type(std::move(s));
+  s_type.set_name("my_struct");
+
+  ast::ExpressionList vals;
+  ast::TypeConstructorExpression t(&s_type, std::move(vals));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  EXPECT_TRUE(td.DetermineResultType(&t)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+
+  EXPECT_EQ(b.GenerateExpression(&t), 3u);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeStruct %2
+%3 = OpConstantNull %1
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_U32_To_I32) {
+  ast::type::U32Type u32;
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 2)));
+
+  ast::TypeConstructorExpression cast(&i32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpTypeInt 32 0
+%4 = OpConstant %3 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpBitcast %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_I32_To_U32) {
+  ast::type::U32Type u32;
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::TypeConstructorExpression cast(&u32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
+%3 = OpTypeInt 32 1
+%4 = OpConstant %3 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpBitcast %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_F32_To_I32) {
+  ast::type::I32Type i32;
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.4)));
+
+  ast::TypeConstructorExpression cast(&i32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpTypeFloat 32
+%4 = OpConstant %3 2.4000001
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpConvertFToS %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_F32_To_U32) {
+  ast::type::U32Type u32;
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.4)));
+
+  ast::TypeConstructorExpression cast(&u32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0
+%3 = OpTypeFloat 32
+%4 = OpConstant %3 2.4000001
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpConvertFToU %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_I32_To_F32) {
+  ast::type::I32Type i32;
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%3 = OpTypeInt 32 1
+%4 = OpConstant %3 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpConvertSToF %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_U32_To_F32) {
+  ast::type::U32Type u32;
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 2)));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  EXPECT_EQ(b.GenerateExpression(&cast), 1u);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%3 = OpTypeInt 32 0
+%4 = OpConstant %3 2
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%1 = OpConvertUToF %2 %4
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_U32_to_I32) {
+  ast::type::U32Type u32;
+  ast::type::VectorType uvec3(&u32, 3);
+  ast::type::I32Type i32;
+  ast::type::VectorType ivec3(&i32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &uvec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&ivec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 1
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpBitcast %7 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_F32_to_I32) {
+  ast::type::I32Type i32;
+  ast::type::VectorType ivec3(&i32, 3);
+  ast::type::F32Type f32;
+  ast::type::VectorType fvec3(&f32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &fvec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&ivec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 1
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpConvertFToS %7 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_I32_to_U32) {
+  ast::type::U32Type u32;
+  ast::type::VectorType uvec3(&u32, 3);
+  ast::type::I32Type i32;
+  ast::type::VectorType ivec3(&i32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &ivec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&uvec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 0
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpBitcast %7 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_F32_to_U32) {
+  ast::type::U32Type u32;
+  ast::type::VectorType uvec3(&u32, 3);
+  ast::type::F32Type f32;
+  ast::type::VectorType fvec3(&f32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &fvec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&uvec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeInt 32 0
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpConvertFToU %7 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_I32_to_F32) {
+  ast::type::I32Type i32;
+  ast::type::VectorType ivec3(&i32, 3);
+  ast::type::F32Type f32;
+  ast::type::VectorType fvec3(&f32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &ivec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&fvec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpConvertSToF %7 %9
+)");
+}
+
+TEST_F(BuilderTest, Constructor_Type_Convert_Vectors_U32_to_F32) {
+  ast::type::U32Type u32;
+  ast::type::VectorType uvec3(&u32, 3);
+  ast::type::F32Type f32;
+  ast::type::VectorType fvec3(&f32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("i", ast::StorageClass::kPrivate, &uvec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("i"));
+
+  ast::TypeConstructorExpression cast(&fvec3, std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+  ASSERT_TRUE(td.DetermineResultType(&cast)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&cast), 6u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%9 = OpLoad %3 %1
+%6 = OpConvertUToF %7 %9
+)");
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 4389ae1..e13fe6b 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -29,7 +29,6 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/constructor_expression.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/decorated_variable.h"
@@ -186,9 +185,6 @@
   if (expr->IsCall()) {
     return EmitCall(expr->AsCall());
   }
-  if (expr->IsCast()) {
-    return EmitCast(expr->AsCast());
-  }
   if (expr->IsIdentifier()) {
     return EmitIdentifier(expr->AsIdentifier());
   }
@@ -269,21 +265,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitCast(ast::CastExpression* expr) {
-  out_ << "cast<";
-  if (!EmitType(expr->type())) {
-    return false;
-  }
-
-  out_ << ">(";
-  if (!EmitExpression(expr->expr())) {
-    return false;
-  }
-
-  out_ << ")";
-  return true;
-}
-
 bool GeneratorImpl::EmitConstructor(ast::ConstructorExpression* expr) {
   if (expr->IsScalarConstructor()) {
     return EmitScalarConstructor(expr->AsScalarConstructor());
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index 5805f09..0a9e6f4 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -98,10 +98,6 @@
   /// @param stmt the statement
   /// @returns true if the statment was emitted successfully
   bool EmitCase(ast::CaseStatement* stmt);
-  /// Handles generating a cast expression
-  /// @param expr the cast expression
-  /// @returns true if the cast was emitted
-  bool EmitCast(ast::CastExpression* expr);
   /// Handles generating a scalar constructor
   /// @param expr the scalar constructor expression
   /// @returns true if the scalar constructor is emitted
diff --git a/src/writer/wgsl/generator_impl_cast_test.cc b/src/writer/wgsl/generator_impl_cast_test.cc
index e388337..ee81a66 100644
--- a/src/writer/wgsl/generator_impl_cast_test.cc
+++ b/src/writer/wgsl/generator_impl_cast_test.cc
@@ -15,9 +15,9 @@
 #include <memory>
 
 #include "gtest/gtest.h"
-#include "src/ast/cast_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/type/f32_type.h"
+#include "src/ast/type_constructor_expression.h"
 #include "src/writer/wgsl/generator_impl.h"
 
 namespace tint {
@@ -29,12 +29,15 @@
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_Cast) {
   ast::type::F32Type f32;
-  auto id = std::make_unique<ast::IdentifierExpression>("id");
-  ast::CastExpression cast(&f32, std::move(id));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("id"));
+
+  ast::TypeConstructorExpression cast(&f32, std::move(params));
 
   GeneratorImpl g;
   ASSERT_TRUE(g.EmitExpression(&cast)) << g.error();
-  EXPECT_EQ(g.result(), "cast<f32>(id)");
+  EXPECT_EQ(g.result(), "f32(id)");
 }
 
 }  // namespace