dawn-cmake/src/tint/reader/wgsl/parser_impl_function_decl_test.cc
Ben Clayton ce31d187ef tint/ast: Change Function::symbol to Function::name
Function::name is an ast::Identifier.

The goal here is to have all AST nodes use an identifier instead of
symbols directly. This will greatly simplify the renamer transform,
and gives the symbol a Source location, which is helpful for
diagnostics and tooling.

Change-Id: I723a9a104668758db2cb32051efa1f6d3c105913
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119280
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
2023-02-09 10:34:14 +00:00

306 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2020 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/ast/workgroup_attribute.h"
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
#include "src/tint/utils/string.h"
namespace tint::reader::wgsl {
namespace {
TEST_F(ParserImplTest, FunctionDecl) {
auto p = parser("fn main(a : i32, b : f32) { return; }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
EXPECT_EQ(f->return_type, nullptr);
ASSERT_EQ(f->params.Length(), 2u);
EXPECT_EQ(f->params[0]->symbol, p->builder().Symbols().Get("a"));
EXPECT_EQ(f->params[1]->symbol, p->builder().Symbols().Get("b"));
EXPECT_EQ(f->return_type, nullptr);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_Unicode) {
const std::string function_ident = // "𝗳𝘂𝗻𝗰𝘁𝗶𝗼𝗻"
"\xf0\x9d\x97\xb3\xf0\x9d\x98\x82\xf0\x9d\x97\xbb\xf0\x9d\x97\xb0\xf0\x9d"
"\x98\x81\xf0\x9d\x97\xb6\xf0\x9d\x97\xbc\xf0\x9d\x97\xbb";
const std::string param_a_ident = // "𝓹𝓪𝓻𝓪𝓶_𝓪"
"\xf0\x9d\x93\xb9\xf0\x9d\x93\xaa\xf0\x9d\x93\xbb\xf0\x9d\x93\xaa\xf0\x9d"
"\x93\xb6\x5f\xf0\x9d\x93\xaa";
const std::string param_b_ident = // "𝕡𝕒𝕣𝕒𝕞_𝕓"
"\xf0\x9d\x95\xa1\xf0\x9d\x95\x92\xf0\x9d\x95\xa3\xf0\x9d\x95\x92\xf0\x9d"
"\x95\x9e\x5f\xf0\x9d\x95\x93";
std::string src = "fn $function($param_a : i32, $param_b : f32) { return; }";
src = utils::ReplaceAll(src, "$function", function_ident);
src = utils::ReplaceAll(src, "$param_a", param_a_ident);
src = utils::ReplaceAll(src, "$param_b", param_b_ident);
auto p = parser(src);
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get(function_ident));
EXPECT_EQ(f->return_type, nullptr);
ASSERT_EQ(f->params.Length(), 2u);
EXPECT_EQ(f->params[0]->symbol, p->builder().Symbols().Get(param_a_ident));
EXPECT_EQ(f->params[1]->symbol, p->builder().Symbols().Get(param_b_ident));
EXPECT_EQ(f->return_type, nullptr);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_AttributeList) {
auto p = parser("@workgroup_size(2, 3, 4) fn main() { return; }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
ASSERT_TRUE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
EXPECT_EQ(f->return_type, nullptr);
ASSERT_EQ(f->params.Length(), 0u);
auto& attributes = f->attributes;
ASSERT_EQ(attributes.Length(), 1u);
ASSERT_TRUE(attributes[0]->Is<ast::WorkgroupAttribute>());
auto values = attributes[0]->As<ast::WorkgroupAttribute>()->Values();
ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 2);
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[1]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->value, 3);
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[2]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->value, 4);
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_AttributeList_MultipleEntries) {
auto p = parser(R"(
@workgroup_size(2, 3, 4) @compute
fn main() { return; })");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
ASSERT_TRUE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
EXPECT_EQ(f->return_type, nullptr);
ASSERT_EQ(f->params.Length(), 0u);
auto& attributes = f->attributes;
ASSERT_EQ(attributes.Length(), 2u);
ASSERT_TRUE(attributes[0]->Is<ast::WorkgroupAttribute>());
auto values = attributes[0]->As<ast::WorkgroupAttribute>()->Values();
ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 2);
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[1]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->value, 3);
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[2]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->value, 4);
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(attributes[1]->Is<ast::StageAttribute>());
EXPECT_EQ(attributes[1]->As<ast::StageAttribute>()->stage, ast::PipelineStage::kCompute);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_AttributeList_MultipleLists) {
auto p = parser(R"(
@workgroup_size(2, 3, 4)
@compute
fn main() { return; })");
auto attributes = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attributes.errored);
ASSERT_TRUE(attributes.matched);
auto f = p->function_decl(attributes.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
EXPECT_EQ(f->return_type, nullptr);
ASSERT_EQ(f->params.Length(), 0u);
auto& attrs = f->attributes;
ASSERT_EQ(attrs.Length(), 2u);
ASSERT_TRUE(attrs[0]->Is<ast::WorkgroupAttribute>());
auto values = attrs[0]->As<ast::WorkgroupAttribute>()->Values();
ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 2);
EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[1]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->value, 3);
EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(values[2]->Is<ast::IntLiteralExpression>());
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->value, 4);
EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->suffix,
ast::IntLiteralExpression::Suffix::kNone);
ASSERT_TRUE(attrs[1]->Is<ast::StageAttribute>());
EXPECT_EQ(attrs[1]->As<ast::StageAttribute>()->stage, ast::PipelineStage::kCompute);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_ReturnTypeAttributeList) {
auto p = parser("fn main() -> @location(1) f32 { return 1.0; }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_FALSE(p->has_error()) << p->error();
EXPECT_FALSE(f.errored);
EXPECT_TRUE(f.matched);
ASSERT_NE(f.value, nullptr);
EXPECT_EQ(f->name->symbol, p->builder().Symbols().Get("main"));
ASSERT_NE(f->return_type, nullptr);
EXPECT_TRUE(f->return_type->Is<ast::F32>());
ASSERT_EQ(f->params.Length(), 0u);
auto& attributes = f->attributes;
EXPECT_EQ(attributes.Length(), 0u);
auto& ret_type_attributes = f->return_type_attributes;
ASSERT_EQ(ret_type_attributes.Length(), 1u);
auto* loc = ret_type_attributes[0]->As<ast::LocationAttribute>();
ASSERT_TRUE(loc != nullptr);
EXPECT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
EXPECT_EQ(1u, exp->value);
auto* body = f->body;
ASSERT_EQ(body->statements.Length(), 1u);
EXPECT_TRUE(body->statements[0]->Is<ast::ReturnStatement>());
}
TEST_F(ParserImplTest, FunctionDecl_InvalidHeader) {
auto p = parser("fn main() -> { }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_TRUE(f.errored);
EXPECT_FALSE(f.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(f.value, nullptr);
EXPECT_EQ(p->error(), "1:14: unable to determine function return type");
}
TEST_F(ParserImplTest, FunctionDecl_InvalidBody) {
auto p = parser("fn main() { return }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_TRUE(f.errored);
EXPECT_FALSE(f.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(f.value, nullptr);
EXPECT_EQ(p->error(), "1:20: expected ';' for return statement");
}
TEST_F(ParserImplTest, FunctionDecl_MissingLeftBrace) {
auto p = parser("fn main() return; }");
auto attrs = p->attribute_list();
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(attrs.errored);
EXPECT_FALSE(attrs.matched);
auto f = p->function_decl(attrs.value);
EXPECT_TRUE(f.errored);
EXPECT_FALSE(f.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(f.value, nullptr);
EXPECT_EQ(p->error(), "1:11: expected '{' for function body");
}
} // namespace
} // namespace tint::reader::wgsl