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, ArrayAccessorExpression::ArrayAccessorExpression(const Source& source,
Expression* array, Expression* array,
Expression* idx_expr) 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&&) = ArrayAccessorExpression::ArrayAccessorExpression(ArrayAccessorExpression&&) =
default; default;
@ -40,15 +43,6 @@ ArrayAccessorExpression* ArrayAccessorExpression::Clone(
return ctx->dst->create<ArrayAccessorExpression>(src, arr, idx); 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, void ArrayAccessorExpression::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -47,9 +47,6 @@ class ArrayAccessorExpression
/// @return the newly cloned node /// @return the newly cloned node
ArrayAccessorExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -44,9 +44,6 @@ class AssignmentStatement : public Castable<AssignmentStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
AssignmentStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -25,7 +25,11 @@ BinaryExpression::BinaryExpression(const Source& source,
BinaryOp op, BinaryOp op,
Expression* lhs, Expression* lhs,
Expression* rhs) 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; BinaryExpression::BinaryExpression(BinaryExpression&&) = default;
@ -39,16 +43,6 @@ BinaryExpression* BinaryExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<BinaryExpression>(src, op_, l, r); 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, void BinaryExpression::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -110,9 +110,6 @@ class BinaryExpression : public Castable<BinaryExpression, Expression> {
/// @return the newly cloned node /// @return the newly cloned node
BinaryExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -24,7 +24,10 @@ namespace ast {
BitcastExpression::BitcastExpression(const Source& source, BitcastExpression::BitcastExpression(const Source& source,
type::Type* type, type::Type* type,
Expression* expr) 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(BitcastExpression&&) = default;
BitcastExpression::~BitcastExpression() = default; BitcastExpression::~BitcastExpression() = default;
@ -37,12 +40,6 @@ BitcastExpression* BitcastExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<BitcastExpression>(src, ty, e); 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, void BitcastExpression::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -43,9 +43,6 @@ class BitcastExpression : public Castable<BitcastExpression, Expression> {
/// @return the newly cloned node /// @return the newly cloned node
BitcastExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -23,7 +23,11 @@ namespace ast {
BlockStatement::BlockStatement(const Source& source, BlockStatement::BlockStatement(const Source& source,
const StatementList& statements) 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; BlockStatement::BlockStatement(BlockStatement&&) = default;
@ -36,15 +40,6 @@ BlockStatement* BlockStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<BlockStatement>(src, stmts); 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, void BlockStatement::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -71,9 +71,6 @@ class BlockStatement : public Castable<BlockStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
BlockStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -36,9 +36,6 @@ class BreakStatement : public Castable<BreakStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
BreakStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -24,7 +24,12 @@ namespace ast {
CallExpression::CallExpression(const Source& source, CallExpression::CallExpression(const Source& source,
Expression* func, Expression* func,
ExpressionList params) 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; CallExpression::CallExpression(CallExpression&&) = default;
@ -38,18 +43,6 @@ CallExpression* CallExpression::Clone(CloneContext* ctx) const {
return ctx->dst->create<CallExpression>(src, fn, p); 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, void CallExpression::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -43,9 +43,6 @@ class CallExpression : public Castable<CallExpression, Expression> {
/// @return the newly cloned node /// @return the newly cloned node
CallExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -41,9 +41,6 @@ class CallStatement : public Castable<CallStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
CallStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -56,9 +56,6 @@ class CaseStatement : public Castable<CaseStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
CaseStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

@ -14,6 +14,7 @@
#include "src/ast/case_statement.h" #include "src/ast/case_statement.h"
#include "gtest/gtest-spi.h"
#include "src/ast/discard_statement.h" #include "src/ast/discard_statement.h"
#include "src/ast/if_statement.h" #include "src/ast/if_statement.h"
#include "src/ast/test_helper.h" #include "src/ast/test_helper.h"
@ -89,36 +90,13 @@ TEST_F(CaseStatementTest, IsCase) {
EXPECT_TRUE(c->Is<CaseStatement>()); EXPECT_TRUE(c->Is<CaseStatement>());
} }
TEST_F(CaseStatementTest, IsValid) { TEST_F(CaseStatementTest, Assert_NullBody) {
auto* c = create<CaseStatement>(CaseSelectorList{}, EXPECT_FATAL_FAILURE(
create<BlockStatement>(StatementList{})); {
EXPECT_TRUE(c->IsValid()); ProgramBuilder b;
} b.create<CaseStatement>(CaseSelectorList{}, nullptr);
},
TEST_F(CaseStatementTest, IsValid_NullBodyStatement) { "internal compiler error");
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, ToStr_WithSelectors_i32) { TEST_F(CaseStatementTest, ToStr_WithSelectors_i32) {

View File

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

View File

@ -36,9 +36,6 @@ class ContinueStatement : public Castable<ContinueStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
ContinueStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -42,9 +42,6 @@ class Decoration : public Castable<Decoration, Node> {
/// @return the decoration kind /// @return the decoration kind
virtual DecorationKind GetKind() const = 0; virtual DecorationKind GetKind() const = 0;
/// @returns true if the node is valid
bool IsValid() const override;
protected: protected:
/// Constructor /// Constructor
/// @param source the source of this decoration /// @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); return ctx->dst->create<DiscardStatement>(src);
} }
bool DiscardStatement::IsValid() const {
return true;
}
void DiscardStatement::to_str(const semantic::Info&, void DiscardStatement::to_str(const semantic::Info&,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -36,9 +36,6 @@ class DiscardStatement : public Castable<DiscardStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
DiscardStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -53,9 +53,6 @@ class ElseStatement : public Castable<ElseStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
ElseStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -36,9 +36,6 @@ class FallthroughStatement : public Castable<FallthroughStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
FallthroughStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -34,7 +34,14 @@ Function::Function(const Source& source,
params_(std::move(params)), params_(std::move(params)),
return_type_(return_type), return_type_(return_type),
body_(body), 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; 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); 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, void Function::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -89,9 +89,6 @@ class Function : public Castable<Function, Node> {
/// @return the newly cloned node /// @return the newly cloned node
Function* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -41,9 +41,6 @@ class IdentifierExpression : public Castable<IdentifierExpression, Expression> {
/// @return the newly cloned node /// @return the newly cloned node
IdentifierExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -28,7 +28,13 @@ IfStatement::IfStatement(const Source& source,
: Base(source), : Base(source),
condition_(condition), condition_(condition),
body_(body), 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; IfStatement::IfStatement(IfStatement&&) = default;
@ -43,30 +49,6 @@ IfStatement* IfStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<IfStatement>(src, cond, b, el); 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, void IfStatement::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -57,9 +57,6 @@ class IfStatement : public Castable<IfStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
IfStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -30,9 +30,6 @@ class Literal : public Castable<Literal, Node> {
/// @returns the type of the literal /// @returns the type of the literal
type::Type* type() const { return type_; } 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -54,9 +54,6 @@ class LoopStatement : public Castable<LoopStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
LoopStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -46,9 +46,6 @@ class MemberAccessorExpression
/// @return the newly cloned node /// @return the newly cloned node
MemberAccessorExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -47,46 +47,6 @@ Module::Module(const Source& source, std::vector<CastableBase*> global_decls)
Module::~Module() = default; 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 { Module* Module::Clone(CloneContext* ctx) const {
auto* out = ctx->dst->create<Module>(); auto* out = ctx->dst->create<Module>();
out->Copy(ctx, this); out->Copy(ctx, this);

View File

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

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/test_helper.h" #include "src/ast/test_helper.h"
namespace tint { namespace tint {
@ -45,78 +46,40 @@ TEST_F(ModuleTest, LookupFunctionMissing) {
program.AST().Functions().Find(program.Symbols().Get("Missing"))); program.AST().Functions().Find(program.Symbols().Get("Missing")));
} }
TEST_F(ModuleTest, IsValid_Empty) { TEST_F(ModuleTest, Assert_Null_GlobalVariable) {
Program program(std::move(*this)); EXPECT_FATAL_FAILURE(
EXPECT_TRUE(program.AST().IsValid()); {
ProgramBuilder builder;
builder.AST().AddGlobalVariable(nullptr);
},
"internal compiler error");
} }
TEST_F(ModuleTest, IsValid_GlobalVariable) { TEST_F(ModuleTest, Assert_Invalid_GlobalVariable) {
Global("var", ty.f32(), StorageClass::kInput); EXPECT_FATAL_FAILURE(
Program program(std::move(*this)); {
EXPECT_TRUE(program.AST().IsValid()); ProgramBuilder builder;
builder.Global("var", nullptr, StorageClass::kInput);
},
"internal compiler error");
} }
TEST_F(ModuleTest, IsValid_Null_GlobalVariable) { TEST_F(ModuleTest, Assert_Null_ConstructedType) {
AST().AddGlobalVariable(nullptr); EXPECT_FATAL_FAILURE(
Program program(std::move(*this)); {
EXPECT_FALSE(program.AST().IsValid()); ProgramBuilder builder;
builder.AST().AddConstructedType(nullptr);
},
"internal compiler error");
} }
TEST_F(ModuleTest, IsValid_Invalid_GlobalVariable) { TEST_F(ModuleTest, Assert_Null_Function) {
Global("var", nullptr, StorageClass::kInput); EXPECT_FATAL_FAILURE(
Program program(std::move(*this)); {
EXPECT_FALSE(program.AST().IsValid()); ProgramBuilder builder;
} builder.AST().AddFunction(nullptr);
},
TEST_F(ModuleTest, IsValid_Alias) { "internal compiler error");
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());
} }
} // namespace } // namespace

View File

@ -40,9 +40,6 @@ class Node : public Castable<Node, Cloneable> {
/// @returns the node source data /// @returns the node source data
const Source& source() const { return source_; } 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @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); 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, void ReturnStatement::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -46,9 +46,6 @@ class ReturnStatement : public Castable<ReturnStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
ReturnStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

@ -52,23 +52,6 @@ TEST_F(ReturnStatementTest, HasValue_WithValue) {
EXPECT_TRUE(r->has_value()); 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) { TEST_F(ReturnStatementTest, ToStr_WithValue) {
auto* expr = Expr("expr"); auto* expr = Expr("expr");
auto* r = create<ReturnStatement>(expr); auto* r = create<ReturnStatement>(expr);

View File

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

View File

@ -42,9 +42,6 @@ class ScalarConstructorExpression
/// @return the newly cloned node /// @return the newly cloned node
ScalarConstructorExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -27,7 +27,14 @@ Struct::Struct(const Source& source,
StructDecorationList decorations) StructDecorationList decorations)
: Base(source), : Base(source),
members_(std::move(members)), 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; Struct::Struct(Struct&&) = default;
@ -59,15 +66,6 @@ Struct* Struct::Clone(CloneContext* ctx) const {
return ctx->dst->create<Struct>(src, mem, decos); 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, void Struct::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -58,9 +58,6 @@ class Struct : public Castable<Struct, Node> {
/// @return the newly cloned node /// @return the newly cloned node
Struct* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

@ -28,7 +28,13 @@ StructMember::StructMember(const Source& source,
: Base(source), : Base(source),
symbol_(sym), symbol_(sym),
type_(type), 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; StructMember::StructMember(StructMember&&) = default;
@ -61,18 +67,6 @@ StructMember* StructMember::Clone(CloneContext* ctx) const {
return ctx->dst->create<StructMember>(src, sym, ty, decos); 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, void StructMember::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -59,9 +59,6 @@ class StructMember : public Castable<StructMember, Node> {
/// @return the newly cloned node /// @return the newly cloned node
StructMember* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -24,7 +24,12 @@ namespace ast {
SwitchStatement::SwitchStatement(const Source& source, SwitchStatement::SwitchStatement(const Source& source,
Expression* condition, Expression* condition,
CaseStatementList body) 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; SwitchStatement::SwitchStatement(SwitchStatement&&) = default;
@ -38,18 +43,6 @@ SwitchStatement* SwitchStatement::Clone(CloneContext* ctx) const {
return ctx->dst->create<SwitchStatement>(src, cond, b); 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, void SwitchStatement::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -49,9 +49,6 @@ class SwitchStatement : public Castable<SwitchStatement, Statement> {
/// @return the newly cloned node /// @return the newly cloned node
SwitchStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -24,7 +24,12 @@ namespace ast {
TypeConstructorExpression::TypeConstructorExpression(const Source& source, TypeConstructorExpression::TypeConstructorExpression(const Source& source,
type::Type* type, type::Type* type,
ExpressionList values) 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::TypeConstructorExpression(
TypeConstructorExpression&&) = default; TypeConstructorExpression&&) = default;
@ -40,21 +45,6 @@ TypeConstructorExpression* TypeConstructorExpression::Clone(
return ctx->dst->create<TypeConstructorExpression>(src, ty, vals); 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, void TypeConstructorExpression::to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,
size_t indent) const { size_t indent) const {

View File

@ -48,9 +48,6 @@ class TypeConstructorExpression
/// @return the newly cloned node /// @return the newly cloned node
TypeConstructorExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -44,9 +44,6 @@ class UnaryOpExpression : public Castable<UnaryOpExpression, Expression> {
/// @return the newly cloned node /// @return the newly cloned node
UnaryOpExpression* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -137,9 +137,6 @@ class Variable : public Castable<Variable, Node> {
/// @return the newly cloned node /// @return the newly cloned node
Variable* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

@ -42,9 +42,6 @@ class VariableDeclStatement
/// @return the newly cloned node /// @return the newly cloned node
VariableDeclStatement* Clone(CloneContext* ctx) const override; 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 /// Writes a representation of the node to the output stream
/// @param sem the semantic info for the program /// @param sem the semantic info for the program
/// @param out the stream to write to /// @param out the stream to write to

View File

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

View File

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

View File

@ -37,7 +37,6 @@ struct Node : public Castable<Node, ast::Node> {
return out; return out;
} }
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream&, size_t) const override {} 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>(); return ctx->dst->create<NotANode>();
} }
bool IsValid() const override { return true; }
void to_str(const semantic::Info&, std::ostream&, size_t) const override {} 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 { bool ProgramBuilder::IsValid() const {
return !diagnostics_.contains_errors() && ast_->IsValid(); return !diagnostics_.contains_errors();
} }
std::string ProgramBuilder::str(const ast::Node* node) const { 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/ast/return_statement.h" #include "src/ast/return_statement.h"
#include "src/ast/test_helper.h" #include "src/ast/test_helper.h"
@ -37,109 +38,43 @@ TEST_F(ProgramTest, ToStrEmitsPreambleAndPostamble) {
EXPECT_EQ(str, expected); EXPECT_EQ(str, expected);
} }
TEST_F(ProgramTest, IsValid_Empty) { TEST_F(ProgramTest, EmptyIsValid) {
Program program(std::move(*this)); Program program(std::move(*this));
EXPECT_TRUE(program.IsValid()); EXPECT_TRUE(program.IsValid());
} }
TEST_F(ProgramTest, IsValid_GlobalVariable) { TEST_F(ProgramTest, Assert_GlobalVariable) {
Global("var", ty.f32(), ast::StorageClass::kInput); Global("var", ty.f32(), ast::StorageClass::kInput);
Program program(std::move(*this)); Program program(std::move(*this));
EXPECT_TRUE(program.IsValid()); EXPECT_TRUE(program.IsValid());
} }
TEST_F(ProgramTest, IsValid_Null_GlobalVariable) { TEST_F(ProgramTest, Assert_NullGlobalVariable) {
AST().AddGlobalVariable(nullptr); EXPECT_FATAL_FAILURE(
{
Program program(std::move(*this)); ProgramBuilder b;
EXPECT_FALSE(program.IsValid()); b.AST().AddGlobalVariable(nullptr);
}
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")),
}, },
ast::FunctionDecorationList{}); "internal compiler error");
Program program(std::move(*this));
EXPECT_FALSE(program.IsValid());
EXPECT_NE(program.Diagnostics().count(), 0u);
} }
TEST_F(ProgramTest, IsValid_GeneratesError) { TEST_F(ProgramTest, Assert_NullConstructedType) {
AST().AddGlobalVariable(nullptr); EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.AST().AddConstructedType(nullptr);
},
"internal compiler error");
}
Program program(std::move(*this)); TEST_F(ProgramTest, Assert_Null_Function) {
EXPECT_FALSE(program.IsValid()); EXPECT_FATAL_FAILURE(
EXPECT_EQ(program.Diagnostics().count(), 1u); {
EXPECT_EQ(program.Diagnostics().error_count(), 1u); ProgramBuilder b;
EXPECT_EQ(program.Diagnostics().begin()->message, b.AST().AddFunction(nullptr);
"invalid program generated"); },
"internal compiler error");
} }
TEST_F(ProgramTest, DiagnosticsMove) { TEST_F(ProgramTest, DiagnosticsMove) {

View File

@ -684,9 +684,6 @@ DefInfo::DefInfo(const spvtools::opt::Instruction& def_inst,
DefInfo::~DefInfo() = default; DefInfo::~DefInfo() = default;
bool StatementBuilder::IsValid() const {
return true;
}
ast::Node* StatementBuilder::Clone(CloneContext*) const { ast::Node* StatementBuilder::Clone(CloneContext*) const {
return nullptr; return nullptr;
} }
@ -4117,6 +4114,9 @@ bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
for (uint32_t iarg = 1; iarg < inst.NumInOperands(); ++iarg) { for (uint32_t iarg = 1; iarg < inst.NumInOperands(); ++iarg) {
params.emplace_back(MakeOperand(inst, iarg).expr); params.emplace_back(MakeOperand(inst, iarg).expr);
} }
if (failed()) {
return false;
}
auto* call_expr = auto* call_expr =
create<ast::CallExpression>(Source{}, function, std::move(params)); create<ast::CallExpression>(Source{}, function, std::move(params));
auto* result_type = parser_impl_.ConvertType(inst.type_id()); 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; virtual ast::Statement* Build(ProgramBuilder* builder) const = 0;
private: private:
bool IsValid() const override;
Node* Clone(CloneContext*) const override; Node* Clone(CloneContext*) const override;
void to_str(const semantic::Info& sem, void to_str(const semantic::Info& sem,
std::ostream& out, std::ostream& out,

View File

@ -50,7 +50,6 @@ class FakeStmt : public ast::Statement {
public: public:
explicit FakeStmt(Source source) : ast::Statement(source) {} explicit FakeStmt(Source source) : ast::Statement(source) {}
FakeStmt* Clone(CloneContext*) const override { return nullptr; } 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 { void to_str(const semantic::Info&, std::ostream& out, size_t) const override {
out << "Fake"; out << "Fake";
} }
@ -60,7 +59,6 @@ class FakeExpr : public ast::Expression {
public: public:
explicit FakeExpr(Source source) : ast::Expression(source) {} explicit FakeExpr(Source source) : ast::Expression(source) {}
FakeExpr* Clone(CloneContext*) const override { return nullptr; } 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 {} 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)) { if (!GenerateLabel(continue_block_id)) {
return false; return false;
} }
if (stmt->has_continuing()) {
if (!GenerateBlockStatementWithoutScoping(stmt->continuing())) { if (!GenerateBlockStatementWithoutScoping(stmt->continuing())) {
return false; return false;
} }
}
scope_stack_.pop_scope(); scope_stack_.pop_scope();