tint: Add 'chromium_experimental_full_ptr_parameters' extension

Removes validation around pointer parameters.

Bug: tint:1758
Change-Id: I3adc48f780fc8c6f5525f9ecc280e2a406069b49
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112286
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc
index be16806..31a8083 100644
--- a/src/tint/ast/extension.cc
+++ b/src/tint/ast/extension.cc
@@ -34,6 +34,9 @@
     if (str == "chromium_experimental_dp4a") {
         return Extension::kChromiumExperimentalDp4A;
     }
+    if (str == "chromium_experimental_full_ptr_parameters") {
+        return Extension::kChromiumExperimentalFullPtrParameters;
+    }
     if (str == "chromium_experimental_push_constant") {
         return Extension::kChromiumExperimentalPushConstant;
     }
@@ -51,6 +54,8 @@
             return out << "chromium_disable_uniformity_analysis";
         case Extension::kChromiumExperimentalDp4A:
             return out << "chromium_experimental_dp4a";
+        case Extension::kChromiumExperimentalFullPtrParameters:
+            return out << "chromium_experimental_full_ptr_parameters";
         case Extension::kChromiumExperimentalPushConstant:
             return out << "chromium_experimental_push_constant";
         case Extension::kF16:
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
index f6fa03f..062e3fc 100644
--- a/src/tint/ast/extension.h
+++ b/src/tint/ast/extension.h
@@ -35,6 +35,7 @@
     kUndefined,
     kChromiumDisableUniformityAnalysis,
     kChromiumExperimentalDp4A,
+    kChromiumExperimentalFullPtrParameters,
     kChromiumExperimentalPushConstant,
     kF16,
 };
@@ -52,6 +53,7 @@
 constexpr const char* kExtensionStrings[] = {
     "chromium_disable_uniformity_analysis",
     "chromium_experimental_dp4a",
+    "chromium_experimental_full_ptr_parameters",
     "chromium_experimental_push_constant",
     "f16",
 };
diff --git a/src/tint/ast/extension_bench.cc b/src/tint/ast/extension_bench.cc
index 94b5216..5f175cc 100644
--- a/src/tint/ast/extension_bench.cc
+++ b/src/tint/ast/extension_bench.cc
@@ -45,20 +45,27 @@
         "chromium_exverimentiil_dp4a",
         "chro8ium_experimenWWal_dp4a",
         "chromiMm_eperimxxntal_dp4a",
-        "chrXmium_experimeggtal_ush_constant",
-        "chromiu_experVmentalpusX_constant",
-        "chro3ium_experimental_push_constant",
+        "chromium_expeggimeXtal_full_ptr_paraeters",
+        "chromium_expVrimental_full_ptr_puraXeer",
+        "chromium_experimental_full_ptr3parameters",
+        "chromium_experimental_full_ptr_parameters",
+        "chromium_experimentalEfull_ptr_parameters",
+        "chromium_experimentalfull_ptr_PPaTTameters",
+        "chromium_ddxperimental_fullptrxxparameters",
+        "c44romium_experimental_push_constant",
+        "chromium_experimental_pSSsVV_constant",
+        "chrom22Rm_experimental_pushRonstant",
         "chromium_experimental_push_constant",
-        "chromium_experEmental_push_constant",
-        "chPPomiumexperimental_push_conTTtant",
-        "chromixxm_experimentddl_push_constnt",
-        "4416",
-        "fSVV6",
-        "RR2",
+        "chromium_exp9rimFntal_ush_constant",
+        "chrmium_experimental_push_constant",
+        "cOOromium_experiVeHtal_puh_conRRtant",
+        "y1",
+        "l77rrn6",
+        "4016",
         "f16",
