Remove @block attribute

Since this was the only attribute allowed on structures, we can also
remove the parsing code for them. However, we still need to have
attributes on the struct AST node, since the AddSpirvBlockAttribute
transform adds one.

Fixed: tint:1324
Change-Id: I7966237765b1d8a58c59908b59e1f1152a8a0439
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/83740
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/docs/tint/origin-trial-changes.md b/docs/tint/origin-trial-changes.md
index 93e0708..ef741c9 100644
--- a/docs/tint/origin-trial-changes.md
+++ b/docs/tint/origin-trial-changes.md
@@ -1,5 +1,11 @@
 # Tint changes during Origin Trial
 
+## Changes for M102
+
+### Breaking changes
+
+* The `@block` attribute has been removed. [tint:1324](crbug.com/tint/1324)
+
 ## Changes for M101
 
 ### New Features
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 1799ee3..54578e7 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -298,8 +298,6 @@
     "ast/stride_attribute.h",
     "ast/struct.cc",
     "ast/struct.h",
-    "ast/struct_block_attribute.cc",
-    "ast/struct_block_attribute.h",
     "ast/struct_member.cc",
     "ast/struct_member.h",
     "ast/struct_member_align_attribute.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 91ce819..011fc39 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -182,8 +182,6 @@
   ast/storage_texture.h
   ast/stride_attribute.cc
   ast/stride_attribute.h
-  ast/struct_block_attribute.cc
-  ast/struct_block_attribute.h
   ast/struct_member_align_attribute.cc
   ast/struct_member_align_attribute.h
   ast/struct_member_offset_attribute.cc
@@ -909,7 +907,6 @@
       reader/wgsl/parser_impl_struct_body_decl_test.cc
       reader/wgsl/parser_impl_struct_decl_test.cc
       reader/wgsl/parser_impl_struct_attribute_decl_test.cc
-      reader/wgsl/parser_impl_struct_attribute_test.cc
       reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc
       reader/wgsl/parser_impl_struct_member_attribute_test.cc
       reader/wgsl/parser_impl_struct_member_test.cc
diff --git a/src/tint/ast/module_clone_test.cc b/src/tint/ast/module_clone_test.cc
index 3879b1a..12853ba 100644
--- a/src/tint/ast/module_clone_test.cc
+++ b/src/tint/ast/module_clone_test.cc
@@ -26,14 +26,13 @@
 #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
   // Shader that exercises the bulk of the AST nodes and types.
   // See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
