// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_EXPLORER_INTERPRETER_VALUE_TRANSFORM_H_ #define CARBON_EXPLORER_INTERPRETER_VALUE_TRANSFORM_H_ #include "explorer/interpreter/value.h" namespace Carbon { // A no-op visitor used to implement `IsRecursivelyTransformable`. The // `operator()` function returns `true_type` if it's called with arguments that // can be used to construct `T`, and `false_type` otherwise. template struct IsRecursivelyTransformableVisitor { template auto operator()(Args&&... args) -> std::integral_constant>; }; // A type trait that indicates whether `T` is transformable. A transformable // type provides a function // // template void Decompose(F f) const; // // that takes a callable `f` and passes it an argument list that can be passed // to the constructor of `T` to create an equivalent value. template constexpr bool IsRecursivelyTransformable = false; template constexpr bool IsRecursivelyTransformable< T, decltype(std::declval().Decompose( IsRecursivelyTransformableVisitor{}))> = true; // Base class for transforms of visitable data types. template class TransformBase { public: explicit TransformBase(Nonnull arena) : arena_(arena) {} template auto Transform(T&& v) -> decltype(auto) { return static_cast(*this)(std::forward(v)); } // Transformable values are recursively transformed by default. template , void*> = nullptr> auto operator()(const T& value) -> T { return value.Decompose([&](auto&&... elements) { return T{Transform(decltype(elements)(elements))...}; }); } // Transformable pointers are recursively transformed and reallocated by // default. template , void*> = nullptr> auto operator()(Nonnull value) -> auto{ return value->Decompose([&](auto&&... elements) { return AllocateTrait::New(arena_, Transform(decltype(elements)(elements))...); }); } // Fundamental types like `int` are assumed to not need transformation. template auto operator()(const T& v) -> std::enable_if_t, T> { return v; } auto operator()(const std::string& str) -> const std::string& { return str; } auto operator()(llvm::StringRef str) -> llvm::StringRef { return str; } // Transform `optional` by transforming the `T` if it's present. template auto operator()(const std::optional& v) -> std::optional { if (!v) { return std::nullopt; } return Transform(*v); } // Transform `pair` by transforming T and U. template auto operator()(const std::pair& pair) -> std::pair { return std::pair{Transform(pair.first), Transform(pair.second)}; } // Transform `vector` by transforming its elements. template auto operator()(const std::vector& vec) -> std::vector { std::vector result; result.reserve(vec.size()); for (auto& value : vec) { result.push_back(Transform(value)); } return result; } // Transform `map` by transforming its keys and values. template auto operator()(const std::map& map) -> std::map { std::map result; for (auto& [key, value] : map) { result.insert({Transform(key), Transform(value)}); } return result; } // Transform `llvm::StringMap` by transforming its keys and values. template auto operator()(const llvm::StringMap& map) -> llvm::StringMap { llvm::StringMap result; for (const auto& it : map) { result.insert({Transform(it.first()), Transform(it.second)}); } return result; } private: Nonnull arena_; }; // Base class for transforms of `Value`s. template class ValueTransform : public TransformBase { public: using TransformBase::TransformBase; using TransformBase::operator(); // Leave references to AST nodes alone by default. // The 'int = 0' parameter avoids this function hiding the `operator()(const // T*)` in the base class. We can remove this once we start using a compiler // that implements P1787R6. template auto operator()(Nonnull node, int /*unused*/ = 0) -> std::enable_if_t, Nonnull> { return node; } auto operator()(Nonnull stack_fragment) -> Nonnull { return stack_fragment; } auto operator()(Address addr) -> Address { return addr; } auto operator()(ValueNodeView value_node) -> ValueNodeView { return value_node; } // For a type that provides a `Visit` function to visit the most-derived // object, visit and transform that most-derived object. template auto TransformDerived(Nonnull value) -> R { return value->template Visit([&](const auto* derived_value) { using DerivedType = std::remove_pointer_t; static_assert(IsRecursivelyTransformable); return this->Transform(derived_value); }); } // For values, dispatch on the value kind and recursively transform. auto operator()(Nonnull value) -> Nonnull { return TransformDerived>(value); } // Provide a more precise type from transforming a `Witness`. auto operator()(Nonnull value) -> Nonnull { return llvm::cast(this->Transform(llvm::cast(value))); } // For elements, dispatch on the element kind and recursively transform. auto operator()(Nonnull elem) -> Nonnull { return TransformDerived>(elem); } // Preserve vtable during transformation. auto operator()(Nonnull vtable) -> Nonnull { return vtable; } // Preserve class value ptr during transformation. auto operator()(Nonnull value_ptr) -> Nonnull { return value_ptr; } }; } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_VALUE_TRANSFORM_H_