-        "96",
-        "f1",
-        "VOR6",
+        "5",
+        "u16",
+        "f",
     };
     for (auto _ : state) {
         for (auto& str : kStrings) {
diff --git a/src/tint/ast/extension_test.cc b/src/tint/ast/extension_test.cc
index 2879bf8..ea08dcc 100644
--- a/src/tint/ast/extension_test.cc
+++ b/src/tint/ast/extension_test.cc
@@ -44,6 +44,8 @@
 static constexpr Case kValidCases[] = {
     {"chromium_disable_uniformity_analysis", Extension::kChromiumDisableUniformityAnalysis},
     {"chromium_experimental_dp4a", Extension::kChromiumExperimentalDp4A},
+    {"chromium_experimental_full_ptr_parameters",
+     Extension::kChromiumExperimentalFullPtrParameters},
     {"chromium_experimental_push_constant", Extension::kChromiumExperimentalPushConstant},
     {"f16", Extension::kF16},
 };
@@ -55,12 +57,15 @@
     {"chro1ium_experimental_dp4a", Extension::kUndefined},
     {"chrJmium_experiqqetal_dp4a", Extension::kUndefined},
     {"chromium_experimenll77l_dp4a", Extension::kUndefined},
-    {"cppromium_experiHHenal_qqush_constant", Extension::kUndefined},
-    {"chromium_xpericental_sh_vonstant", Extension::kUndefined},
-    {"chromium_experimental_Gsh_cbnstant", Extension::kUndefined},
-    {"f1vi", Extension::kUndefined},
-    {"f8WW", Extension::kUndefined},
-    {"fxx", Extension::kUndefined},
+    {"chroium_experimental_full_ptr_paqqppmetHHrs", Extension::kUndefined},
+    {"chrium_evperiental_full_ptr_paraceters", Extension::kUndefined},
+    {"chromium_expGimental_fullbptr_parameters", Extension::kUndefined},
+    {"chvomium_experimental_push_constiint", Extension::kUndefined},
+    {"chromiu8WWexperimental_push_constant", Extension::kUndefined},
+    {"chromium_experiMental_push_costanxx", Extension::kUndefined},
+    {"fgg", Extension::kUndefined},
+    {"X", Extension::kUndefined},
+    {"316", Extension::kUndefined},
 };
 
 using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 6933e57..bcac5a2 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -51,6 +51,9 @@
   chromium_disable_uniformity_analysis
   // A Chromium-specific extension for push constants
   chromium_experimental_push_constant
+  // A Chromium-specific extension that enables passing of uniform, storage and workgroup 
+  // address-spaced pointers as parameters, as well as pointers into sub-objects.
+  chromium_experimental_full_ptr_parameters
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
diff --git a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
index aad8eb6..392f0dd 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -62,7 +62,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -75,7 +75,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension. Did you mean 'f16'?
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -123,7 +123,7 @@
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -134,7 +134,7 @@
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -145,7 +145,7 @@
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 82037af..e94e7b8 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -114,11 +114,29 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, PointerArgument_NotWholeVar) {
+TEST_F(ResolverCallValidationTest, PointerArgument_LetIdentExpr) {
+    // fn foo(p: ptr<function, i32>) {}
+    // fn main() {
+    //   let z: i32 = 1i;
+    //   foo(&z);
+    // }
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Let("z", ty.i32(), Expr(1_i))),
+             CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
+         });
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+}
+
+TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfFunctionMember) {
     // struct S { m: i32; };
     // fn foo(p: ptr<function, i32>) {}
     // fn main() {
-    //   var v: S;
+    //   var v : S;
     //   foo(&v.m);
     // }
     auto* S = Structure("S", utils::Vector{
@@ -138,6 +156,51 @@
               "originating variable");
 }
 
+TEST_F(ResolverCallValidationTest,
+       PointerArgument_AddressOfFunctionMember_WithFullPtrParametersExt) {
+    // enable chromium_experimental_full_ptr_parameters;
+    // struct S { m: i32; };
+    // fn foo(p: ptr<function, i32>) {}
+    // fn main() {
+    //   var v : S;
+    //   foo(&v.m);
+    // }
+    Enable(ast::Extension::kChromiumExperimentalFullPtrParameters);
+    auto* S = Structure("S", utils::Vector{
+                                 Member("m", ty.i32()),
+                             });
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.Of(S))),
+             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
+         });
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfLetMember) {
+    // struct S { m: i32; };
+    // fn foo(p: ptr<function, i32>) {}
+    // fn main() {
+    //   let v: S = S();
+    //   foo(&v.m);
+    // }
+    auto* S = Structure("S", utils::Vector{
+                                 Member("m", ty.i32()),
+                             });
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Let("v", ty.Of(S), Construct(ty.Of(S)))),
+             CallStmt(Call("foo", AddressOf(MemberAccessor(Source{{12, 34}}, "v", "m")))),
+         });
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+}
+
 TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam) {
     // fn foo(p: ptr<function, i32>) {}
     // fn bar(p: ptr<function, i32>) {
@@ -181,12 +244,12 @@
          },
          ty.void_(),
          utils::Vector{
-             CallStmt(Call("foo", Expr("p"))),
+             CallStmt(Call("foo", "p")),
          });
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
              Decl(Var("v", ty.i32(), Expr(1_i))),