-  Source::File file("test.wgsl", R"([[block]]
-struct S0 {
+  Source::File file("test.wgsl", R"(struct S0 {
   @size(4)
   m0 : u32;
   m1 : array<u32>;
 };
 
-[[block]] struct S1 {
+struct S1 {
   @size(4)
   m0 : u32;
   m1 : array<u32, 6>;
diff --git a/src/tint/ast/struct.cc b/src/tint/ast/struct.cc
index b3f5c3a..c33c07d 100644
--- a/src/tint/ast/struct.cc
+++ b/src/tint/ast/struct.cc
@@ -16,7 +16,6 @@
 
 #include <string>
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/program_builder.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::ast::Struct);
diff --git a/src/tint/ast/struct_block_attribute.cc b/src/tint/ast/struct_block_attribute.cc
deleted file mode 100644
index 77ed34e..0000000
--- a/src/tint/ast/struct_block_attribute.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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/tint/ast/struct_block_attribute.h"
-
-#include <string>
-
-#include "src/tint/program_builder.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::StructBlockAttribute);
-
-namespace tint {
-namespace ast {
-
-StructBlockAttribute::StructBlockAttribute(ProgramID pid, const Source& src)
-    : Base(pid, src) {}
-
-StructBlockAttribute::~StructBlockAttribute() = default;
-
-std::string StructBlockAttribute::Name() const {
-  return "block";
-}
-
-const StructBlockAttribute* StructBlockAttribute::Clone(
-    CloneContext* ctx) const {
-  // Clone arguments outside of create() call to have deterministic ordering
-  auto src = ctx->Clone(source);
-  return ctx->dst->create<StructBlockAttribute>(src);
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/tint/ast/struct_block_attribute.h b/src/tint/ast/struct_block_attribute.h
deleted file mode 100644
index ba5c38f..0000000
--- a/src/tint/ast/struct_block_attribute.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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_TINT_AST_STRUCT_BLOCK_ATTRIBUTE_H_
-#define SRC_TINT_AST_STRUCT_BLOCK_ATTRIBUTE_H_
-
-#include <string>
-#include <vector>
-
-#include "src/tint/ast/attribute.h"
-
-namespace tint {
-namespace ast {
-
-/// The struct block attribute
-class StructBlockAttribute final
-    : public Castable<StructBlockAttribute, Attribute> {
- public:
-  /// Constructor
-  /// @param pid the identifier of the program that owns this node
-  /// @param src the source of this node
-  StructBlockAttribute(ProgramID pid, const Source& src);
-  ~StructBlockAttribute() override;
-
-  /// @returns the WGSL name for the attribute
-  std::string Name() const override;
-
-  /// Clones this node and all transitive child nodes using the `CloneContext`
-  /// `ctx`.
-  /// @param ctx the clone context
-  /// @return the newly cloned node
-  const StructBlockAttribute* Clone(CloneContext* ctx) const override;
-};
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_TINT_AST_STRUCT_BLOCK_ATTRIBUTE_H_
diff --git a/src/tint/ast/struct_test.cc b/src/tint/ast/struct_test.cc
index c26d8cc..3b02767 100644
--- a/src/tint/ast/struct_test.cc
+++ b/src/tint/ast/struct_test.cc
@@ -22,17 +22,19 @@
 #include "src/tint/ast/matrix.h"
 #include "src/tint/ast/pointer.h"
 #include "src/tint/ast/sampler.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/test_helper.h"
 #include "src/tint/ast/texture.h"
 #include "src/tint/ast/u32.h"
 #include "src/tint/ast/vector.h"
+#include "src/tint/transform/add_spirv_block_attribute.h"
 
 namespace tint {
 namespace ast {
 namespace {
 
 using AstStructTest = TestHelper;
+using SpirvBlockAttribute =
+    transform::AddSpirvBlockAttribute::SpirvBlockAttribute;
 
 TEST_F(AstStructTest, Creation) {
   auto name = Sym("s");
@@ -50,14 +52,14 @@
 TEST_F(AstStructTest, Creation_WithAttributes) {
   auto name = Sym("s");
   AttributeList attrs;
-  attrs.push_back(create<StructBlockAttribute>());
+  attrs.push_back(ASTNodes().Create<SpirvBlockAttribute>(ID()));
 
   auto* s =
       create<Struct>(name, StructMemberList{Member("a", ty.i32())}, attrs);
   EXPECT_EQ(s->name, name);
   EXPECT_EQ(s->members.size(), 1u);
   ASSERT_EQ(s->attributes.size(), 1u);
-  EXPECT_TRUE(s->attributes[0]->Is<StructBlockAttribute>());
+  EXPECT_TRUE(s->attributes[0]->Is<SpirvBlockAttribute>());
   EXPECT_EQ(s->source.range.begin.line, 0u);
   EXPECT_EQ(s->source.range.begin.column, 0u);
   EXPECT_EQ(s->source.range.end.line, 0u);
@@ -69,11 +71,11 @@
   auto* s = create<Struct>(
       Source{Source::Range{Source::Location{27, 4}, Source::Location{27, 8}}},
       name, StructMemberList{Member("a", ty.i32())},
-      AttributeList{create<StructBlockAttribute>()});
+      AttributeList{ASTNodes().Create<SpirvBlockAttribute>(ID())});
   EXPECT_EQ(s->name, name);
   EXPECT_EQ(s->members.size(), 1u);
   ASSERT_EQ(s->attributes.size(), 1u);
-  EXPECT_TRUE(s->attributes[0]->Is<StructBlockAttribute>());
+  EXPECT_TRUE(s->attributes[0]->Is<SpirvBlockAttribute>());
   EXPECT_EQ(s->source.range.begin.line, 27u);
   EXPECT_EQ(s->source.range.begin.column, 4u);
   EXPECT_EQ(s->source.range.end.line, 27u);
@@ -119,9 +121,9 @@
       {
         ProgramBuilder b1;
         ProgramBuilder b2;
-        b1.create<Struct>(b1.Sym("S"),
-                          StructMemberList{b1.Member("a", b1.ty.i32())},
-                          AttributeList{b2.create<StructBlockAttribute>()});
+        b1.create<Struct>(
+            b1.Sym("S"), StructMemberList{b1.Member("a", b1.ty.i32())},
+            AttributeList{b2.ASTNodes().Create<SpirvBlockAttribute>(b2.ID())});
       },
       "internal compiler error");
 }
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index a1c3ba7..dc8ba8f 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -17,7 +17,6 @@
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/inspector/test_inspector_builder.h"
 #include "src/tint/inspector/test_inspector_runner.h"
@@ -764,7 +763,7 @@
   ast::StructMemberList members;
   members.push_back(
       Member("inner_position", ty.u32(), {Builtin(ast::Builtin::kSampleMask)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -797,7 +796,7 @@
   ast::StructMemberList members;
   members.push_back(Member("inner_sample_mask", ty.u32(),
                            {Builtin(ast::Builtin::kSampleMask)}));
-  Structure("out_struct", members, {});
+  Structure("out_struct", members);
 
   Func("ep_func", {}, ty.type_name("out_struct"),
        {Decl(Var("out_var", ty.type_name("out_struct"))), Return("out_var")},
@@ -829,7 +828,7 @@
   ast::StructMemberList members;
   members.push_back(Member("inner_position", ty.vec4<f32>(),
                            {Builtin(ast::Builtin::kPosition)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -861,7 +860,7 @@
   ast::StructMemberList members;
   members.push_back(Member("inner_position", ty.bool_(),
                            {Builtin(ast::Builtin::kFrontFacing)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -893,7 +892,7 @@
   ast::StructMemberList members;
   members.push_back(Member("inner_position", ty.u32(),
                            {Builtin(ast::Builtin::kSampleIndex)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -925,7 +924,7 @@
   ast::StructMemberList members;
   members.push_back(Member("inner_position", ty.vec3<u32>(),
                            {Builtin(ast::Builtin::kNumWorkgroups)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -942,7 +941,7 @@
 TEST_F(InspectorGetEntryPointTest, ImplicitInterpolate) {
   ast::StructMemberList members;
   members.push_back(Member("struct_inner", ty.f32(), {Location(0)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -966,7 +965,7 @@
   members.push_back(
       Member("struct_inner", ty.f32(),
              {Interpolate(params.in_type, params.in_sampling), Location(0)}));
-  Structure("in_struct", members, {});
+  Structure("in_struct", members);
   auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
   Func("ep_func", {in_var}, ty.void_(), {Return()},
@@ -1548,8 +1547,7 @@
   auto* foo_struct_type = Structure(
       "foo_type",
       {Member("0i32", ty.i32()),
-       Member("b", ty.array(ty.u32(), 4, /*stride*/ 16), {MemberAlign(16)})},
-      {create<ast::StructBlockAttribute>()});
+       Member("b", ty.array(ty.u32(), 4, /*stride*/ 16), {MemberAlign(16)})});
 
   AddUniformBuffer("foo_ub", ty.Of(foo_struct_type), 0, 0);
 
@@ -2948,8 +2946,7 @@
   // This struct should occupy 68 bytes. 4 from the i32 field, and another 64
   // from the 4-element array with 16-byte stride.
   auto* wg_struct_type = MakeStructType(
-      "WgStruct", {ty.i32(), ty.array(ty.i32(), 4, /*stride=*/16)},
-      /*is_block=*/false);
+      "WgStruct", {ty.i32(), ty.array(ty.i32(), 4, /*stride=*/16)});
   AddWorkgroupStorage("wg_struct_var", ty.Of(wg_struct_type));
   MakeStructVariableReferenceBodyFunction("wg_struct_func", "wg_struct_var",
                                           {{0, ty.i32()}});
@@ -2992,8 +2989,7 @@
   const auto* wg_struct_type = MakeStructTypeFromMembers(
       "WgStruct",
       {MakeStructMember(0, ty.f32(),
-                        {create<ast::StructMemberAlignAttribute>(1024)})},
-      /*is_block=*/false);
+                        {create<ast::StructMemberAlignAttribute>(1024)})});
 
   AddWorkgroupStorage("wg_struct_var", ty.Of(wg_struct_type));
   MakeStructVariableReferenceBodyFunction("wg_struct_func", "wg_struct_var",
diff --git a/src/tint/inspector/test_inspector_builder.cc b/src/tint/inspector/test_inspector_builder.cc
index 301d328..183dbeb 100644
--- a/src/tint/inspector/test_inspector_builder.cc
+++ b/src/tint/inspector/test_inspector_builder.cc
@@ -91,24 +91,18 @@
 
 const ast::Struct* InspectorBuilder::MakeStructType(
     const std::string& name,
-    std::vector<const ast::Type*> member_types,
-    bool is_block) {
+    std::vector<const ast::Type*> member_types) {
   ast::StructMemberList members;
   for (auto* type : member_types) {
     members.push_back(MakeStructMember(members.size(), type, {}));
   }
-  return MakeStructTypeFromMembers(name, std::move(members), is_block);
+  return MakeStructTypeFromMembers(name, std::move(members));
 }
 
 const ast::Struct* InspectorBuilder::MakeStructTypeFromMembers(
     const std::string& name,
-    ast::StructMemberList members,
-    bool is_block) {
-  ast::AttributeList attrs;
-  if (is_block) {
-    attrs.push_back(create<ast::StructBlockAttribute>());
-  }
-  return Structure(name, std::move(members), attrs);
+    ast::StructMemberList members) {
+  return Structure(name, std::move(members));
 }
 
 const ast::StructMember* InspectorBuilder::MakeStructMember(
@@ -121,13 +115,13 @@
 const ast::Struct* InspectorBuilder::MakeUniformBufferType(
     const std::string& name,
     std::vector<const ast::Type*> member_types) {
-  return MakeStructType(name, member_types, true);
+  return MakeStructType(name, member_types);
 }
 
 std::function<const ast::TypeName*()> InspectorBuilder::MakeStorageBufferTypes(
     const std::string& name,
     std::vector<const ast::Type*> member_types) {
-  MakeStructType(name, member_types, true);
+  MakeStructType(name, member_types);
   return [this, name] { return ty.type_name(name); };
 }
 
diff --git a/src/tint/inspector/test_inspector_builder.h b/src/tint/inspector/test_inspector_builder.h
index 4101505..b19066d 100644
--- a/src/tint/inspector/test_inspector_builder.h
+++ b/src/tint/inspector/test_inspector_builder.h
@@ -24,7 +24,6 @@
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/depth_texture_type.h"
@@ -153,20 +152,16 @@
   /// Generates a struct type
   /// @param name name for the type
   /// @param member_types a vector of member types
-  /// @param is_block whether or not to decorate as a Block
   /// @returns a struct type
   const ast::Struct* MakeStructType(const std::string& name,
-                                    std::vector<const ast::Type*> member_types,
-                                    bool is_block);
+                                    std::vector<const ast::Type*> member_types);
 
   /// Generates a struct type from a list of member nodes.
   /// @param name name for the struct type
   /// @param members a vector of members
-  /// @param is_block whether or not to decorate as a Block
   /// @returns a struct type
   const ast::Struct* MakeStructTypeFromMembers(const std::string& name,
-                                               ast::StructMemberList members,
-                                               bool is_block);
+                                               ast::StructMemberList members);
 
   /// Generates a struct member with a specified index and type.
   /// @param index index of the field within the struct
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index b791d05..c6a5403 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -62,7 +62,6 @@
 #include "src/tint/ast/stage_attribute.h"
 #include "src/tint/ast/storage_texture.h"
 #include "src/tint/ast/stride_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/struct_member_align_attribute.h"
 #include "src/tint/ast/struct_member_offset_attribute.h"
 #include "src/tint/ast/struct_member_size_attribute.h"
@@ -1901,12 +1900,6 @@
     return create<ast::StructMemberAlignAttribute>(source_, val);
   }
 
-  /// Creates a ast::StructBlockAttribute
-  /// @returns the struct block attribute pointer
-  const ast::StructBlockAttribute* StructBlock() {
-    return create<ast::StructBlockAttribute>();
-  }
-
   /// Creates the ast::GroupAttribute
   /// @param value group attribute index
   /// @returns the group attribute pointer
@@ -2074,16 +2067,14 @@
   /// @param source the source information
   /// @param name the struct name
   /// @param members the struct members
-  /// @param attributes the optional struct attributes
   /// @returns the struct type
   template <typename NAME>
   const ast::Struct* Structure(const Source& source,
                                NAME&& name,
-                               ast::StructMemberList members,
-                               ast::AttributeList attributes = {}) {
+                               ast::StructMemberList members) {
     auto sym = Sym(std::forward<NAME>(name));
     auto* type = create<ast::Struct>(source, sym, std::move(members),
-                                     std::move(attributes));
+                                     ast::AttributeList{});
     AST().AddTypeDecl(type);
     return type;
   }
@@ -2091,15 +2082,12 @@
   /// Creates a ast::Struct registering it with the AST().TypeDecls().
   /// @param name the struct name
   /// @param members the struct members
-  /// @param attributes the optional struct attributes
   /// @returns the struct type
   template <typename NAME>
-  const ast::Struct* Structure(NAME&& name,
-                               ast::StructMemberList members,
-                               ast::AttributeList attributes = {}) {
+  const ast::Struct* Structure(NAME&& name, ast::StructMemberList members) {
     auto sym = Sym(std::forward<NAME>(name));
     auto* type =
-        create<ast::Struct>(sym, std::move(members), std::move(attributes));
+        create<ast::Struct>(sym, std::move(members), ast::AttributeList{});
     AST().AddTypeDecl(type);
     return type;
   }
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 54c53dd..55abfcf 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -29,7 +29,6 @@
 #include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/switch_statement.h"
 #include "src/tint/ast/type_name.h"
 #include "src/tint/ast/unary_op_expression.h"
@@ -111,7 +110,6 @@
 }
 
 const char kBindingAttribute[] = "binding";
-const char kBlockAttribute[] = "block";
 const char kBuiltinAttribute[] = "builtin";
 const char kGroupAttribute[] = "group";
 const char kIdAttribute[] = "id";
@@ -126,8 +124,7 @@
 
 bool is_attribute(Token t) {
   return t == kAlignAttribute || t == kBindingAttribute ||
-         t == kBlockAttribute || t == kBuiltinAttribute ||
-         t == kGroupAttribute || t == kIdAttribute ||
+         t == kBuiltinAttribute || t == kGroupAttribute || t == kIdAttribute ||
          t == kInterpolateAttribute || t == kLocationAttribute ||
          t == kSizeAttribute || t == kStageAttribute || t == kStrideAttribute ||
          t == kWorkgroupSizeAttribute;
@@ -394,7 +391,7 @@
       return true;
     }
 
-    auto str = struct_decl(attrs.value);
+    auto str = struct_decl();
     if (str.errored)
       return Failure::kErrored;
 
@@ -1189,8 +1186,8 @@
 }
 
 // struct_decl
-//   : struct_attribute_decl* STRUCT IDENT struct_body_decl
-Maybe<const ast::Struct*> ParserImpl::struct_decl(ast::AttributeList& attrs) {
+//   : STRUCT IDENT struct_body_decl
+Maybe<const ast::Struct*> ParserImpl::struct_decl() {
   auto t = peek();
   auto source = t.source();
 
@@ -1207,7 +1204,7 @@
 
   auto sym = builder_.Symbols().Register(name.value);
   return create<ast::Struct>(source, sym, std::move(body.value),
-                             std::move(attrs));
+                             ast::AttributeList{});
 }
 
 // struct_body_decl
@@ -3052,11 +3049,6 @@
     });
   }
 
-  if (t == kBlockAttribute) {
-    deprecated(t.source(), "[[block]] attributes have been removed from WGSL");
-    return create<ast::StructBlockAttribute>(t.source());
-  }
-
   if (t == kStrideAttribute) {
     const char* use = "stride attribute";
     return expect_paren_block(use, [&]() -> Result {
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 3ae3739..8c99f0a 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -428,11 +428,9 @@
   /// @param use a description of what was being parsed if an error was raised.
   /// @returns the storage class or StorageClass::kNone if none matched
   Expect<ast::StorageClass> expect_storage_class(std::string_view use);
-  /// Parses a `struct_decl` grammar element with the initial
-  /// `struct_attribute_decl*` provided as `attrs`.
+  /// Parses a `struct_decl` grammar element.
   /// @returns the struct type or nullptr on error
-  /// @param attrs the list of attributes for the struct declaration.
-  Maybe<const ast::Struct*> struct_decl(ast::AttributeList& attrs);
+  Maybe<const ast::Struct*> struct_decl();
   /// Parses a `struct_body_decl` grammar element, erroring on parse failure.
   /// @returns the struct members
   Expect<ast::StructMemberList> expect_struct_body_decl();
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index 3cde554..f8c6136 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -725,42 +725,6 @@
 )");
 }
 
-// TODO(crbug.com/tint/1324): DEPRECATED: Remove when [[block]] is removed.
-TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStructAttrMissingStruct) {
-  EXPECT(
-      "[[block]];",
-      R"(test.wgsl:1:1 warning: use of deprecated language feature: [[attribute]] style attributes have been replaced with @attribute style
-[[block]];
-^^
-
-test.wgsl:1:3 warning: use of deprecated language feature: [[block]] attributes have been removed from WGSL
-[[block]];
-  ^^^^^
-
-test.wgsl:1:10 error: expected declaration after attributes
-[[block]];
-         ^
-)");
-}
-
-// TODO(crbug.com/tint/1324): DEPRECATED: Remove when [[block]] is removed.
-TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStructAttrMissingEnd) {
-  EXPECT(
-      "[[block struct {};",
-      R"(test.wgsl:1:1 warning: use of deprecated language feature: [[attribute]] style attributes have been replaced with @attribute style
-[[block struct {};
-^^
-
-test.wgsl:1:3 warning: use of deprecated language feature: [[block]] attributes have been removed from WGSL
-[[block struct {};
-  ^^^^^
-
-test.wgsl:1:9 error: expected ']]' for attribute list
-[[block struct {};
-        ^^^^^^
-)");
-}
-
 TEST_F(ParserImplErrorTest, GlobalDeclStructDeclMissingIdentifier) {
   EXPECT("struct {};",
          R"(test.wgsl:1:8 error: expected identifier for struct declaration
diff --git a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
index a0ac35c..964f498 100644
--- a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
@@ -193,24 +193,6 @@
   ASSERT_EQ(stride->As<ast::StrideAttribute>()->stride, 4u);
 }
 
-// TODO(crbug.com/tint/1324): DEPRECATED: Remove when @block is removed.
-TEST_F(ParserImplTest, GlobalDecl_Struct_WithAttribute) {
-  auto p = parser("[[block]] struct A { data: f32; }");
-  p->expect_global_decl();
-  ASSERT_FALSE(p->has_error()) << p->error();
-
-  auto program = p->program();
-  ASSERT_EQ(program.AST().TypeDecls().size(), 1u);
-
-  auto* t = program.AST().TypeDecls()[0];
-  ASSERT_NE(t, nullptr);
-  ASSERT_TRUE(t->Is<ast::Struct>());
-
-  auto* str = t->As<ast::Struct>();
-  EXPECT_EQ(str->name, program.Symbols().Get("A"));
-  EXPECT_EQ(str->members.size(), 1u);
-}
-
 TEST_F(ParserImplTest, GlobalDecl_Struct_Invalid) {
   auto p = parser("A {}");
   p->expect_global_decl();
diff --git a/src/tint/reader/wgsl/parser_impl_struct_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_struct_attribute_test.cc
deleted file mode 100644
index 451f749..0000000
--- a/src/tint/reader/wgsl/parser_impl_struct_attribute_test.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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/tint/ast/struct_block_attribute.h"
-#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint {
-namespace reader {
-namespace wgsl {
-namespace {
-
-struct AttributeData {
-  const char* input;
-  bool is_block;
-};
-inline std::ostream& operator<<(std::ostream& out, AttributeData data) {
-  out << std::string(data.input);
-  return out;
-}
-
-class AttributeTest : public ParserImplTestWithParam<AttributeData> {};
-
-TEST_P(AttributeTest, Parses) {
-  auto params = GetParam();
-  auto p = parser(params.input);
-
-  auto attr = p->attribute();
-  ASSERT_FALSE(p->has_error());
-  EXPECT_TRUE(attr.matched);
-  EXPECT_FALSE(attr.errored);
-  ASSERT_NE(attr.value, nullptr);
-  auto* struct_attr = attr.value->As<ast::Attribute>();
-  ASSERT_NE(struct_attr, nullptr);
-  EXPECT_EQ(struct_attr->Is<ast::StructBlockAttribute>(), params.is_block);
-}
-INSTANTIATE_TEST_SUITE_P(ParserImplTest,
-                         AttributeTest,
-                         testing::Values(AttributeData{"block", true}));
-
-TEST_F(ParserImplTest, Attribute_NoMatch) {
-  auto p = parser("not-a-stage");
-  auto attr = p->attribute();
-  EXPECT_FALSE(attr.matched);
-  EXPECT_FALSE(attr.errored);
-  ASSERT_EQ(attr.value, nullptr);
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace reader
-}  // namespace tint
diff --git a/src/tint/reader/wgsl/parser_impl_struct_decl_test.cc b/src/tint/reader/wgsl/parser_impl_struct_decl_test.cc
index 448b31f..3e944ff 100644
--- a/src/tint/reader/wgsl/parser_impl_struct_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_struct_decl_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/reader/wgsl/parser_impl_test_helper.h"
 #include "src/tint/utils/string.h"
 
@@ -27,12 +26,7 @@
   a : i32;
   b : f32;
 })");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 0u);
-
-  auto s = p->struct_decl(attrs.value);
+  auto s = p->struct_decl();
   EXPECT_FALSE(p->has_error());
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
@@ -65,12 +59,8 @@
   src = utils::ReplaceAll(src, "$member_b", member_b_ident);
 
   auto p = parser(src);
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 0u);
 
-  auto s = p->struct_decl(attrs.value);
+  auto s = p->struct_decl();
   EXPECT_FALSE(p->has_error());
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
@@ -83,64 +73,10 @@
             p->builder().Symbols().Register(member_b_ident));
 }
 
-TEST_F(ParserImplTest, StructDecl_ParsesWithAttribute) {
-  auto p = parser(R"(
-[[block]] struct B {
-  a : f32;
-  b : f32;
-})");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_TRUE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 1u);
-
-  auto s = p->struct_decl(attrs.value);
-  EXPECT_FALSE(p->has_error());
-  EXPECT_FALSE(s.errored);
-  EXPECT_TRUE(s.matched);
-  ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->name, p->builder().Symbols().Register("B"));
-  ASSERT_EQ(s->members.size(), 2u);
-  EXPECT_EQ(s->members[0]->symbol, p->builder().Symbols().Register("a"));
-  EXPECT_EQ(s->members[1]->symbol, p->builder().Symbols().Register("b"));
-  ASSERT_EQ(s->attributes.size(), 1u);
-  EXPECT_TRUE(s->attributes[0]->Is<ast::StructBlockAttribute>());
-}
-
-TEST_F(ParserImplTest, StructDecl_ParsesWithMultipleAttribute) {
-  auto p = parser(R"(
-[[block]]
-[[block]] struct S {
-  a : f32;
-  b : f32;
-})");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_TRUE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 2u);
-
-  auto s = p->struct_decl(attrs.value);
-  EXPECT_FALSE(p->has_error());
-  EXPECT_FALSE(s.errored);
-  EXPECT_TRUE(s.matched);
-  ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->name, p->builder().Symbols().Register("S"));
-  ASSERT_EQ(s->members.size(), 2u);
-  EXPECT_EQ(s->members[0]->symbol, p->builder().Symbols().Register("a"));
-  EXPECT_EQ(s->members[1]->symbol, p->builder().Symbols().Register("b"));
-  ASSERT_EQ(s->attributes.size(), 2u);
-  EXPECT_TRUE(s->attributes[0]->Is<ast::StructBlockAttribute>());
-  EXPECT_TRUE(s->attributes[1]->Is<ast::StructBlockAttribute>());
-}
-
 TEST_F(ParserImplTest, StructDecl_EmptyMembers) {
   auto p = parser("struct S {}");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 0u);
 
-  auto s = p->struct_decl(attrs.value);
+  auto s = p->struct_decl();
   EXPECT_FALSE(p->has_error());
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
@@ -150,12 +86,8 @@
 
 TEST_F(ParserImplTest, StructDecl_MissingIdent) {
   auto p = parser("struct {}");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 0u);
 
-  auto s = p->struct_decl(attrs.value);
+  auto s = p->struct_decl();
   EXPECT_TRUE(s.errored);
   EXPECT_FALSE(s.matched);
   EXPECT_EQ(s.value, nullptr);
@@ -166,12 +98,8 @@
 
 TEST_F(ParserImplTest, StructDecl_MissingBracketLeft) {
   auto p = parser("struct S }");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 0u);
 
-  auto s = p->struct_decl(attrs.value);
+  auto s = p->struct_decl();
   EXPECT_TRUE(s.errored);
   EXPECT_FALSE(s.matched);
   EXPECT_EQ(s.value, nullptr);
@@ -180,42 +108,6 @@
   EXPECT_EQ(p->error(), "1:10: expected '{' for struct declaration");
 }
 
-// TODO(crbug.com/tint/1324): DEPRECATED: Remove when @block is removed.
-TEST_F(ParserImplTest, StructDecl_InvalidAttributeDecl) {
-  auto p = parser("[[block struct S { a : i32; }");
-  auto attrs = p->attribute_list();
-  EXPECT_TRUE(attrs.errored);
-  EXPECT_FALSE(attrs.matched);
-
-  auto s = p->struct_decl(attrs.value);
-  EXPECT_FALSE(s.errored);
-  EXPECT_TRUE(s.matched);
-  EXPECT_NE(s.value, nullptr);
-
-  EXPECT_TRUE(p->has_error());
-  EXPECT_EQ(
-      p->error(),
-      R"(1:1: use of deprecated language feature: [[attribute]] style attributes have been replaced with @attribute style
-1:3: use of deprecated language feature: [[block]] attributes have been removed from WGSL
-1:9: expected ']]' for attribute list)");
-}
-
-// TODO(crbug.com/tint/1324): DEPRECATED: Remove when [[block]] is removed.
-TEST_F(ParserImplTest, StructDecl_MissingStruct) {
-  auto p = parser("[[block]] S {}");
-  auto attrs = p->attribute_list();
-  EXPECT_FALSE(attrs.errored);
-  EXPECT_TRUE(attrs.matched);
-  ASSERT_EQ(attrs.value.size(), 1u);
-
-  auto s = p->struct_decl(attrs.value);
-  EXPECT_FALSE(s.errored);
-  EXPECT_FALSE(s.matched);
-  EXPECT_EQ(s.value, nullptr);
-
-  EXPECT_FALSE(p->has_error());
-}
-
 }  // namespace
 }  // namespace wgsl
 }  // namespace reader
diff --git a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index e3f193b..f41ec2d 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/reader/wgsl/parser_impl_test_helper.h"
 
 namespace tint {
@@ -83,10 +82,6 @@
   ast::StructMemberList members;
   members.push_back(mem);
 
-  auto* block_attr = create<ast::StructBlockAttribute>();
-  ast::AttributeList attrs;
-  attrs.push_back(block_attr);
-
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
   ASSERT_TRUE(decl.errored);
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index a69d6a0..3e8f7e6 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "gmock/gmock.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/storage_texture_type.h"
 
@@ -26,11 +25,10 @@
 using ResolverAssignmentValidationTest = ResolverTest;
 
 TEST_F(ResolverAssignmentValidationTest, ReadOnlyBuffer) {
-  // [[block]] struct S { m : i32 };
+  // struct S { m : i32 };
   // @group(0) @binding(0)
   // var<storage,read> a : S;
-  auto* s = Structure("S", {Member("m", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("m", ty.i32())});
   Global(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
          ast::AttributeList{
@@ -250,12 +248,11 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Atomic) {
-  // [[block]] struct S { a : atomic<i32>; };
+  // struct S { a : atomic<i32>; };
   // @group(0) @binding(0) var<storage, read_write> v : S;
   // v.a = v.a;
 
-  auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))});
   Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
          ast::AttributeList{
@@ -272,12 +269,11 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_RuntimeArray) {
-  // [[block]] struct S { a : array<f32>; };
+  // struct S { a : array<f32>; };
   // @group(0) @binding(0) var<storage, read_write> v : S;
   // v.a = v.a;
 
-  auto* s = Structure("S", {Member("a", ty.array(ty.f32()))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.array(ty.f32()))});
   Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
          ast::AttributeList{
@@ -295,7 +291,6 @@
 
 TEST_F(ResolverAssignmentValidationTest,
        AssignToPhony_NonConstructibleStruct_Fail) {
-  // [[block]]
   // struct S {
   //   arr: array<i32>;
   // };
@@ -303,7 +298,7 @@
   // fn f() {
   //   _ = s;
   // }
-  auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
+  auto* s = Structure("S", {Member("arr", ty.array<i32>())});
   Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
   WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
@@ -316,7 +311,6 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
-  // [[block]]
   // struct S {
   //   arr: array<i32>;
   // };
@@ -324,7 +318,7 @@
   // fn f() {
   //   _ = s.arr;
   // }
-  auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
+  auto* s = Structure("S", {Member("arr", ty.array<i32>())});
   Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
   WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
@@ -338,12 +332,10 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
-  // [[block]]
   // struct S {
   //   i:   i32;
   //   arr: array<i32>;
   // };
-  // [[block]]
   // struct U {
   //   i:   i32;
   // };
@@ -367,13 +359,11 @@
   //   _ = wg;
   //   _ = wg[3];
   // }
-  auto* S = Structure("S",
-                      {
-                          Member("i", ty.i32()),
-                          Member("arr", ty.array<i32>()),
-                      },
-                      {StructBlock()});
-  auto* U = Structure("U", {Member("i", ty.i32())}, {StructBlock()});
+  auto* S = Structure("S", {
+                               Member("i", ty.i32()),
+                               Member("arr", ty.array<i32>()),
+                           });
+  auto* U = Structure("U", {Member("i", ty.i32())});
   Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
          GroupAndBinding(0, 0));
   Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
diff --git a/src/tint/resolver/atomics_test.cc b/src/tint/resolver/atomics_test.cc
index 04592b0..0d3b51f 100644
--- a/src/tint/resolver/atomics_test.cc
+++ b/src/tint/resolver/atomics_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/atomic_type.h"
@@ -50,8 +49,8 @@
 }
 
 TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
-  auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s =
+      Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
   auto* g = Global("g", ty.Of(s), ast::StorageClass::kStorage,
                    ast::Access::kReadWrite,
                    ast::AttributeList{
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index 47da93b..dad3559 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -34,8 +34,8 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, StorageClass_Storage) {
-  auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))},
-                      {StructBlock()});
+  auto* s =
+      Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
   Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          GroupAndBinding(0, 0));
 
@@ -211,8 +211,8 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
-  auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))},
-                      {StructBlock()});
+  auto* s =
+      Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead, GroupAndBinding(0, 0));
 
@@ -225,8 +225,8 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
-  auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))},
-                      {StructBlock()});
+  auto* s =
+      Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead, GroupAndBinding(0, 0));
 
@@ -245,8 +245,7 @@
 
   auto* Inner =
       Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
-  auto* Outer =
-      Structure("Outer", {Member("m", ty.Of(Inner))}, {StructBlock()});
+  auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
   Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage,
          ast::Access::kRead, GroupAndBinding(0, 0));
 
@@ -265,8 +264,7 @@
 
   auto* Inner =
       Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
-  auto* Outer =
-      Structure("Outer", {Member("m", ty.Of(Inner))}, {StructBlock()});
+  auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
   Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage,
          ast::Access::kRead, GroupAndBinding(0, 0));
 
@@ -308,7 +306,7 @@
   auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
   auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
   auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
-  auto* s0 = Structure("S0", {Member("x", ty.Of(s1))}, {StructBlock()});
+  auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
   Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage,
          ast::Access::kRead, GroupAndBinding(0, 0));
 
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 7231596..71224c5 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -15,6 +15,7 @@
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/transform/add_spirv_block_attribute.h"
 
 #include "gmock/gmock.h"
 
@@ -63,7 +64,6 @@
   kSize,
   kStage,
   kStride,
-  kStructBlock,
   kWorkgroup,
 
   kBindingAndGroup,
@@ -115,8 +115,6 @@
       return {builder.Stage(source, ast::PipelineStage::kCompute)};
     case AttributeKind::kStride:
       return {builder.create<ast::StrideAttribute>(source, 4u)};
-    case AttributeKind::kStructBlock:
-      return {builder.create<ast::StructBlockAttribute>(source)};
     case AttributeKind::kWorkgroup:
       return {builder.create<ast::WorkgroupAttribute>(source, builder.Expr(1))};
     case AttributeKind::kBindingAndGroup:
@@ -160,7 +158,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -195,7 +192,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 }  // namespace FunctionInputAndOutputTests
@@ -244,7 +240,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -283,7 +278,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -333,7 +327,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -381,7 +374,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -431,7 +423,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -477,7 +468,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -537,13 +527,15 @@
 
 namespace StructAndStructMemberTests {
 using StructAttributeTest = TestWithParams;
+using SpirvBlockAttribute =
+    transform::AddSpirvBlockAttribute::SpirvBlockAttribute;
 TEST_P(StructAttributeTest, IsValid) {
   auto& params = GetParam();
 
-  Structure("mystruct", {Member("a", ty.f32())},
-            createAttributes(Source{{12, 34}}, *this, params.kind));
-
-  WrapInFunction();
+  auto* str = create<ast::Struct>(
+      Sym("mystruct"), ast::StructMemberList{Member("a", ty.f32())},
+      createAttributes(Source{{12, 34}}, *this, params.kind));
+  AST().AddGlobalDeclaration(str);
 
   if (params.should_pass) {
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -568,25 +560,9 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, true},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
-TEST_F(StructAttributeTest, DuplicateAttribute) {
-  Structure("mystruct",
-            {
-                Member("a", ty.i32()),
-            },
-            {
-                create<ast::StructBlockAttribute>(Source{{12, 34}}),
-                create<ast::StructBlockAttribute>(Source{{56, 78}}),
-            });
-  WrapInFunction();
-  EXPECT_FALSE(r()->Resolve());
-  EXPECT_EQ(r()->error(),
-            R"(56:78 error: duplicate block attribute
-12:34 note: first attribute declared here)");
-}
 using StructMemberAttributeTest = TestWithParams;
 TEST_P(StructMemberAttributeTest, IsValid) {
   auto& params = GetParam();
@@ -625,7 +601,6 @@
                     TestParams{AttributeKind::kSize, true},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 TEST_F(StructMemberAttributeTest, DuplicateAttribute) {
@@ -678,11 +653,9 @@
 
   auto* arr = ty.array(ty.f32(), nullptr,
                        createAttributes(Source{{12, 34}}, *this, params.kind));
-  Structure("mystruct",
-            {
-                Member("a", arr),
-            },
-            {create<ast::StructBlockAttribute>()});
+  Structure("mystruct", {
+                            Member("a", arr),
+                        });
 
   WrapInFunction();
 
@@ -709,7 +682,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, true},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -753,7 +725,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, true}));
 
@@ -818,7 +789,6 @@
                     TestParams{AttributeKind::kSize, false},
                     TestParams{AttributeKind::kStage, false},
                     TestParams{AttributeKind::kStride, false},
-                    TestParams{AttributeKind::kStructBlock, false},
                     TestParams{AttributeKind::kWorkgroup, false},
                     TestParams{AttributeKind::kBindingAndGroup, false}));
 
