wsgl parser: add expect_lt_gt_block(), use it

Reduces code, paves the way for multiple errors with resynchronization points.

Bug: tint:282
Change-Id: I68018ea8cabe4ec347afa21d1220126d6348d3d1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32280
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2020-11-11 14:10:25 +00:00 committed by Commit Bot service account
parent a4f49d91dc
commit 00bc8ba88c
6 changed files with 81 additions and 112 deletions

View File

@ -389,17 +389,9 @@ Maybe<ast::type::Type*> ParserImpl::texture_sampler_types() {
if (dim.matched) { if (dim.matched) {
const char* use = "sampled texture type"; const char* use = "sampled texture type";
if (!expect(use, Token::Type::kLessThan)) auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
return Failure::kErrored;
auto subtype = type_decl();
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek().source(), "invalid subtype", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get(std::make_unique<ast::type::SampledTextureType>( return ctx_.type_mgr().Get(std::make_unique<ast::type::SampledTextureType>(
dim.value, subtype.value)); dim.value, subtype.value));
@ -409,17 +401,9 @@ Maybe<ast::type::Type*> ParserImpl::texture_sampler_types() {
if (ms_dim.matched) { if (ms_dim.matched) {
const char* use = "multisampled texture type"; const char* use = "multisampled texture type";
if (!expect(use, Token::Type::kLessThan)) auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
return Failure::kErrored;
auto subtype = type_decl();
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek().source(), "invalid subtype", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get( return ctx_.type_mgr().Get(
std::make_unique<ast::type::MultisampledTextureType>(ms_dim.value, std::make_unique<ast::type::MultisampledTextureType>(ms_dim.value,
@ -430,16 +414,12 @@ Maybe<ast::type::Type*> ParserImpl::texture_sampler_types() {
if (storage.matched) { if (storage.matched) {
const char* use = "storage texture type"; const char* use = "storage texture type";
if (!expect(use, Token::Type::kLessThan)) auto format =
return Failure::kErrored; expect_lt_gt_block(use, [&] { return expect_image_storage_type(use); });
auto format = expect_image_storage_type(use);
if (format.errored) if (format.errored)
return Failure::kErrored; return Failure::kErrored;
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get(std::make_unique<ast::type::StorageTextureType>( return ctx_.type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
storage->first, storage->second, format.value)); storage->first, storage->second, format.value));
} }
@ -773,16 +753,14 @@ Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
// variable_storage_decoration // variable_storage_decoration
// : LESS_THAN storage_class GREATER_THAN // : LESS_THAN storage_class GREATER_THAN
Maybe<ast::StorageClass> ParserImpl::variable_storage_decoration() { Maybe<ast::StorageClass> ParserImpl::variable_storage_decoration() {
if (!match(Token::Type::kLessThan)) if (!peek().IsLessThan())
return Failure::kNoMatch; return Failure::kNoMatch;
const char* use = "variable decoration"; const char* use = "variable decoration";
auto sc = expect_storage_class(use); auto sc = expect_lt_gt_block(use, [&] { return expect_storage_class(use); });
if (sc.errored)
return Failure::kErrored;
if (!expect(use, Token::Type::kGreaterThan)) if (sc.errored)
return Failure::kErrored; return Failure::kErrored;
return sc.value; return sc.value;
@ -904,12 +882,19 @@ Maybe<ast::type::Type*> ParserImpl::type_decl() {
return Failure::kNoMatch; return Failure::kNoMatch;
} }
Expect<ast::type::Type*> ParserImpl::expect_type(const std::string& use) {
auto type = type_decl();
if (type.errored)
return Failure::kErrored;
if (!type.matched)
return add_error(peek().source(), "invalid type", use);
return type.value;
}
Expect<ast::type::Type*> ParserImpl::expect_type_decl_pointer() { Expect<ast::type::Type*> ParserImpl::expect_type_decl_pointer() {
const char* use = "ptr declaration"; const char* use = "ptr declaration";
if (!expect(use, Token::Type::kLessThan)) return expect_lt_gt_block(use, [&]() -> Expect<ast::type::Type*> {
return Failure::kErrored;
auto sc = expect_storage_class(use); auto sc = expect_storage_class(use);
if (sc.errored) if (sc.errored)
return Failure::kErrored; return Failure::kErrored;
@ -917,17 +902,13 @@ Expect<ast::type::Type*> ParserImpl::expect_type_decl_pointer() {
if (!expect(use, Token::Type::kComma)) if (!expect(use, Token::Type::kComma))
return Failure::kErrored; return Failure::kErrored;
auto subtype = type_decl(); auto subtype = expect_type(use);
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek().source(), "missing type", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get( return ctx_.type_mgr().Get(
std::make_unique<ast::type::PointerType>(subtype.value, sc.value)); std::make_unique<ast::type::PointerType>(subtype.value, sc.value));
});
} }
Expect<ast::type::Type*> ParserImpl::expect_type_decl_vector(Token t) { Expect<ast::type::Type*> ParserImpl::expect_type_decl_vector(Token t) {
@ -939,17 +920,9 @@ Expect<ast::type::Type*> ParserImpl::expect_type_decl_vector(Token t) {
const char* use = "vector"; const char* use = "vector";
if (!expect(use, Token::Type::kLessThan)) auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
return Failure::kErrored;
auto subtype = type_decl();
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek().source(), "unable to determine subtype", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get( return ctx_.type_mgr().Get(
std::make_unique<ast::type::VectorType>(subtype.value, count)); std::make_unique<ast::type::VectorType>(subtype.value, count));
@ -959,14 +932,10 @@ Expect<ast::type::Type*> ParserImpl::expect_type_decl_array(
ast::ArrayDecorationList decos) { ast::ArrayDecorationList decos) {
const char* use = "array declaration"; const char* use = "array declaration";
if (!expect(use, Token::Type::kLessThan)) return expect_lt_gt_block(use, [&]() -> Expect<ast::type::Type*> {
return Failure::kErrored; auto subtype = expect_type(use);
auto subtype = type_decl();
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek(), "invalid type for array declaration");
uint32_t size = 0; uint32_t size = 0;
if (match(Token::Type::kComma)) { if (match(Token::Type::kComma)) {
@ -976,12 +945,10 @@ Expect<ast::type::Type*> ParserImpl::expect_type_decl_array(
size = val.value; size = val.value;
} }
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
auto ty = std::make_unique<ast::type::ArrayType>(subtype.value, size); auto ty = std::make_unique<ast::type::ArrayType>(subtype.value, size);
ty->set_decorations(std::move(decos)); ty->set_decorations(std::move(decos));
return ctx_.type_mgr().Get(std::move(ty)); return ctx_.type_mgr().Get(std::move(ty));
});
} }
Expect<ast::type::Type*> ParserImpl::expect_type_decl_matrix(Token t) { Expect<ast::type::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
@ -1000,17 +967,9 @@ Expect<ast::type::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
const char* use = "matrix"; const char* use = "matrix";
if (!expect(use, Token::Type::kLessThan)) auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
return Failure::kErrored;
auto subtype = type_decl();
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
if (!subtype.matched)
return add_error(peek().source(), "unable to determine subtype", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
return ctx_.type_mgr().Get( return ctx_.type_mgr().Get(
std::make_unique<ast::type::MatrixType>(subtype.value, rows, columns)); std::make_unique<ast::type::MatrixType>(subtype.value, rows, columns));
@ -1948,17 +1907,9 @@ Maybe<std::unique_ptr<ast::Expression>> ParserImpl::primary_expression() {
if (match(Token::Type::kBitcast)) { if (match(Token::Type::kBitcast)) {
const char* use = "bitcast expression"; const char* use = "bitcast expression";
if (!expect(use, Token::Type::kLessThan)) auto type = expect_lt_gt_block(use, [&] { return expect_type(use); });
return Failure::kErrored;
auto type = type_decl();
if (type.errored) if (type.errored)
return Failure::kErrored; return Failure::kErrored;
if (!type.matched)
return add_error(peek().source(), "missing type", use);
if (!expect(use, Token::Type::kGreaterThan))
return Failure::kErrored;
auto params = expect_paren_rhs_stmt(); auto params = expect_paren_rhs_stmt();
if (params.errored) if (params.errored)
@ -2927,6 +2878,12 @@ T ParserImpl::expect_brace_block(const std::string& use, F&& body) {
std::forward<F>(body)); std::forward<F>(body));
} }
template <typename F, typename T>
T ParserImpl::expect_lt_gt_block(const std::string& use, F&& body) {
return expect_block(Token::Type::kLessThan, Token::Type::kGreaterThan, use,
std::forward<F>(body));
}
} // namespace wgsl } // namespace wgsl
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -645,7 +645,7 @@ class ParserImpl {
/// @param end the token that ends the lexical block /// @param end the token that ends the lexical block
/// @param use a description of what was being parsed if an error was raised /// @param use a description of what was being parsed if an error was raised
/// @param body a function or lambda that is called to parse the lexical block /// @param body a function or lambda that is called to parse the lexical block
/// body, with the signature: `Expect<Result>()`. /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
/// @return the value returned by |body| if no errors are raised, otherwise /// @return the value returned by |body| if no errors are raised, otherwise
/// an Expect with error state. /// an Expect with error state.
template <typename F, typename T = ReturnType<F>> template <typename F, typename T = ReturnType<F>>
@ -658,7 +658,7 @@ class ParserImpl {
/// and |end| arguments, respectively. /// and |end| arguments, respectively.
/// @param use a description of what was being parsed if an error was raised /// @param use a description of what was being parsed if an error was raised
/// @param body a function or lambda that is called to parse the lexical block /// @param body a function or lambda that is called to parse the lexical block
/// body, with the signature: `Expect<Result>()`. /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
/// @return the value returned by |body| if no errors are raised, otherwise /// @return the value returned by |body| if no errors are raised, otherwise
/// an Expect with error state. /// an Expect with error state.
template <typename F, typename T = ReturnType<F>> template <typename F, typename T = ReturnType<F>>
@ -668,11 +668,21 @@ class ParserImpl {
/// and |end| arguments, respectively. /// and |end| arguments, respectively.
/// @param use a description of what was being parsed if an error was raised /// @param use a description of what was being parsed if an error was raised
/// @param body a function or lambda that is called to parse the lexical block /// @param body a function or lambda that is called to parse the lexical block
/// body, with the signature: `Expect<Result>()`. /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
/// @return the value returned by |body| if no errors are raised, otherwise /// @return the value returned by |body| if no errors are raised, otherwise
/// an Expect with error state. /// an Expect with error state.
template <typename F, typename T = ReturnType<F>> template <typename F, typename T = ReturnType<F>>
T expect_brace_block(const std::string& use, F&& body); T expect_brace_block(const std::string& use, F&& body);
/// A convenience function that calls |expect_block| passing
/// |Token::Type::kLessThan| and |Token::Type::kGreaterThan| for the |start|
/// and |end| arguments, respectively.
/// @param use a description of what was being parsed if an error was raised
/// @param body a function or lambda that is called to parse the lexical block
/// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
/// @return the value returned by |body| if no errors are raised, otherwise
/// an Expect with error state.
template <typename F, typename T = ReturnType<F>>
T expect_lt_gt_block(const std::string& use, F&& body);
/// Downcasts all the decorations in |list| to the type |T|, raising a parser /// Downcasts all the decorations in |list| to the type |T|, raising a parser
/// error if any of the decorations aren't of the type |T|. /// error if any of the decorations aren't of the type |T|.
@ -692,6 +702,8 @@ class ParserImpl {
Expect<std::unique_ptr<ast::ConstructorExpression>> Expect<std::unique_ptr<ast::ConstructorExpression>>
expect_const_expr_internal(uint32_t depth); expect_const_expr_internal(uint32_t depth);
Expect<ast::type::Type*> expect_type(const std::string& use);
Maybe<std::unique_ptr<ast::Statement>> for_header_initializer(); Maybe<std::unique_ptr<ast::Statement>> for_header_initializer();
Maybe<std::unique_ptr<ast::Statement>> for_header_continuing(); Maybe<std::unique_ptr<ast::Statement>> for_header_continuing();

View File

@ -105,7 +105,7 @@ TEST_F(ParserImplErrorTest, BitcastExprMissingGreaterThan) {
TEST_F(ParserImplErrorTest, BitcastExprMissingType) { TEST_F(ParserImplErrorTest, BitcastExprMissingType) {
EXPECT("fn f() -> void { x = bitcast<>(y); }", EXPECT("fn f() -> void { x = bitcast<>(y); }",
"test.wgsl:1:30 error: missing type for bitcast expression\n" "test.wgsl:1:30 error: invalid type for bitcast expression\n"
"fn f() -> void { x = bitcast<>(y); }\n" "fn f() -> void { x = bitcast<>(y); }\n"
" ^\n"); " ^\n");
} }
@ -548,7 +548,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureMissingGreaterThan_Old) {
TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype_Old) { TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype_Old) {
EXPECT("var x : texture_sampled_1d<1>;", EXPECT("var x : texture_sampled_1d<1>;",
"test.wgsl:1:28 error: invalid subtype for sampled texture type\n" "test.wgsl:1:28 error: invalid type for sampled texture type\n"
"var x : texture_sampled_1d<1>;\n" "var x : texture_sampled_1d<1>;\n"
" ^\n"); " ^\n");
} }
@ -569,7 +569,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureMissingGreaterThan) {
TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype) { TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype) {
EXPECT("var x : texture_1d<1>;", EXPECT("var x : texture_1d<1>;",
"test.wgsl:1:20 error: invalid subtype for sampled texture type\n" "test.wgsl:1:20 error: invalid type for sampled texture type\n"
"var x : texture_1d<1>;\n" "var x : texture_1d<1>;\n"
" ^\n"); " ^\n");
} }
@ -590,7 +590,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclMultisampledTextureMissingGreaterThan) {
TEST_F(ParserImplErrorTest, GlobalDeclMultisampledTextureInvalidSubtype) { TEST_F(ParserImplErrorTest, GlobalDeclMultisampledTextureInvalidSubtype) {
EXPECT("var x : texture_multisampled_2d<1>;", EXPECT("var x : texture_multisampled_2d<1>;",
"test.wgsl:1:33 error: invalid subtype for multisampled texture type\n" "test.wgsl:1:33 error: invalid type for multisampled texture type\n"
"var x : texture_multisampled_2d<1>;\n" "var x : texture_multisampled_2d<1>;\n"
" ^\n"); " ^\n");
} }
@ -1009,7 +1009,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarMatrixMissingGreaterThan) {
TEST_F(ParserImplErrorTest, GlobalDeclVarMatrixMissingType) { TEST_F(ParserImplErrorTest, GlobalDeclVarMatrixMissingType) {
EXPECT("var i : mat4x4<1>;", EXPECT("var i : mat4x4<1>;",
"test.wgsl:1:16 error: unable to determine subtype for matrix\n" "test.wgsl:1:16 error: invalid type for matrix\n"
"var i : mat4x4<1>;\n" "var i : mat4x4<1>;\n"
" ^\n"); " ^\n");
} }
@ -1051,7 +1051,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingStorageClass) {
TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingType) { TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingType) {
EXPECT("var i : ptr<in, 1>;", EXPECT("var i : ptr<in, 1>;",
"test.wgsl:1:17 error: missing type for ptr declaration\n" "test.wgsl:1:17 error: invalid type for ptr declaration\n"
"var i : ptr<in, 1>;\n" "var i : ptr<in, 1>;\n"
" ^\n"); " ^\n");
} }
@ -1086,7 +1086,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarVectorMissingGreaterThan) {
TEST_F(ParserImplErrorTest, GlobalDeclVarVectorMissingType) { TEST_F(ParserImplErrorTest, GlobalDeclVarVectorMissingType) {
EXPECT("var i : vec3<1>;", EXPECT("var i : vec3<1>;",
"test.wgsl:1:14 error: unable to determine subtype for vector\n" "test.wgsl:1:14 error: invalid type for vector\n"
"var i : vec3<1>;\n" "var i : vec3<1>;\n"
" ^\n"); " ^\n");
} }

View File

@ -103,7 +103,7 @@ TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_InvalidTypeDecl) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:6: unable to determine subtype for vector"); EXPECT_EQ(p->error(), "1:6: invalid type for vector");
} }
TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_MissingLeftParen) { TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_MissingLeftParen) {
@ -245,7 +245,7 @@ TEST_F(ParserImplTest, PrimaryExpression_Bitcast_MissingType) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:9: missing type for bitcast expression"); EXPECT_EQ(p->error(), "1:9: invalid type for bitcast expression");
} }
TEST_F(ParserImplTest, PrimaryExpression_Bitcast_InvalidType) { TEST_F(ParserImplTest, PrimaryExpression_Bitcast_InvalidType) {

View File

@ -123,7 +123,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType_Old) {
EXPECT_EQ(t.value, nullptr); EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored); EXPECT_TRUE(t.errored);
EXPECT_EQ(p->error(), "1:20: invalid subtype for sampled texture type"); EXPECT_EQ(p->error(), "1:20: invalid type for sampled texture type");
} }
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan_Old) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan_Old) {
@ -203,7 +203,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType) {
EXPECT_EQ(t.value, nullptr); EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored); EXPECT_TRUE(t.errored);
EXPECT_EQ(p->error(), "1:12: invalid subtype for sampled texture type"); EXPECT_EQ(p->error(), "1:12: invalid type for sampled texture type");
} }
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan) {
@ -256,7 +256,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_MissingType) {
EXPECT_EQ(t.value, nullptr); EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored); EXPECT_TRUE(t.errored);
EXPECT_EQ(p->error(), "1:25: invalid subtype for multisampled texture type"); EXPECT_EQ(p->error(), "1:25: invalid type for multisampled texture type");
} }
TEST_F(ParserImplTest, TEST_F(ParserImplTest,

View File

@ -221,7 +221,7 @@ TEST_P(VecMissingType, Handles_Missing_Type) {
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr); ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:6: unable to determine subtype for vector"); ASSERT_EQ(p->error(), "1:6: invalid type for vector");
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingType, VecMissingType,
@ -318,7 +318,7 @@ TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr); ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:14: missing type for ptr declaration"); ASSERT_EQ(p->error(), "1:14: invalid type for ptr declaration");
} }
TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) { TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
@ -718,7 +718,7 @@ TEST_P(MatrixMissingType, Handles_Missing_Type) {
EXPECT_FALSE(t.matched); EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr); ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:8: unable to determine subtype for matrix"); ASSERT_EQ(p->error(), "1:8: invalid type for matrix");
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingType, MatrixMissingType,