tint/resolver: Evaluate const-expr swizzles

Bug: chromium:1341475
Change-Id: I2ac44824b08c460df759a96d0ba96f6045b60f74
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95765
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 42cd0bb..9891f29 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -1976,7 +1976,8 @@
             ret = builder_->create<sem::Reference>(ret, ref->StorageClass(), ref->Access());
         }
 
-        return builder_->create<sem::StructMemberAccess>(expr, ret, current_statement_, object,
+        sem::Constant* val = nullptr;  // TODO(crbug.com/tint/1611): Add structure support.
+        return builder_->create<sem::StructMemberAccess>(expr, ret, current_statement_, val, object,
                                                          member, has_side_effects, source_var);
     }
 
@@ -2043,7 +2044,8 @@
             // the swizzle.
             ret = builder_->create<sem::Vector>(vec->type(), static_cast<uint32_t>(size));
         }
-        return builder_->create<sem::Swizzle>(expr, ret, current_statement_, object,
+        auto* val = EvaluateSwizzleValue(object, ret, swizzle);
+        return builder_->create<sem::Swizzle>(expr, ret, current_statement_, val, object,
                                               std::move(swizzle), has_side_effects, source_var);
     }
 
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 7c1f838..9a9811f 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -218,6 +218,9 @@
         const sem::Type* ty);  // Note: ty is not an array or structure
     const sem::Constant* EvaluateIndexValue(const sem::Expression* obj, const sem::Expression* idx);
     const sem::Constant* EvaluateLiteralValue(const ast::LiteralExpression*, const sem::Type*);
+    const sem::Constant* EvaluateSwizzleValue(const sem::Expression* vector,
+                                              const sem::Type* type,
+                                              const std::vector<uint32_t>& indices);
     const sem::Constant* EvaluateUnaryValue(const sem::Expression*,
                                             const IntrinsicTable::UnaryOperator&);
 
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 0c314cb..30c00e2 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -19,6 +19,7 @@
 #include "src/tint/sem/abstract_float.h"
 #include "src/tint/sem/abstract_int.h"
 #include "src/tint/sem/constant.h"
+#include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/type_constructor.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/transform.h"
@@ -537,6 +538,22 @@
     return obj_val->Index(static_cast<size_t>(idx));
 }
 
+const sem::Constant* Resolver::EvaluateSwizzleValue(const sem::Expression* vec_expr,
+                                                    const sem::Type* type,
+                                                    const std::vector<uint32_t>& indices) {
+    auto* vec_val = vec_expr->ConstantValue();
+    if (!vec_val) {
+        return nullptr;
+    }
+    if (indices.size() == 1) {
+        return static_cast<const Constant*>(vec_val->Index(indices[0]));
+    } else {
+        auto values = utils::Transform(
+            indices, [&](uint32_t i) { return static_cast<const Constant*>(vec_val->Index(i)); });
+        return CreateComposite(*builder_, type, std::move(values));
+    }
+}
+
 const sem::Constant* Resolver::EvaluateBitcastValue(const sem::Expression*, const sem::Type*) {
     // TODO(crbug.com/tint/1581): Implement @const intrinsics
     return nullptr;
diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc
index ff8a189..b8ef1b1 100644
--- a/src/tint/resolver/resolver_constants_test.cc
+++ b/src/tint/resolver/resolver_constants_test.cc
@@ -20,6 +20,7 @@
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/index_accessor_expression.h"
+#include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/test_helper.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -1869,6 +1870,63 @@
     EXPECT_EQ(sem->ConstantValue()->As<i32>(), 1_i);
 }
 
