dawn-cmake/src/sem/statement.h
Ben Clayton ea3eee9885 resolver: Migrate validation to behavior analysis
Migrate some of the validation logic over to use the results of behavior
analysis.

The most significant changes are:
* Unreachable-statements now consider merge-points of control flow. For
  example, if all branches of a if-statement or switch-statement either
  return or discard, the next statement will be considered unreachable.
* Unreachable statements are no longer an error, but a warning. See
  https://github.com/gpuweb/gpuweb/issues/2378.
* Statements that follow a loops that does not break, or have a
  conditional will now be considered unreachable.
* Unreachable statements produced by the SPIR-V reader are now removed
  using the new RemoveUnreachableStatements transform.

Some other new changes include additional validation for the continuing
block for for-loops, to match the rules of a loop continuing block.
The new cases this validation is testing for are not expressible in
WGSL, but some transforms may produce complex continuing statements that
might violate these rules. All the writers are able to decay these
complex for-loop continuing statements to regular loops.

Bug: tint:1302
Change-Id: I0d8a48c73d5d5c30a1cddf92cc3383a692a58e61
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71500
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
2021-12-03 17:51:48 +00:00

189 lines
5.8 KiB
C++

// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_SEM_STATEMENT_H_
#define SRC_SEM_STATEMENT_H_
#include "src/sem/behavior.h"
#include "src/sem/node.h"
// Forward declarations
namespace tint {
namespace ast {
class Function;
class Statement;
} // namespace ast
namespace sem {
class BlockStatement;
} // namespace sem
} // namespace tint
namespace tint {
namespace sem {
/// Forward declaration
class CompoundStatement;
class Function;
namespace detail {
/// FindFirstParentReturn is a traits helper for determining the return type for
/// the template member function Statement::FindFirstParent().
/// For zero or multiple template arguments, FindFirstParentReturn::type
/// resolves to CompoundStatement.
template <typename... TYPES>
struct FindFirstParentReturn {
/// The pointer type returned by Statement::FindFirstParent()
using type = CompoundStatement;
};
/// A specialization of FindFirstParentReturn for a single template argument.
/// FindFirstParentReturn::type resolves to the single template argument.
template <typename T>
struct FindFirstParentReturn<T> {
/// The pointer type returned by Statement::FindFirstParent()
using type = T;
};
template <typename... TYPES>
using FindFirstParentReturnType =
typename FindFirstParentReturn<TYPES...>::type;
} // namespace detail
/// Statement holds the semantic information for a statement.
class Statement : public Castable<Statement, Node> {
public:
/// Constructor
/// @param declaration the AST node for this statement
/// @param parent the owning statement
/// @param function the owning function
Statement(const ast::Statement* declaration,
const CompoundStatement* parent,
const sem::Function* function);
/// Destructor
~Statement() override;
/// @return the AST node for this statement
const ast::Statement* Declaration() const { return declaration_; }
/// @return the statement that encloses this statement
const CompoundStatement* Parent() const { return parent_; }
/// @returns the closest enclosing parent that satisfies the given predicate,
/// which may be the statement itself, or nullptr if no match is found.
/// @param pred a predicate that the resulting block must satisfy
template <typename Pred>
const CompoundStatement* FindFirstParent(Pred&& pred) const;
/// @returns the closest enclosing parent that is of one of the types in
/// `TYPES`, which may be the statement itself, or nullptr if no match is
/// found. If `TYPES` is a single template argument, the return type is a
/// pointer to that template argument type, otherwise a CompoundStatement
/// pointer is returned.
template <typename... TYPES>
const detail::FindFirstParentReturnType<TYPES...>* FindFirstParent() const;
/// @return the closest enclosing block for this statement
const BlockStatement* Block() const;
/// @returns the function that owns this statement
const sem::Function* Function() const { return function_; }
/// @return the behaviors of this statement
const sem::Behaviors& Behaviors() const { return behaviors_; }
/// @return the behaviors of this statement
sem::Behaviors& Behaviors() { return behaviors_; }
/// @returns true if this statement is reachable by control flow according to
/// the behavior analysis
bool IsReachable() const { return is_reachable_; }
/// @param is_reachable whether this statement is reachable by control flow
/// according to the behavior analysis
void SetIsReachable(bool is_reachable) { is_reachable_ = is_reachable; }
private:
const ast::Statement* const declaration_;
const CompoundStatement* const parent_;
const sem::Function* const function_;
sem::Behaviors behaviors_{sem::Behavior::kNext};
bool is_reachable_ = true;
};
/// CompoundStatement is the base class of statements that can hold other
/// statements.
class CompoundStatement : public Castable<Statement, Statement> {
public:
/// Constructor
/// @param declaration the AST node for this statement
/// @param statement the owning statement
/// @param function the owning function
CompoundStatement(const ast::Statement* declaration,
const CompoundStatement* statement,
const sem::Function* function);
/// Destructor
~CompoundStatement() override;
};
template <typename Pred>
const CompoundStatement* Statement::FindFirstParent(Pred&& pred) const {
if (auto* self = As<CompoundStatement>()) {
if (pred(self)) {
return self;
}
}
const auto* curr = parent_;
while (curr && !pred(curr)) {
curr = curr->Parent();
}
return curr;
}
template <typename... TYPES>
const detail::FindFirstParentReturnType<TYPES...>* Statement::FindFirstParent()
const {
using ReturnType = detail::FindFirstParentReturnType<TYPES...>;
if (sizeof...(TYPES) == 1) {
if (auto* p = As<ReturnType>()) {
return p;
}
const auto* curr = parent_;
while (curr) {
if (auto* p = curr->As<ReturnType>()) {
return p;
}
curr = curr->Parent();
}
} else {
if (IsAnyOf<TYPES...>()) {
return As<ReturnType>();
}
const auto* curr = parent_;
while (curr) {
if (curr->IsAnyOf<TYPES...>()) {
return curr->As<ReturnType>();
}
curr = curr->Parent();
}
}
return nullptr;
}
} // namespace sem
} // namespace tint
#endif // SRC_SEM_STATEMENT_H_