diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc index 49e266f1f1..a04e5484ee 100644 --- a/src/tint/reader/wgsl/parser_impl.cc +++ b/src/tint/reader/wgsl/parser_impl.cc @@ -43,6 +43,7 @@ #include "src/tint/sem/external_texture.h" #include "src/tint/sem/multisampled_texture.h" #include "src/tint/sem/sampled_texture.h" +#include "src/tint/utils/reverse.h" #include "src/tint/utils/string.h" namespace tint::reader::wgsl { @@ -3239,34 +3240,50 @@ Maybe ParserImpl::lhs_expression() { return component_or_swizzle_specifier(core_expr.value); } - auto check_lhs = [&](ast::UnaryOp op) -> Maybe { - auto& t = peek(); - auto expr = lhs_expression(); - if (expr.errored) { - return Failure::kErrored; - } - if (!expr.matched) { - return add_error(t, "missing expression"); - } - return create(t.source(), op, expr.value); + // Gather up all the `*`, `&` and `&&` tokens into a list and create all of the unary ops at + // once instead of recursing. This handles the case where the fuzzer decides >8k `*`s would be + // fun. + struct LHSData { + Source source; + ast::UnaryOp op; }; + utils::Vector ops; + while (true) { + auto& t = peek(); + if (!t.Is(Token::Type::kAndAnd) && !t.Is(Token::Type::kAnd) && !t.Is(Token::Type::kStar)) { + break; + } + next(); // consume the peek - // If an `&&` is encountered, split it into two `&`'s - if (match(Token::Type::kAndAnd)) { - // The first `&` is consumed as part of the `&&`, so this needs to run the check itself. - split_token(Token::Type::kAnd, Token::Type::kAnd); - return check_lhs(ast::UnaryOp::kAddressOf); + if (t.Is(Token::Type::kAndAnd)) { + // The first `&` is consumed as part of the `&&`, so we only push one of the two `&`s. + split_token(Token::Type::kAnd, Token::Type::kAnd); + ops.Push({t.source(), ast::UnaryOp::kAddressOf}); + } else if (t.Is(Token::Type::kAnd)) { + ops.Push({t.source(), ast::UnaryOp::kAddressOf}); + } else if (t.Is(Token::Type::kStar)) { + ops.Push({t.source(), ast::UnaryOp::kIndirection}); + } + } + if (ops.IsEmpty()) { + return Failure::kNoMatch; } - if (match(Token::Type::kAnd)) { - return check_lhs(ast::UnaryOp::kAddressOf); + auto& t = peek(); + auto expr = lhs_expression(); + if (expr.errored) { + return Failure::kErrored; + } + if (!expr.matched) { + return add_error(t, "missing expression"); } - if (match(Token::Type::kStar)) { - return check_lhs(ast::UnaryOp::kIndirection); + const ast::Expression* ret = expr.value; + // Consume the ops in reverse order so we have the correct AST ordering. + for (auto& info : utils::Reverse(ops)) { + ret = create(info.source, info.op, ret); } - - return Failure::kNoMatch; + return ret; } // variable_updating_statement diff --git a/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc b/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc index 4e1e0db372..a8a476b48a 100644 --- a/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc +++ b/src/tint/reader/wgsl/parser_impl_lhs_expression_test.cc @@ -75,17 +75,20 @@ TEST_F(ParserImplTest, LHSExpression_InvalidCoreLHSExpr) { } TEST_F(ParserImplTest, LHSExpression_Multiple) { - auto p = parser("*&**&&*a"); + auto p = parser("*&********&&&&&&*a"); auto e = p->lhs_expression(); ASSERT_FALSE(p->has_error()) << p->error(); ASSERT_FALSE(e.errored); EXPECT_TRUE(e.matched); ASSERT_NE(e.value, nullptr); - std::vector results = {ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf, - ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, - ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf, - ast::UnaryOp::kIndirection}; + std::vector results = { + ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf, ast::UnaryOp::kIndirection, + ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, + ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, + ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf, + ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf, + ast::UnaryOp::kAddressOf, ast::UnaryOp::kIndirection}; auto* expr = e.value; for (auto op : results) {