Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,12 @@ class ASTNodeTraverser
}
}

void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *Node) {
Visit(Node->getExpansionPattern());
if (Traversal != TK_IgnoreUnlessSpelledInSource)
Visit(Node->getInstantiations());
}

void VisitCallExpr(const CallExpr *Node) {
for (const auto *Child :
make_filter_range(Node->children(), [this](const Stmt *Child) {
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1247,14 +1247,16 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {

/// Returns true for local variable declarations other than parameters.
/// Note that this includes static variables inside of functions. It also
/// includes variables inside blocks.
/// includes variables inside blocks and expansion statements.
///
/// void foo() { int x; static int y; extern int z; }
bool isLocalVarDecl() const {
if (getKind() != Decl::Var && getKind() != Decl::Decomposition)
return false;
if (const DeclContext *DC = getLexicalDeclContext())
return DC->getRedeclContext()->isFunctionOrMethod();
return DC->getEnclosingNonExpansionStatementContext()
->getRedeclContext()
->isFunctionOrMethod();
return false;
}

Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,10 @@ class DeclContext {
return getDeclKind() == Decl::RequiresExprBody;
}

bool isExpansionStmt() const {
return getDeclKind() == Decl::CXXExpansionStmt;
}

bool isNamespace() const { return getDeclKind() == Decl::Namespace; }

bool isStdNamespace() const;
Expand Down Expand Up @@ -2292,6 +2296,15 @@ class DeclContext {
return const_cast<DeclContext *>(this)->getOuterLexicalRecordContext();
}

/// Retrieve the innermost enclosing context that doesn't belong to an
/// expansion statement. Returns 'this' if this context is not an expansion
/// statement.
DeclContext *getEnclosingNonExpansionStatementContext();
const DeclContext *getEnclosingNonExpansionStatementContext() const {
return const_cast<DeclContext *>(this)
->getEnclosingNonExpansionStatementContext();
}

/// Test if this context is part of the enclosing namespace set of
/// the context NS, as defined in C++0x [namespace.def]p9. If either context
/// isn't a namespace, this is equivalent to Equals().
Expand Down
121 changes: 121 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,127 @@ class TemplateParamObjectDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == TemplateParamObject; }
};

/// Represents a C++26 expansion statement declaration.
///
/// This is a bit of a hack, since expansion statements shouldn't really be
/// 'declarations' per se (they don't declare anything). Nevertheless, we *do*
/// need them to be declaration *contexts*, because the DeclContext is used to
/// compute the 'template depth' of entities enclosed therein. In particular,
/// the 'template depth' is used to find instantiations of parameter variables,
/// and a lambda enclosed within an expansion statement cannot compute its
/// template depth without a pointer to the enclosing expansion statement.
///
/// For the remainder of this comment, let 'expanding' an expansion statement
/// refer to the process of performing template substitution on its body N
/// times, where N is the expansion size (how this size is determined depends on
/// the kind of expansion statement); by contrast we may sometimes 'instantiate'
/// an expansion statement (because it happens to be in a template). This is
/// just regular template instantiation.
///
/// Apart from a template parameter list that contains a template parameter used
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What else does the list contain? And why a list instead of a single template parameter if that is the only thing in the list?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only contains one parameter; as to why we’re storing a template parameter list, er, the fork was using one, and I kind of assumed that we needed to have one because it feels a bit weird to have a template parameter just on its own, but if storing just the parameter works, then I’ll do that instead; I’ll take a look at that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to remove the template parameter list and only store the parameter instead

/// as the expansion index, this node contains a 'CXXExpansionStmtPattern' as
/// well as a 'CXXExpansionStmtInstantiation'. These two members correspond to
/// distinct representations of the expansion statement: the former is used
/// prior to expansion and contains all the parts needed to perform expansion;
/// the latter holds the expanded/desugared AST nodes that result from the
/// expansion.
///
/// After expansion, the 'CXXExpansionStmtPattern' is no longer updated and left
/// as-is; this also means that, if an already-expanded expansion statement is
/// inside a template, and that template is then instantiated, the
/// 'CXXExpansionStmtPattern' is *not* instantiated; only the
/// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for
/// codegen and constant evaluation.
///
/// There are different kinds of expansion statements; see the comment on
/// 'CXXExpansionStmtPattern' for more information.
///
/// As an example, if the user writes the following expansion statement:
/// \verbatim
/// std::tuple<int, int, int> a{1, 2, 3};
/// template for (auto x : a) {
/// // ...
/// }
/// \endverbatim
///
/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl'
/// stores, amongst other things, the declaration of the variable 'x' as well
/// as the expansion-initializer 'a'.
///
/// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, huh.... so it is ALL of the statements wrapped in a compound stmt...

I rather wonder if we're better off NOT doing the compound statement and having code-gen just emit these scopes right. Are there any other advantages to the compound statement?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so that is explained in more detail in the CXXExpansionStmtInstantiation documentation I believe, but the outermost compound statement explicitly does not exist in the AST (only the inner ones shown here do); it pretty much is already as you’re describing (i.e. we handle the outer scope in codegen).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should update this comment to indicate that the outer compound statement is ‘not actually there’; I don’t remember why I didn’t explain that here (and perhaps some of the documentation needs to be deduplicated, and this section here should just say ‘see the comment on CXXExpansionStmtInstantiation for more information on the AST we produce for the expansions’).

Alternatively, as you (or Corentin, I don’t remember?) suggested, I can move all of the expansion statement documentation into one big comment here, and then all the other nodes just have a comment that says ‘see CXXExpansionStmtDecl for documentation on this’, which might be easier to work w/ than having it all fragmented across 6-odd AST nodes...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of having it all in 1 place, but would also like each node to have a quick summary of its participation too.

I DO wonder if the 'big' comment has hit the "should be in the internals manual" though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of having it all in 1 place, but would also like each node to have a quick summary of its participation too.

I’ll see if I can figure out a way to make that work

I DO wonder if the 'big' comment has hit the "should be in the internals manual" though.

It is getting rather huge yeah... but at the same time, a large portion of the big comment(s) is just ‘this is how expansion statements work’, so not sure if that belongs in the internals manuall since those parts aren’t really Clang-specific

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, we SHOULD be putting more of that sorta stuff into the internals manual. These comments get lost/stale/tough to figure out WHERE they are when you need them. So I think it makes sense to be there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided not to put everything in one place after all, but I’ve added a paragraph that explains the compount statement situation to both the decl and the instantiation stmt and overall added some more explanatory notes. Hopefully this makes everything a bit clearer.

/// is *equivalent* to the AST shown below. Note that only the inner '{}' (i.e.
/// those marked as 'Actual "CompoundStmt"' below) are actually present as
/// 'CompoundStmt's in the AST; the outer braces that wrap everything do *not*
/// correspond to an actual 'CompoundStmt' and are implicit in the sense that we
/// simply push a scope when evaluating or emitting IR for a
/// 'CXXExpansionStmtInstantiation'.
///
/// \verbatim
/// { // Not actually present in the AST.
/// auto [__u0, __u1, __u2] = a;
/// { // Actual 'CompoundStmt'.
/// auto x = __u0;
/// // ...
/// }
/// { // Actual 'CompoundStmt'.
/// auto x = __u1;
/// // ...
/// }
/// { // Actual 'CompoundStmt'.
/// auto x = __u2;
/// // ...
/// }
/// }
/// \endverbatim
///
/// See the documentation around 'CXXExpansionStmtInstantiation' for more notes
/// as to why this node exist and how it is used.
///
/// \see CXXExpansionStmtPattern
/// \see CXXExpansionStmtInstantiation
class CXXExpansionStmtDecl : public Decl, public DeclContext {
CXXExpansionStmtPattern *Expansion = nullptr;
NonTypeTemplateParmDecl *IndexNTTP = nullptr;
CXXExpansionStmtInstantiation *Instantiations = nullptr;

CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
NonTypeTemplateParmDecl *NTTP);

public:
friend class ASTDeclReader;

static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation Loc,
NonTypeTemplateParmDecl *NTTP);
static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C,
GlobalDeclID ID);

CXXExpansionStmtPattern *getExpansionPattern() { return Expansion; }
const CXXExpansionStmtPattern *getExpansionPattern() const {
return Expansion;
}
void setExpansionPattern(CXXExpansionStmtPattern *S) { Expansion = S; }

CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
const CXXExpansionStmtInstantiation *getInstantiations() const {
return Instantiations;
}

void setInstantiations(CXXExpansionStmtInstantiation *S) {
Instantiations = S;
}

NonTypeTemplateParmDecl *getIndexTemplateParm() { return IndexNTTP; }
const NonTypeTemplateParmDecl *getIndexTemplateParm() const {
return IndexNTTP;
}

SourceRange getSourceRange() const override LLVM_READONLY;

static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == CXXExpansionStmt; }
};

inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
return PD;
Expand Down
46 changes: 46 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -5499,6 +5499,52 @@ class BuiltinBitCastExpr final
}
};

/// Helper that selects an expression from an InitListExpr depending
/// on the current expansion index.
///
/// \see CXXExpansionStmtPattern
class CXXExpansionSelectExpr : public Expr {
friend class ASTStmtReader;

enum SubExpr { RANGE, INDEX, COUNT };
Expr *SubExprs[COUNT];

public:
CXXExpansionSelectExpr(EmptyShell Empty);
CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);

InitListExpr *getRangeExpr() {
return cast<InitListExpr>(SubExprs[RANGE]);
}

const InitListExpr *getRangeExpr() const {
return cast<InitListExpr>(SubExprs[RANGE]);
}

void setRangeExpr(InitListExpr *E) { SubExprs[RANGE] = E; }

Expr *getIndexExpr() { return SubExprs[INDEX]; }
const Expr *getIndexExpr() const { return SubExprs[INDEX]; }
void setIndexExpr(Expr *E) { SubExprs[INDEX] = E; }

SourceLocation getBeginLoc() const { return getRangeExpr()->getBeginLoc(); }
SourceLocation getEndLoc() const { return getRangeExpr()->getEndLoc(); }

child_range children() {
return child_range(reinterpret_cast<Stmt **>(SubExprs),
reinterpret_cast<Stmt **>(SubExprs + COUNT));
}

const_child_range children() const {
return const_child_range(
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs)),
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs + COUNT)));
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXExpansionSelectExprClass;
}
};
} // namespace clang

#endif // LLVM_CLANG_AST_EXPRCXX_H
12 changes: 12 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {})

DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})

DEF_TRAVERSE_DECL(CXXExpansionStmtDecl, {
if (D->getInstantiations() &&
getDerived().shouldVisitTemplateInstantiations())
TRY_TO(TraverseStmt(D->getInstantiations()));

TRY_TO(TraverseStmt(D->getExpansionPattern()));
})

DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, {
for (auto *I : D->varlist()) {
TRY_TO(TraverseStmt(I));
Expand Down Expand Up @@ -3117,6 +3125,10 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseConceptRequirement(Req));
})

DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {})
DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
DEF_TRAVERSE_STMT(CXXExpansionSelectExpr, {})

// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
Expand Down
Loading
Loading