Add `requires` directive
This Cl adds the requires directive into Tint. Using the directive is currently always an error as there is no valid value which can be used. Bug: tint:1843 Change-Id: Idf77ba4e95ff0c1e177d02d1ba9598edc89a9812 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/120740 Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: David Neto <dneto@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
f35f1ff98c
commit
187e0d5fe7
|
@ -1716,6 +1716,7 @@ if (tint_build_unittests) {
|
||||||
"reader/wgsl/parser_impl_paren_expression_test.cc",
|
"reader/wgsl/parser_impl_paren_expression_test.cc",
|
||||||
"reader/wgsl/parser_impl_primary_expression_test.cc",
|
"reader/wgsl/parser_impl_primary_expression_test.cc",
|
||||||
"reader/wgsl/parser_impl_relational_expression_test.cc",
|
"reader/wgsl/parser_impl_relational_expression_test.cc",
|
||||||
|
"reader/wgsl/parser_impl_require_directive_test.cc",
|
||||||
"reader/wgsl/parser_impl_reserved_keyword_test.cc",
|
"reader/wgsl/parser_impl_reserved_keyword_test.cc",
|
||||||
"reader/wgsl/parser_impl_shift_expression_test.cc",
|
"reader/wgsl/parser_impl_shift_expression_test.cc",
|
||||||
"reader/wgsl/parser_impl_singular_expression_test.cc",
|
"reader/wgsl/parser_impl_singular_expression_test.cc",
|
||||||
|
|
|
@ -1099,6 +1099,7 @@ if(TINT_BUILD_TESTS)
|
||||||
reader/wgsl/parser_impl_primary_expression_test.cc
|
reader/wgsl/parser_impl_primary_expression_test.cc
|
||||||
reader/wgsl/parser_impl_relational_expression_test.cc
|
reader/wgsl/parser_impl_relational_expression_test.cc
|
||||||
reader/wgsl/parser_impl_reserved_keyword_test.cc
|
reader/wgsl/parser_impl_reserved_keyword_test.cc
|
||||||
|
reader/wgsl/parser_impl_require_directive_test.cc
|
||||||
reader/wgsl/parser_impl_shift_expression_test.cc
|
reader/wgsl/parser_impl_shift_expression_test.cc
|
||||||
reader/wgsl/parser_impl_singular_expression_test.cc
|
reader/wgsl/parser_impl_singular_expression_test.cc
|
||||||
reader/wgsl/parser_impl_statement_test.cc
|
reader/wgsl/parser_impl_statement_test.cc
|
||||||
|
|
|
@ -1181,6 +1181,9 @@ Token Lexer::check_keyword(const Source& source, std::string_view str) {
|
||||||
if (str == "return") {
|
if (str == "return") {
|
||||||
return {Token::Type::kReturn, source, "return"};
|
return {Token::Type::kReturn, source, "return"};
|
||||||
}
|
}
|
||||||
|
if (str == "requires") {
|
||||||
|
return {Token::Type::kRequires, source, "requires"};
|
||||||
|
}
|
||||||
if (str == "struct") {
|
if (str == "struct") {
|
||||||
return {Token::Type::kStruct, source, "struct"};
|
return {Token::Type::kStruct, source, "struct"};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1078,6 +1078,7 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
|
||||||
TokenData{"loop", Token::Type::kLoop},
|
TokenData{"loop", Token::Type::kLoop},
|
||||||
TokenData{"override", Token::Type::kOverride},
|
TokenData{"override", Token::Type::kOverride},
|
||||||
TokenData{"return", Token::Type::kReturn},
|
TokenData{"return", Token::Type::kReturn},
|
||||||
|
TokenData{"requires", Token::Type::kRequires},
|
||||||
TokenData{"struct", Token::Type::kStruct},
|
TokenData{"struct", Token::Type::kStruct},
|
||||||
TokenData{"switch", Token::Type::kSwitch},
|
TokenData{"switch", Token::Type::kSwitch},
|
||||||
TokenData{"true", Token::Type::kTrue},
|
TokenData{"true", Token::Type::kTrue},
|
||||||
|
|
|
@ -94,9 +94,9 @@ bool is_reserved(const Token& t) {
|
||||||
t == "partition" || t == "pass" || t == "patch" || t == "pixelfragment" ||
|
t == "partition" || t == "pass" || t == "patch" || t == "pixelfragment" ||
|
||||||
t == "precise" || t == "precision" || t == "premerge" || t == "priv" ||
|
t == "precise" || t == "precision" || t == "premerge" || t == "priv" ||
|
||||||
t == "protected" || t == "pub" || t == "public" || t == "readonly" || t == "ref" ||
|
t == "protected" || t == "pub" || t == "public" || t == "readonly" || t == "ref" ||
|
||||||
t == "regardless" || t == "register" || t == "reinterpret_cast" || t == "requires" ||
|
t == "regardless" || t == "register" || t == "reinterpret_cast" || t == "resource" ||
|
||||||
t == "resource" || t == "restrict" || t == "self" || t == "set" || t == "shared" ||
|
t == "restrict" || t == "self" || t == "set" || t == "shared" || t == "signed" ||
|
||||||
t == "signed" || t == "sizeof" || t == "smooth" || t == "snorm" || t == "static" ||
|
t == "sizeof" || t == "smooth" || t == "snorm" || t == "static" ||
|
||||||
t == "static_assert" || t == "static_cast" || t == "std" || t == "subroutine" ||
|
t == "static_assert" || t == "static_cast" || t == "std" || t == "subroutine" ||
|
||||||
t == "super" || t == "target" || t == "template" || t == "this" || t == "thread_local" ||
|
t == "super" || t == "target" || t == "template" || t == "this" || t == "thread_local" ||
|
||||||
t == "throw" || t == "trait" || t == "try" || t == "type" || t == "typedef" ||
|
t == "throw" || t == "trait" || t == "try" || t == "type" || t == "typedef" ||
|
||||||
|
@ -341,6 +341,7 @@ void ParserImpl::translation_unit() {
|
||||||
|
|
||||||
// global_directive
|
// global_directive
|
||||||
// : diagnostic_directive
|
// : diagnostic_directive
|
||||||
|
// | requires_directive
|
||||||
// | enable_directive
|
// | enable_directive
|
||||||
Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
|
Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
|
||||||
auto& p = peek();
|
auto& p = peek();
|
||||||
|
@ -348,6 +349,9 @@ Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
|
||||||
if (!result.errored && !result.matched) {
|
if (!result.errored && !result.matched) {
|
||||||
result = enable_directive();
|
result = enable_directive();
|
||||||
}
|
}
|
||||||
|
if (!result.errored && !result.matched) {
|
||||||
|
result = requires_directive();
|
||||||
|
}
|
||||||
|
|
||||||
if (result.matched && have_parsed_decl) {
|
if (result.matched && have_parsed_decl) {
|
||||||
return add_error(p, "directives must come before all global declarations");
|
return add_error(p, "directives must come before all global declarations");
|
||||||
|
@ -428,6 +432,67 @@ Maybe<Void> ParserImpl::enable_directive() {
|
||||||
return Failure::kNoMatch;
|
return Failure::kNoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// requires_directive
|
||||||
|
// : require identifier (COMMA identifier)? SEMICLON
|
||||||
|
Maybe<Void> ParserImpl::requires_directive() {
|
||||||
|
auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
|
||||||
|
if (!match(Token::Type::kRequires)) {
|
||||||
|
return Failure::kNoMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the require name.
|
||||||
|
auto& t = peek();
|
||||||
|
if (handle_error(t)) {
|
||||||
|
// The token might itself be an error.
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.Is(Token::Type::kParenLeft)) {
|
||||||
|
// A common error case is writing `require(foo);` instead of `require foo;`.
|
||||||
|
synchronized_ = false;
|
||||||
|
return add_error(t.source(), "requires directives don't take parenthesis");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (continue_parsing()) {
|
||||||
|
auto& t2 = peek();
|
||||||
|
|
||||||
|
// Match the require name.
|
||||||
|
if (handle_error(t2)) {
|
||||||
|
// The token might itself be an error.
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t2.IsIdentifier()) {
|
||||||
|
// TODO(dsinclair): When there are actual values for a requires directive they
|
||||||
|
// should be checked here.
|
||||||
|
|
||||||
|
// Any identifer is a valid feature name, so we correctly handle new feature
|
||||||
|
// names getting added in the future, they just all get flagged as not supported.
|
||||||
|
return add_error(t2.source(), "feature '" + t2.to_str() + "' is not supported");
|
||||||
|
}
|
||||||
|
if (t2.Is(Token::Type::kSemicolon)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!match(Token::Type::kComma)) {
|
||||||
|
return add_error(t2.source(), "invalid feature name for requires");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(dsinclair): When there are actual values for a requires directive then the
|
||||||
|
// `while` will need to keep track if any were seen, and this needs to become
|
||||||
|
// conditional.
|
||||||
|
return add_error(t.source(), "missing feature names in requires directive");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (decl.errored) {
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
if (decl.matched) {
|
||||||
|
return kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Failure::kNoMatch;
|
||||||
|
}
|
||||||
|
|
||||||
// global_decl
|
// global_decl
|
||||||
// : SEMICOLON
|
// : SEMICOLON
|
||||||
// | global_variable_decl SEMICOLON
|
// | global_variable_decl SEMICOLON
|
||||||
|
|
|
@ -381,6 +381,9 @@ class ParserImpl {
|
||||||
/// Parses the `enable_directive` grammar element, erroring on parse failure.
|
/// Parses the `enable_directive` grammar element, erroring on parse failure.
|
||||||
/// @return true on parse success, otherwise an error or no-match.
|
/// @return true on parse success, otherwise an error or no-match.
|
||||||
Maybe<Void> enable_directive();
|
Maybe<Void> enable_directive();
|
||||||
|
/// Parses the `requires_directive` grammar element, erroring on parse failure.
|
||||||
|
/// @return true on parse success, otherwise an error or no-match.
|
||||||
|
Maybe<Void> requires_directive();
|
||||||
/// Parses the `global_decl` grammar element, erroring on parse failure.
|
/// Parses the `global_decl` grammar element, erroring on parse failure.
|
||||||
/// @return true on parse success, otherwise an error or no-match.
|
/// @return true on parse success, otherwise an error or no-match.
|
||||||
Maybe<Void> global_decl();
|
Maybe<Void> global_decl();
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2023 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/reader/wgsl/parser_impl_test_helper.h"
|
||||||
|
|
||||||
|
namespace tint::reader::wgsl {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using RequiresDirectiveTest = ParserImplTest;
|
||||||
|
|
||||||
|
// Test a valid require directive.
|
||||||
|
// There currently are no valid require directives
|
||||||
|
TEST_F(RequiresDirectiveTest, DISABLED_Valid) {
|
||||||
|
auto p = parser("requires <sometime>;");
|
||||||
|
p->requires_directive();
|
||||||
|
EXPECT_FALSE(p->has_error()) << p->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test an unknown require identifier.
|
||||||
|
TEST_F(RequiresDirectiveTest, InvalidIdentifier) {
|
||||||
|
auto p = parser("requires NotAValidRequireName;");
|
||||||
|
p->requires_directive();
|
||||||
|
// Error when unknown require found
|
||||||
|
EXPECT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), R"(1:10: feature 'NotAValidRequireName' is not supported)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the special error message when require are used with parenthesis.
|
||||||
|
TEST_F(RequiresDirectiveTest, ParenthesisSpecialCase) {
|
||||||
|
auto p = parser("requires(Something);");
|
||||||
|
p->translation_unit();
|
||||||
|
EXPECT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), "1:9: requires directives don't take parenthesis");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using invalid tokens in an require directive.
|
||||||
|
TEST_F(RequiresDirectiveTest, InvalidTokens) {
|
||||||
|
{
|
||||||
|
auto p = parser("requires <Something;");
|
||||||
|
p->translation_unit();
|
||||||
|
EXPECT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), R"(1:10: invalid feature name for requires)");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p = parser("requires =;");
|
||||||
|
p->translation_unit();
|
||||||
|
EXPECT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), R"(1:10: invalid feature name for requires)");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto p = parser("requires;");
|
||||||
|
p->translation_unit();
|
||||||
|
EXPECT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), R"(1:9: missing feature names in requires directive)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint::reader::wgsl
|
|
@ -193,7 +193,6 @@ INSTANTIATE_TEST_SUITE_P(ParserImplReservedKeywordTest,
|
||||||
"regardless",
|
"regardless",
|
||||||
"register",
|
"register",
|
||||||
"reinterpret_cast",
|
"reinterpret_cast",
|
||||||
"requires",
|
|
||||||
"resource",
|
"resource",
|
||||||
"restrict",
|
"restrict",
|
||||||
"self",
|
"self",
|
||||||
|
|
|
@ -181,6 +181,8 @@ std::string_view Token::TypeToName(Type type) {
|
||||||
return "override";
|
return "override";
|
||||||
case Token::Type::kReturn:
|
case Token::Type::kReturn:
|
||||||
return "return";
|
return "return";
|
||||||
|
case Token::Type::kRequires:
|
||||||
|
return "requires";
|
||||||
case Token::Type::kStruct:
|
case Token::Type::kStruct:
|
||||||
return "struct";
|
return "struct";
|
||||||
case Token::Type::kSwitch:
|
case Token::Type::kSwitch:
|
||||||
|
|
|
@ -193,6 +193,8 @@ class Token {
|
||||||
kOverride,
|
kOverride,
|
||||||
/// A 'return'
|
/// A 'return'
|
||||||
kReturn,
|
kReturn,
|
||||||
|
/// A 'requires'
|
||||||
|
kRequires,
|
||||||
/// A 'struct'
|
/// A 'struct'
|
||||||
kStruct,
|
kStruct,
|
||||||
/// A 'switch'
|
/// A 'switch'
|
||||||
|
|
Loading…
Reference in New Issue