[tint] Promote ast::BinaryOp to core::BinaryOp

Needs to be used by the intrinsic::Table.

Change-Id: Id16b68e2215472b4d1ae2a1f4005433e7e3496a0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/144123
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f86032b..a762cfb 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -789,6 +789,8 @@
     "lang/core/address_space.h",
     "lang/core/attribute.cc",
     "lang/core/attribute.h",
+    "lang/core/binary_op.cc",
+    "lang/core/binary_op.h",
     "lang/core/builtin.cc",
     "lang/core/builtin.h",
     "lang/core/builtin_value.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 45c7601..a4c928d 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -82,6 +82,8 @@
   ../../include/tint/tint.h
   lang/wgsl/ast/clone_context.cc
   lang/wgsl/ast/clone_context.h
+  lang/core/binary_op.cc
+  lang/core/binary_op.h
   lang/core/fluent_types.h
   lang/core/number.cc
   lang/core/number.h
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/expression_size_test.cc b/src/tint/fuzzers/tint_ast_fuzzer/expression_size_test.cc
index 76d5571..e9b22e6 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/expression_size_test.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/expression_size_test.cc
@@ -49,10 +49,10 @@
             const auto* binary_expr = expr->As<ast::BinaryExpression>();
             ASSERT_TRUE(binary_expr != nullptr);
             switch (binary_expr->op) {
-                case ast::BinaryOp::kAdd:
+                case core::BinaryOp::kAdd:
                     ASSERT_EQ(3, expression_size(expr));
                     break;
-                case ast::BinaryOp::kMultiply:
+                case core::BinaryOp::kMultiply:
                     ASSERT_EQ(7, expression_size(expr));
                     break;
                 default:
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.cc
index eb3f040..84acd75 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.cc
@@ -31,24 +31,24 @@
     // Go through each binary expression in the AST and add a mutation that
     // replaces its operator with some other type-compatible operator.
 
-    const std::vector<ast::BinaryOp> all_binary_operators = {ast::BinaryOp::kAnd,
-                                                             ast::BinaryOp::kOr,
-                                                             ast::BinaryOp::kXor,
-                                                             ast::BinaryOp::kLogicalAnd,
-                                                             ast::BinaryOp::kLogicalOr,
-                                                             ast::BinaryOp::kEqual,
-                                                             ast::BinaryOp::kNotEqual,
-                                                             ast::BinaryOp::kLessThan,
-                                                             ast::BinaryOp::kGreaterThan,
-                                                             ast::BinaryOp::kLessThanEqual,
-                                                             ast::BinaryOp::kGreaterThanEqual,
-                                                             ast::BinaryOp::kShiftLeft,
-                                                             ast::BinaryOp::kShiftRight,
-                                                             ast::BinaryOp::kAdd,
-                                                             ast::BinaryOp::kSubtract,
-                                                             ast::BinaryOp::kMultiply,
-                                                             ast::BinaryOp::kDivide,
-                                                             ast::BinaryOp::kModulo};
+    const std::vector<core::BinaryOp> all_binary_operators = {core::BinaryOp::kAnd,
+                                                              core::BinaryOp::kOr,
+                                                              core::BinaryOp::kXor,
+                                                              core::BinaryOp::kLogicalAnd,
+                                                              core::BinaryOp::kLogicalOr,
+                                                              core::BinaryOp::kEqual,
+                                                              core::BinaryOp::kNotEqual,
+                                                              core::BinaryOp::kLessThan,
+                                                              core::BinaryOp::kGreaterThan,
+                                                              core::BinaryOp::kLessThanEqual,
+                                                              core::BinaryOp::kGreaterThanEqual,
+                                                              core::BinaryOp::kShiftLeft,
+                                                              core::BinaryOp::kShiftRight,
+                                                              core::BinaryOp::kAdd,
+                                                              core::BinaryOp::kSubtract,
+                                                              core::BinaryOp::kMultiply,
+                                                              core::BinaryOp::kDivide,
+                                                              core::BinaryOp::kModulo};
 
     for (const auto* node : program.ASTNodes().Objects()) {
         const auto* binary_expr = As<ast::BinaryExpression>(node);
@@ -57,7 +57,7 @@
         }
 
         // Get vector of all operators this could be replaced with.
-        std::vector<ast::BinaryOp> allowed_replacements;
+        std::vector<core::BinaryOp> allowed_replacements;
         for (auto candidate_op : all_binary_operators) {
             if (MutationChangeBinaryOperator::CanReplaceBinaryOperator(program, *binary_expr,
                                                                        candidate_op)) {
@@ -67,7 +67,7 @@
 
         if (!allowed_replacements.empty()) {
             // Choose an available replacement operator at random.
-            const ast::BinaryOp replacement =
+            const core::BinaryOp replacement =
                 allowed_replacements[probability_context->GetRandomIndex(allowed_replacements)];
             // Add a mutation according to the chosen replacement.
             result.push_back(std::make_unique<MutationChangeBinaryOperator>(
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
index 8d7591d..6b554ef 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
@@ -39,32 +39,32 @@
 
 bool CanReplaceAddSubtractWith(const type::Type* lhs_type,
                                const type::Type* rhs_type,
-                               ast::BinaryOp new_operator) {
+                               core::BinaryOp new_operator) {
     // The program is assumed to be well-typed, so this method determines when
     // 'new_operator' can be used as a type-preserving replacement in an '+' or
     // '-' expression.
     switch (new_operator) {
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
             // '+' and '-' are fully type compatible.
             return true;
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             // These operators do not have a mixed vector-scalar form, and only work
             // on integer types.
             return lhs_type == rhs_type && lhs_type->is_integer_scalar_or_vector();
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             // '+' and '*' are largely type-compatible, but for matrices they are only
             // type-compatible if the matrices are square.
             return !lhs_type->is_float_matrix() || lhs_type->is_square_float_matrix();
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
             // '/' is not defined for matrices.
             return lhs_type->is_numeric_scalar_or_vector() &&
                    rhs_type->is_numeric_scalar_or_vector();
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return IsSuitableForShift(lhs_type, rhs_type);
         default:
             return false;
@@ -73,15 +73,15 @@
 
 bool CanReplaceMultiplyWith(const type::Type* lhs_type,
                             const type::Type* rhs_type,
-                            ast::BinaryOp new_operator) {
+                            core::BinaryOp new_operator) {
     // The program is assumed to be well-typed, so this method determines when
     // 'new_operator' can be used as a type-preserving replacement in a '*'
     // expression.
     switch (new_operator) {
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             return true;
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
             // '*' is type-compatible with '+' and '-' for square matrices, and for
             // numeric scalars/vectors.
             if (lhs_type->is_square_float_matrix() && rhs_type->is_square_float_matrix()) {
@@ -89,18 +89,18 @@
             }
             return lhs_type->is_numeric_scalar_or_vector() &&
                    rhs_type->is_numeric_scalar_or_vector();
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             // These operators require homogeneous integer types.
             return lhs_type == rhs_type && lhs_type->is_integer_scalar_or_vector();
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
             // '/' is not defined for matrices.
             return lhs_type->is_numeric_scalar_or_vector() &&
                    rhs_type->is_numeric_scalar_or_vector();
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return IsSuitableForShift(lhs_type, rhs_type);
         default:
             return false;
@@ -109,39 +109,39 @@
 
 bool CanReplaceDivideOrModuloWith(const type::Type* lhs_type,
                                   const type::Type* rhs_type,
-                                  ast::BinaryOp new_operator) {
+                                  core::BinaryOp new_operator) {
     // The program is assumed to be well-typed, so this method determines when
     // 'new_operator' can be used as a type-preserving replacement in a '/'
     // expression.
     switch (new_operator) {
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
-        case ast::BinaryOp::kMultiply:
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
+        case core::BinaryOp::kMultiply:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
             // These operators work in all contexts where '/' works.
             return true;
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             // These operators require homogeneous integer types.
             return lhs_type == rhs_type && lhs_type->is_integer_scalar_or_vector();
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return IsSuitableForShift(lhs_type, rhs_type);
         default:
             return false;
     }
 }
 
-bool CanReplaceLogicalAndLogicalOrWith(ast::BinaryOp new_operator) {
+bool CanReplaceLogicalAndLogicalOrWith(core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr:
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kEqual:
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
             // These operators all work whenever '&&' and '||' work.
             return true;
         default:
@@ -151,31 +151,31 @@
 
 bool CanReplaceAndOrWith(const type::Type* lhs_type,
                          const type::Type* rhs_type,
-                         ast::BinaryOp new_operator) {
+                         core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
             // '&' and '|' work in all the same contexts.
             return true;
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
-        case ast::BinaryOp::kMultiply:
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
+        case core::BinaryOp::kMultiply:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
+        case core::BinaryOp::kXor:
             // '&' and '|' can be applied to booleans. In all other contexts,
             // integer numeric operators work.
             return !lhs_type->is_bool_scalar_or_vector();
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return IsSuitableForShift(lhs_type, rhs_type);
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr:
             // '&' and '|' can be applied to booleans, and for boolean scalar
             // scalar contexts, their logical counterparts work.
             return lhs_type->Is<type::Bool>();
-        case ast::BinaryOp::kEqual:
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
             // '&' and '|' can be applied to booleans, and in these contexts equality
             // comparison operators also work.
             return lhs_type->is_bool_scalar_or_vector();
@@ -186,21 +186,21 @@
 
 bool CanReplaceXorWith(const type::Type* lhs_type,
                        const type::Type* rhs_type,
-                       ast::BinaryOp new_operator) {
+                       core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
-        case ast::BinaryOp::kMultiply:
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
+        case core::BinaryOp::kMultiply:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             // '^' only works on integer types, and in any such context, all other
             // integer operators also work.
             return true;
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return IsSuitableForShift(lhs_type, rhs_type);
         default:
             return false;
@@ -209,20 +209,20 @@
 
 bool CanReplaceShiftLeftShiftRightWith(const type::Type* lhs_type,
                                        const type::Type* rhs_type,
-                                       ast::BinaryOp new_operator) {
+                                       core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             // These operators are type-compatible.
             return true;
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
-        case ast::BinaryOp::kMultiply:
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
+        case core::BinaryOp::kMultiply:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             // Shift operators allow mixing of signed and unsigned arguments, but in
             // the case where the arguments are homogeneous, they are type-compatible
             // with other numeric operators.
@@ -232,26 +232,26 @@
     }
 }
 
-bool CanReplaceEqualNotEqualWith(const type::Type* lhs_type, ast::BinaryOp new_operator) {
+bool CanReplaceEqualNotEqualWith(const type::Type* lhs_type, core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kEqual:
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
             // These operators are type-compatible.
             return true;
-        case ast::BinaryOp::kLessThan:
-        case ast::BinaryOp::kLessThanEqual:
-        case ast::BinaryOp::kGreaterThan:
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThanEqual:
             // An equality comparison between numeric types can be changed to an
             // ordered comparison.
             return lhs_type->is_numeric_scalar_or_vector();
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr:
             // An equality comparison between boolean scalars can be turned into a
             // logical operation.
             return lhs_type->Is<type::Bool>();
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
             // An equality comparison between boolean scalars or vectors can be turned
             // into a component-wise non-short-circuit logical operation.
             return lhs_type->is_bool_scalar_or_vector();
@@ -260,14 +260,14 @@
     }
 }
 
-bool CanReplaceLessThanLessThanEqualGreaterThanGreaterThanEqualWith(ast::BinaryOp new_operator) {
+bool CanReplaceLessThanLessThanEqualGreaterThanGreaterThanEqualWith(core::BinaryOp new_operator) {
     switch (new_operator) {
-        case ast::BinaryOp::kEqual:
-        case ast::BinaryOp::kNotEqual:
-        case ast::BinaryOp::kLessThan:
-        case ast::BinaryOp::kLessThanEqual:
-        case ast::BinaryOp::kGreaterThan:
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
+        case core::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThanEqual:
             // Ordered comparison operators can be interchanged, and equality
             // operators can be used in their place.
             return true;
@@ -282,7 +282,7 @@
     : message_(std::move(message)) {}
 
 MutationChangeBinaryOperator::MutationChangeBinaryOperator(uint32_t binary_expr_id,
-                                                           ast::BinaryOp new_operator) {
+                                                           core::BinaryOp new_operator) {
     message_.set_binary_expr_id(binary_expr_id);
     message_.set_new_operator(static_cast<uint32_t>(new_operator));
 }
@@ -290,7 +290,7 @@
 bool MutationChangeBinaryOperator::CanReplaceBinaryOperator(
     const Program& program,
     const ast::BinaryExpression& binary_expr,
-    ast::BinaryOp new_operator) {
+    core::BinaryOp new_operator) {
     if (new_operator == binary_expr.op) {
         // An operator should not be replaced with itself, as this would be a no-op.
         return false;
@@ -307,33 +307,32 @@
         rhs_type->Is<type::Reference>() ? rhs_type->As<type::Reference>()->StoreType() : rhs_type;
 
     switch (binary_expr.op) {
-        case ast::BinaryOp::kAdd:
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
             return CanReplaceAddSubtractWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             return CanReplaceMultiplyWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kDivide:
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
             return CanReplaceDivideOrModuloWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kAnd:
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
             return CanReplaceAndOrWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             return CanReplaceXorWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kShiftLeft:
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return CanReplaceShiftLeftShiftRightWith(lhs_basic_type, rhs_basic_type, new_operator);
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr:
             return CanReplaceLogicalAndLogicalOrWith(new_operator);
-        case ast::BinaryOp::kEqual:
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
             return CanReplaceEqualNotEqualWith(lhs_basic_type, new_operator);
-        case ast::BinaryOp::kLessThan:
-        case ast::BinaryOp::kLessThanEqual:
-        case ast::BinaryOp::kGreaterThan:
-        case ast::BinaryOp::kGreaterThanEqual:
-        case ast::BinaryOp::kNone:
+        case core::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThanEqual:
             return CanReplaceLessThanLessThanEqualGreaterThanGreaterThanEqualWith(new_operator);
             assert(false && "Unreachable");
             return false;
@@ -350,7 +349,7 @@
         return false;
     }
     // Check whether the replacement is acceptable.
-    const auto new_operator = static_cast<ast::BinaryOp>(message_.new_operator());
+    const auto new_operator = static_cast<core::BinaryOp>(message_.new_operator());
     return CanReplaceBinaryOperator(program, *binary_expr_node, new_operator);
 }
 
@@ -363,90 +362,87 @@
 
     // Clone the binary expression, with the appropriate new operator.
     const ast::BinaryExpression* cloned_replacement;
-    switch (static_cast<ast::BinaryOp>(message_.new_operator())) {
-        case ast::BinaryOp::kAnd:
+    switch (static_cast<core::BinaryOp>(message_.new_operator())) {
+        case core::BinaryOp::kAnd:
             cloned_replacement = clone_context.dst->And(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             cloned_replacement = clone_context.dst->Or(clone_context.Clone(binary_expr_node->lhs),
                                                        clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             cloned_replacement = clone_context.dst->Xor(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalAnd:
             cloned_replacement =
                 clone_context.dst->LogicalAnd(clone_context.Clone(binary_expr_node->lhs),
                                               clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalOr:
             cloned_replacement =
                 clone_context.dst->LogicalOr(clone_context.Clone(binary_expr_node->lhs),
                                              clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             cloned_replacement =
                 clone_context.dst->Equal(clone_context.Clone(binary_expr_node->lhs),
                                          clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             cloned_replacement =
                 clone_context.dst->NotEqual(clone_context.Clone(binary_expr_node->lhs),
                                             clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             cloned_replacement =
                 clone_context.dst->LessThan(clone_context.Clone(binary_expr_node->lhs),
                                             clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             cloned_replacement =
                 clone_context.dst->GreaterThan(clone_context.Clone(binary_expr_node->lhs),
                                                clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             cloned_replacement =
                 clone_context.dst->LessThanEqual(clone_context.Clone(binary_expr_node->lhs),
                                                  clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             cloned_replacement =
                 clone_context.dst->GreaterThanEqual(clone_context.Clone(binary_expr_node->lhs),
                                                     clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             cloned_replacement = clone_context.dst->Shl(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             cloned_replacement = clone_context.dst->Shr(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             cloned_replacement = clone_context.dst->Add(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             cloned_replacement = clone_context.dst->Sub(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             cloned_replacement = clone_context.dst->Mul(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             cloned_replacement = clone_context.dst->Div(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             cloned_replacement = clone_context.dst->Mod(clone_context.Clone(binary_expr_node->lhs),
                                                         clone_context.Clone(binary_expr_node->rhs));
             break;
-        case ast::BinaryOp::kNone:
-            cloned_replacement = nullptr;
-            assert(false && "Unreachable");
     }
     // Set things up so that the original binary expression will be replaced with
     // its clone, and update the id mapping.
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h
index 375d49e..d414458 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h
@@ -34,7 +34,7 @@
     /// @param binary_expr_id - the id of a binary expression.
     /// @param new_operator - a new binary operator to replace the one used in the
     /// expression.
-    MutationChangeBinaryOperator(uint32_t binary_expr_id, ast::BinaryOp new_operator);
+    MutationChangeBinaryOperator(uint32_t binary_expr_id, core::BinaryOp new_operator);
 
     /// @copybrief Mutation::IsApplicable
     ///
@@ -67,7 +67,7 @@
     /// @return `true` if and only if the replacement would be well-typed.
     static bool CanReplaceBinaryOperator(const Program& program,
                                          const ast::BinaryExpression& binary_expr,
-                                         ast::BinaryOp new_operator);
+                                         core::BinaryOp new_operator);
 
   private:
     protobufs::MutationChangeBinaryOperator message_;
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator_test.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator_test.cc
index 3c16628..00df1e0 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator_test.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator_test.cc
@@ -29,48 +29,47 @@
 namespace tint::fuzzers::ast_fuzzer {
 namespace {
 
-std::string OpToString(ast::BinaryOp op) {
+std::string OpToString(core::BinaryOp op) {
     switch (op) {
-        case ast::BinaryOp::kNone:
-            assert(false && "Unreachable");
-            return "";
-        case ast::BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             return "&";
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             return "|";
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             return "^";
-        case ast::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalAnd:
             return "&&";
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalOr:
             return "||";
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             return "==";
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             return "!=";
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             return "<";
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             return ">";
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             return "<=";
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             return ">=";
-        case ast::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             return "<<";
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             return ">>";
-        case ast::BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             return "+";
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             return "-";
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             return "*";
-        case ast::BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             return "/";
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             return "%";
     }
+    TINT_UNREACHABLE() << "unhandled BinaryOp: " << op;
+    return "<error>";
 }
 
 TEST(ChangeBinaryOperatorTest, NotApplicable_Simple) {
@@ -99,19 +98,19 @@
     ASSERT_NE(sum_expr_id, 0);
 
     // binary_expr_id is invalid.
-    EXPECT_FALSE(MutationChangeBinaryOperator(0, ast::BinaryOp::kSubtract)
+    EXPECT_FALSE(MutationChangeBinaryOperator(0, core::BinaryOp::kSubtract)
                      .IsApplicable(program, node_id_map));
 
     // binary_expr_id is not a binary expression.
-    EXPECT_FALSE(MutationChangeBinaryOperator(a_var_id, ast::BinaryOp::kSubtract)
+    EXPECT_FALSE(MutationChangeBinaryOperator(a_var_id, core::BinaryOp::kSubtract)
                      .IsApplicable(program, node_id_map));
 
     // new_operator is applicable to the argument types.
-    EXPECT_FALSE(MutationChangeBinaryOperator(0, ast::BinaryOp::kLogicalAnd)
+    EXPECT_FALSE(MutationChangeBinaryOperator(0, core::BinaryOp::kLogicalAnd)
                      .IsApplicable(program, node_id_map));
 
     // new_operator does not have the right result type.
-    EXPECT_FALSE(MutationChangeBinaryOperator(0, ast::BinaryOp::kLessThan)
+    EXPECT_FALSE(MutationChangeBinaryOperator(0, core::BinaryOp::kLessThan)
                      .IsApplicable(program, node_id_map));
 }
 
@@ -138,7 +137,7 @@
     ASSERT_NE(sum_expr_id, 0);
 
     ASSERT_TRUE(MaybeApplyMutation(
-        program, MutationChangeBinaryOperator(sum_expr_id, ast::BinaryOp::kSubtract), node_id_map,
+        program, MutationChangeBinaryOperator(sum_expr_id, core::BinaryOp::kSubtract), node_id_map,
         &program, &node_id_map, nullptr));
     ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
 
@@ -156,31 +155,31 @@
 void CheckMutations(const std::string& lhs_type,
                     const std::string& rhs_type,
                     const std::string& result_type,
-                    ast::BinaryOp original_operator,
-                    const std::unordered_set<ast::BinaryOp>& allowed_replacement_operators) {
+                    core::BinaryOp original_operator,
+                    const std::unordered_set<core::BinaryOp>& allowed_replacement_operators) {
     std::stringstream shader;
     shader << "fn foo(a : " << lhs_type << ", b : " << rhs_type + ") {\n"
            << "  let r : " << result_type << " = (a " + OpToString(original_operator)
            << " b);\n}\n";
 
-    const std::vector<ast::BinaryOp> all_operators = {ast::BinaryOp::kAnd,
-                                                      ast::BinaryOp::kOr,
-                                                      ast::BinaryOp::kXor,
-                                                      ast::BinaryOp::kLogicalAnd,
-                                                      ast::BinaryOp::kLogicalOr,
-                                                      ast::BinaryOp::kEqual,
-                                                      ast::BinaryOp::kNotEqual,
-                                                      ast::BinaryOp::kLessThan,
-                                                      ast::BinaryOp::kGreaterThan,
-                                                      ast::BinaryOp::kLessThanEqual,
-                                                      ast::BinaryOp::kGreaterThanEqual,
-                                                      ast::BinaryOp::kShiftLeft,
-                                                      ast::BinaryOp::kShiftRight,
-                                                      ast::BinaryOp::kAdd,
-                                                      ast::BinaryOp::kSubtract,
-                                                      ast::BinaryOp::kMultiply,
-                                                      ast::BinaryOp::kDivide,
-                                                      ast::BinaryOp::kModulo};
+    const std::vector<core::BinaryOp> all_operators = {core::BinaryOp::kAnd,
+                                                       core::BinaryOp::kOr,
+                                                       core::BinaryOp::kXor,
+                                                       core::BinaryOp::kLogicalAnd,
+                                                       core::BinaryOp::kLogicalOr,
+                                                       core::BinaryOp::kEqual,
+                                                       core::BinaryOp::kNotEqual,
+                                                       core::BinaryOp::kLessThan,
+                                                       core::BinaryOp::kGreaterThan,
+                                                       core::BinaryOp::kLessThanEqual,
+                                                       core::BinaryOp::kGreaterThanEqual,
+                                                       core::BinaryOp::kShiftLeft,
+                                                       core::BinaryOp::kShiftRight,
+                                                       core::BinaryOp::kAdd,
+                                                       core::BinaryOp::kSubtract,
+                                                       core::BinaryOp::kMultiply,
+                                                       core::BinaryOp::kDivide,
+                                                       core::BinaryOp::kModulo};
 
     for (auto new_operator : all_operators) {
         Source::File file("test.wgsl", shader.str());
@@ -229,57 +228,57 @@
 }
 
 TEST(ChangeBinaryOperatorTest, AddSubtract) {
-    for (auto op : {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract}) {
-        const ast::BinaryOp other_op =
-            op == ast::BinaryOp::kAdd ? ast::BinaryOp::kSubtract : ast::BinaryOp::kAdd;
+    for (auto op : {core::BinaryOp::kAdd, core::BinaryOp::kSubtract}) {
+        const core::BinaryOp other_op =
+            op == core::BinaryOp::kAdd ? core::BinaryOp::kSubtract : core::BinaryOp::kAdd;
         for (std::string type : {"i32", "vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
-            CheckMutations(
-                type, type, type, op,
-                {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide, ast::BinaryOp::kModulo,
-                 ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor});
+            CheckMutations(type, type, type, op,
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+                            core::BinaryOp::kXor});
         }
         for (std::string type : {"u32", "vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
             CheckMutations(
                 type, type, type, op,
-                {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide, ast::BinaryOp::kModulo,
-                 ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor,
-                 ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight});
+                {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                 core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+                 core::BinaryOp::kXor, core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight});
         }
         for (std::string type : {"f32", "vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
             CheckMutations(type, type, type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
         }
         for (std::string vector_type : {"vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
             std::string scalar_type = "i32";
             CheckMutations(vector_type, scalar_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
             CheckMutations(scalar_type, vector_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
         }
         for (std::string vector_type : {"vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
             std::string scalar_type = "u32";
             CheckMutations(vector_type, scalar_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
             CheckMutations(scalar_type, vector_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
         }
         for (std::string vector_type : {"vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
             std::string scalar_type = "f32";
             CheckMutations(vector_type, scalar_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
             CheckMutations(scalar_type, vector_type, vector_type, op,
-                           {other_op, ast::BinaryOp::kMultiply, ast::BinaryOp::kDivide,
-                            ast::BinaryOp::kModulo});
+                           {other_op, core::BinaryOp::kMultiply, core::BinaryOp::kDivide,
+                            core::BinaryOp::kModulo});
         }
         for (std::string square_matrix_type : {"mat2x2<f32>", "mat3x3<f32>", "mat4x4<f32>"}) {
             CheckMutations(square_matrix_type, square_matrix_type, square_matrix_type, op,
-                           {other_op, ast::BinaryOp::kMultiply});
+                           {other_op, core::BinaryOp::kMultiply});
         }
         for (std::string non_square_matrix_type : {"mat2x3<f32>", "mat2x4<f32>", "mat3x2<f32>",
                                                    "mat3x4<f32>", "mat4x2<f32>", "mat4x3<f32>"}) {
@@ -291,222 +290,223 @@
 
 TEST(ChangeBinaryOperatorTest, Mul) {
     for (std::string type : {"i32", "vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
-        CheckMutations(
-            type, type, type, ast::BinaryOp::kMultiply,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-             ast::BinaryOp::kModulo, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor});
+        CheckMutations(type, type, type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+                        core::BinaryOp::kXor});
     }
     for (std::string type : {"u32", "vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         CheckMutations(
-            type, type, type, ast::BinaryOp::kMultiply,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-             ast::BinaryOp::kModulo, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor,
-             ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight});
+            type, type, type, core::BinaryOp::kMultiply,
+            {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+             core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+             core::BinaryOp::kXor, core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight});
     }
     for (std::string type : {"f32", "vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
-        CheckMutations(type, type, type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(type, type, type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
         std::string scalar_type = "i32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         std::string scalar_type = "u32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
         std::string scalar_type = "f32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kMultiply,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kDivide,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kDivide,
+                        core::BinaryOp::kModulo});
     }
     for (std::string square_matrix_type : {"mat2x2<f32>", "mat3x3<f32>", "mat4x4<f32>"}) {
         CheckMutations(square_matrix_type, square_matrix_type, square_matrix_type,
-                       ast::BinaryOp::kMultiply, {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract});
+                       core::BinaryOp::kMultiply,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract});
     }
 
-    CheckMutations("vec2<f32>", "mat2x2<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec2<f32>", "mat3x2<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec2<f32>", "mat4x2<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("vec2<f32>", "mat2x2<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec2<f32>", "mat3x2<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec2<f32>", "mat4x2<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat2x2<f32>", "vec2<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x2<f32>", "mat3x2<f32>", "mat3x2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x2<f32>", "mat4x2<f32>", "mat4x2<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x2<f32>", "vec2<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x2<f32>", "mat3x2<f32>", "mat3x2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x2<f32>", "mat4x2<f32>", "mat4x2<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat2x3<f32>", "vec2<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x3<f32>", "mat2x2<f32>", "mat2x3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x3<f32>", "mat3x2<f32>", "mat3x3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x3<f32>", "mat4x2<f32>", "mat4x3<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x3<f32>", "vec2<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x3<f32>", "mat2x2<f32>", "mat2x3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x3<f32>", "mat3x2<f32>", "mat3x3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x3<f32>", "mat4x2<f32>", "mat4x3<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat2x4<f32>", "vec2<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x4<f32>", "mat2x2<f32>", "mat2x4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x4<f32>", "mat3x2<f32>", "mat3x4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat2x4<f32>", "mat4x2<f32>", "mat4x4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x4<f32>", "vec2<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x4<f32>", "mat2x2<f32>", "mat2x4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x4<f32>", "mat3x2<f32>", "mat3x4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat2x4<f32>", "mat4x2<f32>", "mat4x4<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("vec3<f32>", "mat2x3<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec3<f32>", "mat3x3<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec3<f32>", "mat4x3<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("vec3<f32>", "mat2x3<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec3<f32>", "mat3x3<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec3<f32>", "mat4x3<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat3x2<f32>", "vec3<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x2<f32>", "mat2x3<f32>", "mat2x2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x2<f32>", "mat3x3<f32>", "mat3x2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x2<f32>", "mat4x3<f32>", "mat4x2<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x2<f32>", "vec3<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x2<f32>", "mat2x3<f32>", "mat2x2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x2<f32>", "mat3x3<f32>", "mat3x2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x2<f32>", "mat4x3<f32>", "mat4x2<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat3x3<f32>", "vec3<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x3<f32>", "mat2x3<f32>", "mat2x3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x3<f32>", "mat4x3<f32>", "mat4x3<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x3<f32>", "vec3<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x3<f32>", "mat2x3<f32>", "mat2x3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x3<f32>", "mat4x3<f32>", "mat4x3<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat3x4<f32>", "vec3<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x4<f32>", "mat2x3<f32>", "mat2x4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x4<f32>", "mat3x3<f32>", "mat3x4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat3x4<f32>", "mat4x3<f32>", "mat4x4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x4<f32>", "vec3<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x4<f32>", "mat2x3<f32>", "mat2x4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x4<f32>", "mat3x3<f32>", "mat3x4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat3x4<f32>", "mat4x3<f32>", "mat4x4<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("vec4<f32>", "mat2x4<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec4<f32>", "mat3x4<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("vec4<f32>", "mat4x4<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("vec4<f32>", "mat2x4<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec4<f32>", "mat3x4<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("vec4<f32>", "mat4x4<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat4x2<f32>", "vec4<f32>", "vec2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x2<f32>", "mat2x4<f32>", "mat2x2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x2<f32>", "mat3x4<f32>", "mat3x2<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x2<f32>", "mat4x4<f32>", "mat4x2<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x2<f32>", "vec4<f32>", "vec2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x2<f32>", "mat2x4<f32>", "mat2x2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x2<f32>", "mat3x4<f32>", "mat3x2<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x2<f32>", "mat4x4<f32>", "mat4x2<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat4x3<f32>", "vec4<f32>", "vec3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x3<f32>", "mat2x4<f32>", "mat2x3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x3<f32>", "mat3x4<f32>", "mat3x3<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x3<f32>", "mat4x4<f32>", "mat4x3<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x3<f32>", "vec4<f32>", "vec3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x3<f32>", "mat2x4<f32>", "mat2x3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x3<f32>", "mat3x4<f32>", "mat3x3<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x3<f32>", "mat4x4<f32>", "mat4x3<f32>", core::BinaryOp::kMultiply, {});
 
-    CheckMutations("mat4x4<f32>", "vec4<f32>", "vec4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x4<f32>", "mat2x4<f32>", "mat2x4<f32>", ast::BinaryOp::kMultiply, {});
-    CheckMutations("mat4x4<f32>", "mat3x4<f32>", "mat3x4<f32>", ast::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x4<f32>", "vec4<f32>", "vec4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x4<f32>", "mat2x4<f32>", "mat2x4<f32>", core::BinaryOp::kMultiply, {});
+    CheckMutations("mat4x4<f32>", "mat3x4<f32>", "mat3x4<f32>", core::BinaryOp::kMultiply, {});
 }
 
 TEST(ChangeBinaryOperatorTest, DivideAndModulo) {
     for (std::string type : {"i32", "vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
-        CheckMutations(
-            type, type, type, ast::BinaryOp::kDivide,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-             ast::BinaryOp::kModulo, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor});
+        CheckMutations(type, type, type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+                        core::BinaryOp::kXor});
     }
     for (std::string type : {"u32", "vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         CheckMutations(
-            type, type, type, ast::BinaryOp::kDivide,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-             ast::BinaryOp::kModulo, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor,
-             ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight});
+            type, type, type, core::BinaryOp::kDivide,
+            {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+             core::BinaryOp::kModulo, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+             core::BinaryOp::kXor, core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight});
     }
     for (std::string type : {"f32", "vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
-        CheckMutations(type, type, type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(type, type, type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
         std::string scalar_type = "i32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         std::string scalar_type = "u32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
     }
     for (std::string vector_type : {"vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
         std::string scalar_type = "f32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kDivide,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kModulo});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kDivide,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kModulo});
     }
     for (std::string type : {"i32", "vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
-        CheckMutations(
-            type, type, type, ast::BinaryOp::kModulo,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-             ast::BinaryOp::kDivide, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor});
+        CheckMutations(type, type, type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+                        core::BinaryOp::kXor});
     }
     for (std::string type : {"u32", "vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         CheckMutations(
-            type, type, type, ast::BinaryOp::kModulo,
-            {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-             ast::BinaryOp::kDivide, ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor,
-             ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight});
+            type, type, type, core::BinaryOp::kModulo,
+            {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+             core::BinaryOp::kDivide, core::BinaryOp::kAnd, core::BinaryOp::kOr,
+             core::BinaryOp::kXor, core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight});
     }
     for (std::string type : {"f32", "vec2<f32>", "vec3<f32>", "vec4<f32>"}) {
-        CheckMutations(type, type, type, ast::BinaryOp::kModulo,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kDivide});
+        CheckMutations(type, type, type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide});
     }
     for (std::string vector_type : {"vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
         std::string scalar_type = "i32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kModulo,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kDivide});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kModulo,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kDivide});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide});
     }
     for (std::string vector_type : {"vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
         std::string scalar_type = "u32";
-        CheckMutations(vector_type, scalar_type, vector_type, ast::BinaryOp::kModulo,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kDivide});
-        CheckMutations(scalar_type, vector_type, vector_type, ast::BinaryOp::kModulo,
-                       {ast::BinaryOp::kAdd, ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-                        ast::BinaryOp::kDivide});
+        CheckMutations(vector_type, scalar_type, vector_type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide});
+        CheckMutations(scalar_type, vector_type, vector_type, core::BinaryOp::kModulo,
+                       {core::BinaryOp::kAdd, core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+                        core::BinaryOp::kDivide});
     }
 }
 
 TEST(ChangeBinaryOperatorTest, AndOrXor) {
-    for (auto op : {ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kXor}) {
-        std::unordered_set<ast::BinaryOp> allowed_replacement_operators_signed{
-            ast::BinaryOp::kAdd,    ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-            ast::BinaryOp::kDivide, ast::BinaryOp::kModulo,   ast::BinaryOp::kAnd,
-            ast::BinaryOp::kOr,     ast::BinaryOp::kXor};
+    for (auto op : {core::BinaryOp::kAnd, core::BinaryOp::kOr, core::BinaryOp::kXor}) {
+        std::unordered_set<core::BinaryOp> allowed_replacement_operators_signed{
+            core::BinaryOp::kAdd,    core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+            core::BinaryOp::kDivide, core::BinaryOp::kModulo,   core::BinaryOp::kAnd,
+            core::BinaryOp::kOr,     core::BinaryOp::kXor};
         allowed_replacement_operators_signed.erase(op);
         for (std::string type : {"i32", "vec2<i32>", "vec3<i32>", "vec4<i32>"}) {
             CheckMutations(type, type, type, op, allowed_replacement_operators_signed);
         }
-        std::unordered_set<ast::BinaryOp> allowed_replacement_operators_unsigned{
-            ast::BinaryOp::kAdd,        ast::BinaryOp::kSubtract, ast::BinaryOp::kMultiply,
-            ast::BinaryOp::kDivide,     ast::BinaryOp::kModulo,   ast::BinaryOp::kShiftLeft,
-            ast::BinaryOp::kShiftRight, ast::BinaryOp::kAnd,      ast::BinaryOp::kOr,
-            ast::BinaryOp::kXor};
+        std::unordered_set<core::BinaryOp> allowed_replacement_operators_unsigned{
+            core::BinaryOp::kAdd,        core::BinaryOp::kSubtract, core::BinaryOp::kMultiply,
+            core::BinaryOp::kDivide,     core::BinaryOp::kModulo,   core::BinaryOp::kShiftLeft,
+            core::BinaryOp::kShiftRight, core::BinaryOp::kAnd,      core::BinaryOp::kOr,
+            core::BinaryOp::kXor};
         allowed_replacement_operators_unsigned.erase(op);
         for (std::string type : {"u32", "vec2<u32>", "vec3<u32>", "vec4<u32>"}) {
             CheckMutations(type, type, type, op, allowed_replacement_operators_unsigned);
         }
-        if (op != ast::BinaryOp::kXor) {
+        if (op != core::BinaryOp::kXor) {
             for (std::string type : {"bool", "vec2<bool>", "vec3<bool>", "vec4<bool>"}) {
-                std::unordered_set<ast::BinaryOp> allowed_replacement_operators_bool{
-                    ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kEqual,
-                    ast::BinaryOp::kNotEqual};
+                std::unordered_set<core::BinaryOp> allowed_replacement_operators_bool{
+                    core::BinaryOp::kAnd, core::BinaryOp::kOr, core::BinaryOp::kEqual,
+                    core::BinaryOp::kNotEqual};
                 allowed_replacement_operators_bool.erase(op);
                 if (type == "bool") {
-                    allowed_replacement_operators_bool.insert(ast::BinaryOp::kLogicalAnd);
-                    allowed_replacement_operators_bool.insert(ast::BinaryOp::kLogicalOr);
+                    allowed_replacement_operators_bool.insert(core::BinaryOp::kLogicalAnd);
+                    allowed_replacement_operators_bool.insert(core::BinaryOp::kLogicalOr);
                 }
                 CheckMutations(type, type, type, op, allowed_replacement_operators_bool);
             }
@@ -515,7 +515,7 @@
 }
 
 TEST(ChangeBinaryOperatorTest, EqualNotEqual) {
-    for (auto op : {ast::BinaryOp::kEqual, ast::BinaryOp::kNotEqual}) {
+    for (auto op : {core::BinaryOp::kEqual, core::BinaryOp::kNotEqual}) {
         for (std::string element_type : {"i32", "u32", "f32"}) {
             for (size_t element_count = 1; element_count <= 4; element_count++) {
                 std::stringstream argument_type;
@@ -527,28 +527,28 @@
                     argument_type << "vec" << element_count << "<" << element_type << ">";
                     result_type << "vec" << element_count << "<bool>";
                 }
-                std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-                    ast::BinaryOp::kLessThan,    ast::BinaryOp::kLessThanEqual,
-                    ast::BinaryOp::kGreaterThan, ast::BinaryOp::kGreaterThanEqual,
-                    ast::BinaryOp::kEqual,       ast::BinaryOp::kNotEqual};
+                std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+                    core::BinaryOp::kLessThan,    core::BinaryOp::kLessThanEqual,
+                    core::BinaryOp::kGreaterThan, core::BinaryOp::kGreaterThanEqual,
+                    core::BinaryOp::kEqual,       core::BinaryOp::kNotEqual};
                 allowed_replacement_operators.erase(op);
                 CheckMutations(argument_type.str(), argument_type.str(), result_type.str(), op,
                                allowed_replacement_operators);
             }
         }
         {
-            std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-                ast::BinaryOp::kLogicalAnd, ast::BinaryOp::kLogicalOr, ast::BinaryOp::kAnd,
-                ast::BinaryOp::kOr,         ast::BinaryOp::kEqual,     ast::BinaryOp::kNotEqual};
+            std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+                core::BinaryOp::kLogicalAnd, core::BinaryOp::kLogicalOr, core::BinaryOp::kAnd,
+                core::BinaryOp::kOr,         core::BinaryOp::kEqual,     core::BinaryOp::kNotEqual};
             allowed_replacement_operators.erase(op);
             CheckMutations("bool", "bool", "bool", op, allowed_replacement_operators);
         }
         for (size_t element_count = 2; element_count <= 4; element_count++) {
             std::stringstream argument_and_result_type;
             argument_and_result_type << "vec" << element_count << "<bool>";
-            std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-                ast::BinaryOp::kAnd, ast::BinaryOp::kOr, ast::BinaryOp::kEqual,
-                ast::BinaryOp::kNotEqual};
+            std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+                core::BinaryOp::kAnd, core::BinaryOp::kOr, core::BinaryOp::kEqual,
+                core::BinaryOp::kNotEqual};
             allowed_replacement_operators.erase(op);
             CheckMutations(argument_and_result_type.str(), argument_and_result_type.str(),
                            argument_and_result_type.str(), op, allowed_replacement_operators);
@@ -557,8 +557,8 @@
 }
 
 TEST(ChangeBinaryOperatorTest, LessThanLessThanEqualGreaterThanGreaterThanEqual) {
-    for (auto op : {ast::BinaryOp::kLessThan, ast::BinaryOp::kLessThanEqual,
-                    ast::BinaryOp::kGreaterThan, ast::BinaryOp::kGreaterThanEqual}) {
+    for (auto op : {core::BinaryOp::kLessThan, core::BinaryOp::kLessThanEqual,
+                    core::BinaryOp::kGreaterThan, core::BinaryOp::kGreaterThanEqual}) {
         for (std::string element_type : {"i32", "u32", "f32"}) {
             for (size_t element_count = 1; element_count <= 4; element_count++) {
                 std::stringstream argument_type;
@@ -570,10 +570,10 @@
                     argument_type << "vec" << element_count << "<" << element_type << ">";
                     result_type << "vec" << element_count << "<bool>";
                 }
-                std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-                    ast::BinaryOp::kLessThan,    ast::BinaryOp::kLessThanEqual,
-                    ast::BinaryOp::kGreaterThan, ast::BinaryOp::kGreaterThanEqual,
-                    ast::BinaryOp::kEqual,       ast::BinaryOp::kNotEqual};
+                std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+                    core::BinaryOp::kLessThan,    core::BinaryOp::kLessThanEqual,
+                    core::BinaryOp::kGreaterThan, core::BinaryOp::kGreaterThanEqual,
+                    core::BinaryOp::kEqual,       core::BinaryOp::kNotEqual};
                 allowed_replacement_operators.erase(op);
                 CheckMutations(argument_type.str(), argument_type.str(), result_type.str(), op,
                                allowed_replacement_operators);
@@ -583,17 +583,17 @@
 }
 
 TEST(ChangeBinaryOperatorTest, LogicalAndLogicalOr) {
-    for (auto op : {ast::BinaryOp::kLogicalAnd, ast::BinaryOp::kLogicalOr}) {
-        std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-            ast::BinaryOp::kLogicalAnd, ast::BinaryOp::kLogicalOr, ast::BinaryOp::kAnd,
-            ast::BinaryOp::kOr,         ast::BinaryOp::kEqual,     ast::BinaryOp::kNotEqual};
+    for (auto op : {core::BinaryOp::kLogicalAnd, core::BinaryOp::kLogicalOr}) {
+        std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+            core::BinaryOp::kLogicalAnd, core::BinaryOp::kLogicalOr, core::BinaryOp::kAnd,
+            core::BinaryOp::kOr,         core::BinaryOp::kEqual,     core::BinaryOp::kNotEqual};
         allowed_replacement_operators.erase(op);
         CheckMutations("bool", "bool", "bool", op, allowed_replacement_operators);
     }
 }
 
 TEST(ChangeBinaryOperatorTest, ShiftLeftShiftRight) {
-    for (auto op : {ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight}) {
+    for (auto op : {core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight}) {
         for (std::string lhs_element_type : {"i32", "u32"}) {
             for (size_t element_count = 1; element_count <= 4; element_count++) {
                 std::stringstream lhs_and_result_type;
@@ -605,18 +605,18 @@
                     lhs_and_result_type << "vec" << element_count << "<" << lhs_element_type << ">";
                     rhs_type << "vec" << element_count << "<u32>";
                 }
-                std::unordered_set<ast::BinaryOp> allowed_replacement_operators{
-                    ast::BinaryOp::kShiftLeft, ast::BinaryOp::kShiftRight};
+                std::unordered_set<core::BinaryOp> allowed_replacement_operators{
+                    core::BinaryOp::kShiftLeft, core::BinaryOp::kShiftRight};
                 allowed_replacement_operators.erase(op);
                 if (lhs_element_type == "u32") {
-                    allowed_replacement_operators.insert(ast::BinaryOp::kAdd);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kSubtract);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kMultiply);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kDivide);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kModulo);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kAnd);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kOr);
-                    allowed_replacement_operators.insert(ast::BinaryOp::kXor);
+                    allowed_replacement_operators.insert(core::BinaryOp::kAdd);
+                    allowed_replacement_operators.insert(core::BinaryOp::kSubtract);
+                    allowed_replacement_operators.insert(core::BinaryOp::kMultiply);
+                    allowed_replacement_operators.insert(core::BinaryOp::kDivide);
+                    allowed_replacement_operators.insert(core::BinaryOp::kModulo);
+                    allowed_replacement_operators.insert(core::BinaryOp::kAnd);
+                    allowed_replacement_operators.insert(core::BinaryOp::kOr);
+                    allowed_replacement_operators.insert(core::BinaryOp::kXor);
                 }
                 CheckMutations(lhs_and_result_type.str(), rhs_type.str(), lhs_and_result_type.str(),
                                op, allowed_replacement_operators);
diff --git a/src/tint/lang/core/binary_op.cc b/src/tint/lang/core/binary_op.cc
new file mode 100644
index 0000000..86cc638
--- /dev/null
+++ b/src/tint/lang/core/binary_op.cc
@@ -0,0 +1,61 @@
+// Copyright 2023 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/tint/lang/core/binary_op.h"
+
+namespace tint::core {
+
+std::string_view ToString(BinaryOp value) {
+    switch (value) {
+        case BinaryOp::kAnd:
+            return "&";
+        case BinaryOp::kOr:
+            return "|";
+        case BinaryOp::kXor:
+            return "^";
+        case BinaryOp::kLogicalAnd:
+            return "&&";
+        case BinaryOp::kLogicalOr:
+            return "||";
+        case BinaryOp::kEqual:
+            return "==";
+        case BinaryOp::kNotEqual:
+            return "!=";
+        case BinaryOp::kLessThan:
+            return "<";
+        case BinaryOp::kGreaterThan:
+            return ">";
+        case BinaryOp::kLessThanEqual:
+            return "<=";
+        case BinaryOp::kGreaterThanEqual:
+            return ">=";
+        case BinaryOp::kShiftLeft:
+            return "<<";
+        case BinaryOp::kShiftRight:
+            return ">>";
+        case BinaryOp::kAdd:
+            return "+";
+        case BinaryOp::kSubtract:
+            return "-";
+        case BinaryOp::kMultiply:
+            return "*";
+        case BinaryOp::kDivide:
+            return "/";
+        case BinaryOp::kModulo:
+            return "%";
+    }
+    return "<unknown>";
+}
+
+}  // namespace tint::core
diff --git a/src/tint/lang/core/binary_op.h b/src/tint/lang/core/binary_op.h
new file mode 100644
index 0000000..f8db628
--- /dev/null
+++ b/src/tint/lang/core/binary_op.h
@@ -0,0 +1,58 @@
+// Copyright 2023 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_TINT_LANG_CORE_BINARY_OP_H_
+#define SRC_TINT_LANG_CORE_BINARY_OP_H_
+
+#include "src/tint/utils/traits/traits.h"
+
+namespace tint::core {
+
+/// An enumerator of binary operators.
+enum class BinaryOp {
+    kAnd,  // &
+    kOr,   // |
+    kXor,
+    kLogicalAnd,  // &&
+    kLogicalOr,   // ||
+    kEqual,
+    kNotEqual,
+    kLessThan,
+    kGreaterThan,
+    kLessThanEqual,
+    kGreaterThanEqual,
+    kShiftLeft,
+    kShiftRight,
+    kAdd,
+    kSubtract,
+    kMultiply,
+    kDivide,
+    kModulo,
+};
+
+/// @param value the enum value
+/// @returns the string for the given enum value
+std::string_view ToString(BinaryOp value);
+
+/// @param out the stream to write to
+/// @param value the BinaryOp
+/// @returns @p out so calls can be chained
+template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
+auto& operator<<(STREAM& out, BinaryOp value) {
+    return out << ToString(value);
+}
+
+}  // namespace tint::core
+
+#endif  // SRC_TINT_LANG_CORE_BINARY_OP_H_
diff --git a/src/tint/lang/core/intrinsic/table.cc b/src/tint/lang/core/intrinsic/table.cc
index 9dbfa05..1813157 100644
--- a/src/tint/lang/core/intrinsic/table.cc
+++ b/src/tint/lang/core/intrinsic/table.cc
@@ -966,7 +966,7 @@
                          sem::EvaluationStage earliest_eval_stage,
                          const Source& source) override;
 
-    BinaryOperator Lookup(ast::BinaryOp op,
+    BinaryOperator Lookup(core::BinaryOp op,
                           const type::Type* lhs,
                           const type::Type* rhs,
                           sem::EvaluationStage earliest_eval_stage,
@@ -1224,7 +1224,7 @@
     };
 }
 
-Table::BinaryOperator Impl::Lookup(ast::BinaryOp op,
+Table::BinaryOperator Impl::Lookup(core::BinaryOp op,
                                    const type::Type* lhs,
                                    const type::Type* rhs,
                                    sem::EvaluationStage earliest_eval_stage,
@@ -1232,45 +1232,45 @@
                                    bool is_compound) {
     auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<size_t, const char*> {
         switch (op) {
-            case ast::BinaryOp::kAnd:
+            case core::BinaryOp::kAnd:
                 return {kBinaryOperatorAnd, is_compound ? "operator &= " : "operator & "};
-            case ast::BinaryOp::kOr:
+            case core::BinaryOp::kOr:
                 return {kBinaryOperatorOr, is_compound ? "operator |= " : "operator | "};
-            case ast::BinaryOp::kXor:
+            case core::BinaryOp::kXor:
                 return {kBinaryOperatorXor, is_compound ? "operator ^= " : "operator ^ "};
-            case ast::BinaryOp::kLogicalAnd:
+            case core::BinaryOp::kLogicalAnd:
                 return {kBinaryOperatorLogicalAnd, "operator && "};
-            case ast::BinaryOp::kLogicalOr:
+            case core::BinaryOp::kLogicalOr:
                 return {kBinaryOperatorLogicalOr, "operator || "};
-            case ast::BinaryOp::kEqual:
+            case core::BinaryOp::kEqual:
                 return {kBinaryOperatorEqual, "operator == "};
-            case ast::BinaryOp::kNotEqual:
+            case core::BinaryOp::kNotEqual:
                 return {kBinaryOperatorNotEqual, "operator != "};
-            case ast::BinaryOp::kLessThan:
+            case core::BinaryOp::kLessThan:
                 return {kBinaryOperatorLessThan, "operator < "};
-            case ast::BinaryOp::kGreaterThan:
+            case core::BinaryOp::kGreaterThan:
                 return {kBinaryOperatorGreaterThan, "operator > "};
-            case ast::BinaryOp::kLessThanEqual:
+            case core::BinaryOp::kLessThanEqual:
                 return {kBinaryOperatorLessThanEqual, "operator <= "};
-            case ast::BinaryOp::kGreaterThanEqual:
+            case core::BinaryOp::kGreaterThanEqual:
                 return {kBinaryOperatorGreaterThanEqual, "operator >= "};
-            case ast::BinaryOp::kShiftLeft:
+            case core::BinaryOp::kShiftLeft:
                 return {kBinaryOperatorShiftLeft, is_compound ? "operator <<= " : "operator << "};
-            case ast::BinaryOp::kShiftRight:
+            case core::BinaryOp::kShiftRight:
                 return {kBinaryOperatorShiftRight, is_compound ? "operator >>= " : "operator >> "};
-            case ast::BinaryOp::kAdd:
+            case core::BinaryOp::kAdd:
                 return {kBinaryOperatorPlus, is_compound ? "operator += " : "operator + "};
-            case ast::BinaryOp::kSubtract:
+            case core::BinaryOp::kSubtract:
                 return {kBinaryOperatorMinus, is_compound ? "operator -= " : "operator - "};
-            case ast::BinaryOp::kMultiply:
+            case core::BinaryOp::kMultiply:
                 return {kBinaryOperatorStar, is_compound ? "operator *= " : "operator * "};
-            case ast::BinaryOp::kDivide:
+            case core::BinaryOp::kDivide:
                 return {kBinaryOperatorDivide, is_compound ? "operator /= " : "operator / "};
-            case ast::BinaryOp::kModulo:
+            case core::BinaryOp::kModulo:
                 return {kBinaryOperatorModulo, is_compound ? "operator %= " : "operator % "};
-            default:
-                return {0, "<unknown>"};
         }
+        TINT_UNREACHABLE() << "unhandled BinaryOp: " << op;
+        return {};
     }();
 
     Vector args{lhs, rhs};
diff --git a/src/tint/lang/core/intrinsic/table.h b/src/tint/lang/core/intrinsic/table.h
index 7e07149..a731f5c 100644
--- a/src/tint/lang/core/intrinsic/table.h
+++ b/src/tint/lang/core/intrinsic/table.h
@@ -130,7 +130,7 @@
     /// @param is_compound true if the binary operator is being used as a compound assignment
     /// @return the operator call target signature. If the operator was not found
     ///         BinaryOperator::result will be nullptr.
-    virtual BinaryOperator Lookup(ast::BinaryOp op,
+    virtual BinaryOperator Lookup(core::BinaryOp op,
                                   const type::Type* lhs,
                                   const type::Type* rhs,
                                   sem::EvaluationStage earliest_eval_stage,
diff --git a/src/tint/lang/core/intrinsic/table_test.cc b/src/tint/lang/core/intrinsic/table_test.cc
index eb0d44c..4ce80eb 100644
--- a/src/tint/lang/core/intrinsic/table_test.cc
+++ b/src/tint/lang/core/intrinsic/table_test.cc
@@ -566,8 +566,8 @@
 TEST_F(IntrinsicTableTest, MatchDifferentArgsElementType_Binary_ConstantEval) {
     auto* ai = create<type::AbstractInt>();
     auto* u32 = create<type::U32>();
-    auto result = table->Lookup(ast::BinaryOp::kShiftLeft, ai, u32, sem::EvaluationStage::kConstant,
-                                Source{}, false);
+    auto result = table->Lookup(core::BinaryOp::kShiftLeft, ai, u32,
+                                sem::EvaluationStage::kConstant, Source{}, false);
     ASSERT_NE(result.result, nullptr) << Diagnostics().str();
     ASSERT_NE(result.const_eval_fn, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
@@ -579,7 +579,7 @@
 TEST_F(IntrinsicTableTest, MatchDifferentArgsElementType_Binary_RuntimeEval) {
     auto* ai = create<type::AbstractInt>();
     auto* u32 = create<type::U32>();
-    auto result = table->Lookup(ast::BinaryOp::kShiftLeft, ai, u32, sem::EvaluationStage::kRuntime,
+    auto result = table->Lookup(core::BinaryOp::kShiftLeft, ai, u32, sem::EvaluationStage::kRuntime,
                                 Source{}, false);
     ASSERT_NE(result.result, nullptr) << Diagnostics().str();
     ASSERT_NE(result.const_eval_fn, nullptr) << Diagnostics().str();
@@ -733,7 +733,7 @@
 TEST_F(IntrinsicTableTest, MatchBinaryOp) {
     auto* i32 = create<type::I32>();
     auto* vec3_i32 = create<type::Vector>(i32, 3u);
-    auto result = table->Lookup(ast::BinaryOp::kMultiply, i32, vec3_i32,
+    auto result = table->Lookup(core::BinaryOp::kMultiply, i32, vec3_i32,
                                 sem::EvaluationStage::kConstant, Source{{12, 34}},
                                 /* is_compound */ false);
     EXPECT_EQ(result.result, vec3_i32);
@@ -745,7 +745,7 @@
 TEST_F(IntrinsicTableTest, MismatchBinaryOp) {
     auto* f32 = create<type::F32>();
     auto* bool_ = create<type::Bool>();
-    auto result = table->Lookup(ast::BinaryOp::kMultiply, f32, bool_,
+    auto result = table->Lookup(core::BinaryOp::kMultiply, f32, bool_,
                                 sem::EvaluationStage::kConstant, Source{{12, 34}},
                                 /* is_compound */ false);
     ASSERT_EQ(result.result, nullptr);
@@ -767,7 +767,7 @@
 TEST_F(IntrinsicTableTest, MatchCompoundOp) {
     auto* i32 = create<type::I32>();
     auto* vec3_i32 = create<type::Vector>(i32, 3u);
-    auto result = table->Lookup(ast::BinaryOp::kMultiply, i32, vec3_i32,
+    auto result = table->Lookup(core::BinaryOp::kMultiply, i32, vec3_i32,
                                 sem::EvaluationStage::kConstant, Source{{12, 34}},
                                 /* is_compound */ true);
     EXPECT_EQ(result.result, vec3_i32);
@@ -779,7 +779,7 @@
 TEST_F(IntrinsicTableTest, MismatchCompoundOp) {
     auto* f32 = create<type::F32>();
     auto* bool_ = create<type::Bool>();
-    auto result = table->Lookup(ast::BinaryOp::kMultiply, f32, bool_,
+    auto result = table->Lookup(core::BinaryOp::kMultiply, f32, bool_,
                                 sem::EvaluationStage::kConstant, Source{{12, 34}},
                                 /* is_compound */ true);
     ASSERT_EQ(result.result, nullptr);
@@ -1080,7 +1080,7 @@
 TEST_P(IntrinsicTableAbstractBinaryTest, MatchAdd) {
     auto* arg_lhs = GetParam().arg_lhs(*this);
     auto* arg_rhs = GetParam().arg_rhs(*this);
-    auto result = table->Lookup(ast::BinaryOp::kAdd, arg_lhs, arg_rhs,
+    auto result = table->Lookup(core::BinaryOp::kAdd, arg_lhs, arg_rhs,
                                 sem::EvaluationStage::kConstant, Source{{12, 34}},
                                 /* is_compound */ false);
 
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index 31ca84f..93707ed 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -97,11 +97,11 @@
     return tint::IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
-bool IsRelational(tint::ast::BinaryOp op) {
-    return op == tint::ast::BinaryOp::kEqual || op == tint::ast::BinaryOp::kNotEqual ||
-           op == tint::ast::BinaryOp::kLessThan || op == tint::ast::BinaryOp::kGreaterThan ||
-           op == tint::ast::BinaryOp::kLessThanEqual ||
-           op == tint::ast::BinaryOp::kGreaterThanEqual;
+bool IsRelational(tint::core::BinaryOp op) {
+    return op == tint::core::BinaryOp::kEqual || op == tint::core::BinaryOp::kNotEqual ||
+           op == tint::core::BinaryOp::kLessThan || op == tint::core::BinaryOp::kGreaterThan ||
+           op == tint::core::BinaryOp::kLessThanEqual ||
+           op == tint::core::BinaryOp::kGreaterThanEqual;
 }
 
 bool RequiresOESSampleVariables(tint::core::BuiltinValue builtin) {
@@ -525,22 +525,22 @@
 
 void ASTPrinter::EmitVectorRelational(StringStream& out, const ast::BinaryExpression* expr) {
     switch (expr->op) {
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             out << "equal";
             break;
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             out << "notEqual";
             break;
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             out << "lessThan";
             break;
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             out << "greaterThan";
             break;
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             out << "lessThanEqual";
             break;
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             out << "greaterThanEqual";
             break;
         default:
@@ -567,12 +567,12 @@
         EmitExpression(out, expr->lhs);
     }
     // Emit operator.
-    if (expr->op == ast::BinaryOp::kAnd) {
+    if (expr->op == core::BinaryOp::kAnd) {
         out << " & ";
-    } else if (TINT_LIKELY(expr->op == ast::BinaryOp::kOr)) {
+    } else if (TINT_LIKELY(expr->op == core::BinaryOp::kOr)) {
         out << " | ";
     } else {
-        TINT_ICE() << "unexpected binary op: " << FriendlyName(expr->op);
+        TINT_ICE() << "unexpected binary op: " << expr->op;
         return;
     }
 
@@ -638,7 +638,7 @@
         return;
     }
 
-    if (expr->op == ast::BinaryOp::kLogicalAnd || expr->op == ast::BinaryOp::kLogicalOr) {
+    if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
         auto name = UniqueIdentifier(kTempNamePrefix);
 
         {
@@ -648,7 +648,7 @@
             pre << ";";
         }
 
-        if (expr->op == ast::BinaryOp::kLogicalOr) {
+        if (expr->op == core::BinaryOp::kLogicalOr) {
             Line() << "if (!" << name << ") {";
         } else {
             Line() << "if (" << name << ") {";
@@ -668,13 +668,13 @@
         return;
     }
 
-    if ((expr->op == ast::BinaryOp::kAnd || expr->op == ast::BinaryOp::kOr) &&
+    if ((expr->op == core::BinaryOp::kAnd || expr->op == core::BinaryOp::kOr) &&
         TypeOf(expr->lhs)->UnwrapRef()->is_bool_scalar_or_vector()) {
         EmitBitwiseBoolOp(out, expr);
         return;
     }
 
-    if (expr->op == ast::BinaryOp::kModulo &&
+    if (expr->op == core::BinaryOp::kModulo &&
         (TypeOf(expr->lhs)->UnwrapRef()->is_float_scalar_or_vector() ||
          TypeOf(expr->rhs)->UnwrapRef()->is_float_scalar_or_vector())) {
         EmitFloatModulo(out, expr);
@@ -686,64 +686,61 @@
     out << " ";
 
     switch (expr->op) {
-        case ast::BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             out << "&";
             break;
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             out << "|";
             break;
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             out << "^";
             break;
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr: {
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr: {
             // These are both handled above.
             TINT_UNREACHABLE();
             return;
         }
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             out << "==";
             break;
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             out << "!=";
             break;
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             out << "<";
             break;
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             out << ">";
             break;
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             out << "<=";
             break;
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             out << ">=";
             break;
-        case ast::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             out << "<<";
             break;
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             out << R"(>>)";
             break;
 
-        case ast::BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             out << "+";
             break;
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             out << "-";
             break;
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             out << "*";
             break;
-        case ast::BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             out << "/";
             break;
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             out << "%";
             break;
-        case ast::BinaryOp::kNone:
-            diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-            return;
     }
     out << " ";
     EmitExpression(out, expr->rhs);
diff --git a/src/tint/lang/glsl/writer/ast_printer/binary_test.cc b/src/tint/lang/glsl/writer/ast_printer/binary_test.cc
index 08df615..ab4268c 100644
--- a/src/tint/lang/glsl/writer/ast_printer/binary_test.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/binary_test.cc
@@ -29,7 +29,7 @@
 
 struct BinaryData {
     const char* result;
-    ast::BinaryOp op;
+    core::BinaryOp op;
 };
 inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
     StringStream str;
@@ -43,9 +43,9 @@
     auto params = GetParam();
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kAnd || params.op == ast::BinaryOp::kOr ||
-        params.op == ast::BinaryOp::kXor || params.op == ast::BinaryOp::kShiftLeft ||
-        params.op == ast::BinaryOp::kShiftRight || params.op == ast::BinaryOp::kModulo) {
+    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
+        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
+        params.op == core::BinaryOp::kShiftRight || params.op == core::BinaryOp::kModulo) {
         return;
     }
 
@@ -70,9 +70,9 @@
     auto params = GetParam();
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kAnd || params.op == ast::BinaryOp::kOr ||
-        params.op == ast::BinaryOp::kXor || params.op == ast::BinaryOp::kShiftLeft ||
-        params.op == ast::BinaryOp::kShiftRight || params.op == ast::BinaryOp::kModulo) {
+    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
+        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
+        params.op == core::BinaryOp::kShiftRight || params.op == core::BinaryOp::kModulo) {
         return;
     }
 
@@ -119,7 +119,7 @@
     auto params = GetParam();
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kShiftLeft || params.op == ast::BinaryOp::kShiftRight) {
+    if (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight) {
         return;
     }
 
@@ -143,29 +143,29 @@
 INSTANTIATE_TEST_SUITE_P(
     GlslASTPrinterTest,
     GlslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", ast::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", ast::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", ast::BinaryOp::kXor},
-                    BinaryData{"(left == right)", ast::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", ast::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", ast::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", ast::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", ast::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", ast::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", ast::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", ast::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", ast::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", ast::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", ast::BinaryOp::kMultiply},
-                    BinaryData{"(left / right)", ast::BinaryOp::kDivide},
-                    BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
+    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
+                    BinaryData{"(left | right)", core::BinaryOp::kOr},
+                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
+                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
+                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
+                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
+                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
+                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
+                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
+                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
+                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
+                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
+                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
+                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
+                    BinaryData{"(left / right)", core::BinaryOp::kDivide},
+                    BinaryData{"(left % right)", core::BinaryOp::kModulo}));
 
 TEST_F(GlslASTPrinterTest_Binary, Multiply_VectorScalar_f32) {
     GlobalVar("a", Call<vec3<f32>>(1_f, 1_f, 1_f), core::AddressSpace::kPrivate);
     auto* lhs = Expr("a");
     auto* rhs = Expr(1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -184,7 +184,7 @@
     auto* lhs = Expr("a");
     auto* rhs = Expr(1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -201,7 +201,7 @@
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("a");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -220,7 +220,7 @@
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("a");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -237,7 +237,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -255,7 +255,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -271,7 +271,7 @@
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -289,7 +289,7 @@
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -305,7 +305,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -323,7 +323,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -339,7 +339,7 @@
     auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -357,7 +357,7 @@
     auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -372,7 +372,7 @@
     GlobalVar("lhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -389,7 +389,7 @@
     GlobalVar("lhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -404,7 +404,7 @@
     GlobalVar("a", ty.f32(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.f32(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -421,7 +421,7 @@
     GlobalVar("a", ty.f16(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.f16(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -436,7 +436,7 @@
     GlobalVar("a", ty.vec3<f32>(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.vec3<f32>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -453,7 +453,7 @@
     GlobalVar("a", ty.vec3<f16>(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.vec3<f16>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -468,7 +468,7 @@
     GlobalVar("a", ty.vec3<f32>(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.f32(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -485,7 +485,7 @@
     GlobalVar("a", ty.vec3<f16>(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.f16(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -500,7 +500,7 @@
     GlobalVar("a", ty.f32(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.vec3<f32>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -517,7 +517,7 @@
     GlobalVar("a", ty.f16(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.vec3<f16>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -533,11 +533,11 @@
     GlobalVar("b", ty.f32(), core::AddressSpace::kPrivate);
 
     auto* expr_vec_mod_vec =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("a"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("a"));
     auto* expr_vec_mod_scalar =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     auto* expr_scalar_mod_vec =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("b"), Expr("a"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("b"), Expr("a"));
     WrapInFunction(expr_vec_mod_vec, expr_vec_mod_scalar, expr_scalar_mod_vec);
 
     ASTPrinter& gen = Build();
@@ -578,11 +578,11 @@
     GlobalVar("b", ty.f16(), core::AddressSpace::kPrivate);
 
     auto* expr_vec_mod_vec =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("a"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("a"));
     auto* expr_vec_mod_scalar =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("a"), Expr("b"));
     auto* expr_scalar_mod_vec =
-        create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("b"), Expr("a"));
+        create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr("b"), Expr("a"));
     WrapInFunction(expr_vec_mod_vec, expr_vec_mod_scalar, expr_scalar_mod_vec);
 
     ASTPrinter& gen = Build();
@@ -621,7 +621,7 @@
     GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -645,9 +645,9 @@
     GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
-        ast::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("c"), Expr("d")));
+        core::BinaryOp::kLogicalOr,
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -675,7 +675,7 @@
     GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -705,9 +705,9 @@
     GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr =
-        If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
            Block(Return(1_i)),
-           Else(If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
+           Else(If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
                    Block(Return(2_i)), Else(Block(Return(3_i))))));
     Func("func", tint::Empty, ty.i32(), Vector{WrapInStatement(expr)});
 
@@ -743,8 +743,8 @@
     GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
-        ast::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        core::BinaryOp::kLogicalOr,
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
         Expr("c")));
     Func("func", tint::Empty, ty.bool_(), Vector{WrapInStatement(expr)});
 
@@ -775,8 +775,8 @@
     auto* expr =
         Assign(Expr("a"),
                create<ast::BinaryExpression>(
-                   ast::BinaryOp::kLogicalAnd,
-                   create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
+                   core::BinaryOp::kLogicalAnd,
+                   create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
                    Expr("d")));
     WrapInFunction(expr);
 
@@ -806,8 +806,8 @@
     auto* var =
         Var("a", ty.bool_(),
             create<ast::BinaryExpression>(
-                ast::BinaryOp::kLogicalOr,
-                create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
+                core::BinaryOp::kLogicalOr,
+                create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
                 Expr("d")));
 
     auto* decl = Decl(var);
@@ -845,12 +845,12 @@
     GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
 
     Vector params{
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("c"), Expr("d")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")),
         create<ast::BinaryExpression>(
-            ast::BinaryOp::kLogicalAnd,
-            create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("c")),
-            create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("d"))),
+            core::BinaryOp::kLogicalAnd,
+            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("c")),
+            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("d"))),
     };
 
     auto* expr = CallStmt(Call("foo", params));
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
index c0be95f..b164212 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
@@ -907,7 +907,7 @@
 }
 
 bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
-    if (expr->op == ast::BinaryOp::kLogicalAnd || expr->op == ast::BinaryOp::kLogicalOr) {
+    if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
         auto name = UniqueIdentifier(kTempNamePrefix);
 
         {
@@ -919,7 +919,7 @@
             pre << ";";
         }
 
-        if (expr->op == ast::BinaryOp::kLogicalOr) {
+        if (expr->op == core::BinaryOp::kLogicalOr) {
             Line() << "if (!" << name << ") {";
         } else {
             Line() << "if (" << name << ") {";
@@ -945,7 +945,7 @@
     auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
     // Multiplying by a matrix requires the use of `mul` in order to get the
     // type of multiply we desire.
-    if (expr->op == ast::BinaryOp::kMultiply &&
+    if (expr->op == core::BinaryOp::kMultiply &&
         ((lhs_type->Is<type::Vector>() && rhs_type->Is<type::Matrix>()) ||
          (lhs_type->Is<type::Matrix>() && rhs_type->Is<type::Vector>()) ||
          (lhs_type->Is<type::Matrix>() && rhs_type->Is<type::Matrix>()))) {
@@ -971,43 +971,43 @@
     out << " ";
 
     switch (expr->op) {
-        case ast::BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             out << "&";
             break;
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             out << "|";
             break;
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             out << "^";
             break;
-        case ast::BinaryOp::kLogicalAnd:
-        case ast::BinaryOp::kLogicalOr: {
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr: {
             // These are both handled above.
             TINT_UNREACHABLE();
             return false;
         }
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             out << "==";
             break;
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             out << "!=";
             break;
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             out << "<";
             break;
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             out << ">";
             break;
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             out << "<=";
             break;
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             out << ">=";
             break;
-        case ast::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             out << "<<";
             break;
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
             // implementation-defined behaviour for negative LHS.  We may have to
             // generate extra code to implement WGSL-specified behaviour for negative
@@ -1015,24 +1015,21 @@
             out << R"(>>)";
             break;
 
-        case ast::BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             out << "+";
             break;
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             out << "-";
             break;
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             out << "*";
             break;
-        case ast::BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             out << "/";
             break;
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             out << "%";
             break;
-        case ast::BinaryOp::kNone:
-            diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-            return false;
     }
     out << " ";
 
diff --git a/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc b/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc
index ffe77a3..6c01562 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc
@@ -27,7 +27,7 @@
 
 struct BinaryData {
     const char* result;
-    ast::BinaryOp op;
+    core::BinaryOp op;
 
     enum Types { All = 0b11, Integer = 0b10, Float = 0b01 };
     Types valid_for = Types::All;
@@ -48,9 +48,9 @@
     }
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kAnd || params.op == ast::BinaryOp::kOr ||
-        params.op == ast::BinaryOp::kXor || params.op == ast::BinaryOp::kShiftLeft ||
-        params.op == ast::BinaryOp::kShiftRight) {
+    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
+        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
+        params.op == core::BinaryOp::kShiftRight) {
         return;
     }
 
@@ -78,9 +78,9 @@
     }
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kAnd || params.op == ast::BinaryOp::kOr ||
-        params.op == ast::BinaryOp::kXor || params.op == ast::BinaryOp::kShiftLeft ||
-        params.op == ast::BinaryOp::kShiftRight) {
+    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
+        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
+        params.op == core::BinaryOp::kShiftRight) {
         return;
     }
 
@@ -133,7 +133,7 @@
     }
 
     // Skip ops that are illegal for this type
-    if (params.op == ast::BinaryOp::kShiftLeft || params.op == ast::BinaryOp::kShiftRight) {
+    if (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight) {
         return;
     }
 
@@ -156,31 +156,31 @@
 INSTANTIATE_TEST_SUITE_P(
     HlslASTPrinterTest,
     HlslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", ast::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", ast::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", ast::BinaryOp::kXor},
-                    BinaryData{"(left == right)", ast::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", ast::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", ast::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", ast::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", ast::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", ast::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", ast::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", ast::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", ast::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", ast::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", ast::BinaryOp::kMultiply},
+    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
+                    BinaryData{"(left | right)", core::BinaryOp::kOr},
+                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
+                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
+                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
+                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
+                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
+                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
+                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
+                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
+                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
+                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
+                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
+                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
                     // NOTE: Integer divide covered by DivOrModBy* tests below
-                    BinaryData{"(left / right)", ast::BinaryOp::kDivide, BinaryData::Types::Float},
+                    BinaryData{"(left / right)", core::BinaryOp::kDivide, BinaryData::Types::Float},
                     // NOTE: Integer modulo covered by DivOrModBy* tests below
-                    BinaryData{"(left % right)", ast::BinaryOp::kModulo,
+                    BinaryData{"(left % right)", core::BinaryOp::kModulo,
                                BinaryData::Types::Float}));
 
 TEST_F(HlslASTPrinterTest_Binary, Multiply_VectorScalar_f32) {
     auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
     auto* rhs = Expr(1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -197,7 +197,7 @@
     auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
     auto* rhs = Expr(1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -212,7 +212,7 @@
     auto* lhs = Expr(1_f);
     auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -229,7 +229,7 @@
     auto* lhs = Expr(1_h);
     auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -245,7 +245,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -262,7 +262,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -277,7 +277,7 @@
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -294,7 +294,7 @@
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -309,7 +309,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -326,7 +326,7 @@
     auto* lhs = Expr("mat");
     auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -341,7 +341,7 @@
     auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -358,7 +358,7 @@
     auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
     auto* rhs = Expr("mat");
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -372,7 +372,7 @@
     GlobalVar("lhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -388,7 +388,7 @@
     GlobalVar("lhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
     GlobalVar("rhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -402,7 +402,7 @@
     GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -425,9 +425,9 @@
     GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
-        ast::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("c"), Expr("d")));
+        core::BinaryOp::kLogicalOr,
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -454,7 +454,7 @@
     GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
     GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
 
     ASTPrinter& gen = Build();
@@ -483,9 +483,9 @@
     GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr =
-        If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
            Block(Return(1_i)),
-           Else(If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
+           Else(If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
                    Block(Return(2_i)), Else(Block(Return(3_i))))));
     Func("func", tint::Empty, ty.i32(), Vector{WrapInStatement(expr)});
 
@@ -520,8 +520,8 @@
     GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
-        ast::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        core::BinaryOp::kLogicalOr,
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
         Expr("c")));
     Func("func", tint::Empty, ty.bool_(), Vector{WrapInStatement(expr)});
 
@@ -551,8 +551,8 @@
     auto* expr =
         Assign(Expr("a"),
                create<ast::BinaryExpression>(
-                   ast::BinaryOp::kLogicalAnd,
-                   create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
+                   core::BinaryOp::kLogicalAnd,
+                   create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
                    Expr("d")));
     WrapInFunction(expr);
 
@@ -581,8 +581,8 @@
     auto* var =
         Var("a", ty.bool_(), core::AddressSpace::kUndefined,
             create<ast::BinaryExpression>(
-                ast::BinaryOp::kLogicalOr,
-                create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
+                core::BinaryOp::kLogicalOr,
+                create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
                 Expr("d")));
 
     auto* decl = Decl(var);
@@ -619,12 +619,12 @@
     GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
 
     Vector params{
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("c"), Expr("d")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")),
         create<ast::BinaryExpression>(
-            ast::BinaryOp::kLogicalAnd,
-            create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("c")),
-            create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"), Expr("d"))),
+            core::BinaryOp::kLogicalAnd,
+            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("c")),
+            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("d"))),
     };
 
     auto* expr = CallStmt(Call("foo", params));
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
index 34acb4f..230aa06 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
@@ -401,43 +401,43 @@
         out << " ";
 
         switch (expr->op) {
-            case ast::BinaryOp::kAnd:
+            case core::BinaryOp::kAnd:
                 out << "&";
                 break;
-            case ast::BinaryOp::kOr:
+            case core::BinaryOp::kOr:
                 out << "|";
                 break;
-            case ast::BinaryOp::kXor:
+            case core::BinaryOp::kXor:
                 out << "^";
                 break;
-            case ast::BinaryOp::kLogicalAnd:
+            case core::BinaryOp::kLogicalAnd:
                 out << "&&";
                 break;
-            case ast::BinaryOp::kLogicalOr:
+            case core::BinaryOp::kLogicalOr:
                 out << "||";
                 break;
-            case ast::BinaryOp::kEqual:
+            case core::BinaryOp::kEqual:
                 out << "==";
                 break;
-            case ast::BinaryOp::kNotEqual:
+            case core::BinaryOp::kNotEqual:
                 out << "!=";
                 break;
-            case ast::BinaryOp::kLessThan:
+            case core::BinaryOp::kLessThan:
                 out << "<";
                 break;
-            case ast::BinaryOp::kGreaterThan:
+            case core::BinaryOp::kGreaterThan:
                 out << ">";
                 break;
-            case ast::BinaryOp::kLessThanEqual:
+            case core::BinaryOp::kLessThanEqual:
                 out << "<=";
                 break;
-            case ast::BinaryOp::kGreaterThanEqual:
+            case core::BinaryOp::kGreaterThanEqual:
                 out << ">=";
                 break;
-            case ast::BinaryOp::kShiftLeft:
+            case core::BinaryOp::kShiftLeft:
                 out << "<<";
                 break;
-            case ast::BinaryOp::kShiftRight:
+            case core::BinaryOp::kShiftRight:
                 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
                 // implementation-defined behaviour for negative LHS.  We may have to
                 // generate extra code to implement WGSL-specified behaviour for
@@ -445,24 +445,21 @@
                 out << R"(>>)";
                 break;
 
-            case ast::BinaryOp::kAdd:
+            case core::BinaryOp::kAdd:
                 out << "+";
                 break;
-            case ast::BinaryOp::kSubtract:
+            case core::BinaryOp::kSubtract:
                 out << "-";
                 break;
-            case ast::BinaryOp::kMultiply:
+            case core::BinaryOp::kMultiply:
                 out << "*";
                 break;
-            case ast::BinaryOp::kDivide:
+            case core::BinaryOp::kDivide:
                 out << "/";
                 break;
-            case ast::BinaryOp::kModulo:
+            case core::BinaryOp::kModulo:
                 out << "%";
                 break;
-            case ast::BinaryOp::kNone:
-                diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-                return false;
         }
         out << " ";
         return true;
@@ -490,7 +487,7 @@
     auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
 
     // Handle fmod
-    if (expr->op == ast::BinaryOp::kModulo && lhs_type->is_float_scalar_or_vector()) {
+    if (expr->op == core::BinaryOp::kModulo && lhs_type->is_float_scalar_or_vector()) {
         out << "fmod";
         ScopedParen sp(out);
         if (!EmitExpression(out, expr->lhs)) {
diff --git a/src/tint/lang/msl/writer/ast_printer/binary_test.cc b/src/tint/lang/msl/writer/ast_printer/binary_test.cc
index 0de442a..f0acfd9 100644
--- a/src/tint/lang/msl/writer/ast_printer/binary_test.cc
+++ b/src/tint/lang/msl/writer/ast_printer/binary_test.cc
@@ -20,7 +20,7 @@
 
 struct BinaryData {
     const char* result;
-    ast::BinaryOp op;
+    core::BinaryOp op;
 };
 inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
     StringStream str;
@@ -33,8 +33,8 @@
     auto params = GetParam();
 
     auto type = [&] {
-        return ((params.op == ast::BinaryOp::kLogicalAnd) ||
-                (params.op == ast::BinaryOp::kLogicalOr))
+        return ((params.op == core::BinaryOp::kLogicalAnd) ||
+                (params.op == core::BinaryOp::kLogicalOr))
                    ? ty.bool_()
                    : ty.u32();
     };
@@ -54,24 +54,24 @@
 INSTANTIATE_TEST_SUITE_P(
     MslASTPrinterTest,
     MslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", ast::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", ast::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", ast::BinaryOp::kXor},
-                    BinaryData{"(left && right)", ast::BinaryOp::kLogicalAnd},
-                    BinaryData{"(left || right)", ast::BinaryOp::kLogicalOr},
-                    BinaryData{"(left == right)", ast::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", ast::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", ast::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", ast::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", ast::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", ast::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", ast::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", ast::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", ast::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", ast::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", ast::BinaryOp::kMultiply},
-                    BinaryData{"(left / right)", ast::BinaryOp::kDivide},
-                    BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
+    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
+                    BinaryData{"(left | right)", core::BinaryOp::kOr},
+                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
+                    BinaryData{"(left && right)", core::BinaryOp::kLogicalAnd},
+                    BinaryData{"(left || right)", core::BinaryOp::kLogicalOr},
+                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
+                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
+                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
+                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
+                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
+                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
+                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
+                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
+                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
+                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
+                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
+                    BinaryData{"(left / right)", core::BinaryOp::kDivide},
+                    BinaryData{"(left % right)", core::BinaryOp::kModulo}));
 
 using MslBinaryTest_SignedOverflowDefinedBehaviour = TestParamHelper<BinaryData>;
 TEST_P(MslBinaryTest_SignedOverflowDefinedBehaviour, Emit) {
@@ -79,7 +79,7 @@
 
     auto a_type = ty.i32();
     auto b_type =
-        (params.op == ast::BinaryOp::kShiftLeft || params.op == ast::BinaryOp::kShiftRight)
+        (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight)
             ? ty.u32()
             : ty.i32();
 
@@ -95,7 +95,7 @@
     ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
-using Op = ast::BinaryOp;
+using Op = core::BinaryOp;
 constexpr BinaryData signed_overflow_defined_behaviour_cases[] = {
     {"as_type<int>((as_type<uint>(a) << b))", Op::kShiftLeft},
     {"(a >> b)", Op::kShiftRight},
@@ -112,7 +112,7 @@
 
     auto a_type = ty.i32();
     auto b_type =
-        (params.op == ast::BinaryOp::kShiftLeft || params.op == ast::BinaryOp::kShiftRight)
+        (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight)
             ? ty.u32()
             : ty.i32();
 
@@ -129,7 +129,7 @@
     ASSERT_TRUE(gen.EmitExpression(out, expr2)) << gen.Diagnostics();
     EXPECT_EQ(out.str(), params.result);
 }
-using Op = ast::BinaryOp;
+using Op = core::BinaryOp;
 constexpr BinaryData signed_overflow_defined_behaviour_chained_cases[] = {
     {R"(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) << b))) << b)))",
      Op::kShiftLeft},
@@ -147,7 +147,7 @@
 TEST_F(MslBinaryTest, ModF32) {
     auto* left = Var("left", ty.f32());
     auto* right = Var("right", ty.f32());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
@@ -162,7 +162,7 @@
 
     auto* left = Var("left", ty.f16());
     auto* right = Var("right", ty.f16());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
@@ -175,7 +175,7 @@
 TEST_F(MslBinaryTest, ModVec3F32) {
     auto* left = Var("left", ty.vec3<f32>());
     auto* right = Var("right", ty.vec3<f32>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
@@ -190,7 +190,7 @@
 
     auto* left = Var("left", ty.vec3<f16>());
     auto* right = Var("right", ty.vec3<f16>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
@@ -203,7 +203,7 @@
 TEST_F(MslBinaryTest, BoolAnd) {
     auto* left = Var("left", Expr(true));
     auto* right = Var("right", Expr(false));
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kAnd, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kAnd, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
@@ -216,7 +216,7 @@
 TEST_F(MslBinaryTest, BoolOr) {
     auto* left = Var("left", Expr(true));
     auto* right = Var("right", Expr(false));
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kOr, Expr(left), Expr(right));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kOr, Expr(left), Expr(right));
     WrapInFunction(left, right, expr);
 
     ASTPrinter& gen = Build();
diff --git a/src/tint/lang/msl/writer/ast_printer/loop_test.cc b/src/tint/lang/msl/writer/ast_printer/loop_test.cc
index 86bd80d..e27f73e 100644
--- a/src/tint/lang/msl/writer/ast_printer/loop_test.cc
+++ b/src/tint/lang/msl/writer/ast_printer/loop_test.cc
@@ -417,7 +417,7 @@
 
     auto* t = Let("t", Expr(true));
     auto* multi_stmt = LogicalAnd(t, false);
-    // create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(t), Expr(false));
+    // create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(t), Expr(false));
     auto* f = While(multi_stmt, Block(Return()));
     WrapInFunction(t, f);
 
diff --git a/src/tint/lang/spirv/reader/ast_parser/function.cc b/src/tint/lang/spirv/reader/ast_parser/function.cc
index fe3b65d..103095a 100644
--- a/src/tint/lang/spirv/reader/ast_parser/function.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/function.cc
@@ -205,15 +205,15 @@
 
 // Converts a SPIR-V opcode to its corresponding AST binary opcode, if any
 // @param opcode SPIR-V opcode
-// @returns the AST binary op for the given opcode, or kNone
-ast::BinaryOp ConvertBinaryOp(spv::Op opcode) {
+// @returns the AST binary op for the given opcode, or std::nullopt
+std::optional<core::BinaryOp> ConvertBinaryOp(spv::Op opcode) {
     switch (opcode) {
         case spv::Op::OpIAdd:
         case spv::Op::OpFAdd:
-            return ast::BinaryOp::kAdd;
+            return core::BinaryOp::kAdd;
         case spv::Op::OpISub:
         case spv::Op::OpFSub:
-            return ast::BinaryOp::kSubtract;
+            return core::BinaryOp::kSubtract;
         case spv::Op::OpIMul:
         case spv::Op::OpFMul:
         case spv::Op::OpVectorTimesScalar:
@@ -221,80 +221,80 @@
         case spv::Op::OpVectorTimesMatrix:
         case spv::Op::OpMatrixTimesVector:
         case spv::Op::OpMatrixTimesMatrix:
-            return ast::BinaryOp::kMultiply;
+            return core::BinaryOp::kMultiply;
         case spv::Op::OpUDiv:
         case spv::Op::OpSDiv:
         case spv::Op::OpFDiv:
-            return ast::BinaryOp::kDivide;
+            return core::BinaryOp::kDivide;
         case spv::Op::OpUMod:
         case spv::Op::OpSMod:
         case spv::Op::OpFRem:
-            return ast::BinaryOp::kModulo;
+            return core::BinaryOp::kModulo;
         case spv::Op::OpLogicalEqual:
         case spv::Op::OpIEqual:
         case spv::Op::OpFOrdEqual:
-            return ast::BinaryOp::kEqual;
+            return core::BinaryOp::kEqual;
         case spv::Op::OpLogicalNotEqual:
         case spv::Op::OpINotEqual:
         case spv::Op::OpFOrdNotEqual:
-            return ast::BinaryOp::kNotEqual;
+            return core::BinaryOp::kNotEqual;
         case spv::Op::OpBitwiseAnd:
-            return ast::BinaryOp::kAnd;
+            return core::BinaryOp::kAnd;
         case spv::Op::OpBitwiseOr:
-            return ast::BinaryOp::kOr;
+            return core::BinaryOp::kOr;
         case spv::Op::OpBitwiseXor:
-            return ast::BinaryOp::kXor;
+            return core::BinaryOp::kXor;
         case spv::Op::OpLogicalAnd:
-            return ast::BinaryOp::kAnd;
+            return core::BinaryOp::kAnd;
         case spv::Op::OpLogicalOr:
-            return ast::BinaryOp::kOr;
+            return core::BinaryOp::kOr;
         case spv::Op::OpUGreaterThan:
         case spv::Op::OpSGreaterThan:
         case spv::Op::OpFOrdGreaterThan:
-            return ast::BinaryOp::kGreaterThan;
+            return core::BinaryOp::kGreaterThan;
         case spv::Op::OpUGreaterThanEqual:
         case spv::Op::OpSGreaterThanEqual:
         case spv::Op::OpFOrdGreaterThanEqual:
-            return ast::BinaryOp::kGreaterThanEqual;
+            return core::BinaryOp::kGreaterThanEqual;
         case spv::Op::OpULessThan:
         case spv::Op::OpSLessThan:
         case spv::Op::OpFOrdLessThan:
-            return ast::BinaryOp::kLessThan;
+            return core::BinaryOp::kLessThan;
         case spv::Op::OpULessThanEqual:
         case spv::Op::OpSLessThanEqual:
         case spv::Op::OpFOrdLessThanEqual:
-            return ast::BinaryOp::kLessThanEqual;
+            return core::BinaryOp::kLessThanEqual;
         default:
             break;
     }
     // It's not clear what OpSMod should map to.
     // https://bugs.chromium.org/p/tint/issues/detail?id=52
-    return ast::BinaryOp::kNone;
+    return std::nullopt;
 }
 
 // If the given SPIR-V opcode is a floating point unordered comparison,
 // then returns the binary float comparison for which it is the negation.
-// Othewrise returns BinaryOp::kNone.
+// Otherwise returns std::nullopt.
 // @param opcode SPIR-V opcode
 // @returns operation corresponding to negated version of the SPIR-V opcode
-ast::BinaryOp NegatedFloatCompare(spv::Op opcode) {
+std::optional<core::BinaryOp> NegatedFloatCompare(spv::Op opcode) {
     switch (opcode) {
         case spv::Op::OpFUnordEqual:
-            return ast::BinaryOp::kNotEqual;
+            return core::BinaryOp::kNotEqual;
         case spv::Op::OpFUnordNotEqual:
-            return ast::BinaryOp::kEqual;
+            return core::BinaryOp::kEqual;
         case spv::Op::OpFUnordLessThan:
-            return ast::BinaryOp::kGreaterThanEqual;
+            return core::BinaryOp::kGreaterThanEqual;
         case spv::Op::OpFUnordLessThanEqual:
-            return ast::BinaryOp::kGreaterThan;
+            return core::BinaryOp::kGreaterThan;
         case spv::Op::OpFUnordGreaterThan:
-            return ast::BinaryOp::kLessThanEqual;
+            return core::BinaryOp::kLessThanEqual;
         case spv::Op::OpFUnordGreaterThanEqual:
-            return ast::BinaryOp::kLessThan;
+            return core::BinaryOp::kLessThan;
         default:
             break;
     }
-    return ast::BinaryOp::kNone;
+    return std::nullopt;
 }
 
 // Returns the WGSL standard library function for the given
@@ -3787,8 +3787,7 @@
         }
     }
 
-    auto binary_op = ConvertBinaryOp(op);
-    if (binary_op != ast::BinaryOp::kNone) {
+    if (auto binary_op = ConvertBinaryOp(op)) {
         auto arg0 = MakeOperand(inst, 0);
         auto arg1 =
             parser_impl_.RectifySecondOperandSignedness(inst, arg0.type, MakeOperand(inst, 1));
@@ -3796,7 +3795,7 @@
             return {};
         }
         auto* binary_expr =
-            create<ast::BinaryExpression>(Source{}, binary_op, arg0.expr, arg1.expr);
+            create<ast::BinaryExpression>(Source{}, *binary_op, arg0.expr, arg1.expr);
         TypedExpression result{ast_type, binary_expr};
         return parser_impl_.RectifyForcedResultType(result, inst, arg0.type);
     }
@@ -3848,32 +3847,32 @@
         // since the shift is modulo the bit width of the first operand.
         auto arg1 = parser_impl_.AsUnsigned(MakeOperand(inst, 1));
 
+        std::optional<core::BinaryOp> binary_op;
         switch (op) {
             case spv::Op::OpShiftLeftLogical:
-                binary_op = ast::BinaryOp::kShiftLeft;
+                binary_op = core::BinaryOp::kShiftLeft;
                 break;
             case spv::Op::OpShiftRightLogical:
                 arg0 = parser_impl_.AsUnsigned(arg0);
-                binary_op = ast::BinaryOp::kShiftRight;
+                binary_op = core::BinaryOp::kShiftRight;
                 break;
             case spv::Op::OpShiftRightArithmetic:
                 arg0 = parser_impl_.AsSigned(arg0);
-                binary_op = ast::BinaryOp::kShiftRight;
+                binary_op = core::BinaryOp::kShiftRight;
                 break;
             default:
                 break;
         }
         TypedExpression result{
-            ast_type, create<ast::BinaryExpression>(Source{}, binary_op, arg0.expr, arg1.expr)};
+            ast_type, create<ast::BinaryExpression>(Source{}, *binary_op, arg0.expr, arg1.expr)};
         return parser_impl_.RectifyForcedResultType(result, inst, arg0.type);
     }
 
-    auto negated_op = NegatedFloatCompare(op);
-    if (negated_op != ast::BinaryOp::kNone) {
+    if (auto negated_op = NegatedFloatCompare(op)) {
         auto arg0 = MakeOperand(inst, 0);
         auto arg1 = MakeOperand(inst, 1);
         auto* binary_expr =
-            create<ast::BinaryExpression>(Source{}, negated_op, arg0.expr, arg1.expr);
+            create<ast::BinaryExpression>(Source{}, *negated_op, arg0.expr, arg1.expr);
         auto* negated_expr =
             create<ast::UnaryOpExpression>(Source{}, ast::UnaryOp::kNot, binary_expr);
         return {ast_type, negated_expr};
@@ -4024,7 +4023,7 @@
                                                            normal.expr),
                             normal.expr,
                             create<ast::BinaryExpression>(
-                                Source{}, ast::BinaryOp::kLessThan,
+                                Source{}, core::BinaryOp::kLessThan,
                                 builder_.Mul({}, incident.expr, nref.expr), builder_.Expr(0_f)),
                         }),
                 };
@@ -6230,7 +6229,7 @@
         for (uint32_t irow = 0; irow < result_ty->rows; irow++) {
             auto* column_factor =
                 create<ast::MemberAccessorExpression>(Source{}, col.expr, Swizzle(irow));
-            auto* elem = create<ast::BinaryExpression>(Source{}, ast::BinaryOp::kMultiply,
+            auto* elem = create<ast::BinaryExpression>(Source{}, core::BinaryOp::kMultiply,
                                                        row_factor, column_factor);
             result_row.Push(elem);
         }
diff --git a/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc b/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
index ecc74e8..49e6239 100644
--- a/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
@@ -24,7 +24,7 @@
 using SpirvASTPrinterTest = TestHelper;
 
 struct BinaryData {
-    ast::BinaryOp op;
+    core::BinaryOp op;
     std::string name;
 };
 inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
@@ -62,8 +62,8 @@
     auto param = GetParam();
 
     // Skip ops that are illegal for this type
-    if (param.op == ast::BinaryOp::kAnd || param.op == ast::BinaryOp::kOr ||
-        param.op == ast::BinaryOp::kXor) {
+    if (param.op == core::BinaryOp::kAnd || param.op == core::BinaryOp::kOr ||
+        param.op == core::BinaryOp::kXor) {
         return;
     }
 
@@ -119,14 +119,14 @@
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryArithSignedIntegerTest,
                          // NOTE: No left and right shift as they require u32 for rhs operand
-                         testing::Values(BinaryData{ast::BinaryOp::kAdd, "OpIAdd"},
-                                         BinaryData{ast::BinaryOp::kAnd, "OpBitwiseAnd"},
-                                         BinaryData{ast::BinaryOp::kDivide, "OpSDiv"},
-                                         BinaryData{ast::BinaryOp::kModulo, "OpSRem"},
-                                         BinaryData{ast::BinaryOp::kMultiply, "OpIMul"},
-                                         BinaryData{ast::BinaryOp::kOr, "OpBitwiseOr"},
-                                         BinaryData{ast::BinaryOp::kSubtract, "OpISub"},
-                                         BinaryData{ast::BinaryOp::kXor, "OpBitwiseXor"}));
+                         testing::Values(BinaryData{core::BinaryOp::kAdd, "OpIAdd"},
+                                         BinaryData{core::BinaryOp::kAnd, "OpBitwiseAnd"},
+                                         BinaryData{core::BinaryOp::kDivide, "OpSDiv"},
+                                         BinaryData{core::BinaryOp::kModulo, "OpSRem"},
+                                         BinaryData{core::BinaryOp::kMultiply, "OpIMul"},
+                                         BinaryData{core::BinaryOp::kOr, "OpBitwiseOr"},
+                                         BinaryData{core::BinaryOp::kSubtract, "OpISub"},
+                                         BinaryData{core::BinaryOp::kXor, "OpBitwiseXor"}));
 
 using BinaryArithUnsignedIntegerTest = TestParamHelper<BinaryData>;
 TEST_P(BinaryArithUnsignedIntegerTest, Scalar) {
@@ -155,8 +155,8 @@
     auto param = GetParam();
 
     // Skip ops that are illegal for this type
-    if (param.op == ast::BinaryOp::kAnd || param.op == ast::BinaryOp::kOr ||
-        param.op == ast::BinaryOp::kXor) {
+    if (param.op == core::BinaryOp::kAnd || param.op == core::BinaryOp::kOr ||
+        param.op == core::BinaryOp::kXor) {
         return;
     }
 
@@ -183,16 +183,16 @@
 INSTANTIATE_TEST_SUITE_P(
     SpirvASTPrinterTest,
     BinaryArithUnsignedIntegerTest,
-    testing::Values(BinaryData{ast::BinaryOp::kAdd, "OpIAdd"},
-                    BinaryData{ast::BinaryOp::kAnd, "OpBitwiseAnd"},
-                    BinaryData{ast::BinaryOp::kDivide, "OpUDiv"},
-                    BinaryData{ast::BinaryOp::kModulo, "OpUMod"},
-                    BinaryData{ast::BinaryOp::kMultiply, "OpIMul"},
-                    BinaryData{ast::BinaryOp::kOr, "OpBitwiseOr"},
-                    BinaryData{ast::BinaryOp::kShiftLeft, "OpShiftLeftLogical"},
-                    BinaryData{ast::BinaryOp::kShiftRight, "OpShiftRightLogical"},
-                    BinaryData{ast::BinaryOp::kSubtract, "OpISub"},
-                    BinaryData{ast::BinaryOp::kXor, "OpBitwiseXor"}));
+    testing::Values(BinaryData{core::BinaryOp::kAdd, "OpIAdd"},
+                    BinaryData{core::BinaryOp::kAnd, "OpBitwiseAnd"},
+                    BinaryData{core::BinaryOp::kDivide, "OpUDiv"},
+                    BinaryData{core::BinaryOp::kModulo, "OpUMod"},
+                    BinaryData{core::BinaryOp::kMultiply, "OpIMul"},
+                    BinaryData{core::BinaryOp::kOr, "OpBitwiseOr"},
+                    BinaryData{core::BinaryOp::kShiftLeft, "OpShiftLeftLogical"},
+                    BinaryData{core::BinaryOp::kShiftRight, "OpShiftRightLogical"},
+                    BinaryData{core::BinaryOp::kSubtract, "OpISub"},
+                    BinaryData{core::BinaryOp::kXor, "OpBitwiseXor"}));
 
 using BinaryArithF32Test = TestParamHelper<BinaryData>;
 TEST_P(BinaryArithF32Test, Scalar) {
@@ -243,11 +243,11 @@
 }
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryArithF32Test,
-                         testing::Values(BinaryData{ast::BinaryOp::kAdd, "OpFAdd"},
-                                         BinaryData{ast::BinaryOp::kDivide, "OpFDiv"},
-                                         BinaryData{ast::BinaryOp::kModulo, "OpFRem"},
-                                         BinaryData{ast::BinaryOp::kMultiply, "OpFMul"},
-                                         BinaryData{ast::BinaryOp::kSubtract, "OpFSub"}));
+                         testing::Values(BinaryData{core::BinaryOp::kAdd, "OpFAdd"},
+                                         BinaryData{core::BinaryOp::kDivide, "OpFDiv"},
+                                         BinaryData{core::BinaryOp::kModulo, "OpFRem"},
+                                         BinaryData{core::BinaryOp::kMultiply, "OpFMul"},
+                                         BinaryData{core::BinaryOp::kSubtract, "OpFSub"}));
 
 using BinaryArithF16Test = TestParamHelper<BinaryData>;
 TEST_P(BinaryArithF16Test, Scalar) {
@@ -302,11 +302,11 @@
 }
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryArithF16Test,
-                         testing::Values(BinaryData{ast::BinaryOp::kAdd, "OpFAdd"},
-                                         BinaryData{ast::BinaryOp::kDivide, "OpFDiv"},
-                                         BinaryData{ast::BinaryOp::kModulo, "OpFRem"},
-                                         BinaryData{ast::BinaryOp::kMultiply, "OpFMul"},
-                                         BinaryData{ast::BinaryOp::kSubtract, "OpFSub"}));
+                         testing::Values(BinaryData{core::BinaryOp::kAdd, "OpFAdd"},
+                                         BinaryData{core::BinaryOp::kDivide, "OpFDiv"},
+                                         BinaryData{core::BinaryOp::kModulo, "OpFRem"},
+                                         BinaryData{core::BinaryOp::kMultiply, "OpFMul"},
+                                         BinaryData{core::BinaryOp::kSubtract, "OpFSub"}));
 
 using BinaryOperatorBoolTest = TestParamHelper<BinaryData>;
 TEST_P(BinaryOperatorBoolTest, Scalar) {
@@ -359,10 +359,10 @@
 }
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryOperatorBoolTest,
-                         testing::Values(BinaryData{ast::BinaryOp::kEqual, "OpLogicalEqual"},
-                                         BinaryData{ast::BinaryOp::kNotEqual, "OpLogicalNotEqual"},
-                                         BinaryData{ast::BinaryOp::kAnd, "OpLogicalAnd"},
-                                         BinaryData{ast::BinaryOp::kOr, "OpLogicalOr"}));
+                         testing::Values(BinaryData{core::BinaryOp::kEqual, "OpLogicalEqual"},
+                                         BinaryData{core::BinaryOp::kNotEqual, "OpLogicalNotEqual"},
+                                         BinaryData{core::BinaryOp::kAnd, "OpLogicalAnd"},
+                                         BinaryData{core::BinaryOp::kOr, "OpLogicalOr"}));
 
 using BinaryCompareUnsignedIntegerTest = TestParamHelper<BinaryData>;
 TEST_P(BinaryCompareUnsignedIntegerTest, Scalar) {
@@ -417,12 +417,12 @@
 INSTANTIATE_TEST_SUITE_P(
     SpirvASTPrinterTest,
     BinaryCompareUnsignedIntegerTest,
-    testing::Values(BinaryData{ast::BinaryOp::kEqual, "OpIEqual"},
-                    BinaryData{ast::BinaryOp::kGreaterThan, "OpUGreaterThan"},
-                    BinaryData{ast::BinaryOp::kGreaterThanEqual, "OpUGreaterThanEqual"},
-                    BinaryData{ast::BinaryOp::kLessThan, "OpULessThan"},
-                    BinaryData{ast::BinaryOp::kLessThanEqual, "OpULessThanEqual"},
-                    BinaryData{ast::BinaryOp::kNotEqual, "OpINotEqual"}));
+    testing::Values(BinaryData{core::BinaryOp::kEqual, "OpIEqual"},
+                    BinaryData{core::BinaryOp::kGreaterThan, "OpUGreaterThan"},
+                    BinaryData{core::BinaryOp::kGreaterThanEqual, "OpUGreaterThanEqual"},
+                    BinaryData{core::BinaryOp::kLessThan, "OpULessThan"},
+                    BinaryData{core::BinaryOp::kLessThanEqual, "OpULessThanEqual"},
+                    BinaryData{core::BinaryOp::kNotEqual, "OpINotEqual"}));
 
 using BinaryCompareSignedIntegerTest = TestParamHelper<BinaryData>;
 TEST_P(BinaryCompareSignedIntegerTest, Scalar) {
@@ -477,12 +477,12 @@
 INSTANTIATE_TEST_SUITE_P(
     SpirvASTPrinterTest,
     BinaryCompareSignedIntegerTest,
-    testing::Values(BinaryData{ast::BinaryOp::kEqual, "OpIEqual"},
-                    BinaryData{ast::BinaryOp::kGreaterThan, "OpSGreaterThan"},
-                    BinaryData{ast::BinaryOp::kGreaterThanEqual, "OpSGreaterThanEqual"},
-                    BinaryData{ast::BinaryOp::kLessThan, "OpSLessThan"},
-                    BinaryData{ast::BinaryOp::kLessThanEqual, "OpSLessThanEqual"},
-                    BinaryData{ast::BinaryOp::kNotEqual, "OpINotEqual"}));
+    testing::Values(BinaryData{core::BinaryOp::kEqual, "OpIEqual"},
+                    BinaryData{core::BinaryOp::kGreaterThan, "OpSGreaterThan"},
+                    BinaryData{core::BinaryOp::kGreaterThanEqual, "OpSGreaterThanEqual"},
+                    BinaryData{core::BinaryOp::kLessThan, "OpSLessThan"},
+                    BinaryData{core::BinaryOp::kLessThanEqual, "OpSLessThanEqual"},
+                    BinaryData{core::BinaryOp::kNotEqual, "OpINotEqual"}));
 
 using BinaryCompareF32Test = TestParamHelper<BinaryData>;
 TEST_P(BinaryCompareF32Test, Scalar) {
@@ -537,12 +537,12 @@
 INSTANTIATE_TEST_SUITE_P(
     SpirvASTPrinterTest,
     BinaryCompareF32Test,
-    testing::Values(BinaryData{ast::BinaryOp::kEqual, "OpFOrdEqual"},
-                    BinaryData{ast::BinaryOp::kGreaterThan, "OpFOrdGreaterThan"},
-                    BinaryData{ast::BinaryOp::kGreaterThanEqual, "OpFOrdGreaterThanEqual"},
-                    BinaryData{ast::BinaryOp::kLessThan, "OpFOrdLessThan"},
-                    BinaryData{ast::BinaryOp::kLessThanEqual, "OpFOrdLessThanEqual"},
-                    BinaryData{ast::BinaryOp::kNotEqual, "OpFOrdNotEqual"}));
+    testing::Values(BinaryData{core::BinaryOp::kEqual, "OpFOrdEqual"},
+                    BinaryData{core::BinaryOp::kGreaterThan, "OpFOrdGreaterThan"},
+                    BinaryData{core::BinaryOp::kGreaterThanEqual, "OpFOrdGreaterThanEqual"},
+                    BinaryData{core::BinaryOp::kLessThan, "OpFOrdLessThan"},
+                    BinaryData{core::BinaryOp::kLessThanEqual, "OpFOrdLessThanEqual"},
+                    BinaryData{core::BinaryOp::kNotEqual, "OpFOrdNotEqual"}));
 
 using BinaryCompareF16Test = TestParamHelper<BinaryData>;
 TEST_P(BinaryCompareF16Test, Scalar) {
@@ -601,18 +601,18 @@
 INSTANTIATE_TEST_SUITE_P(
     SpirvASTPrinterTest,
     BinaryCompareF16Test,
-    testing::Values(BinaryData{ast::BinaryOp::kEqual, "OpFOrdEqual"},
-                    BinaryData{ast::BinaryOp::kGreaterThan, "OpFOrdGreaterThan"},
-                    BinaryData{ast::BinaryOp::kGreaterThanEqual, "OpFOrdGreaterThanEqual"},
-                    BinaryData{ast::BinaryOp::kLessThan, "OpFOrdLessThan"},
-                    BinaryData{ast::BinaryOp::kLessThanEqual, "OpFOrdLessThanEqual"},
-                    BinaryData{ast::BinaryOp::kNotEqual, "OpFOrdNotEqual"}));
+    testing::Values(BinaryData{core::BinaryOp::kEqual, "OpFOrdEqual"},
+                    BinaryData{core::BinaryOp::kGreaterThan, "OpFOrdGreaterThan"},
+                    BinaryData{core::BinaryOp::kGreaterThanEqual, "OpFOrdGreaterThanEqual"},
+                    BinaryData{core::BinaryOp::kLessThan, "OpFOrdLessThan"},
+                    BinaryData{core::BinaryOp::kLessThanEqual, "OpFOrdLessThanEqual"},
+                    BinaryData{core::BinaryOp::kNotEqual, "OpFOrdNotEqual"}));
 
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_VectorScalar_F32) {
     auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
     auto* rhs = Expr(1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -637,7 +637,7 @@
     auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
     auto* rhs = Expr(1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -660,7 +660,7 @@
     auto* lhs = Expr(1_f);
     auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -685,7 +685,7 @@
     auto* lhs = Expr(1_h);
     auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
 
     WrapInFunction(expr);
 
@@ -706,7 +706,7 @@
 
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_MatrixScalar_F32) {
     auto* var = Var("mat", ty.mat3x3<f32>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), Expr(1_f));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), Expr(1_f));
 
     WrapInFunction(var, expr);
 
@@ -734,7 +734,7 @@
     Enable(core::Extension::kF16);
 
     auto* var = Var("mat", ty.mat3x3<f16>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), Expr(1_h));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), Expr(1_h));
 
     WrapInFunction(var, expr);
 
@@ -760,7 +760,7 @@
 
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_ScalarMatrix_F32) {
     auto* var = Var("mat", ty.mat3x3<f32>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr(1_f), Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr(1_f), Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -788,7 +788,7 @@
     Enable(core::Extension::kF16);
 
     auto* var = Var("mat", ty.mat3x3<f16>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr(1_h), Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr(1_h), Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -815,7 +815,7 @@
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_MatrixVector_F32) {
     auto* var = Var("mat", ty.mat3x3<f32>());
     auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), rhs);
 
     WrapInFunction(var, expr);
 
@@ -845,7 +845,7 @@
 
     auto* var = Var("mat", ty.mat3x3<f16>());
     auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), rhs);
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), rhs);
 
     WrapInFunction(var, expr);
 
@@ -873,7 +873,7 @@
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_VectorMatrix_F32) {
     auto* var = Var("mat", ty.mat3x3<f32>());
     auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -904,7 +904,7 @@
     auto* var = Var("mat", ty.mat3x3<f16>());
     auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -931,7 +931,7 @@
 
 TEST_F(SpirvASTPrinterTest, Binary_Multiply_MatrixMatrix_F32) {
     auto* var = Var("mat", ty.mat3x3<f32>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -959,7 +959,7 @@
     Enable(core::Extension::kF16);
 
     auto* var = Var("mat", ty.mat3x3<f16>());
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("mat"), Expr("mat"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("mat"), Expr("mat"));
 
     WrapInFunction(var, expr);
 
@@ -1036,7 +1036,7 @@
 TEST_F(SpirvASTPrinterTest, Binary_LogicalAnd_WithLoads) {
     auto* a_var = GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate, Expr(true));
     auto* b_var = GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate, Expr(false));
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
 
     WrapInFunction(expr);
 
@@ -1078,10 +1078,10 @@
     auto* f = Let("f", Expr(false));
 
     auto* logical_and_expr =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(t), Expr(f));
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(t), Expr(f));
 
     auto* expr =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr(t), logical_and_expr);
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr(t), logical_and_expr);
 
     WrapInFunction(t, f, expr);
 
@@ -1123,10 +1123,10 @@
     auto* f = Let("f", Expr(false));
 
     auto* logical_or_expr =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr(t), Expr(f));
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr(t), Expr(f));
 
     auto* expr =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(t), logical_or_expr);
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(t), logical_or_expr);
 
     WrapInFunction(t, f, expr);
 
@@ -1213,7 +1213,7 @@
     auto* a_var = GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate, Expr(true));
     auto* b_var = GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate, Expr(false));
 
-    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
+    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
 
     WrapInFunction(expr);
 
@@ -1339,7 +1339,7 @@
 
 struct Param {
     Type type;
-    ast::BinaryOp op;
+    core::BinaryOp op;
     std::string name;
 };
 
@@ -1450,30 +1450,30 @@
 }
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryArithVectorScalarTest,
-                         testing::Values(Param{Type::f32, ast::BinaryOp::kAdd, "OpFAdd"},
-                                         Param{Type::f32, ast::BinaryOp::kDivide, "OpFDiv"},
+                         testing::Values(Param{Type::f32, core::BinaryOp::kAdd, "OpFAdd"},
+                                         Param{Type::f32, core::BinaryOp::kDivide, "OpFDiv"},
                                          // NOTE: Modulo not allowed on mixed float scalar-vector
-                                         // Param{Type::f32, ast::BinaryOp::kModulo, "OpFMod"},
+                                         // Param{Type::f32, core::BinaryOp::kModulo, "OpFMod"},
                                          // NOTE: We test f32 multiplies separately as we emit
                                          // OpVectorTimesScalar for this case
-                                         // Param{Type::i32, ast::BinaryOp::kMultiply, "OpIMul"},
-                                         Param{Type::f32, ast::BinaryOp::kSubtract, "OpFSub"},
+                                         // Param{Type::i32, core::BinaryOp::kMultiply, "OpIMul"},
+                                         Param{Type::f32, core::BinaryOp::kSubtract, "OpFSub"},
 
-                                         Param{Type::f16, ast::BinaryOp::kAdd, "OpFAdd"},
-                                         Param{Type::f16, ast::BinaryOp::kDivide, "OpFDiv"},
-                                         Param{Type::f16, ast::BinaryOp::kSubtract, "OpFSub"},
+                                         Param{Type::f16, core::BinaryOp::kAdd, "OpFAdd"},
+                                         Param{Type::f16, core::BinaryOp::kDivide, "OpFDiv"},
+                                         Param{Type::f16, core::BinaryOp::kSubtract, "OpFSub"},
 
-                                         Param{Type::i32, ast::BinaryOp::kAdd, "OpIAdd"},
-                                         Param{Type::i32, ast::BinaryOp::kDivide, "OpSDiv"},
-                                         Param{Type::i32, ast::BinaryOp::kModulo, "OpSRem"},
-                                         Param{Type::i32, ast::BinaryOp::kMultiply, "OpIMul"},
-                                         Param{Type::i32, ast::BinaryOp::kSubtract, "OpISub"},
+                                         Param{Type::i32, core::BinaryOp::kAdd, "OpIAdd"},
+                                         Param{Type::i32, core::BinaryOp::kDivide, "OpSDiv"},
+                                         Param{Type::i32, core::BinaryOp::kModulo, "OpSRem"},
+                                         Param{Type::i32, core::BinaryOp::kMultiply, "OpIMul"},
+                                         Param{Type::i32, core::BinaryOp::kSubtract, "OpISub"},
 
-                                         Param{Type::u32, ast::BinaryOp::kAdd, "OpIAdd"},
-                                         Param{Type::u32, ast::BinaryOp::kDivide, "OpUDiv"},
-                                         Param{Type::u32, ast::BinaryOp::kModulo, "OpUMod"},
-                                         Param{Type::u32, ast::BinaryOp::kMultiply, "OpIMul"},
-                                         Param{Type::u32, ast::BinaryOp::kSubtract, "OpISub"}));
+                                         Param{Type::u32, core::BinaryOp::kAdd, "OpIAdd"},
+                                         Param{Type::u32, core::BinaryOp::kDivide, "OpUDiv"},
+                                         Param{Type::u32, core::BinaryOp::kModulo, "OpUMod"},
+                                         Param{Type::u32, core::BinaryOp::kMultiply, "OpIMul"},
+                                         Param{Type::u32, core::BinaryOp::kSubtract, "OpISub"}));
 
 using BinaryArithVectorScalarMultiplyTest = TestParamHelper<Param>;
 TEST_P(BinaryArithVectorScalarMultiplyTest, VectorScalar) {
@@ -1574,8 +1574,8 @@
 }
 INSTANTIATE_TEST_SUITE_P(SpirvASTPrinterTest,
                          BinaryArithVectorScalarMultiplyTest,
-                         testing::Values(Param{Type::f32, ast::BinaryOp::kMultiply, "OpFMul"},
-                                         Param{Type::f16, ast::BinaryOp::kMultiply, "OpFMul"}));
+                         testing::Values(Param{Type::f32, core::BinaryOp::kMultiply, "OpFMul"},
+                                         Param{Type::f16, core::BinaryOp::kMultiply, "OpFMul"}));
 
 }  // namespace BinaryArithVectorScalar
 
@@ -1634,7 +1634,7 @@
 
 struct Param {
     Type type;
-    ast::BinaryOp op;
+    core::BinaryOp op;
     std::string name;
 };
 
@@ -1696,10 +1696,10 @@
 INSTANTIATE_TEST_SUITE_P(  //
     SpirvASTPrinterTest,
     BinaryArithMatrixMatrix,
-    testing::Values(Param{Type::f32, ast::BinaryOp::kAdd, "OpFAdd"},
-                    Param{Type::f32, ast::BinaryOp::kSubtract, "OpFSub"},
-                    Param{Type::f16, ast::BinaryOp::kAdd, "OpFAdd"},
-                    Param{Type::f16, ast::BinaryOp::kSubtract, "OpFSub"}));
+    testing::Values(Param{Type::f32, core::BinaryOp::kAdd, "OpFAdd"},
+                    Param{Type::f32, core::BinaryOp::kSubtract, "OpFSub"},
+                    Param{Type::f16, core::BinaryOp::kAdd, "OpFAdd"},
+                    Param{Type::f16, core::BinaryOp::kSubtract, "OpFSub"}));
 
 using BinaryArithMatrixMatrixMultiply = TestParamHelper<Param>;
 TEST_P(BinaryArithMatrixMatrixMultiply, Multiply) {
@@ -1755,8 +1755,8 @@
 INSTANTIATE_TEST_SUITE_P(  //
     SpirvASTPrinterTest,
     BinaryArithMatrixMatrixMultiply,
-    testing::Values(Param{Type::f32, ast::BinaryOp::kMultiply, ""},
-                    Param{Type::f16, ast::BinaryOp::kMultiply, ""}));
+    testing::Values(Param{Type::f32, core::BinaryOp::kMultiply, ""},
+                    Param{Type::f16, core::BinaryOp::kMultiply, ""}));
 
 }  // namespace BinaryArithMatrixMatrix
 
diff --git a/src/tint/lang/spirv/writer/ast_printer/entry_point_test.cc b/src/tint/lang/spirv/writer/ast_printer/entry_point_test.cc
index 492e7d2..f473e43 100644
--- a/src/tint/lang/spirv/writer/ast_printer/entry_point_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/entry_point_test.cc
@@ -125,7 +125,7 @@
                              Flat(),
                          });
     auto* cond =
-        create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThan, Expr("loc_in"), Expr(10_u));
+        create<ast::BinaryExpression>(core::BinaryOp::kGreaterThan, Expr("loc_in"), Expr(10_u));
     Func("frag_main", Vector{loc_in}, ty.f32(),
          Vector{
              If(cond, Block(Return(0.5_f))),
diff --git a/src/tint/lang/wgsl/ast/binary_expression.cc b/src/tint/lang/wgsl/ast/binary_expression.cc
index adae2e3..903e6b4 100644
--- a/src/tint/lang/wgsl/ast/binary_expression.cc
+++ b/src/tint/lang/wgsl/ast/binary_expression.cc
@@ -24,7 +24,7 @@
 BinaryExpression::BinaryExpression(GenerationID pid,
                                    NodeID nid,
                                    const Source& src,
-                                   BinaryOp o,
+                                   core::BinaryOp o,
                                    const Expression* l,
                                    const Expression* r)
     : Base(pid, nid, src), op(o), lhs(l), rhs(r) {
@@ -32,7 +32,6 @@
     TINT_ASSERT_GENERATION_IDS_EQUAL_IF_VALID(lhs, generation_id);
     TINT_ASSERT(rhs);
     TINT_ASSERT_GENERATION_IDS_EQUAL_IF_VALID(rhs, generation_id);
-    TINT_ASSERT(op != BinaryOp::kNone);
 }
 
 BinaryExpression::~BinaryExpression() = default;
diff --git a/src/tint/lang/wgsl/ast/binary_expression.h b/src/tint/lang/wgsl/ast/binary_expression.h
index 81eae37..12c39a1 100644
--- a/src/tint/lang/wgsl/ast/binary_expression.h
+++ b/src/tint/lang/wgsl/ast/binary_expression.h
@@ -15,33 +15,11 @@
 #ifndef SRC_TINT_LANG_WGSL_AST_BINARY_EXPRESSION_H_
 #define SRC_TINT_LANG_WGSL_AST_BINARY_EXPRESSION_H_
 
+#include "src/tint/lang/core/binary_op.h"
 #include "src/tint/lang/wgsl/ast/expression.h"
 
 namespace tint::ast {
 
-/// The operator type
-enum class BinaryOp {
-    kNone = 0,
-    kAnd,  // &
-    kOr,   // |
-    kXor,
-    kLogicalAnd,  // &&
-    kLogicalOr,   // ||
-    kEqual,
-    kNotEqual,
-    kLessThan,
-    kGreaterThan,
-    kLessThanEqual,
-    kGreaterThanEqual,
-    kShiftLeft,
-    kShiftRight,
-    kAdd,
-    kSubtract,
-    kMultiply,
-    kDivide,
-    kModulo,
-};
-
 /// An binary expression
 class BinaryExpression final : public Castable<BinaryExpression, Expression> {
   public:
@@ -55,7 +33,7 @@
     BinaryExpression(GenerationID pid,
                      NodeID nid,
                      const Source& source,
-                     BinaryOp op,
+                     core::BinaryOp op,
                      const Expression* lhs,
                      const Expression* rhs);
     /// Move constructor
@@ -63,41 +41,41 @@
     ~BinaryExpression() override;
 
     /// @returns true if the op is and
-    bool IsAnd() const { return op == BinaryOp::kAnd; }
+    bool IsAnd() const { return op == core::BinaryOp::kAnd; }
     /// @returns true if the op is or
-    bool IsOr() const { return op == BinaryOp::kOr; }
+    bool IsOr() const { return op == core::BinaryOp::kOr; }
     /// @returns true if the op is xor
-    bool IsXor() const { return op == BinaryOp::kXor; }
+    bool IsXor() const { return op == core::BinaryOp::kXor; }
     /// @returns true if the op is logical and
-    bool IsLogicalAnd() const { return op == BinaryOp::kLogicalAnd; }
+    bool IsLogicalAnd() const { return op == core::BinaryOp::kLogicalAnd; }
     /// @returns true if the op is logical or
-    bool IsLogicalOr() const { return op == BinaryOp::kLogicalOr; }
+    bool IsLogicalOr() const { return op == core::BinaryOp::kLogicalOr; }
     /// @returns true if the op is equal
-    bool IsEqual() const { return op == BinaryOp::kEqual; }
+    bool IsEqual() const { return op == core::BinaryOp::kEqual; }
     /// @returns true if the op is not equal
-    bool IsNotEqual() const { return op == BinaryOp::kNotEqual; }
+    bool IsNotEqual() const { return op == core::BinaryOp::kNotEqual; }
     /// @returns true if the op is less than
-    bool IsLessThan() const { return op == BinaryOp::kLessThan; }
+    bool IsLessThan() const { return op == core::BinaryOp::kLessThan; }
     /// @returns true if the op is greater than
-    bool IsGreaterThan() const { return op == BinaryOp::kGreaterThan; }
+    bool IsGreaterThan() const { return op == core::BinaryOp::kGreaterThan; }
     /// @returns true if the op is less than equal
-    bool IsLessThanEqual() const { return op == BinaryOp::kLessThanEqual; }
+    bool IsLessThanEqual() const { return op == core::BinaryOp::kLessThanEqual; }
     /// @returns true if the op is greater than equal
-    bool IsGreaterThanEqual() const { return op == BinaryOp::kGreaterThanEqual; }
+    bool IsGreaterThanEqual() const { return op == core::BinaryOp::kGreaterThanEqual; }
     /// @returns true if the op is shift left
-    bool IsShiftLeft() const { return op == BinaryOp::kShiftLeft; }
+    bool IsShiftLeft() const { return op == core::BinaryOp::kShiftLeft; }
     /// @returns true if the op is shift right
-    bool IsShiftRight() const { return op == BinaryOp::kShiftRight; }
+    bool IsShiftRight() const { return op == core::BinaryOp::kShiftRight; }
     /// @returns true if the op is add
-    bool IsAdd() const { return op == BinaryOp::kAdd; }
+    bool IsAdd() const { return op == core::BinaryOp::kAdd; }
     /// @returns true if the op is subtract
-    bool IsSubtract() const { return op == BinaryOp::kSubtract; }
+    bool IsSubtract() const { return op == core::BinaryOp::kSubtract; }
     /// @returns true if the op is multiply
-    bool IsMultiply() const { return op == BinaryOp::kMultiply; }
+    bool IsMultiply() const { return op == core::BinaryOp::kMultiply; }
     /// @returns true if the op is divide
-    bool IsDivide() const { return op == BinaryOp::kDivide; }
+    bool IsDivide() const { return op == core::BinaryOp::kDivide; }
     /// @returns true if the op is modulo
-    bool IsModulo() const { return op == BinaryOp::kModulo; }
+    bool IsModulo() const { return op == core::BinaryOp::kModulo; }
     /// @returns true if the op is an arithmetic operation
     bool IsArithmetic() const;
     /// @returns true if the op is a comparison operation
@@ -116,7 +94,7 @@
     const BinaryExpression* Clone(CloneContext& ctx) const override;
 
     /// the binary op type
-    const BinaryOp op;
+    const core::BinaryOp op;
     /// the left side expression
     const Expression* const lhs;
     /// the right side expression
@@ -125,13 +103,13 @@
 
 /// @param op the operator
 /// @returns true if the op is an arithmetic operation
-inline bool IsArithmetic(BinaryOp op) {
+inline bool IsArithmetic(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kAdd:
-        case BinaryOp::kSubtract:
-        case BinaryOp::kMultiply:
-        case BinaryOp::kDivide:
-        case BinaryOp::kModulo:
+        case core::BinaryOp::kAdd:
+        case core::BinaryOp::kSubtract:
+        case core::BinaryOp::kMultiply:
+        case core::BinaryOp::kDivide:
+        case core::BinaryOp::kModulo:
             return true;
         default:
             return false;
@@ -140,14 +118,14 @@
 
 /// @param op the operator
 /// @returns true if the op is a comparison operation
-inline bool IsComparison(BinaryOp op) {
+inline bool IsComparison(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kEqual:
-        case BinaryOp::kNotEqual:
-        case BinaryOp::kLessThan:
-        case BinaryOp::kLessThanEqual:
-        case BinaryOp::kGreaterThan:
-        case BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kEqual:
+        case core::BinaryOp::kNotEqual:
+        case core::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThanEqual:
             return true;
         default:
             return false;
@@ -156,11 +134,11 @@
 
 /// @param op the operator
 /// @returns true if the op is a bitwise operation
-inline bool IsBitwise(BinaryOp op) {
+inline bool IsBitwise(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kAnd:
-        case BinaryOp::kOr:
-        case BinaryOp::kXor:
+        case core::BinaryOp::kAnd:
+        case core::BinaryOp::kOr:
+        case core::BinaryOp::kXor:
             return true;
         default:
             return false;
@@ -169,10 +147,10 @@
 
 /// @param op the operator
 /// @returns true if the op is a bit shift operation
-inline bool IsBitshift(BinaryOp op) {
+inline bool IsBitshift(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kShiftLeft:
-        case BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftRight:
             return true;
         default:
             return false;
@@ -181,8 +159,8 @@
 
 inline bool BinaryExpression::IsLogical() const {
     switch (op) {
-        case BinaryOp::kLogicalAnd:
-        case BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalOr:
             return true;
         default:
             return false;
@@ -205,103 +183,99 @@
     return ast::IsBitshift(op);
 }
 
-/// @returns the human readable name of the given BinaryOp
-/// @param op the BinaryOp
-constexpr const char* FriendlyName(BinaryOp op) {
+/// @returns the human readable name of the given core::BinaryOp
+/// @param op the core::BinaryOp
+constexpr const char* FriendlyName(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kNone:
-            return "none";
-        case BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             return "and";
-        case BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             return "or";
-        case BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             return "xor";
-        case BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalAnd:
             return "logical_and";
-        case BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalOr:
             return "logical_or";
-        case BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             return "equal";
-        case BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             return "not_equal";
-        case BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             return "less_than";
-        case BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             return "greater_than";
-        case BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             return "less_than_equal";
-        case BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             return "greater_than_equal";
-        case BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             return "shift_left";
-        case BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             return "shift_right";
-        case BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             return "add";
-        case BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             return "subtract";
-        case BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             return "multiply";
-        case BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             return "divide";
-        case BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             return "modulo";
     }
     return "<invalid>";
 }
 
-/// @returns the WGSL operator of the BinaryOp
-/// @param op the BinaryOp
-constexpr const char* Operator(BinaryOp op) {
+/// @returns the WGSL operator of the core::BinaryOp
+/// @param op the core::BinaryOp
+constexpr const char* Operator(core::BinaryOp op) {
     switch (op) {
-        case BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             return "&";
-        case BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             return "|";
-        case BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             return "^";
-        case BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalAnd:
             return "&&";
-        case BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalOr:
             return "||";
-        case BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             return "==";
-        case BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             return "!=";
-        case BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             return "<";
-        case BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             return ">";
-        case BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             return "<=";
-        case BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             return ">=";
-        case BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             return "<<";
-        case BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             return ">>";
-        case BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             return "+";
-        case BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             return "-";
-        case BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             return "*";
-        case BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             return "/";
-        case BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             return "%";
-        default:
-            break;
     }
     return "<invalid>";
 }
 
 /// @param out the stream to write to
-/// @param op the BinaryOp
+/// @param op the core::BinaryOp
 /// @return the stream so calls can be chained
 template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
-auto& operator<<(STREAM& out, BinaryOp op) {
+auto& operator<<(STREAM& out, core::BinaryOp op) {
     out << FriendlyName(op);
     return out;
 }
diff --git a/src/tint/lang/wgsl/ast/binary_expression_test.cc b/src/tint/lang/wgsl/ast/binary_expression_test.cc
index 2790786..0076c47 100644
--- a/src/tint/lang/wgsl/ast/binary_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/binary_expression_test.cc
@@ -24,17 +24,18 @@
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
 
-    auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
+    auto* r = create<BinaryExpression>(core::BinaryOp::kEqual, lhs, rhs);
     EXPECT_EQ(r->lhs, lhs);
     EXPECT_EQ(r->rhs, rhs);
-    EXPECT_EQ(r->op, BinaryOp::kEqual);
+    EXPECT_EQ(r->op, core::BinaryOp::kEqual);
 }
 
 TEST_F(BinaryExpressionTest, Creation_WithSource) {
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
 
-    auto* r = create<BinaryExpression>(Source{Source::Location{20, 2}}, BinaryOp::kEqual, lhs, rhs);
+    auto* r =
+        create<BinaryExpression>(Source{Source::Location{20, 2}}, core::BinaryOp::kEqual, lhs, rhs);
     auto src = r->source;
     EXPECT_EQ(src.range.begin.line, 20u);
     EXPECT_EQ(src.range.begin.column, 2u);
@@ -44,7 +45,7 @@
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
 
-    auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
+    auto* r = create<BinaryExpression>(core::BinaryOp::kEqual, lhs, rhs);
     EXPECT_TRUE(r->Is<BinaryExpression>());
 }
 
@@ -52,7 +53,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.create<BinaryExpression>(BinaryOp::kEqual, nullptr, b.Expr("rhs"));
+            b.create<BinaryExpression>(core::BinaryOp::kEqual, nullptr, b.Expr("rhs"));
         },
         "internal compiler error");
 }
@@ -61,7 +62,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.create<BinaryExpression>(BinaryOp::kEqual, b.Expr("lhs"), nullptr);
+            b.create<BinaryExpression>(core::BinaryOp::kEqual, b.Expr("lhs"), nullptr);
         },
         "internal compiler error");
 }
@@ -71,7 +72,7 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.create<BinaryExpression>(BinaryOp::kEqual, b2.Expr("lhs"), b1.Expr("rhs"));
+            b1.create<BinaryExpression>(core::BinaryOp::kEqual, b2.Expr("lhs"), b1.Expr("rhs"));
         },
         "internal compiler error");
 }
@@ -81,7 +82,7 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.create<BinaryExpression>(BinaryOp::kEqual, b1.Expr("lhs"), b2.Expr("rhs"));
+            b1.create<BinaryExpression>(core::BinaryOp::kEqual, b1.Expr("lhs"), b2.Expr("rhs"));
         },
         "internal compiler error");
 }
diff --git a/src/tint/lang/wgsl/ast/builder.h b/src/tint/lang/wgsl/ast/builder.h
index c2596bc..3b93839 100644
--- a/src/tint/lang/wgsl/ast/builder.h
+++ b/src/tint/lang/wgsl/ast/builder.h
@@ -2001,7 +2001,7 @@
     /// @returns a `ast::BinaryExpression` summing the arguments `lhs` and `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Add(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kAdd, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kAdd, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2011,7 +2011,7 @@
     /// @returns a `ast::BinaryExpression` summing the arguments `lhs` and `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Add(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kAdd,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kAdd,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2021,7 +2021,7 @@
     /// @returns a `ast::BinaryExpression` bitwise anding `lhs` and `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* And(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kAnd, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kAnd, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2030,7 +2030,7 @@
     /// @returns a `ast::BinaryExpression` bitwise or-ing `lhs` and `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Or(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kOr, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kOr, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2039,8 +2039,8 @@
     /// @returns a `ast::BinaryExpression` subtracting `rhs` from `lhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Sub(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kSubtract, Expr(std::forward<LHS>(lhs)),
-                                             Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(
+            core::BinaryOp::kSubtract, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param lhs the left hand argument to the multiplication operation
@@ -2048,8 +2048,8 @@
     /// @returns a `ast::BinaryExpression` multiplying `rhs` from `lhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Mul(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr(std::forward<LHS>(lhs)),
-                                             Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(
+            core::BinaryOp::kMultiply, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param source the source information
@@ -2058,7 +2058,7 @@
     /// @returns a `ast::BinaryExpression` multiplying `rhs` from `lhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Mul(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kMultiply,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kMultiply,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2068,7 +2068,7 @@
     /// @returns a `ast::BinaryExpression` dividing `lhs` by `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Div(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kDivide, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kDivide, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2078,7 +2078,7 @@
     /// @returns a `ast::BinaryExpression` dividing `lhs` by `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Div(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kDivide,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kDivide,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2088,7 +2088,7 @@
     /// @returns a `ast::BinaryExpression` applying modulo of `lhs` by `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Mod(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2097,8 +2097,9 @@
     /// @returns a `ast::BinaryExpression` bit shifting right `lhs` by `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Shr(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(
-            ast::BinaryOp::kShiftRight, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(core::BinaryOp::kShiftRight,
+                                             Expr(std::forward<LHS>(lhs)),
+                                             Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param lhs the left hand argument to the bit shift left operation
@@ -2107,7 +2108,7 @@
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Shl(LHS&& lhs, RHS&& rhs) {
         return create<ast::BinaryExpression>(
-            ast::BinaryOp::kShiftLeft, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
+            core::BinaryOp::kShiftLeft, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param source the source information
@@ -2116,7 +2117,7 @@
     /// @returns a `ast::BinaryExpression` bit shifting left `lhs` by `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Shl(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kShiftLeft,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kShiftLeft,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2126,7 +2127,7 @@
     /// @returns a `ast::BinaryExpression` bitwise xor-ing `lhs` and `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Xor(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kXor, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kXor, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2135,8 +2136,9 @@
     /// @returns a `ast::BinaryExpression` of `lhs` && `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LogicalAnd(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(
-            ast::BinaryOp::kLogicalAnd, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd,
+                                             Expr(std::forward<LHS>(lhs)),
+                                             Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param source the source information
@@ -2145,7 +2147,7 @@
     /// @returns a `ast::BinaryExpression` of `lhs` && `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LogicalAnd(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kLogicalAnd,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kLogicalAnd,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2156,7 +2158,7 @@
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LogicalOr(LHS&& lhs, RHS&& rhs) {
         return create<ast::BinaryExpression>(
-            ast::BinaryOp::kLogicalOr, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
+            core::BinaryOp::kLogicalOr, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param source the source information
@@ -2165,7 +2167,7 @@
     /// @returns a `ast::BinaryExpression` of `lhs` || `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LogicalOr(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kLogicalOr,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kLogicalOr,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2175,7 +2177,7 @@
     /// @returns a `ast::BinaryExpression` of `lhs` > `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* GreaterThan(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThan,
+        return create<ast::BinaryExpression>(core::BinaryOp::kGreaterThan,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2185,7 +2187,7 @@
     /// @returns a `ast::BinaryExpression` of `lhs` >= `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* GreaterThanEqual(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThanEqual,
+        return create<ast::BinaryExpression>(core::BinaryOp::kGreaterThanEqual,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2195,8 +2197,8 @@
     /// @returns a `ast::BinaryExpression` of `lhs` < `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LessThan(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kLessThan, Expr(std::forward<LHS>(lhs)),
-                                             Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(
+            core::BinaryOp::kLessThan, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param lhs the left hand argument to the less than or equal operation
@@ -2204,7 +2206,7 @@
     /// @returns a `ast::BinaryExpression` of `lhs` <= `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* LessThanEqual(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kLessThanEqual,
+        return create<ast::BinaryExpression>(core::BinaryOp::kLessThanEqual,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2214,7 +2216,7 @@
     /// @returns a `ast::BinaryExpression` comparing `lhs` equal to `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Equal(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kEqual, Expr(std::forward<LHS>(lhs)),
+        return create<ast::BinaryExpression>(core::BinaryOp::kEqual, Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
 
@@ -2224,7 +2226,7 @@
     /// @returns a `ast::BinaryExpression` comparing `lhs` equal to `rhs`
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* Equal(const Source& source, LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(source, ast::BinaryOp::kEqual,
+        return create<ast::BinaryExpression>(source, core::BinaryOp::kEqual,
                                              Expr(std::forward<LHS>(lhs)),
                                              Expr(std::forward<RHS>(rhs)));
     }
@@ -2235,8 +2237,8 @@
     ///          disequality
     template <typename LHS, typename RHS>
     const ast::BinaryExpression* NotEqual(LHS&& lhs, RHS&& rhs) {
-        return create<ast::BinaryExpression>(ast::BinaryOp::kNotEqual, Expr(std::forward<LHS>(lhs)),
-                                             Expr(std::forward<RHS>(rhs)));
+        return create<ast::BinaryExpression>(
+            core::BinaryOp::kNotEqual, Expr(std::forward<LHS>(lhs)), Expr(std::forward<RHS>(rhs)));
     }
 
     /// @param source the source information
@@ -2724,7 +2726,7 @@
     const ast::CompoundAssignmentStatement* CompoundAssign(const Source& source,
                                                            LhsExpressionInit&& lhs,
                                                            RhsExpressionInit&& rhs,
-                                                           ast::BinaryOp op) {
+                                                           core::BinaryOp op) {
         return create<ast::CompoundAssignmentStatement>(
             source, Expr(std::forward<LhsExpressionInit>(lhs)),
             Expr(std::forward<RhsExpressionInit>(rhs)), op);
@@ -2739,7 +2741,7 @@
     template <typename LhsExpressionInit, typename RhsExpressionInit>
     const ast::CompoundAssignmentStatement* CompoundAssign(LhsExpressionInit&& lhs,
                                                            RhsExpressionInit&& rhs,
-                                                           ast::BinaryOp op) {
+                                                           core::BinaryOp op) {
         return create<ast::CompoundAssignmentStatement>(Expr(std::forward<LhsExpressionInit>(lhs)),
                                                         Expr(std::forward<RhsExpressionInit>(rhs)),
                                                         op);
diff --git a/src/tint/lang/wgsl/ast/compound_assignment_statement.cc b/src/tint/lang/wgsl/ast/compound_assignment_statement.cc
index 2609bad..2fabc9b 100644
--- a/src/tint/lang/wgsl/ast/compound_assignment_statement.cc
+++ b/src/tint/lang/wgsl/ast/compound_assignment_statement.cc
@@ -26,7 +26,7 @@
                                                          const Source& src,
                                                          const Expression* l,
                                                          const Expression* r,
-                                                         BinaryOp o)
+                                                         core::BinaryOp o)
     : Base(pid, nid, src), lhs(l), rhs(r), op(o) {
     TINT_ASSERT(lhs);
     TINT_ASSERT_GENERATION_IDS_EQUAL_IF_VALID(lhs, generation_id);
diff --git a/src/tint/lang/wgsl/ast/compound_assignment_statement.h b/src/tint/lang/wgsl/ast/compound_assignment_statement.h
index 6e70d1c..10ea942 100644
--- a/src/tint/lang/wgsl/ast/compound_assignment_statement.h
+++ b/src/tint/lang/wgsl/ast/compound_assignment_statement.h
@@ -36,7 +36,7 @@
                                 const Source& source,
                                 const Expression* lhs,
                                 const Expression* rhs,
-                                BinaryOp op);
+                                core::BinaryOp op);
 
     /// Destructor
     ~CompoundAssignmentStatement() override;
@@ -54,7 +54,7 @@
     const Expression* const rhs;
 
     /// the binary operator
-    const BinaryOp op;
+    const core::BinaryOp op;
 };
 
 }  // namespace tint::ast
diff --git a/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc b/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
index a018902..4d3ac96 100644
--- a/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
@@ -27,7 +27,7 @@
 TEST_F(CompoundAssignmentStatementTest, Creation) {
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
-    auto op = BinaryOp::kAdd;
+    auto op = core::BinaryOp::kAdd;
 
     auto* stmt = create<CompoundAssignmentStatement>(lhs, rhs, op);
     EXPECT_EQ(stmt->lhs, lhs);
@@ -38,7 +38,7 @@
 TEST_F(CompoundAssignmentStatementTest, CreationWithSource) {
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
-    auto op = BinaryOp::kMultiply;
+    auto op = core::BinaryOp::kMultiply;
 
     auto* stmt = create<CompoundAssignmentStatement>(Source{Source::Location{20, 2}}, lhs, rhs, op);
     auto src = stmt->source;
@@ -49,7 +49,7 @@
 TEST_F(CompoundAssignmentStatementTest, IsCompoundAssign) {
     auto* lhs = Expr("lhs");
     auto* rhs = Expr("rhs");
-    auto op = BinaryOp::kSubtract;
+    auto op = core::BinaryOp::kSubtract;
 
     auto* stmt = create<CompoundAssignmentStatement>(lhs, rhs, op);
     EXPECT_TRUE(stmt->Is<CompoundAssignmentStatement>());
@@ -59,7 +59,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.create<CompoundAssignmentStatement>(nullptr, b.Expr(1_i), BinaryOp::kAdd);
+            b.create<CompoundAssignmentStatement>(nullptr, b.Expr(1_i), core::BinaryOp::kAdd);
         },
         "internal compiler error");
 }
@@ -68,7 +68,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.create<CompoundAssignmentStatement>(b.Expr(1_i), nullptr, BinaryOp::kAdd);
+            b.create<CompoundAssignmentStatement>(b.Expr(1_i), nullptr, core::BinaryOp::kAdd);
         },
         "internal compiler error");
 }
@@ -78,7 +78,8 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.create<CompoundAssignmentStatement>(b2.Expr("lhs"), b1.Expr("rhs"), BinaryOp::kAdd);
+            b1.create<CompoundAssignmentStatement>(b2.Expr("lhs"), b1.Expr("rhs"),
+                                                   core::BinaryOp::kAdd);
         },
         "internal compiler error");
 }
@@ -88,7 +89,8 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.create<CompoundAssignmentStatement>(b1.Expr("lhs"), b2.Expr("rhs"), BinaryOp::kAdd);
+            b1.create<CompoundAssignmentStatement>(b1.Expr("lhs"), b2.Expr("rhs"),
+                                                   core::BinaryOp::kAdd);
         },
         "internal compiler error");
 }
diff --git a/src/tint/lang/wgsl/ast/for_loop_statement_test.cc b/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
index a8910f4..40894e1 100644
--- a/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
@@ -26,7 +26,7 @@
 
 TEST_F(ForLoopStatementTest, Creation) {
     auto* init = Decl(Var("i", ty.u32()));
-    auto* cond = create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5_u));
+    auto* cond = create<BinaryExpression>(core::BinaryOp::kLessThan, Expr("i"), Expr(5_u));
     auto* cont = Assign("i", Add("i", 1_u));
     auto* body = Block(Return());
     auto* l = For(init, cond, cont, body);
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
index 11c6344..89b0a43 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
@@ -40,7 +40,7 @@
 namespace tint::ast::transform {
 
 /// BinaryOpSignature is tuple of a binary op, LHS type and RHS type
-using BinaryOpSignature = std::tuple<BinaryOp, const type::Type*, const type::Type*>;
+using BinaryOpSignature = std::tuple<core::BinaryOp, const type::Type*, const type::Type*>;
 
 /// PIMPL state for the transform
 struct BuiltinPolyfill::State {
@@ -71,8 +71,8 @@
                         return;  // Don't polyfill @const expressions
                     }
                     switch (bin_op->op) {
-                        case BinaryOp::kShiftLeft:
-                        case BinaryOp::kShiftRight: {
+                        case core::BinaryOp::kShiftLeft:
+                        case core::BinaryOp::kShiftRight: {
                             if (cfg.builtins.bitshift_modulo) {
                                 ctx.Replace(bin_op,
                                             [this, bin_op] { return BitshiftModulo(bin_op); });
@@ -80,7 +80,7 @@
                             }
                             break;
                         }
-                        case BinaryOp::kDivide: {
+                        case core::BinaryOp::kDivide: {
                             if (cfg.builtins.int_div_mod) {
                                 auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
                                 if (lhs_ty->is_integer_scalar_or_vector()) {
@@ -91,7 +91,7 @@
                             }
                             break;
                         }
-                        case BinaryOp::kModulo: {
+                        case core::BinaryOp::kModulo: {
                             if (cfg.builtins.int_div_mod) {
                                 auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
                                 if (lhs_ty->is_integer_scalar_or_vector()) {
@@ -903,7 +903,7 @@
         auto* rhs_ty = src->TypeOf(bin_op->rhs)->UnwrapRef();
         BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
         auto fn = binary_op_polyfills.GetOrCreate(sig, [&] {
-            const bool is_div = bin_op->op == BinaryOp::kDivide;
+            const bool is_div = bin_op->op == core::BinaryOp::kDivide;
 
             const auto [lhs_el_ty, lhs_width] = lhs_ty->Elements(lhs_ty, 1);
             const auto [rhs_el_ty, rhs_width] = rhs_ty->Elements(rhs_ty, 1);
diff --git a/src/tint/lang/wgsl/ast/transform/decompose_memory_access.cc b/src/tint/lang/wgsl/ast/transform/decompose_memory_access.cc
index 35f23c2..d1ebbd1 100644
--- a/src/tint/lang/wgsl/ast/transform/decompose_memory_access.cc
+++ b/src/tint/lang/wgsl/ast/transform/decompose_memory_access.cc
@@ -101,7 +101,7 @@
 /// OffsetBinOp is an implementation of Offset that constructs a binary-op of
 /// two Offsets.
 struct OffsetBinOp : Offset {
-    BinaryOp op;
+    core::BinaryOp op;
     Offset const* lhs = nullptr;
     Offset const* rhs = nullptr;
 
@@ -391,7 +391,7 @@
             }
         }
         auto* out = offsets_.Create<OffsetBinOp>();
-        out->op = BinaryOp::kAdd;
+        out->op = core::BinaryOp::kAdd;
         out->lhs = lhs;
         out->rhs = rhs;
         return out;
@@ -423,7 +423,7 @@
             return offsets_.Create<OffsetLiteral>(lhs_lit->literal * rhs_lit->literal);
         }
         auto* out = offsets_.Create<OffsetBinOp>();
-        out->op = BinaryOp::kMultiply;
+        out->op = core::BinaryOp::kMultiply;
         out->lhs = lhs;
         out->rhs = rhs;
         return out;
@@ -499,7 +499,7 @@
                     TINT_ICE() << "unexpected non-constant array count";
                     arr_cnt = 1;
                 }
-                auto* for_cond = b.create<BinaryExpression>(BinaryOp::kLessThan, b.Expr(i),
+                auto* for_cond = b.create<BinaryExpression>(core::BinaryOp::kLessThan, b.Expr(i),
                                                             b.Expr(u32(arr_cnt.value())));
                 auto* for_cont = b.Assign(i, b.Add(i, 1_u));
                 auto* arr_el = b.IndexAccessor(arr, i);
@@ -585,8 +585,8 @@
                             TINT_ICE() << "unexpected non-constant array count";
                             arr_cnt = 1;
                         }
-                        auto* for_cond = b.create<BinaryExpression>(BinaryOp::kLessThan, b.Expr(i),
-                                                                    b.Expr(u32(arr_cnt.value())));
+                        auto* for_cond = b.create<BinaryExpression>(
+                            core::BinaryOp::kLessThan, b.Expr(i), b.Expr(u32(arr_cnt.value())));
                         auto* for_cont = b.Assign(i, b.Add(i, 1_u));
                         auto* arr_el = b.IndexAccessor(array, i);
                         auto* el_offset = b.Add(b.Expr("offset"), b.Mul(i, u32(arr_ty->Stride())));
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
index c9296e0..cd766a6 100644
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
+++ b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
@@ -61,7 +61,10 @@
     /// @param lhs the lhs expression from the source statement
     /// @param rhs the rhs expression in the destination module
     /// @param op the binary operator
-    void Expand(const Statement* stmt, const Expression* lhs, const Expression* rhs, BinaryOp op) {
+    void Expand(const Statement* stmt,
+                const Expression* lhs,
+                const Expression* rhs,
+                core::BinaryOp op) {
         // Helper function to create the new LHS expression. This will be called
         // twice when building the non-compound assignment statement, so must
         // not produce expressions that cause side effects.
@@ -178,7 +181,7 @@
             state.Expand(assign, assign->lhs, ctx.Clone(assign->rhs), assign->op);
         } else if (auto* inc_dec = node->As<IncrementDecrementStatement>()) {
             // For increment/decrement statements, `i++` becomes `i = i + 1`.
-            auto op = inc_dec->increment ? BinaryOp::kAdd : BinaryOp::kSubtract;
+            auto op = inc_dec->increment ? core::BinaryOp::kAdd : core::BinaryOp::kSubtract;
             state.Expand(inc_dec, inc_dec->lhs, ctx.dst->Expr(1_a), op);
         }
     }
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
index 229003e..5043610 100644
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
@@ -630,7 +630,7 @@
     ProgramBuilder b;
     b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
     auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), BinaryOp::kAdd);
+    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
     auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
     b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
 
@@ -681,7 +681,7 @@
     ProgramBuilder b;
     b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
     auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), BinaryOp::kAdd);
+    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
     auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
     b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
 
@@ -1050,7 +1050,7 @@
     ProgramBuilder b;
     b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
     auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), BinaryOp::kAdd);
+    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
     auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
     b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
 
@@ -1100,7 +1100,7 @@
     ProgramBuilder b;
     b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
     auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), BinaryOp::kAdd);
+    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
     auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
     b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
 
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
index 076ed54..3000954 100644
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
@@ -230,7 +230,7 @@
                 //  }
                 auto idx = b.Symbols().New("idx");
                 auto* init = b.Decl(b.Var(idx, b.ty.u32(), local_index()));
-                auto* cond = b.create<BinaryExpression>(BinaryOp::kLessThan, b.Expr(idx),
+                auto* cond = b.create<BinaryExpression>(core::BinaryOp::kLessThan, b.Expr(idx),
                                                         b.Expr(u32(num_iterations)));
                 auto* cont = b.Assign(
                     idx, b.Add(idx, workgroup_size_const ? b.Expr(u32(workgroup_size_const))
@@ -250,7 +250,7 @@
                 //  if (local_index < num_iterations) {
                 //    ...
                 //  }
-                auto* cond = b.create<BinaryExpression>(BinaryOp::kLessThan, local_index(),
+                auto* cond = b.create<BinaryExpression>(core::BinaryOp::kLessThan, local_index(),
                                                         b.Expr(u32(num_iterations)));
                 auto block = DeclareArrayIndices(num_iterations, array_indices,
                                                  [&] { return b.Expr(local_index()); });
@@ -380,7 +380,7 @@
         for (auto index : array_indices) {
             auto name = array_index_names.at(index);
             auto* mod = (num_iterations > index.modulo)
-                            ? b.create<BinaryExpression>(BinaryOp::kModulo, iteration(),
+                            ? b.create<BinaryExpression>(core::BinaryOp::kModulo, iteration(),
                                                          b.Expr(u32(index.modulo)))
                             : iteration();
             auto* div = (index.division != 1u) ? b.Div(mod, u32(index.division)) : mod;
diff --git a/src/tint/lang/wgsl/ast/while_statement_test.cc b/src/tint/lang/wgsl/ast/while_statement_test.cc
index 5c2be80..ede5cef 100644
--- a/src/tint/lang/wgsl/ast/while_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/while_statement_test.cc
@@ -25,7 +25,7 @@
 using WhileStatementTest = TestHelper;
 
 TEST_F(WhileStatementTest, Creation) {
-    auto* cond = create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5_u));
+    auto* cond = create<BinaryExpression>(core::BinaryOp::kLessThan, Expr("i"), Expr(5_u));
     auto* body = Block(Return());
     auto* l = While(cond, body);
 
@@ -34,7 +34,7 @@
 }
 
 TEST_F(WhileStatementTest, Creation_WithSource) {
-    auto* cond = create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5_u));
+    auto* cond = create<BinaryExpression>(core::BinaryOp::kLessThan, Expr("i"), Expr(5_u));
     auto* body = Block(Return());
     auto* l = While(Source{{20u, 2u}}, cond, body);
     auto src = l->source;
@@ -45,7 +45,7 @@
 TEST_F(WhileStatementTest, Creation_WithAttributes) {
     auto* attr1 = DiagnosticAttribute(core::DiagnosticSeverity::kOff, "foo");
     auto* attr2 = DiagnosticAttribute(core::DiagnosticSeverity::kOff, "bar");
-    auto* cond = create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5_u));
+    auto* cond = create<BinaryExpression>(core::BinaryOp::kLessThan, Expr("i"), Expr(5_u));
     auto* body = Block(Return());
     auto* l = While(cond, body, tint::Vector{attr1, attr2});
 
@@ -66,7 +66,8 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            auto* cond = b.create<BinaryExpression>(BinaryOp::kLessThan, b.Expr("i"), b.Expr(5_u));
+            auto* cond =
+                b.create<BinaryExpression>(core::BinaryOp::kLessThan, b.Expr("i"), b.Expr(5_u));
             b.While(cond, nullptr);
         },
         "internal compiler error");
diff --git a/src/tint/lang/wgsl/reader/parser/additive_expression_test.cc b/src/tint/lang/wgsl/reader/parser/additive_expression_test.cc
index 649d897..5d28f5b 100644
--- a/src/tint/lang/wgsl/reader/parser/additive_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/additive_expression_test.cc
@@ -32,7 +32,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -53,7 +53,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -74,7 +74,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -102,7 +102,7 @@
     // op: -
     // rhs: d
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -113,7 +113,7 @@
     // op: +
     // rhs: c
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -124,7 +124,7 @@
     // op: -
     // rhs: b
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -148,7 +148,7 @@
     // op: -
     // rhs: d
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -159,7 +159,7 @@
     // op: -
     // rhs: b * c
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kSubtract, rel->op);
+    EXPECT_EQ(core::BinaryOp::kSubtract, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -170,7 +170,7 @@
     // op: *
     // rhs: c
     rel = rel->rhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/assignment_stmt_test.cc b/src/tint/lang/wgsl/reader/parser/assignment_stmt_test.cc
index 6a1f323..4f8e283 100644
--- a/src/tint/lang/wgsl/reader/parser/assignment_stmt_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/assignment_stmt_test.cc
@@ -153,7 +153,7 @@
 
 struct CompoundData {
     std::string str;
-    ast::BinaryOp op;
+    core::BinaryOp op;
 };
 using CompoundOpTest = WGSLParserTestWithParam<CompoundData>;
 TEST_P(CompoundOpTest, CompoundOp) {
@@ -187,16 +187,16 @@
 }
 INSTANTIATE_TEST_SUITE_P(WGSLParserTest,
                          CompoundOpTest,
-                         testing::Values(CompoundData{"+=", ast::BinaryOp::kAdd},
-                                         CompoundData{"-=", ast::BinaryOp::kSubtract},
-                                         CompoundData{"*=", ast::BinaryOp::kMultiply},
-                                         CompoundData{"/=", ast::BinaryOp::kDivide},
-                                         CompoundData{"%=", ast::BinaryOp::kModulo},
-                                         CompoundData{"&=", ast::BinaryOp::kAnd},
-                                         CompoundData{"|=", ast::BinaryOp::kOr},
-                                         CompoundData{"^=", ast::BinaryOp::kXor},
-                                         CompoundData{">>=", ast::BinaryOp::kShiftRight},
-                                         CompoundData{"<<=", ast::BinaryOp::kShiftLeft}));
+                         testing::Values(CompoundData{"+=", core::BinaryOp::kAdd},
+                                         CompoundData{"-=", core::BinaryOp::kSubtract},
+                                         CompoundData{"*=", core::BinaryOp::kMultiply},
+                                         CompoundData{"/=", core::BinaryOp::kDivide},
+                                         CompoundData{"%=", core::BinaryOp::kModulo},
+                                         CompoundData{"&=", core::BinaryOp::kAnd},
+                                         CompoundData{"|=", core::BinaryOp::kOr},
+                                         CompoundData{"^=", core::BinaryOp::kXor},
+                                         CompoundData{">>=", core::BinaryOp::kShiftRight},
+                                         CompoundData{"<<=", core::BinaryOp::kShiftLeft}));
 
 TEST_F(WGSLParserTest, AssignmentStmt_MissingEqual) {
     auto p = parser("a.b.c[2].d 123");
diff --git a/src/tint/lang/wgsl/reader/parser/bitwise_expression_test.cc b/src/tint/lang/wgsl/reader/parser/bitwise_expression_test.cc
index 5ca42e3..8791e3e 100644
--- a/src/tint/lang/wgsl/reader/parser/bitwise_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/bitwise_expression_test.cc
@@ -43,7 +43,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kOr, rel->op);
+    EXPECT_EQ(core::BinaryOp::kOr, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -67,7 +67,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kOr, rel->op);
+    EXPECT_EQ(core::BinaryOp::kOr, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -114,7 +114,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kXor, rel->op);
+    EXPECT_EQ(core::BinaryOp::kXor, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -137,7 +137,7 @@
     // rhs: b
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kXor, rel->op);
+    EXPECT_EQ(core::BinaryOp::kXor, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -184,7 +184,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAnd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -207,7 +207,7 @@
     // rhs: b
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAnd, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -242,7 +242,7 @@
     // rhs: true
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAnd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ASSERT_TRUE(rel->rhs->Is<ast::BoolLiteralExpression>());
diff --git a/src/tint/lang/wgsl/reader/parser/expression_test.cc b/src/tint/lang/wgsl/reader/parser/expression_test.cc
index 7f3d38f..a0daed5 100644
--- a/src/tint/lang/wgsl/reader/parser/expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/expression_test.cc
@@ -43,7 +43,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLogicalOr, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -65,7 +65,7 @@
     // lhs: (a || true)
     // rhs: b
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLogicalOr, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -75,7 +75,7 @@
     // lhs: a
     // rhs: true
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLogicalOr, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -110,7 +110,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLogicalAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLogicalAnd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -132,7 +132,7 @@
     // lhs: (a && true)
     // rhs: b
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLogicalAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLogicalAnd, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -190,7 +190,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAnd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -211,7 +211,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLessThanEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLessThanEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/function_attribute_test.cc b/src/tint/lang/wgsl/reader/parser/function_attribute_test.cc
index 5866974..8656b34 100644
--- a/src/tint/lang/wgsl/reader/parser/function_attribute_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/function_attribute_test.cc
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(values[0]->Is<ast::BinaryExpression>());
     auto* expr = values[0]->As<ast::BinaryExpression>();
-    EXPECT_EQ(expr->op, ast::BinaryOp::kAdd);
+    EXPECT_EQ(expr->op, core::BinaryOp::kAdd);
 
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->value, 4);
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->suffix,
@@ -149,7 +149,7 @@
 
     ASSERT_TRUE(values[1]->Is<ast::BinaryExpression>());
     auto* expr = values[1]->As<ast::BinaryExpression>();
-    EXPECT_EQ(expr->op, ast::BinaryOp::kSubtract);
+    EXPECT_EQ(expr->op, core::BinaryOp::kSubtract);
 
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->value, 5);
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->suffix,
@@ -252,7 +252,7 @@
 
     ASSERT_TRUE(values[2]->Is<ast::BinaryExpression>());
     auto* expr = values[2]->As<ast::BinaryExpression>();
-    EXPECT_EQ(expr->op, ast::BinaryOp::kShiftLeft);
+    EXPECT_EQ(expr->op, core::BinaryOp::kShiftLeft);
 
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->value, 6);
     EXPECT_EQ(expr->lhs->As<ast::IntLiteralExpression>()->suffix,
diff --git a/src/tint/lang/wgsl/reader/parser/math_expression_test.cc b/src/tint/lang/wgsl/reader/parser/math_expression_test.cc
index 036fa16..0933817 100644
--- a/src/tint/lang/wgsl/reader/parser/math_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/math_expression_test.cc
@@ -27,7 +27,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -51,7 +51,7 @@
     // op: +
     // rhs: c
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -62,7 +62,7 @@
     // op: *
     // rhs: b
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -83,7 +83,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -107,7 +107,7 @@
     // op: +
     // rhs: b * c
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -118,7 +118,7 @@
     // op: *
     // rhs: c
     rel = rel->rhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/multiplicative_expression_test.cc b/src/tint/lang/wgsl/reader/parser/multiplicative_expression_test.cc
index baab807..87d6b1e 100644
--- a/src/tint/lang/wgsl/reader/parser/multiplicative_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/multiplicative_expression_test.cc
@@ -27,7 +27,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -48,7 +48,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -73,7 +73,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kDivide, rel->op);
+    EXPECT_EQ(core::BinaryOp::kDivide, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -94,7 +94,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kModulo, rel->op);
+    EXPECT_EQ(core::BinaryOp::kModulo, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -118,7 +118,7 @@
     // op: *
     // rhs: e
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -129,7 +129,7 @@
     // op: %
     // rhs: d
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kModulo, rel->op);
+    EXPECT_EQ(core::BinaryOp::kModulo, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -140,7 +140,7 @@
     // op: /
     // rhs: c
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kDivide, rel->op);
+    EXPECT_EQ(core::BinaryOp::kDivide, rel->op);
 
     ASSERT_TRUE(rel->rhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->rhs->As<ast::IdentifierExpression>();
@@ -151,7 +151,7 @@
     // op: *
     // rhs: b
     rel = rel->lhs->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/parser.cc b/src/tint/lang/wgsl/reader/parser/parser.cc
index e3d7796..cbef6b0 100644
--- a/src/tint/lang/wgsl/reader/parser/parser.cc
+++ b/src/tint/lang/wgsl/reader/parser/parser.cc
@@ -2171,16 +2171,16 @@
     const ast::Expression* lhs) {
     auto& t = peek();
 
-    ast::BinaryOp op = ast::BinaryOp::kXor;
+    std::optional<core::BinaryOp> op;
     switch (t.type()) {
         case Token::Type::kAnd:
-            op = ast::BinaryOp::kAnd;
+            op = core::BinaryOp::kAnd;
             break;
         case Token::Type::kOr:
-            op = ast::BinaryOp::kOr;
+            op = core::BinaryOp::kOr;
             break;
         case Token::Type::kXor:
-            op = ast::BinaryOp::kXor;
+            op = core::BinaryOp::kXor;
             break;
         default:
             return Failure::kNoMatch;
@@ -2197,7 +2197,7 @@
                                          std::string(t.to_name()) + " expression");
         }
 
-        lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
+        lhs = create<ast::BinaryExpression>(t.source(), *op, lhs, rhs.value);
 
         if (!match(t.type())) {
             return lhs;
@@ -2210,15 +2210,15 @@
 //   : FORWARD_SLASH
 //   | MODULO
 //   | STAR
-Maybe<ast::BinaryOp> Parser::multiplicative_operator() {
+Maybe<core::BinaryOp> Parser::multiplicative_operator() {
     if (match(Token::Type::kForwardSlash)) {
-        return ast::BinaryOp::kDivide;
+        return core::BinaryOp::kDivide;
     }
     if (match(Token::Type::kMod)) {
-        return ast::BinaryOp::kModulo;
+        return core::BinaryOp::kModulo;
     }
     if (match(Token::Type::kStar)) {
-        return ast::BinaryOp::kMultiply;
+        return core::BinaryOp::kMultiply;
     }
 
     return Failure::kNoMatch;
@@ -2259,9 +2259,9 @@
 //
 // Note, this also splits a `--` token. This is currently safe as the only way to get into
 // here is through additive expression and rules for where `--` are allowed are very restrictive.
-Maybe<ast::BinaryOp> Parser::additive_operator() {
+Maybe<core::BinaryOp> Parser::additive_operator() {
     if (match(Token::Type::kPlus)) {
-        return ast::BinaryOp::kAdd;
+        return core::BinaryOp::kAdd;
     }
 
     auto& t = peek();
@@ -2274,7 +2274,7 @@
         return Failure::kNoMatch;
     }
 
-    return ast::BinaryOp::kSubtract;
+    return core::BinaryOp::kSubtract;
 }
 
 // additive_expression.pos.unary_expression
@@ -2356,12 +2356,12 @@
     auto& t = peek();
     if (match(Token::Type::kShiftLeft) || match(Token::Type::kShiftRight)) {
         std::string name;
-        ast::BinaryOp op = ast::BinaryOp::kNone;
+        std::optional<core::BinaryOp> op;
         if (t.Is(Token::Type::kShiftLeft)) {
-            op = ast::BinaryOp::kShiftLeft;
+            op = core::BinaryOp::kShiftLeft;
             name = "<<";
         } else if (t.Is(Token::Type::kShiftRight)) {
-            op = ast::BinaryOp::kShiftRight;
+            op = core::BinaryOp::kShiftRight;
             name = ">>";
         }
 
@@ -2374,7 +2374,7 @@
             return add_error(rhs_start,
                              std::string("unable to parse right side of ") + name + " expression");
         }
-        return create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
+        return create<ast::BinaryExpression>(t.source(), *op, lhs, rhs.value);
     }
 
     return expect_math_expression_post_unary_expression(lhs);
@@ -2413,25 +2413,25 @@
 
     auto& tok_op = peek();
 
-    ast::BinaryOp op = ast::BinaryOp::kNone;
+    std::optional<core::BinaryOp> op;
     switch (tok_op.type()) {
         case Token::Type::kLessThan:
-            op = ast::BinaryOp::kLessThan;
+            op = core::BinaryOp::kLessThan;
             break;
         case Token::Type::kGreaterThan:
-            op = ast::BinaryOp::kGreaterThan;
+            op = core::BinaryOp::kGreaterThan;
             break;
         case Token::Type::kLessThanEqual:
-            op = ast::BinaryOp::kLessThanEqual;
+            op = core::BinaryOp::kLessThanEqual;
             break;
         case Token::Type::kGreaterThanEqual:
-            op = ast::BinaryOp::kGreaterThanEqual;
+            op = core::BinaryOp::kGreaterThanEqual;
             break;
         case Token::Type::kEqualEqual:
-            op = ast::BinaryOp::kEqual;
+            op = core::BinaryOp::kEqual;
             break;
         case Token::Type::kNotEqual:
-            op = ast::BinaryOp::kNotEqual;
+            op = core::BinaryOp::kNotEqual;
             break;
         default:
             return lhs;
@@ -2449,7 +2449,7 @@
                                       std::string(tok_op.to_name()) + " expression");
     }
 
-    return create<ast::BinaryExpression>(tok_op.source(), op, lhs, rhs.value);
+    return create<ast::BinaryExpression>(tok_op.source(), *op, lhs, rhs.value);
 }
 
 Expect<const ast::Expression*> Parser::expect_expression(std::string_view use) {
@@ -2548,11 +2548,11 @@
 
         auto& t = peek();
         if (t.Is(Token::Type::kAndAnd) || t.Is(Token::Type::kOrOr)) {
-            ast::BinaryOp op = ast::BinaryOp::kNone;
+            core::BinaryOp op;
             if (t.Is(Token::Type::kAndAnd)) {
-                op = ast::BinaryOp::kLogicalAnd;
+                op = core::BinaryOp::kLogicalAnd;
             } else if (t.Is(Token::Type::kOrOr)) {
-                op = ast::BinaryOp::kLogicalOr;
+                op = core::BinaryOp::kLogicalOr;
             }
 
             while (continue_parsing()) {
@@ -2677,32 +2677,32 @@
 //   | xor_equal
 //   | shift_right_equal
 //   | shift_left_equal
-Maybe<ast::BinaryOp> Parser::compound_assignment_operator() {
-    ast::BinaryOp compound_op = ast::BinaryOp::kNone;
+Maybe<core::BinaryOp> Parser::compound_assignment_operator() {
+    std::optional<core::BinaryOp> compound_op;
     if (peek_is(Token::Type::kPlusEqual)) {
-        compound_op = ast::BinaryOp::kAdd;
+        compound_op = core::BinaryOp::kAdd;
     } else if (peek_is(Token::Type::kMinusEqual)) {
-        compound_op = ast::BinaryOp::kSubtract;
+        compound_op = core::BinaryOp::kSubtract;
     } else if (peek_is(Token::Type::kTimesEqual)) {
-        compound_op = ast::BinaryOp::kMultiply;
+        compound_op = core::BinaryOp::kMultiply;
     } else if (peek_is(Token::Type::kDivisionEqual)) {
-        compound_op = ast::BinaryOp::kDivide;
+        compound_op = core::BinaryOp::kDivide;
     } else if (peek_is(Token::Type::kModuloEqual)) {
-        compound_op = ast::BinaryOp::kModulo;
+        compound_op = core::BinaryOp::kModulo;
     } else if (peek_is(Token::Type::kAndEqual)) {
-        compound_op = ast::BinaryOp::kAnd;
+        compound_op = core::BinaryOp::kAnd;
     } else if (peek_is(Token::Type::kOrEqual)) {
-        compound_op = ast::BinaryOp::kOr;
+        compound_op = core::BinaryOp::kOr;
     } else if (peek_is(Token::Type::kXorEqual)) {
-        compound_op = ast::BinaryOp::kXor;
+        compound_op = core::BinaryOp::kXor;
     } else if (peek_is(Token::Type::kShiftLeftEqual)) {
-        compound_op = ast::BinaryOp::kShiftLeft;
+        compound_op = core::BinaryOp::kShiftLeft;
     } else if (peek_is(Token::Type::kShiftRightEqual)) {
-        compound_op = ast::BinaryOp::kShiftRight;
+        compound_op = core::BinaryOp::kShiftRight;
     }
-    if (compound_op != ast::BinaryOp::kNone) {
+    if (compound_op) {
         next();
-        return compound_op;
+        return *compound_op;
     }
     return Failure::kNoMatch;
 }
@@ -2813,7 +2813,7 @@
 
     Source source;
     const ast::Expression* lhs = nullptr;
-    ast::BinaryOp compound_op = ast::BinaryOp::kNone;
+    std::optional<core::BinaryOp> compound_op;
     if (peek_is(Token::Type::kUnderscore)) {
         next();  // Consume the peek.
 
@@ -2865,8 +2865,8 @@
         return add_error(peek(), "unable to parse right side of assignment");
     }
 
-    if (compound_op != ast::BinaryOp::kNone) {
-        return create<ast::CompoundAssignmentStatement>(source, lhs, rhs.value, compound_op);
+    if (compound_op) {
+        return create<ast::CompoundAssignmentStatement>(source, lhs, rhs.value, *compound_op);
     }
     return create<ast::AssignmentStatement>(source, lhs, rhs.value);
 }
diff --git a/src/tint/lang/wgsl/reader/parser/parser.h b/src/tint/lang/wgsl/reader/parser/parser.h
index ac9ecac..4c3f2b4 100644
--- a/src/tint/lang/wgsl/reader/parser/parser.h
+++ b/src/tint/lang/wgsl/reader/parser/parser.h
@@ -578,7 +578,7 @@
         const ast::Expression* lhs);
     /// Parse the `multiplicative_operator` grammar element
     /// @returns the parsed operator if successful
-    Maybe<ast::BinaryOp> multiplicative_operator();
+    Maybe<core::BinaryOp> multiplicative_operator();
     /// Parses multiplicative elements
     /// @param lhs the left side of the expression
     /// @returns the parsed expression or `lhs` if no match
@@ -612,10 +612,10 @@
         const ast::Expression* lhs);
     /// Parse the `additive_operator` grammar element
     /// @returns the parsed operator if successful
-    Maybe<ast::BinaryOp> additive_operator();
+    Maybe<core::BinaryOp> additive_operator();
     /// Parses a `compound_assignment_operator` grammar element
     /// @returns the parsed compound assignment operator
-    Maybe<ast::BinaryOp> compound_assignment_operator();
+    Maybe<core::BinaryOp> compound_assignment_operator();
     /// Parses a `core_lhs_expression` grammar element
     /// @returns the parsed expression or a non-kMatched failure
     Maybe<const ast::Expression*> core_lhs_expression();
diff --git a/src/tint/lang/wgsl/reader/parser/relational_expression_test.cc b/src/tint/lang/wgsl/reader/parser/relational_expression_test.cc
index 9c4d2a2..19fd6b1 100644
--- a/src/tint/lang/wgsl/reader/parser/relational_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/relational_expression_test.cc
@@ -32,7 +32,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLessThan, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLessThan, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kGreaterThan, rel->op);
+    EXPECT_EQ(core::BinaryOp::kGreaterThan, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -82,7 +82,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kLessThanEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kLessThanEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -107,7 +107,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kGreaterThanEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kGreaterThanEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -132,7 +132,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -157,7 +157,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kNotEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kNotEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -196,7 +196,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kGreaterThanEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kGreaterThanEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -240,7 +240,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -265,7 +265,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kNotEqual, rel->op);
+    EXPECT_EQ(core::BinaryOp::kNotEqual, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/shift_expression_test.cc b/src/tint/lang/wgsl/reader/parser/shift_expression_test.cc
index 95c53f8..4e7f6b2 100644
--- a/src/tint/lang/wgsl/reader/parser/shift_expression_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/shift_expression_test.cc
@@ -32,7 +32,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kShiftLeft, rel->op);
+    EXPECT_EQ(core::BinaryOp::kShiftLeft, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -57,7 +57,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kShiftRight, rel->op);
+    EXPECT_EQ(core::BinaryOp::kShiftRight, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -77,7 +77,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -98,7 +98,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
+    EXPECT_EQ(core::BinaryOp::kMultiply, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
@@ -157,7 +157,7 @@
 
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
-    EXPECT_EQ(ast::BinaryOp::kShiftLeft, rel->op);
+    EXPECT_EQ(core::BinaryOp::kShiftLeft, rel->op);
 
     ASSERT_TRUE(rel->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = rel->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/parser/struct_member_attribute_test.cc b/src/tint/lang/wgsl/reader/parser/struct_member_attribute_test.cc
index c3cd0a0..76865bb 100644
--- a/src/tint/lang/wgsl/reader/parser/struct_member_attribute_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/struct_member_attribute_test.cc
@@ -50,7 +50,7 @@
     ASSERT_TRUE(o->expr->Is<ast::BinaryExpression>());
     auto* expr = o->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
@@ -152,7 +152,7 @@
     ASSERT_TRUE(o->expr->Is<ast::BinaryExpression>());
     auto* expr = o->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
diff --git a/src/tint/lang/wgsl/reader/parser/switch_body_test.cc b/src/tint/lang/wgsl/reader/parser/switch_body_test.cc
index 1545e66..450d214 100644
--- a/src/tint/lang/wgsl/reader/parser/switch_body_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/switch_body_test.cc
@@ -60,7 +60,7 @@
     ASSERT_TRUE(sel->expr->Is<ast::BinaryExpression>());
     auto* expr = sel->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 1u);
diff --git a/src/tint/lang/wgsl/reader/parser/type_decl_test.cc b/src/tint/lang/wgsl/reader/parser/type_decl_test.cc
index ccaff08..5fef8f7 100644
--- a/src/tint/lang/wgsl/reader/parser/type_decl_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/type_decl_test.cc
@@ -319,7 +319,7 @@
 
     auto* count = As<ast::BinaryExpression>(arr->arguments[1]);
     ASSERT_NE(count, nullptr);
-    EXPECT_EQ(ast::BinaryOp::kAdd, count->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, count->op);
 
     auto* count_lhs = As<ast::IdentifierExpression>(count->lhs);
     ASSERT_NE(count_lhs, nullptr);
diff --git a/src/tint/lang/wgsl/reader/parser/variable_attribute_test.cc b/src/tint/lang/wgsl/reader/parser/variable_attribute_test.cc
index 1549b05..e31246a 100644
--- a/src/tint/lang/wgsl/reader/parser/variable_attribute_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/variable_attribute_test.cc
@@ -51,7 +51,7 @@
     ASSERT_TRUE(loc->expr->Is<ast::BinaryExpression>());
     auto* expr = loc->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
@@ -150,7 +150,7 @@
     ASSERT_TRUE(loc->expr->Is<ast::BinaryExpression>());
     auto* expr = loc->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
@@ -465,7 +465,7 @@
     ASSERT_TRUE(binding->expr->Is<ast::BinaryExpression>());
     auto* expr = binding->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
@@ -566,7 +566,7 @@
     ASSERT_TRUE(group->expr->Is<ast::BinaryExpression>());
     auto* expr = group->expr->As<ast::BinaryExpression>();
 
-    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    EXPECT_EQ(core::BinaryOp::kAdd, expr->op);
     auto* v = expr->lhs->As<ast::IntLiteralExpression>();
     ASSERT_NE(nullptr, v);
     EXPECT_EQ(v->value, 4u);
diff --git a/src/tint/lang/wgsl/reader/parser/variable_stmt_test.cc b/src/tint/lang/wgsl/reader/parser/variable_stmt_test.cc
index 03c5ffc..c00e33a 100644
--- a/src/tint/lang/wgsl/reader/parser/variable_stmt_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/variable_stmt_test.cc
@@ -164,7 +164,7 @@
 
     ASSERT_TRUE(decl->variable->initializer->Is<ast::BinaryExpression>());
     auto* expr = decl->variable->initializer->As<ast::BinaryExpression>();
-    EXPECT_EQ(expr->op, ast::BinaryOp::kAdd);
+    EXPECT_EQ(expr->op, core::BinaryOp::kAdd);
 
     ASSERT_TRUE(expr->lhs->Is<ast::IdentifierExpression>());
     auto* ident_expr = expr->lhs->As<ast::IdentifierExpression>();
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/binary_test.cc b/src/tint/lang/wgsl/reader/program_to_ir/binary_test.cc
index e115e5a..e07317f 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/binary_test.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/binary_test.cc
@@ -73,7 +73,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundAdd) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kAdd);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kAdd);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -142,7 +142,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundSubtract) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kSubtract);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kSubtract);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -188,7 +188,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundMultiply) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kMultiply);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kMultiply);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -234,7 +234,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundDiv) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kDivide);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kDivide);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -280,7 +280,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundModulo) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kModulo);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kModulo);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -326,7 +326,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundAnd) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.bool_());
-    auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kAnd);
+    auto* expr = CompoundAssign("v1", false, core::BinaryOp::kAnd);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -372,7 +372,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundOr) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.bool_());
-    auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kOr);
+    auto* expr = CompoundAssign("v1", false, core::BinaryOp::kOr);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -418,7 +418,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundXor) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kXor);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kXor);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -674,7 +674,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundShiftLeft) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftLeft);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kShiftLeft);
     WrapInFunction(expr);
 
     auto m = Build();
@@ -720,7 +720,7 @@
 
 TEST_F(ProgramToIRBinaryTest, EmitExpression_Binary_CompoundShiftRight) {
     GlobalVar("v1", core::AddressSpace::kPrivate, ty.u32());
-    auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftRight);
+    auto* expr = CompoundAssign("v1", 1_u, core::BinaryOp::kShiftRight);
     WrapInFunction(expr);
 
     auto m = Build();
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
index 6d97947..6cca0bb 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
@@ -528,7 +528,7 @@
                         : builder_.Constant(1_u);
 
         EmitCompoundAssignment(lhs, one,
-                               stmt->increment ? ast::BinaryOp::kAdd : ast::BinaryOp::kSubtract);
+                               stmt->increment ? core::BinaryOp::kAdd : core::BinaryOp::kSubtract);
     }
 
     void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) {
@@ -542,7 +542,7 @@
         EmitCompoundAssignment(lhs, rhs, stmt->op);
     }
 
-    void EmitCompoundAssignment(ValueOrVecElAccess lhs, ir::Value* rhs, ast::BinaryOp op) {
+    void EmitCompoundAssignment(ValueOrVecElAccess lhs, ir::Value* rhs, core::BinaryOp op) {
         auto b = builder_.Append(current_block_);
         if (auto* v = std::get_if<ir::Value*>(&lhs)) {
             auto* load = b.Load(*v);
@@ -1182,7 +1182,7 @@
                 auto* result = b.InstructionResult(b.ir.Types().bool_());
                 if_inst->SetResults(result);
 
-                if (expr->op == ast::BinaryOp::kLogicalAnd) {
+                if (expr->op == core::BinaryOp::kLogicalAnd) {
                     if_inst->False()->Append(b.ExitIf(if_inst, b.Constant(false)));
                     PushBlock(if_inst->True());
                 } else {
@@ -1216,8 +1216,8 @@
                 tint::Switch(
                     expr,  //
                     [&](const ast::BinaryExpression* e) {
-                        if (e->op == ast::BinaryOp::kLogicalAnd ||
-                            e->op == ast::BinaryOp::kLogicalOr) {
+                        if (e->op == core::BinaryOp::kLogicalAnd ||
+                            e->op == core::BinaryOp::kLogicalOr) {
                             tasks.Push([=] { EndShortCircuit(e); });
                             tasks.Push([=] { Process(e->rhs); });
                             tasks.Push([=] { BeginShortCircuit(e); });
@@ -1349,47 +1349,44 @@
             });
     }
 
-    ir::Binary* BinaryOp(const type::Type* ty, ir::Value* lhs, ir::Value* rhs, ast::BinaryOp op) {
+    ir::Binary* BinaryOp(const type::Type* ty, ir::Value* lhs, ir::Value* rhs, core::BinaryOp op) {
         switch (op) {
-            case ast::BinaryOp::kAnd:
+            case core::BinaryOp::kAnd:
                 return builder_.And(ty, lhs, rhs);
-            case ast::BinaryOp::kOr:
+            case core::BinaryOp::kOr:
                 return builder_.Or(ty, lhs, rhs);
-            case ast::BinaryOp::kXor:
+            case core::BinaryOp::kXor:
                 return builder_.Xor(ty, lhs, rhs);
-            case ast::BinaryOp::kEqual:
+            case core::BinaryOp::kEqual:
                 return builder_.Equal(ty, lhs, rhs);
-            case ast::BinaryOp::kNotEqual:
+            case core::BinaryOp::kNotEqual:
                 return builder_.NotEqual(ty, lhs, rhs);
-            case ast::BinaryOp::kLessThan:
+            case core::BinaryOp::kLessThan:
                 return builder_.LessThan(ty, lhs, rhs);
-            case ast::BinaryOp::kGreaterThan:
+            case core::BinaryOp::kGreaterThan:
                 return builder_.GreaterThan(ty, lhs, rhs);
-            case ast::BinaryOp::kLessThanEqual:
+            case core::BinaryOp::kLessThanEqual:
                 return builder_.LessThanEqual(ty, lhs, rhs);
-            case ast::BinaryOp::kGreaterThanEqual:
+            case core::BinaryOp::kGreaterThanEqual:
                 return builder_.GreaterThanEqual(ty, lhs, rhs);
-            case ast::BinaryOp::kShiftLeft:
+            case core::BinaryOp::kShiftLeft:
                 return builder_.ShiftLeft(ty, lhs, rhs);
-            case ast::BinaryOp::kShiftRight:
+            case core::BinaryOp::kShiftRight:
                 return builder_.ShiftRight(ty, lhs, rhs);
-            case ast::BinaryOp::kAdd:
+            case core::BinaryOp::kAdd:
                 return builder_.Add(ty, lhs, rhs);
-            case ast::BinaryOp::kSubtract:
+            case core::BinaryOp::kSubtract:
                 return builder_.Subtract(ty, lhs, rhs);
-            case ast::BinaryOp::kMultiply:
+            case core::BinaryOp::kMultiply:
                 return builder_.Multiply(ty, lhs, rhs);
-            case ast::BinaryOp::kDivide:
+            case core::BinaryOp::kDivide:
                 return builder_.Divide(ty, lhs, rhs);
-            case ast::BinaryOp::kModulo:
+            case core::BinaryOp::kModulo:
                 return builder_.Modulo(ty, lhs, rhs);
-            case ast::BinaryOp::kLogicalAnd:
-            case ast::BinaryOp::kLogicalOr:
+            case core::BinaryOp::kLogicalAnd:
+            case core::BinaryOp::kLogicalOr:
                 TINT_ICE() << "short circuit op should have already been handled";
                 return nullptr;
-            case ast::BinaryOp::kNone:
-                TINT_ICE() << "missing binary operand type";
-                return nullptr;
         }
         TINT_UNREACHABLE();
         return nullptr;
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/var_test.cc b/src/tint/lang/wgsl/reader/program_to_ir/var_test.cc
index b6b4f44..9dcd669 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/var_test.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/var_test.cc
@@ -287,7 +287,7 @@
 
 TEST_F(ProgramToIRVarTest, Emit_Var_CompoundAssign_42i) {
     WrapInFunction(Var("a", ty.i32(), core::AddressSpace::kFunction),  //
-                   CompoundAssign("a", 42_i, ast::BinaryOp::kAdd));
+                   CompoundAssign("a", 42_i, core::BinaryOp::kAdd));
 
     auto m = Build();
     ASSERT_TRUE(m) << (!m ? m.Failure() : "");
@@ -326,7 +326,7 @@
 
     WrapInFunction(
         Var("a", ty.array<array<array<i32, 5>, 5>, 5>(), core::AddressSpace::kFunction),  //
-        CompoundAssign(lhs, rhs, ast::BinaryOp::kAdd));
+        CompoundAssign(lhs, rhs, core::BinaryOp::kAdd));
 
     auto m = Build();
     ASSERT_TRUE(m) << (!m ? m.Failure() : "");
@@ -378,7 +378,7 @@
             Call("f", 6_i));
 
     WrapInFunction(Var("a", ty.array<mat3x4<f32>, 5>(), core::AddressSpace::kFunction),  //
-                   CompoundAssign(lhs, rhs, ast::BinaryOp::kAdd));
+                   CompoundAssign(lhs, rhs, core::BinaryOp::kAdd));
 
     auto m = Build();
     ASSERT_TRUE(m) << (!m ? m.Failure() : "");
diff --git a/src/tint/lang/wgsl/resolver/alias_analysis_test.cc b/src/tint/lang/wgsl/resolver/alias_analysis_test.cc
index ea9fc24..fe9b36e 100644
--- a/src/tint/lang/wgsl/resolver/alias_analysis_test.cc
+++ b/src/tint/lang/wgsl/resolver/alias_analysis_test.cc
@@ -525,7 +525,7 @@
 
 TEST_P(Use, Write_CompoundAssignment_LHS) {
     // *p2 += 42;
-    Run(CompoundAssign(Deref("p2"), 42_a, ast::BinaryOp::kAdd),
+    Run(CompoundAssign(Deref("p2"), 42_a, core::BinaryOp::kAdd),
         R"(56:78 error: invalid aliased pointer argument
 12:34 note: aliases with another argument passed here)");
 }
@@ -534,7 +534,7 @@
     // var<private> global : i32;
     // global += *p2;
     GlobalVar("global", core::AddressSpace::kPrivate, ty.i32());
-    Run(CompoundAssign("global", Deref("p2"), ast::BinaryOp::kAdd),
+    Run(CompoundAssign("global", Deref("p2"), core::BinaryOp::kAdd),
         R"(56:78 error: invalid aliased pointer argument
 12:34 note: aliases with another argument passed here)");
 }
diff --git a/src/tint/lang/wgsl/resolver/compound_assignment_validation_test.cc b/src/tint/lang/wgsl/resolver/compound_assignment_validation_test.cc
index b1c44c0..1f44f6b 100644
--- a/src/tint/lang/wgsl/resolver/compound_assignment_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/compound_assignment_validation_test.cc
@@ -31,7 +31,7 @@
     // var a : i32 = 2;
     // a += 2
     auto* var = Var("a", ty.i32(), Expr(2_i));
-    WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2_i, ast::BinaryOp::kAdd));
+    WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2_i, core::BinaryOp::kAdd));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -42,7 +42,7 @@
     // a += 2
     auto* myint = Alias("myint", ty.i32());
     auto* var = Var("a", ty.Of(myint), Expr(2_i));
-    WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2_i, ast::BinaryOp::kAdd));
+    WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2_i, core::BinaryOp::kAdd));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -54,7 +54,7 @@
     auto* var_a = Var("a", ty.i32(), core::AddressSpace::kFunction, Expr(2_i));
     auto* var_b = Let("b", ty.ptr<function, i32>(), AddressOf(Expr("a")));
     WrapInFunction(var_a, var_b,
-                   CompoundAssign(Source{{12, 34}}, Deref("b"), 2_i, ast::BinaryOp::kAdd));
+                   CompoundAssign(Source{{12, 34}}, Deref("b"), 2_i, core::BinaryOp::kAdd));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -67,7 +67,7 @@
 
     auto* var = Var("a", ty.i32(), Expr(2_i));
 
-    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2.3_f, ast::BinaryOp::kAdd);
+    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2.3_f, core::BinaryOp::kAdd);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -84,7 +84,7 @@
 
     auto* var = Var("a", ty.f32(), Expr(1_f));
 
-    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2_f, ast::BinaryOp::kOr);
+    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2_f, core::BinaryOp::kOr);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -101,7 +101,7 @@
 
     auto* var = Var("a", ty.vec4<f32>());
 
-    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 1_f, ast::BinaryOp::kAdd);
+    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 1_f, core::BinaryOp::kAdd);
     WrapInFunction(var, assign);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -115,7 +115,7 @@
 
     auto* var = Var("a", ty.f32());
 
-    auto* assign = CompoundAssign(Source{{12, 34}}, "a", Call<vec4<f32>>(), ast::BinaryOp::kAdd);
+    auto* assign = CompoundAssign(Source{{12, 34}}, "a", Call<vec4<f32>>(), core::BinaryOp::kAdd);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -131,7 +131,7 @@
 
     auto* var = Var("a", ty.mat4x4<f32>());
 
-    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2_f, ast::BinaryOp::kMultiply);
+    auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2_f, core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -146,7 +146,7 @@
     auto* var = Var("a", ty.f32());
 
     auto* assign =
-        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x4<f32>>(), ast::BinaryOp::kMultiply);
+        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x4<f32>>(), core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -163,7 +163,7 @@
     auto* var = Var("a", ty.vec4<f32>());
 
     auto* assign =
-        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x4<f32>>(), ast::BinaryOp::kMultiply);
+        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x4<f32>>(), core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -178,7 +178,7 @@
     auto* var = Var("a", ty.vec4<f32>());
 
     auto* assign =
-        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x2<f32>>(), ast::BinaryOp::kMultiply);
+        CompoundAssign(Source{{12, 34}}, "a", Call<mat4x2<f32>>(), core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -197,7 +197,7 @@
     auto* var = Var("a", ty.vec4<f32>());
 
     auto* assign =
-        CompoundAssign(Source{{12, 34}}, "a", Call<mat2x4<f32>>(), ast::BinaryOp::kMultiply);
+        CompoundAssign(Source{{12, 34}}, "a", Call<mat2x4<f32>>(), core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -214,7 +214,7 @@
     auto* var = Var("a", ty.mat4x4<f32>());
 
     auto* assign =
-        CompoundAssign(Source{{12, 34}}, "a", Call<vec4<f32>>(), ast::BinaryOp::kMultiply);
+        CompoundAssign(Source{{12, 34}}, "a", Call<vec4<f32>>(), core::BinaryOp::kMultiply);
     WrapInFunction(var, assign);
 
     ASSERT_FALSE(r()->Resolve());
@@ -226,7 +226,7 @@
     // {
     //   _ += 1i;
     // }
-    WrapInFunction(CompoundAssign(Source{{56, 78}}, Phony(), 1_i, ast::BinaryOp::kAdd));
+    WrapInFunction(CompoundAssign(Source{{56, 78}}, Phony(), 1_i, core::BinaryOp::kAdd));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_THAT(r()->error(),
                 HasSubstr("56:78 error: no matching overload for operator += (void, i32)"));
@@ -239,7 +239,7 @@
     // }
     GlobalVar(Source{{12, 34}}, "a", ty.i32(), core::AddressSpace::kStorage, core::Access::kRead,
               Group(0_a), Binding(0_a));
-    WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1_i, ast::BinaryOp::kAdd));
+    WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1_i, core::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -250,7 +250,7 @@
     // let a = 1i;
     // a += 1i;
     auto* a = Let(Source{{12, 34}}, "a", Expr(1_i));
-    WrapInFunction(a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1_i, ast::BinaryOp::kAdd));
+    WrapInFunction(a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1_i, core::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to let 'a'
@@ -260,7 +260,7 @@
 
 TEST_F(ResolverCompoundAssignmentValidationTest, LhsLiteral) {
     // 1i += 1i;
-    WrapInFunction(CompoundAssign(Expr(Source{{56, 78}}, 1_i), 1_i, ast::BinaryOp::kAdd));
+    WrapInFunction(CompoundAssign(Expr(Source{{56, 78}}, 1_i), 1_i, core::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to value expression of type 'i32')");
@@ -270,7 +270,7 @@
     // var<workgroup> a : atomic<i32>;
     // a += a;
     GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), core::AddressSpace::kWorkgroup);
-    WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", "a", ast::BinaryOp::kAdd));
+    WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", "a", core::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_THAT(
diff --git a/src/tint/lang/wgsl/resolver/const_eval_binary_op_test.cc b/src/tint/lang/wgsl/resolver/const_eval_binary_op_test.cc
index c648ada..35abf79 100644
--- a/src/tint/lang/wgsl/resolver/const_eval_binary_op_test.cc
+++ b/src/tint/lang/wgsl/resolver/const_eval_binary_op_test.cc
@@ -82,7 +82,7 @@
     return o;
 }
 
-using ResolverConstEvalBinaryOpTest = ResolverTestWithParam<std::tuple<ast::BinaryOp, Case>>;
+using ResolverConstEvalBinaryOpTest = ResolverTestWithParam<std::tuple<core::BinaryOp, Case>>;
 TEST_P(ResolverConstEvalBinaryOpTest, Test) {
     Enable(core::Extension::kF16);
     auto op = std::get<0>(GetParam());
@@ -113,7 +113,7 @@
 
 INSTANTIATE_TEST_SUITE_P(MixedAbstractArgs,
                          ResolverConstEvalBinaryOpTest,
-                         testing::Combine(testing::Values(ast::BinaryOp::kAdd),
+                         testing::Combine(testing::Values(core::BinaryOp::kAdd),
                                           testing::ValuesIn(std::vector{
                                               // Mixed abstract type args
                                               C(1_a, 2.3_a, 3.3_a),
@@ -168,7 +168,7 @@
 }
 INSTANTIATE_TEST_SUITE_P(Add,
                          ResolverConstEvalBinaryOpTest,
-                         testing::Combine(testing::Values(ast::BinaryOp::kAdd),
+                         testing::Combine(testing::Values(core::BinaryOp::kAdd),
                                           testing::ValuesIn(Concat(  //
                                               OpAddIntCases<AInt>(),
                                               OpAddIntCases<i32>(),
@@ -224,7 +224,7 @@
 }
 INSTANTIATE_TEST_SUITE_P(Sub,
                          ResolverConstEvalBinaryOpTest,
-                         testing::Combine(testing::Values(ast::BinaryOp::kSubtract),
+                         testing::Combine(testing::Values(core::BinaryOp::kSubtract),
                                           testing::ValuesIn(Concat(  //
                                               OpSubIntCases<AInt>(),
                                               OpSubIntCases<i32>(),
@@ -401,7 +401,7 @@
 INSTANTIATE_TEST_SUITE_P(Mul,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kMultiply),
+                             testing::Values(core::BinaryOp::kMultiply),
                              testing::ValuesIn(Concat(  //
                                  OpMulScalarCases<AInt>(),
                                  OpMulScalarCases<i32>(),
@@ -478,7 +478,7 @@
 INSTANTIATE_TEST_SUITE_P(Div,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kDivide),
+                             testing::Values(core::BinaryOp::kDivide),
                              testing::ValuesIn(Concat(  //
                                  OpDivIntCases<AInt>(),
                                  OpDivIntCases<i32>(),
@@ -633,7 +633,7 @@
 INSTANTIATE_TEST_SUITE_P(Mod,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kModulo),
+                             testing::Values(core::BinaryOp::kModulo),
                              testing::ValuesIn(Concat(  //
                                  OpModCases<AInt>(),
                                  OpModCases<i32>(),
@@ -657,7 +657,7 @@
 INSTANTIATE_TEST_SUITE_P(Equal,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kEqual),
+                             testing::Values(core::BinaryOp::kEqual),
                              testing::ValuesIn(Concat(  //
                                  OpEqualCases<AInt, true>(),
                                  OpEqualCases<i32, true>(),
@@ -669,7 +669,7 @@
 INSTANTIATE_TEST_SUITE_P(NotEqual,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kNotEqual),
+                             testing::Values(core::BinaryOp::kNotEqual),
                              testing::ValuesIn(Concat(  //
                                  OpEqualCases<AInt, false>(),
                                  OpEqualCases<i32, false>(),
@@ -695,7 +695,7 @@
 INSTANTIATE_TEST_SUITE_P(LessThan,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kLessThan),
+                             testing::Values(core::BinaryOp::kLessThan),
                              testing::ValuesIn(Concat(  //
                                  OpLessThanCases<AInt, true>(),
                                  OpLessThanCases<i32, true>(),
@@ -706,7 +706,7 @@
 INSTANTIATE_TEST_SUITE_P(GreaterThanEqual,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kGreaterThanEqual),
+                             testing::Values(core::BinaryOp::kGreaterThanEqual),
                              testing::ValuesIn(Concat(  //
                                  OpLessThanCases<AInt, false>(),
                                  OpLessThanCases<i32, false>(),
@@ -731,7 +731,7 @@
 INSTANTIATE_TEST_SUITE_P(GreaterThan,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kGreaterThan),
+                             testing::Values(core::BinaryOp::kGreaterThan),
                              testing::ValuesIn(Concat(  //
                                  OpGreaterThanCases<AInt, true>(),
                                  OpGreaterThanCases<i32, true>(),
@@ -742,7 +742,7 @@
 INSTANTIATE_TEST_SUITE_P(LessThanEqual,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kLessThanEqual),
+                             testing::Values(core::BinaryOp::kLessThanEqual),
                              testing::ValuesIn(Concat(  //
                                  OpGreaterThanCases<AInt, false>(),
                                  OpGreaterThanCases<i32, false>(),
@@ -753,7 +753,7 @@
 
 // Test that we can compare the maximum and minimum AFloat values in vectors (crbug.com/tint/1999).
 struct AbstractFloatVectorCompareCase {
-    ast::BinaryOp op;
+    core::BinaryOp op;
     bool expected_0;
     bool expected_1;
 };
@@ -777,12 +777,13 @@
 INSTANTIATE_TEST_SUITE_P(
     HighestLowest,
     ResolverConstEvalBinaryOpAbstractFloatVectorCompareTest,
-    testing::Values(AbstractFloatVectorCompareCase{ast::BinaryOp::kEqual, false, false},
-                    AbstractFloatVectorCompareCase{ast::BinaryOp::kNotEqual, true, true},
-                    AbstractFloatVectorCompareCase{ast::BinaryOp::kLessThan, false, true},
-                    AbstractFloatVectorCompareCase{ast::BinaryOp::kLessThanEqual, false, true},
-                    AbstractFloatVectorCompareCase{ast::BinaryOp::kGreaterThan, true, false},
-                    AbstractFloatVectorCompareCase{ast::BinaryOp::kGreaterThanEqual, true, false}));
+    testing::Values(AbstractFloatVectorCompareCase{core::BinaryOp::kEqual, false, false},
+                    AbstractFloatVectorCompareCase{core::BinaryOp::kNotEqual, true, true},
+                    AbstractFloatVectorCompareCase{core::BinaryOp::kLessThan, false, true},
+                    AbstractFloatVectorCompareCase{core::BinaryOp::kLessThanEqual, false, true},
+                    AbstractFloatVectorCompareCase{core::BinaryOp::kGreaterThan, true, false},
+                    AbstractFloatVectorCompareCase{core::BinaryOp::kGreaterThanEqual, true,
+                                                   false}));
 
 static std::vector<Case> OpLogicalAndCases() {
     return {
@@ -795,7 +796,7 @@
 INSTANTIATE_TEST_SUITE_P(LogicalAnd,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kLogicalAnd),
+                             testing::Values(core::BinaryOp::kLogicalAnd),
                              testing::ValuesIn(OpLogicalAndCases())));
 
 static std::vector<Case> OpLogicalOrCases() {
@@ -809,7 +810,7 @@
 INSTANTIATE_TEST_SUITE_P(LogicalOr,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kLogicalOr),
+                             testing::Values(core::BinaryOp::kLogicalOr),
                              testing::ValuesIn(OpLogicalOrCases())));
 
 static std::vector<Case> OpAndBoolCases() {
@@ -857,7 +858,7 @@
 INSTANTIATE_TEST_SUITE_P(And,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kAnd),
+                             testing::Values(core::BinaryOp::kAnd),
                              testing::ValuesIn(            //
                                  Concat(OpAndBoolCases(),  //
                                         OpAndIntCases<AInt>(),
@@ -909,7 +910,7 @@
 INSTANTIATE_TEST_SUITE_P(Or,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kOr),
+                             testing::Values(core::BinaryOp::kOr),
                              testing::ValuesIn(Concat(OpOrBoolCases(),
                                                       OpOrIntCases<AInt>(),
                                                       OpOrIntCases<i32>(),
@@ -973,7 +974,7 @@
 INSTANTIATE_TEST_SUITE_P(Xor,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kXor),
+                             testing::Values(core::BinaryOp::kXor),
                              testing::ValuesIn(Concat(XorCases<AInt>(),  //
                                                       XorCases<i32>(),   //
                                                       XorCases<u32>()))));
@@ -1088,7 +1089,7 @@
 INSTANTIATE_TEST_SUITE_P(ShiftLeft,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kShiftLeft),
+                             testing::Values(core::BinaryOp::kShiftLeft),
                              testing::ValuesIn(Concat(ShiftLeftCases<AInt>(),  //
                                                       ShiftLeftCases<i32>(),   //
                                                       ShiftLeftCases<u32>()))));
@@ -1145,7 +1146,7 @@
     AbstractMixed,
     ResolverConstEvalBinaryOpTest,
     testing::Combine(
-        testing::Values(ast::BinaryOp::kAdd),
+        testing::Values(core::BinaryOp::kAdd),
         testing::Values(C(Val(1_a), Val(2.3_a), Val(3.3_a)),
                         C(Val(2.3_a), Val(1_a), Val(3.3_a)),
                         C(Val(1_a), Vec(2.3_a, 2.3_a, 2.3_a), Vec(3.3_a, 3.3_a, 3.3_a)),
@@ -1413,7 +1414,7 @@
 INSTANTIATE_TEST_SUITE_P(ShiftRight,
                          ResolverConstEvalBinaryOpTest,
                          testing::Combine(  //
-                             testing::Values(ast::BinaryOp::kShiftRight),
+                             testing::Values(core::BinaryOp::kShiftRight),
                              testing::ValuesIn(Concat(ShiftRightCases<AInt>(),  //
                                                       ShiftRightCases<i32>(),   //
                                                       ShiftRightCases<u32>()))));
diff --git a/src/tint/lang/wgsl/resolver/load_test.cc b/src/tint/lang/wgsl/resolver/load_test.cc
index 1d021b5..26cb8c8 100644
--- a/src/tint/lang/wgsl/resolver/load_test.cc
+++ b/src/tint/lang/wgsl/resolver/load_test.cc
@@ -82,7 +82,7 @@
     auto* ident = Expr("ref");
     WrapInFunction(Var("ref", Expr(1_i)),  //
                    Var("v", ty.i32()),     //
-                   CompoundAssign("v", ident, ast::BinaryOp::kAdd));
+                   CompoundAssign("v", ident, core::BinaryOp::kAdd));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
     auto* load = Sem().Get<sem::Load>(ident);
diff --git a/src/tint/lang/wgsl/resolver/materialize_test.cc b/src/tint/lang/wgsl/resolver/materialize_test.cc
index b260f29..28fc798 100644
--- a/src/tint/lang/wgsl/resolver/materialize_test.cc
+++ b/src/tint/lang/wgsl/resolver/materialize_test.cc
@@ -401,7 +401,7 @@
         }
         case Method::kCompoundAssign:
             WrapInFunction(Decl(Var("a", target_ty())),
-                           CompoundAssign("a", abstract_expr, ast::BinaryOp::kAdd));
+                           CompoundAssign("a", abstract_expr, core::BinaryOp::kAdd));
             break;
     }
 
diff --git a/src/tint/lang/wgsl/resolver/resolver_test.cc b/src/tint/lang/wgsl/resolver/resolver_test.cc
index d5da8bc..daeea7c 100644
--- a/src/tint/lang/wgsl/resolver/resolver_test.cc
+++ b/src/tint/lang/wgsl/resolver/resolver_test.cc
@@ -68,7 +68,7 @@
 using alias2 = builder::alias2<T>;
 template <typename T>
 using alias3 = builder::alias3<T>;
-using Op = ast::BinaryOp;
+using Op = core::BinaryOp;
 
 TEST_F(ResolverTest, Stmt_Assign) {
     auto* v = Var("v", ty.f32());
@@ -1368,7 +1368,7 @@
 };
 
 struct Params {
-    ast::BinaryOp op;
+    core::BinaryOp op;
     builder::ast_type_func_ptr create_lhs_type;
     builder::ast_type_func_ptr create_rhs_type;
     builder::ast_type_func_ptr create_lhs_alias_type;
@@ -1377,7 +1377,7 @@
 };
 
 template <typename LHS, typename RHS, typename RES>
-constexpr Params ParamsFor(ast::BinaryOp op) {
+constexpr Params ParamsFor(core::BinaryOp op) {
     return Params{op,
                   DataType<LHS>::AST,
                   DataType<RHS>::AST,
@@ -1386,25 +1386,25 @@
                   DataType<RES>::Sem};
 }
 
-static constexpr ast::BinaryOp all_ops[] = {
-    ast::BinaryOp::kAnd,
-    ast::BinaryOp::kOr,
-    ast::BinaryOp::kXor,
-    ast::BinaryOp::kLogicalAnd,
-    ast::BinaryOp::kLogicalOr,
-    ast::BinaryOp::kEqual,
-    ast::BinaryOp::kNotEqual,
-    ast::BinaryOp::kLessThan,
-    ast::BinaryOp::kGreaterThan,
-    ast::BinaryOp::kLessThanEqual,
-    ast::BinaryOp::kGreaterThanEqual,
-    ast::BinaryOp::kShiftLeft,
-    ast::BinaryOp::kShiftRight,
-    ast::BinaryOp::kAdd,
-    ast::BinaryOp::kSubtract,
-    ast::BinaryOp::kMultiply,
-    ast::BinaryOp::kDivide,
-    ast::BinaryOp::kModulo,
+static constexpr core::BinaryOp all_ops[] = {
+    core::BinaryOp::kAnd,
+    core::BinaryOp::kOr,
+    core::BinaryOp::kXor,
+    core::BinaryOp::kLogicalAnd,
+    core::BinaryOp::kLogicalOr,
+    core::BinaryOp::kEqual,
+    core::BinaryOp::kNotEqual,
+    core::BinaryOp::kLessThan,
+    core::BinaryOp::kGreaterThan,
+    core::BinaryOp::kLessThanEqual,
+    core::BinaryOp::kGreaterThanEqual,
+    core::BinaryOp::kShiftLeft,
+    core::BinaryOp::kShiftRight,
+    core::BinaryOp::kAdd,
+    core::BinaryOp::kSubtract,
+    core::BinaryOp::kMultiply,
+    core::BinaryOp::kDivide,
+    core::BinaryOp::kModulo,
 };
 
 static constexpr builder::ast_type_func_ptr all_create_type_funcs[] = {
@@ -1700,11 +1700,11 @@
 // (type * type * op), and processing only the triplets that are not found in
 // the `all_valid_cases` table.
 using Expr_Binary_Test_Invalid = ResolverTestWithParam<
-    std::tuple<builder::ast_type_func_ptr, builder::ast_type_func_ptr, ast::BinaryOp>>;
+    std::tuple<builder::ast_type_func_ptr, builder::ast_type_func_ptr, core::BinaryOp>>;
 TEST_P(Expr_Binary_Test_Invalid, All) {
     const builder::ast_type_func_ptr& lhs_create_type_func = std::get<0>(GetParam());
     const builder::ast_type_func_ptr& rhs_create_type_func = std::get<1>(GetParam());
-    const ast::BinaryOp op = std::get<2>(GetParam());
+    const core::BinaryOp op = std::get<2>(GetParam());
 
     // Skip if valid case
     // TODO(amaiorano): replace linear lookup with O(1) if too slow
diff --git a/src/tint/lang/wgsl/resolver/validation_test.cc b/src/tint/lang/wgsl/resolver/validation_test.cc
index 78a830e..a3355f8 100644
--- a/src/tint/lang/wgsl/resolver/validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/validation_test.cc
@@ -535,7 +535,7 @@
     auto* body =
         Block(If(Expr(true), Block(Continue(cont_loc))), Decl(Var(decl_loc, "z", ty.i32())));
     auto* compare =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLessThan, Expr(ref_loc, "z"), Expr(2_i));
+        create<ast::BinaryExpression>(core::BinaryOp::kLessThan, Expr(ref_loc, "z"), Expr(2_i));
     auto* continuing = Block(If(compare, Block()));
     auto* loop_stmt = Loop(body, continuing);
     WrapInFunction(loop_stmt);
diff --git a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
index d3298c0..3016460 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.cc
@@ -543,66 +543,64 @@
     out << ")";
 }
 
-void ASTPrinter::EmitBinaryOp(StringStream& out, const ast::BinaryOp op) {
+void ASTPrinter::EmitBinaryOp(StringStream& out, const core::BinaryOp op) {
     switch (op) {
-        case ast::BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             out << "&";
-            break;
-        case ast::BinaryOp::kOr:
+            return;
+        case core::BinaryOp::kOr:
             out << "|";
-            break;
-        case ast::BinaryOp::kXor:
+            return;
+        case core::BinaryOp::kXor:
             out << "^";
-            break;
-        case ast::BinaryOp::kLogicalAnd:
+            return;
+        case core::BinaryOp::kLogicalAnd:
             out << "&&";
-            break;
-        case ast::BinaryOp::kLogicalOr:
+            return;
+        case core::BinaryOp::kLogicalOr:
             out << "||";
-            break;
-        case ast::BinaryOp::kEqual:
+            return;
+        case core::BinaryOp::kEqual:
             out << "==";
-            break;
-        case ast::BinaryOp::kNotEqual:
+            return;
+        case core::BinaryOp::kNotEqual:
             out << "!=";
-            break;
-        case ast::BinaryOp::kLessThan:
+            return;
+        case core::BinaryOp::kLessThan:
             out << "<";
-            break;
-        case ast::BinaryOp::kGreaterThan:
+            return;
+        case core::BinaryOp::kGreaterThan:
             out << ">";
-            break;
-        case ast::BinaryOp::kLessThanEqual:
+            return;
+        case core::BinaryOp::kLessThanEqual:
             out << "<=";
-            break;
-        case ast::BinaryOp::kGreaterThanEqual:
+            return;
+        case core::BinaryOp::kGreaterThanEqual:
             out << ">=";
-            break;
-        case ast::BinaryOp::kShiftLeft:
+            return;
+        case core::BinaryOp::kShiftLeft:
             out << "<<";
-            break;
-        case ast::BinaryOp::kShiftRight:
+            return;
+        case core::BinaryOp::kShiftRight:
             out << ">>";
-            break;
-        case ast::BinaryOp::kAdd:
+            return;
+        case core::BinaryOp::kAdd:
             out << "+";
-            break;
-        case ast::BinaryOp::kSubtract:
+            return;
+        case core::BinaryOp::kSubtract:
             out << "-";
-            break;
-        case ast::BinaryOp::kMultiply:
+            return;
+        case core::BinaryOp::kMultiply:
             out << "*";
-            break;
-        case ast::BinaryOp::kDivide:
+            return;
+        case core::BinaryOp::kDivide:
             out << "/";
-            break;
-        case ast::BinaryOp::kModulo:
+            return;
+        case core::BinaryOp::kModulo:
             out << "%";
-            break;
-        case ast::BinaryOp::kNone:
-            diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
-            break;
+            return;
     }
+    TINT_ICE() << "invalid binary op " << op;
 }
 
 void ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
diff --git a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
index 6674c22..c84fa13 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/wgsl/writer/ast_printer/ast_printer.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "src/tint/lang/core/binary_op.h"
 #include "src/tint/lang/wgsl/sem/struct.h"
 #include "src/tint/utils/generator/text_generator.h"
 #include "src/tint/utils/text/string_stream.h"
@@ -29,7 +30,6 @@
 class AssignmentStatement;
 class Attribute;
 class BinaryExpression;
-enum class BinaryOp;
 class BitcastExpression;
 class BlockStatement;
 class BlockStatement;
@@ -108,7 +108,7 @@
     /// Handles generating a binary operator
     /// @param out the output stream
     /// @param op the binary operator
-    void EmitBinaryOp(StringStream& out, const ast::BinaryOp op);
+    void EmitBinaryOp(StringStream& out, const core::BinaryOp op);
     /// Handles generating a bitcast expression
     /// @param out the output stream
     /// @param expr the bitcast expression
diff --git a/src/tint/lang/wgsl/writer/ast_printer/binary_test.cc b/src/tint/lang/wgsl/writer/ast_printer/binary_test.cc
index faf1056..5ea237f 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/binary_test.cc
+++ b/src/tint/lang/wgsl/writer/ast_printer/binary_test.cc
@@ -22,7 +22,7 @@
 
 struct BinaryData {
     const char* result;
-    ast::BinaryOp op;
+    core::BinaryOp op;
 };
 inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
     StringStream str;
@@ -35,7 +35,7 @@
     auto params = GetParam();
 
     auto op_ty = [&] {
-        if (params.op == ast::BinaryOp::kLogicalAnd || params.op == ast::BinaryOp::kLogicalOr) {
+        if (params.op == core::BinaryOp::kLogicalAnd || params.op == core::BinaryOp::kLogicalOr) {
             return ty.bool_();
         } else {
             return ty.u32();
@@ -60,24 +60,24 @@
 INSTANTIATE_TEST_SUITE_P(
     WgslASTPrinterTest,
     WgslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", ast::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", ast::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", ast::BinaryOp::kXor},
-                    BinaryData{"(left && right)", ast::BinaryOp::kLogicalAnd},
-                    BinaryData{"(left || right)", ast::BinaryOp::kLogicalOr},
-                    BinaryData{"(left == right)", ast::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", ast::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", ast::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", ast::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", ast::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", ast::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", ast::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", ast::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", ast::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", ast::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", ast::BinaryOp::kMultiply},
-                    BinaryData{"(left / right)", ast::BinaryOp::kDivide},
-                    BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
+    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
+                    BinaryData{"(left | right)", core::BinaryOp::kOr},
+                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
+                    BinaryData{"(left && right)", core::BinaryOp::kLogicalAnd},
+                    BinaryData{"(left || right)", core::BinaryOp::kLogicalOr},
+                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
+                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
+                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
+                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
+                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
+                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
+                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
+                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
+                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
+                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
+                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
+                    BinaryData{"(left / right)", core::BinaryOp::kDivide},
+                    BinaryData{"(left % right)", core::BinaryOp::kModulo}));
 
 }  // namespace
 }  // namespace tint::wgsl::writer
diff --git a/src/tint/lang/wgsl/writer/ast_printer/loop_test.cc b/src/tint/lang/wgsl/writer/ast_printer/loop_test.cc
index 016fcfe..c3a952f 100644
--- a/src/tint/lang/wgsl/writer/ast_printer/loop_test.cc
+++ b/src/tint/lang/wgsl/writer/ast_printer/loop_test.cc
@@ -280,7 +280,7 @@
     // }
 
     auto* multi_stmt =
-        create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
+        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
     auto* f = While(multi_stmt, Block(Return()));
     WrapInFunction(f);
 
diff --git a/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.cc b/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.cc
index a64fcb9..946b567 100644
--- a/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.cc
+++ b/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.cc
@@ -669,63 +669,63 @@
     Line() << "]";
 }
 
-void SyntaxTreePrinter::EmitBinaryOp(const ast::BinaryOp op) {
+void SyntaxTreePrinter::EmitBinaryOp(const core::BinaryOp op) {
     switch (op) {
-        case ast::BinaryOp::kAnd:
+        case core::BinaryOp::kAnd:
             Line() << "&";
             break;
-        case ast::BinaryOp::kOr:
+        case core::BinaryOp::kOr:
             Line() << "|";
             break;
-        case ast::BinaryOp::kXor:
+        case core::BinaryOp::kXor:
             Line() << "^";
             break;
-        case ast::BinaryOp::kLogicalAnd:
+        case core::BinaryOp::kLogicalAnd:
             Line() << "&&";
             break;
-        case ast::BinaryOp::kLogicalOr:
+        case core::BinaryOp::kLogicalOr:
             Line() << "||";
             break;
-        case ast::BinaryOp::kEqual:
+        case core::BinaryOp::kEqual:
             Line() << "==";
             break;
-        case ast::BinaryOp::kNotEqual:
+        case core::BinaryOp::kNotEqual:
             Line() << "!=";
             break;
-        case ast::BinaryOp::kLessThan:
+        case core::BinaryOp::kLessThan:
             Line() << "<";
             break;
-        case ast::BinaryOp::kGreaterThan:
+        case core::BinaryOp::kGreaterThan:
             Line() << ">";
             break;
-        case ast::BinaryOp::kLessThanEqual:
+        case core::BinaryOp::kLessThanEqual:
             Line() << "<=";
             break;
-        case ast::BinaryOp::kGreaterThanEqual:
+        case core::BinaryOp::kGreaterThanEqual:
             Line() << ">=";
             break;
-        case ast::BinaryOp::kShiftLeft:
+        case core::BinaryOp::kShiftLeft:
             Line() << "<<";
             break;
-        case ast::BinaryOp::kShiftRight:
+        case core::BinaryOp::kShiftRight:
             Line() << ">>";
             break;
-        case ast::BinaryOp::kAdd:
+        case core::BinaryOp::kAdd:
             Line() << "+";
             break;
-        case ast::BinaryOp::kSubtract:
+        case core::BinaryOp::kSubtract:
             Line() << "-";
             break;
-        case ast::BinaryOp::kMultiply:
+        case core::BinaryOp::kMultiply:
             Line() << "*";
             break;
-        case ast::BinaryOp::kDivide:
+        case core::BinaryOp::kDivide:
             Line() << "/";
             break;
-        case ast::BinaryOp::kModulo:
+        case core::BinaryOp::kModulo:
             Line() << "%";
             break;
-        case ast::BinaryOp::kNone:
+        default:
             diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
             break;
     }
diff --git a/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.h b/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.h
index 72947ab..94e0522 100644
--- a/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.h
+++ b/src/tint/lang/wgsl/writer/syntax_tree_printer/syntax_tree_printer.h
@@ -23,11 +23,13 @@
 #include "src/tint/utils/text/string_stream.h"
 
 // Forward declarations
+namespace tint::core {
+enum class BinaryOp;
+}
 namespace tint::ast {
 class AssignmentStatement;
 class Attribute;
 class BinaryExpression;
-enum class BinaryOp;
 class BitcastExpression;
 class BlockStatement;
 class BlockStatement;
@@ -101,7 +103,7 @@
     void EmitBinary(const ast::BinaryExpression* expr);
     /// Handles generating a binary operator
     /// @param op the binary operator
-    void EmitBinaryOp(const ast::BinaryOp op);
+    void EmitBinaryOp(const core::BinaryOp op);
     /// Handles generating a bitcast expression
     /// @param expr the bitcast expression
     void EmitBitcast(const ast::BitcastExpression* expr);
diff --git a/src/tint/tint.natvis b/src/tint/tint.natvis
index 838d54b..41b089c 100644
--- a/src/tint/tint.natvis
+++ b/src/tint/tint.natvis
@@ -157,25 +157,25 @@
 	</Type>
 
 	<Type Name="tint::ast::BinaryExpression">
-		<DisplayString Condition="op==tint::ast::BinaryOp::kNone">({*lhs} NONE {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kAnd">({*lhs} &amp; {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kOr">({*lhs} | {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kXor">({*lhs} ^ {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kLogicalAnd">({*lhs} &amp;&amp; {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kLogicalOr">({*lhs} || {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kEqual">({*lhs} == {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kNotEqual">({*lhs} != {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kLessThan">({*lhs} &lt; {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kGreaterThan">({*lhs} &gt; {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kLessThanEqual">({*lhs} &lt;= {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kGreaterThanEqual">({*lhs} &gt;= {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kShiftLeft">({*lhs} &lt;&lt; {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kShiftRight">({*lhs} >> {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kAdd">({*lhs} + {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kSubtract">({*lhs} - {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kMultiply">({*lhs} * {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kDivide">({*lhs} / {*rhs})</DisplayString>
-		<DisplayString Condition="op==tint::ast::BinaryOp::kModulo">({*lhs} % {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kNone">({*lhs} NONE {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kAnd">({*lhs} &amp; {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kOr">({*lhs} | {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kXor">({*lhs} ^ {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kLogicalAnd">({*lhs} &amp;&amp; {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kLogicalOr">({*lhs} || {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kEqual">({*lhs} == {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kNotEqual">({*lhs} != {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kLessThan">({*lhs} &lt; {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kGreaterThan">({*lhs} &gt; {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kLessThanEqual">({*lhs} &lt;= {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kGreaterThanEqual">({*lhs} &gt;= {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kShiftLeft">({*lhs} &lt;&lt; {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kShiftRight">({*lhs} >> {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kAdd">({*lhs} + {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kSubtract">({*lhs} - {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kMultiply">({*lhs} * {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kDivide">({*lhs} / {*rhs})</DisplayString>
+		<DisplayString Condition="op==tint::core::BinaryOp::kModulo">({*lhs} % {*rhs})</DisplayString>
 	</Type>
 
 	<Type Name="tint::ast::CallExpression">