@@ -968,8 +938,7 @@
 
 using ResourceAttributeTest = ResolverTest;
 TEST_F(ResourceAttributeTest, UniformBufferMissingBinding) {
-  auto* s = Structure("S", {Member("x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.i32())});
   Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
 
   EXPECT_FALSE(r()->Resolve());
@@ -979,8 +948,7 @@
 }
 
 TEST_F(ResourceAttributeTest, StorageBufferMissingBinding) {
-  auto* s = Structure("S", {Member("x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.i32())});
   Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead);
 
@@ -1344,8 +1312,7 @@
       {
           Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
           Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
-      },
-      {});
+      });
   Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
        ast::AttributeList{Stage(ast::PipelineStage::kVertex)});
 
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index d449678..5b01025 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -25,7 +25,6 @@
 #include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/switch_statement.h"
 #include "src/tint/ast/unary_op_expression.h"
 #include "src/tint/ast/variable_decl_statement.h"
@@ -633,8 +632,7 @@
 
 TEST_F(ResolverBuiltinDataTest, ArrayLength_Vector) {
   auto* ary = ty.array<i32>();
-  auto* str =
-      Structure("S", {Member("x", ary)}, {create<ast::StructBlockAttribute>()});
+  auto* str = Structure("S", {Member("x", ary)});
   Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 4e18ce6..8d6ef88 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -379,8 +379,7 @@
             ast::IdAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
             ast::InvariantAttribute, ast::LocationAttribute,
             ast::StageAttribute, ast::StrideAttribute,
-            ast::StructBlockAttribute, ast::StructMemberAlignAttribute,
-            ast::StructMemberOffsetAttribute,
+            ast::StructMemberAlignAttribute, ast::StructMemberOffsetAttribute,
             ast::StructMemberSizeAttribute>()) {
       return;
     }
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 1f61452..b8a83e6 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -16,7 +16,6 @@
 #include "src/tint/ast/location_attribute.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 
@@ -540,16 +539,13 @@
 }
 
 TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
-  // [[block]]
   // struct Input {
   //   @location(0) a : array<f32>;
   // };
   // @stage(fragment)
   // fn main(param : Input) {}
-  auto* input = Structure(
-      "Input",
-      {Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})},
-      {create<ast::StructBlockAttribute>()});
+  auto* input = Structure("Input", {Member(Source{{13, 43}}, "a",
+                                           ty.array<float>(), {Location(0)})});
   auto* param = Param("param", ty.Of(input));
   Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
        {Stage(ast::PipelineStage::kFragment)});
