ast: Replace IsValid() with TINT_ASSERT()

The readers must not produce invalid ASTs.
If readers cannot produce a valid AST, then they should error instead.
If a reader does produce an invalid AST, this change catches this bad behavior early, significantly helping identify the root of the broken logic.

IsValid() made a bit more sense in the days where the AST was mutable, and was constructed by calling setters on the nodes to build up the tree.
In order to detect bad ASTs, IsValid() would have to perform an entire AST traversal and give a yes / no answer for the entire tree. Not only was this slow, an answer of 'no' didn't tell you *where* the AST was invalid, resulting in a lot of manual debugging.
Now that the AST is fully immutable, all child nodes need to be built before their parents. The AST node constructors now become a perfect place to perform pointer sanity checking.

The argument for attempting to catch and handle invalid ASTs is not a compelling one.
Invalid ASTs are invalid compiler behavior, not something that should ever happen with a correctly functioning compiler.
If this were to happen in production, the user would be utterly clueless to _why_ the program is invalid, or _how_ to fix it.
Attempting to handle invalid ASTs is just masking a much larger problem.

Let's just let the fuzzers do their job to catch any of these cases early.

Fixed: chromium:1185569
Change-Id: I6496426a3a9da9d42627d2c1ca23917bfd04cc5c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44048
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2021-03-10 11:41:49 +00:00 committed by Commit Bot service account
parent 6ce58becd2
commit 8454d824d4
96 changed files with 476 additions and 1354 deletions

View File

