[ast] Add AccessControlType.

This CL adds the AST for an AccessControlType. This type contains an
access control (ReadOnly, WriteOnly, or ReadWrite) along with another
type pointer.

Bug: tint:208 tint:108
Change-Id: I2eed03f8760239f7e71dc2edd4a19a7c1661746e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31060
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 6881312..103c941 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -319,6 +319,8 @@
     "src/ast/struct_member_offset_decoration.h",
     "src/ast/switch_statement.cc",
     "src/ast/switch_statement.h",
+    "src/ast/type/access_control_type.cc",
+    "src/ast/type/access_control_type.h",
     "src/ast/type/alias_type.cc",
     "src/ast/type/alias_type.h",
     "src/ast/type/array_type.cc",
@@ -740,6 +742,7 @@
     "src/ast/struct_member_test.cc",
     "src/ast/struct_test.cc",
     "src/ast/switch_statement_test.cc",
+    "src/ast/type/access_control_type_test.cc",
     "src/ast/type/alias_type_test.cc",
     "src/ast/type/array_type_test.cc",
     "src/ast/type/bool_type_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 13a2937..7a04fc7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -142,6 +142,8 @@
   ast/switch_statement.h
   ast/type_constructor_expression.h
   ast/type_constructor_expression.cc
+  ast/type/access_control_type.cc
+  ast/type/access_control_type.h
   ast/type/alias_type.cc
   ast/type/alias_type.h
   ast/type/array_type.cc
@@ -350,6 +352,7 @@
   ast/struct_member_offset_decoration_test.cc
   ast/struct_test.cc
   ast/switch_statement_test.cc
+  ast/type/access_control_type_test.cc
   ast/type/alias_type_test.cc
   ast/type/array_type_test.cc
   ast/type/bool_type_test.cc