@@ -563,14 +559,13 @@
 }
 
 TEST_F(LocationAttributeTests, BadMemberType_Input) {
-  // [[block]]
   // struct S { @location(0) m: array<i32>; };
   // @stage(fragment)
   // fn frag_main( a: S) {}
 
   auto* m = Member(Source{{34, 56}}, "m", ty.array<i32>(),
                    ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-  auto* s = Structure("S", {m}, ast::AttributeList{StructBlock()});
+  auto* s = Structure("S", {m});
   auto* p = Param("a", ty.Of(s));
 
   Func("frag_main", {p}, ty.void_(), {},
@@ -682,7 +677,6 @@
 }
 
 TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
-  // [[block]]
   // struct Output {
   //   @location(0) a : array<f32>;
   // };
@@ -690,10 +684,9 @@
   // fn main() -> Output {
   //   return Output();
   // }
-  auto* output = Structure("Output",
-                           {Member(Source{{13, 43}}, "a", ty.array<float>(),
-                                   {Location(Source{{12, 34}}, 0)})},
-                           {create<ast::StructBlockAttribute>()});
+  auto* output =
+      Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<float>(),
+                                  {Location(Source{{12, 34}}, 0)})});
   Func(Source{{12, 34}}, "main", {}, ty.Of(output),
        {Return(Construct(ty.Of(output)))},
        {Stage(ast::PipelineStage::kFragment)});
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index f876bfd..d01470d 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "gmock/gmock.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/struct.h"
 
@@ -26,8 +25,7 @@
 using ResolverHostShareableValidationTest = ResolverTest;
 
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
 
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
@@ -46,8 +44,7 @@
 }
 
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
 
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
@@ -67,8 +64,7 @@
 
 TEST_F(ResolverHostShareableValidationTest, Aliases) {
   auto* a1 = Alias("a1", ty.bool_());
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))});
   auto* a2 = Alias("a2", ty.Of(s));
   Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage,
          ast::Access::kRead,
@@ -91,8 +87,7 @@
   auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", ty.Of(i1))});
   auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", ty.Of(i2))});
 
-  auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
   Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
@@ -132,8 +127,7 @@
                                  Member(Source{{6, 1}}, "z3", ty.Of(a2)),
                              });
 
-  auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
   Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index 534cb07..676ca68 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 
@@ -154,7 +153,7 @@
 
 TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
   auto* member = Member("x", ty.i32());
