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/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ features cannot lower the translation-unit ABI level;
parameter that follows a parameter pack (e.g.
`template <typename... T> S::S(T..., int = 10) {}`). (#GH216211)

- Fixed an assertion failure in an enumerating expansion statement
(`template for`) when an element of the expansion-init-list needed cleanups,
e.g. a temporary bound to a reference parameter such as `{g(1), g(2)}` with
`int g(const int&)`, or a temporary of a type with a non-trivial destructor.
(#GH212630)

#### Bug Fixes to AST Handling

- Fixed a non-deterministic ordering of unused local typedefs that made
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1965,9 +1965,14 @@ void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
assert(Actions.CurContext->isExpansionStmt());
Sema::ContextRAII CtxGuard(Actions, Actions.CurContext->getParent(),
/*NewThis=*/false);
FRI.RangeExpr =
Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression();
FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr);
if (Tok.is(tok::l_brace)) {
// The elements are only evaluated as the initializer of the expansion
// variable in each expansion, so their cleanups belong there.
FRI.RangeExpr = ParseExpansionInitList();
Actions.DiscardCleanupsInEvaluationContext();
} else {
FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(ParseExpression());
}
} else if (Tok.is(tok::l_brace)) {
FRI.RangeExpr = ParseBraceInitializer();
} else {
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Sema/SemaExpand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,12 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
}

ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
if (Idx->isValueDependent() || InitListContainsPack(Range))
if (Idx->isValueDependent() || InitListContainsPack(Range)) {
// The elements are only evaluated by the expansion that selects them, so
// their cleanups must not wrap this expression.
DiscardCleanupsInEvaluationContext();
return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
}

// The index is a DRE to a template parameter; we should never
// fail to evaluate it.
Expand Down
49 changes: 49 additions & 0 deletions clang/test/SemaTemplate/GH212630.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %clang_cc1 -std=c++26 -fsyntax-only -verify %s
// expected-no-diagnostics

namespace GH212630 {

void f(int g(const int&)) {
template for (auto x : {g(1), g(2), g(3)})
g(0);
}

struct M {
int m(const int &x) const { return x; }
};

int overloaded(const int &);
long overloaded(const long &);

void related(int (*fp)(const int &), int (&fr)(const int &), M m) {
template for (auto x : {fp(1), fr(2), m.m(3), overloaded(4), overloaded(5L)}) {}
}

constexpr int h(const int &x) { return x * 2; }

struct S {
int v;
constexpr S(int v) : v(v) {}
constexpr ~S() {}
};

constexpr int direct() {
int sum = 0;
template for (auto x : {h(1), h(2), h(3)}) { sum += x; }
template for (constexpr auto x : {h(1), h(2), h(3)}) { sum += x; }
template for (auto s : {S(1), S(2)}) { sum += s.v; }
return sum;
}
static_assert(direct() == 27);

// With a pack, the elements are rebuilt when the template is instantiated.
template <typename... Ts>
constexpr int pack(Ts... ts) {
int sum = 0;
template for (auto x : {h(1), h(ts)...}) { sum += x; }
template for (auto s : {S(ts)...}) { sum += s.v; }
return sum;
}
static_assert(pack(2, 3) == 17);

} // namespace GH212630
Loading