-             CallStmt(Call("foo", AddressOf(Expr("v")))),
+             CallStmt(Call("foo", AddressOf("v"))),
          },
          utils::Vector{
              Stage(ast::PipelineStage::kFragment),
@@ -195,40 +258,15 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam_NotWholeVar) {
-    // fn foo(p: ptr<function, i32>) {}
-    // fn bar(p: ptr<function, array<i32, 4>>) {
-    //   foo(&(*p)[0]);
-    // }
-    Func("foo",
-         utils::Vector{
-             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
-         },
-         ty.void_(), utils::Empty);
-    Func("bar",
-         utils::Vector{
-             Param("p", ty.pointer(ty.array<i32, 4>(), ast::AddressSpace::kFunction)),
-         },
-         ty.void_(),
-         utils::Vector{
-             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, IndexAccessor(Deref("p"), 0_a)))),
-         });
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: arguments of pointer type must not point to a subset of the "
-              "originating variable");
-}
-
 TEST_F(ResolverCallValidationTest, LetPointer) {
     // fn foo(p : ptr<function, i32>) {}
     // @fragment
     // fn main() {
     //   var v: i32;
     //   let p: ptr<function, i32> = &v;
-    //   foo(p);
+    //   x(p);
     // }
-    Func("foo",
+    Func("x",
          utils::Vector{
              Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
@@ -237,7 +275,7 @@
          utils::Vector{
              Decl(Var("v", ty.i32())),
              Decl(Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction), AddressOf("v"))),
-             CallStmt(Call("foo", Expr(Source{{12, 34}}, "p"))),
+             CallStmt(Call("x", "p")),
          },
          utils::Vector{
              Stage(ast::PipelineStage::kFragment),
@@ -247,10 +285,10 @@
 
 TEST_F(ResolverCallValidationTest, LetPointerPrivate) {
     // fn foo(p : ptr<private, i32>) {}
-    // var<private> v: i32;
+    // var v : i32;
     // @fragment
     // fn main() {
-    //   let p: ptr<private, i32> = &v;
+    //   let p : ptr<private, i32> = &v;
     //   foo(p);
     // }
     Func("foo",
@@ -299,6 +337,34 @@
               "originating variable");
 }
 
+TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar_WithFullPtrParametersExt) {
+    // enable chromium_experimental_full_ptr_parameters;
+    // fn foo(p : ptr<function, i32>) {}
+    // @fragment
+    // fn main() {
+    //   var v: array<i32, 4>;
+    //   let p: ptr<function, i32> = &(v[0]);
+    //   x(p);
+    // }
+    Enable(ast::Extension::kChromiumExperimentalFullPtrParameters);
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.array<i32, 4>())),
+             Decl(Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction),
+                      AddressOf(IndexAccessor("v", 0_a)))),
+             CallStmt(Call("foo", Expr(Source{{12, 34}}, "p"))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+    EXPECT_TRUE(r()->Resolve());
+}
+
 TEST_F(ResolverCallValidationTest, ComplexPointerChain) {
     // fn foo(p : ptr<function, array<i32, 4>>) {}
     // @fragment
@@ -360,6 +426,37 @@
               "originating variable");
 }
 
+TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar_WithFullPtrParametersExt) {
+    // enable chromium_experimental_full_ptr_parameters;
+    // fn foo(p : ptr<function, i32>) {}
+    // @fragment
+    // fn main() {
+    //   var v: array<i32, 4>;
+    //   let p1 = &v;
+    //   let p2 = p1;
+    //   let p3 = &(*p2)[0];
+    //   foo(&*p);
+    // }
+    Enable(ast::Extension::kChromiumExperimentalFullPtrParameters);
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.array<i32, 4>())),
+             Decl(Let("p1", AddressOf("v"))),
+             Decl(Let("p2", Expr("p1"))),
+             Decl(Let("p3", AddressOf(IndexAccessor(Deref("p2"), 0_a)))),
+             CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Deref("p3")))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+    EXPECT_TRUE(r()->Resolve());
+}
+
 TEST_F(ResolverCallValidationTest, CallVariable) {
     // var v : i32;
     // fn f() {
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 808f556..922a1dc 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -1041,21 +1041,26 @@
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
 }
 
+enum class Expectation {
+    kAlwaysPass,
+    kPassWithFullPtrParameterExtension,
+    kAlwaysFail,
+};
 struct TestParams {
     ast::AddressSpace address_space;
-    bool should_pass;
+    Expectation expectation;
 };
 
 struct TestWithParams : ResolverTestWithParam<TestParams> {};
 
 using ResolverFunctionParameterValidationTest = TestWithParams;
-TEST_P(ResolverFunctionParameterValidationTest, AddressSpace) {
+TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceNoExtension) {
     auto& param = GetParam();
     auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.address_space);
     auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
     Func("f", utils::Vector{arg}, ty.void_(), utils::Empty);
 
