llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.15k stars 12.03k forks source link

Crash when evaluating folds involving lambdas and variadic templates #99877

Open ilya-biryukov opened 3 months ago

ilya-biryukov commented 3 months ago

On this code:

struct tuple {
    int x[3];
};

template <class F>
void apply(F f, tuple v) {
    return f(v.x[0], v.x[1], v.x[2]);
}

void Cartesian(auto x, auto y) {
    apply([&](auto... xs) {
        (apply([xs](auto... ys) {
            (ys + ...);
        }, y), ...);
    }, x);
}

int main() {
    auto x = tuple({1, 2, 3});
    auto y = tuple({4, 5, 6});
    Cartesian(x, y);
}

Clang will crash with a following assertion error:

clang++: /root/llvm-project/clang/lib/Sema/TreeTransform.h:15248: clang::ExprResult clang::TreeTransform<Derived>::TransformCXXFoldExpr(clang::CXXFoldExpr*) [with Derived = {anonymous}::TemplateInstantiator; clang::ExprResult = clang::ActionResult<clang::Expr*>]: Assertion `!Unexpanded.empty() && "Pack expansion without parameter packs?"' failed.

The problem arises because when substituting into inner lambdas, Clang will incorrectly loose the ContainsUnexpandedPack dependency on a lambda expression. In turn, this confuses the CXXFoldExpr that relies in this flag to distinguish left and right folds, which will pass the nullptr expr to CollectUnexpandedParameterPacks and cause it to not find any packs when transforming the fold expression, leading to an asssertion.

I have already been working on a fix for a few days and more sophisticated tests, so assigning to myself right away.

llvmbot commented 3 months ago

@llvm/issue-subscribers-clang-frontend

Author: Ilya Biryukov (ilya-biryukov)

On this code: ```cpp struct tuple { int x[3]; }; template <class F> void apply(F f, tuple v) { return f(v.x[0], v.x[1], v.x[2]); } void Cartesian(auto x, auto y) { apply([&](auto... xs) { (apply([xs](auto... ys) { (ys + ...); }, y), ...); }, x); } int main() { auto x = tuple({1, 2, 3}); auto y = tuple({4, 5, 6}); Cartesian(x, y); } ``` Clang will crash with a following assertion error: ``` clang++: /root/llvm-project/clang/lib/Sema/TreeTransform.h:15248: clang::ExprResult clang::TreeTransform<Derived>::TransformCXXFoldExpr(clang::CXXFoldExpr*) [with Derived = {anonymous}::TemplateInstantiator; clang::ExprResult = clang::ActionResult<clang::Expr*>]: Assertion `!Unexpanded.empty() && "Pack expansion without parameter packs?"' failed. ``` The problem arises because when substituting into inner lambdas, Clang will incorrectly loose the `ContainsUnexpandedPack` dependency on a lambda expression. In turn, this confuses the `CXXFoldExpr` that relies in this flag to distinguish left and right folds, which will pass the `nullptr` expr to `CollectUnexpandedParameterPacks` and cause it to not find any packs when transforming the fold expression, leading to an asssertion. I have already been working on a fix for a few days and more sophisticated tests, so assigning to myself right away.
ilya-biryukov commented 3 months ago

After the PRs referenced in the issue, there is one more example that I came up with that still breaks and will need another follow up: unexpanded packs in C++20 constraints used in the template parameter list of a lambda expression: https://gcc.godbolt.org/z/T7ea68rME

template <int ...x>
int Cartesian3(auto y) {
    return [&]<int ...xs>(Ints<xs...>) {
        // check in default template arguments for
        // - requirements of type template parameters,
        return (apply([]<LessThan<xs> = IntValue<0>>(auto... ys) {
          return (ys + ...);
        }, y) + ...);
    }(Ints<x...>());
}