-  auto* str = Structure("S", {member}, {create<ast::StructBlockAttribute>()});
+  auto* str = Structure("S", {member});
 
   auto* expected_type = create<sem::Struct>(
       str, str->name,
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index fa26304..1a1e83c 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/reference_type.h"
@@ -61,8 +60,7 @@
 TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
   // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-  auto* buf = Structure("S", {Member("m", ty.i32())},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("m", ty.i32())});
   auto* function = Var("f", ty.i32());
   auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
   auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
diff --git a/src/tint/resolver/ptr_ref_validation_test.cc b/src/tint/resolver/ptr_ref_validation_test.cc
index 2bec6ef..ccc3991 100644
--- a/src/tint/resolver/ptr_ref_validation_test.cc
+++ b/src/tint/resolver/ptr_ref_validation_test.cc
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/tint/ast/bitcast_expression.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/reference_type.h"
@@ -139,7 +138,7 @@
   // struct Inner {
   //    arr: array<i32, 4>;
   // }
-  // [[block]] struct S {
+  // struct S {
   //    inner: Inner;
   // }
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -147,8 +146,7 @@
   //   let p : pointer<storage, i32> = &s.inner.arr[2];
   // }
   auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-  auto* buf = Structure("S", {Member("inner", ty.Of(inner))},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
   auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
                          ast::Access::kReadWrite,
                          ast::AttributeList{
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index ec0b26d..3d12b75 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -30,7 +30,6 @@
 #include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/switch_statement.h"
 #include "src/tint/ast/unary_op_expression.h"
 #include "src/tint/ast/variable_decl_statement.h"
@@ -783,8 +782,7 @@
 }
 
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
-  auto* s = Structure("S", {Member("m", ty.u32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("m", ty.u32())});
 
   auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage,
                         ast::Access::kReadWrite,
@@ -817,8 +815,7 @@
 }
 
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
-  auto* s = Structure("S", {Member("m", ty.u32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("m", ty.u32())});
 
   auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage,
                         ast::Access::kReadWrite,
@@ -1800,10 +1797,9 @@
 }
 
 TEST_F(ResolverTest, Access_SetForStorageBuffer) {
-  // [[block]] struct S { x : i32 };
+  // struct S { x : i32 };
   // var<storage> g : S;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
   auto* var =
       Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
              ast::AttributeList{
diff --git a/src/tint/resolver/resolver_validation.cc b/src/tint/resolver/resolver_validation.cc
index a23079d..8dcd207 100644
--- a/src/tint/resolver/resolver_validation.cc
+++ b/src/tint/resolver/resolver_validation.cc
@@ -2133,7 +2133,7 @@
   }
 
   for (auto* attr : str->Declaration()->attributes) {
-    if (!(attr->IsAnyOf<ast::StructBlockAttribute, ast::InternalAttribute>())) {
+    if (!(attr->IsAnyOf<ast::InternalAttribute>())) {
       AddError("attribute is not valid for struct declarations", attr->source);
       return false;
     }
diff --git a/src/tint/resolver/storage_class_layout_validation_test.cc b/src/tint/resolver/storage_class_layout_validation_test.cc
index 406fdb8..103948c 100644
--- a/src/tint/resolver/storage_class_layout_validation_test.cc
+++ b/src/tint/resolver/storage_class_layout_validation_test.cc
@@ -26,7 +26,6 @@
 // Detect unaligned member for storage buffers
 TEST_F(ResolverStorageClassLayoutValidationTest,
        StorageBuffer_UnalignedMember) {
-  // [[block]]
   // struct S {
   //     @size(5) a : f32;
   //     @align(1) b : f32;
@@ -36,8 +35,7 @@
 
   Structure(Source{{12, 34}}, "S",
             {Member("a", ty.f32(), {MemberSize(5)}),
-             Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})},
-            {StructBlock()});
+             Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})});
 
   Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
          GroupAndBinding(0, 0));
@@ -57,7 +55,6 @@
 
 TEST_F(ResolverStorageClassLayoutValidationTest,
        StorageBuffer_UnalignedMember_SuggestedFix) {
-  // [[block]]
   // struct S {
   //     @size(5) a : f32;
   //     @align(4) b : f32;
@@ -67,8 +64,7 @@
 
   Structure(Source{{12, 34}}, "S",
             {Member("a", ty.f32(), {MemberSize(5)}),
-             Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})},
-            {StructBlock()});
+             Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})});
 
   Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
          GroupAndBinding(0, 0));
@@ -83,7 +79,6 @@
   //   scalar : i32;
   // };
   //
-  // [[block]]
   // struct Outer {
   //   scalar : f32;
   //   inner : Inner;
@@ -98,8 +93,7 @@
             {
                 Member("scalar", ty.f32()),
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -126,7 +120,6 @@
   //   scalar : i32;
   // };
   //
-  // [[block]]
   // struct Outer {
   //   scalar : f32;
   //   @align(16) inner : Inner;
@@ -142,8 +135,7 @@
                 Member("scalar", ty.f32()),
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner"),
                        {MemberAlign(16)}),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -156,7 +148,6 @@
        UniformBuffer_UnalignedMember_Array) {
   // type Inner = @stride(16) array<f32, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   scalar : f32;
   //   inner : Inner;
@@ -170,8 +161,7 @@
             {
                 Member("scalar", ty.f32()),
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -192,7 +182,6 @@
        UniformBuffer_UnalignedMember_Array_SuggestedFix) {
   // type Inner = @stride(16) array<f32, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   scalar : f32;
   //   @align(16) inner : Inner;
@@ -207,8 +196,7 @@
                 Member("scalar", ty.f32()),
                 Member(Source{{34, 56}}, "inner", ty.type_name("Inner"),
                        {MemberAlign(16)}),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -224,7 +212,6 @@
   //   @align(1) @size(5) scalar : i32;
   // };
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -240,8 +227,7 @@
             {
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
                 Member(Source{{78, 90}}, "scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{22, 24}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -273,7 +259,6 @@
   //   @align(1) @size(5) scalar : i32;
   // };
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -294,8 +279,7 @@
             {
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
                 Member(Source{{78, 90}}, "scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{22, 24}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -326,7 +310,6 @@
   //   @align(1) @size(5) scalar : i32;
   // };
   //
-  // [[block]]
   // struct Outer {
   //   @align(16) inner : Inner;
   //   scalar : i32;
@@ -342,8 +325,7 @@
             {
                 Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
                 Member(Source{{78, 90}}, "scalar", ty.i32(), {MemberAlign(16)}),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{22, 34}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -355,7 +337,6 @@
 // size is 12. 's' should be at offset 12, which is okay here.
 TEST_F(ResolverStorageClassLayoutValidationTest,
        UniformBuffer_Vec3MemberOffset_NoFail) {
-  // [[block]]
   // struct ScalarPackedAtEndOfVec3 {
   //     v : vec3<f32>;
   //     s : f32;
@@ -363,12 +344,10 @@
   // @group(0) @binding(0)
   // var<uniform> a : ScalarPackedAtEndOfVec3;
 
-  Structure("ScalarPackedAtEndOfVec3",
-            {
-                Member("v", ty.vec3(ty.f32())),
-                Member("s", ty.f32()),
-            },
-            {StructBlock()});
+  Structure("ScalarPackedAtEndOfVec3", {
+                                           Member("v", ty.vec3(ty.f32())),
+                                           Member("s", ty.f32()),
+                                       });
 
   Global(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -381,7 +360,6 @@
        UniformBuffer_InvalidArrayStride_Scalar) {
   // type Inner = array<f32, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -396,8 +374,7 @@
             {
                 Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                 Member("scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -418,7 +395,6 @@
        UniformBuffer_InvalidArrayStride_Vector) {
   // type Inner = array<vec2<f32>, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -433,8 +409,7 @@
             {
                 Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                 Member("scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -460,7 +435,6 @@
   // }
   // type Inner = array<ArrayElem, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -479,8 +453,7 @@
             {
                 Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                 Member("scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -523,8 +496,7 @@
       Source{{12, 34}}, "Outer",
       {
           Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4), 4)),
-      },
-      {StructBlock()});
+      });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
@@ -544,7 +516,6 @@
        UniformBuffer_InvalidArrayStride_SuggestedFix) {
   // type Inner = @stride(16) array<f32, 10>;
   //
-  // [[block]]
   // struct Outer {
   //   inner : Inner;
   //   scalar : i32;
@@ -559,8 +530,7 @@
             {
                 Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                 Member("scalar", ty.i32()),
-            },
-            {StructBlock()});
+            });
 
   Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
          ast::StorageClass::kUniform, GroupAndBinding(0, 0));
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index 0c33b8b..2afab8c 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "gmock/gmock.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/struct.h"
 
@@ -57,7 +56,7 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArrayInStruct) {
-  auto* s = Structure("S", {Member("m", ty.array(ty.i32()))}, {StructBlock()});
+  auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
   Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
 
   EXPECT_FALSE(r()->Resolve());
@@ -80,7 +79,7 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArrayInStruct) {
-  auto* s = Structure("S", {Member("m", ty.array(ty.i32()))}, {StructBlock()});
+  auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
   Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
 
   EXPECT_FALSE(r()->Resolve());
@@ -192,10 +191,9 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
-  // [[block]] struct S { x : i32 };
+  // struct S { x : i32 };
   // var<storage, read> g : S;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kRead,
          ast::AttributeList{
@@ -207,11 +205,10 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
-  // [[block]] struct S { x : i32 };
+  // struct S { x : i32 };
   // type a1 = S;
   // var<storage, read> g : a1;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
   auto* a1 = Alias("a1", ty.Of(s));
   auto* a2 = Alias("a2", ty.Of(a1));
   Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage,
@@ -225,11 +222,10 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBuffer_Struct_Runtime) {
-  // [[block]] struct S { m:  array<f32>; };
+  // struct S { m:  array<f32>; };
   // @group(0) @binding(0) var<uniform, > svar : S;
 
-  auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())});
 
   Global(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
@@ -336,10 +332,9 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
-  // [[block]] struct S { x : i32 };
+  // struct S { x : i32 };
   // var<uniform> g :  S;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
   Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
@@ -350,11 +345,10 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
-  // [[block]] struct S { x : i32 };
+  // struct S { x : i32 };
   // type a1 = S;
   // var<uniform> g : a1;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
   auto* a1 = Alias("a1", ty.Of(s));
   Global(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
          ast::AttributeList{
diff --git a/src/tint/resolver/struct_layout_test.cc b/src/tint/resolver/struct_layout_test.cc
index f8e76fd..e2b017f 100644
--- a/src/tint/resolver/struct_layout_test.cc
+++ b/src/tint/resolver/struct_layout_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "gmock/gmock.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/struct.h"
 
@@ -129,11 +128,9 @@
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
-  auto* s = Structure("S",
-                      {
-                          Member("c", ty.array<f32>()),
-                      },
-                      ast::AttributeList{create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("c", ty.array<f32>()),
+                           });
 
   ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -149,11 +146,9 @@
 }
 
 TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
-  auto* s = Structure("S",
-                      {
-                          Member("c", ty.array<f32>(/*stride*/ 32)),
-                      },
-                      ast::AttributeList{create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("c", ty.array<f32>(/*stride*/ 32)),
+                           });
 
   ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/struct_storage_class_use_test.cc b/src/tint/resolver/struct_storage_class_use_test.cc
index 2c0e9cf..55a1330 100644
--- a/src/tint/resolver/struct_storage_class_use_test.cc
+++ b/src/tint/resolver/struct_storage_class_use_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "gmock/gmock.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/struct.h"
 
@@ -168,8 +167,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
-  auto* s = Structure("S", {Member("a", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.f32())});
   Global("x", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 1d41858..179f064 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -15,7 +15,6 @@
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/multisampled_texture_type.h"
@@ -481,18 +480,15 @@
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLast_Pass) {
-  // [[block]]
   // struct Foo {
   //   vf: f32;
   //   rt: array<f32>;
   // };
 
-  Structure("Foo",
-            {
-                Member("vf", ty.f32()),
-                Member("rt", ty.array<f32>()),
-            },
-            {create<ast::StructBlockAttribute>()});
+  Structure("Foo", {
+                       Member("vf", ty.f32()),
+                       Member("rt", ty.array<f32>()),
+                   });
 
   WrapInFunction();
 
@@ -547,18 +543,15 @@
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {
-  // [[block]]
   // struct Foo {
   //   rt: array<f32>;
   //   vf: f32;
   // };
 
-  Structure("Foo",
-            {
-                Member(Source{{12, 34}}, "rt", ty.array<f32>()),
-                Member("vf", ty.f32()),
-            },
-            {create<ast::StructBlockAttribute>()});
+  Structure("Foo", {
+                       Member(Source{{12, 34}}, "rt", ty.array<f32>()),
+                       Member("vf", ty.f32()),
+                   });
 
   WrapInFunction();
 
@@ -639,7 +632,6 @@
 }
 
 TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsNotLast_Fail) {
-  // [[block]]
   // type RTArr = array<u32>;
   // struct s {
   //  b: RTArr;
@@ -647,12 +639,10 @@
   //}
 
   auto* alias = Alias("RTArr", ty.array<u32>());
-  Structure("s",
-            {
-                Member(Source{{12, 34}}, "b", ty.Of(alias)),
-                Member("a", ty.u32()),
-            },
-            {create<ast::StructBlockAttribute>()});
+  Structure("s", {
+                     Member(Source{{12, 34}}, "b", ty.Of(alias)),
+                     Member("a", ty.u32()),
+                 });
 
   WrapInFunction();
 
@@ -663,7 +653,6 @@
 }
 
 TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsLast_Pass) {
-  // [[block]]
   // type RTArr = array<u32>;
   // struct s {
   //  a: u32;
@@ -671,12 +660,10 @@
   //}
 
   auto* alias = Alias("RTArr", ty.array<u32>());
-  Structure("s",
-            {
-                Member("a", ty.u32()),
-                Member("b", ty.Of(alias)),
-            },
-            {create<ast::StructBlockAttribute>()});
+  Structure("s", {
+                     Member("a", ty.u32()),
+                     Member("b", ty.Of(alias)),
+                 });
 
   WrapInFunction();
 
diff --git a/src/tint/resolver/var_let_test.cc b/src/tint/resolver/var_let_test.cc
index 79f2f0a..0d47712 100644
--- a/src/tint/resolver/var_let_test.cc
+++ b/src/tint/resolver/var_let_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/reference_type.h"
@@ -216,8 +215,7 @@
 TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
   // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-  auto* buf = Structure("S", {Member("m", ty.i32())},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("m", ty.i32())});
   auto* function = Var("f", ty.i32());
   auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
   auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
@@ -264,8 +262,7 @@
 TEST_F(ResolverVarLetTest, ExplicitVarStorageClass) {
   // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-  auto* buf = Structure("S", {Member("m", ty.i32())},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("m", ty.i32())});
   auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
                          ast::Access::kReadWrite,
                          ast::AttributeList{
@@ -285,7 +282,7 @@
   // struct Inner {
   //    arr: array<i32, 4>;
   // }
-  // [[block]] struct S {
+  // struct S {
   //    inner: Inner;
   // }
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -293,8 +290,7 @@
   //   let p = &s.inner.arr[2];
   // }
   auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-  auto* buf = Structure("S", {Member("inner", ty.Of(inner))},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
   auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
                          ast::Access::kReadWrite,
                          ast::AttributeList{
diff --git a/src/tint/resolver/var_let_validation_test.cc b/src/tint/resolver/var_let_validation_test.cc
index fbb570e..2d26988 100644
--- a/src/tint/resolver/var_let_validation_test.cc
+++ b/src/tint/resolver/var_let_validation_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 
@@ -220,7 +219,7 @@
   // struct Inner {
   //    arr: array<i32, 4>;
   // }
-  // [[block]] struct S {
+  // struct S {
   //    inner: Inner;
   // }
   // @group(0) @binding(0) var<storage> s : S;
@@ -228,8 +227,7 @@
   //   let p : pointer<storage, i32, read_write> = &s.inner.arr[2];
   // }
   auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-  auto* buf = Structure("S", {Member("inner", ty.Of(inner))},
-                        {create<ast::StructBlockAttribute>()});
+  auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
   auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
                          ast::AttributeList{
                              create<ast::BindingAttribute>(0),
@@ -262,8 +260,7 @@
 }
 
 TEST_F(ResolverVarLetValidationTest, NonConstructibleType_RuntimeArray) {
-  auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))},
-                      {StructBlock()});
+  auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
   auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
   WrapInFunction(v);
 
diff --git a/src/tint/transform/localize_struct_array_assignment_test.cc b/src/tint/transform/localize_struct_array_assignment_test.cc
index 349858d..e16304a 100644
--- a/src/tint/transform/localize_struct_array_assignment_test.cc
+++ b/src/tint/transform/localize_struct_array_assignment_test.cc
@@ -34,7 +34,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, StructArray) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 
@@ -57,7 +57,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
 }
@@ -109,7 +108,7 @@
   v : i32;
 };
 
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 )";
@@ -137,7 +136,6 @@
   v : i32;
 }
 
-@block
 struct Uniforms {
   i : u32;
 }
@@ -150,7 +148,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 
@@ -177,7 +175,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
 }
