-
Notifications
You must be signed in to change notification settings - Fork 11.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Ill-formed block expressions within a lambda can yield assert-failures #109148
Comments
@llvm/issue-subscribers-clang-frontend Author: Daniel M. Katz (katzdm)
The following program produces an assert failure with a `clang` binary built with assertions, when compiled with `-fblocks`:
template<typename... Ts>
struct Cls {
static_assert([] consteval -> void {
(^Ts);
});
}; The assertion:
Here is another example that fails the same assertion: template <typename... Ts>
void fn(int = [] consteval -> int { (^Ts); return 0; }()); Both have the same root cause:
Although I can imagine a few ways to patch this (e.g., keep an "error" representation of the ill-formed |
Looks like this goes back to clang-13: https://godbolt.org/z/PMshGMY3E It does not require |
I haven’t tried messing with this yet, but this feels like it might be the issue: llvm-project/clang/lib/Sema/SemaExpr.cpp Lines 16080 to 16089 in 9ec229d
We diagnose unexpanded parameter packs... and then promptly drop them. I think the ‘proper’ solution would be to resolve this fixme; This comment is from 2012, so maybe we can handle blocks with unexpanded paramter packs just fine now? I’ll try that, and if that doesn’t work, I think we can just reset the |
Actually, that would only return |
Oh wait, duh, I just confused myself: it does seem to happen here, it just returns |
#110762) Consider #109148: ```c++ template <typename ...Ts> void f() { [] { (^Ts); }; } ``` When we encounter `^Ts`, we try to parse a block and subsequently call `DiagnoseUnexpandedParameterPack()` (in `ActOnBlockArguments()`), which sees `Ts` and sets `ContainsUnexpandedParameterPack` to `true` in the `LambdaScopeInfo` of the enclosing lambda. However, the entire block is subsequently discarded entirely because it isn’t even syntactically well-formed. As a result, `ContainsUnexpandedParameterPack` is `true` despite the lambda’s body no longer containing any unexpanded packs, which causes an assertion the next time `DiagnoseUnexpandedParameterPack()` is called. This pr moves handling of unexpanded parameter packs into `CapturingScopeInfo` instead so that the same logic is used for both blocks and lambdas. This fixes this issue since the `ContainsUnexpandedParameterPack` flag is now part of the block (and before that, its `CapturingScopeInfo`) and no longer affects the surrounding lambda directly when the block is parsed. Moreover, this change makes blocks actually usable with pack expansion. This fixes #109148.
llvm#110762) Consider llvm#109148: ```c++ template <typename ...Ts> void f() { [] { (^Ts); }; } ``` When we encounter `^Ts`, we try to parse a block and subsequently call `DiagnoseUnexpandedParameterPack()` (in `ActOnBlockArguments()`), which sees `Ts` and sets `ContainsUnexpandedParameterPack` to `true` in the `LambdaScopeInfo` of the enclosing lambda. However, the entire block is subsequently discarded entirely because it isn’t even syntactically well-formed. As a result, `ContainsUnexpandedParameterPack` is `true` despite the lambda’s body no longer containing any unexpanded packs, which causes an assertion the next time `DiagnoseUnexpandedParameterPack()` is called. This pr moves handling of unexpanded parameter packs into `CapturingScopeInfo` instead so that the same logic is used for both blocks and lambdas. This fixes this issue since the `ContainsUnexpandedParameterPack` flag is now part of the block (and before that, its `CapturingScopeInfo`) and no longer affects the surrounding lambda directly when the block is parsed. Moreover, this change makes blocks actually usable with pack expansion. This fixes llvm#109148.
llvm#110762) Consider llvm#109148: ```c++ template <typename ...Ts> void f() { [] { (^Ts); }; } ``` When we encounter `^Ts`, we try to parse a block and subsequently call `DiagnoseUnexpandedParameterPack()` (in `ActOnBlockArguments()`), which sees `Ts` and sets `ContainsUnexpandedParameterPack` to `true` in the `LambdaScopeInfo` of the enclosing lambda. However, the entire block is subsequently discarded entirely because it isn’t even syntactically well-formed. As a result, `ContainsUnexpandedParameterPack` is `true` despite the lambda’s body no longer containing any unexpanded packs, which causes an assertion the next time `DiagnoseUnexpandedParameterPack()` is called. This pr moves handling of unexpanded parameter packs into `CapturingScopeInfo` instead so that the same logic is used for both blocks and lambdas. This fixes this issue since the `ContainsUnexpandedParameterPack` flag is now part of the block (and before that, its `CapturingScopeInfo`) and no longer affects the surrounding lambda directly when the block is parsed. Moreover, this change makes blocks actually usable with pack expansion. This fixes llvm#109148.
The following program produces an assert failure with a
clang
binary built with assertions, when compiled with-fblocks
:The assertion:
Here is another example that fails the same assertion:
Both have the same root cause:
CompoundStmt
body of the lambda, clang begins to parse^Ts
as aBlockExpr
.Sema::ActOnBlockArguments
function invokesDiagnoseUnexpandedParameterPacks
, which setsLambdaScopeInfo::ContainsUnexpandedParameterPacks
totrue
, prior to recognizing that theBlockExpr
is ill-formed.BlockExpr
is diagnosed, and is subsequently discarded from theCompoundStmt
representing the body of the lambda.LambdaScopeInfo
is used to initialize theLambdaExpr
, resulting inLambdaExpr::ContainsUnexpandedParameterPack
being set totrue
.LambdaExpr
becomes an argument to some other semantic construct (e.g.,StaticAssertDecl
,ParameterDecl
), whose semantic analysis callsDiagnoseUnexpandedParameterPack
.LambdaExpr
reports that it contains an unexpanded parameter pack, but the tree walk fails to find any pack (since the ill-formedBlockExpr
was discarded from theCompoundStmt
).Although I can imagine a few ways to patch this (e.g., keep an "error" representation of the ill-formed
BlockExpr
inCompoundStmt
, rollback the setting ofLambdaScopeInfo::ContainsUnexpandedParameterPack
when theBlockExpr
is ill-formed, just remove the assertion fromDiagnoseUnexpandedParameterPacks
), I'm not sure I love any of them. For now, I figured I'd just report the issue.The text was updated successfully, but these errors were encountered: