Files
2026-07-13 12:40:42 +08:00

500 lines
21 KiB
C++

// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
//
// 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.
#pragma once
#ifndef _WIN32
#include <experimental/type_traits>
#endif
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#include "paddle/ap/include/axpr/adt.h"
#include "paddle/ap/include/axpr/builtin_class_instance.h"
#include "paddle/ap/include/axpr/builtin_func_type.h"
#include "paddle/ap/include/axpr/class_instance.h"
#include "paddle/ap/include/axpr/constants.h"
#include "paddle/ap/include/axpr/type.h"
namespace ap::axpr {
template <typename ValueT>
class InterpreterBase;
template <typename ValueT>
using BuiltinUnaryFuncImpl =
std::variant<adt::Nothing,
adt::Result<ValueT> (*)(const ValueT&),
adt::Result<ValueT> (*)(InterpreterBase<ValueT>*,
const ValueT&)>;
template <typename ValueT>
struct BuiltinUnaryFunc : public BuiltinUnaryFuncImpl<ValueT> {
using BuiltinUnaryFuncImpl<ValueT>::BuiltinUnaryFuncImpl;
ADT_DEFINE_VARIANT_METHODS(BuiltinUnaryFuncImpl<ValueT>);
};
template <typename ValueT, BuiltinFuncType<ValueT> BuiltinFunc>
adt::Result<ValueT> UnaryFuncReturnCapturedValue(const ValueT&) {
return BuiltinFunc;
}
template <typename ValueT>
using BuiltinBinaryFuncImpl =
std::variant<adt::Nothing,
adt::Result<ValueT> (*)(const ValueT&, const ValueT&),
adt::Result<ValueT> (*)(
InterpreterBase<ValueT>*, const ValueT&, const ValueT&)>;
template <typename ValueT>
struct BuiltinBinaryFunc : public BuiltinBinaryFuncImpl<ValueT> {
using BuiltinBinaryFuncImpl<ValueT>::BuiltinBinaryFuncImpl;
ADT_DEFINE_VARIANT_METHODS(BuiltinBinaryFuncImpl<ValueT>);
};
template <typename ValueT>
struct EmptyMethodClass {
template <typename BuiltinUnarySymbol>
static BuiltinUnaryFunc<ValueT> GetBuiltinUnaryFunc() {
return adt::Nothing{};
}
template <typename BuiltinBinarySymbol>
static BuiltinBinaryFunc<ValueT> GetBuiltinBinaryFunc() {
return adt::Nothing{};
}
};
template <typename ValueT, typename T>
struct MethodClassImpl;
namespace detail {
#ifndef _WIN32
template <template <typename...> class Op, typename... Args>
constexpr bool is_detected_v = std::experimental::is_detected_v<Op, Args...>;
#else
template <template <typename...> class Op, typename, typename... Args>
struct detector : std::false_type {};
template <template <typename...> class Op, typename... Args>
struct detector<Op, std::void_t<Op<Args...>>, Args...> : std::true_type {};
template <template <typename...> class Op, typename... Args>
constexpr bool is_detected_v = detector<Op, void, Args...>::value;
#endif
template <typename ValueT, typename T, typename BuiltinSymbol>
struct BuiltinMethodHelperImpl;
#define SPECIALIZE_BuiltinMethodHelperImpl(symbol_name, op) \
template <typename ValueT, typename T> \
struct BuiltinMethodHelperImpl<ValueT, T, builtin_symbol::symbol_name> { \
using This = \
BuiltinMethodHelperImpl<ValueT, T, builtin_symbol::symbol_name>; \
\
template <typename ObjT> \
using UnaryMethodRetT = \
decltype(std::declval<MethodClassImpl<ValueT, ObjT>&>().symbol_name( \
std::declval<const ObjT&>())); \
\
template <typename ObjT> \
using HighOrderUnaryMethodRetT = \
decltype(std::declval<MethodClassImpl<ValueT, ObjT>&>().symbol_name( \
std::declval<InterpreterBase<ValueT>*>(), \
std::declval<const ObjT&>())); \
\
static constexpr bool HasUnaryMethod() { \
return builtin_symbol::symbol_name::num_operands == 1 && \
is_detected_v<UnaryMethodRetT, T>; \
} \
\
static constexpr bool HasHighOrderUnaryMethod() { \
return builtin_symbol::symbol_name::num_operands == 1 && \
is_detected_v<HighOrderUnaryMethodRetT, T>; \
} \
\
static adt::Result<ValueT> UnaryCall(const T& obj) { \
if constexpr (This::HasUnaryMethod()) { \
return MethodClassImpl<ValueT, T>{}.symbol_name(obj); \
} else { \
return adt::errors::RuntimeError{"`" #symbol_name \
"` method not found."}; \
} \
} \
\
static adt::Result<ValueT> HighOrderUnaryCall( \
InterpreterBase<ValueT>* interpreter, const T& obj) { \
if constexpr (This::HasHighOrderUnaryMethod()) { \
return MethodClassImpl<ValueT, T>{}.symbol_name(interpreter, obj); \
} else { \
return adt::errors::RuntimeError{"`" #symbol_name \
"` method not found."}; \
} \
} \
\
template <typename ObjT> \
using BinaryMethodRetT = \
decltype(std::declval<MethodClassImpl<ValueT, ObjT>&>().symbol_name( \
std::declval<const ObjT&>(), std::declval<const ValueT&>())); \
\
template <typename ObjT> \
using HighOrderBinaryMethodRetT = \
decltype(std::declval<MethodClassImpl<ValueT, ObjT>&>().symbol_name( \
std::declval<InterpreterBase<ValueT>*>(), \
std::declval<const ObjT&>(), \
std::declval<const ValueT&>())); \
\
static constexpr bool HasBinaryMethod() { \
return builtin_symbol::symbol_name::num_operands == 2 && \
is_detected_v<BinaryMethodRetT, T>; \
} \
\
static constexpr bool HasHighOrderBinaryMethod() { \
return builtin_symbol::symbol_name::num_operands == 2 && \
is_detected_v<HighOrderBinaryMethodRetT, T>; \
} \
\
static adt::Result<ValueT> BinaryCall(const T& obj, const ValueT& arg) { \
if constexpr (This::HasBinaryMethod()) { \
return MethodClassImpl<ValueT, T>{}.symbol_name(obj, arg); \
} else { \
return adt::errors::RuntimeError{"`" #symbol_name \
"` method not found."}; \
} \
} \
static adt::Result<ValueT> HighOrderBinaryCall( \
InterpreterBase<ValueT>* interpreter, \
const T& obj, \
const ValueT& arg) { \
if constexpr (This::HasHighOrderBinaryMethod()) { \
return MethodClassImpl<ValueT, T>{}.symbol_name( \
interpreter, obj, arg); \
} else { \
return adt::errors::RuntimeError{"`" #symbol_name \
"` method not found."}; \
} \
} \
};
AXPR_FOR_EACH_SYMBOL_OP(SPECIALIZE_BuiltinMethodHelperImpl)
#undef SPECIALIZE_BuiltinMethodHelperImpl
template <typename VariantT, typename T>
struct DirectAlternative {
static adt::Result<T> TryGet(const VariantT& val) {
if (val.template Has<T>()) {
return val.template Get<T>();
}
return adt::errors::TypeError{"cast failed."};
}
};
template <typename ValueT, typename T>
struct IndirectAlternative {
static adt::Result<T> TryGet(const ValueT& val) {
using TypeT = typename TypeTrait<ValueT>::TypeT;
ADT_LET_CONST_REF(type, DirectAlternative<ValueT, TypeT>::TryGet(val));
return DirectAlternative<TypeT, T>::TryGet(type);
}
};
template <typename ValueT,
typename T,
typename BuiltinSymbol,
template <typename, typename>
class Alternative>
struct BuiltinMethodHelper {
using This = BuiltinMethodHelper;
using Impl = BuiltinMethodHelperImpl<ValueT, T, BuiltinSymbol>;
static constexpr bool HasUnaryMethod() { return Impl::HasUnaryMethod(); }
static constexpr bool HasHighOrderUnaryMethod() {
return Impl::HasHighOrderUnaryMethod();
}
static constexpr BuiltinUnaryFunc<ValueT> GetBuiltinUnaryMethod() {
return &This::MakeBuiltinUnaryFunc<&Impl::UnaryCall>;
}
static constexpr BuiltinUnaryFunc<ValueT> GetBuiltinHighOrderUnaryMethod() {
return &This::MakeBuiltinHighOrderUnaryFunc<&Impl::HighOrderUnaryCall>;
}
static BuiltinUnaryFunc<ValueT> GetBuiltinUnaryFunc() {
static const MethodClassImpl<ValueT, T>
detect_specialization_of_method_class_impl;
(void)detect_specialization_of_method_class_impl;
if constexpr (HasUnaryMethod()) {
return GetBuiltinUnaryMethod();
} else if constexpr (HasHighOrderUnaryMethod()) {
return GetBuiltinHighOrderUnaryMethod();
} else if constexpr (HasDefaultUnaryMethod()) {
return MethodClassImpl<ValueT,
T>::template GetBuiltinUnaryFunc<BuiltinSymbol>();
} else {
return adt::Nothing{};
}
}
template <typename ObjT>
using UnaryMethodRetT =
decltype(MethodClassImpl<ValueT, ObjT>::template GetBuiltinUnaryFunc<
BuiltinSymbol>());
static constexpr bool HasDefaultUnaryMethod() {
return is_detected_v<UnaryMethodRetT, T>;
}
static BuiltinBinaryFunc<ValueT> GetBuiltinBinaryFunc() {
static const MethodClassImpl<ValueT, T>
detect_specialization_of_method_class_impl;
(void)detect_specialization_of_method_class_impl;
if constexpr (Impl::HasBinaryMethod()) {
return &This::MakeBuiltinBinaryFunc<&Impl::BinaryCall>;
} else if constexpr (Impl::HasHighOrderBinaryMethod()) {
return &This::MakeBuiltinHighOrderBinaryFunc<&Impl::HighOrderBinaryCall>;
} else if constexpr (HasDefaultBinaryMethod()) {
return MethodClassImpl<ValueT,
T>::template GetBuiltinBinaryFunc<BuiltinSymbol>();
} else {
return adt::Nothing{};
}
}
template <typename ObjT>
using BinaryMethodRetT =
decltype(MethodClassImpl<ValueT, ObjT>::template GetBuiltinBinaryFunc<
BuiltinSymbol>());
static constexpr bool HasDefaultBinaryMethod() {
return is_detected_v<BinaryMethodRetT, T>;
}
template <adt::Result<ValueT> (*UnaryFunc)(const T&)>
static adt::Result<ValueT> MakeBuiltinUnaryFunc(const ValueT& obj_val) {
ADT_LET_CONST_REF(obj, Alternative<ValueT, T>::TryGet(obj_val));
const auto& ret = UnaryFunc(obj);
return ret;
}
template <adt::Result<ValueT> (*UnaryFunc)(InterpreterBase<ValueT>*,
const T&)>
static adt::Result<ValueT> MakeBuiltinHighOrderUnaryFunc(
InterpreterBase<ValueT>* interpreter, const ValueT& obj_val) {
ADT_LET_CONST_REF(obj, Alternative<ValueT, T>::TryGet(obj_val));
const auto& ret = UnaryFunc(interpreter, obj);
return ret;
}
template <adt::Result<ValueT> (*BinaryFunc)(const T&, const ValueT&)>
static adt::Result<ValueT> MakeBuiltinBinaryFunc(const ValueT& obj_val,
const ValueT& arg) {
ADT_LET_CONST_REF(obj, Alternative<ValueT, T>::TryGet(obj_val));
return BinaryFunc(obj, arg);
}
template <adt::Result<ValueT> (*BinaryFunc)(
InterpreterBase<ValueT>*, const T&, const ValueT&)>
static adt::Result<ValueT> MakeBuiltinHighOrderBinaryFunc(
InterpreterBase<ValueT>* interpreter,
const ValueT& obj_val,
const ValueT& arg) {
ADT_LET_CONST_REF(obj, Alternative<ValueT, T>::TryGet(obj_val));
return BinaryFunc(interpreter, obj, arg);
}
};
} // namespace detail
template <typename ValueT>
struct MethodClass {
using This = MethodClass;
static BuiltinUnaryFunc<ValueT> Hash(const ValueT& val) {
using S = builtin_symbol::Hash;
return val.Match([](const auto& impl) -> BuiltinUnaryFunc<ValueT> {
using T = std::decay_t<decltype(impl)>;
if constexpr (IsType<T>()) {
return impl.Match([](const auto& type_impl)
-> BuiltinUnaryFunc<ValueT> {
using TT = std::decay_t<decltype(type_impl)>;
using Helper = detail::
BuiltinMethodHelper<ValueT, TT, S, detail::IndirectAlternative>;
if constexpr (Helper::HasUnaryMethod()) {
return Helper::GetBuiltinUnaryMethod();
} else if constexpr (Helper::HasHighOrderUnaryMethod()) {
return Helper::GetBuiltinHighOrderUnaryMethod();
} else {
return &This::TypeDefaultHash<TT>;
}
});
} else {
using Helper = detail::
BuiltinMethodHelper<ValueT, T, S, detail::DirectAlternative>;
if constexpr (Helper::HasUnaryMethod()) {
return Helper::GetBuiltinUnaryMethod();
} else if constexpr (Helper::HasHighOrderUnaryMethod()) {
return Helper::GetBuiltinHighOrderUnaryMethod();
} else {
return &This::InstanceDefaultHash<T>;
}
}
});
}
template <typename TT>
static adt::Result<ValueT> TypeDefaultHash(const ValueT& val) {
int64_t hash_value = std::hash<const char*>()(typeid(TT).name());
return hash_value;
}
template <typename T>
static adt::Result<ValueT> InstanceDefaultHash(const ValueT& val) {
ADT_LET_CONST_REF(impl, val.template TryGet<T>());
// please implement MethodClassImpl<ValueT, T>::Hash if T is not defined
// by ADT_DEFINE_RC.
const void* ptr = impl.__adt_rc_shared_ptr_raw_ptr();
return reinterpret_cast<int64_t>(ptr);
}
static BuiltinUnaryFunc<ValueT> ToString(const ValueT& val) {
using S = builtin_symbol::ToString;
return val.Match([](const auto& impl) -> BuiltinUnaryFunc<ValueT> {
using T = std::decay_t<decltype(impl)>;
if constexpr (IsType<T>()) {
return impl.Match([](const auto& type_impl)
-> BuiltinUnaryFunc<ValueT> {
using TT = std::decay_t<decltype(type_impl)>;
using Helper = detail::
BuiltinMethodHelper<ValueT, TT, S, detail::IndirectAlternative>;
if constexpr (Helper::HasUnaryMethod()) {
return Helper::GetBuiltinUnaryMethod();
} else if constexpr (Helper::HasHighOrderUnaryMethod()) {
return Helper::GetBuiltinHighOrderUnaryMethod();
} else {
return &This::TypeDefaultToString<TT>;
}
});
} else {
using Helper = detail::
BuiltinMethodHelper<ValueT, T, S, detail::DirectAlternative>;
if constexpr (Helper::HasUnaryMethod()) {
return Helper::GetBuiltinUnaryMethod();
} else if constexpr (Helper::HasHighOrderUnaryMethod()) {
return Helper::GetBuiltinHighOrderUnaryMethod();
} else {
return &This::InstanceDefaultToString<T>;
}
}
});
}
template <typename TT>
static adt::Result<ValueT> TypeDefaultToString(const ValueT& val) {
std::ostringstream ss;
ss << "<class '" << TT{}.Name() << "'>";
return ss.str();
}
template <typename T>
static adt::Result<ValueT> InstanceDefaultToString(const ValueT& val) {
std::ostringstream ss;
ADT_LET_CONST_REF(impl, val.template TryGet<T>());
// please implement MethodClassImpl<ValueT, T>::ToString if T is not defined
// by ADT_DEFINE_RC.
const void* ptr = impl.__adt_rc_shared_ptr_raw_ptr();
ss << "<" << TypeImpl<T>{}.Name() << " object at " << ptr << ">";
return ss.str();
}
template <typename BuiltinUnarySymbol>
static BuiltinUnaryFunc<ValueT> GetBuiltinUnaryFunc(const ValueT& val) {
using S = BuiltinUnarySymbol;
return val.Match([](const auto& impl) -> BuiltinUnaryFunc<ValueT> {
using T = std::decay_t<decltype(impl)>;
if constexpr (IsType<T>()) {
return impl.Match([](const auto& type_impl)
-> BuiltinUnaryFunc<ValueT> {
using TT = std::decay_t<decltype(type_impl)>;
using Helper = detail::
BuiltinMethodHelper<ValueT, TT, S, detail::IndirectAlternative>;
return Helper::GetBuiltinUnaryFunc();
});
} else {
using Helper = detail::
BuiltinMethodHelper<ValueT, T, S, detail::DirectAlternative>;
return Helper::GetBuiltinUnaryFunc();
}
});
}
template <typename BuiltinBinarySymbol>
static BuiltinBinaryFunc<ValueT> GetBuiltinBinaryFunc(const ValueT& val) {
using S = BuiltinBinarySymbol;
return val.Match([](const auto& impl) -> BuiltinBinaryFunc<ValueT> {
using T = std::decay_t<decltype(impl)>;
if constexpr (IsType<T>()) {
return impl.Match([](const auto& type_impl)
-> BuiltinBinaryFunc<ValueT> {
using TT = std::decay_t<decltype(type_impl)>;
using Helper = detail::
BuiltinMethodHelper<ValueT, TT, S, detail::IndirectAlternative>;
return Helper::GetBuiltinBinaryFunc();
});
} else {
using Helper = detail::
BuiltinMethodHelper<ValueT, T, S, detail::DirectAlternative>;
return Helper::GetBuiltinBinaryFunc();
}
});
}
};
template <typename ValueT, typename T>
using __AltT = decltype(std::declval<ValueT&>().template Get<T>());
template <typename T, typename ValueT>
adt::Result<T> TryGetAlternative(const ValueT& val) {
if constexpr (detail::is_detected_v<__AltT, ValueT, T>) {
return val.template TryGet<T>();
} else {
return detail::IndirectAlternative<ValueT, T>::TryGet(val);
}
}
template <typename T, typename ValueT>
adt::Result<T> TryGetImpl(const ValueT& val) {
return TryGetAlternative<T, ValueT>(val);
}
template <typename ValueT>
std::string GetTypeName(const ValueT& val) {
return val.Match(
[](const BuiltinClassInstance<ValueT>& impl) -> std::string {
return impl.type.class_attrs()->class_name;
},
[](const ClassInstance<ValueT>& impl) -> std::string {
return impl->type.class_attrs->class_name;
},
[](const auto& impl) -> std::string {
using T = std::decay_t<decltype(impl)>;
return TypeImpl<T>{}.Name();
});
}
} // namespace ap::axpr