+TEST_F(ResolverConstantsTest, Vec3_Swizzle_Scalar) {
+    auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "y");
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Swizzle_Vector) {
+    auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "zx");
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 3._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 1._a);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Swizzle_Chain) {
+    auto* expr =  // (1, 2, 3) -> (2, 3, 1) -> (3, 2) -> 2
+        MemberAccessor(MemberAccessor(MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "gbr"), "yx"), "y");
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
+}
+
 TEST_F(ResolverConstantsTest, Mat3x2_Index) {
     auto* expr = IndexAccessor(
         mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)), 2_i);
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index 05e5dac..45a67ca 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -35,7 +35,7 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
-    /// @param constant the constant value of the expression. May be invalid
+    /// @param constant the constant value of the expression. May be null
     /// @param has_side_effects true if this expression may have side-effects
     /// @param source_var the (optional) source variable for this expression
     Expression(const ast::Expression* declaration,
diff --git a/src/tint/sem/index_accessor_expression.h b/src/tint/sem/index_accessor_expression.h
index 233b0fa..3ba10ea 100644
--- a/src/tint/sem/index_accessor_expression.h
+++ b/src/tint/sem/index_accessor_expression.h
@@ -35,7 +35,7 @@
     /// @param object the object expression that is being indexed
     /// @param index the index expression
     /// @param statement the statement that owns this expression
-    /// @param constant the constant value of the expression. May be invalid
+    /// @param constant the constant value of the expression. May be null
     /// @param has_side_effects whether this expression may have side effects
     /// @param source_var the (optional) source variable for this expression
     IndexAccessorExpression(const ast::IndexAccessorExpression* declaration,
diff --git a/src/tint/sem/member_accessor_expression.cc b/src/tint/sem/member_accessor_expression.cc
index f9929c7..7c31b0e 100644
--- a/src/tint/sem/member_accessor_expression.cc
+++ b/src/tint/sem/member_accessor_expression.cc
@@ -26,32 +26,36 @@
 MemberAccessorExpression::MemberAccessorExpression(const ast::MemberAccessorExpression* declaration,
                                                    const sem::Type* type,
                                                    const Statement* statement,
+                                                   const Constant* constant,
                                                    const Expression* object,
                                                    bool has_side_effects,
                                                    const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, nullptr, has_side_effects, source_var), object_(object) {}
+    : Base(declaration, type, statement, constant, has_side_effects, source_var), object_(object) {}
 
 MemberAccessorExpression::~MemberAccessorExpression() = default;
 
 StructMemberAccess::StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                                        const sem::Type* type,
                                        const Statement* statement,
+                                       const Constant* constant,
                                        const Expression* object,
                                        const StructMember* member,
                                        bool has_side_effects,
                                        const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, object, has_side_effects, source_var), member_(member) {}
+    : Base(declaration, type, statement, constant, object, has_side_effects, source_var),
+      member_(member) {}
 
 StructMemberAccess::~StructMemberAccess() = default;
 
 Swizzle::Swizzle(const ast::MemberAccessorExpression* declaration,
                  const sem::Type* type,
                  const Statement* statement,
+                 const Constant* constant,
                  const Expression* object,
                  std::vector<uint32_t> indices,
                  bool has_side_effects,
                  const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, object, has_side_effects, source_var),
+    : Base(declaration, type, statement, constant, object, has_side_effects, source_var),
       indices_(std::move(indices)) {}
 
 Swizzle::~Swizzle() = default;
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 3d60816..d8484a8 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -38,12 +38,14 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param constant the constant value of the expression. May be null.
     /// @param object the object that holds the member being accessed
     /// @param has_side_effects whether this expression may have side effects
     /// @param source_var the (optional) source variable for this expression
     MemberAccessorExpression(const ast::MemberAccessorExpression* declaration,
                              const sem::Type* type,
                              const Statement* statement,
+                             const Constant* constant,
                              const Expression* object,
                              bool has_side_effects,
                              const Variable* source_var = nullptr);
@@ -67,6 +69,7 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param constant the constant value of the expression. May be null
     /// @param object the object that holds the member being accessed
     /// @param member the structure member
     /// @param has_side_effects whether this expression may have side effects
@@ -74,6 +77,7 @@
     StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                        const sem::Type* type,
                        const Statement* statement,
+                       const Constant* constant,
                        const Expression* object,
                        const StructMember* member,
                        bool has_side_effects,
@@ -97,6 +101,7 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param constant the constant value of the expression. May be null
     /// @param object the object that holds the member being accessed
     /// @param indices the swizzle indices
     /// @param has_side_effects whether this expression may have side effects
@@ -104,6 +109,7 @@
     Swizzle(const ast::MemberAccessorExpression* declaration,
             const sem::Type* type,
             const Statement* statement,
+            const Constant* constant,
             const Expression* object,
             std::vector<uint32_t> indices,
             bool has_side_effects,
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 28e8f97..e5a5cec 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -47,7 +47,7 @@
     /// @param type the variable type
     /// @param storage_class the variable storage class
     /// @param access the variable access control type
-    /// @param constant_value the constant value for the variable. May be invalid
+    /// @param constant_value the constant value for the variable. May be null
     Variable(const ast::Variable* declaration,
              const sem::Type* type,
              ast::StorageClass storage_class,
@@ -105,7 +105,7 @@
     /// @param storage_class the variable storage class
     /// @param access the variable access control type
     /// @param statement the statement that declared this local variable
-    /// @param constant_value the constant value for the variable. May be invalid
+    /// @param constant_value the constant value for the variable. May be null
     LocalVariable(const ast::Variable* declaration,
                   const sem::Type* type,
                   ast::StorageClass storage_class,
@@ -139,7 +139,7 @@
     /// @param type the variable type
     /// @param storage_class the variable storage class
     /// @param access the variable access control type
-    /// @param constant_value the constant value for the variable. May be invalid
+    /// @param constant_value the constant value for the variable. May be null
     /// @param binding_point the optional resource binding point of the variable
     GlobalVariable(const ast::Variable* declaration,
                    const sem::Type* type,