[clang] [clang][bytecode] Use ExtendingDecl mechanism for primitives as well (PR #128141)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 20 23:21:23 PST 2025
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/128141
... when creating the temporary variables for a
MaterializeTemporaryExpr.
>From c097cff3502bf0fbbbd2a1c8c791f578fd1ce507 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 20 Feb 2025 20:22:15 +0100
Subject: [PATCH] [clang][bytecode] Use ExtendingDecl mechanism for primitives
as well
... when creating the temporary variables for a
MaterializeTemporaryExpr.
---
clang/lib/AST/ByteCode/Compiler.cpp | 89 +++---
clang/lib/AST/ByteCode/Compiler.h | 2 +-
.../ByteCode/libcxx/primitive-temporary.cpp | 271 ++++++++++++++++++
3 files changed, 319 insertions(+), 43 deletions(-)
create mode 100644 clang/test/AST/ByteCode/libcxx/primitive-temporary.cpp
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 503c58a67adeb..ec18f9d72b66f 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -567,8 +567,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
// Location for the SubExpr.
// Since SubExpr is of complex type, visiting it results in a pointer
// anyway, so we just create a temporary pointer variable.
- unsigned SubExprOffset = allocateLocalPrimitive(
- SubExpr, PT_Ptr, /*IsConst=*/true, /*IsExtended=*/false);
+ unsigned SubExprOffset =
+ allocateLocalPrimitive(SubExpr, PT_Ptr, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(PT_Ptr, SubExprOffset, CE))
@@ -611,8 +611,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
const auto *VT = CE->getType()->getAs<VectorType>();
PrimType ElemT = classifyPrim(SubExpr->getType());
- unsigned ElemOffset = allocateLocalPrimitive(
- SubExpr, ElemT, /*IsConst=*/true, /*IsExtended=*/false);
+ unsigned ElemOffset =
+ allocateLocalPrimitive(SubExpr, ElemT, /*IsConst=*/true);
// Prepare a local variable for the scalar value.
if (!this->visit(SubExpr))
@@ -1104,7 +1104,7 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
PrimType ResultElemT = this->classifyComplexElementType(E->getType());
unsigned ResultOffset = ~0u;
if (!DiscardResult)
- ResultOffset = this->allocateLocalPrimitive(E, PT_Ptr, true, false);
+ ResultOffset = this->allocateLocalPrimitive(E, PT_Ptr, /*IsConst=*/true);
// Save result pointer in ResultOffset
if (!this->DiscardResult) {
@@ -1178,14 +1178,14 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
// Evaluate LHS and save value to LHSOffset.
if (LHSType->isAnyComplexType()) {
- LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
+ LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
return false;
} else {
PrimType LHST = classifyPrim(LHSType);
- LHSOffset = this->allocateLocalPrimitive(LHS, LHST, true, false);
+ LHSOffset = this->allocateLocalPrimitive(LHS, LHST, /*IsConst=*/true);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(LHST, LHSOffset, E))
@@ -1195,14 +1195,14 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
// Same with RHS.
unsigned RHSOffset;
if (RHSType->isAnyComplexType()) {
- RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
+ RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
return false;
} else {
PrimType RHST = classifyPrim(RHSType);
- RHSOffset = this->allocateLocalPrimitive(RHS, RHST, true, false);
+ RHSOffset = this->allocateLocalPrimitive(RHS, RHST, /*IsConst=*/true);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(RHST, RHSOffset, E))
@@ -1342,14 +1342,16 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
PrimType ResultElemT = this->classifyVectorElementType(E->getType());
// Evaluate LHS and save value to LHSOffset.
- unsigned LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
+ unsigned LHSOffset =
+ this->allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
return false;
// Evaluate RHS and save value to RHSOffset.
- unsigned RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
+ unsigned RHSOffset =
+ this->allocateLocalPrimitive(RHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
@@ -2710,8 +2712,8 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
// For everyhing else, use local variables.
if (SubExprT) {
bool IsConst = SubExpr->getType().isConstQualified();
- unsigned LocalIndex = allocateLocalPrimitive(E, *SubExprT, IsConst,
- /*IsExtended=*/true);
+ unsigned LocalIndex =
+ allocateLocalPrimitive(E, *SubExprT, IsConst, E->getExtendingDecl());
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(*SubExprT, LocalIndex, E))
@@ -2781,7 +2783,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
unsigned LocalIndex;
if (T)
- LocalIndex = this->allocateLocalPrimitive(Init, *T, false, false);
+ LocalIndex = this->allocateLocalPrimitive(Init, *T, /*IsConst=*/false);
else if (std::optional<unsigned> MaybeIndex = this->allocateLocal(Init))
LocalIndex = *MaybeIndex;
else
@@ -3337,8 +3339,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
PrimType SizeT = classifyPrim(Stripped->getType());
// Save evaluated array size to a variable.
- unsigned ArrayLen = allocateLocalPrimitive(
- Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
+ unsigned ArrayLen =
+ allocateLocalPrimitive(Stripped, SizeT, /*IsConst=*/false);
if (!this->visit(Stripped))
return false;
if (!this->emitSetLocal(SizeT, ArrayLen, E))
@@ -3416,8 +3418,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
}
// Create loop variables.
- unsigned Iter = allocateLocalPrimitive(
- Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
+ unsigned Iter =
+ allocateLocalPrimitive(Stripped, SizeT, /*IsConst=*/false);
if (!this->emitConst(StaticInitElems, SizeT, E))
return false;
if (!this->emitSetLocal(SizeT, Iter, E))
@@ -3668,8 +3670,8 @@ template <class Emitter>
bool Compiler<Emitter>::VisitAddrLabelExpr(const AddrLabelExpr *E) {
assert(E->getType()->isVoidPointerType());
- unsigned Offset = allocateLocalPrimitive(
- E->getLabel(), PT_Ptr, /*IsConst=*/true, /*IsExtended=*/false);
+ unsigned Offset =
+ allocateLocalPrimitive(E->getLabel(), PT_Ptr, /*IsConst=*/true);
return this->emitGetLocal(PT_Ptr, Offset, E);
}
@@ -3684,7 +3686,8 @@ bool Compiler<Emitter>::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
QualType SrcType = Src->getType();
PrimType SrcElemT = classifyVectorElementType(SrcType);
- unsigned SrcOffset = this->allocateLocalPrimitive(Src, PT_Ptr, true, false);
+ unsigned SrcOffset =
+ this->allocateLocalPrimitive(Src, PT_Ptr, /*IsConst=*/true);
if (!this->visit(Src))
return false;
if (!this->emitSetLocal(PT_Ptr, SrcOffset, E))
@@ -3727,8 +3730,8 @@ bool Compiler<Emitter>::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
// Save both input vectors to a local variable.
unsigned VectorOffsets[2];
for (unsigned I = 0; I != 2; ++I) {
- VectorOffsets[I] = this->allocateLocalPrimitive(
- Vecs[I], PT_Ptr, /*IsConst=*/true, /*IsExtended=*/false);
+ VectorOffsets[I] =
+ this->allocateLocalPrimitive(Vecs[I], PT_Ptr, /*IsConst=*/true);
if (!this->visit(Vecs[I]))
return false;
if (!this->emitSetLocal(PT_Ptr, VectorOffsets[I], E))
@@ -3780,8 +3783,7 @@ bool Compiler<Emitter>::VisitExtVectorElementExpr(
}
// Create a local variable for the base.
- unsigned BaseOffset = allocateLocalPrimitive(Base, PT_Ptr, /*IsConst=*/true,
- /*IsExtended=*/false);
+ unsigned BaseOffset = allocateLocalPrimitive(Base, PT_Ptr, /*IsConst=*/true);
if (!this->visit(Base))
return false;
if (!this->emitSetLocal(PT_Ptr, BaseOffset, E))
@@ -4193,9 +4195,8 @@ bool Compiler<Emitter>::emitConst(const APSInt &Value, const Expr *E) {
}
template <class Emitter>
-unsigned Compiler<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty,
- bool IsConst,
- bool IsExtended) {
+unsigned Compiler<Emitter>::allocateLocalPrimitive(
+ DeclTy &&Src, PrimType Ty, bool IsConst, const ValueDecl *ExtendingDecl) {
// Make sure we don't accidentally register the same decl twice.
if (const auto *VD =
dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
@@ -4212,7 +4213,10 @@ unsigned Compiler<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty,
Scope::Local Local = this->createLocal(D);
if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>()))
Locals.insert({VD, Local});
- VarScope->add(Local, IsExtended);
+ if (ExtendingDecl)
+ VarScope->addExtended(Local, ExtendingDecl);
+ else
+ VarScope->add(Local, false);
return Local.Offset;
}
@@ -4780,7 +4784,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
// decl as the function pointer.
const Expr *Callee = E->getCallee();
CalleeOffset =
- this->allocateLocalPrimitive(Callee, PT_MemberPtr, true, false);
+ this->allocateLocalPrimitive(Callee, PT_MemberPtr, /*IsConst=*/true);
if (!this->visit(Callee))
return false;
if (!this->emitSetLocal(PT_MemberPtr, *CalleeOffset, E))
@@ -4802,7 +4806,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
return this->emitKill(E);
} else if (!FuncDecl) {
const Expr *Callee = E->getCallee();
- CalleeOffset = this->allocateLocalPrimitive(Callee, PT_FnPtr, true, false);
+ CalleeOffset =
+ this->allocateLocalPrimitive(Callee, PT_FnPtr, /*IsConst=*/true);
if (!this->visit(Callee))
return false;
if (!this->emitSetLocal(PT_FnPtr, *CalleeOffset, E))
@@ -5404,7 +5409,8 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
LabelTy EndLabel = this->getLabel();
OptLabelTy DefaultLabel = std::nullopt;
- unsigned CondVar = this->allocateLocalPrimitive(Cond, CondT, true, false);
+ unsigned CondVar =
+ this->allocateLocalPrimitive(Cond, CondT, /*IsConst=*/true);
if (const auto *CondInit = S->getInit())
if (!visitStmt(CondInit))
@@ -6067,7 +6073,8 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) {
// The offset of the temporary, if we created one.
unsigned SubExprOffset = ~0u;
auto createTemp = [=, &SubExprOffset]() -> bool {
- SubExprOffset = this->allocateLocalPrimitive(SubExpr, PT_Ptr, true, false);
+ SubExprOffset =
+ this->allocateLocalPrimitive(SubExpr, PT_Ptr, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
return this->emitSetLocal(PT_Ptr, SubExprOffset, E);
@@ -6181,7 +6188,7 @@ bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
// The offset of the temporary, if we created one.
unsigned SubExprOffset =
- this->allocateLocalPrimitive(SubExpr, PT_Ptr, true, false);
+ this->allocateLocalPrimitive(SubExpr, PT_Ptr, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(PT_Ptr, SubExprOffset, E))
@@ -6554,8 +6561,7 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
if (LHS->getType()->isAnyComplexType()) {
LHSIsComplex = true;
ElemT = classifyComplexElementType(LHS->getType());
- LHSOffset = allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true,
- /*IsExtended=*/false);
+ LHSOffset = allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
@@ -6563,7 +6569,7 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
} else {
LHSIsComplex = false;
PrimType LHST = classifyPrim(LHS->getType());
- LHSOffset = this->allocateLocalPrimitive(LHS, LHST, true, false);
+ LHSOffset = this->allocateLocalPrimitive(LHS, LHST, /*IsConst=*/true);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(LHST, LHSOffset, E))
@@ -6575,8 +6581,7 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
if (RHS->getType()->isAnyComplexType()) {
RHSIsComplex = true;
ElemT = classifyComplexElementType(RHS->getType());
- RHSOffset = allocateLocalPrimitive(RHS, PT_Ptr, /*IsConst=*/true,
- /*IsExtended=*/false);
+ RHSOffset = allocateLocalPrimitive(RHS, PT_Ptr, /*IsConst=*/true);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
@@ -6584,7 +6589,7 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
} else {
RHSIsComplex = false;
PrimType RHST = classifyPrim(RHS->getType());
- RHSOffset = this->allocateLocalPrimitive(RHS, RHST, true, false);
+ RHSOffset = this->allocateLocalPrimitive(RHS, RHST, /*IsConst=*/true);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(RHST, RHSOffset, E))
@@ -6761,8 +6766,8 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
if (!this->visit(SubExpr))
return false;
} else if (std::optional<PrimType> FromT = classify(SubExpr)) {
- unsigned TempOffset = allocateLocalPrimitive(
- SubExpr, *FromT, /*IsConst=*/true, /*IsExtended=*/false);
+ unsigned TempOffset =
+ allocateLocalPrimitive(SubExpr, *FromT, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(*FromT, TempOffset, E))
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 0a93c46a40ef5..77fcc3d1b41ce 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -303,7 +303,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
- bool IsExtended = false);
+ const ValueDecl *ExtendingDecl = nullptr);
/// Allocates a space storing a local given its type.
std::optional<unsigned>
diff --git a/clang/test/AST/ByteCode/libcxx/primitive-temporary.cpp b/clang/test/AST/ByteCode/libcxx/primitive-temporary.cpp
new file mode 100644
index 0000000000000..475f7ffef4525
--- /dev/null
+++ b/clang/test/AST/ByteCode/libcxx/primitive-temporary.cpp
@@ -0,0 +1,271 @@
+// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s
+// RUN: %clang_cc1 -std=c++2c -verify=ref,both %s
+
+// both-no-diagnostics
+
+namespace std {
+inline namespace {
+template <class _Tp, _Tp __v> struct integral_constant {
+ static const _Tp value = __v;
+};
+template <bool _Val> using _BoolConstant = integral_constant<bool, _Val>;
+template <class _Tp> using __remove_cv_t = __remove_cv(_Tp);
+template <class _Tp> using remove_cv_t = __remove_cv_t<_Tp>;
+} // namespace
+inline namespace __1 {
+template <class _Tp>
+using __libcpp_remove_reference_t = __remove_reference_t(_Tp);
+template <bool, class _IfRes, class> using conditional_t = _IfRes;
+template <class _Tp, class _Up>
+using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>;
+template <class _Tp> struct enable_if {
+ typedef _Tp type;
+};
+template <bool, class _Tp = void> using __enable_if_t = _Tp;
+template <class _Bp, class _Dp>
+constexpr bool is_base_of_v = __is_base_of(_Bp, _Dp);
+template <class _Tp> _Tp __declval(long);
+template <class _Tp> decltype(__declval<_Tp>(0)) declval();
+template <class _Fp, class... _Args>
+constexpr decltype(declval<_Fp>()(declval<_Args>()...))
+__invoke(_Fp __f, _Args... __args) {
+ return (__f)((__args)...);
+}
+template <class, class _Fp, class... _Args> struct __invokable_r {
+ template <class _XFp, class... _XArgs>
+ static decltype(__invoke(declval<_XFp>(), declval<_XArgs>()...))
+ __try_call(int);
+ using _Result = decltype(__try_call<_Fp, _Args...>(0));
+};
+template <class _Func, class... _Args>
+struct __invoke_result
+ : enable_if<typename __invokable_r<void, _Func, _Args...>::_Result> {};
+template <class _Fn, class... _Args>
+using invoke_result_t = __invoke_result<_Fn, _Args...>::type;
+template <class _Tp> constexpr __libcpp_remove_reference_t<_Tp> &&move(_Tp &&);
+template <class _From, class _To>
+constexpr bool is_convertible_v = __is_convertible(_From, _To);
+template <class _From, class _To>
+concept convertible_to =
+ is_convertible_v<_From, _To> && requires { (declval<_From>()); };
+template <class _Tp, class _Up>
+concept __same_as_impl = _IsSame<_Tp, _Up>::value;
+template <class _Tp, class _Up>
+concept same_as = __same_as_impl<_Tp, _Up> && __same_as_impl<_Up, _Tp>;
+template <class _Tp> using __remove_cvref_t = __remove_cvref(_Tp);
+template <class _Tp> using remove_cvref_t = __remove_cvref_t<_Tp>;
+template <class _Xp, class _Yp>
+using __cond_res =
+ decltype(false ? declval<_Xp (&)()>()() : declval<_Yp (&)()>()());
+template <class...> struct common_reference;
+template <class... _Types>
+using common_reference_t = common_reference<_Types...>::type;
+template <class, class> struct __common_reference_sub_bullet3;
+template <class _Tp, class _Up>
+struct common_reference<_Tp, _Up> : __common_reference_sub_bullet3<_Tp, _Up> {};
+template <class _Tp, class _Up>
+ requires requires { typename __cond_res<_Tp, _Up>; }
+struct __common_reference_sub_bullet3<_Tp, _Up> {
+ using type = __cond_res<_Tp, _Up>;
+};
+template <class _Tp, class _Up>
+concept common_reference_with =
+ same_as<common_reference_t<_Tp, _Up>, common_reference_t<_Up, _Tp>> &&
+ convertible_to<_Tp, common_reference_t<_Tp, _Up>> &&
+ convertible_to<_Up, common_reference_t<_Tp, _Up>>;
+template <class _Tp>
+using __make_const_lvalue_ref = __libcpp_remove_reference_t<_Tp>;
+template <class _Lhs, class _Rhs>
+concept assignable_from =
+ common_reference_with<__make_const_lvalue_ref<_Lhs>,
+ __make_const_lvalue_ref<_Rhs>> &&
+ requires(_Lhs __lhs, _Rhs __rhs) {
+ { __lhs = (__rhs) };
+ };
+template <class _Tp>
+concept default_initializable = requires { _Tp{}; };
+template <class _Tp> constexpr bool is_object_v = __is_object(_Tp);
+template <class _Dp, class _Bp>
+concept derived_from = is_base_of_v<_Bp, _Dp> && is_convertible_v<_Dp *, _Bp *>;
+template <class _Tp, class _Up>
+concept __weakly_equality_comparable_with = requires(
+ __make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) {
+ { __u };
+};
+template <class _Fn, class... _Args>
+constexpr invoke_result_t<_Fn, _Args...> invoke(_Fn __f, _Args &&...__args) {
+ return __invoke((__f), (__args)...);
+}
+template <class _Fn, class... _Args>
+concept invocable =
+ requires(_Fn __fn, _Args... __args) { invoke((__fn), (__args)...); };
+template <class _Fn, class... _Args>
+concept regular_invocable = invocable<_Fn, _Args...>;
+template <template <class> class, class>
+integral_constant<bool, false> __sfinae_test_impl();
+template <template <class> class _Templ, class... _Args>
+using _IsValidExpansion = decltype(__sfinae_test_impl<_Templ, _Args...>());
+template <class _Tp>
+using __test_for_primary_template =
+ __enable_if_t<_IsSame<_Tp, typename _Tp::__primary_template>::value>;
+template <class _Tp>
+using __is_primary_template =
+ _IsValidExpansion<__test_for_primary_template, _Tp>;
+template <class> struct __cond_value_type;
+template <class _Tp>
+ requires is_object_v<_Tp>
+struct __cond_value_type<_Tp> {
+ using value_type = remove_cv_t<_Tp>;
+};
+template <class _Tp>
+concept __has_member_value_type = requires { typename _Tp; };
+template <class> struct indirectly_readable_traits;
+template <class _Tp>
+struct indirectly_readable_traits<_Tp *> : __cond_value_type<_Tp> {};
+template <__has_member_value_type _Tp>
+struct indirectly_readable_traits<_Tp>
+ : __cond_value_type<typename _Tp::value_type> {};
+template <bool> struct _OrImpl;
+template <> struct _OrImpl<true> {
+ template <class, class _First, class... _Rest>
+ using _Result =
+ _OrImpl<!bool() && sizeof...(_Rest)>::template _Result<_First, _Rest...>;
+};
+template <> struct _OrImpl<false> {
+ template <class _Res> using _Result = _Res;
+};
+template <class... _Args>
+using _Or =
+ _OrImpl<sizeof...(_Args) !=
+ 0>::template _Result<integral_constant<bool, false>, _Args...>;
+template <class _Tp>
+concept __dereferenceable = requires(_Tp __t) {
+ { __t };
+};
+template <__dereferenceable _Tp>
+using iter_reference_t = decltype(*declval<_Tp>());
+struct input_iterator_tag {};
+struct forward_iterator_tag : input_iterator_tag {};
+struct bidirectional_iterator_tag : forward_iterator_tag {};
+struct __iter_concept_random_fallback {
+ template <class>
+ using _Apply = __enable_if_t<__is_primary_template<int>::value,
+ bidirectional_iterator_tag>;
+};
+template <class _Tester> struct __test_iter_concept : _Tester {};
+struct __iter_concept_cache {
+ using type = _Or<__test_iter_concept<__iter_concept_random_fallback>>;
+};
+template <class _Iter>
+using _ITER_CONCEPT = __iter_concept_cache::type::_Apply<_Iter>;
+template <class _Ip>
+using iter_value_t =
+ conditional_t<__is_primary_template<int>::value,
+ indirectly_readable_traits<remove_cvref_t<_Ip>>,
+ int>::value_type;
+namespace ranges {
+struct Trans_NS___iter_move___fn {
+ template <class _Ip> auto operator()(_Ip __i) const -> decltype(move(*(__i)));
+};
+inline namespace {
+auto iter_move = Trans_NS___iter_move___fn{};
+}
+} // namespace ranges
+template <__dereferenceable _Tp>
+ requires requires {
+ { ranges::iter_move };
+ }
+using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<_Tp>()));
+template <class _In>
+concept __indirectly_readable_impl =
+ requires(_In __i) {
+ { ranges::iter_move(__i) } -> same_as<iter_rvalue_reference_t<_In>>;
+ } && common_reference_with<iter_reference_t<_In>, iter_value_t<_In>> &&
+ common_reference_with<iter_reference_t<_In>,
+ iter_rvalue_reference_t<_In>> &&
+ common_reference_with<iter_rvalue_reference_t<_In>, iter_value_t<_In>>;
+template <class _In>
+concept indirectly_readable = __indirectly_readable_impl<remove_cvref_t<_In>>;
+template <class _Tp> struct __indirect_value_t_impl {
+ using type = iter_value_t<_Tp>;
+};
+template <indirectly_readable _Tp>
+using __indirect_value_t = __indirect_value_t_impl<_Tp>::type;
+template <class _Sp, class _Ip>
+concept sentinel_for = __weakly_equality_comparable_with<_Sp, _Ip>;
+template <class _Ip>
+concept input_iterator = requires { typename _ITER_CONCEPT<_Ip>; } &&
+ derived_from<_ITER_CONCEPT<_Ip>, input_iterator_tag>;
+template <class _Fp, class _It>
+concept indirectly_unary_invocable =
+ invocable<_Fp, __indirect_value_t<_It>> &&
+ invocable<_Fp, iter_reference_t<_It>> &&
+ common_reference_with<invoke_result_t<_Fp, __indirect_value_t<_It>>,
+ invoke_result_t<_Fp, iter_reference_t<_It>>>;
+template <class _Fp, class _It>
+concept indirectly_regular_unary_invocable =
+ regular_invocable<_Fp, __indirect_value_t<_It>> &&
+ regular_invocable<_Fp, iter_reference_t<_It>> &&
+ common_reference_with<invoke_result_t<_Fp, __indirect_value_t<_It>>,
+ invoke_result_t<_Fp, iter_reference_t<_It>>>;
+template <class _Fp, class... _Its>
+ requires(indirectly_readable<_Its> && ...) &&
+ invocable<_Fp, iter_reference_t<_Its>...>
+using indirect_result_t = invoke_result_t<_Fp, iter_reference_t<_Its>...>;
+template <class _It, class _Proj> struct __projected_impl {
+ struct __type {
+ using value_type = remove_cvref_t<indirect_result_t<_Proj, _It>>;
+ indirect_result_t<_Proj, _It> operator*();
+ };
+};
+template <indirectly_readable _It,
+ indirectly_regular_unary_invocable<_It> _Proj>
+using projected = __projected_impl<_It, _Proj>::__type;
+namespace ranges {
+template <class, class> using for_each_result = int;
+
+struct Z {};
+constexpr Z zomg() {
+ return {};
+}
+constexpr Z zomg2(Z z) {
+ return {};
+}
+
+struct __for_each {
+
+ template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj,
+ indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
+ constexpr for_each_result<_Iter, _Func>
+ operator()(_Iter __first, _Sent __last, _Func __func, _Proj __proj) const {
+
+ for (; __first != __last; ++__first)
+ invoke(__func, invoke(__proj, *__first));
+ return {};
+ }
+
+};
+inline namespace {
+auto for_each = __for_each{};
+}
+} // namespace ranges
+} // namespace __1
+} // namespace std
+
+
+
+struct T {};
+struct Proj {
+ constexpr void *operator()(T) { return nullptr; }
+};
+struct UnaryVoid {
+ constexpr void operator()(void *) {}
+};
+
+constexpr bool all_the_algorithms() {
+ T a[2];
+ T *last = a + 2;
+ std::ranges::for_each(a, last, UnaryVoid(), Proj());
+ return true;
+}
+static_assert(all_the_algorithms());
More information about the cfe-commits
mailing list