Validator: Strip out unreachable code
A large chunk of validation is now handled by the IntrinsicTable. Remove this.
Also fail validation and propagate diagnostics from the Program to the validator if attempting to validate a broken program. This should prevent undefined behaviour if the user forgets to check the program.IsValid() after parsing.
Change-Id: I2972e8ce296d6d6fca318cee48bc6929e5ed52db
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41549
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/semantic/function.h b/src/semantic/function.h
index 9dc200f..e5b3aa7 100644
--- a/src/semantic/function.h
+++ b/src/semantic/function.h
@@ -48,12 +48,12 @@
};
/// Constructor
- /// @param ast the ast::Function
+ /// @param declaration the ast::Function
/// @param referenced_module_vars the referenced module variables
/// @param local_referenced_module_vars the locally referenced module
/// variables
/// @param ancestor_entry_points the ancestor entry points
- Function(ast::Function* ast,
+ Function(ast::Function* declaration,
std::vector<const Variable*> referenced_module_vars,
std::vector<const Variable*> local_referenced_module_vars,
std::vector<Symbol> ancestor_entry_points);
@@ -61,6 +61,9 @@
/// Destructor
~Function() override;
+ /// @returns the ast::Function declaration
+ ast::Function* Declaration() const { return declaration_; }
+
/// Note: If this function calls other functions, the return will also include
/// all of the referenced variables from the callees.
/// @returns the referenced module variables
@@ -143,6 +146,7 @@
const std::vector<std::pair<const Variable*, BindingInfo>>
ReferencedSampledTextureVariablesImpl(bool multisampled) const;
+ ast::Function* const declaration_;
std::vector<const Variable*> const referenced_module_vars_;
std::vector<const Variable*> const local_referenced_module_vars_;
std::vector<Symbol> const ancestor_entry_points_;
diff --git a/src/semantic/sem_function.cc b/src/semantic/sem_function.cc
index 9862075..a6c69d7 100644
--- a/src/semantic/sem_function.cc
+++ b/src/semantic/sem_function.cc
@@ -45,11 +45,12 @@
} // namespace
-Function::Function(ast::Function* ast,
+Function::Function(ast::Function* declaration,
std::vector<const Variable*> referenced_module_vars,
std::vector<const Variable*> local_referenced_module_vars,
std::vector<Symbol> ancestor_entry_points)
- : Base(ast->return_type(), GetParameters(ast)),
+ : Base(declaration->return_type(), GetParameters(declaration)),
+ declaration_(declaration),
referenced_module_vars_(std::move(referenced_module_vars)),
local_referenced_module_vars_(std::move(local_referenced_module_vars)),
ancestor_entry_points_(std::move(ancestor_entry_points)) {}
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index f379508..c25e3d2 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -31,6 +31,7 @@
#include "src/ast/variable_decl_statement.h"
#include "src/semantic/call.h"
#include "src/semantic/expression.h"
+#include "src/semantic/function.h"
#include "src/semantic/intrinsic.h"
#include "src/semantic/variable.h"
#include "src/type/alias_type.h"
@@ -47,225 +48,6 @@
namespace tint {
-namespace {
-
-using IntrinsicType = semantic::IntrinsicType;
-
-enum class IntrinsicDataType {
- kMixed,
- kFloatOrIntScalarOrVector,
- kFloatScalarOrVector,
- kIntScalarOrVector,
- kFloatVector,
- kFloatScalar,
- kMatrix,
- kBoolVector,
- kBoolScalar,
- kBoolScalarOrVector,
-};
-
-struct IntrinsicData {
- IntrinsicType intrinsic;
- uint32_t param_count;
- IntrinsicDataType data_type;
- uint32_t vector_size;
- bool all_types_match;
-};
-
-// Note, this isn't all the intrinsics. Some are handled specially before
-// we get to the generic code. See the ValidateCallExpr code below.
-constexpr const IntrinsicData kIntrinsicData[] = {
- {IntrinsicType::kAbs, 1, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
- true},
- {IntrinsicType::kAcos, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kAll, 1, IntrinsicDataType::kBoolVector, 0, false},
- {IntrinsicType::kAny, 1, IntrinsicDataType::kBoolVector, 0, false},
- {IntrinsicType::kArrayLength, 1, IntrinsicDataType::kMixed, 0, false},
- {IntrinsicType::kAsin, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kAtan, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kAtan2, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kCeil, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kClamp, 3, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
- true},
- {IntrinsicType::kCos, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kCosh, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kCountOneBits, 1, IntrinsicDataType::kIntScalarOrVector, 0,
- true},
- {IntrinsicType::kCross, 2, IntrinsicDataType::kFloatVector, 3, true},
- {IntrinsicType::kDeterminant, 1, IntrinsicDataType::kMatrix, 0, false},
- {IntrinsicType::kDistance, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
- false},
- {IntrinsicType::kDot, 2, IntrinsicDataType::kFloatVector, 0, false},
- {IntrinsicType::kDpdx, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kDpdxCoarse, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kDpdxFine, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kDpdy, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kDpdyCoarse, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kDpdyFine, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kExp, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kExp2, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kFaceForward, 3, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kFloor, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kFma, 3, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kFract, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kFrexp, 2, IntrinsicDataType::kMixed, 0, false},
- {IntrinsicType::kFwidth, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kFwidthCoarse, 1, IntrinsicDataType::kFloatScalarOrVector,
- 0, true},
- {IntrinsicType::kFwidthFine, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kInverseSqrt, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kLdexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kLength, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- false},
- {IntrinsicType::kLog, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kLog2, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kMax, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
- true},
- {IntrinsicType::kMin, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
- true},
- {IntrinsicType::kMix, 3, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kModf, 2, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kNormalize, 1, IntrinsicDataType::kFloatVector, 0, true},
- {IntrinsicType::kPack4x8Snorm, 1, IntrinsicDataType::kFloatVector, 4,
- false},
- {IntrinsicType::kPack4x8Unorm, 1, IntrinsicDataType::kFloatVector, 4,
- false},
- {IntrinsicType::kPack2x16Snorm, 1, IntrinsicDataType::kFloatVector, 2,
- false},
- {IntrinsicType::kPack2x16Unorm, 1, IntrinsicDataType::kFloatVector, 2,
- false},
- {IntrinsicType::kPack2x16Float, 1, IntrinsicDataType::kFloatVector, 2,
- false},
- {IntrinsicType::kPow, 2, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kReflect, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kReverseBits, 1, IntrinsicDataType::kIntScalarOrVector, 0,
- true},
- {IntrinsicType::kRound, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kSelect, 3, IntrinsicDataType::kMixed, 0, false},
- {IntrinsicType::kSign, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kSin, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kSinh, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kSmoothStep, 3, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
- {IntrinsicType::kSqrt, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kStep, 2, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kTan, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kTanh, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
- {IntrinsicType::kTrunc, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
- true},
-};
-
-constexpr const uint32_t kIntrinsicDataCount =
- sizeof(kIntrinsicData) / sizeof(IntrinsicData);
-
-bool IsValidType(type::Type* type,
- const Source& source,
- const std::string& name,
- const IntrinsicDataType& data_type,
- uint32_t vector_size,
- ValidatorImpl* impl) {
- type = type->UnwrapPtrIfNeeded();
- switch (data_type) {
- case IntrinsicDataType::kFloatOrIntScalarOrVector:
- if (!type->is_float_scalar_or_vector() &&
- !type->is_integer_scalar_or_vector()) {
- impl->add_error(source,
- "incorrect type for " + name +
- ". Requires int or float, scalar or vector value");
- return false;
- }
- break;
- case IntrinsicDataType::kFloatScalarOrVector:
- if (!type->is_float_scalar_or_vector()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires float scalar or vector value");
- return false;
- }
- break;
- case IntrinsicDataType::kIntScalarOrVector:
- if (!type->is_integer_scalar_or_vector()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires int scalar or vector value");
- return false;
- }
- break;
- case IntrinsicDataType::kFloatVector:
- if (!type->is_float_vector()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires float vector value");
- return false;
- }
- if (vector_size > 0 && vector_size != type->As<type::Vector>()->size()) {
- impl->add_error(source, "incorrect vector size for " + name +
- ". Requires " +
- std::to_string(vector_size) + " elements");
- return false;
- }
- break;
- case IntrinsicDataType::kFloatScalar:
- if (!type->Is<type::F32>()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires float scalar value");
- return false;
- }
- break;
- case IntrinsicDataType::kMatrix:
- if (!type->Is<type::Matrix>()) {
- impl->add_error(
- source, "incorrect type for " + name + ". Requires matrix value");
- return false;
- }
- break;
- case IntrinsicDataType::kBoolVector:
- if (!type->is_bool_vector()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires bool vector value");
- return false;
- }
- if (vector_size > 0 && vector_size != type->As<type::Vector>()->size()) {
- impl->add_error(source, "incorrect vector size for " + name +
- ". Requires " +
- std::to_string(vector_size) + " elements");
- return false;
- }
- break;
- case IntrinsicDataType::kBoolScalar:
- if (!type->Is<type::Bool>()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires bool scalar value");
- return false;
- }
- break;
- case IntrinsicDataType::kBoolScalarOrVector:
- if (!type->is_bool_scalar_or_vector()) {
- impl->add_error(source, "incorrect type for " + name +
- ". Requires bool scalar or vector value");
- return false;
- }
- break;
- default:
- break;
- }
-
- return true;
-}
-
-} // namespace
-
ValidatorImpl::ValidatorImpl(const Program* program) : program_(program) {}
ValidatorImpl::~ValidatorImpl() = default;
@@ -286,6 +68,13 @@
}
bool ValidatorImpl::Validate() {
+ if (!program_->IsValid()) {
+ // If we're attempting to validate an invalid program, fail with the
+ // program's diagnostics.
+ diags_.add(program_->Diagnostics());
+ return false;
+ }
+
// Validate global declarations in the order they appear in the module.
for (auto* decl : program_->AST().GlobalDeclarations()) {
if (auto* ty = decl->As<type::Type>()) {
@@ -643,253 +432,26 @@
return false;
}
- if (auto* intrinsic = call->Target()->As<semantic::Intrinsic>()) {
- const IntrinsicData* data = nullptr;
- for (uint32_t i = 0; i < kIntrinsicDataCount; ++i) {
- if (intrinsic->Type() == kIntrinsicData[i].intrinsic) {
- data = &kIntrinsicData[i];
- break;
- }
- }
+ auto* target = call->Target();
- if (data != nullptr) {
- std::string builtin = intrinsic->str();
- if (expr->params().size() != data->param_count) {
- add_error(expr->source(),
- "incorrect number of parameters for " + builtin +
- " expected " + std::to_string(data->param_count) +
- " got " + std::to_string(expr->params().size()));
- return false;
- }
+ if (target->Is<semantic::Intrinsic>()) {
+ // TODO(bclayton): Add intrinsic validation checks here.
+ return true;
+ }
- if (data->all_types_match) {
- // Check that the type is an acceptable one.
- if (!IsValidType(program_->TypeOf(expr), expr->source(), builtin,
- data->data_type, data->vector_size, this)) {
- return false;
- }
-
- // Check that all params match the result type.
- for (uint32_t i = 0; i < data->param_count; ++i) {
- if (program_->TypeOf(expr)->UnwrapPtrIfNeeded() !=
- program_->TypeOf(expr->params()[i])->UnwrapPtrIfNeeded()) {
- add_error(expr->params()[i]->source(),
- "expected parameter " + std::to_string(i) +
- "'s unwrapped type to match result type for " +
- builtin);
- return false;
- }
- }
- } else {
- if (data->data_type != IntrinsicDataType::kMixed) {
- auto* p0 = expr->params()[0];
- if (!IsValidType(program_->TypeOf(p0), p0->source(), builtin,
- data->data_type, data->vector_size, this)) {
- return false;
- }
-
- // Check that parameters are valid types.
- for (uint32_t i = 1; i < expr->params().size(); ++i) {
- if (program_->TypeOf(p0)->UnwrapPtrIfNeeded() !=
- program_->TypeOf(expr->params()[i])->UnwrapPtrIfNeeded()) {
- add_error(expr->source(),
- "parameter " + std::to_string(i) +
- "'s unwrapped type must match parameter 0's type");
- return false;
- }
- }
- } else {
- // Special cases.
- if (data->intrinsic == IntrinsicType::kFrexp) {
- auto* p0 = expr->params()[0];
- auto* p1 = expr->params()[1];
- auto* t0 = program_->TypeOf(p0)->UnwrapPtrIfNeeded();
- auto* t1 = program_->TypeOf(p1)->UnwrapPtrIfNeeded();
- if (!IsValidType(t0, p0->source(), builtin,
- IntrinsicDataType::kFloatScalarOrVector, 0,
- this)) {
- return false;
- }
- if (!IsValidType(t1, p1->source(), builtin,
- IntrinsicDataType::kIntScalarOrVector, 0, this)) {
- return false;
- }
-
- if (t0->is_scalar()) {
- if (!t1->is_scalar()) {
- add_error(
- expr->source(),
- "incorrect types for " + builtin +
- ". Parameters must be matched scalars or vectors");
- return false;
- }
- } else {
- if (t1->is_integer_scalar()) {
- add_error(
- expr->source(),
- "incorrect types for " + builtin +
- ". Parameters must be matched scalars or vectors");
- return false;
- }
- const auto* v0 = t0->As<type::Vector>();
- const auto* v1 = t1->As<type::Vector>();
- if (v0->size() != v1->size()) {
- add_error(expr->source(),
- "incorrect types for " + builtin +
- ". Parameter vector sizes must match");
- return false;
- }
- }
- }
-
- if (data->intrinsic == IntrinsicType::kSelect) {
- auto* type = program_->TypeOf(expr);
- auto* t0 = program_->TypeOf(expr->params()[0])->UnwrapPtrIfNeeded();
- auto* t1 = program_->TypeOf(expr->params()[1])->UnwrapPtrIfNeeded();
- auto* t2 = program_->TypeOf(expr->params()[2])->UnwrapPtrIfNeeded();
- if (!type->is_scalar() && !type->Is<type::Vector>()) {
- add_error(expr->source(),
- "incorrect type for " + builtin +
- ". Requires bool, int or float scalar or vector");
- return false;
- }
-
- if (type != t0 || type != t1) {
- add_error(expr->source(),
- "incorrect type for " + builtin +
- ". Value parameter types must match result type");
- return false;
- }
-
- if (!t2->is_bool_scalar_or_vector()) {
- add_error(expr->params()[2]->source(),
- "incorrect type for " + builtin +
- ". Selector must be a bool scalar or vector value");
- return false;
- }
-
- if (type->Is<type::Vector>()) {
- auto size = type->As<type::Vector>()->size();
- if (t2->is_scalar() || size != t2->As<type::Vector>()->size()) {
- add_error(expr->params()[2]->source(),
- "incorrect type for " + builtin +
- ". Selector must be a vector with the same "
- "number of elements as the result type");
- return false;
- }
- } else {
- if (!t2->is_scalar()) {
- add_error(expr->params()[2]->source(),
- "incorrect type for " + builtin +
- ". Selector must be a bool scalar to match "
- "scalar result type");
- return false;
- }
- }
- }
-
- if (data->intrinsic == IntrinsicType::kArrayLength) {
- if (!program_->TypeOf(expr)->UnwrapPtrIfNeeded()->Is<type::U32>()) {
- add_error(
- expr->source(),
- "incorrect type for " + builtin +
- ". Result type must be an unsigned int scalar value");
- return false;
- }
-
- auto* p0 = program_->TypeOf(expr->params()[0])->UnwrapPtrIfNeeded();
- if (!p0->Is<type::Array>() ||
- !p0->As<type::Array>()->IsRuntimeArray()) {
- add_error(expr->params()[0]->source(),
- "incorrect type for " + builtin +
- ". Input must be a runtime array");
- return false;
- }
- }
- }
-
- // Result types don't match parameter types.
- if (data->intrinsic == IntrinsicType::kAll ||
- data->intrinsic == IntrinsicType::kAny) {
- if (!IsValidType(program_->TypeOf(expr), expr->source(), builtin,
- IntrinsicDataType::kBoolScalar, 0, this)) {
- return false;
- }
- }
-
- if (data->intrinsic == IntrinsicType::kDot) {
- if (!IsValidType(program_->TypeOf(expr), expr->source(), builtin,
- IntrinsicDataType::kFloatScalar, 0, this)) {
- return false;
- }
- }
-
- if (semantic::IsDataPackingIntrinsic(data->intrinsic)) {
- if (!program_->TypeOf(expr)->Is<type::U32>()) {
- add_error(expr->source(),
- "incorrect type for " + builtin +
- ". Result type must be an unsigned int scalar");
- return false;
- }
- }
-
- if (data->intrinsic == IntrinsicType::kLength ||
- data->intrinsic == IntrinsicType::kDistance ||
- data->intrinsic == IntrinsicType::kDeterminant) {
- if (!IsValidType(program_->TypeOf(expr), expr->source(), builtin,
- IntrinsicDataType::kFloatScalar, 0, this)) {
- return false;
- }
- }
-
- // Must be a square matrix.
- if (data->intrinsic == IntrinsicType::kDeterminant) {
- const auto* matrix =
- program_->TypeOf(expr->params()[0])->As<type::Matrix>();
- if (matrix->rows() != matrix->columns()) {
- add_error(
- expr->params()[0]->source(),
- "incorrect type for " + builtin + ". Requires a square matrix");
- return false;
- }
- }
- }
-
- // Last parameter must be a pointer.
- if (data->intrinsic == IntrinsicType::kFrexp ||
- data->intrinsic == IntrinsicType::kModf) {
- auto* last_param = expr->params()[data->param_count - 1];
- if (!program_->TypeOf(last_param)->Is<type::Pointer>()) {
- add_error(last_param->source(), "incorrect type for " + builtin +
- ". Requires pointer value");
- return false;
- }
- }
+ if (auto* func = target->As<semantic::Function>()) {
+ if (current_function_ == func->Declaration()) {
+ add_error(expr->source(), "v-0004",
+ "recursion is not allowed: '" +
+ program_->Symbols().NameFor(current_function_->symbol()) +
+ "'");
+ return false;
}
return true;
}
- if (auto* ident = expr->func()->As<ast::IdentifierExpression>()) {
- auto symbol = ident->symbol();
- if (!function_stack_.has(symbol)) {
- add_error(expr->source(), "v-0005",
- "function must be declared before use: '" +
- program_->Symbols().NameFor(symbol) + "'");
- return false;
- }
- if (symbol == current_function_->symbol()) {
- add_error(expr->source(), "v-0004",
- "recursion is not allowed: '" +
- program_->Symbols().NameFor(symbol) + "'");
- return false;
- }
-
- } else {
- add_error(expr->source(), "Invalid function call expression");
- return false;
- }
-
- return true;
+ add_error(expr->source(), "Invalid function call expression");
+ return false;
}
bool ValidatorImpl::ValidateBadAssignmentToIdentifier(