-    if (param.should_pass) {
+    if (param.expectation == Expectation::kAlwaysPass) {
         ASSERT_TRUE(r()->Resolve()) << r()->error();
     } else {
         std::stringstream ss;
@@ -1065,17 +1070,36 @@
                                     ss.str() + "' address space");
     }
 }
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
-                         ResolverFunctionParameterValidationTest,
-                         testing::Values(TestParams{ast::AddressSpace::kNone, false},
-                                         TestParams{ast::AddressSpace::kIn, false},
-                                         TestParams{ast::AddressSpace::kOut, false},
-                                         TestParams{ast::AddressSpace::kUniform, false},
-                                         TestParams{ast::AddressSpace::kWorkgroup, false},
-                                         TestParams{ast::AddressSpace::kHandle, false},
-                                         TestParams{ast::AddressSpace::kStorage, false},
-                                         TestParams{ast::AddressSpace::kPrivate, true},
-                                         TestParams{ast::AddressSpace::kFunction, true}));
+TEST_P(ResolverFunctionParameterValidationTest, AddressSpaceWithExtension) {
+    auto& param = GetParam();
+    auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.address_space);
+    auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
+    Enable(ast::Extension::kChromiumExperimentalFullPtrParameters);
+    Func("f", utils::Vector{arg}, ty.void_(), utils::Empty);
+
+    if (param.expectation != Expectation::kAlwaysFail) {
+        ASSERT_TRUE(r()->Resolve()) << r()->error();
+    } else {
+        std::stringstream ss;
+        ss << param.address_space;
+        EXPECT_FALSE(r()->Resolve());
+        EXPECT_EQ(r()->error(), "12:34 error: function parameter of pointer type cannot be in '" +
+                                    ss.str() + "' address space");
+    }
+}
+INSTANTIATE_TEST_SUITE_P(
+    ResolverTest,
+    ResolverFunctionParameterValidationTest,
+    testing::Values(
+        TestParams{ast::AddressSpace::kNone, Expectation::kAlwaysFail},
+        TestParams{ast::AddressSpace::kIn, Expectation::kAlwaysFail},
+        TestParams{ast::AddressSpace::kOut, Expectation::kAlwaysFail},
+        TestParams{ast::AddressSpace::kUniform, Expectation::kPassWithFullPtrParameterExtension},
+        TestParams{ast::AddressSpace::kWorkgroup, Expectation::kPassWithFullPtrParameterExtension},
+        TestParams{ast::AddressSpace::kHandle, Expectation::kAlwaysFail},
+        TestParams{ast::AddressSpace::kStorage, Expectation::kPassWithFullPtrParameterExtension},
+        TestParams{ast::AddressSpace::kPrivate, Expectation::kAlwaysPass},
+        TestParams{ast::AddressSpace::kFunction, Expectation::kAlwaysPass}));
 
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 70e1de1..9c3c591 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -800,15 +800,31 @@
     }
 
     if (auto* ref = var->Type()->As<sem::Pointer>()) {
-        auto address_space = ref->AddressSpace();
-        if (!(address_space == ast::AddressSpace::kFunction ||
-              address_space == ast::AddressSpace::kPrivate) &&
-            IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
-            std::stringstream ss;
-            ss << "function parameter of pointer type cannot be in '" << address_space
-               << "' address space";
-            AddError(ss.str(), decl->source);
-            return false;
+        if (IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
+            bool ok = false;
+
+            auto sc = ref->AddressSpace();
+            switch (sc) {
+                case ast::AddressSpace::kFunction:
+                case ast::AddressSpace::kPrivate:
+                    ok = true;
+                    break;
+                case ast::AddressSpace::kStorage:
+                case ast::AddressSpace::kUniform:
+                case ast::AddressSpace::kWorkgroup:
+                    ok = enabled_extensions_.Contains(
+                        ast::Extension::kChromiumExperimentalFullPtrParameters);
+                    break;
+                default:
+                    break;
+            }
+            if (!ok) {
+                std::stringstream ss;
+                ss << "function parameter of pointer type cannot be in '" << sc
+                   << "' address space";
+                AddError(ss.str(), decl->source);
+                return false;
+            }
         }
     }
 
@@ -1654,7 +1670,8 @@
             return false;
         }
 
-        if (param_type->Is<sem::Pointer>()) {
+        if (param_type->Is<sem::Pointer>() &&
+            !enabled_extensions_.Contains(ast::Extension::kChromiumExperimentalFullPtrParameters)) {
             // https://gpuweb.github.io/gpuweb/wgsl/#function-restriction
             // Each argument of pointer type to a user-defined function must have the same memory
             // view as its root identifier.