diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc index be16806cb1..31a80839c6 100644 --- a/src/tint/ast/extension.cc +++ b/src/tint/ast/extension.cc @@ -34,6 +34,9 @@ Extension ParseExtension(std::string_view str) { 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 @@ std::ostream& operator<<(std::ostream& out, Extension value) { 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 f6fa03f700..062e3fc5bd 100644 --- a/src/tint/ast/extension.h +++ b/src/tint/ast/extension.h @@ -35,6 +35,7 @@ enum class Extension { kUndefined, kChromiumDisableUniformityAnalysis, kChromiumExperimentalDp4A, + kChromiumExperimentalFullPtrParameters, kChromiumExperimentalPushConstant, kF16, }; @@ -52,6 +53,7 @@ Extension ParseExtension(std::string_view str); 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 94b52160a5..5f175cc502 100644 --- a/src/tint/ast/extension_bench.cc +++ b/src/tint/ast/extension_bench.cc @@ -45,20 +45,27 @@ void ExtensionParser(::benchmark::State& state) { "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 2879bf828c..ea08dccef0 100644 --- a/src/tint/ast/extension_test.cc +++ b/src/tint/ast/extension_test.cc @@ -44,6 +44,8 @@ inline std::ostream& operator<<(std::ostream& out, Case c) { 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 @@ static constexpr Case kInvalidCases[] = { {"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; diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def index 6933e57bf7..bcac5a2be8 100644 --- a/src/tint/intrinsics.def +++ b/src/tint/intrinsics.def @@ -51,6 +51,9 @@ enum extension { 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 aad8eb62b5..392f0dd2a3 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 @@ TEST_F(EnableDirectiveTest, InvalidIdentifier) { // 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 @@ TEST_F(EnableDirectiveTest, InvalidIdentifierSuggest) { // 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 @@ TEST_F(EnableDirectiveTest, InvalidTokens) { 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 @@ Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_ 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 @@ Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_ 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 82037af7c3..e94e7b86a9 100644 --- a/src/tint/resolver/call_validation_test.cc +++ b/src/tint/resolver/call_validation_test.cc @@ -114,11 +114,29 @@ TEST_F(ResolverCallValidationTest, PointerArgument_VariableIdentExpr) { EXPECT_TRUE(r()->Resolve()) << r()->error(); } -TEST_F(ResolverCallValidationTest, PointerArgument_NotWholeVar) { +TEST_F(ResolverCallValidationTest, PointerArgument_LetIdentExpr) { + // fn foo(p: ptr) {} + // fn main() { + // let z: i32 = 1i; + // foo(&z); + // } + auto* param = Param("p", ty.pointer(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) {} // fn main() { - // var v: S; + // var v : S; // foo(&v.m); // } auto* S = Structure("S", utils::Vector{ @@ -138,6 +156,51 @@ TEST_F(ResolverCallValidationTest, PointerArgument_NotWholeVar) { "originating variable"); } +TEST_F(ResolverCallValidationTest, + PointerArgument_AddressOfFunctionMember_WithFullPtrParametersExt) { + // enable chromium_experimental_full_ptr_parameters; + // struct S { m: i32; }; + // fn foo(p: ptr) {} + // 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(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) {} + // 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(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) {} // fn bar(p: ptr) { @@ -181,12 +244,12 @@ TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParamWithMain) { }, 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 @@ TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParamWithMain) { EXPECT_TRUE(r()->Resolve()) << r()->error(); } -TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam_NotWholeVar) { - // fn foo(p: ptr) {} - // fn bar(p: ptr>) { - // foo(&(*p)[0]); - // } - Func("foo", - utils::Vector{ - Param("p", ty.pointer(ast::AddressSpace::kFunction)), - }, - ty.void_(), utils::Empty); - Func("bar", - utils::Vector{ - Param("p", ty.pointer(ty.array(), 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) {} // @fragment // fn main() { // var v: i32; // let p: ptr = &v; - // foo(p); + // x(p); // } - Func("foo", + Func("x", utils::Vector{ Param("p", ty.pointer(ast::AddressSpace::kFunction)), }, @@ -237,7 +275,7 @@ TEST_F(ResolverCallValidationTest, LetPointer) { 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, LetPointer) { TEST_F(ResolverCallValidationTest, LetPointerPrivate) { // fn foo(p : ptr) {} - // var v: i32; + // var v : i32; // @fragment // fn main() { - // let p: ptr = &v; + // let p : ptr = &v; // foo(p); // } Func("foo", @@ -299,6 +337,34 @@ TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar) { "originating variable"); } +TEST_F(ResolverCallValidationTest, LetPointer_NotWholeVar_WithFullPtrParametersExt) { + // enable chromium_experimental_full_ptr_parameters; + // fn foo(p : ptr) {} + // @fragment + // fn main() { + // var v: array; + // let p: ptr = &(v[0]); + // x(p); + // } + Enable(ast::Extension::kChromiumExperimentalFullPtrParameters); + Func("foo", + utils::Vector{ + Param("p", ty.pointer(ast::AddressSpace::kFunction)), + }, + ty.void_(), utils::Empty); + Func("main", utils::Empty, ty.void_(), + utils::Vector{ + Decl(Var("v", ty.array())), + 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>) {} // @fragment @@ -360,6 +426,37 @@ TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar) { "originating variable"); } +TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar_WithFullPtrParametersExt) { + // enable chromium_experimental_full_ptr_parameters; + // fn foo(p : ptr) {} + // @fragment + // fn main() { + // var v: array; + // let p1 = &v; + // let p2 = p1; + // let p3 = &(*p2)[0]; + // foo(&*p); + // } + Enable(ast::Extension::kChromiumExperimentalFullPtrParameters); + Func("foo", + utils::Vector{ + Param("p", ty.pointer(ast::AddressSpace::kFunction)), + }, + ty.void_(), utils::Empty); + Func("main", utils::Empty, ty.void_(), + utils::Vector{ + Decl(Var("v", ty.array())), + 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 808f556777..922a1dcea9 100644 --- a/src/tint/resolver/function_validation_test.cc +++ b/src/tint/resolver/function_validation_test.cc @@ -1041,21 +1041,26 @@ TEST_F(ResolverFunctionValidationTest, ParameterMatrixNoType) { 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 {}; 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 @@ TEST_P(ResolverFunctionParameterValidationTest, AddressSpace) { 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 70e1de1db8..9c3c591d23 100644 --- a/src/tint/resolver/validator.cc +++ b/src/tint/resolver/validator.cc @@ -800,15 +800,31 @@ bool Validator::Parameter(const ast::Function* func, const sem::Variable* var) c } if (auto* ref = var->Type()->As()) { - 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 @@ bool Validator::FunctionCall(const sem::Call* call, sem::Statement* current_stat return false; } - if (param_type->Is()) { + if (param_type->Is() && + !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.