Start cleaning up tests (4/N)
Remove Source{} with ast::Builder::create<> Use Builder helpers where possible Change-Id: I07fdefdbbcc4a9289069e2e494f475c7baea7531 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35741 Auto-Submit: Ben Clayton <bclayton@google.com> Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
983e8dd938
commit
7e805ba44a
|
@ -75,6 +75,18 @@ Variable* Builder::Const(const std::string& name,
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variable* Builder::Const(const Source& source,
|
||||||
|
const std::string& name,
|
||||||
|
StorageClass storage,
|
||||||
|
type::Type* type,
|
||||||
|
Expression* constructor,
|
||||||
|
VariableDecorationList decorations) {
|
||||||
|
auto* var = create<Variable>(source, name, storage, type, true, constructor,
|
||||||
|
decorations);
|
||||||
|
OnVariableBuilt(var);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
BuilderWithModule::BuilderWithModule() : Builder(new Module()) {}
|
BuilderWithModule::BuilderWithModule() : Builder(new Module()) {}
|
||||||
|
|
||||||
BuilderWithModule::~BuilderWithModule() {
|
BuilderWithModule::~BuilderWithModule() {
|
||||||
|
|
|
@ -499,6 +499,20 @@ class Builder {
|
||||||
Expression* constructor,
|
Expression* constructor,
|
||||||
VariableDecorationList decorations);
|
VariableDecorationList decorations);
|
||||||
|
|
||||||
|
/// @param source the variable source
|
||||||
|
/// @param name the variable name
|
||||||
|
/// @param storage the variable storage class
|
||||||
|
/// @param type the variable type
|
||||||
|
/// @param constructor optional constructor expression
|
||||||
|
/// @param decorations optional variable decorations
|
||||||
|
/// @returns a constant `Variable` with the given name, storage and type
|
||||||
|
Variable* Const(const Source& source,
|
||||||
|
const std::string& name,
|
||||||
|
StorageClass storage,
|
||||||
|
type::Type* type,
|
||||||
|
Expression* constructor,
|
||||||
|
VariableDecorationList decorations);
|
||||||
|
|
||||||
/// @param func the function name
|
/// @param func the function name
|
||||||
/// @param args the function call arguments
|
/// @param args the function call arguments
|
||||||
/// @returns a `CallExpression` to the function `func`, with the
|
/// @returns a `CallExpression` to the function `func`, with the
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "src/ast/builder.h"
|
||||||
#include "src/ast/function.h"
|
#include "src/ast/function.h"
|
||||||
#include "src/ast/pipeline_stage.h"
|
#include "src/ast/pipeline_stage.h"
|
||||||
#include "src/ast/stage_decoration.h"
|
#include "src/ast/stage_decoration.h"
|
||||||
|
@ -34,10 +35,9 @@ namespace tint {
|
||||||
namespace transform {
|
namespace transform {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class VertexPullingHelper {
|
class VertexPullingHelper : public ast::BuilderWithModule {
|
||||||
public:
|
public:
|
||||||
VertexPullingHelper() {
|
VertexPullingHelper() {
|
||||||
mod_ = std::make_unique<ast::Module>();
|
|
||||||
manager_ = std::make_unique<Manager>();
|
manager_ = std::make_unique<Manager>();
|
||||||
auto transform = std::make_unique<VertexPulling>();
|
auto transform = std::make_unique<VertexPulling>();
|
||||||
transform_ = transform.get();
|
transform_ = transform.get();
|
||||||
|
@ -47,19 +47,19 @@ class VertexPullingHelper {
|
||||||
// Create basic module with an entry point and vertex function
|
// Create basic module with an entry point and vertex function
|
||||||
void InitBasicModule() {
|
void InitBasicModule() {
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("main"), "main", ast::VariableList{},
|
mod->RegisterSymbol("main"), "main", ast::VariableList{},
|
||||||
mod_->create<ast::type::Void>(),
|
mod->create<ast::type::Void>(),
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{}),
|
create<ast::BlockStatement>(ast::StatementList{}),
|
||||||
ast::FunctionDecorationList{create<ast::StageDecoration>(
|
ast::FunctionDecorationList{
|
||||||
Source{}, ast::PipelineStage::kVertex)});
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the transformation, after building the module
|
// Set up the transformation, after building the module
|
||||||
void InitTransform(VertexStateDescriptor vertex_state) {
|
void InitTransform(VertexStateDescriptor vertex_state) {
|
||||||
EXPECT_TRUE(mod_->IsValid());
|
EXPECT_TRUE(mod->IsValid());
|
||||||
|
|
||||||
TypeDeterminer td(mod_.get());
|
TypeDeterminer td(mod);
|
||||||
EXPECT_TRUE(td.Determine());
|
EXPECT_TRUE(td.Determine());
|
||||||
|
|
||||||
transform_->SetVertexState(vertex_state);
|
transform_->SetVertexState(vertex_state);
|
||||||
|
@ -70,37 +70,18 @@ class VertexPullingHelper {
|
||||||
void AddVertexInputVariable(uint32_t location,
|
void AddVertexInputVariable(uint32_t location,
|
||||||
std::string name,
|
std::string name,
|
||||||
ast::type::Type* type) {
|
ast::type::Type* type) {
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var(name, ast::StorageClass::kInput, type, nullptr,
|
||||||
Source{}, // source
|
ast::VariableDecorationList{
|
||||||
name, // name
|
create<ast::LocationDecoration>(location),
|
||||||
ast::StorageClass::kInput, // storage_class
|
});
|
||||||
type, // type
|
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{
|
|
||||||
// decorations
|
|
||||||
create<ast::LocationDecoration>(Source{}, location),
|
|
||||||
});
|
|
||||||
|
|
||||||
mod_->AddGlobalVariable(var);
|
mod->AddGlobalVariable(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Module* mod() { return mod_.get(); }
|
|
||||||
|
|
||||||
Manager* manager() { return manager_.get(); }
|
Manager* manager() { return manager_.get(); }
|
||||||
VertexPulling* transform() { return transform_; }
|
VertexPulling* transform() { return transform_; }
|
||||||
|
|
||||||
/// Creates a new `ast::Node` owned by the Module. When the Module is
|
|
||||||
/// destructed, the `ast::Node` will also be destructed.
|
|
||||||
/// @param args the arguments to pass to the type constructor
|
|
||||||
/// @returns the node pointer
|
|
||||||
template <typename T, typename... ARGS>
|
|
||||||
T* create(ARGS&&... args) {
|
|
||||||
return mod_->create<T>(std::forward<ARGS>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ast::Module> mod_;
|
|
||||||
std::unique_ptr<Manager> manager_;
|
std::unique_ptr<Manager> manager_;
|
||||||
VertexPulling* transform_;
|
VertexPulling* transform_;
|
||||||
};
|
};
|
||||||
|
@ -108,7 +89,7 @@ class VertexPullingHelper {
|
||||||
class VertexPullingTest : public VertexPullingHelper, public testing::Test {};
|
class VertexPullingTest : public VertexPullingHelper, public testing::Test {};
|
||||||
|
|
||||||
TEST_F(VertexPullingTest, Error_NoVertexState) {
|
TEST_F(VertexPullingTest, Error_NoVertexState) {
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
EXPECT_TRUE(result.diagnostics.contains_errors());
|
EXPECT_TRUE(result.diagnostics.contains_errors());
|
||||||
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
||||||
"error: SetVertexState not called");
|
"error: SetVertexState not called");
|
||||||
|
@ -116,7 +97,7 @@ TEST_F(VertexPullingTest, Error_NoVertexState) {
|
||||||
|
|
||||||
TEST_F(VertexPullingTest, Error_NoEntryPoint) {
|
TEST_F(VertexPullingTest, Error_NoEntryPoint) {
|
||||||
transform()->SetVertexState({});
|
transform()->SetVertexState({});
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
EXPECT_TRUE(result.diagnostics.contains_errors());
|
EXPECT_TRUE(result.diagnostics.contains_errors());
|
||||||
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
||||||
"error: Vertex stage entry point not found");
|
"error: Vertex stage entry point not found");
|
||||||
|
@ -127,7 +108,7 @@ TEST_F(VertexPullingTest, Error_InvalidEntryPoint) {
|
||||||
InitTransform({});
|
InitTransform({});
|
||||||
transform()->SetEntryPoint("_");
|
transform()->SetEntryPoint("_");
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
EXPECT_TRUE(result.diagnostics.contains_errors());
|
EXPECT_TRUE(result.diagnostics.contains_errors());
|
||||||
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
||||||
"error: Vertex stage entry point not found");
|
"error: Vertex stage entry point not found");
|
||||||
|
@ -135,16 +116,16 @@ TEST_F(VertexPullingTest, Error_InvalidEntryPoint) {
|
||||||
|
|
||||||
TEST_F(VertexPullingTest, Error_EntryPointWrongStage) {
|
TEST_F(VertexPullingTest, Error_EntryPointWrongStage) {
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("main"), "main", ast::VariableList{},
|
mod->RegisterSymbol("main"), "main", ast::VariableList{},
|
||||||
mod()->create<ast::type::Void>(),
|
mod->create<ast::type::Void>(),
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{}),
|
create<ast::BlockStatement>(ast::StatementList{}),
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
|
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
InitTransform({});
|
InitTransform({});
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
EXPECT_TRUE(result.diagnostics.contains_errors());
|
EXPECT_TRUE(result.diagnostics.contains_errors());
|
||||||
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
EXPECT_EQ(diag::Formatter().format(result.diagnostics),
|
||||||
"error: Vertex stage entry point not found");
|
"error: Vertex stage entry point not found");
|
||||||
|
@ -153,7 +134,7 @@ TEST_F(VertexPullingTest, Error_EntryPointWrongStage) {
|
||||||
TEST_F(VertexPullingTest, BasicModule) {
|
TEST_F(VertexPullingTest, BasicModule) {
|
||||||
InitBasicModule();
|
InitBasicModule();
|
||||||
InitTransform({});
|
InitTransform({});
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +147,7 @@ TEST_F(VertexPullingTest, OneAttribute) {
|
||||||
|
|
||||||
InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}});
|
InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}});
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
@ -253,7 +234,7 @@ TEST_F(VertexPullingTest, OneInstancedAttribute) {
|
||||||
InitTransform(
|
InitTransform(
|
||||||
{{{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 0}}}}});
|
{{{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 0}}}}});
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
@ -340,7 +321,7 @@ TEST_F(VertexPullingTest, OneAttributeDifferentOutputSet) {
|
||||||
InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}});
|
InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}});
|
||||||
transform()->SetPullingBufferBindingSet(5);
|
transform()->SetPullingBufferBindingSet(5);
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
@ -428,35 +409,23 @@ TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex) {
|
||||||
|
|
||||||
ast::type::I32 i32;
|
ast::type::I32 i32;
|
||||||
|
|
||||||
mod()->AddGlobalVariable(create<ast::Variable>(
|
mod->AddGlobalVariable(
|
||||||
Source{}, // source
|
Var("custom_vertex_index", ast::StorageClass::kInput, &i32, nullptr,
|
||||||
"custom_vertex_index", // name
|
ast::VariableDecorationList{
|
||||||
ast::StorageClass::kInput, // storage_class
|
create<ast::BuiltinDecoration>(ast::Builtin::kVertexIdx),
|
||||||
&i32, // type
|
}));
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{
|
|
||||||
// decorations
|
|
||||||
create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kVertexIdx),
|
|
||||||
}));
|
|
||||||
|
|
||||||
mod()->AddGlobalVariable(create<ast::Variable>(
|
mod->AddGlobalVariable(
|
||||||
Source{}, // source
|
Var("custom_instance_index", ast::StorageClass::kInput, &i32, nullptr,
|
||||||
"custom_instance_index", // name
|
ast::VariableDecorationList{
|
||||||
ast::StorageClass::kInput, // storage_class
|
create<ast::BuiltinDecoration>(ast::Builtin::kInstanceIdx),
|
||||||
&i32, // type
|
}));
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{
|
|
||||||
// decorations
|
|
||||||
create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kInstanceIdx),
|
|
||||||
}));
|
|
||||||
|
|
||||||
InitTransform(
|
InitTransform(
|
||||||
{{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}},
|
{{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}},
|
||||||
{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}}}});
|
{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}}}});
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
@ -598,7 +567,7 @@ TEST_F(VertexPullingTest, TwoAttributesSameBuffer) {
|
||||||
InputStepMode::kVertex,
|
InputStepMode::kVertex,
|
||||||
{{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}});
|
{{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}});
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
@ -785,7 +754,7 @@ TEST_F(VertexPullingTest, FloatVectorAttributes) {
|
||||||
{12, InputStepMode::kVertex, {{VertexFormat::kVec3F32, 0, 1}}},
|
{12, InputStepMode::kVertex, {{VertexFormat::kVec3F32, 0, 1}}},
|
||||||
{16, InputStepMode::kVertex, {{VertexFormat::kVec4F32, 0, 2}}}}});
|
{16, InputStepMode::kVertex, {{VertexFormat::kVec4F32, 0, 2}}}}});
|
||||||
|
|
||||||
auto result = manager()->Run(mod());
|
auto result = manager()->Run(mod);
|
||||||
ASSERT_FALSE(result.diagnostics.contains_errors())
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||||||
<< diag::Formatter().format(result.diagnostics);
|
<< diag::Formatter().format(result.diagnostics);
|
||||||
|
|
||||||
|
|
|
@ -42,32 +42,20 @@ TEST_F(ValidateControlBlockTest, SwitchSelectorExpressionNoneIntegerType_Fail) {
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(3.14f),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &f32, 3.14f)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = create<ast::IdentifierExpression>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("a"), "a");
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("a"), "a");
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
|
||||||
ast::CaseStatementList body;
|
ast::CaseStatementList body;
|
||||||
body.push_back(
|
body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, body),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
|
@ -81,33 +69,21 @@ TEST_F(ValidateControlBlockTest, SwitchWithoutDefault_Fail) {
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
ast::CaseSelectorList csl;
|
ast::CaseSelectorList csl;
|
||||||
csl.push_back(create<ast::SintLiteral>(Source{}, &i32, 1));
|
csl.push_back(Literal(1));
|
||||||
ast::CaseStatementList body;
|
ast::CaseStatementList body;
|
||||||
body.push_back(create<ast::CaseStatement>(
|
body.push_back(create<ast::CaseStatement>(
|
||||||
Source{}, csl,
|
csl, create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(Source{Source::Location{12, 34}}, cond,
|
||||||
create<ast::SwitchStatement>(
|
body),
|
||||||
Source{Source::Location{12, 34}}, cond, body),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
|
@ -123,47 +99,32 @@ TEST_F(ValidateControlBlockTest, SwitchWithTwoDefault_Fail) {
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::CaseStatementList switch_body;
|
ast::CaseStatementList switch_body;
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl_1;
|
ast::CaseSelectorList default_csl_1;
|
||||||
auto* block_default_1 =
|
auto* block_default_1 = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
|
||||||
switch_body.push_back(
|
switch_body.push_back(
|
||||||
create<ast::CaseStatement>(Source{}, default_csl_1, block_default_1));
|
create<ast::CaseStatement>(default_csl_1, block_default_1));
|
||||||
|
|
||||||
ast::CaseSelectorList csl_case_1;
|
ast::CaseSelectorList csl_case_1;
|
||||||
csl_case_1.push_back(create<ast::SintLiteral>(Source{}, &i32, 1));
|
csl_case_1.push_back(Literal(1));
|
||||||
auto* block_case_1 =
|
auto* block_case_1 = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
switch_body.push_back(create<ast::CaseStatement>(csl_case_1, block_case_1));
|
||||||
switch_body.push_back(
|
|
||||||
create<ast::CaseStatement>(Source{}, csl_case_1, block_case_1));
|
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl_2;
|
ast::CaseSelectorList default_csl_2;
|
||||||
auto* block_default_2 =
|
auto* block_default_2 = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
|
||||||
switch_body.push_back(
|
switch_body.push_back(
|
||||||
create<ast::CaseStatement>(Source{}, default_csl_2, block_default_2));
|
create<ast::CaseStatement>(default_csl_2, block_default_2));
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(Source{Source::Location{12, 34}}, cond,
|
||||||
create<ast::SwitchStatement>(
|
switch_body),
|
||||||
Source{Source::Location{12, 34}}, cond, switch_body),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
|
@ -179,40 +140,26 @@ TEST_F(ValidateControlBlockTest,
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::U32 u32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
ast::type::I32 i32;
|
ast::VariableDecorationList{});
|
||||||
auto* var = create<ast::Variable>(
|
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::CaseStatementList switch_body;
|
ast::CaseStatementList switch_body;
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl;
|
ast::CaseSelectorList csl;
|
||||||
csl.push_back(create<ast::UintLiteral>(Source{}, &u32, 1));
|
csl.push_back(create<ast::UintLiteral>(ty.u32, 1));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{Source::Location{12, 34}}, csl,
|
Source{Source::Location{12, 34}}, csl,
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
switch_body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
switch_body.push_back(
|
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, switch_body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, switch_body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -227,40 +174,28 @@ TEST_F(ValidateControlBlockTest,
|
||||||
// case -1: {}
|
// case -1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::U32 u32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.u32,
|
||||||
ast::type::I32 i32;
|
create<ast::ScalarConstructorExpression>(
|
||||||
auto* var = create<ast::Variable>(
|
create<ast::UintLiteral>(ty.u32, 2)),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&u32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::UintLiteral>(Source{}, &u32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::CaseStatementList switch_body;
|
ast::CaseStatementList switch_body;
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl;
|
ast::CaseSelectorList csl;
|
||||||
csl.push_back(create<ast::SintLiteral>(Source{}, &i32, -1));
|
csl.push_back(Literal(-1));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{Source::Location{12, 34}}, csl,
|
Source{Source::Location{12, 34}}, csl,
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
switch_body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
switch_body.push_back(
|
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, switch_body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, switch_body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -275,46 +210,34 @@ TEST_F(ValidateControlBlockTest, NonUniqueCaseSelectorValueUint_Fail) {
|
||||||
// case 2, 2: {}
|
// case 2, 2: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::U32 u32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.u32,
|
||||||
auto* var = create<ast::Variable>(
|
create<ast::ScalarConstructorExpression>(
|
||||||
Source{}, // source
|
create<ast::UintLiteral>(ty.u32, 3)),
|
||||||
"a", // name
|
ast::VariableDecorationList{});
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&u32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::UintLiteral>(Source{}, &u32, 3)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::CaseStatementList switch_body;
|
ast::CaseStatementList switch_body;
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl_1;
|
ast::CaseSelectorList csl_1;
|
||||||
csl_1.push_back(create<ast::UintLiteral>(Source{}, &u32, 0));
|
csl_1.push_back(create<ast::UintLiteral>(ty.u32, 0));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{}, csl_1,
|
csl_1, create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl_2;
|
ast::CaseSelectorList csl_2;
|
||||||
csl_2.push_back(create<ast::UintLiteral>(Source{}, &u32, 2));
|
csl_2.push_back(create<ast::UintLiteral>(ty.u32, 2));
|
||||||
csl_2.push_back(create<ast::UintLiteral>(Source{}, &u32, 2));
|
csl_2.push_back(create<ast::UintLiteral>(ty.u32, 2));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{Source::Location{12, 34}}, csl_2,
|
Source{Source::Location{12, 34}}, csl_2,
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
switch_body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
switch_body.push_back(
|
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, switch_body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, switch_body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -329,48 +252,34 @@ TEST_F(ValidateControlBlockTest, NonUniqueCaseSelectorValueSint_Fail) {
|
||||||
// case 0,1,2,10: {}
|
// case 0,1,2,10: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::CaseStatementList switch_body;
|
ast::CaseStatementList switch_body;
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl_1;
|
ast::CaseSelectorList csl_1;
|
||||||
csl_1.push_back(create<ast::SintLiteral>(Source{}, &i32, 10));
|
csl_1.push_back(Literal(10));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{}, csl_1,
|
csl_1, create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
|
||||||
|
|
||||||
ast::CaseSelectorList csl_2;
|
ast::CaseSelectorList csl_2;
|
||||||
csl_2.push_back(create<ast::SintLiteral>(Source{}, &i32, 0));
|
csl_2.push_back(Literal(0));
|
||||||
csl_2.push_back(create<ast::SintLiteral>(Source{}, &i32, 1));
|
csl_2.push_back(Literal(1));
|
||||||
csl_2.push_back(create<ast::SintLiteral>(Source{}, &i32, 2));
|
csl_2.push_back(Literal(2));
|
||||||
csl_2.push_back(create<ast::SintLiteral>(Source{}, &i32, 10));
|
csl_2.push_back(Literal(10));
|
||||||
switch_body.push_back(create<ast::CaseStatement>(
|
switch_body.push_back(create<ast::CaseStatement>(
|
||||||
Source{Source::Location{12, 34}}, csl_2,
|
Source{Source::Location{12, 34}}, csl_2,
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{})));
|
create<ast::BlockStatement>(ast::StatementList{})));
|
||||||
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
switch_body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
switch_body.push_back(
|
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, switch_body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, switch_body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -383,35 +292,23 @@ TEST_F(ValidateControlBlockTest, LastClauseLastStatementIsFallthrough_Fail) {
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// default: { fallthrough; }
|
// default: { fallthrough; }
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default = create<ast::BlockStatement>(
|
auto* block_default = create<ast::BlockStatement>(
|
||||||
Source{},
|
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
create<ast::FallthroughStatement>(Source{Source::Location{12, 34}}),
|
create<ast::FallthroughStatement>(Source{Source::Location{12, 34}}),
|
||||||
});
|
});
|
||||||
ast::CaseStatementList body;
|
ast::CaseStatementList body;
|
||||||
body.push_back(
|
body.push_back(create<ast::CaseStatement>(default_csl, block_default));
|
||||||
create<ast::CaseStatement>(Source{}, default_csl, block_default));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -425,37 +322,24 @@ TEST_F(ValidateControlBlockTest, SwitchCase_Pass) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// case 5: {}
|
// case 5: {}
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
|
||||||
ast::CaseStatementList body;
|
ast::CaseStatementList body;
|
||||||
body.push_back(create<ast::CaseStatement>(Source{Source::Location{12, 34}},
|
body.push_back(create<ast::CaseStatement>(Source{Source::Location{12, 34}},
|
||||||
default_csl, block_default));
|
default_csl, block_default));
|
||||||
ast::CaseSelectorList case_csl;
|
ast::CaseSelectorList case_csl;
|
||||||
case_csl.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
|
case_csl.push_back(Literal(5));
|
||||||
auto* block_case =
|
auto* block_case = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
body.push_back(create<ast::CaseStatement>(case_csl, block_case));
|
||||||
body.push_back(create<ast::CaseStatement>(Source{}, case_csl, block_case));
|
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, body),
|
});
|
||||||
});
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_TRUE(v()->ValidateStatements(block)) << v()->error();
|
EXPECT_TRUE(v()->ValidateStatements(block)) << v()->error();
|
||||||
}
|
}
|
||||||
|
@ -467,35 +351,23 @@ TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ast::type::U32 u32;
|
ast::type::Alias my_int{mod->RegisterSymbol("MyInt"), "MyInt", ty.u32};
|
||||||
ast::type::Alias my_int{mod()->RegisterSymbol("MyInt"), "MyInt", &u32};
|
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var("a", ast::StorageClass::kNone, &my_int, Expr(2u),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&my_int, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &u32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* cond = create<ast::IdentifierExpression>(
|
auto* cond = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
ast::CaseSelectorList default_csl;
|
ast::CaseSelectorList default_csl;
|
||||||
auto* block_default =
|
auto* block_default = create<ast::BlockStatement>(ast::StatementList{});
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{});
|
|
||||||
ast::CaseStatementList body;
|
ast::CaseStatementList body;
|
||||||
body.push_back(create<ast::CaseStatement>(Source{Source::Location{12, 34}},
|
body.push_back(create<ast::CaseStatement>(Source{Source::Location{12, 34}},
|
||||||
default_csl, block_default));
|
default_csl, block_default));
|
||||||
|
|
||||||
auto* block = create<ast::BlockStatement>(
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::SwitchStatement>(cond, body),
|
||||||
create<ast::SwitchStatement>(Source{}, cond, body),
|
});
|
||||||
});
|
mod->AddConstructedType(&my_int);
|
||||||
mod()->AddConstructedType(&my_int);
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
EXPECT_TRUE(v()->ValidateStatements(block)) << v()->error();
|
EXPECT_TRUE(v()->ValidateStatements(block)) << v()->error();
|
||||||
|
|
|
@ -38,100 +38,74 @@ class ValidateFunctionTest : public ValidatorTestHelper,
|
||||||
TEST_F(ValidateFunctionTest, VoidFunctionEndWithoutReturnStatement_Pass) {
|
TEST_F(ValidateFunctionTest, VoidFunctionEndWithoutReturnStatement_Pass) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn func -> void { var a:i32 = 2; }
|
// fn func -> void { var a:i32 = 2; }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
ast::type::Void void_type;
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("func"), "func",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("func"), "func",
|
||||||
params, &void_type, body,
|
params, ty.void_, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod()));
|
EXPECT_TRUE(v()->Validate(mod));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest,
|
TEST_F(ValidateFunctionTest,
|
||||||
VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
|
VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn func -> void {}
|
// fn func -> void {}
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("func"), "func",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("func"), "func",
|
||||||
params, &void_type,
|
params, ty.void_, create<ast::BlockStatement>(ast::StatementList{}),
|
||||||
create<ast::BlockStatement>(Source{}, ast::StatementList{}),
|
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod()));
|
EXPECT_TRUE(v()->Validate(mod));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
|
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||||
// fn func -> int { var a:i32 = 2; }
|
// fn func -> int { var a:i32 = 2; }
|
||||||
|
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
ast::type::Void void_type;
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("func"), "func",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("func"), "func",
|
||||||
params, &i32, body, ast::FunctionDecorationList{});
|
params, ty.i32, body, ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0002: non-void function must end with a return statement");
|
"12:34 v-0002: non-void function must end with a return statement");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||||
// fn func -> int {}
|
// fn func -> int {}
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("func"), "func",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("func"), "func",
|
||||||
params, &i32, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
|
params, ty.i32, create<ast::BlockStatement>(ast::StatementList{}),
|
||||||
ast::FunctionDecorationList{});
|
ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0002: non-void function must end with a return statement");
|
"12:34 v-0002: non-void function must end with a return statement");
|
||||||
}
|
}
|
||||||
|
@ -139,45 +113,39 @@ TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn func -> void { return; }
|
// fn func -> void { return; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("func"), "func", params, &void_type, body,
|
mod->RegisterSymbol("func"), "func", params, ty.void_, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineFunctions(mod()->functions())) << td()->error();
|
EXPECT_TRUE(td()->DetermineFunctions(mod->functions())) << td()->error();
|
||||||
EXPECT_TRUE(v()->ValidateFunctions(mod()->functions())) << v()->error();
|
EXPECT_TRUE(v()->ValidateFunctions(mod->functions())) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
||||||
// fn func -> void { return 2; }
|
// fn func -> void { return 2; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* return_expr = create<ast::ScalarConstructorExpression>(
|
auto* return_expr = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
|
||||||
create<ast::ReturnStatement>(
|
return_expr),
|
||||||
Source{Source::Location{12, 34}}, return_expr),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func = create<ast::Function>(Source{}, mod()->RegisterSymbol("func"),
|
auto* func =
|
||||||
"func", params, &void_type, body,
|
create<ast::Function>(mod->RegisterSymbol("func"), "func", params,
|
||||||
ast::FunctionDecorationList{});
|
ty.void_, body, ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
// TODO(sarahM0): replace 000y with a rule number
|
// TODO(sarahM0): replace 000y with a rule number
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-000y: return statement type must match its function "
|
"12:34 v-000y: return statement type must match its function "
|
||||||
|
@ -186,25 +154,21 @@ TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
||||||
// fn func -> f32 { return 2; }
|
// fn func -> f32 { return 2; }
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* return_expr = create<ast::ScalarConstructorExpression>(
|
auto* return_expr = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
|
||||||
create<ast::ReturnStatement>(
|
return_expr),
|
||||||
Source{Source::Location{12, 34}}, return_expr),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func =
|
auto* func =
|
||||||
create<ast::Function>(Source{}, mod()->RegisterSymbol("func"), "func",
|
create<ast::Function>(mod->RegisterSymbol("func"), "func", params, ty.f32,
|
||||||
params, &f32, body, ast::FunctionDecorationList{});
|
body, ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
// TODO(sarahM0): replace 000y with a rule number
|
// TODO(sarahM0): replace 000y with a rule number
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-000y: return statement type must match its function "
|
"12:34 v-000y: return statement type must match its function "
|
||||||
|
@ -214,127 +178,100 @@ TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
||||||
TEST_F(ValidateFunctionTest, FunctionNamesMustBeUnique_fail) {
|
TEST_F(ValidateFunctionTest, FunctionNamesMustBeUnique_fail) {
|
||||||
// fn func -> i32 { return 2; }
|
// fn func -> i32 { return 2; }
|
||||||
// fn func -> i32 { return 2; }
|
// fn func -> i32 { return 2; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* return_expr = create<ast::ScalarConstructorExpression>(
|
auto* return_expr = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(return_expr),
|
||||||
create<ast::ReturnStatement>(Source{}, return_expr),
|
});
|
||||||
});
|
|
||||||
auto* func =
|
auto* func =
|
||||||
create<ast::Function>(Source{}, mod()->RegisterSymbol("func"), "func",
|
create<ast::Function>(mod->RegisterSymbol("func"), "func", params, ty.i32,
|
||||||
params, &i32, body, ast::FunctionDecorationList{});
|
body, ast::FunctionDecorationList{});
|
||||||
|
|
||||||
ast::VariableList params_copy;
|
ast::VariableList params_copy;
|
||||||
auto* return_expr_copy = create<ast::ScalarConstructorExpression>(
|
auto* return_expr_copy = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
auto* body_copy = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body_copy = create<ast::BlockStatement>(
|
create<ast::ReturnStatement>(return_expr_copy),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::ReturnStatement>(Source{}, return_expr_copy),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* func_copy = create<ast::Function>(
|
auto* func_copy = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("func"), "func",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("func"), "func",
|
||||||
params_copy, &i32, body_copy, ast::FunctionDecorationList{});
|
params_copy, ty.i32, body_copy, ast::FunctionDecorationList{});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
mod()->AddFunction(func_copy);
|
mod->AddFunction(func_copy);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0016: function names must be unique 'func'");
|
EXPECT_EQ(v()->error(), "12:34 v-0016: function names must be unique 'func'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, RecursionIsNotAllowed_Fail) {
|
TEST_F(ValidateFunctionTest, RecursionIsNotAllowed_Fail) {
|
||||||
// fn func() -> void {func(); return; }
|
// fn func() -> void {func(); return; }
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::ExpressionList call_params;
|
ast::ExpressionList call_params;
|
||||||
auto* call_expr = create<ast::CallExpression>(
|
auto* call_expr = create<ast::CallExpression>(
|
||||||
Source{Source::Location{12, 34}},
|
Source{Source::Location{12, 34}}, Expr("func"), call_params);
|
||||||
create<ast::IdentifierExpression>(Source{}, mod()->RegisterSymbol("func"),
|
|
||||||
"func"),
|
|
||||||
call_params);
|
|
||||||
ast::VariableList params0;
|
ast::VariableList params0;
|
||||||
auto* body0 = create<ast::BlockStatement>(
|
auto* body0 = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::CallStatement>(call_expr),
|
||||||
create<ast::CallStatement>(Source{}, call_expr),
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
auto* func0 =
|
||||||
auto* func0 = create<ast::Function>(Source{}, mod()->RegisterSymbol("func"),
|
create<ast::Function>(mod->RegisterSymbol("func"), "func", params0,
|
||||||
"func", params0, &f32, body0,
|
ty.f32, body0, ast::FunctionDecorationList{});
|
||||||
ast::FunctionDecorationList{});
|
mod->AddFunction(func0);
|
||||||
mod()->AddFunction(func0);
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
EXPECT_FALSE(v()->Validate(mod)) << v()->error();
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0004: recursion is not allowed: 'func'");
|
EXPECT_EQ(v()->error(), "12:34 v-0004: recursion is not allowed: 'func'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, RecursionIsNotAllowedExpr_Fail) {
|
TEST_F(ValidateFunctionTest, RecursionIsNotAllowedExpr_Fail) {
|
||||||
// fn func() -> i32 {var a: i32 = func(); return 2; }
|
// fn func() -> i32 {var a: i32 = func(); return 2; }
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::ExpressionList call_params;
|
ast::ExpressionList call_params;
|
||||||
auto* call_expr = create<ast::CallExpression>(
|
auto* call_expr = create<ast::CallExpression>(
|
||||||
Source{Source::Location{12, 34}},
|
Source{Source::Location{12, 34}}, Expr("func"), call_params);
|
||||||
create<ast::IdentifierExpression>(Source{}, mod()->RegisterSymbol("func"),
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, call_expr,
|
||||||
"func"),
|
ast::VariableDecorationList{});
|
||||||
call_params);
|
|
||||||
auto* var =
|
|
||||||
create<ast::Variable>(Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
call_expr, // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::VariableList params0;
|
ast::VariableList params0;
|
||||||
auto* return_expr = create<ast::ScalarConstructorExpression>(
|
auto* return_expr = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body0 = create<ast::BlockStatement>(
|
auto* body0 = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::ReturnStatement>(return_expr),
|
||||||
create<ast::ReturnStatement>(Source{}, return_expr),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func0 = create<ast::Function>(Source{}, mod()->RegisterSymbol("func"),
|
auto* func0 =
|
||||||
"func", params0, &i32, body0,
|
create<ast::Function>(mod->RegisterSymbol("func"), "func", params0,
|
||||||
ast::FunctionDecorationList{});
|
ty.i32, body0, ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func0);
|
mod->AddFunction(func0);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
EXPECT_FALSE(v()->Validate(mod)) << v()->error();
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0004: recursion is not allowed: 'func'");
|
EXPECT_EQ(v()->error(), "12:34 v-0004: recursion is not allowed: 'func'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
|
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn vtx_main() -> i32 { return 0; }
|
// fn vtx_main() -> i32 { return 0; }
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* return_expr = create<ast::ScalarConstructorExpression>(
|
auto* return_expr = Expr(0);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 0));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(return_expr),
|
||||||
create<ast::ReturnStatement>(Source{}, return_expr),
|
});
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("vtx_main"),
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("vtx_main"),
|
||||||
"vtx_main", params, &i32, body,
|
"vtx_main", params, ty.i32, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0024: Entry point function must return void: 'vtx_main'");
|
"12:34 v-0024: Entry point function must return void: 'vtx_main'");
|
||||||
}
|
}
|
||||||
|
@ -342,31 +279,22 @@ TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
|
||||||
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_WithParams_Fail) {
|
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_WithParams_Fail) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn vtx_func(a : i32) -> void { return; }
|
// fn vtx_func(a : i32) -> void { return; }
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
params.push_back(
|
params.push_back(Var("a", ast::StorageClass::kNone, ty.i32, nullptr,
|
||||||
create<ast::Variable>(Source{}, // source
|
ast::VariableDecorationList{}));
|
||||||
"a", // name
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
ast::StorageClass::kNone, // storage_class
|
create<ast::ReturnStatement>(),
|
||||||
&i32, // type
|
});
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
|
||||||
Source{}, ast::StatementList{
|
|
||||||
create<ast::ReturnStatement>(Source{}),
|
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("vtx_func"),
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("vtx_func"),
|
||||||
"vtx_func", params, &void_type, body,
|
"vtx_func", params, ty.void_, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0023: Entry point function must accept no parameters: "
|
"12:34 v-0023: Entry point function must accept no parameters: "
|
||||||
"'vtx_func'");
|
"'vtx_func'");
|
||||||
|
@ -376,23 +304,21 @@ TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
|
||||||
// [[stage(fragment)]]
|
// [[stage(fragment)]]
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn main() -> void { return; }
|
// fn main() -> void { return; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("main"), "main",
|
Source{Source::Location{12, 34}}, mod->RegisterSymbol("main"), "main",
|
||||||
params, &void_type, body,
|
params, ty.void_, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
|
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
|
||||||
});
|
});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
v()->error(),
|
v()->error(),
|
||||||
"12:34 v-0020: only one stage decoration permitted per entry point");
|
"12:34 v-0020: only one stage decoration permitted per entry point");
|
||||||
|
@ -401,39 +327,34 @@ TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
|
||||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(vertex)]]
|
||||||
// fn vtx_func() -> void { return; }
|
// fn vtx_func() -> void { return; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("vtx_func"), "vtx_func", params,
|
mod->RegisterSymbol("vtx_func"), "vtx_func", params, ty.void_, body,
|
||||||
&void_type, body,
|
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod)) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
||||||
// fn vtx_func() -> void { return; }
|
// fn vtx_func() -> void { return; }
|
||||||
ast::type::Void void_type;
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
auto* func =
|
||||||
auto* func = create<ast::Function>(
|
create<ast::Function>(mod->RegisterSymbol("vtx_func"), "vtx_func", params,
|
||||||
Source{}, mod()->RegisterSymbol("vtx_func"), "vtx_func", params,
|
ty.void_, body, ast::FunctionDecorationList{});
|
||||||
&void_type, body, ast::FunctionDecorationList{});
|
mod->AddFunction(func);
|
||||||
mod()->AddFunction(func);
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"v-0003: At least one of vertex, fragment or compute shader must "
|
"v-0003: At least one of vertex, fragment or compute shader must "
|
||||||
"be present");
|
"be present");
|
||||||
|
|
|
@ -62,13 +62,11 @@ class ValidatorTest : public ValidatorTestHelper, public testing::Test {};
|
||||||
|
|
||||||
TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) {
|
TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) {
|
||||||
// 1 = my_var;
|
// 1 = my_var;
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
auto* lhs = create<ast::ScalarConstructorExpression>(
|
auto* lhs = Expr(1);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 1));
|
auto* rhs = Expr("my_var");
|
||||||
auto* rhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{}, mod()->RegisterSymbol("my_var"), "my_var");
|
create<ast::AssignmentStatement>(lhs, rhs);
|
||||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}}, lhs, rhs);
|
|
||||||
|
|
||||||
// TODO(sarahM0): Invalidate assignment to scalar.
|
// TODO(sarahM0): Invalidate assignment to scalar.
|
||||||
ASSERT_TRUE(v()->has_error());
|
ASSERT_TRUE(v()->has_error());
|
||||||
|
@ -78,14 +76,11 @@ TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) {
|
||||||
|
|
||||||
TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
|
TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
|
||||||
// b = 2;
|
// b = 2;
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("b"), "b");
|
auto* lhs = Expr("b");
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
auto* rhs = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
|
||||||
auto* assign = create<ast::AssignmentStatement>(
|
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs);
|
|
||||||
|
|
||||||
EXPECT_FALSE(td()->DetermineResultType(assign));
|
EXPECT_FALSE(td()->DetermineResultType(assign));
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
|
@ -96,18 +91,14 @@ TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
||||||
// {
|
// {
|
||||||
// b = 2;
|
// b = 2;
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("b"), "b");
|
auto* lhs = Expr("b");
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
auto* rhs = Expr(2);
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::AssignmentStatement>(lhs, rhs),
|
||||||
create<ast::AssignmentStatement>(
|
});
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_FALSE(td()->DetermineStatements(body));
|
EXPECT_FALSE(td()->DetermineStatements(body));
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
|
@ -117,29 +108,19 @@ TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
||||||
TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
|
TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
|
||||||
// var a :i32 = 2;
|
// var a :i32 = 2;
|
||||||
// a = 2
|
// a = 2
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
auto* rhs = Expr(2);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}}, lhs, rhs);
|
auto* assign = create<ast::AssignmentStatement>(
|
||||||
|
Source{Source::Location{12, 34}}, lhs, rhs);
|
||||||
td()->RegisterVariableForTesting(var);
|
td()->RegisterVariableForTesting(var);
|
||||||
EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error();
|
EXPECT_TRUE(td()->DetermineResultType(assign)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
ASSERT_NE(rhs->result_type(), nullptr);
|
ASSERT_NE(rhs->result_type(), nullptr);
|
||||||
EXPECT_TRUE(v()->ValidateResultTypes(&assign));
|
EXPECT_TRUE(v()->ValidateResultTypes(assign));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) {
|
TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) {
|
||||||
|
@ -147,32 +128,21 @@ TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) {
|
||||||
// var a :i32 = 2;
|
// var a :i32 = 2;
|
||||||
// a = 2.3;
|
// a = 2.3;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
auto* rhs = Expr(2.3f);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.3f));
|
|
||||||
|
|
||||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}}, lhs, rhs);
|
auto* assign = create<ast::AssignmentStatement>(
|
||||||
|
Source{Source::Location{12, 34}}, lhs, rhs);
|
||||||
td()->RegisterVariableForTesting(var);
|
td()->RegisterVariableForTesting(var);
|
||||||
EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error();
|
EXPECT_TRUE(td()->DetermineResultType(assign)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
ASSERT_NE(rhs->result_type(), nullptr);
|
ASSERT_NE(rhs->result_type(), nullptr);
|
||||||
|
|
||||||
EXPECT_FALSE(v()->ValidateResultTypes(&assign));
|
EXPECT_FALSE(v()->ValidateResultTypes(assign));
|
||||||
ASSERT_TRUE(v()->has_error());
|
ASSERT_TRUE(v()->has_error());
|
||||||
// TODO(sarahM0): figure out what should be the error number.
|
// TODO(sarahM0): figure out what should be the error number.
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -184,29 +154,17 @@ TEST_F(ValidatorTest, AssignCompatibleTypesInBlockStatement_Pass) {
|
||||||
// var a :i32 = 2;
|
// var a :i32 = 2;
|
||||||
// a = 2
|
// a = 2
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
auto* rhs = Expr(2);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
create<ast::AssignmentStatement>(
|
rhs),
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
|
@ -220,36 +178,24 @@ TEST_F(ValidatorTest, AssignIncompatibleTypesInBlockStatement_Fail) {
|
||||||
// var a :i32 = 2;
|
// var a :i32 = 2;
|
||||||
// a = 2.3;
|
// a = 2.3;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::I32 i32;
|
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.3f));
|
|
||||||
|
|
||||||
ast::BlockStatement block(
|
auto* lhs = Expr("a");
|
||||||
Source{}, ast::StatementList{
|
auto* rhs = Expr(2.3f);
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
|
||||||
create<ast::AssignmentStatement>(
|
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(&block)) << td()->error();
|
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||||
|
create<ast::VariableDeclStatement>(var),
|
||||||
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
|
rhs),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_TRUE(td()->DetermineStatements(block)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
ASSERT_NE(rhs->result_type(), nullptr);
|
ASSERT_NE(rhs->result_type(), nullptr);
|
||||||
|
|
||||||
EXPECT_FALSE(v()->ValidateStatements(&block));
|
EXPECT_FALSE(v()->ValidateStatements(block));
|
||||||
ASSERT_TRUE(v()->has_error());
|
ASSERT_TRUE(v()->has_error());
|
||||||
// TODO(sarahM0): figure out what should be the error number.
|
// TODO(sarahM0): figure out what should be the error number.
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
|
@ -258,49 +204,31 @@ TEST_F(ValidatorTest, AssignIncompatibleTypesInBlockStatement_Fail) {
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
|
TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
|
||||||
// var<in> gloabl_var: f32;
|
// var<in> gloabl_var: f32;
|
||||||
ast::type::F32 f32;
|
mod->AddGlobalVariable(Var(Source{Source::Location{12, 34}}, "global_var",
|
||||||
mod()->AddGlobalVariable(
|
ast::StorageClass::kInput, ty.f32, nullptr,
|
||||||
create<ast::Variable>(Source{Source::Location{12, 34}}, // source
|
ast::VariableDecorationList{}));
|
||||||
"global_var", // name
|
EXPECT_TRUE(v()->ValidateGlobalVariables(mod->global_variables()))
|
||||||
ast::StorageClass::kInput, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
|
||||||
<< v()->error();
|
<< v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
|
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
|
||||||
// var gloabl_var: f32;
|
// var gloabl_var: f32;
|
||||||
ast::type::F32 f32;
|
mod->AddGlobalVariable(Var(Source{Source::Location{12, 34}}, "global_var",
|
||||||
mod()->AddGlobalVariable(
|
ast::StorageClass::kNone, ty.f32, nullptr,
|
||||||
create<ast::Variable>(Source{Source::Location{12, 34}}, // source
|
ast::VariableDecorationList{}));
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0022: global variables must have a storage class");
|
"12:34 v-0022: global variables must have a storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalConstantWithStorageClass_Fail) {
|
TEST_F(ValidatorTest, GlobalConstantWithStorageClass_Fail) {
|
||||||
// const<in> gloabl_var: f32;
|
// const<in> gloabl_var: f32;
|
||||||
ast::type::F32 f32;
|
mod->AddGlobalVariable(Const(Source{Source::Location{12, 34}}, "global_var",
|
||||||
mod()->AddGlobalVariable(
|
ast::StorageClass::kInput, ty.f32, nullptr,
|
||||||
create<ast::Variable>(Source{Source::Location{12, 34}}, // source
|
ast::VariableDecorationList{}));
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kInput, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
true, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
v()->error(),
|
v()->error(),
|
||||||
"12:34 v-global01: global constants shouldn't have a storage class");
|
"12:34 v-global01: global constants shouldn't have a storage class");
|
||||||
|
@ -308,17 +236,11 @@ TEST_F(ValidatorTest, GlobalConstantWithStorageClass_Fail) {
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
|
TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
|
||||||
// const gloabl_var: f32;
|
// const gloabl_var: f32;
|
||||||
ast::type::F32 f32;
|
mod->AddGlobalVariable(Const(Source{Source::Location{12, 34}}, "global_var",
|
||||||
mod()->AddGlobalVariable(
|
ast::StorageClass::kNone, ty.f32, nullptr,
|
||||||
create<ast::Variable>(Source{Source::Location{12, 34}}, // source
|
ast::VariableDecorationList{}));
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
true, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
EXPECT_FALSE(v()->Validate(mod)) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
|
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
|
||||||
|
@ -326,37 +248,25 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
|
||||||
// fn my_func() -> f32 {
|
// fn my_func() -> f32 {
|
||||||
// not_global_var = 3.14f;
|
// not_global_var = 3.14f;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
mod->AddGlobalVariable(Var("global_var", ast::StorageClass::kPrivate, ty.f32,
|
||||||
mod()->AddGlobalVariable(create<ast::Variable>(
|
Expr(2.1f), ast::VariableDecorationList{}));
|
||||||
Source{}, // source
|
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.1)), // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("not_global_var"),
|
auto* lhs = Expr("not_global_var");
|
||||||
"not_global_var");
|
auto* rhs = Expr(3.14f);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.14f));
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
create<ast::AssignmentStatement>(
|
rhs),
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func = create<ast::Function>(Source{}, mod()->RegisterSymbol("my_func"),
|
auto* func =
|
||||||
"my_func", params, &f32, body,
|
create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
|
||||||
ast::FunctionDecorationList{});
|
ty.f32, body, ast::FunctionDecorationList{});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0006: 'not_global_var' is not declared");
|
EXPECT_EQ(v()->error(), "12:34 v-0006: 'not_global_var' is not declared");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,44 +276,31 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
||||||
// global_var = 3.14;
|
// global_var = 3.14;
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::Void void_type;
|
|
||||||
|
|
||||||
mod()->AddGlobalVariable(create<ast::Variable>(
|
mod->AddGlobalVariable(Var("global_var", ast::StorageClass::kPrivate, ty.f32,
|
||||||
Source{}, // source
|
Expr(2.1f), ast::VariableDecorationList{}));
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.1)), // constructor
|
|
||||||
ast::VariableDecorationList{})); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = create<ast::IdentifierExpression>(
|
||||||
Source{}, mod()->RegisterSymbol("global_var"), "global_var");
|
mod->RegisterSymbol("global_var"), "global_var");
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
auto* rhs = Expr(3.14f);
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.14f));
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
create<ast::AssignmentStatement>(
|
rhs),
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("my_func"), "my_func", params, &void_type,
|
mod->RegisterSymbol("my_func"), "my_func", params, ty.void_, body,
|
||||||
body,
|
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod)) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, UsingUndefinedVariableInnerScope_Fail) {
|
TEST_F(ValidatorTest, UsingUndefinedVariableInnerScope_Fail) {
|
||||||
|
@ -411,38 +308,24 @@ TEST_F(ValidatorTest, UsingUndefinedVariableInnerScope_Fail) {
|
||||||
// if (true) { var a : f32 = 2.0; }
|
// if (true) { var a : f32 = 2.0; }
|
||||||
// a = 3.14;
|
// a = 3.14;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::type::Bool bool_type;
|
ast::type::Bool bool_type;
|
||||||
auto* cond = create<ast::ScalarConstructorExpression>(
|
auto* cond = Expr(true);
|
||||||
Source{}, create<ast::BoolLiteral>(Source{}, &bool_type, true));
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("a"), "a");
|
auto* lhs = Expr("a");
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
auto* rhs = Expr(3.14f);
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.14f));
|
|
||||||
|
|
||||||
auto* outer_body = create<ast::BlockStatement>(
|
auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||||
create<ast::IfStatement>(Source{}, cond, body,
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
ast::ElseStatementList{}),
|
rhs),
|
||||||
create<ast::AssignmentStatement>(
|
});
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
|
@ -456,38 +339,24 @@ TEST_F(ValidatorTest, UsingUndefinedVariableOuterScope_Pass) {
|
||||||
// var a : f32 = 2.0;
|
// var a : f32 = 2.0;
|
||||||
// if (true) { a = 3.14; }
|
// if (true) { a = 3.14; }
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
SetSource(Source{Source::Location{12, 34}});
|
||||||
Source{Source::Location{12, 34}}, mod()->RegisterSymbol("a"), "a");
|
auto* lhs = Expr("a");
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
auto* rhs = Expr(3.14f);
|
||||||
Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.14f));
|
|
||||||
|
|
||||||
ast::type::Bool bool_type;
|
ast::type::Bool bool_type;
|
||||||
auto* cond = create<ast::ScalarConstructorExpression>(
|
auto* cond = Expr(true);
|
||||||
Source{}, create<ast::BoolLiteral>(Source{}, &bool_type, true));
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
Source{}, ast::StatementList{
|
rhs),
|
||||||
create<ast::AssignmentStatement>(
|
});
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* outer_body = create<ast::BlockStatement>(
|
auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||||
create<ast::IfStatement>(Source{}, cond, body,
|
});
|
||||||
ast::ElseStatementList{}),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
|
@ -498,66 +367,32 @@ TEST_F(ValidatorTest, UsingUndefinedVariableOuterScope_Pass) {
|
||||||
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
||||||
// var global_var0 : f32 = 0.1;
|
// var global_var0 : f32 = 0.1;
|
||||||
// var global_var1 : i32 = 0;
|
// var global_var1 : i32 = 0;
|
||||||
ast::type::F32 f32;
|
auto* var0 = Var("global_var0", ast::StorageClass::kPrivate, ty.f32,
|
||||||
ast::type::I32 i32;
|
Expr(0.1f), ast::VariableDecorationList{});
|
||||||
auto* var0 = create<ast::Variable>(
|
mod->AddGlobalVariable(var0);
|
||||||
Source{}, // source
|
|
||||||
"global_var0", // name
|
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 0.1)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
mod()->AddGlobalVariable(var0);
|
|
||||||
|
|
||||||
auto* var1 = create<ast::Variable>(
|
auto* var1 = Var(Source{Source::Location{12, 34}}, "global_var1",
|
||||||
Source{Source::Location{12, 34}}, // source
|
ast::StorageClass::kPrivate, ty.f32, Expr(0),
|
||||||
"global_var1", // name
|
ast::VariableDecorationList{});
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
mod->AddGlobalVariable(var1);
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
mod()->AddGlobalVariable(var1);
|
|
||||||
|
|
||||||
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
EXPECT_TRUE(v()->ValidateGlobalVariables(mod->global_variables()))
|
||||||
<< v()->error();
|
<< v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
|
TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
|
||||||
// var global_var : f32 = 0.1;
|
// var global_var : f32 = 0.1;
|
||||||
// var global_var : i32 = 0;
|
// var global_var : i32 = 0;
|
||||||
ast::type::F32 f32;
|
auto* var0 = Var("global_var", ast::StorageClass::kPrivate, ty.f32,
|
||||||
ast::type::I32 i32;
|
Expr(0.1f), ast::VariableDecorationList{});
|
||||||
auto* var0 = create<ast::Variable>(
|
mod->AddGlobalVariable(var0);
|
||||||
Source{}, // source
|
|
||||||
"global_var", // name
|
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 0.1)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
mod()->AddGlobalVariable(var0);
|
|
||||||
|
|
||||||
auto* var1 = create<ast::Variable>(
|
auto* var1 = Var(Source{Source::Location{12, 34}}, "global_var",
|
||||||
Source{Source::Location{12, 34}}, // source
|
ast::StorageClass::kPrivate, ty.i32, Expr(0),
|
||||||
"global_var", // name
|
ast::VariableDecorationList{});
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
mod->AddGlobalVariable(var1);
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
mod()->AddGlobalVariable(var1);
|
|
||||||
|
|
||||||
EXPECT_FALSE(v()->ValidateGlobalVariables(mod()->global_variables()));
|
EXPECT_FALSE(v()->ValidateGlobalVariables(mod->global_variables()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0011: redeclared global identifier 'global_var'");
|
"12:34 v-0011: redeclared global identifier 'global_var'");
|
||||||
}
|
}
|
||||||
|
@ -567,29 +402,17 @@ TEST_F(ValidatorTest, AssignToConstant_Fail) {
|
||||||
// const a :i32 = 2;
|
// const a :i32 = 2;
|
||||||
// a = 2
|
// a = 2
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Const("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
true, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
auto* rhs = Expr(2);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
create<ast::AssignmentStatement>(
|
rhs),
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
|
@ -606,46 +429,26 @@ TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) {
|
||||||
// return 0;
|
// return 0;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ast::type::Void void_type;
|
auto* global_var = Var("a", ast::StorageClass::kPrivate, ty.f32, Expr(2.1f),
|
||||||
ast::type::F32 f32;
|
ast::VariableDecorationList{});
|
||||||
auto* global_var = create<ast::Variable>(
|
mod->AddGlobalVariable(global_var);
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kPrivate, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.1)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
mod()->AddGlobalVariable(global_var);
|
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}}, var),
|
||||||
create<ast::VariableDeclStatement>(
|
});
|
||||||
Source{Source::Location{12, 34}}, var),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* func = create<ast::Function>(Source{}, mod()->RegisterSymbol("my_func"),
|
auto* func =
|
||||||
"my_func", params, &void_type, body,
|
create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
|
||||||
ast::FunctionDecorationList{});
|
ty.void_, body, ast::FunctionDecorationList{});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(td()->DetermineFunction(func)) << td()->error();
|
EXPECT_TRUE(td()->DetermineFunction(func)) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
EXPECT_FALSE(v()->Validate(mod)) << v()->error();
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0013: redeclared identifier 'a'");
|
EXPECT_EQ(v()->error(), "12:34 v-0013: redeclared identifier 'a'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,48 +457,28 @@ TEST_F(ValidatorTest, RedeclaredIndentifier_Fail) {
|
||||||
// var a :i32 = 2;
|
// var a :i32 = 2;
|
||||||
// var a :f21 = 2.0;
|
// var a :f21 = 2.0;
|
||||||
// }
|
// }
|
||||||
ast::type::Void void_type;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, Expr(2),
|
||||||
ast::type::I32 i32;
|
ast::VariableDecorationList{});
|
||||||
ast::type::F32 f32;
|
|
||||||
auto* var = create<ast::Variable>(
|
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::SintLiteral>(Source{}, &i32, 2)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* var_a_float = create<ast::Variable>(
|
auto* var_a_float = Var("a", ast::StorageClass::kNone, ty.f32, Expr(0.1f),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 0.1)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
|
||||||
create<ast::VariableDeclStatement>(
|
var_a_float),
|
||||||
Source{Source::Location{12, 34}}, var_a_float),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func = create<ast::Function>(Source{}, mod()->RegisterSymbol("my_func"),
|
auto* func =
|
||||||
"my_func", params, &void_type, body,
|
create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
|
||||||
ast::FunctionDecorationList{});
|
ty.void_, body, ast::FunctionDecorationList{});
|
||||||
|
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(td()->DetermineFunction(func)) << td()->error();
|
EXPECT_TRUE(td()->DetermineFunction(func)) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(), "12:34 v-0014: redeclared identifier 'a'");
|
EXPECT_EQ(v()->error(), "12:34 v-0014: redeclared identifier 'a'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,44 +487,23 @@ TEST_F(ValidatorTest, RedeclaredIdentifierInnerScope_Pass) {
|
||||||
// if (true) { var a : f32 = 2.0; }
|
// if (true) { var a : f32 = 2.0; }
|
||||||
// var a : f32 = 3.14;
|
// var a : f32 = 3.14;
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
auto* var = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::type::Bool bool_type;
|
ast::type::Bool bool_type;
|
||||||
auto* cond = create<ast::ScalarConstructorExpression>(
|
auto* cond = Expr(true);
|
||||||
Source{}, create<ast::BoolLiteral>(Source{}, &bool_type, true));
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* var_a_float = create<ast::Variable>(
|
auto* var_a_float = Var("a", ast::StorageClass::kNone, ty.f32, Expr(3.1f),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 3.14)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* outer_body = create<ast::BlockStatement>(
|
auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||||
create<ast::IfStatement>(Source{}, cond, body,
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
|
||||||
ast::ElseStatementList{}),
|
var_a_float),
|
||||||
create<ast::VariableDeclStatement>(
|
});
|
||||||
Source{Source::Location{12, 34}}, var_a_float),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
||||||
EXPECT_TRUE(v()->ValidateStatements(outer_body)) << v()->error();
|
EXPECT_TRUE(v()->ValidateStatements(outer_body)) << v()->error();
|
||||||
|
@ -754,44 +516,22 @@ TEST_F(ValidatorTest, DISABLED_RedeclaredIdentifierInnerScope_False) {
|
||||||
// var a : f32 = 3.14;
|
// var a : f32 = 3.14;
|
||||||
// if (true) { var a : f32 = 2.0; }
|
// if (true) { var a : f32 = 2.0; }
|
||||||
// }
|
// }
|
||||||
ast::type::F32 f32;
|
auto* var_a_float = Var("a", ast::StorageClass::kNone, ty.f32, Expr(3.1f),
|
||||||
auto* var_a_float = create<ast::Variable>(
|
ast::VariableDecorationList{});
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 3.14)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(
|
auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::type::Bool bool_type;
|
ast::type::Bool bool_type;
|
||||||
auto* cond = create<ast::ScalarConstructorExpression>(
|
auto* cond = Expr(true);
|
||||||
Source{}, create<ast::BoolLiteral>(Source{}, &bool_type, true));
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}}, var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(
|
|
||||||
Source{Source::Location{12, 34}}, var),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* outer_body = create<ast::BlockStatement>(
|
auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var_a_float),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var_a_float),
|
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||||
create<ast::IfStatement>(Source{}, cond, body,
|
});
|
||||||
ast::ElseStatementList{}),
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(outer_body)) << td()->error();
|
||||||
EXPECT_FALSE(v()->ValidateStatements(outer_body));
|
EXPECT_FALSE(v()->ValidateStatements(outer_body));
|
||||||
|
@ -801,61 +541,40 @@ TEST_F(ValidatorTest, DISABLED_RedeclaredIdentifierInnerScope_False) {
|
||||||
TEST_F(ValidatorTest, RedeclaredIdentifierDifferentFunctions_Pass) {
|
TEST_F(ValidatorTest, RedeclaredIdentifierDifferentFunctions_Pass) {
|
||||||
// func0 { var a : f32 = 2.0; return; }
|
// func0 { var a : f32 = 2.0; return; }
|
||||||
// func1 { var a : f32 = 3.0; return; }
|
// func1 { var a : f32 = 3.0; return; }
|
||||||
ast::type::F32 f32;
|
auto* var0 = Var("a", ast::StorageClass::kNone, ty.f32, Expr(2.0f),
|
||||||
ast::type::Void void_type;
|
ast::VariableDecorationList{});
|
||||||
auto* var0 = create<ast::Variable>(
|
|
||||||
Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&f32, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 2.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
auto* var1 = create<ast::Variable>(
|
auto* var1 = Var("a", ast::StorageClass::kNone, ty.void_, Expr(1.0f),
|
||||||
Source{}, // source
|
ast::VariableDecorationList{});
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&void_type, // type
|
|
||||||
false, // is_const
|
|
||||||
create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{},
|
|
||||||
create<ast::FloatLiteral>(Source{}, &f32, 1.0)), // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
ast::VariableList params0;
|
ast::VariableList params0;
|
||||||
auto* body0 = create<ast::BlockStatement>(
|
auto* body0 = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
|
||||||
create<ast::VariableDeclStatement>(
|
var0),
|
||||||
Source{Source::Location{12, 34}}, var0),
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto* func0 = create<ast::Function>(Source{}, mod()->RegisterSymbol("func0"),
|
auto* func0 =
|
||||||
"func0", params0, &void_type, body0,
|
create<ast::Function>(mod->RegisterSymbol("func0"), "func0", params0,
|
||||||
ast::FunctionDecorationList{});
|
ty.void_, body0, ast::FunctionDecorationList{});
|
||||||
|
|
||||||
ast::VariableList params1;
|
ast::VariableList params1;
|
||||||
auto* body1 = create<ast::BlockStatement>(
|
auto* body1 = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(Source{Source::Location{13, 34}},
|
||||||
create<ast::VariableDeclStatement>(
|
var1),
|
||||||
Source{Source::Location{13, 34}}, var1),
|
create<ast::ReturnStatement>(),
|
||||||
create<ast::ReturnStatement>(Source{}),
|
});
|
||||||
});
|
|
||||||
auto* func1 = create<ast::Function>(
|
auto* func1 = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("func1"), "func1", params1, &void_type,
|
mod->RegisterSymbol("func1"), "func1", params1, ty.void_, body1,
|
||||||
body1,
|
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
|
|
||||||
mod()->AddFunction(func0);
|
mod->AddFunction(func0);
|
||||||
mod()->AddFunction(func1);
|
mod->AddFunction(func1);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
EXPECT_TRUE(v()->Validate(mod)) << v()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, VariableDeclNoConstructor_Pass) {
|
TEST_F(ValidatorTest, VariableDeclNoConstructor_Pass) {
|
||||||
|
@ -863,28 +582,18 @@ TEST_F(ValidatorTest, VariableDeclNoConstructor_Pass) {
|
||||||
// var a :i32;
|
// var a :i32;
|
||||||
// a = 2;
|
// a = 2;
|
||||||
// }
|
// }
|
||||||
ast::type::I32 i32;
|
auto* var = Var("a", ast::StorageClass::kNone, ty.i32, nullptr,
|
||||||
auto* var =
|
ast::VariableDecorationList{});
|
||||||
create<ast::Variable>(Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&i32, // type
|
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
|
|
||||||
td()->RegisterVariableForTesting(var);
|
td()->RegisterVariableForTesting(var);
|
||||||
auto* lhs = create<ast::IdentifierExpression>(
|
auto* lhs = Expr("a");
|
||||||
Source{}, mod()->RegisterSymbol("a"), "a");
|
auto* rhs = Expr(2);
|
||||||
auto* rhs = create<ast::ScalarConstructorExpression>(
|
|
||||||
Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
|
|
||||||
|
|
||||||
auto* body = create<ast::BlockStatement>(
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
Source{}, ast::StatementList{
|
create<ast::VariableDeclStatement>(var),
|
||||||
create<ast::VariableDeclStatement>(Source{}, var),
|
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
|
||||||
create<ast::AssignmentStatement>(
|
rhs),
|
||||||
Source{Source::Location{12, 34}}, lhs, rhs),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(body)) << td()->error();
|
||||||
ASSERT_NE(lhs->result_type(), nullptr);
|
ASSERT_NE(lhs->result_type(), nullptr);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
|
||||||
ValidatorTestHelper::ValidatorTestHelper() {
|
ValidatorTestHelper::ValidatorTestHelper() {
|
||||||
td_ = std::make_unique<TypeDeterminer>(&mod_);
|
td_ = std::make_unique<TypeDeterminer>(mod);
|
||||||
v_ = std::make_unique<ValidatorImpl>();
|
v_ = std::make_unique<ValidatorImpl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/ast/builder.h"
|
||||||
#include "src/ast/type/void_type.h"
|
#include "src/ast/type/void_type.h"
|
||||||
#include "src/type_determiner.h"
|
#include "src/type_determiner.h"
|
||||||
#include "src/validator/validator_impl.h"
|
#include "src/validator/validator_impl.h"
|
||||||
|
@ -25,11 +26,11 @@
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
|
||||||
/// A helper for testing validation
|
/// A helper for testing validation
|
||||||
class ValidatorTestHelper {
|
class ValidatorTestHelper : public ast::BuilderWithModule {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
ValidatorTestHelper();
|
ValidatorTestHelper();
|
||||||
~ValidatorTestHelper();
|
~ValidatorTestHelper() override;
|
||||||
|
|
||||||
/// A handle to validator
|
/// A handle to validator
|
||||||
/// @returns a pointer to the validator
|
/// @returns a pointer to the validator
|
||||||
|
@ -37,24 +38,10 @@ class ValidatorTestHelper {
|
||||||
/// A handle to type_determiner
|
/// A handle to type_determiner
|
||||||
/// @returns a pointer to the type_determiner object
|
/// @returns a pointer to the type_determiner object
|
||||||
TypeDeterminer* td() const { return td_.get(); }
|
TypeDeterminer* td() const { return td_.get(); }
|
||||||
/// A handle to the created module
|
|
||||||
/// @return a pointer to the test module
|
|
||||||
ast::Module* mod() { return &mod_; }
|
|
||||||
|
|
||||||
/// Creates a new `ast::Node` owned by the Module. When the Module is
|
|
||||||
/// destructed, the `ast::Node` will also be destructed.
|
|
||||||
/// @param args the arguments to pass to the type constructor
|
|
||||||
/// @returns the node pointer
|
|
||||||
template <typename T, typename... ARGS>
|
|
||||||
T* create(ARGS&&... args) {
|
|
||||||
return mod_.create<T>(std::forward<ARGS>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ValidatorImpl> v_;
|
std::unique_ptr<ValidatorImpl> v_;
|
||||||
ast::Module mod_;
|
|
||||||
std::unique_ptr<TypeDeterminer> td_;
|
std::unique_ptr<TypeDeterminer> td_;
|
||||||
ast::type::Void void_type_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -42,25 +42,23 @@ TEST_F(ValidatorTypeTest, RuntimeArrayIsLast_Pass) {
|
||||||
// rt: array<f32>;
|
// rt: array<f32>;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::Array arr(&f32, 0, ast::ArrayDecorationList{});
|
|
||||||
ast::StructMemberList members;
|
ast::StructMemberList members;
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(Source{}, "vf", &f32, deco));
|
members.push_back(create<ast::StructMember>("vf", ty.f32, deco));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(
|
members.push_back(create<ast::StructMember>(
|
||||||
Source{Source::Location{12, 34}}, "rt", &arr, deco));
|
Source{Source::Location{12, 34}}, "rt", ty.array<f32>(), deco));
|
||||||
}
|
}
|
||||||
ast::StructDecorationList decos;
|
ast::StructDecorationList decos;
|
||||||
decos.push_back(create<ast::StructBlockDecoration>(Source{}));
|
decos.push_back(create<ast::StructBlockDecoration>());
|
||||||
auto* st = create<ast::Struct>(Source{}, members, decos);
|
auto* st = create<ast::Struct>(members, decos);
|
||||||
ast::type::Struct struct_type(mod()->RegisterSymbol("Foo"), "Foo", st);
|
ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
|
||||||
|
|
||||||
mod()->AddConstructedType(&struct_type);
|
mod->AddConstructedType(&struct_type);
|
||||||
EXPECT_TRUE(v()->ValidateConstructedTypes(mod()->constructed_types()));
|
EXPECT_TRUE(v()->ValidateConstructedTypes(mod->constructed_types()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTypeTest, RuntimeArrayIsLastNoBlock_Fail) {
|
TEST_F(ValidatorTypeTest, RuntimeArrayIsLastNoBlock_Fail) {
|
||||||
|
@ -69,24 +67,22 @@ TEST_F(ValidatorTypeTest, RuntimeArrayIsLastNoBlock_Fail) {
|
||||||
// rt: array<f32>;
|
// rt: array<f32>;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::Array arr(&f32, 0, ast::ArrayDecorationList{});
|
|
||||||
ast::StructMemberList members;
|
ast::StructMemberList members;
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(Source{}, "vf", &f32, deco));
|
members.push_back(create<ast::StructMember>("vf", ty.f32, deco));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(
|
members.push_back(create<ast::StructMember>(
|
||||||
Source{Source::Location{12, 34}}, "rt", &arr, deco));
|
Source{Source::Location{12, 34}}, "rt", ty.array<f32>(), deco));
|
||||||
}
|
}
|
||||||
ast::StructDecorationList decos;
|
ast::StructDecorationList decos;
|
||||||
auto* st = create<ast::Struct>(Source{}, members, decos);
|
auto* st = create<ast::Struct>(members, decos);
|
||||||
ast::type::Struct struct_type(mod()->RegisterSymbol("Foo"), "Foo", st);
|
ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
|
||||||
|
|
||||||
mod()->AddConstructedType(&struct_type);
|
mod->AddConstructedType(&struct_type);
|
||||||
EXPECT_FALSE(v()->ValidateConstructedTypes(mod()->constructed_types()));
|
EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0031: a struct containing a runtime-sized array must be "
|
"12:34 v-0031: a struct containing a runtime-sized array must be "
|
||||||
"in the 'storage' storage class: 'Foo'");
|
"in the 'storage' storage class: 'Foo'");
|
||||||
|
@ -99,25 +95,23 @@ TEST_F(ValidatorTypeTest, RuntimeArrayIsNotLast_Fail) {
|
||||||
// vf: f32;
|
// vf: f32;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
ast::type::F32 f32;
|
|
||||||
ast::type::Array arr(&f32, 0, ast::ArrayDecorationList{});
|
|
||||||
ast::StructMemberList members;
|
ast::StructMemberList members;
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(
|
members.push_back(create<ast::StructMember>(
|
||||||
Source{Source::Location{12, 34}}, "rt", &arr, deco));
|
Source{Source::Location{12, 34}}, "rt", ty.array<f32>(), deco));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(Source{}, "vf", &f32, deco));
|
members.push_back(create<ast::StructMember>("vf", ty.f32, deco));
|
||||||
}
|
}
|
||||||
ast::StructDecorationList decos;
|
ast::StructDecorationList decos;
|
||||||
decos.push_back(create<ast::StructBlockDecoration>(Source{}));
|
decos.push_back(create<ast::StructBlockDecoration>());
|
||||||
auto* st = create<ast::Struct>(Source{}, members, decos);
|
auto* st = create<ast::Struct>(members, decos);
|
||||||
ast::type::Struct struct_type(mod()->RegisterSymbol("Foo"), "Foo", st);
|
ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
|
||||||
|
|
||||||
mod()->AddConstructedType(&struct_type);
|
mod->AddConstructedType(&struct_type);
|
||||||
EXPECT_FALSE(v()->ValidateConstructedTypes(mod()->constructed_types()));
|
EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0015: runtime arrays may only appear as the last member "
|
"12:34 v-0015: runtime arrays may only appear as the last member "
|
||||||
"of a struct: 'rt'");
|
"of a struct: 'rt'");
|
||||||
|
@ -131,9 +125,8 @@ TEST_F(ValidatorTypeTest, AliasRuntimeArrayIsNotLast_Fail) {
|
||||||
// a: u32;
|
// a: u32;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
ast::type::F32 u32;
|
ast::type::Alias alias{mod->RegisterSymbol("RTArr"), "RTArr",
|
||||||
ast::type::Array array(&u32, 0, ast::ArrayDecorationList{});
|
ty.array<u32>()};
|
||||||
ast::type::Alias alias{mod()->RegisterSymbol("RTArr"), "RTArr", &array};
|
|
||||||
|
|
||||||
ast::StructMemberList members;
|
ast::StructMemberList members;
|
||||||
{
|
{
|
||||||
|
@ -143,15 +136,15 @@ TEST_F(ValidatorTypeTest, AliasRuntimeArrayIsNotLast_Fail) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(Source{}, "a", &u32, deco));
|
members.push_back(create<ast::StructMember>("a", ty.u32, deco));
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::StructDecorationList decos;
|
ast::StructDecorationList decos;
|
||||||
decos.push_back(create<ast::StructBlockDecoration>(Source{}));
|
decos.push_back(create<ast::StructBlockDecoration>());
|
||||||
auto* st = create<ast::Struct>(Source{}, members, decos);
|
auto* st = create<ast::Struct>(members, decos);
|
||||||
ast::type::Struct struct_type(mod()->RegisterSymbol("s"), "s", st);
|
ast::type::Struct struct_type(mod->RegisterSymbol("s"), "s", st);
|
||||||
mod()->AddConstructedType(&struct_type);
|
mod->AddConstructedType(&struct_type);
|
||||||
EXPECT_FALSE(v()->ValidateConstructedTypes(mod()->constructed_types()));
|
EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0015: runtime arrays may only appear as the last member "
|
"12:34 v-0015: runtime arrays may only appear as the last member "
|
||||||
"of a struct: 'b'");
|
"of a struct: 'b'");
|
||||||
|
@ -165,14 +158,13 @@ TEST_F(ValidatorTypeTest, AliasRuntimeArrayIsLast_Pass) {
|
||||||
// b: RTArr;
|
// b: RTArr;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
ast::type::F32 u32;
|
ast::type::Alias alias{mod->RegisterSymbol("RTArr"), "RTArr",
|
||||||
ast::type::Array array(&u32, 0, ast::ArrayDecorationList{});
|
ty.array<u32>()};
|
||||||
ast::type::Alias alias{mod()->RegisterSymbol("RTArr"), "RTArr", &array};
|
|
||||||
|
|
||||||
ast::StructMemberList members;
|
ast::StructMemberList members;
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
members.push_back(create<ast::StructMember>(Source{}, "a", &u32, deco));
|
members.push_back(create<ast::StructMember>("a", ty.u32, deco));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ast::StructMemberDecorationList deco;
|
ast::StructMemberDecorationList deco;
|
||||||
|
@ -180,43 +172,31 @@ TEST_F(ValidatorTypeTest, AliasRuntimeArrayIsLast_Pass) {
|
||||||
Source{Source::Location{12, 34}}, "b", &alias, deco));
|
Source{Source::Location{12, 34}}, "b", &alias, deco));
|
||||||
}
|
}
|
||||||
ast::StructDecorationList decos;
|
ast::StructDecorationList decos;
|
||||||
decos.push_back(create<ast::StructBlockDecoration>(Source{}));
|
decos.push_back(create<ast::StructBlockDecoration>());
|
||||||
auto* st = create<ast::Struct>(Source{}, members, decos);
|
auto* st = create<ast::Struct>(members, decos);
|
||||||
ast::type::Struct struct_type(mod()->RegisterSymbol("s"), "s", st);
|
ast::type::Struct struct_type(mod->RegisterSymbol("s"), "s", st);
|
||||||
mod()->AddConstructedType(&struct_type);
|
mod->AddConstructedType(&struct_type);
|
||||||
EXPECT_TRUE(v()->ValidateConstructedTypes(mod()->constructed_types()));
|
EXPECT_TRUE(v()->ValidateConstructedTypes(mod->constructed_types()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTypeTest, RuntimeArrayInFunction_Fail) {
|
TEST_F(ValidatorTypeTest, RuntimeArrayInFunction_Fail) {
|
||||||
/// [[stage(vertex)]]
|
/// [[stage(vertex)]]
|
||||||
// fn func -> void { var a : array<i32>; }
|
// fn func -> void { var a : array<i32>; }
|
||||||
ast::type::I32 i32;
|
|
||||||
ast::type::Array array(&i32, 0, ast::ArrayDecorationList{});
|
|
||||||
|
|
||||||
auto* var =
|
auto* var = Var("a", ast::StorageClass::kNone, ty.array<i32>());
|
||||||
create<ast::Variable>(Source{}, // source
|
|
||||||
"a", // name
|
|
||||||
ast::StorageClass::kNone, // storage_class
|
|
||||||
&array, // type
|
|
||||||
false, // is_const
|
|
||||||
nullptr, // constructor
|
|
||||||
ast::VariableDecorationList{}); // decorations
|
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
ast::type::Void void_type;
|
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||||
auto* body = create<ast::BlockStatement>(
|
create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}}, var),
|
||||||
Source{}, ast::StatementList{
|
});
|
||||||
create<ast::VariableDeclStatement>(
|
|
||||||
Source{Source::Location{12, 34}}, var),
|
|
||||||
});
|
|
||||||
auto* func = create<ast::Function>(
|
auto* func = create<ast::Function>(
|
||||||
Source{}, mod()->RegisterSymbol("func"), "func", params, &void_type, body,
|
mod->RegisterSymbol("func"), "func", params, ty.void_, body,
|
||||||
ast::FunctionDecorationList{
|
ast::FunctionDecorationList{
|
||||||
create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
|
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||||
});
|
});
|
||||||
mod()->AddFunction(func);
|
mod->AddFunction(func);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||||
EXPECT_FALSE(v()->Validate(mod()));
|
EXPECT_FALSE(v()->Validate(mod));
|
||||||
EXPECT_EQ(v()->error(),
|
EXPECT_EQ(v()->error(),
|
||||||
"12:34 v-0015: runtime arrays may only appear as the last member "
|
"12:34 v-0015: runtime arrays may only appear as the last member "
|
||||||
"of a struct: 'a'");
|
"of a struct: 'a'");
|
||||||
|
|
Loading…
Reference in New Issue