diff --git a/src/ast/type/access_control_type.cc b/src/ast/type/access_control_type.cc
new file mode 100644
index 0000000..1abae24
--- /dev/null
+++ b/src/ast/type/access_control_type.cc
@@ -0,0 +1,57 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/ast/type/access_control_type.h"
+
+#include <assert.h>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+AccessControlType::AccessControlType(AccessControl access, Type* subtype)
+    : access_(access), subtype_(subtype) {
+  assert(subtype_);
+  assert(!subtype_->IsAccessControl());
+}
+
+AccessControlType::~AccessControlType() = default;
+
+bool AccessControlType::IsAccessControl() const {
+  return true;
+}
+
+std::string AccessControlType::type_name() const {
+  std::string name = "__access_control_";
+  switch (access_) {
+    case AccessControl::kReadOnly:
+      name += "read_only";
+      break;
+    case AccessControl::kWriteOnly:
+      name += "write_only";
+      break;
+    case AccessControl::kReadWrite:
+      name += "read_write";
+      break;
+  }
+  return name + subtype_->type_name();
+}
+
+uint64_t AccessControlType::MinBufferBindingSize() const {
+  return subtype_->MinBufferBindingSize();
+}
+
+}  // namespace type
+}  // namespace ast
+}  // namespace tint
diff --git a/src/ast/type/access_control_type.h b/src/ast/type/access_control_type.h
new file mode 100644
index 0000000..5e696fd
--- /dev/null
+++ b/src/ast/type/access_control_type.h
@@ -0,0 +1,74 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_AST_TYPE_ACCESS_CONTROL_TYPE_H_
+#define SRC_AST_TYPE_ACCESS_CONTROL_TYPE_H_
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// The access control settings
+enum class AccessControl {
+  /// Read only
+  kReadOnly = 0,
+  /// Write only
+  kWriteOnly,
+  /// Read write
+  kReadWrite
+};
+
+/// An access control type. Holds an access setting and pointer to another type.
+class AccessControlType : public Type {
+ public:
+  /// Constructor
+  /// @param access the access control setting
+  /// @param subtype the access controlled type
+  AccessControlType(AccessControl access, Type* subtype);
+  /// Move constructor
+  AccessControlType(AccessControlType&&) = default;
+  ~AccessControlType() override;
+
+  /// @returns true if the type is an access control type
+  bool IsAccessControl() const override;
+
+  /// @returns true if the access control is read only
+  bool IsReadOnly() const { return access_ == AccessControl::kReadOnly; }
+  /// @returns true if the access control is write only
+  bool IsWriteOnly() const { return access_ == AccessControl::kWriteOnly; }
+  /// @returns true if the access control is read/write
+  bool IsReadWrite() const { return access_ == AccessControl::kReadWrite; }
+
+  /// @returns the subtype type
+  Type* type() const { return subtype_; }
+
+  /// @returns the name for this type
+  std::string type_name() const override;
+
+  /// @returns minimum size required for this type, in bytes.
+  /// 0 for non-host shareable types.
+  uint64_t MinBufferBindingSize() const override;
+
+ private:
+  AccessControl access_ = AccessControl::kReadOnly;
+  Type* subtype_ = nullptr;
+};
+
+}  // namespace type
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_AST_TYPE_ACCESS_CONTROL_TYPE_H_
diff --git a/src/ast/type/access_control_type_test.cc b/src/ast/type/access_control_type_test.cc
new file mode 100644
index 0000000..0f31121
--- /dev/null
+++ b/src/ast/type/access_control_type_test.cc
@@ -0,0 +1,89 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/ast/type/access_control_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/storage_class.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/pointer_type.h"
+#include "src/ast/type/u32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+namespace {
+
+using AccessControlTypeTest = testing::Test;
+
+TEST_F(AccessControlTypeTest, Create) {
+  U32Type u32;
+  AccessControlType a{AccessControl::kReadWrite, &u32};
+  EXPECT_TRUE(a.IsReadWrite());
+  EXPECT_EQ(a.type(), &u32);
+}
+
+TEST_F(AccessControlTypeTest, Is) {
+  I32Type i32;
+
+  AccessControlType at{AccessControl::kReadOnly, &i32};
+  EXPECT_TRUE(at.IsAccessControl());
+  EXPECT_FALSE(at.IsAlias());
+  EXPECT_FALSE(at.IsArray());
+  EXPECT_FALSE(at.IsBool());
+  EXPECT_FALSE(at.IsF32());
+  EXPECT_FALSE(at.IsI32());
+  EXPECT_FALSE(at.IsMatrix());
+  EXPECT_FALSE(at.IsPointer());
+  EXPECT_FALSE(at.IsSampler());
+  EXPECT_FALSE(at.IsStruct());
+  EXPECT_FALSE(at.IsTexture());
+  EXPECT_FALSE(at.IsU32());
+  EXPECT_FALSE(at.IsVector());
+}
+
+TEST_F(AccessControlTypeTest, AccessRead) {
+  I32Type i32;
+  AccessControlType at{AccessControl::kReadOnly, &i32};
+  EXPECT_TRUE(at.IsReadOnly());
+  EXPECT_FALSE(at.IsWriteOnly());
+  EXPECT_FALSE(at.IsReadWrite());
+
+  EXPECT_EQ(at.type_name(), "__access_control_read_only__i32");
+}
+
+TEST_F(AccessControlTypeTest, AccessWrite) {
+  I32Type i32;
+  AccessControlType at{AccessControl::kWriteOnly, &i32};
+  EXPECT_FALSE(at.IsReadOnly());
+  EXPECT_TRUE(at.IsWriteOnly());
+  EXPECT_FALSE(at.IsReadWrite());
+
+  EXPECT_EQ(at.type_name(), "__access_control_write_only__i32");
+}
+
+TEST_F(AccessControlTypeTest, AccessReadWrite) {
+  I32Type i32;
+  AccessControlType at{AccessControl::kReadWrite, &i32};
+  EXPECT_FALSE(at.IsReadOnly());
+  EXPECT_FALSE(at.IsWriteOnly());
+  EXPECT_TRUE(at.IsReadWrite());
+
+  EXPECT_EQ(at.type_name(), "__access_control_read_write__i32");
+}
+
+}  // namespace
+}  // namespace type
+}  // namespace ast
+}  // namespace tint
diff --git a/src/ast/type/alias_type_test.cc b/src/ast/type/alias_type_test.cc
index ac021ae..76b547a 100644
--- a/src/ast/type/alias_type_test.cc
+++ b/src/ast/type/alias_type_test.cc
@@ -38,6 +38,7 @@
   I32Type i32;
 
   AliasType at{"a", &i32};
+  EXPECT_FALSE(at.IsAccessControl());
   EXPECT_TRUE(at.IsAlias());
   EXPECT_FALSE(at.IsArray());
   EXPECT_FALSE(at.IsBool());
diff --git a/src/ast/type/array_type_test.cc b/src/ast/type/array_type_test.cc
index b76cb08..71b4343 100644
--- a/src/ast/type/array_type_test.cc
+++ b/src/ast/type/array_type_test.cc
@@ -48,6 +48,7 @@
   I32Type i32;
 
   ArrayType arr{&i32, 3};
+  EXPECT_FALSE(arr.IsAccessControl());
   EXPECT_FALSE(arr.IsAlias());
   EXPECT_TRUE(arr.IsArray());
   EXPECT_FALSE(arr.IsBool());
diff --git a/src/ast/type/bool_type_test.cc b/src/ast/type/bool_type_test.cc
index f3cc966..4bcf482 100644
--- a/src/ast/type/bool_type_test.cc
+++ b/src/ast/type/bool_type_test.cc
@@ -25,6 +25,7 @@
 
 TEST_F(BoolTypeTest, Is) {
   BoolType b;
+  EXPECT_FALSE(b.IsAccessControl());
   EXPECT_FALSE(b.IsAlias());
   EXPECT_FALSE(b.IsArray());
   EXPECT_TRUE(b.IsBool());
diff --git a/src/ast/type/depth_texture_type_test.cc b/src/ast/type/depth_texture_type_test.cc
index 6e79d41..e8b5d59 100644
--- a/src/ast/type/depth_texture_type_test.cc
+++ b/src/ast/type/depth_texture_type_test.cc
@@ -25,6 +25,7 @@
 
 TEST_F(DepthTextureTypeTest, Is) {
   DepthTextureType d(TextureDimension::kCube);
+  EXPECT_FALSE(d.IsAccessControl());
   EXPECT_FALSE(d.IsAlias());
   EXPECT_FALSE(d.IsArray());
   EXPECT_FALSE(d.IsBool());
diff --git a/src/ast/type/f32_type_test.cc b/src/ast/type/f32_type_test.cc
index fc42b00..88bef62 100644
--- a/src/ast/type/f32_type_test.cc
+++ b/src/ast/type/f32_type_test.cc
@@ -25,6 +25,7 @@
 
 TEST_F(F32TypeTest, Is) {
   F32Type f;
+  EXPECT_FALSE(f.IsAccessControl());
   EXPECT_FALSE(f.IsAlias());
   EXPECT_FALSE(f.IsArray());
   EXPECT_FALSE(f.IsBool());
diff --git a/src/ast/type/i32_type_test.cc b/src/ast/type/i32_type_test.cc
index f69958d..12e0795 100644
--- a/src/ast/type/i32_type_test.cc
+++ b/src/ast/type/i32_type_test.cc
@@ -25,6 +25,7 @@
 
 TEST_F(I32TypeTest, Is) {
   I32Type i;
+  EXPECT_FALSE(i.IsAccessControl());
   EXPECT_FALSE(i.IsAlias());
   EXPECT_FALSE(i.IsArray());
   EXPECT_FALSE(i.IsBool());
diff --git a/src/ast/type/matrix_type_test.cc b/src/ast/type/matrix_type_test.cc
index c659e40..0ab07ac 100644
--- a/src/ast/type/matrix_type_test.cc
+++ b/src/ast/type/matrix_type_test.cc
@@ -35,6 +35,7 @@
 TEST_F(MatrixTypeTest, Is) {
   I32Type i32;
   MatrixType m{&i32, 2, 3};
+  EXPECT_FALSE(m.IsAccessControl());
   EXPECT_FALSE(m.IsAlias());
   EXPECT_FALSE(m.IsArray());
   EXPECT_FALSE(m.IsBool());
diff --git a/src/ast/type/multisampled_texture_type_test.cc b/src/ast/type/multisampled_texture_type_test.cc
index c50cef9..4d3d059 100644
--- a/src/ast/type/multisampled_texture_type_test.cc
+++ b/src/ast/type/multisampled_texture_type_test.cc
@@ -27,6 +27,7 @@
 TEST_F(MultisampledTextureTypeTest, Is) {
   F32Type f32;
   MultisampledTextureType s(TextureDimension::kCube, &f32);
+  EXPECT_FALSE(s.IsAccessControl());
   EXPECT_FALSE(s.IsAlias());
   EXPECT_FALSE(s.IsArray());
   EXPECT_FALSE(s.IsBool());
diff --git a/src/ast/type/pointer_type_test.cc b/src/ast/type/pointer_type_test.cc
index 0d825f5..9f10ac9 100644
--- a/src/ast/type/pointer_type_test.cc
+++ b/src/ast/type/pointer_type_test.cc
@@ -34,6 +34,7 @@
 TEST_F(PointerTypeTest, Is) {
   I32Type i32;
   PointerType p{&i32, StorageClass::kFunction};
+  EXPECT_FALSE(p.IsAccessControl());
   EXPECT_FALSE(p.IsAlias());
   EXPECT_FALSE(p.IsArray());
   EXPECT_FALSE(p.IsBool());
diff --git a/src/ast/type/sampled_texture_type_test.cc b/src/ast/type/sampled_texture_type_test.cc
index ce10639..a464459 100644
--- a/src/ast/type/sampled_texture_type_test.cc
+++ b/src/ast/type/sampled_texture_type_test.cc
@@ -27,6 +27,7 @@
 TEST_F(SampledTextureTypeTest, Is) {
   F32Type f32;
   SampledTextureType s(TextureDimension::kCube, &f32);
+  EXPECT_FALSE(s.IsAccessControl());
   EXPECT_FALSE(s.IsAlias());
   EXPECT_FALSE(s.IsArray());
   EXPECT_FALSE(s.IsBool());
diff --git a/src/ast/type/sampler_type_test.cc b/src/ast/type/sampler_type_test.cc
index 60921ed..34f835c 100644
--- a/src/ast/type/sampler_type_test.cc
+++ b/src/ast/type/sampler_type_test.cc
@@ -36,6 +36,7 @@
 
 TEST_F(SamplerTypeTest, Is) {
   SamplerType s{SamplerKind::kSampler};
+  EXPECT_FALSE(s.IsAccessControl());
   EXPECT_FALSE(s.IsAlias());
   EXPECT_FALSE(s.IsArray());
   EXPECT_FALSE(s.IsBool());
diff --git a/src/ast/type/storage_texture_type_test.cc b/src/ast/type/storage_texture_type_test.cc
index bc0f503..bbc999b 100644
--- a/src/ast/type/storage_texture_type_test.cc
+++ b/src/ast/type/storage_texture_type_test.cc
@@ -29,6 +29,7 @@
 TEST_F(StorageTextureTypeTest, Is) {
   StorageTextureType s(TextureDimension::k2dArray, StorageAccess::kRead,
                        ImageFormat::kRgba32Float);
+  EXPECT_FALSE(s.IsAccessControl());
   EXPECT_FALSE(s.IsAlias());
   EXPECT_FALSE(s.IsArray());
   EXPECT_FALSE(s.IsBool());
diff --git a/src/ast/type/struct_type_test.cc b/src/ast/type/struct_type_test.cc
index 3b11b4a..775c9e9 100644
--- a/src/ast/type/struct_type_test.cc
+++ b/src/ast/type/struct_type_test.cc
@@ -36,6 +36,7 @@
 TEST_F(StructTypeTest, Is) {
   auto impl = std::make_unique<Struct>();
   StructType s{"S", std::move(impl)};
+  EXPECT_FALSE(s.IsAccessControl());
   EXPECT_FALSE(s.IsAlias());
   EXPECT_FALSE(s.IsArray());
   EXPECT_FALSE(s.IsBool());
diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc
index ee8fa42..7f596af 100644
--- a/src/ast/type/type.cc
+++ b/src/ast/type/type.cc
@@ -16,6 +16,7 @@
 
 #include <assert.h>
 
+#include "src/ast/type/access_control_type.h"
 #include "src/ast/type/alias_type.h"
 #include "src/ast/type/array_type.h"
 #include "src/ast/type/bool_type.h"
@@ -57,6 +58,10 @@
   return UnwrapAliasesIfNeeded()->UnwrapPtrIfNeeded()->UnwrapAliasesIfNeeded();
 }
 
+bool Type::IsAccessControl() const {
+  return false;
+}
+
 bool Type::IsAlias() const {
   return false;
 }
@@ -157,6 +162,11 @@
   return is_unsigned_scalar_or_vector() || is_signed_scalar_or_vector();
 }
 
+const AccessControlType* Type::AsAccessControl() const {
+  assert(IsAccessControl());
+  return static_cast<const AccessControlType*>(this);
+}
+
 const AliasType* Type::AsAlias() const {
   assert(IsAlias());
   return static_cast<const AliasType*>(this);
@@ -222,6 +232,11 @@
   return static_cast<const VoidType*>(this);
 }
 
+AccessControlType* Type::AsAccessControl() {
+  assert(IsAccessControl());
+  return static_cast<AccessControlType*>(this);
+}
+
 AliasType* Type::AsAlias() {
   assert(IsAlias());
   return static_cast<AliasType*>(this);
diff --git a/src/ast/type/type.h b/src/ast/type/type.h
index aabde8d..ff02f28 100644
--- a/src/ast/type/type.h
+++ b/src/ast/type/type.h
@@ -21,6 +21,7 @@
 namespace ast {
 namespace type {
 
+class AccessControlType;
 class AliasType;
 class ArrayType;
 class BoolType;
@@ -42,6 +43,8 @@
   Type(Type&&) = default;
   virtual ~Type();
 
+  /// @returns true if the type is an access control type
+  virtual bool IsAccessControl() const;
   /// @returns true if the type is an alias type
   virtual bool IsAlias() const;
   /// @returns true if the type is an array type
@@ -117,6 +120,8 @@
   /// @returns true if this type is an integer scalar or vector
   bool is_integer_scalar_or_vector();
 
+  /// @returns the type as an access control type
+  const AccessControlType* AsAccessControl() const;
   /// @returns the type as an alias type
   const AliasType* AsAlias() const;
   /// @returns the type as an array type
@@ -144,6 +149,8 @@
   /// @returns the type as a void type
   const VoidType* AsVoid() const;
 
+  /// @returns the type as an access control type
+  AccessControlType* AsAccessControl();
   /// @returns the type as an alias type
   AliasType* AsAlias();
   /// @returns the type as an array type
diff --git a/src/ast/type/u32_type_test.cc b/src/ast/type/u32_type_test.cc
index 7c42772..ef0ff20 100644
--- a/src/ast/type/u32_type_test.cc
+++ b/src/ast/type/u32_type_test.cc
@@ -25,6 +25,7 @@
 
 TEST_F(U32TypeTest, Is) {
   U32Type u;
+  EXPECT_FALSE(u.IsAccessControl());
   EXPECT_FALSE(u.IsAlias());
   EXPECT_FALSE(u.IsArray());
   EXPECT_FALSE(u.IsBool());
diff --git a/src/ast/type/vector_type_test.cc b/src/ast/type/vector_type_test.cc
index baa1342..1cc5390 100644
--- a/src/ast/type/vector_type_test.cc
+++ b/src/ast/type/vector_type_test.cc
@@ -34,6 +34,7 @@
 TEST_F(VectorTypeTest, Is) {
   I32Type i32;
   VectorType v{&i32, 4};
+  EXPECT_FALSE(v.IsAccessControl());
   EXPECT_FALSE(v.IsAlias());
   EXPECT_FALSE(v.IsArray());
   EXPECT_FALSE(v.IsBool());