================
@@ -524,6 +525,581 @@ class CoreturnStmt : public Stmt {
   }
 };
 
+/// CXXExpansionStmtPattern - Represents an unexpanded C++ expansion statement.
+///
+/// There are four kinds of expansion statements.
+///
+/// 1. Enumerating expansion statements.
+/// 2. Iterating expansion statements.
+/// 3. Destructuring expansion statements.
+/// 4. Dependent expansion statements.
+///
+/// 1. An 'enumerating' expansion statement is one whose expansion-initializer
+/// is a brace-enclosed expression-list; this list is syntactically similar to
+/// an initializer list, but it isn't actually an expression in and of itself
+/// (in that it is never evaluated or emitted) and instead is just treated as
+/// a group of expressions. The expansion initializer of this is always a
+/// syntactic-form 'InitListExpr'.
+///
+/// Example:
+/// \verbatim
+///   template for (auto x : { 1, 2, 3 }) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Note that the expression-list may also contain pack expansions, e.g.
+/// '{ 1, xs... }', in which case the expansion size is dependent.
+///
+/// Here, the '{ 1, 2, 3 }' is parsed as an 'InitListExpr'. This node
+/// handles storing (and pack-expanding) the individual expressions.
+///
+/// Sema then wraps this with a 'CXXExpansionSelectExpr', which also
+/// contains a reference to an integral NTTP that is used as the expansion
+/// index; this index is either dependent (if the expansion-size is dependent),
+/// or set to a value of I in the I-th expansion during the expansion process.
+///
+/// The actual expansion is done by 'BuildCXXExpansionSelectExpr()': for
+/// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
+/// BuildCXXExpansionSelectExpr(), when called via TreeTransform,
+/// 'instantiates' the expression '{ a, b, c }' to just 'b'.
+///
+/// 2. Represents an unexpanded iterating expansion statement.
+///
+/// An 'iterating' expansion statement is one whose expansion-initializer is a
+/// a range, i.e. it has a corresponding 'begin()'/'end()' pair that is
+/// determined based on a number of conditions as stated in [stmt.expand] and
+/// [stmt.ranged].
+///
+/// Specifically, let E denote the expansion-initializer; the expansion
+/// statement is iterating if the type of E is not an array type, and either
+///
+///   2a. 'E.begin' and 'E.end' *exist* (irrespective of whether they're
+///        accessible, deleted, or even callable), or
+///
+///   2b. ADL for 'begin(E)' and 'end(E)' finds at least one viable function.
+///
+/// If neither A nor B apply to E (or if E is an array type), we treat this as
+/// a destructuring expansion statement instead (see case 3 below).
+///
+/// Notably, case 2a only checks whether the 'begin' and 'end' members exist 
and
+/// does *not* perform proper overload resolution; this is because if there is
+/// a begin/end function, but it for some reason is not usable (e.g. because it
+/// is non-const but E is const), then we'd rather error and tell the user that
+/// their begin/end function is wrong rather than falling back to 
destructuring.
+///
+/// Conversely, case 2b *does* perform overload resolution, simply because ADL
+/// may find quite a few begin/end overloads for unrelated types that happen to
+/// be in the same namespace. E.g. if the type of E is 'std::tuple', then there
+/// are quite a few begin/end pairs in the namespace 'std', but non of them can
+/// actually be used for a 'std::tuple', and we definitely want to destructure 
a
+/// tuple rather than error about it not being iterable.
+///
+/// In either case, once we've decided that the expansion statement is indeed
+/// iterating, we *do* make sure that the expression 'E.begin()'/'begin(E)' is
+/// well-formed, but any error at that point is a hard error and does not make
+/// us switch to destructuring instead.
+///
+/// The result of this expression is stored in a variable 'begin', which is 
then
+/// used to compute another variable 'iter' (which is just 'begin' + the
+/// expansion index) during expansion. During the N-th expansion, the expansion
+/// variable is then set to '*iter'. See [stmt.expand] for more information.
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion. See 
Sema::ComputeExpansionSize()
+/// for more information about this.
+///
+/// Example:
+/// \verbatim
+///   static constexpr std::string_view foo = "abcd";
+///   template for (auto x : foo) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Here, 'begin' is 'foo.begin()', and during e.g. the 0-th expansion, 'iter'
+/// is 'begin + 0', and thus '*iter' yields 'a', which results in 'x' being
+/// a variable of type 'char' with value 'a'.
+///
+/// 3. Represents an unexpanded destructuring expansion statement.
+///
+/// A 'destructuring' expansion statement is any expansion statement that is
+/// not enumerating or iterating (i.e. destructuring is the last thing we try,
+/// and if it doesn't work, the program is ill-formed).
+///
+/// This essentially involves treating the expansion-initializer as the
+/// initializer of a structured-binding declaration, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// During the N-th expansion, the expansion variable is then initialized with
+/// the N-th binding of the structured-binding declaration. This is implemented
+/// by wrapping the initializer with a CXXExpansionSelectExpr, which selects a
+/// binding based on the current expansion index when called from 
TreeTransform.
+///
+/// Example:
+/// \verbatim
+///   std::tuple<int, long, unsigned> a {1, 2l, 3u};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Here, we build 'auto [_U0, _U1, _U2] = a', and during e.g. the 0-th
+/// expansion, 'x' is initialized with '_U0'.
+///
+/// 4. Represents an expansion statement whose expansion-initializer is
+/// type-dependent.
+///
+/// This will eventually become an iterating or destructuring expansion
+/// statement once the expansion-initializer is no longer dependent.
+///
+/// Dependent expansion statements can never be enumerating: even if the
+/// expansion size of an enumerating expansion statement is dependent (which
+/// is possible if the expression-list contains a pack), we still don't build
+/// an 'Enumerating' 'CXXExpansionStmtPattern' for it.
+///
+/// Example:
+/// \verbatim
+///   template <typename T>
+///   void f() {
+///     template for (auto x : T()) {
+///       // ...
+///     }
+///   }
+/// \endverbatim
+///
+/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
+class CXXExpansionStmtPattern final
+    : public Stmt,
+      llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+
+public:
+  enum class ExpansionStmtKind : uint8_t {
+    Enumerating,
+    Iterating,
+    Destructuring,
+    Dependent,
+  };
+
+private:
+  ExpansionStmtKind PatternKind;
+  SourceLocation LParenLoc;
+  SourceLocation ColonLoc;
+  SourceLocation RParenLoc;
----------------
cor3ntin wrote:

@erichkeane ???

https://github.com/llvm/llvm-project/pull/169680
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to