Add support for GLSL length.

This CL adds support for the GLSL length command.

Bug: tint:5
Change-Id: I2704bc04e493fb3aef8e5cd58039b6b863cc80f8
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20001
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 06e45c5..dce43c1 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -668,6 +668,24 @@
     }
 
     return params[0]->result_type();
+  } else if (name == "length") {
+    if (params.size() != 1) {
+      error_ = "incorrect number of parameters for " + name +
+               ". Expected 1 got " + std::to_string(params.size());
+      return nullptr;
+    }
+    if (!params[0]->result_type()->is_float_scalar_or_vector()) {
+      error_ = "incorrect type for " + name +
+               ". Requires a float scalar or a float vector";
+      return nullptr;
+    }
+
+    *id = GLSLstd450Length;
+    auto* result_type = params[0]->result_type();
+
+    // Length returns a scalar of the same type as the parameter.
+    return result_type->is_float_scalar() ? result_type
+                                          : result_type->AsVector()->type();
   }
 
   return nullptr;
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index cbb52bf..4dbfc2f 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -1618,5 +1618,91 @@
                     GLSLData{"inversesqrt", GLSLstd450InverseSqrt},
                     GLSLData{"normalize", GLSLstd450Normalize}));
 
+TEST_F(TypeDeterminerTest, ImportData_Length_Scalar) {
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&f32, 1.f)));
+
+  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+
+  uint32_t id = 0;
+  auto* type = td()->GetImportData("GLSL.std.450", "length", params, &id);
+  ASSERT_NE(type, nullptr);
+  EXPECT_TRUE(type->is_float_scalar());
+  EXPECT_EQ(id, GLSLstd450Length);
+}
+
+TEST_F(TypeDeterminerTest, ImportData_Length_FloatVector) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+
+  uint32_t id = 0;
+  auto* type = td()->GetImportData("GLSL.std.450", "length", params, &id);
+  ASSERT_NE(type, nullptr);
+  EXPECT_TRUE(type->is_float_scalar());
+  EXPECT_EQ(id, GLSLstd450Length);
+}
+
+TEST_F(TypeDeterminerTest, ImportData_Length_Error_Integer) {
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+
+  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+
+  uint32_t id = 0;
+  auto* type = td()->GetImportData("GLSL.std.450", "length", params, &id);
+  ASSERT_EQ(type, nullptr);
+  EXPECT_EQ(
+      td()->error(),
+      "incorrect type for length. Requires a float scalar or a float vector");
+}
+
+TEST_F(TypeDeterminerTest, ImportData_Length_Error_NoParams) {
+  ast::ExpressionList params;
+  uint32_t id = 0;
+  auto* type = td()->GetImportData("GLSL.std.450", "length", params, &id);
+  ASSERT_EQ(type, nullptr);
+  EXPECT_EQ(td()->error(),
+            "incorrect number of parameters for length. Expected 1 got 0");
+}
+
+TEST_F(TypeDeterminerTest, ImportData_Length_Error_MultipleParams) {
+  ast::type::F32Type f32;
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+
+  uint32_t id = 0;
+  auto* type = td()->GetImportData("GLSL.std.450", "length", params, &id);
+  ASSERT_EQ(type, nullptr);
+  EXPECT_EQ(td()->error(),
+            "incorrect number of parameters for length. Expected 1 got 3");
+}
+
 }  // namespace
 }  // namespace tint