@ -24,7 +24,10 @@ namespace ast {
ArrayAccessorExpression::ArrayAccessorExpression(const Source& source,
Expression* array,
Expression* idx_expr)
: Base(source), array_(array), idx_expr_(idx_expr) {}
: Base(source), array_(array), idx_expr_(idx_expr) {
TINT_ASSERT(array_);
TINT_ASSERT(idx_expr_);
}
ArrayAccessorExpression::ArrayAccessorExpression(ArrayAccessorExpression&&) =
default;
@ -40,15 +43,6 @@ ArrayAccessorExpression* ArrayAccessorExpression::Clone(
return ctx->dst->create<ArrayAccessorExpression>(src, arr, idx);
}
bool ArrayAccessorExpression::IsValid() const {
if (array_ == nullptr || !array_->IsValid())
return false;
if (idx_expr_ == nullptr || !idx_expr_->IsValid())
return false;
return true;
}
void ArrayAccessorExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -47,9 +47,6 @@ class ArrayAccessorExpression
/// @return the newly cloned node
ArrayAccessorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -48,40 +49,22 @@ TEST_F(ArrayAccessorExpressionTest, IsArrayAccessor) {
EXPECT_TRUE(exp->Is<ArrayAccessorExpression>());
}
TEST_F(ArrayAccessorExpressionTest, IsValid) {
auto* ary = Expr("ary");
auto* idx = Expr("idx");
auto* exp = create<ArrayAccessorExpression>(ary, idx);
EXPECT_TRUE(exp->IsValid());
TEST_F(ArrayAccessorExpressionTest, Assert_NullArray) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ArrayAccessorExpression>(nullptr, b.Expr("idx"));
},
"internal compiler error");
}
TEST_F(ArrayAccessorExpressionTest, IsValid_MissingArray) {
auto* idx = Expr("idx");
auto* exp = create<ArrayAccessorExpression>(nullptr, idx);
EXPECT_FALSE(exp->IsValid());
}
TEST_F(ArrayAccessorExpressionTest, IsValid_MissingIndex) {
auto* ary = Expr("ary");
auto* exp = create<ArrayAccessorExpression>(ary, nullptr);
EXPECT_FALSE(exp->IsValid());
}
TEST_F(ArrayAccessorExpressionTest, IsValid_InvalidArray) {
auto* ary = Expr("");
auto* idx = Expr("idx");
auto* exp = create<ArrayAccessorExpression>(ary, idx);
EXPECT_FALSE(exp->IsValid());
}
TEST_F(ArrayAccessorExpressionTest, IsValid_InvalidIndex) {
auto* ary = Expr("ary");
auto* idx = Expr("");
auto* exp = create<ArrayAccessorExpression>(ary, idx);
EXPECT_FALSE(exp->IsValid());
TEST_F(ArrayAccessorExpressionTest, Assert_NullIndex) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ArrayAccessorExpression>(b.Expr("arr"), nullptr);
},
"internal compiler error");
}
TEST_F(ArrayAccessorExpressionTest, ToStr) {

View File

@ -24,7 +24,10 @@ namespace ast {
AssignmentStatement::AssignmentStatement(const Source& source,
Expression* lhs,
Expression* rhs)
: Base(source), lhs_(lhs), rhs_(rhs) {}
: Base(source), lhs_(lhs), rhs_(rhs) {
TINT_ASSERT(lhs_);
TINT_ASSERT(rhs_);
}
AssignmentStatement::AssignmentStatement(AssignmentStatement&&) = default;
@ -38,15 +41,6 @@ AssignmentStatement* AssignmentStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<AssignmentStatement>(src, l, r);
}
bool AssignmentStatement::IsValid() const {
if (lhs_ == nullptr || !lhs_->IsValid())
return false;
if (rhs_ == nullptr || !rhs_->IsValid())
return false;
return true;
}
void AssignmentStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -44,9 +44,6 @@ class AssignmentStatement : public Castable<AssignmentStatement, Statement> {
/// @return the newly cloned node
AssignmentStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/assignment_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -50,40 +51,22 @@ TEST_F(AssignmentStatementTest, IsAssign) {
EXPECT_TRUE(stmt->Is<AssignmentStatement>());
}
TEST_F(AssignmentStatementTest, IsValid) {
auto* lhs = Expr("lhs");
auto* rhs = Expr("rhs");
auto* stmt = create<AssignmentStatement>(lhs, rhs);
EXPECT_TRUE(stmt->IsValid());
TEST_F(AssignmentStatementTest, Assert_NullLHS) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<AssignmentStatement>(nullptr, b.Expr(1));
},
"internal compiler error");
}
TEST_F(AssignmentStatementTest, IsValid_MissingLHS) {
auto* rhs = Expr("rhs");
auto* stmt = create<AssignmentStatement>(nullptr, rhs);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(AssignmentStatementTest, IsValid_MissingRHS) {
auto* lhs = Expr("lhs");
auto* stmt = create<AssignmentStatement>(lhs, nullptr);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(AssignmentStatementTest, IsValid_InvalidLHS) {
auto* lhs = Expr("");
auto* rhs = Expr("rhs");
auto* stmt = create<AssignmentStatement>(lhs, rhs);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(AssignmentStatementTest, IsValid_InvalidRHS) {
auto* lhs = Expr("lhs");
auto* rhs = Expr("");
auto* stmt = create<AssignmentStatement>(lhs, rhs);
EXPECT_FALSE(stmt->IsValid());
TEST_F(AssignmentStatementTest, Assert_NullRHS) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<AssignmentStatement>(b.Expr(1), nullptr);
},
"internal compiler error");
}
TEST_F(AssignmentStatementTest, ToStr) {

View File

@ -25,7 +25,11 @@ BinaryExpression::BinaryExpression(const Source& source,
BinaryOp op,
Expression* lhs,
Expression* rhs)
: Base(source), op_(op), lhs_(lhs), rhs_(rhs) {}
: Base(source), op_(op), lhs_(lhs), rhs_(rhs) {
TINT_ASSERT(lhs_);
TINT_ASSERT(rhs_);
TINT_ASSERT(op_ != BinaryOp::kNone);
}
BinaryExpression::BinaryExpression(BinaryExpression&&) = default;
@ -39,16 +43,6 @@ BinaryExpression* BinaryExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<BinaryExpression>(src, op_, l, r);
}
bool BinaryExpression::IsValid() const {
if (lhs_ == nullptr || !lhs_->IsValid()) {
return false;
}
if (rhs_ == nullptr || !rhs_->IsValid()) {
return false;
}
return op_ != BinaryOp::kNone;
}
void BinaryExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -110,9 +110,6 @@ class BinaryExpression : public Castable<BinaryExpression, Expression> {
/// @return the newly cloned node
BinaryExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -49,50 +50,22 @@ TEST_F(BinaryExpressionTest, IsBinary) {
EXPECT_TRUE(r->Is<BinaryExpression>());
}
TEST_F(BinaryExpressionTest, IsValid) {
auto* lhs = Expr("lhs");
auto* rhs = Expr("rhs");
auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
EXPECT_TRUE(r->IsValid());
}
TEST_F(BinaryExpressionTest, IsValid_Null_LHS) {
auto* rhs = Expr("rhs");
auto* r = create<BinaryExpression>(BinaryOp::kEqual, nullptr, rhs);
EXPECT_FALSE(r->IsValid());
}
TEST_F(BinaryExpressionTest, IsValid_Invalid_LHS) {
auto* lhs = Expr("");
auto* rhs = Expr("rhs");
auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
EXPECT_FALSE(r->IsValid());
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<BinaryExpression>(BinaryOp::kEqual, nullptr, b.Expr("rhs"));
},
"internal compiler error");
}
TEST_F(BinaryExpressionTest, IsValid_Null_RHS) {
auto* lhs = Expr("lhs");
auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, nullptr);
EXPECT_FALSE(r->IsValid());
}
TEST_F(BinaryExpressionTest, IsValid_Invalid_RHS) {
auto* lhs = Expr("lhs");
auto* rhs = Expr("");
auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
EXPECT_FALSE(r->IsValid());
}
TEST_F(BinaryExpressionTest, IsValid_Binary_None) {
auto* lhs = Expr("lhs");
auto* rhs = Expr("rhs");
auto* r = create<BinaryExpression>(BinaryOp::kNone, lhs, rhs);
EXPECT_FALSE(r->IsValid());
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<BinaryExpression>(BinaryOp::kEqual, b.Expr("lhs"), nullptr);
},
"internal compiler error");
}
TEST_F(BinaryExpressionTest, ToStr) {

View File

@ -24,7 +24,10 @@ namespace ast {
BitcastExpression::BitcastExpression(const Source& source,
type::Type* type,
Expression* expr)
: Base(source), type_(type), expr_(expr) {}
: Base(source), type_(type), expr_(expr) {
TINT_ASSERT(type_);
TINT_ASSERT(expr_);
}
BitcastExpression::BitcastExpression(BitcastExpression&&) = default;
BitcastExpression::~BitcastExpression() = default;
@ -37,12 +40,6 @@ BitcastExpression* BitcastExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<BitcastExpression>(src, ty, e);
}
bool BitcastExpression::IsValid() const {
if (expr_ == nullptr || !expr_->IsValid())
return false;
return type_ != nullptr;
}
void BitcastExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -43,9 +43,6 @@ class BitcastExpression : public Castable<BitcastExpression, Expression> {
/// @return the newly cloned node
BitcastExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/bitcast_expression.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -47,29 +48,22 @@ TEST_F(BitcastExpressionTest, IsBitcast) {
EXPECT_TRUE(exp->Is<BitcastExpression>());
}
TEST_F(BitcastExpressionTest, IsValid) {
auto* expr = Expr("expr");
auto* exp = create<BitcastExpression>(ty.f32(), expr);
EXPECT_TRUE(exp->IsValid());
TEST_F(BitcastExpressionTest, Assert_NullType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<BitcastExpression>(nullptr, b.Expr("idx"));
},
"internal compiler error");
}
TEST_F(BitcastExpressionTest, IsValid_MissingType) {
auto* expr = Expr("expr");
auto* exp = create<BitcastExpression>(nullptr, expr);
EXPECT_FALSE(exp->IsValid());
}
TEST_F(BitcastExpressionTest, IsValid_MissingExpr) {
auto* exp = create<BitcastExpression>(ty.f32(), nullptr);
EXPECT_FALSE(exp->IsValid());
}
TEST_F(BitcastExpressionTest, IsValid_InvalidExpr) {
auto* expr = Expr("");
auto* e = create<BitcastExpression>(ty.f32(), expr);
EXPECT_FALSE(e->IsValid());
TEST_F(BitcastExpressionTest, Assert_NullExpr) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<BitcastExpression>(b.ty.f32(), nullptr);
},
"internal compiler error");
}
TEST_F(BitcastExpressionTest, ToStr) {

View File

@ -23,7 +23,11 @@ namespace ast {
BlockStatement::BlockStatement(const Source& source,
const StatementList& statements)
: Base(source), statements_(std::move(statements)) {}
: Base(source), statements_(std::move(statements)) {
for (auto* stmt : *this) {
TINT_ASSERT(stmt);
}
}
BlockStatement::BlockStatement(BlockStatement&&) = default;
@ -36,15 +40,6 @@ BlockStatement* BlockStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<BlockStatement>(src, stmts);
}
bool BlockStatement::IsValid() const {
for (auto* stmt : *this) {
if (stmt == nullptr || !stmt->IsValid()) {
return false;
}
}
return true;
}
void BlockStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -71,9 +71,6 @@ class BlockStatement : public Castable<BlockStatement, Statement> {
/// @return the newly cloned node
BlockStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/if_statement.h"
#include "src/ast/test_helper.h"
@ -45,35 +46,13 @@ TEST_F(BlockStatementTest, IsBlock) {
EXPECT_TRUE(b->Is<BlockStatement>());
}
TEST_F(BlockStatementTest, IsValid) {
auto* b = create<BlockStatement>(ast::StatementList{
create<DiscardStatement>(),
});
EXPECT_TRUE(b->IsValid());
}
TEST_F(BlockStatementTest, IsValid_Empty) {
auto* b = create<BlockStatement>(ast::StatementList{});
EXPECT_TRUE(b->IsValid());
}
TEST_F(BlockStatementTest, IsValid_NullBodyStatement) {
auto* b = create<BlockStatement>(ast::StatementList{
create<DiscardStatement>(),
nullptr,
});
EXPECT_FALSE(b->IsValid());
}
TEST_F(BlockStatementTest, IsValid_InvalidBodyStatement) {
auto* b = create<BlockStatement>(
ast::StatementList{
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ElseStatementList{}),
});
EXPECT_FALSE(b->IsValid());
TEST_F(BlockStatementTest, Assert_NullStatement) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<BlockStatement>(ast::StatementList{nullptr});
},
"internal compiler error");
}
TEST_F(BlockStatementTest, ToStr) {

View File

@ -33,10 +33,6 @@ BreakStatement* BreakStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<BreakStatement>(src);
}
bool BreakStatement::IsValid() const {
return true;
}
void BreakStatement::to_str(const semantic::Info&,
std::ostream& out,
size_t indent) const {

View File

@ -36,9 +36,6 @@ class BreakStatement : public Castable<BreakStatement, Statement> {
/// @return the newly cloned node
BreakStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -34,11 +34,6 @@ TEST_F(BreakStatementTest, IsBreak) {
EXPECT_TRUE(stmt->Is<BreakStatement>());
}
TEST_F(BreakStatementTest, IsValid) {
auto* stmt = create<BreakStatement>();
EXPECT_TRUE(stmt->IsValid());
}
TEST_F(BreakStatementTest, ToStr) {
auto* stmt = create<BreakStatement>();
EXPECT_EQ(str(stmt), R"(Break{}

View File

@ -24,7 +24,12 @@ namespace ast {
CallExpression::CallExpression(const Source& source,
Expression* func,
ExpressionList params)
: Base(source), func_(func), params_(params) {}
: Base(source), func_(func), params_(params) {
TINT_ASSERT(func_);
for (auto* param : params_) {
TINT_ASSERT(param);
}
}
CallExpression::CallExpression(CallExpression&&) = default;
@ -38,18 +43,6 @@ CallExpression* CallExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<CallExpression>(src, fn, p);
}
bool CallExpression::IsValid() const {
if (func_ == nullptr || !func_->IsValid())
return false;
// All params must be valid
for (auto* param : params_) {
if (param == nullptr || !param->IsValid())
return false;
}
return true;
}
void CallExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -43,9 +43,6 @@ class CallExpression : public Castable<CallExpression, Expression> {
/// @return the newly cloned node
CallExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -50,44 +51,26 @@ TEST_F(CallExpressionTest, IsCall) {
EXPECT_TRUE(stmt->Is<CallExpression>());
}
TEST_F(CallExpressionTest, IsValid) {
auto* func = Expr("func");
auto* stmt = create<CallExpression>(func, ExpressionList{});
EXPECT_TRUE(stmt->IsValid());
TEST_F(CallExpressionTest, Assert_NullFunction) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<CallExpression>(nullptr, ExpressionList{});
},
"internal compiler error");
}
TEST_F(CallExpressionTest, IsValid_MissingFunction) {
auto* stmt = create<CallExpression>(nullptr, ExpressionList{});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(CallExpressionTest, IsValid_NullParam) {
auto* func = Expr("func");
TEST_F(CallExpressionTest, Assert_NullParam) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
ExpressionList params;
params.push_back(Expr("param1"));
params.push_back(b.Expr("param1"));
params.push_back(nullptr);
params.push_back(Expr("param2"));
auto* stmt = create<CallExpression>(func, params);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(CallExpressionTest, IsValid_InvalidFunction) {
auto* func = Expr("");
ExpressionList params;
params.push_back(Expr("param1"));
auto* stmt = create<CallExpression>(func, params);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(CallExpressionTest, IsValid_InvalidParam) {
auto* func = Expr("func");
ExpressionList params;
params.push_back(Expr(""));
auto* stmt = create<CallExpression>(func, params);
EXPECT_FALSE(stmt->IsValid());
params.push_back(b.Expr("param2"));
b.create<CallExpression>(b.Expr("func"), params);
},
"internal compiler error");
}
TEST_F(CallExpressionTest, ToStr_NoParams) {

View File

@ -22,7 +22,9 @@ namespace tint {
namespace ast {
CallStatement::CallStatement(const Source& source, CallExpression* call)
: Base(source), call_(call) {}
: Base(source), call_(call) {
TINT_ASSERT(call_);
}
CallStatement::CallStatement(CallStatement&&) = default;
@ -35,10 +37,6 @@ CallStatement* CallStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<CallStatement>(src, call);
}
bool CallStatement::IsValid() const {
return call_ != nullptr && call_->IsValid();
}
void CallStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -41,9 +41,6 @@ class CallStatement : public Castable<CallStatement, Statement> {
/// @return the newly cloned node
CallStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/call_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -30,25 +31,17 @@ TEST_F(CallStatementTest, Creation) {
}
TEST_F(CallStatementTest, IsCall) {
auto* c = create<CallStatement>(nullptr);
auto* c = create<CallStatement>(Call("f"));
EXPECT_TRUE(c->Is<CallStatement>());
}
TEST_F(CallStatementTest, IsValid) {
auto* c = create<CallStatement>(
create<CallExpression>(Expr("func"), ExpressionList{}));
EXPECT_TRUE(c->IsValid());
}
TEST_F(CallStatementTest, IsValid_MissingExpr) {
auto* c = create<CallStatement>(nullptr);
EXPECT_FALSE(c->IsValid());
}
TEST_F(CallStatementTest, IsValid_InvalidExpr) {
auto* c = create<CallStatement>(
create<CallExpression>(nullptr, ast::ExpressionList{}));
EXPECT_FALSE(c->IsValid());
TEST_F(CallStatementTest, Assert_NullCall) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<CallStatement>(nullptr);
},
"internal compiler error");
}
TEST_F(CallStatementTest, ToStr) {

View File

@ -24,7 +24,9 @@ namespace ast {
CaseStatement::CaseStatement(const Source& source,
CaseSelectorList selectors,
BlockStatement* body)
: Base(source), selectors_(selectors), body_(body) {}
: Base(source), selectors_(selectors), body_(body) {
TINT_ASSERT(body_);
}
CaseStatement::CaseStatement(CaseStatement&&) = default;
@ -38,10 +40,6 @@ CaseStatement* CaseStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<CaseStatement>(src, sel, b);
}
bool CaseStatement::IsValid() const {
return body_ != nullptr && body_->IsValid();
}
void CaseStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -56,9 +56,6 @@ class CaseStatement : public Castable<CaseStatement, Statement> {
/// @return the newly cloned node
CaseStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/case_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/if_statement.h"
#include "src/ast/test_helper.h"
@ -89,36 +90,13 @@ TEST_F(CaseStatementTest, IsCase) {
EXPECT_TRUE(c->Is<CaseStatement>());
}
TEST_F(CaseStatementTest, IsValid) {
auto* c = create<CaseStatement>(CaseSelectorList{},
create<BlockStatement>(StatementList{}));
EXPECT_TRUE(c->IsValid());
}
TEST_F(CaseStatementTest, IsValid_NullBodyStatement) {
CaseSelectorList b;
b.push_back(create<SintLiteral>(ty.i32(), 2));
auto* body = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
nullptr,
});
auto* c = create<CaseStatement>(b, body);
EXPECT_FALSE(c->IsValid());
}
TEST_F(CaseStatementTest, IsValid_InvalidBodyStatement) {
CaseSelectorList b;
b.push_back(create<SintLiteral>(ty.i32(), 2));
auto* body = create<BlockStatement>(
StatementList{
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ElseStatementList{}),
});
auto* c = create<CaseStatement>(CaseSelectorList{b}, body);
EXPECT_FALSE(c->IsValid());
TEST_F(CaseStatementTest, Assert_NullBody) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<CaseStatement>(CaseSelectorList{}, nullptr);
},
"internal compiler error");
}
TEST_F(CaseStatementTest, ToStr_WithSelectors_i32) {

View File

@ -33,10 +33,6 @@ ContinueStatement* ContinueStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<ContinueStatement>(src);
}
bool ContinueStatement::IsValid() const {
return true;
}
void ContinueStatement::to_str(const semantic::Info&,
std::ostream& out,
size_t indent) const {

View File

@ -36,9 +36,6 @@ class ContinueStatement : public Castable<ContinueStatement, Statement> {
/// @return the newly cloned node
ContinueStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -34,11 +34,6 @@ TEST_F(ContinueStatementTest, IsContinue) {
EXPECT_TRUE(stmt->Is<ContinueStatement>());
}
TEST_F(ContinueStatementTest, IsValid) {
auto* stmt = create<ContinueStatement>();
EXPECT_TRUE(stmt->IsValid());
}
TEST_F(ContinueStatementTest, ToStr) {
auto* stmt = create<ContinueStatement>();
EXPECT_EQ(str(stmt), R"(Continue{}

View File

@ -39,9 +39,5 @@ std::ostream& operator<<(std::ostream& out, DecorationKind data) {
return out << "<unknown>";
}
bool Decoration::IsValid() const {
return true;
}
} // namespace ast
} // namespace tint

View File

@ -42,9 +42,6 @@ class Decoration : public Castable<Decoration, Node> {
/// @return the decoration kind
virtual DecorationKind GetKind() const = 0;
/// @returns true if the node is valid
bool IsValid() const override;
protected:
/// Constructor
/// @param source the source of this decoration

View File

@ -33,10 +33,6 @@ DiscardStatement* DiscardStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<DiscardStatement>(src);
}
bool DiscardStatement::IsValid() const {
return true;
}
void DiscardStatement::to_str(const semantic::Info&,
std::ostream& out,
size_t indent) const {

View File

@ -36,9 +36,6 @@ class DiscardStatement : public Castable<DiscardStatement, Statement> {
/// @return the newly cloned node
DiscardStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -44,11 +44,6 @@ TEST_F(DiscardStatementTest, IsDiscard) {
EXPECT_TRUE(stmt->Is<DiscardStatement>());
}
TEST_F(DiscardStatementTest, IsValid) {
auto* stmt = create<DiscardStatement>();
EXPECT_TRUE(stmt->IsValid());
}
TEST_F(DiscardStatementTest, ToStr) {
auto* stmt = create<DiscardStatement>();
EXPECT_EQ(str(stmt), R"(Discard{}

View File

@ -24,7 +24,9 @@ namespace ast {
ElseStatement::ElseStatement(const Source& source,
Expression* condition,
BlockStatement* body)
: Base(source), condition_(condition), body_(body) {}
: Base(source), condition_(condition), body_(body) {
TINT_ASSERT(body_);
}
ElseStatement::ElseStatement(ElseStatement&&) = default;
@ -38,13 +40,6 @@ ElseStatement* ElseStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<ElseStatement>(src, cond, b);
}
bool ElseStatement::IsValid() const {
if (body_ == nullptr || !body_->IsValid()) {
return false;
}
return condition_ == nullptr || condition_->IsValid();
}
void ElseStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -53,9 +53,6 @@ class ElseStatement : public Castable<ElseStatement, Statement> {
/// @return the newly cloned node
ElseStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/if_statement.h"
#include "src/ast/test_helper.h"
@ -36,7 +37,7 @@ TEST_F(ElseStatementTest, Creation) {
}
TEST_F(ElseStatementTest, Creation_WithSource) {
auto* e = create<ElseStatement>(Source{Source::Location{20, 2}}, nullptr,
auto* e = create<ElseStatement>(Source{Source::Location{20, 2}}, Expr(true),
create<BlockStatement>(StatementList{}));
auto src = e->source();
EXPECT_EQ(src.range.begin.line, 20u);
@ -62,45 +63,13 @@ TEST_F(ElseStatementTest, HasContition_NullCondition) {
EXPECT_FALSE(e->HasCondition());
}
TEST_F(ElseStatementTest, IsValid) {
auto* e =
create<ElseStatement>(nullptr, create<BlockStatement>(StatementList{}));
EXPECT_TRUE(e->IsValid());
}
TEST_F(ElseStatementTest, IsValid_WithBody) {
auto* body = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
});
auto* e = create<ElseStatement>(nullptr, body);
EXPECT_TRUE(e->IsValid());
}
TEST_F(ElseStatementTest, IsValid_WithNullBodyStatement) {
auto* body = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
nullptr,
});
auto* e = create<ElseStatement>(nullptr, body);
EXPECT_FALSE(e->IsValid());
}
TEST_F(ElseStatementTest, IsValid_InvalidCondition) {
auto* cond = create<ScalarConstructorExpression>(nullptr);
auto* e =
create<ElseStatement>(cond, create<BlockStatement>(StatementList{}));
EXPECT_FALSE(e->IsValid());
}
TEST_F(ElseStatementTest, IsValid_InvalidBodyStatement) {
auto* body = create<BlockStatement>(
StatementList{
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ElseStatementList{}),
});
auto* e = create<ElseStatement>(nullptr, body);
EXPECT_FALSE(e->IsValid());
TEST_F(ElseStatementTest, Assert_NullBody) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ElseStatement>(b.Expr(true), nullptr);
},
"internal compiler error");
}
TEST_F(ElseStatementTest, ToStr) {

View File

@ -34,10 +34,6 @@ FallthroughStatement* FallthroughStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<FallthroughStatement>(src);
}
bool FallthroughStatement::IsValid() const {
return true;
}
void FallthroughStatement::to_str(const semantic::Info&,
std::ostream& out,
size_t indent) const {

View File

@ -36,9 +36,6 @@ class FallthroughStatement : public Castable<FallthroughStatement, Statement> {
/// @return the newly cloned node
FallthroughStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -42,11 +42,6 @@ TEST_F(FallthroughStatementTest, IsFallthrough) {
EXPECT_TRUE(stmt->Is<FallthroughStatement>());
}
TEST_F(FallthroughStatementTest, IsValid) {
auto* stmt = create<FallthroughStatement>();
EXPECT_TRUE(stmt->IsValid());
}
TEST_F(FallthroughStatementTest, ToStr) {
auto* stmt = create<FallthroughStatement>();
EXPECT_EQ(str(stmt), R"(Fallthrough{}

View File

@ -34,7 +34,14 @@ Function::Function(const Source& source,
params_(std::move(params)),
return_type_(return_type),
body_(body),
decorations_(std::move(decorations)) {}
decorations_(std::move(decorations)) {
for (auto* param : params_) {
TINT_ASSERT(param);
}
TINT_ASSERT(body_);
TINT_ASSERT(symbol_.IsValid());
TINT_ASSERT(return_type_);
}
Function::Function(Function&&) = default;
@ -73,23 +80,6 @@ Function* Function::Clone(CloneContext* ctx) const {
return ctx->dst->create<Function>(src, sym, p, ret, b, decos);
}
bool Function::IsValid() const {
for (auto* param : params_) {
if (param == nullptr || !param->IsValid())
return false;
}
if (body_ == nullptr || !body_->IsValid()) {
return false;
}
if (!symbol_.IsValid()) {
return false;
}
if (return_type_ == nullptr) {
return false;
}
return true;
}
void Function::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -89,9 +89,6 @@ class Function : public Castable<Function, Node> {
/// @return the newly cloned node
Function* Clone(CloneContext* ctx) const override;
/// @returns true if the symbol and type are both present
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/stage_decoration.h"
#include "src/ast/test_helper.h"
@ -47,80 +48,38 @@ TEST_F(FunctionTest, Creation_WithSource) {
EXPECT_EQ(src.range.begin.column, 2u);
}
TEST_F(FunctionTest, IsValid) {
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
auto* f = Func("func", params, ty.void_(),
StatementList{
create<DiscardStatement>(),
},
TEST_F(FunctionTest, Assert_InvalidName) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Func("", VariableList{}, b.ty.void_(), StatementList{},
FunctionDecorationList{});
EXPECT_TRUE(f->IsValid());
},
"internal compiler error");
}
TEST_F(FunctionTest, IsValid_InvalidName) {
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
auto* f =
Func("", params, ty.void_(), StatementList{}, FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
TEST_F(FunctionTest, Assert_NullReturnType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Func("f", VariableList{}, nullptr, StatementList{},
FunctionDecorationList{});
},
"internal compiler error");
}
TEST_F(FunctionTest, IsValid_MissingReturnType) {
TEST_F(FunctionTest, Assert_NullParam) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
auto* f =
Func("func", params, nullptr, StatementList{}, FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
}
TEST_F(FunctionTest, IsValid_NullParam) {
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
params.push_back(b.Var("var", b.ty.i32(), StorageClass::kNone));
params.push_back(nullptr);
auto* f = Func("func", params, ty.void_(), StatementList{},
b.Func("f", params, b.ty.void_(), StatementList{},
FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
}
TEST_F(FunctionTest, IsValid_InvalidParam) {
VariableList params;
params.push_back(Var("var", nullptr, StorageClass::kNone));
auto* f = Func("func", params, ty.void_(), StatementList{},
FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
}
TEST_F(FunctionTest, IsValid_NullBodyStatement) {
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
auto* f = Func("func", params, ty.void_(),
StatementList{
create<DiscardStatement>(),
nullptr,
},
FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
}
TEST_F(FunctionTest, IsValid_InvalidBodyStatement) {
VariableList params;
params.push_back(Var("var", ty.i32(), StorageClass::kNone));
auto* f = Func("func", params, ty.void_(),
StatementList{
create<DiscardStatement>(),
nullptr,
},
FunctionDecorationList{});
EXPECT_FALSE(f->IsValid());
"internal compiler error");
}
TEST_F(FunctionTest, ToStr) {

View File

@ -22,7 +22,9 @@ namespace tint {
namespace ast {
IdentifierExpression::IdentifierExpression(const Source& source, Symbol sym)
: Base(source), sym_(sym) {}
: Base(source), sym_(sym) {
TINT_ASSERT(sym_.IsValid());
}
IdentifierExpression::IdentifierExpression(IdentifierExpression&&) = default;
@ -35,10 +37,6 @@ IdentifierExpression* IdentifierExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<IdentifierExpression>(src, sym);
}
bool IdentifierExpression::IsValid() const {
return sym_.IsValid();
}
void IdentifierExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -41,9 +41,6 @@ class IdentifierExpression : public Castable<IdentifierExpression, Expression> {
/// @return the newly cloned node
IdentifierExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -39,9 +40,13 @@ TEST_F(IdentifierExpressionTest, IsIdentifier) {
EXPECT_TRUE(i->Is<IdentifierExpression>());
}
TEST_F(IdentifierExpressionTest, IsValid) {
auto* i = Expr("ident");
EXPECT_TRUE(i->IsValid());
TEST_F(IdentifierExpressionTest, Assert_InvalidSymbol) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Expr("");
},
"internal compiler error");
}
TEST_F(IdentifierExpressionTest, ToStr) {

View File

@ -28,7 +28,13 @@ IfStatement::IfStatement(const Source& source,
: Base(source),
condition_(condition),
body_(body),
else_statements_(std::move(else_stmts)) {}
else_statements_(std::move(else_stmts)) {
TINT_ASSERT(condition_);
TINT_ASSERT(body);
for (auto* el : else_statements_) {
TINT_ASSERT(el);
}
}
IfStatement::IfStatement(IfStatement&&) = default;
@ -43,30 +49,6 @@ IfStatement* IfStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<IfStatement>(src, cond, b, el);
}
bool IfStatement::IsValid() const {
if (condition_ == nullptr || !condition_->IsValid()) {
return false;
}
if (body_ == nullptr || !body_->IsValid()) {
return false;
}
bool found_else = false;
for (auto* el : else_statements_) {
// Else statement must be last
if (found_else)
return false;
if (el == nullptr || !el->IsValid())
return false;
if (el->condition() == nullptr)
found_else = true;
}
return true;
}
void IfStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -57,9 +57,6 @@ class IfStatement : public Castable<IfStatement, Statement> {
/// @return the newly cloned node
IfStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/if_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/test_helper.h"
@ -36,128 +37,37 @@ TEST_F(IfStatementTest, Creation) {
TEST_F(IfStatementTest, IsIf) {
auto* stmt = create<IfStatement>(
nullptr, create<BlockStatement>(StatementList{}), ElseStatementList{});
Expr(true), create<BlockStatement>(StatementList{}), ElseStatementList{});
EXPECT_TRUE(stmt->Is<IfStatement>());
}
TEST_F(IfStatementTest, IsValid) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(cond, body, ElseStatementList{});
EXPECT_TRUE(stmt->IsValid());
TEST_F(IfStatementTest, Assert_NullCondition) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
auto* body = b.create<BlockStatement>(StatementList{});
b.create<IfStatement>(nullptr, body, ElseStatementList{});
},
"internal compiler error");
}
TEST_F(IfStatementTest, IsValid_WithElseStatements) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(
cond, body,
ElseStatementList{
create<ElseStatement>(Expr("Ident"),
create<BlockStatement>(StatementList{})),
create<ElseStatement>(nullptr,
create<BlockStatement>(StatementList{})),
});
EXPECT_TRUE(stmt->IsValid());
TEST_F(IfStatementTest, Assert_NullBody) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<IfStatement>(b.Expr(true), nullptr, ElseStatementList{});
},
"internal compiler error");
}
TEST_F(IfStatementTest, IsValid_MissingCondition) {
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(nullptr, body, ElseStatementList{});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_InvalidCondition) {
auto* cond = Expr("");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(cond, body, ElseStatementList{});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_NullBodyStatement) {
auto* cond = Expr("cond");
auto* body = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
nullptr,
});
auto* stmt = create<IfStatement>(cond, body, ElseStatementList{});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_InvalidBodyStatement) {
auto* cond = Expr("cond");
auto* body = create<BlockStatement>(
StatementList{
create<DiscardStatement>(),
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ast::ElseStatementList{}),
});
auto* stmt = create<IfStatement>(cond, body, ElseStatementList{});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_NullElseStatement) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(
cond, body,
ElseStatementList{
create<ElseStatement>(Expr("Ident"),
create<BlockStatement>(StatementList{})),
create<ElseStatement>(nullptr,
create<BlockStatement>(StatementList{})),
nullptr,
});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_InvalidElseStatement) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(
cond, body,
ElseStatementList{
create<ElseStatement>(Expr(""),
create<BlockStatement>(StatementList{})),
});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_MultipleElseWiththoutCondition) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(
cond, body,
ElseStatementList{
create<ElseStatement>(nullptr,
create<BlockStatement>(StatementList{})),
create<ElseStatement>(nullptr,
create<BlockStatement>(StatementList{})),
});
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(IfStatementTest, IsValid_ElseNotLast) {
auto* cond = Expr("cond");
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* stmt = create<IfStatement>(
cond, body,
ElseStatementList{
create<ElseStatement>(nullptr,
create<BlockStatement>(StatementList{})),
create<ElseStatement>(Expr("Ident"),
create<BlockStatement>(StatementList{})),
});
EXPECT_FALSE(stmt->IsValid());
TEST_F(IfStatementTest, Assert_NullElseStatement) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
auto* body = b.create<BlockStatement>(StatementList{});
b.create<IfStatement>(b.Expr(true), body, ElseStatementList{nullptr});
},
"internal compiler error");
}
TEST_F(IfStatementTest, ToStr) {

View File

@ -24,10 +24,6 @@ Literal::Literal(const Source& source, type::Type* type)
Literal::~Literal() = default;
bool Literal::IsValid() const {
return true;
}
void Literal::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -30,9 +30,6 @@ class Literal : public Castable<Literal, Node> {
/// @returns the type of the literal
type::Type* type() const { return type_; }
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -24,7 +24,9 @@ namespace ast {
LoopStatement::LoopStatement(const Source& source,
BlockStatement* body,
BlockStatement* continuing)
: Base(source), body_(body), continuing_(continuing) {}
: Base(source), body_(body), continuing_(continuing) {
TINT_ASSERT(body_);
}
LoopStatement::LoopStatement(LoopStatement&&) = default;
@ -38,16 +40,6 @@ LoopStatement* LoopStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<LoopStatement>(src, b, cont);
}
bool LoopStatement::IsValid() const {
if (body_ == nullptr || !body_->IsValid()) {
return false;
}
if (continuing_ == nullptr || !continuing_->IsValid()) {
return false;
}
return true;
}
void LoopStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -54,9 +54,6 @@ class LoopStatement : public Castable<LoopStatement, Statement> {
/// @return the newly cloned node
LoopStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/loop_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h"
#include "src/ast/if_statement.h"
#include "src/ast/test_helper.h"
@ -78,88 +79,13 @@ TEST_F(LoopStatementTest, HasContinuing_WithContinuing) {
EXPECT_TRUE(l->has_continuing());
}
TEST_F(LoopStatementTest, IsValid) {
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* continuing =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* l = create<LoopStatement>(body, continuing);
EXPECT_TRUE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_WithoutContinuing) {
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* l =
create<LoopStatement>(body, create<BlockStatement>(StatementList{}));
EXPECT_TRUE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_WithoutBody) {
auto* l = create<LoopStatement>(create<BlockStatement>(StatementList{}),
create<BlockStatement>(StatementList{}));
EXPECT_TRUE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_NullBodyStatement) {
auto* body = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
nullptr,
});
auto* continuing =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* l = create<LoopStatement>(body, continuing);
EXPECT_FALSE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_InvalidBodyStatement) {
auto* body = create<BlockStatement>(
StatementList{
create<DiscardStatement>(),
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ElseStatementList{}),
});
auto* continuing =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* l = create<LoopStatement>(body, continuing);
EXPECT_FALSE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_NullContinuingStatement) {
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* continuing = create<BlockStatement>(StatementList{
create<DiscardStatement>(),
nullptr,
});
auto* l = create<LoopStatement>(body, continuing);
EXPECT_FALSE(l->IsValid());
}
TEST_F(LoopStatementTest, IsValid_InvalidContinuingStatement) {
auto* body =
create<BlockStatement>(StatementList{create<DiscardStatement>()});
auto* continuing = create<BlockStatement>(
StatementList{
create<DiscardStatement>(),
create<IfStatement>(nullptr, create<BlockStatement>(StatementList{}),
ElseStatementList{}),
});
auto* l = create<LoopStatement>(body, continuing);
EXPECT_FALSE(l->IsValid());
TEST_F(LoopStatementTest, Assert_NullBody) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<LoopStatement>(nullptr, nullptr);
},
"internal compiler error");
}
TEST_F(LoopStatementTest, ToStr) {

View File

@ -24,7 +24,10 @@ namespace ast {
MemberAccessorExpression::MemberAccessorExpression(const Source& source,
Expression* structure,
IdentifierExpression* member)
: Base(source), struct_(structure), member_(member) {}
: Base(source), struct_(structure), member_(member) {
TINT_ASSERT(structure);
TINT_ASSERT(member);
}
MemberAccessorExpression::MemberAccessorExpression(MemberAccessorExpression&&) =
default;
@ -40,16 +43,6 @@ MemberAccessorExpression* MemberAccessorExpression::Clone(
return ctx->dst->create<MemberAccessorExpression>(src, str, mem);
}
bool MemberAccessorExpression::IsValid() const {
if (struct_ == nullptr || !struct_->IsValid()) {
return false;
}
if (member_ == nullptr || !member_->IsValid()) {
return false;
}
return true;
}
void MemberAccessorExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -46,9 +46,6 @@ class MemberAccessorExpression
/// @return the newly cloned node
MemberAccessorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -43,30 +44,22 @@ TEST_F(MemberAccessorExpressionTest, IsMemberAccessor) {
EXPECT_TRUE(stmt->Is<MemberAccessorExpression>());
}
TEST_F(MemberAccessorExpressionTest, IsValid) {
auto* stmt =
create<MemberAccessorExpression>(Expr("structure"), Expr("member"));
EXPECT_TRUE(stmt->IsValid());
TEST_F(MemberAccessorExpressionTest, Assert_NullStruct) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<MemberAccessorExpression>(nullptr, b.Expr("member"));
},
"internal compiler error");
}
TEST_F(MemberAccessorExpressionTest, IsValid_NullStruct) {
auto* stmt = create<MemberAccessorExpression>(nullptr, Expr("member"));
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(MemberAccessorExpressionTest, IsValid_InvalidStruct) {
auto* stmt = create<MemberAccessorExpression>(Expr(""), Expr("member"));
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(MemberAccessorExpressionTest, IsValid_NullMember) {
auto* stmt = create<MemberAccessorExpression>(Expr("structure"), nullptr);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(MemberAccessorExpressionTest, IsValid_InvalidMember) {
auto* stmt = create<MemberAccessorExpression>(Expr("structure"), Expr(""));
EXPECT_FALSE(stmt->IsValid());
TEST_F(MemberAccessorExpressionTest, Assert_NullMember) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<MemberAccessorExpression>(b.Expr("struct"), nullptr);
},
"internal compiler error");
}
TEST_F(MemberAccessorExpressionTest, ToStr) {

View File

@ -47,46 +47,6 @@ Module::Module(const Source& source, std::vector<CastableBase*> global_decls)
Module::~Module() = default;
bool Module::IsValid() const {
for (auto* decl : global_declarations_) {
if (decl == nullptr) {
return false;
}
}
for (auto* var : global_variables_) {
if (var == nullptr || !var->IsValid()) {
return false;
}
}
for (auto* const ty : constructed_types_) {
if (ty == nullptr) {
return false;
}
if (auto* alias = ty->As<type::Alias>()) {
if (alias->type() == nullptr) {
return false;
}
if (auto* str = alias->type()->As<type::Struct>()) {
if (!str->symbol().IsValid()) {
return false;
}
}
} else if (auto* str = ty->As<type::Struct>()) {
if (!str->symbol().IsValid()) {
return false;
}
} else {
return false;
}
}
for (auto* func : functions_) {
if (func == nullptr || !func->IsValid()) {
return false;
}
}
return true;
}
Module* Module::Clone(CloneContext* ctx) const {
auto* out = ctx->dst->create<Module>();
out->Copy(ctx, this);

View File

@ -48,6 +48,7 @@ class Module : public Castable<Module, Node> {
/// Add a global variable to the Builder
/// @param var the variable to add
void AddGlobalVariable(ast::Variable* var) {
TINT_ASSERT(var);
global_variables_.push_back(var);
global_declarations_.push_back(var);
}
@ -62,6 +63,7 @@ class Module : public Castable<Module, Node> {
/// The type must be an alias or a struct.
/// @param type the constructed type to add
void AddConstructedType(type::Type* type) {
TINT_ASSERT(type);
constructed_types_.push_back(type);
global_declarations_.push_back(type);
}
@ -74,6 +76,7 @@ class Module : public Castable<Module, Node> {
/// Add a function to the Builder
/// @param func the function to add
void AddFunction(ast::Function* func) {
TINT_ASSERT(func);
functions_.push_back(func);
global_declarations_.push_back(func);
}
@ -81,9 +84,6 @@ class Module : public Castable<Module, Node> {
/// @returns the functions declared in the translation unit
const FunctionList& Functions() const { return functions_; }
/// @returns true if all required fields in the AST are present.
bool IsValid() const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -45,78 +46,40 @@ TEST_F(ModuleTest, LookupFunctionMissing) {
program.AST().Functions().Find(program.Symbols().Get("Missing")));
}
TEST_F(ModuleTest, IsValid_Empty) {
Program program(std::move(*this));
EXPECT_TRUE(program.AST().IsValid());
TEST_F(ModuleTest, Assert_Null_GlobalVariable) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder builder;
builder.AST().AddGlobalVariable(nullptr);
},
"internal compiler error");
}
TEST_F(ModuleTest, IsValid_GlobalVariable) {
Global("var", ty.f32(), StorageClass::kInput);
Program program(std::move(*this));
EXPECT_TRUE(program.AST().IsValid());
TEST_F(ModuleTest, Assert_Invalid_GlobalVariable) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder builder;
builder.Global("var", nullptr, StorageClass::kInput);
},
"internal compiler error");
}
TEST_F(ModuleTest, IsValid_Null_GlobalVariable) {
AST().AddGlobalVariable(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
TEST_F(ModuleTest, Assert_Null_ConstructedType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder builder;
builder.AST().AddConstructedType(nullptr);
},
"internal compiler error");
}
TEST_F(ModuleTest, IsValid_Invalid_GlobalVariable) {
Global("var", nullptr, StorageClass::kInput);
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Alias) {
auto* alias = ty.alias("alias", ty.f32());
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_TRUE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Null_Alias) {
AST().AddConstructedType(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Struct) {
auto* st = ty.struct_("name", {});
auto* alias = ty.alias("name", st);
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_TRUE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Struct_EmptyName) {
auto* st = ty.struct_("", {});
auto* alias = ty.alias("name", st);
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Function) {
Func("main", VariableList(), ty.f32(), StatementList{},
ast::FunctionDecorationList{});
Program program(std::move(*this));
EXPECT_TRUE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Null_Function) {
AST().AddFunction(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
}
TEST_F(ModuleTest, IsValid_Invalid_Function) {
Func("main", VariableList{}, nullptr, StatementList{},
ast::FunctionDecorationList{});
Program program(std::move(*this));
EXPECT_FALSE(program.AST().IsValid());
TEST_F(ModuleTest, Assert_Null_Function) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder builder;
builder.AST().AddFunction(nullptr);
},
"internal compiler error");
}
} // namespace

View File

@ -40,9 +40,6 @@ class Node : public Castable<Node, Cloneable> {
/// @returns the node source data
const Source& source() const { return source_; }
/// @returns true if the node is valid
virtual bool IsValid() const = 0;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -38,13 +38,6 @@ ReturnStatement* ReturnStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<ReturnStatement>(src, ret);
}
bool ReturnStatement::IsValid() const {
if (value_ != nullptr) {
return value_->IsValid();
}
return true;
}
void ReturnStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -46,9 +46,6 @@ class ReturnStatement : public Castable<ReturnStatement, Statement> {
/// @return the newly cloned node
ReturnStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -52,23 +52,6 @@ TEST_F(ReturnStatementTest, HasValue_WithValue) {
EXPECT_TRUE(r->has_value());
}
TEST_F(ReturnStatementTest, IsValid_WithoutValue) {
auto* r = create<ReturnStatement>();
EXPECT_TRUE(r->IsValid());
}
TEST_F(ReturnStatementTest, IsValid_WithValue) {
auto* expr = Expr("expr");
auto* r = create<ReturnStatement>(expr);
EXPECT_TRUE(r->IsValid());
}
TEST_F(ReturnStatementTest, IsValid_InvalidValue) {
auto* expr = Expr("");
auto* r = create<ReturnStatement>(expr);
EXPECT_FALSE(r->IsValid());
}
TEST_F(ReturnStatementTest, ToStr_WithValue) {
auto* expr = Expr("expr");
auto* r = create<ReturnStatement>(expr);

View File

@ -22,8 +22,10 @@ namespace tint {
namespace ast {
ScalarConstructorExpression::ScalarConstructorExpression(const Source& source,
Literal* litearl)
: Base(source), literal_(litearl) {}
Literal* literal)
: Base(source), literal_(literal) {
TINT_ASSERT(literal);
}
ScalarConstructorExpression::ScalarConstructorExpression(
ScalarConstructorExpression&&) = default;
@ -38,10 +40,6 @@ ScalarConstructorExpression* ScalarConstructorExpression::Clone(
return ctx->dst->create<ScalarConstructorExpression>(src, lit);
}
bool ScalarConstructorExpression::IsValid() const {
return literal_ != nullptr;
}
void ScalarConstructorExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -42,9 +42,6 @@ class ScalarConstructorExpression
/// @return the newly cloned node
ScalarConstructorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -33,14 +34,13 @@ TEST_F(ScalarConstructorExpressionTest, Creation_WithSource) {
EXPECT_EQ(src.range.begin.column, 2u);
}
TEST_F(ScalarConstructorExpressionTest, IsValid) {
auto* c = Expr(true);
EXPECT_TRUE(c->IsValid());
}
TEST_F(ScalarConstructorExpressionTest, IsValid_MissingLiteral) {
auto* c = create<ScalarConstructorExpression>(nullptr);
EXPECT_FALSE(c->IsValid());
TEST_F(ScalarConstructorExpressionTest, Assert_NullLiteral) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<ScalarConstructorExpression>(nullptr);
},
"internal compiler error");
}
TEST_F(ScalarConstructorExpressionTest, ToStr) {

View File

@ -27,7 +27,14 @@ Struct::Struct(const Source& source,
StructDecorationList decorations)
: Base(source),
members_(std::move(members)),
decorations_(std::move(decorations)) {}
decorations_(std::move(decorations)) {
for (auto* mem : members_) {
TINT_ASSERT(mem);
}
for (auto* deco : decorations_) {
TINT_ASSERT(deco);
}
}
Struct::Struct(Struct&&) = default;
@ -59,15 +66,6 @@ Struct* Struct::Clone(CloneContext* ctx) const {
return ctx->dst->create<Struct>(src, mem, decos);
}
bool Struct::IsValid() const {
for (auto* mem : members_) {
if (mem == nullptr || !mem->IsValid()) {
return false;
}
}
return true;
}
void Struct::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -58,9 +58,6 @@ class Struct : public Castable<Struct, Node> {
/// @return the newly cloned node
Struct* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -28,7 +28,13 @@ StructMember::StructMember(const Source& source,
: Base(source),
symbol_(sym),
type_(type),
decorations_(std::move(decorations)) {}
decorations_(std::move(decorations)) {
TINT_ASSERT(type);
TINT_ASSERT(symbol_.IsValid());
for (auto* deco : decorations_) {
TINT_ASSERT(deco);
}
}
StructMember::StructMember(StructMember&&) = default;
@ -61,18 +67,6 @@ StructMember* StructMember::Clone(CloneContext* ctx) const {
return ctx->dst->create<StructMember>(src, sym, ty, decos);
}
bool StructMember::IsValid() const {
if (type_ == nullptr || !symbol_.IsValid()) {
return false;
}
for (auto* deco : decorations_) {
if (deco == nullptr) {
return false;
}
}
return true;
}
void StructMember::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -59,9 +59,6 @@ class StructMember : public Castable<StructMember, Node> {
/// @return the newly cloned node
StructMember* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -45,24 +46,31 @@ TEST_F(StructMemberTest, CreationWithSource) {
EXPECT_EQ(st->source().range.end.column, 8u);
}
TEST_F(StructMemberTest, IsValid) {
auto* st = Member("a", ty.i32());
EXPECT_TRUE(st->IsValid());
TEST_F(StructMemberTest, Assert_EmptySymbol) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Member("", b.ty.i32());
},
"internal compiler error");
}
TEST_F(StructMemberTest, IsValid_EmptySymbol) {
auto* st = Member("", ty.i32());
EXPECT_FALSE(st->IsValid());
TEST_F(StructMemberTest, Assert_NullType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Member("a", nullptr);
},
"internal compiler error");
}
TEST_F(StructMemberTest, IsValid_NullType) {
auto* st = Member("a", nullptr);
EXPECT_FALSE(st->IsValid());
}
TEST_F(StructMemberTest, IsValid_Null_Decoration) {
auto* st = Member("a", ty.i32(), {MemberOffset(4), nullptr});
EXPECT_FALSE(st->IsValid());
TEST_F(StructMemberTest, Assert_NullDecoration) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Member("a", b.ty.i32(), {b.MemberOffset(4), nullptr});
},
"internal compiler error");
}
TEST_F(StructMemberTest, ToStr) {

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/test_helper.h"
@ -62,21 +63,24 @@ TEST_F(StructTest, CreationWithSourceAndDecorations) {
EXPECT_EQ(s->source().range.end.column, 8u);
}
TEST_F(StructTest, IsValid) {
auto* s = create<Struct>(StructMemberList{}, StructDecorationList{});
EXPECT_TRUE(s->IsValid());
}
TEST_F(StructTest, IsValid_Null_StructMember) {
auto* s = create<Struct>(StructMemberList{Member("a", ty.i32()), nullptr},
TEST_F(StructTest, Assert_Null_StructMember) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<Struct>(StructMemberList{b.Member("a", b.ty.i32()), nullptr},
StructDecorationList{});
EXPECT_FALSE(s->IsValid());
},
"internal compiler error");
}
TEST_F(StructTest, IsValid_Invalid_StructMember) {
auto* s = create<Struct>(StructMemberList{Member("", ty.i32())},
ast::StructDecorationList{});
EXPECT_FALSE(s->IsValid());
TEST_F(StructTest, Assert_Null_Decoration) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<Struct>(StructMemberList{b.Member("a", b.ty.i32())},
StructDecorationList{nullptr});
},
"internal compiler error");
}
TEST_F(StructTest, ToStr) {

View File

@ -24,7 +24,12 @@ namespace ast {
SwitchStatement::SwitchStatement(const Source& source,
Expression* condition,
CaseStatementList body)
: Base(source), condition_(condition), body_(body) {}
: Base(source), condition_(condition), body_(body) {
TINT_ASSERT(condition_);
for (auto* stmt : body_) {
TINT_ASSERT(stmt);
}
}
SwitchStatement::SwitchStatement(SwitchStatement&&) = default;
@ -38,18 +43,6 @@ SwitchStatement* SwitchStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<SwitchStatement>(src, cond, b);
}
bool SwitchStatement::IsValid() const {
if (condition_ == nullptr || !condition_->IsValid()) {
return false;
}
for (auto* stmt : body_) {
if (stmt == nullptr || !stmt->IsValid()) {
return false;
}
}
return true;
}
void SwitchStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -49,9 +49,6 @@ class SwitchStatement : public Castable<SwitchStatement, Statement> {
/// @return the newly cloned node
SwitchStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/switch_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -61,69 +62,26 @@ TEST_F(SwitchStatementTest, IsSwitch) {
EXPECT_TRUE(stmt->Is<SwitchStatement>());
}
TEST_F(SwitchStatementTest, IsValid) {
CaseSelectorList lit;
lit.push_back(create<SintLiteral>(ty.i32(), 2));
auto* ident = Expr("ident");
CaseStatementList body;
body.push_back(
create<CaseStatement>(lit, create<BlockStatement>(StatementList{})));
auto* stmt = create<SwitchStatement>(ident, body);
EXPECT_TRUE(stmt->IsValid());
TEST_F(SwitchStatementTest, Assert_Null_Condition) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
CaseStatementList cases;
cases.push_back(
b.create<CaseStatement>(CaseSelectorList{b.Literal(1)},
b.create<BlockStatement>(StatementList{})));
b.create<SwitchStatement>(nullptr, cases);
},
"internal compiler error");
}
TEST_F(SwitchStatementTest, IsValid_Null_Condition) {
CaseSelectorList lit;
lit.push_back(create<SintLiteral>(ty.i32(), 2));
CaseStatementList body;
body.push_back(
create<CaseStatement>(lit, create<BlockStatement>(StatementList{})));
auto* stmt = create<SwitchStatement>(nullptr, body);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(SwitchStatementTest, IsValid_Invalid_Condition) {
CaseSelectorList lit;
lit.push_back(create<SintLiteral>(ty.i32(), 2));
auto* ident = Expr("");
CaseStatementList body;
body.push_back(
create<CaseStatement>(lit, create<BlockStatement>(StatementList{})));
auto* stmt = create<SwitchStatement>(ident, body);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(SwitchStatementTest, IsValid_Null_BodyStatement) {
CaseSelectorList lit;
lit.push_back(create<SintLiteral>(ty.i32(), 2));
auto* ident = Expr("ident");
CaseStatementList body;
body.push_back(
create<CaseStatement>(lit, create<BlockStatement>(StatementList{})));
body.push_back(nullptr);
auto* stmt = create<SwitchStatement>(ident, body);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(SwitchStatementTest, IsValid_Invalid_BodyStatement) {
auto* ident = Expr("ident");
auto* case_body = create<BlockStatement>(StatementList{
nullptr,
});
CaseStatementList body;
body.push_back(create<CaseStatement>(CaseSelectorList{}, case_body));
auto* stmt = create<SwitchStatement>(ident, body);
EXPECT_FALSE(stmt->IsValid());
TEST_F(SwitchStatementTest, Assert_Null_CaseStatement) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr});
},
"internal compiler error");
}
TEST_F(SwitchStatementTest, ToStr_Empty) {

View File

@ -24,7 +24,12 @@ namespace ast {
TypeConstructorExpression::TypeConstructorExpression(const Source& source,
type::Type* type,
ExpressionList values)
: Base(source), type_(type), values_(std::move(values)) {}
: Base(source), type_(type), values_(std::move(values)) {
TINT_ASSERT(type);
for (auto* val : values_) {
TINT_ASSERT(val);
}
}
TypeConstructorExpression::TypeConstructorExpression(
TypeConstructorExpression&&) = default;
@ -40,21 +45,6 @@ TypeConstructorExpression* TypeConstructorExpression::Clone(
return ctx->dst->create<TypeConstructorExpression>(src, ty, vals);
}
bool TypeConstructorExpression::IsValid() const {
if (values_.empty()) {
return true;
}
if (type_ == nullptr) {
return false;
}
for (auto* val : values_) {
if (val == nullptr || !val->IsValid()) {
return false;
}
}
return true;
}
void TypeConstructorExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -48,9 +48,6 @@ class TypeConstructorExpression
/// @return the newly cloned node
TypeConstructorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -49,44 +50,23 @@ TEST_F(TypeConstructorExpressionTest, IsTypeConstructor) {
EXPECT_TRUE(t->Is<TypeConstructorExpression>());
}
TEST_F(TypeConstructorExpressionTest, IsValid) {
ExpressionList expr;
expr.push_back(Expr("expr"));
auto* t = create<TypeConstructorExpression>(ty.f32(), expr);
EXPECT_TRUE(t->IsValid());
TEST_F(TypeConstructorExpressionTest, Assert_NullType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<TypeConstructorExpression>(nullptr, ExpressionList{b.Expr(1)});
},
"internal compiler error");
}
TEST_F(TypeConstructorExpressionTest, IsValid_EmptyValue) {
ExpressionList expr;
auto* t = create<TypeConstructorExpression>(ty.f32(), expr);
EXPECT_TRUE(t->IsValid());
}
TEST_F(TypeConstructorExpressionTest, IsValid_NullType) {
ExpressionList expr;
expr.push_back(Expr("expr"));
auto* t = create<TypeConstructorExpression>(nullptr, expr);
EXPECT_FALSE(t->IsValid());
}
TEST_F(TypeConstructorExpressionTest, IsValid_NullValue) {
ExpressionList expr;
expr.push_back(Expr("expr"));
expr.push_back(nullptr);
auto* t = create<TypeConstructorExpression>(ty.f32(), expr);
EXPECT_FALSE(t->IsValid());
}
TEST_F(TypeConstructorExpressionTest, IsValid_InvalidValue) {
ExpressionList expr;
expr.push_back(Expr(""));
auto* t = create<TypeConstructorExpression>(ty.f32(), expr);
EXPECT_FALSE(t->IsValid());
TEST_F(TypeConstructorExpressionTest, Assert_NullValue) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<TypeConstructorExpression>(b.ty.i32(),
ExpressionList{nullptr});
},
"internal compiler error");
}
TEST_F(TypeConstructorExpressionTest, ToStr) {

View File

@ -24,7 +24,9 @@ namespace ast {
UnaryOpExpression::UnaryOpExpression(const Source& source,
UnaryOp op,
Expression* expr)
: Base(source), op_(op), expr_(expr) {}
: Base(source), op_(op), expr_(expr) {
TINT_ASSERT(expr_);
}
UnaryOpExpression::UnaryOpExpression(UnaryOpExpression&&) = default;
@ -37,10 +39,6 @@ UnaryOpExpression* UnaryOpExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<UnaryOpExpression>(src, op_, e);
}
bool UnaryOpExpression::IsValid() const {
return expr_ != nullptr && expr_->IsValid();
}
void UnaryOpExpression::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -44,9 +44,6 @@ class UnaryOpExpression : public Castable<UnaryOpExpression, Expression> {
/// @return the newly cloned node
UnaryOpExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/unary_op_expression.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -45,21 +46,13 @@ TEST_F(UnaryOpExpressionTest, IsUnaryOp) {
EXPECT_TRUE(u->Is<UnaryOpExpression>());
}
TEST_F(UnaryOpExpressionTest, IsValid) {
auto* ident = Expr("ident");
auto* u = create<UnaryOpExpression>(UnaryOp::kNot, ident);
EXPECT_TRUE(u->IsValid());
}
TEST_F(UnaryOpExpressionTest, IsValid_NullExpression) {
auto* u = create<UnaryOpExpression>(UnaryOp::kNot, nullptr);
EXPECT_FALSE(u->IsValid());
}
TEST_F(UnaryOpExpressionTest, IsValid_InvalidExpression) {
auto* ident = Expr("");
auto* u = create<UnaryOpExpression>(UnaryOp::kNot, ident);
EXPECT_FALSE(u->IsValid());
TEST_F(UnaryOpExpressionTest, Assert_NullExpression) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<UnaryOpExpression>(UnaryOp::kNot, nullptr);
},
"internal compiler error");
}
TEST_F(UnaryOpExpressionTest, ToStr) {

View File

@ -36,7 +36,10 @@ Variable::Variable(const Source& source,
is_const_(is_const),
constructor_(constructor),
decorations_(std::move(decorations)),
declared_storage_class_(sc) {}
declared_storage_class_(sc) {
TINT_ASSERT(symbol_.IsValid());
TINT_ASSERT(type_);
}
Variable::Variable(Variable&&) = default;
@ -79,7 +82,7 @@ LocationDecoration* Variable::GetLocationDecoration() const {
}
uint32_t Variable::constant_id() const {
assert(HasConstantIdDecoration());
TINT_ASSERT(HasConstantIdDecoration());
for (auto* deco : decorations_) {
if (auto* cid = deco->As<ConstantIdDecoration>()) {
return cid->value();
@ -98,19 +101,6 @@ Variable* Variable::Clone(CloneContext* ctx) const {
is_const_, ctor, decos);
}
bool Variable::IsValid() const {
if (!symbol_.IsValid()) {
return false;
}
if (type_ == nullptr) {
return false;
}
if (constructor_ && !constructor_->IsValid()) {
return false;
}
return true;
}
void Variable::info_to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -137,9 +137,6 @@ class Variable : public Castable<Variable, Node> {
/// @return the newly cloned node
Variable* Clone(CloneContext* ctx) const override;
/// @returns true if the variable is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -23,7 +23,9 @@ namespace ast {
VariableDeclStatement::VariableDeclStatement(const Source& source,
Variable* variable)
: Base(source), variable_(variable) {}
: Base(source), variable_(variable) {
TINT_ASSERT(variable_);
}
VariableDeclStatement::VariableDeclStatement(VariableDeclStatement&&) = default;
@ -36,10 +38,6 @@ VariableDeclStatement* VariableDeclStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<VariableDeclStatement>(src, var);
}
bool VariableDeclStatement::IsValid() const {
return variable_ != nullptr && variable_->IsValid();
}
void VariableDeclStatement::to_str(const semantic::Info& sem,
std::ostream& out,
size_t indent) const {

View File

@ -42,9 +42,6 @@ class VariableDeclStatement
/// @return the newly cloned node
VariableDeclStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;
/// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program
/// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/variable_decl_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h"
namespace tint {
@ -46,21 +47,13 @@ TEST_F(VariableDeclStatementTest, IsVariableDecl) {
EXPECT_TRUE(stmt->Is<VariableDeclStatement>());
}
TEST_F(VariableDeclStatementTest, IsValid) {
auto* var = Var("a", ty.f32(), StorageClass::kNone);
auto* stmt = create<VariableDeclStatement>(var);
EXPECT_TRUE(stmt->IsValid());
}
TEST_F(VariableDeclStatementTest, IsValid_InvalidVariable) {
auto* var = Var("", ty.f32(), StorageClass::kNone);
auto* stmt = create<VariableDeclStatement>(var);
EXPECT_FALSE(stmt->IsValid());
}
TEST_F(VariableDeclStatementTest, IsValid_NullVariable) {
auto* stmt = create<VariableDeclStatement>(nullptr);
EXPECT_FALSE(stmt->IsValid());
TEST_F(VariableDeclStatementTest, Assert_NullVariable) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.create<VariableDeclStatement>(nullptr);
},
"internal compiler error");
}
TEST_F(VariableDeclStatementTest, ToStr) {

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/constant_id_decoration.h"
#include "src/ast/test_helper.h"
@ -62,34 +63,22 @@ TEST_F(VariableTest, CreationEmpty) {
EXPECT_EQ(v->source().range.end.column, 7u);
}
TEST_F(VariableTest, IsValid) {
auto* v = Var("my_var", ty.i32(), StorageClass::kNone);
EXPECT_TRUE(v->IsValid());
TEST_F(VariableTest, Assert_MissingSymbol) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Var("", b.ty.i32(), StorageClass::kNone);
},
"internal compiler error");
}
TEST_F(VariableTest, IsValid_WithConstructor) {
auto* v = Var("my_var", ty.i32(), StorageClass::kNone, Expr("ident"));
EXPECT_TRUE(v->IsValid());
}
TEST_F(VariableTest, IsValid_MissingSymbol) {
auto* v = Var("", ty.i32(), StorageClass::kNone);
EXPECT_FALSE(v->IsValid());
}
TEST_F(VariableTest, IsValid_MissingType) {
auto* v = Var("x", nullptr, StorageClass::kNone);
EXPECT_FALSE(v->IsValid());
}
TEST_F(VariableTest, IsValid_MissingBoth) {
auto* v = Var("", nullptr, StorageClass::kNone);
EXPECT_FALSE(v->IsValid());
}
TEST_F(VariableTest, IsValid_InvalidConstructor) {
auto* v = Var("my_var", ty.i32(), StorageClass::kNone, Expr(""));
EXPECT_FALSE(v->IsValid());
TEST_F(VariableTest, Assert_NullType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Var("x", nullptr, StorageClass::kNone);
},
"internal compiler error");
}
TEST_F(VariableTest, to_str) {

View File

@ -37,7 +37,6 @@ struct Node : public Castable<Node, ast::Node> {
return out;
}
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
};
@ -55,7 +54,6 @@ struct NotANode : public Castable<NotANode, ast::Node> {
return ctx->dst->create<NotANode>();
}
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
};

View File

@ -60,7 +60,7 @@ ProgramBuilder ProgramBuilder::Wrap(const Program* program) {
}
bool ProgramBuilder::IsValid() const {
return !diagnostics_.contains_errors() && ast_->IsValid();
return !diagnostics_.contains_errors();
}
std::string ProgramBuilder::str(const ast::Node* node) const {

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/return_statement.h"
#include "src/ast/test_helper.h"
@ -37,109 +38,43 @@ TEST_F(ProgramTest, ToStrEmitsPreambleAndPostamble) {
EXPECT_EQ(str, expected);
}
TEST_F(ProgramTest, IsValid_Empty) {
TEST_F(ProgramTest, EmptyIsValid) {
Program program(std::move(*this));
EXPECT_TRUE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_GlobalVariable) {
TEST_F(ProgramTest, Assert_GlobalVariable) {
Global("var", ty.f32(), ast::StorageClass::kInput);
Program program(std::move(*this));
EXPECT_TRUE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Null_GlobalVariable) {
AST().AddGlobalVariable(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Invalid_GlobalVariable) {
Global("var", nullptr, ast::StorageClass::kInput);
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Alias) {
auto* alias = ty.alias("alias", ty.f32());
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_TRUE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Null_Alias) {
AST().AddConstructedType(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Struct) {
auto* st = ty.struct_("name", {});
auto* alias = ty.alias("name", st);
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_TRUE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Struct_EmptyName) {
auto* st = ty.struct_("", {});
auto* alias = ty.alias("name", st);
AST().AddConstructedType(alias);
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Function) {
Func("main", ast::VariableList(), ty.f32(), ast::StatementList{},
ast::FunctionDecorationList{});
Program program(std::move(*this));
EXPECT_TRUE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Null_Function) {
AST().AddFunction(nullptr);
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Invalid_Function) {
Func("main", ast::VariableList{}, nullptr, ast::StatementList{},
ast::FunctionDecorationList{});
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
}
TEST_F(ProgramTest, IsValid_Invalid_UnknownVar) {
Func("main", ast::VariableList{}, nullptr,
ast::StatementList{
create<ast::ReturnStatement>(Expr("unknown_ident")),
TEST_F(ProgramTest, Assert_NullGlobalVariable) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.AST().AddGlobalVariable(nullptr);
},
ast::FunctionDecorationList{});
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
EXPECT_NE(program.Diagnostics().count(), 0u);
"internal compiler error");
}
TEST_F(ProgramTest, IsValid_GeneratesError) {
AST().AddGlobalVariable(nullptr);
TEST_F(ProgramTest, Assert_NullConstructedType) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.AST().AddConstructedType(nullptr);
},
"internal compiler error");
}
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
EXPECT_EQ(program.Diagnostics().count(), 1u);
EXPECT_EQ(program.Diagnostics().error_count(), 1u);
EXPECT_EQ(program.Diagnostics().begin()->message,
"invalid program generated");
TEST_F(ProgramTest, Assert_Null_Function) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.AST().AddFunction(nullptr);
},
"internal compiler error");
}
TEST_F(ProgramTest, DiagnosticsMove) {

View File

@ -684,9 +684,6 @@ DefInfo::DefInfo(const spvtools::opt::Instruction& def_inst,
DefInfo::~DefInfo() = default;
bool StatementBuilder::IsValid() const {
return true;
}
ast::Node* StatementBuilder::Clone(CloneContext*) const {
return nullptr;
}
@ -4117,6 +4114,9 @@ bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
for (uint32_t iarg = 1; iarg < inst.NumInOperands(); ++iarg) {
params.emplace_back(MakeOperand(inst, iarg).expr);
}
if (failed()) {
return false;
}
auto* call_expr =
create<ast::CallExpression>(Source{}, function, std::move(params));
auto* result_type = parser_impl_.ConvertType(inst.type_id());

View File

@ -368,7 +368,6 @@ class StatementBuilder : public Castable<StatementBuilder, ast::Statement> {
virtual ast::Statement* Build(ProgramBuilder* builder) const = 0;
private:
bool IsValid() const override;
Node* Clone(CloneContext*) const override;
void to_str(const semantic::Info& sem,
std::ostream& out,

View File

@ -50,7 +50,6 @@ class FakeStmt : public ast::Statement {
public:
explicit FakeStmt(Source source) : ast::Statement(source) {}
FakeStmt* Clone(CloneContext*) const override { return nullptr; }
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream& out, size_t) const override {
out << "Fake";
}
@ -60,7 +59,6 @@ class FakeExpr : public ast::Expression {
public:
explicit FakeExpr(Source source) : ast::Expression(source) {}
FakeExpr* Clone(CloneContext*) const override { return nullptr; }
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
};

View File

@ -2715,9 +2715,11 @@ bool Builder::GenerateLoopStatement(ast::LoopStatement* stmt) {
if (!GenerateLabel(continue_block_id)) {
return false;
}
if (stmt->has_continuing()) {
if (!GenerateBlockStatementWithoutScoping(stmt->continuing())) {
return false;
}
}
scope_stack_.pop_scope();