@@ -237,7 +234,7 @@
   v : i32;
 };
 
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 )";
@@ -269,7 +266,6 @@
   v : i32;
 }
 
-@block
 struct Uniforms {
   i : u32;
 }
@@ -282,7 +278,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, StructArrayArray) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
   j : u32;
 };
@@ -306,7 +302,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
   j : u32;
@@ -342,7 +337,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, StructArrayStruct) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 
@@ -369,7 +364,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
 }
@@ -408,7 +402,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, StructArrayStructArray) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
   j : u32;
 };
@@ -436,7 +430,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
   j : u32;
@@ -479,7 +472,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, IndexingWithSideEffectFunc) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
   j : u32;
 };
@@ -513,7 +506,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
   j : u32;
@@ -573,7 +565,7 @@
 
 @group(1) @binding(4) var<uniform> uniforms : Uniforms;
 
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
   j : u32;
 };
@@ -615,7 +607,6 @@
 
 @group(1) @binding(4) var<uniform> uniforms : Uniforms;
 
-@block
 struct Uniforms {
   i : u32;
   j : u32;
@@ -648,7 +639,7 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg) {
   auto* src = R"(
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 struct InnerS {
@@ -672,7 +663,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
 }
@@ -731,7 +721,7 @@
 
 @group(1) @binding(4) var<uniform> uniforms : Uniforms;
 
-@block struct Uniforms {
+struct Uniforms {
   i : u32;
 };
 )";
@@ -763,7 +753,6 @@
 
 @group(1) @binding(4) var<uniform> uniforms : Uniforms;
 
-@block
 struct Uniforms {
   i : u32;
 }
@@ -776,7 +765,6 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerVar) {
   auto* src = R"(
-@block
 struct Uniforms {
   i : u32;
 };
@@ -805,7 +793,6 @@
 )";
 
   auto* expect = R"(
-@block
 struct Uniforms {
   i : u32;
 }
@@ -845,12 +832,10 @@
 
 TEST_F(LocalizeStructArrayAssignmentTest, VectorAssignment) {
   auto* src = R"(
-@block
 struct Uniforms {
   i : u32;
 }
 
-@block
 struct OuterS {
   a1 : array<u32, 8>;
 }
diff --git a/src/tint/transform/transform_test.cc b/src/tint/transform/transform_test.cc
index d166529..318778d 100644
--- a/src/tint/transform/transform_test.cc
+++ b/src/tint/transform/transform_test.cc
@@ -108,7 +108,7 @@
 
 TEST_F(CreateASTTypeForTest, Struct) {
   auto* str = create([](ProgramBuilder& b) {
-    auto* decl = b.Structure("S", {}, {});
+    auto* decl = b.Structure("S", {});
     return b.create<sem::Struct>(decl, decl->name, sem::StructMemberList{},
                                  4u /* align */, 4u /* size */,
                                  4u /* size_no_padding */);
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index c2edb1d..6a42143 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -14,7 +14,6 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/writer/glsl/test_helper.h"
@@ -329,8 +328,7 @@
 #endif
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
-  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())},
-                           {create<ast::StructBlockAttribute>()});
+  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
   auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
                      ast::AttributeList{
                          create<ast::BindingAttribute>(0),
@@ -385,8 +383,7 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_UniformStruct) {
-  auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
 
   Global("uniforms", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
@@ -429,12 +426,10 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_RW_StorageBuffer_Read) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -484,12 +479,10 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_RO_StorageBuffer_Read) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -539,12 +532,10 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_WO_StorageBuffer_Store) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
          ast::AttributeList{
@@ -590,12 +581,10 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_StorageBuffer_Store) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -642,8 +631,7 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
-  auto* s = Structure("S", {Member("x", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.f32())});
   Global("coord", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
@@ -694,8 +682,7 @@
 
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
-  auto* s = Structure("S", {Member("x", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.f32())});
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
          ast::AttributeList{
@@ -912,7 +899,7 @@
 // https://crbug.com/tint/297
 TEST_F(GlslGeneratorImplTest_Function,
        Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
-  // [[block]] struct Data {
+  // struct Data {
   //   d : f32;
   // };
   // @binding(0) @group(0) var<storage> data : Data;
@@ -929,8 +916,7 @@
   //   return;
   // }
 
-  auto* s = Structure("Data", {Member("d", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {Member("d", ty.f32())});
 
   Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index b15343d..4bf817f 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -14,7 +14,6 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/writer/glsl/test_helper.h"
 
 namespace tint {
@@ -95,8 +94,7 @@
   void SetupStorageBuffer(ast::StructMemberList members) {
     ProgramBuilder& b = *this;
 
-    auto* s =
-        b.Structure("Data", members, {b.create<ast::StructBlockAttribute>()});
+    auto* s = b.Structure("Data", members);
 
     b.Global("data", b.ty.Of(s), ast::StorageClass::kStorage,
              ast::Access::kReadWrite,
diff --git a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
index 2d353ab..6447d2c 100644
--- a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/glsl/test_helper.h"
 
@@ -26,8 +25,7 @@
 using GlslSanitizerTest = TestHelper;
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -67,12 +65,10 @@
 }
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
-  auto* s = Structure("my_struct",
-                      {
-                          Member(0, "z", ty.f32()),
-                          Member(4, "a", ty.array<f32>(4)),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {
+                                       Member(0, "z", ty.f32()),
+                                       Member(4, "a", ty.array<f32>(4)),
+                                   });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -114,8 +110,7 @@
 }
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength_ViaLets) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index bc08fd2..1d29857 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -15,7 +15,6 @@
 #include "gmock/gmock.h"
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/sem/multisampled_texture_type.h"
 #include "src/tint/sem/sampled_texture_type.h"
@@ -189,12 +188,10 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Struct_WithOffsetAttributes) {
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32(), {MemberOffset(0)}),
-                          Member("b", ty.f32(), {MemberOffset(8)}),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32(), {MemberOffset(0)}),
+                               Member("b", ty.f32(), {MemberOffset(8)}),
+                           });
   Global("g", ty.Of(s), ast::StorageClass::kPrivate);
 
   GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index ca5aba7..c3306ef 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -14,7 +14,6 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/writer/hlsl/test_helper.h"
@@ -337,8 +336,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
-  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())},
-                           {create<ast::StructBlockAttribute>()});
+  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
   auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
                      ast::AttributeList{
                          create<ast::BindingAttribute>(0),
@@ -386,8 +384,7 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_UniformStruct) {
-  auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
 
   Global("uniforms", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
@@ -423,12 +420,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_RW_StorageBuffer_Read) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -464,12 +459,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_RO_StorageBuffer_Read) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -504,12 +497,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_WO_StorageBuffer_Store) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
          ast::AttributeList{
@@ -541,12 +532,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_EntryPoint_With_StorageBuffer_Store) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -579,8 +568,7 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
-  auto* s = Structure("S", {Member("x", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.f32())});
   Global("coord", ty.Of(s), ast::StorageClass::kUniform,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
@@ -624,8 +612,7 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
-  auto* s = Structure("S", {Member("x", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("x", ty.f32())});
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
          ast::AttributeList{
@@ -856,7 +843,7 @@
 // https://crbug.com/tint/297
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
-  // [[block]] struct Data {
+  // struct Data {
   //   d : f32;
   // };
   // @binding(0) @group(0) var<storage> data : Data;
@@ -873,8 +860,7 @@
   //   return;
   // }
 
-  auto* s = Structure("Data", {Member("d", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {Member("d", ty.f32())});
 
   Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index 6d11eb8..7942ba4 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -14,7 +14,6 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/writer/hlsl/test_helper.h"
 
 namespace tint {
@@ -95,8 +94,7 @@
   void SetupStorageBuffer(ast::StructMemberList members) {
     ProgramBuilder& b = *this;
 
-    auto* s =
-        b.Structure("Data", members, {b.create<ast::StructBlockAttribute>()});
+    auto* s = b.Structure("Data", members);
 
     b.Global("data", b.ty.Of(s), ast::StorageClass::kStorage,
              ast::Access::kReadWrite,
diff --git a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
index 9c5899a..3a01bb5 100644
--- a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/hlsl/test_helper.h"
 
@@ -26,8 +25,7 @@
 using HlslSanitizerTest = TestHelper;
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -62,12 +60,10 @@
 }
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
-  auto* s = Structure("my_struct",
-                      {
-                          Member(0, "z", ty.f32()),
-                          Member(4, "a", ty.array<f32>(4)),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {
+                                       Member(0, "z", ty.f32()),
+                                       Member(4, "a", ty.array<f32>(4)),
+                                   });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -103,8 +99,7 @@
 }
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ViaLets) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -145,8 +140,7 @@
 }
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
diff --git a/src/tint/writer/hlsl/generator_impl_type_test.cc b/src/tint/writer/hlsl/generator_impl_type_test.cc
index f79164d..ce179a9 100644
--- a/src/tint/writer/hlsl/generator_impl_type_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_type_test.cc
@@ -15,7 +15,6 @@
 #include "gmock/gmock.h"
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/sem/multisampled_texture_type.h"
 #include "src/tint/sem/sampled_texture_type.h"
@@ -154,12 +153,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl_OmittedIfStorageBuffer) {
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.f32()),
+                           });
   Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
@@ -207,12 +204,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_WithOffsetAttributes) {
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32(), {MemberOffset(0)}),
-                          Member("b", ty.f32(), {MemberOffset(8)}),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32(), {MemberOffset(0)}),
+                               Member("b", ty.f32(), {MemberOffset(8)}),
+                           });
   Global("g", ty.Of(s), ast::StorageClass::kPrivate);
 
   GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index bbf3546..12c8421 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/msl/test_helper.h"
 
