dawn-cmake/src/clone_context.h
Ben Clayton 5b36d2c612 Remove all unnecessary includes
All includes from .cc to .h are preserved, even when transitively included.

It's clear that there are far too many includes in header files, and we should be more aggressive with forward declarations. tint:532 will continue to track this work.

There are, however, plenty of includes that have accumulated over time which are no longer required directly or transitively, so this change starts with a clean slate of *required* includes.

Bug: tint:532
Change-Id: Ie1718dad565f8309fa180ef91bcf3920e76dba18
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44042
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
2021-03-09 11:11:17 +00:00

401 lines
14 KiB
C++

// Copyright 2020 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_CLONE_CONTEXT_H_
#define SRC_CLONE_CONTEXT_H_
#include <functional>
#include <unordered_map>
#include <utility>
#include <vector>
#include "src/castable.h"
#include "src/debug.h"
#include "src/symbol.h"
#include "src/traits.h"
namespace tint {
// Forward declarations
class CloneContext;
class Program;
class ProgramBuilder;
namespace ast {
class FunctionList;
} // namespace ast
/// Cloneable is the base class for all objects that can be cloned
class Cloneable : public Castable<Cloneable> {
public:
/// Performs a deep clone of this object using the CloneContext `ctx`.
/// @param ctx the clone context
/// @return the newly cloned object
virtual Cloneable* Clone(CloneContext* ctx) const = 0;
};
/// CloneContext holds the state used while cloning AST nodes and types.
class CloneContext {
/// ParamTypeIsPtrOf<F, T>::value is true iff the first parameter of
/// F is a pointer of (or derives from) type T.
template <typename F, typename T>
using ParamTypeIsPtrOf = traits::IsTypeOrDerived<
typename std::remove_pointer<traits::ParamTypeT<F, 0>>::type,
T>;
public:
/// SymbolTransform is a function that takes a symbol and returns a new
/// symbol.
using SymbolTransform = std::function<Symbol(Symbol)>;
/// Constructor
/// @param to the target ProgramBuilder to clone into
/// @param from the source Program to clone from
CloneContext(ProgramBuilder* to, Program const* from);
/// Destructor
~CloneContext();
/// Clones the Node or type::Type `a` into the ProgramBuilder #dst if `a` is
/// not null. If `a` is null, then Clone() returns null. If `a` has been
/// cloned already by this CloneContext then the same cloned pointer is
/// returned.
///
/// Clone() may use a function registered with ReplaceAll() to create a
/// transformed version of the object. See ReplaceAll() for more information.
///
/// The Node or type::Type `a` must be owned by the Program #src.
///
/// @param a the `Node` or `type::Type` to clone
/// @return the cloned node
template <typename T>
T* Clone(T* a) {
// If the input is nullptr, there's nothing to clone - just return nullptr.
if (a == nullptr) {
return nullptr;
}
// See if we've already cloned this object - if we have return the
// previously cloned pointer.
// If we haven't cloned this before, try cloning using a replacer transform.
if (auto* c = LookupOrTransform(a)) {
return CheckedCast<T>(c);
}
// First time clone and no replacer transforms matched.
// Clone with T::Clone().
auto* c = a->Clone(this);
cloned_.emplace(a, c);
return CheckedCast<T>(c);
}
/// Clones the Node or type::Type `a` into the ProgramBuilder #dst if `a` is
/// not null. If `a` is null, then Clone() returns null. If `a` has been
/// cloned already by this CloneContext then the same cloned pointer is
/// returned.
///
/// Unlike Clone(), this method does not invoke or use any transformations
/// registered by ReplaceAll().
///
/// The Node or type::Type `a` must be owned by the Program #src.
///
/// @param a the `Node` or `type::Type` to clone
/// @return the cloned node
template <typename T>
T* CloneWithoutTransform(T* a) {
// If the input is nullptr, there's nothing to clone - just return nullptr.
if (a == nullptr) {
return nullptr;
}
// Have we seen this object before? If so, return the previously cloned
// version instead of making yet another copy.
auto it = cloned_.find(a);
if (it != cloned_.end()) {
return CheckedCast(it->second);
}
// First time clone and no replacer transforms matched.
// Clone with T::Clone().
auto* c = a->Clone(this);
cloned_.emplace(a, c);
return CheckedCast(c);
}
/// Clones the Source `s` into `dst`
/// TODO(bclayton) - Currently this 'clone' is a shallow copy. If/when
/// `Source.File`s are owned by the Program this should make a copy of the
/// file.
/// @param s the `Source` to clone
/// @return the cloned source
Source Clone(const Source& s) const { return s; }
/// Clones the Symbol `s` into `dst`
///
/// The Symbol `s` must be owned by the Program #src.
///
/// @param s the Symbol to clone
/// @return the cloned source
Symbol Clone(const Symbol& s) const;
/// Clones each of the elements of the vector `v` into the ProgramBuilder
/// #dst.
///
/// All the elements of the vector `v` must be owned by the Program #src.
///
/// @param v the vector to clone
/// @return the cloned vector
template <typename T>
std::vector<T> Clone(const std::vector<T>& v) {
std::vector<T> out;
out.reserve(v.size());
for (auto& el : v) {
out.emplace_back(Clone(el));
}
return out;
}
/// Clones each of the elements of the vector `v` into the ProgramBuilder
/// #dst, inserting any additional elements into the list that were registered
/// with calls to InsertBefore().
///
/// All the elements of the vector `v` must be owned by the Program #src.
///
/// @param v the vector to clone
/// @return the cloned vector
template <typename T>
std::vector<T*> Clone(const std::vector<T*>& v) {
std::vector<T*> out;
out.reserve(v.size());
for (auto& el : v) {
auto it = insert_before_.find(el);
if (it != insert_before_.end()) {
for (auto insert : it->second) {
out.emplace_back(CheckedCast<T>(insert));
}
}
out.emplace_back(Clone(el));
}
return out;
}
/// Clones each of the elements of the vector `v` into the ProgramBuilder
/// #dst.
///
/// All the elements of the vector `v` must be owned by the Program #src.
///
/// @param v the vector to clone
/// @return the cloned vector
ast::FunctionList Clone(const ast::FunctionList& v);
/// ReplaceAll() registers `replacer` to be called whenever the Clone() method
/// is called with a Cloneable type that matches (or derives from) the type of
/// the single parameter of `replacer`.
/// The returned Cloneable of `replacer` will be used as the replacement for
/// all references to the object that's being cloned. This returned Cloneable
/// must be owned by the Program #dst.
///
/// `replacer` must be function-like with the signature: `T* (T*)`
/// where `T` is a type deriving from Cloneable.
///
/// If `replacer` returns a nullptr then Clone() will call `T::Clone()` to
/// clone the object.
///
/// Example:
///
/// ```
/// // Replace all ast::UintLiterals with the number 42
/// CloneCtx ctx(&out, in);
/// ctx.ReplaceAll([&] (ast::UintLiteral* l) {
/// return ctx->dst->create<ast::UintLiteral>(
/// ctx->Clone(l->source()),
/// ctx->Clone(l->type()),
/// 42);
/// });
/// ctx.Clone();
/// ```
///
/// @warning a single handler can only be registered for any given type.
/// Attempting to register two handlers for the same type will result in an
/// ICE.
/// @warning The replacement object must be of the correct type for all
/// references of the original object. A type mismatch will result in an
/// assertion in debug builds, and undefined behavior in release builds.
/// @param replacer a function or function-like object with the signature
/// `T* (T*)`, where `T` derives from Cloneable
/// @returns this CloneContext so calls can be chained
template <typename F>
traits::EnableIf<ParamTypeIsPtrOf<F, Cloneable>::value, CloneContext>&
ReplaceAll(F&& replacer) {
using TPtr = traits::ParamTypeT<F, 0>;
using T = typename std::remove_pointer<TPtr>::type;
for (auto& transform : transforms_) {
if (transform.typeinfo->Is(TypeInfo::Of<T>()) ||
TypeInfo::Of<T>().Is(*transform.typeinfo)) {
TINT_ICE(Diagnostics())
<< "ReplaceAll() called with a handler for type "
<< TypeInfo::Of<T>().name
<< " that is already handled by a handler for type "
<< transform.typeinfo->name;
return *this;
}
}
CloneableTransform transform;
transform.typeinfo = &TypeInfo::Of<T>();
transform.function = [=](Cloneable* in) { return replacer(in->As<T>()); };
transforms_.emplace_back(std::move(transform));
return *this;
}
/// ReplaceAll() registers `replacer` to be called whenever the Clone() method
/// is called with a Symbol.
/// The returned symbol of `replacer` will be used as the replacement for
/// all references to the symbol that's being cloned. This returned Symbol
/// must be owned by the Program #dst.
/// @param replacer a function the signature `Symbol(Symbol)`.
/// @warning a SymbolTransform can only be registered once. Attempting to
/// register a SymbolTransform more than once will result in an ICE.
/// @returns this CloneContext so calls can be chained
CloneContext& ReplaceAll(const SymbolTransform& replacer) {
if (symbol_transform_) {
TINT_ICE(Diagnostics()) << "ReplaceAll(const SymbolTransform&) called "
"multiple times on the same CloneContext";
return *this;
}
symbol_transform_ = replacer;
return *this;
}
/// Replace replaces all occurrences of `what` in #src with `with` in #dst
/// when calling Clone().
/// @param what a pointer to the object in #src that will be replaced with
/// `with`
/// @param with a pointer to the replacement object owned by #dst that will be
/// used as a replacement for `what`
/// @warning The replacement object must be of the correct type for all
/// references of the original object. A type mismatch will result in an
/// assertion in debug builds, and undefined behavior in release builds.
/// @returns this CloneContext so calls can be chained
template <typename WHAT, typename WITH>
CloneContext& Replace(WHAT* what, WITH* with) {
cloned_[what] = with;
return *this;
}
/// Inserts `object` before `before` whenever a vector containing `object` is
/// cloned.
/// @param before a pointer to the object in #src
/// @param object a pointer to the object in #dst that will be inserted before
/// any occurrence of the clone of `before`
/// @returns this CloneContext so calls can be chained
template <typename BEFORE, typename OBJECT>
CloneContext& InsertBefore(BEFORE* before, OBJECT* object) {
auto& list = insert_before_[before];
list.emplace_back(object);
return *this;
}
/// Clone performs the clone of the Program's AST nodes, types and symbols
/// from #src to #dst. Semantic nodes are not cloned, as these will be rebuilt
/// when the ProgramBuilder #dst builds its Program.
void Clone();
/// The target ProgramBuilder to clone into.
ProgramBuilder* const dst;
/// The source Program to clone from.
Program const* const src;
private:
struct CloneableTransform {
/// Constructor
CloneableTransform();
/// Copy constructor
/// @param other the CloneableTransform to copy
CloneableTransform(const CloneableTransform& other);
/// Destructor
~CloneableTransform();
// TypeInfo of the Cloneable that the transform operates on
const TypeInfo* typeinfo;
std::function<Cloneable*(Cloneable*)> function;
};
CloneContext(const CloneContext&) = delete;
CloneContext& operator=(const CloneContext&) = delete;
/// LookupOrTransform is the template-independent logic of Clone().
/// This is outside of Clone() to reduce the amount of template-instantiated
/// code.
Cloneable* LookupOrTransform(Cloneable* a) {
// Have we seen this object before? If so, return the previously cloned
// version instead of making yet another copy.
auto it = cloned_.find(a);
if (it != cloned_.end()) {
return it->second;
}
// Attempt to clone using the registered replacer functions.
auto& typeinfo = a->TypeInfo();
for (auto& transform : transforms_) {
if (!typeinfo.Is(*transform.typeinfo)) {
continue;
}
if (Cloneable* c = transform.function(a)) {
cloned_.emplace(a, c);
return c;
}
break;
}
// No luck, Clone() will have to call T::Clone().
return nullptr;
}
/// Cast `obj` from type `FROM` to type `TO`, returning the cast object.
/// Reports an internal compiler error if the cast failed.
template <typename TO, typename FROM>
TO* CheckedCast(FROM* obj) {
TO* cast = obj->template As<TO>();
if (!cast) {
TINT_ICE(Diagnostics()) << "Cloned object was not of the expected type";
}
return cast;
}
/// @returns the diagnostic list of #dst
diag::List& Diagnostics() const;
/// A vector of Cloneable*
using CloneableList = std::vector<Cloneable*>;
/// A map of object in #src to their cloned equivalent in #dst
std::unordered_map<Cloneable*, Cloneable*> cloned_;
/// A map of object in #src to the list of cloned objects in #dst.
/// Clone(const std::vector<T*>& v) will use this to insert the map-value list
/// into the target vector/ before cloning and inserting the map-key.
std::unordered_map<Cloneable*, CloneableList> insert_before_;
/// Cloneable transform functions registered with ReplaceAll()
std::vector<CloneableTransform> transforms_;
/// Symbol transform registered with ReplaceAll()
SymbolTransform symbol_transform_;
};
} // namespace tint
#endif // SRC_CLONE_CONTEXT_H_