[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