@@ -329,12 +328,10 @@
 
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionAttribute_EntryPoint_With_RW_StorageBuffer) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -376,12 +373,10 @@
 
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionAttribute_EntryPoint_With_RO_StorageBuffer) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -421,8 +416,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
-  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())},
-                           {create<ast::StructBlockAttribute>()});
+  auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
   auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
                      ast::AttributeList{
                          create<ast::BindingAttribute>(0),
@@ -474,12 +468,10 @@
 
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionAttribute_Called_By_EntryPoint_With_RW_StorageBuffer) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage,
          ast::Access::kReadWrite,
@@ -532,12 +524,10 @@
 
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionAttribute_Called_By_EntryPoint_With_RO_StorageBuffer) {
-  auto* s = Structure("Data",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {
+                                  Member("a", ty.i32()),
+                                  Member("b", ty.f32()),
+                              });
 
   Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -644,7 +634,7 @@
 // https://crbug.com/tint/297
 TEST_F(MslGeneratorImplTest,
        Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
-  // [[block]] struct Data {
+  // struct Data {
   //   d : f32;
   // };
   // @binding(0) @group(0) var<storage> data : Data;
@@ -659,8 +649,7 @@
   //   return;
   // }
 
-  auto* s = Structure("Data", {Member("d", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {Member("d", ty.f32())});
 
   Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index 66cf16a..fe39d96 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -15,7 +15,6 @@
 #include "gmock/gmock.h"
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/msl/test_helper.h"
 
@@ -29,8 +28,7 @@
 using MslSanitizerTest = TestHelper;
 
 TEST_F(MslSanitizerTest, Call_ArrayLength) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -72,12 +70,10 @@
 }
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
-  auto* s = Structure("my_struct",
-                      {
-                          Member(0, "z", ty.f32()),
-                          Member(4, "a", ty.array<f32>(4)),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {
+                                       Member(0, "z", ty.f32()),
+                                       Member(4, "a", ty.array<f32>(4)),
+                                   });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -121,8 +117,7 @@
 }
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ViaLets) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -170,8 +165,7 @@
 }
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -227,8 +221,7 @@
 
 TEST_F(MslSanitizerTest,
        Call_ArrayLength_ArrayLengthFromUniformMissingBinding) {
-  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 57a9e59..08f49a2 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -16,7 +16,6 @@
 
 #include "gmock/gmock.h"
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_multisampled_texture_type.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/sem/multisampled_texture_type.h"
@@ -121,8 +120,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_ArrayWithStride) {
-  auto* s = Structure("s", {Member("arr", ty.array<f32, 4>(64))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("s", {Member("arr", ty.array<f32, 4>(64))});
   auto* ubo = Global("ubo", ty.Of(s), ast::StorageClass::kUniform,
                      ast::AttributeList{
                          create<ast::GroupAttribute>(0),
@@ -231,37 +229,35 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_NonComposites) {
-  auto* s =
-      Structure("S",
-                {
-                    Member("a", ty.i32(), {MemberSize(32)}),
-                    Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}),
-                    Member("c", ty.vec2<f32>()),
-                    Member("d", ty.u32()),
-                    Member("e", ty.vec3<f32>()),
-                    Member("f", ty.u32()),
-                    Member("g", ty.vec4<f32>()),
-                    Member("h", ty.u32()),
-                    Member("i", ty.mat2x2<f32>()),
-                    Member("j", ty.u32()),
-                    Member("k", ty.mat2x3<f32>()),
-                    Member("l", ty.u32()),
-                    Member("m", ty.mat2x4<f32>()),
-                    Member("n", ty.u32()),
-                    Member("o", ty.mat3x2<f32>()),
-                    Member("p", ty.u32()),
-                    Member("q", ty.mat3x3<f32>()),
-                    Member("r", ty.u32()),
-                    Member("s", ty.mat3x4<f32>()),
-                    Member("t", ty.u32()),
-                    Member("u", ty.mat4x2<f32>()),
-                    Member("v", ty.u32()),
-                    Member("w", ty.mat4x3<f32>()),
-                    Member("x", ty.u32()),
-                    Member("y", ty.mat4x4<f32>()),
-                    Member("z", ty.f32()),
-                },
-                {create<ast::StructBlockAttribute>()});
+  auto* s = Structure(
+      "S", {
+               Member("a", ty.i32(), {MemberSize(32)}),
+               Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}),
+               Member("c", ty.vec2<f32>()),
+               Member("d", ty.u32()),
+               Member("e", ty.vec3<f32>()),
+               Member("f", ty.u32()),
+               Member("g", ty.vec4<f32>()),
+               Member("h", ty.u32()),
+               Member("i", ty.mat2x2<f32>()),
+               Member("j", ty.u32()),
+               Member("k", ty.mat2x3<f32>()),
+               Member("l", ty.u32()),
+               Member("m", ty.mat2x4<f32>()),
+               Member("n", ty.u32()),
+               Member("o", ty.mat3x2<f32>()),
+               Member("p", ty.u32()),
+               Member("q", ty.mat3x3<f32>()),
+               Member("r", ty.u32()),
+               Member("s", ty.mat3x4<f32>()),
+               Member("t", ty.u32()),
+               Member("u", ty.mat4x2<f32>()),
+               Member("v", ty.u32()),
+               Member("w", ty.mat4x3<f32>()),
+               Member("x", ty.u32()),
+               Member("y", ty.mat4x4<f32>()),
+               Member("z", ty.f32()),
+           });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -363,15 +359,13 @@
                                Member("b", ty.f32()),
                            });
 
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.Of(inner_x)),
-                          Member("c", ty.f32()),
-                          Member("d", ty.Of(inner_y)),
-                          Member("e", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.Of(inner_x)),
+                               Member("c", ty.f32()),
+                               Member("d", ty.Of(inner_y)),
+                               Member("e", ty.f32()),
+                           });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -456,16 +450,14 @@
   // array_z: size(4), align(4)
   auto* array_z = ty.array<f32>();
 
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", array_x),
-                          Member("c", ty.f32()),
-                          Member("d", array_y),
-                          Member("e", ty.f32()),
-                          Member("f", array_z),
-                      },
-                      ast::AttributeList{create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", array_x),
+                               Member("c", ty.f32()),
+                               Member("d", array_y),
+                               Member("e", ty.f32()),
+                               Member("f", array_z),
+                           });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -544,13 +536,11 @@
   // array: size(64), align(16)
   auto* array = ty.array(ty.vec3<f32>(), 4);
 
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", array),
-                          Member("c", ty.i32()),
-                      },
-                      ast::AttributeList{create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", array),
+                               Member("c", ty.i32()),
+                           });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -612,8 +602,7 @@
           Member("tint_pad_28", ty.u32()),
           Member("tint_pad_4", ty.mat4x4<f32>()),
           Member("tint_pad_21", ty.f32()),
-      },
-      {create<ast::StructBlockAttribute>()});
+      });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -671,12 +660,10 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Struct_WithAttribute) {
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.f32()),
+                           });
 
   Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index bcd7596..b7f6a6a 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/utils/string.h"
 #include "src/tint/writer/spirv/spv_dump.h"
