validation: unary operands

logical negation operand must be 'bool' or 'vecN<bool>'
complement operand must be be 'i32', 'u32', 'vecN<i32>' or 'vecN<u32>'
Negation operand must be 'i32', 'f32', 'vecN<i32>' or 'vecN<f32>'

Bug: tint:916 chromium:1216597
Change-Id: Ic88a124a32d16b542560da3ca1c159968d4043a0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55860
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index d16c958..9c302e4 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -2733,12 +2733,41 @@
   const sem::Type* type = nullptr;
 
   switch (unary->op()) {
-    case ast::UnaryOp::kComplement:
-    case ast::UnaryOp::kNegation:
     case ast::UnaryOp::kNot:
       // Result type matches the deref'd inner type.
       type_name = TypeNameOf(unary->expr());
       type = expr_type->UnwrapRef();
+      if (!type->Is<sem::Bool>() && !type->is_bool_vector()) {
+        AddError("cannot logical negate expression of type '" +
+                     TypeNameOf(unary->expr()),
+                 unary->expr()->source());
+        return false;
+      }
+      break;
+
+    case ast::UnaryOp::kComplement:
+      // Result type matches the deref'd inner type.
+      type_name = TypeNameOf(unary->expr());
+      type = expr_type->UnwrapRef();
+      if (!type->is_integer_scalar_or_vector()) {
+        AddError("cannot bitwise complement expression of type '" +
+                     TypeNameOf(unary->expr()),
+                 unary->expr()->source());
+        return false;
+      }
+      break;
+
+    case ast::UnaryOp::kNegation:
+      // Result type matches the deref'd inner type.
+      type_name = TypeNameOf(unary->expr());
+      type = expr_type->UnwrapRef();
+      if (!(type->IsAnyOf<sem::F32, sem::I32>() ||
+            type->is_signed_integer_vector() || type->is_float_vector())) {
+        AddError(
+            "cannot negate expression of type '" + TypeNameOf(unary->expr()),
+            unary->expr()->source());
+        return false;
+      }
       break;
 
     case ast::UnaryOp::kAddressOf:
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 90fe982..bbd9051 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -1917,7 +1917,13 @@
 TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
   auto op = GetParam();
 
-  Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+  if (op == ast::UnaryOp::kNot) {
+    Global("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
+  } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
+    Global("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
+  } else {
+    Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+  }
   auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
   WrapInFunction(der);
 
@@ -1925,7 +1931,13 @@
 
   ASSERT_NE(TypeOf(der), nullptr);
   ASSERT_TRUE(TypeOf(der)->Is<sem::Vector>());
-  EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::F32>());
+  if (op == ast::UnaryOp::kNot) {
+    EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::Bool>());
+  } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
+    EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::I32>());
+  } else {
+    EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::F32>());
+  }
   EXPECT_EQ(TypeOf(der)->As<sem::Vector>()->size(), 4u);
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTest,
@@ -2181,6 +2193,38 @@
       "encountered twice in the same AST of a Program");
 }
 
+TEST_F(ResolverTest, UnaryOp_Not) {
+  Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+  auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot,
+                                             Expr(Source{{12, 34}}, "ident"));
+  WrapInFunction(der);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: cannot logical negate expression of type 'vec4<f32>");
+}
+
+TEST_F(ResolverTest, UnaryOp_Complement) {
+  Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+  auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement,
+                                             Expr(Source{{12, 34}}, "ident"));
+  WrapInFunction(der);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(
+      r()->error(),
+      "12:34 error: cannot bitwise complement expression of type 'vec4<f32>");
+}
+
+TEST_F(ResolverTest, UnaryOp_Negation) {
+  Global("ident", ty.u32(), ast::StorageClass::kPrivate);
+  auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation,
+                                             Expr(Source{{12, 34}}, "ident"));
+  WrapInFunction(der);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "12:34 error: cannot negate expression of type 'u32");
+}
 }  // namespace
 }  // namespace resolver
 }  // namespace tint
diff --git a/src/writer/hlsl/generator_impl_unary_op_test.cc b/src/writer/hlsl/generator_impl_unary_op_test.cc
index 6674692..5fb7cd6 100644
--- a/src/writer/hlsl/generator_impl_unary_op_test.cc
+++ b/src/writer/hlsl/generator_impl_unary_op_test.cc
@@ -35,7 +35,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Complement) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.u32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
   WrapInFunction(op);
@@ -64,7 +64,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Not) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
   auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
   WrapInFunction(op);
 
@@ -76,7 +76,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Negation) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.i32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
   WrapInFunction(op);
diff --git a/src/writer/msl/generator_impl_unary_op_test.cc b/src/writer/msl/generator_impl_unary_op_test.cc
index 6918c45..2dfe8a9 100644
--- a/src/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/writer/msl/generator_impl_unary_op_test.cc
@@ -34,7 +34,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Complement) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.i32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
   WrapInFunction(op);
@@ -61,7 +61,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Not) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
   auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
   WrapInFunction(op);
 
@@ -72,7 +72,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Negation) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.i32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
   WrapInFunction(op);
diff --git a/src/writer/wgsl/generator_impl_unary_op_test.cc b/src/writer/wgsl/generator_impl_unary_op_test.cc
index e3f5b09..16b1a62 100644
--- a/src/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/writer/wgsl/generator_impl_unary_op_test.cc
@@ -34,7 +34,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Complement) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.u32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
   WrapInFunction(op);
@@ -61,7 +61,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Not) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
   auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
   WrapInFunction(op);
 
@@ -72,7 +72,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Negation) {
-  Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+  Global("expr", ty.i32(), ast::StorageClass::kPrivate);
   auto* op =
       create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
   WrapInFunction(op);