@@ -1482,8 +1481,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength) {
-  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1527,12 +1525,10 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength_OtherMembersInStruct) {
-  auto* s = Structure("my_struct",
-                      {
-                          Member("z", ty.f32()),
-                          Member(4, "a", ty.array<f32>(4)),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {
+                                       Member("z", ty.f32()),
+                                       Member(4, "a", ty.array<f32>(4)),
+                                   });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1576,8 +1572,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength_ViaLets) {
-  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1626,7 +1621,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength_ViaLets_WithPtrNoise) {
-  // [[block]] struct my_struct {
+  // struct my_struct {
   //   a : @stride(4) array<f32>;
   // };
   // @binding(1) @group(2) var<storage, read> b : my_struct;
@@ -1637,8 +1632,7 @@
   //   let p3 = &((*p).a);
   //   arrayLength(&*p3);
   // }
-  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1689,7 +1683,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_AtomicLoad) {
-  // [[block]] struct S {
+  // struct S {
   //   u : atomic<u32>;
   //   i : atomic<i32>;
   // }
@@ -1700,12 +1694,10 @@
   //   let u : u32 = atomicLoad(&b.u);
   //   let i : i32 = atomicLoad(&b.i);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("u", ty.atomic<u32>()),
-                          Member("i", ty.atomic<i32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("u", ty.atomic<u32>()),
+                               Member("i", ty.atomic<i32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1755,7 +1747,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_AtomicStore) {
-  // [[block]] struct S {
+  // struct S {
   //   u : atomic<u32>;
   //   i : atomic<i32>;
   // }
@@ -1768,12 +1760,10 @@
   //   atomicStore(&b.u, u);
   //   atomicStore(&b.i, i);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("u", ty.atomic<u32>()),
-                          Member("i", ty.atomic<i32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("u", ty.atomic<u32>()),
+                               Member("i", ty.atomic<i32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1835,7 +1825,7 @@
 
 using Builtin_Builtin_AtomicRMW_i32 = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(Builtin_Builtin_AtomicRMW_i32, Test) {
-  // [[block]] struct S {
+  // struct S {
   //   v : atomic<i32>;
   // }
   //
@@ -1845,11 +1835,9 @@
   //   var v = 10;
   //   let x : i32 = atomicOP(&b.v, v);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("v", ty.atomic<i32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("v", ty.atomic<i32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1912,7 +1900,7 @@
 
 using Builtin_Builtin_AtomicRMW_u32 = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(Builtin_Builtin_AtomicRMW_u32, Test) {
-  // [[block]] struct S {
+  // struct S {
   //   v : atomic<u32>;
   // }
   //
@@ -1922,11 +1910,9 @@
   //   var v = 10u;
   //   let x : u32 = atomicOP(&b.v, v);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("v", ty.atomic<u32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("v", ty.atomic<u32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -1987,7 +1973,7 @@
                     BuiltinData{"atomicXor", "OpAtomicXor"}));
 
 TEST_F(BuiltinBuilderTest, Call_AtomicExchange) {
-  // [[block]] struct S {
+  // struct S {
   //   u : atomic<u32>;
   //   i : atomic<i32>;
   // }
@@ -2000,12 +1986,10 @@
   //   let r : u32 = atomicExchange(&b.u, u);
   //   let s : i32 = atomicExchange(&b.i, i);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("u", ty.atomic<u32>()),
-                          Member("i", ty.atomic<i32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("u", ty.atomic<u32>()),
+                               Member("i", ty.atomic<i32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
@@ -2069,7 +2053,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_AtomicCompareExchangeWeak) {
-  // [[block]] struct S {
+  // struct S {
   //   u : atomic<u32>;
   //   i : atomic<i32>;
   // }
@@ -2080,12 +2064,10 @@
   //   let u : vec2<u32> = atomicCompareExchangeWeak(&b.u, 10u);
   //   let i : vec2<i32> = atomicCompareExchangeWeak(&b.i, 10);
   // }
-  auto* s = Structure("S",
-                      {
-                          Member("u", ty.atomic<u32>()),
-                          Member("i", ty.atomic<i32>()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("u", ty.atomic<u32>()),
+                               Member("i", ty.atomic<i32>()),
+                           });
   Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
              create<ast::BindingAttribute>(1),
diff --git a/src/tint/writer/spirv/builder_function_test.cc b/src/tint/writer/spirv/builder_function_test.cc
index ec94bda..b318563 100644
--- a/src/tint/writer/spirv/builder_function_test.cc
+++ b/src/tint/writer/spirv/builder_function_test.cc
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/writer/spirv/spv_dump.h"
 #include "src/tint/writer/spirv/test_helper.h"
 
@@ -186,7 +185,7 @@
 
 // https://crbug.com/tint/297
 TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
-  // [[block]] struct Data {
+  // struct Data {
   //   d : f32;
   // };
   // @binding(0) @group(0) var<storage> data : Data;
@@ -201,8 +200,7 @@
   //   return;
   // }
 
-  auto* s = Structure("Data", {Member("d", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {Member("d", ty.f32())});
 
   Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index d2b6eca..b7234fd 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/writer/spirv/spv_dump.h"
 #include "src/tint/writer/spirv/test_helper.h"
 
@@ -377,12 +376,10 @@
   // };
   // var b<storage, read> : A
 
-  auto* A = Structure("A",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.i32()),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* A = Structure("A", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.i32()),
+                           });
 
   Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -423,8 +420,7 @@
   // type B = A;
   // var b<storage, read> : B
 
-  auto* A = Structure("A", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* A = Structure("A", {Member("a", ty.i32())});
   auto* B = Alias("B", ty.Of(A));
   Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -463,8 +459,7 @@
   // type B = A;
   // var<storage, read> b : B
 
-  auto* A = Structure("A", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* A = Structure("A", {Member("a", ty.i32())});
   auto* B = Alias("B", ty.Of(A));
   Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
@@ -503,8 +498,7 @@
   // var<storage, read> b : A
   // var<storage, read_write> c : A
 
-  auto* A = Structure("A", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* A = Structure("A", {Member("a", ty.i32())});
   Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::GroupAttribute>(0),
@@ -629,12 +623,10 @@
   auto* type_array = ty.array<f32, 16>();
   auto* var_array = Global("b", type_array, ast::StorageClass::kWorkgroup);
 
-  auto* type_struct = Structure("C",
-                                {
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.i32()),
-                                },
-                                {create<ast::StructBlockAttribute>()});
+  auto* type_struct = Structure("C", {
+                                         Member("a", ty.i32()),
+                                         Member("b", ty.i32()),
+                                     });
   auto* var_struct =
       Global("c", ty.Of(type_struct), ast::StorageClass::kWorkgroup);
 
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index e7ccd5d..3a8941c 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/sem/multisampled_texture_type.h"
 #include "src/tint/sem/sampled_texture_type.h"
@@ -28,8 +27,7 @@
 
 TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
   auto* ary = ty.array(ty.i32());
-  auto* str =
-      Structure("S", {Member("x", ary)}, {create<ast::StructBlockAttribute>()});
+  auto* str = Structure("S", {Member("x", ary)});
   Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
@@ -49,8 +47,7 @@
 
 TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
   auto* ary = ty.array(ty.i32());
-  auto* str =
-      Structure("S", {Member("x", ary)}, {create<ast::StructBlockAttribute>()});
+  auto* str = Structure("S", {Member("x", ary)});
   Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
          ast::AttributeList{
              create<ast::BindingAttribute>(0),
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 05a39ff..267cef7 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -41,7 +41,6 @@
 #include "src/tint/ast/stage_attribute.h"
 #include "src/tint/ast/storage_texture.h"
 #include "src/tint/ast/stride_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/struct_member_align_attribute.h"
 #include "src/tint/ast/struct_member_offset_attribute.h"
 #include "src/tint/ast/struct_member_size_attribute.h"
@@ -713,10 +712,6 @@
           out << ")";
           return true;
         },
-        [&](const ast::StructBlockAttribute*) {  //
-          out << "block";
-          return true;
-        },
         [&](const ast::StageAttribute* stage) {
           out << "stage(" << stage->stage << ")";
           return true;
diff --git a/src/tint/writer/wgsl/generator_impl_function_test.cc b/src/tint/writer/wgsl/generator_impl_function_test.cc
index ebdfcfe..b60e257 100644
--- a/src/tint/writer/wgsl/generator_impl_function_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_function_test.cc
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/writer/wgsl/test_helper.h"
@@ -153,7 +152,7 @@
 // https://crbug.com/tint/297
 TEST_F(WgslGeneratorImplTest,
        Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
-  // [[block]] struct Data {
+  // struct Data {
   //   d : f32;
   // };
   // @binding(0) @group(0) var<storage> data : Data;
@@ -168,8 +167,7 @@
   //   return;
   // }
 
-  auto* s = Structure("Data", {Member("d", ty.f32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("Data", {Member("d", ty.f32())});
 
   Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::AttributeList{
@@ -210,8 +208,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
-  EXPECT_EQ(gen.result(), R"(@block
-struct Data {
+  EXPECT_EQ(gen.result(), R"(struct Data {
   d : f32;
 }
 
diff --git a/src/tint/writer/wgsl/generator_impl_type_test.cc b/src/tint/writer/wgsl/generator_impl_type_test.cc
index 28f9a2e..e42c0dd 100644
--- a/src/tint/writer/wgsl/generator_impl_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_type_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/sem/depth_texture_type.h"
 #include "src/tint/sem/multisampled_texture_type.h"
 #include "src/tint/sem/sampled_texture_type.h"
@@ -230,18 +229,15 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithAttribute) {
-  auto* s = Structure("S",
-                      {
-                          Member("a", ty.i32()),
-                          Member("b", ty.f32(), {MemberAlign(8)}),
-                      },
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.f32(), {MemberAlign(8)}),
+                           });
 
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
-  EXPECT_EQ(gen.result(), R"(@block
-struct S {
+  EXPECT_EQ(gen.result(), R"(struct S {
   a : i32;
   @align(8)
   b : f32;
@@ -250,21 +246,15 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithEntryPointAttributes) {
-  ast::AttributeList attrs;
-  attrs.push_back(create<ast::StructBlockAttribute>());
-
   auto* s = Structure(
-      "S",
-      ast::StructMemberList{
-          Member("a", ty.u32(), {Builtin(ast::Builtin::kVertexIndex)}),
-          Member("b", ty.f32(), {Location(2u)})},
-      attrs);
+      "S", ast::StructMemberList{
+               Member("a", ty.u32(), {Builtin(ast::Builtin::kVertexIndex)}),
+               Member("b", ty.f32(), {Location(2u)})});
 
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
-  EXPECT_EQ(gen.result(), R"(@block
-struct S {
+  EXPECT_EQ(gen.result(), R"(struct S {
   @builtin(vertex_index)
   a : u32;
   @location(2)
diff --git a/src/tint/writer/wgsl/generator_impl_variable_test.cc b/src/tint/writer/wgsl/generator_impl_variable_test.cc
index 37bf574..f9fe0bb 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_test.cc
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/ast/struct_block_attribute.h"
 #include "src/tint/writer/wgsl/test_helper.h"
 
 namespace tint {
@@ -43,8 +42,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Read) {
-  auto* s = Structure("S", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.i32())});
   auto* v =
       Global("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
              ast::AttributeList{
@@ -60,8 +58,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Write) {
-  auto* s = Structure("S", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.i32())});
   auto* v =
       Global("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
              ast::AttributeList{
@@ -77,8 +74,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_ReadWrite) {
-  auto* s = Structure("S", {Member("a", ty.i32())},
-                      {create<ast::StructBlockAttribute>()});
+  auto* s = Structure("S", {Member("a", ty.i32())});
   auto* v = Global("a", ty.Of(s), ast::StorageClass::kStorage,
                    ast::Access::kReadWrite,
                    ast::AttributeList{
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
index 54def39..302fb65 100644
--- a/test/tint/BUILD.gn
+++ b/test/tint/BUILD.gn
@@ -510,7 +510,6 @@
     "../../src/tint/reader/wgsl/parser_impl_storage_class_test.cc",
     "../../src/tint/reader/wgsl/parser_impl_storage_texture_type_test.cc",
     "../../src/tint/reader/wgsl/parser_impl_struct_attribute_decl_test.cc",
-    "../../src/tint/reader/wgsl/parser_impl_struct_attribute_test.cc",
     "../../src/tint/reader/wgsl/parser_impl_struct_body_decl_test.cc",
     "../../src/tint/reader/wgsl/parser_impl_struct_decl_test.cc",
     "../../src/tint/reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc",