Skip to content
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 error: lambda-coroutine with operator new in promise_type #84064

Closed
andreasfertig opened this issue Mar 5, 2024 · 13 comments · Fixed by #84193
Closed

Clang error: lambda-coroutine with operator new in promise_type #84064

andreasfertig opened this issue Mar 5, 2024 · 13 comments · Fixed by #84193
Assignees
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" coroutines C++20 coroutines

Comments

@andreasfertig
Copy link
Contributor

Hello,

the following code is accepted by g++ and MSVC, but rejected by Clang:

https://compiler-explorer.com/z/hG58nq7TE (lambda)
https://compiler-explorer.com/z/7WM7eP3h7 (struct and coroutine member function)

Without the This parameter for operator new and passing another parameter, the code is accepted by Clang but rejected by g++ and MSVC:

https://compiler-explorer.com/z/dnEhsj6hP

With my reading of http://eel.is/c++draft/dcl.fct.def.coroutine#4 and http://eel.is/c++draft/dcl.fct.def.coroutine#9.1 Clang is wrong in both cases. It should accept the first and second example but reject the third.

@github-actions github-actions bot added the clang Clang issues not falling into any other category label Mar 5, 2024
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" coroutines C++20 coroutines and removed clang Clang issues not falling into any other category labels Mar 5, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 5, 2024

@llvm/issue-subscribers-coroutines

Author: Andreas Fertig (andreasfertig)

Hello,

the following code is accepted by g++ and MSVC, but rejected by Clang:

https://compiler-explorer.com/z/hG58nq7TE (lambda)
https://compiler-explorer.com/z/7WM7eP3h7 (struct and coroutine member function)

Without the This parameter for operator new and passing another parameter, the code is accepted by Clang but rejected by g++ and MSVC:

https://compiler-explorer.com/z/dnEhsj6hP

With my reading of http://eel.is/c++draft/dcl.fct.def.coroutine#4 and http://eel.is/c++draft/dcl.fct.def.coroutine#9.1 Clang is wrong in both cases. It should accept the first and second example but reject the third.

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 5, 2024

@llvm/issue-subscribers-clang-frontend

Author: Andreas Fertig (andreasfertig)

Hello,

the following code is accepted by g++ and MSVC, but rejected by Clang:

https://compiler-explorer.com/z/hG58nq7TE (lambda)
https://compiler-explorer.com/z/7WM7eP3h7 (struct and coroutine member function)

Without the This parameter for operator new and passing another parameter, the code is accepted by Clang but rejected by g++ and MSVC:

https://compiler-explorer.com/z/dnEhsj6hP

With my reading of http://eel.is/c++draft/dcl.fct.def.coroutine#4 and http://eel.is/c++draft/dcl.fct.def.coroutine#9.1 Clang is wrong in both cases. It should accept the first and second example but reject the third.

@yuxuanchen1997
Copy link
Member

I don't see clang rejecting your example for "struct and coroutine member function"?

@yuxuanchen1997
Copy link
Member

I also don't quite get why you expect the third to be rejected. The third has a int parameter so a lvalue of int being passed to a int &. It seems ok?

@zygoloid
Copy link
Collaborator

zygoloid commented Mar 5, 2024

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

@ChuanqiXu9
Copy link
Member

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.

CC: @cor3ntin

@cor3ntin
Copy link
Contributor

cor3ntin commented Mar 6, 2024

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.

CC: @cor3ntin

in collectPlacementArgs (SemaCoroutine.cpp) we single out lambdas, which is probably incorrect.

@ChuanqiXu9 ChuanqiXu9 self-assigned this Mar 6, 2024
@andreasfertig
Copy link
Contributor Author

Hello,

I don't see clang rejecting your example for "struct and coroutine member function"?

Yes! Sorry, I was tired and seemed to have looked at something different. Strike the second example.

I also don't quite get why you expect the third to be rejected. The third has a int parameter so a lvalue of int being passed to a int &. It seems ok?

Yes, but according to http://eel.is/c++draft/dcl.fct.def.coroutine#4, in case of a member function, the first parameter is an lvalue of *this. With C++23s static operator()(), the code would be correct (and is accepted by Clang and g++).

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.
CC: @cor3ntin

in collectPlacementArgs (SemaCoroutine.cpp) we single out lambdas, which is probably incorrect.

I thought so, too! If I find the time, I'll try to look into it.

Andreas

@ChuanqiXu9
Copy link
Member

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.
CC: @cor3ntin

in collectPlacementArgs (SemaCoroutine.cpp) we single out lambdas, which is probably incorrect.

It turns out that it introduced in 9860622, which is pretty early.

However, after I removed that limitation, I fall into the diagnostic at line 1434, which shows the type of this is Null

ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
QualType ThisTy = getCurrentThisType();
if (ThisTy.isNull()) {
DeclContext *DC = getFunctionLevelDeclContext();
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
Method && Method->isExplicitObjectMemberFunction()) {
return Diag(Loc, diag::err_invalid_this_use) << 1;
}
if (isLambdaCallWithExplicitObjectParameter(CurContext))
return Diag(Loc, diag::err_invalid_this_use) << 1;
return Diag(Loc, diag::err_invalid_this_use) << 0;
}
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}

I don't have an idea what happened.

@ChuanqiXu9
Copy link
Member

I thought so, too! If I find the time, I'll try to look into it.

Great. If you'd like to take it, you can assign this to yourself. Then we can avoid repeating ourselves.

@cor3ntin
Copy link
Contributor

cor3ntin commented Mar 6, 2024

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.
CC: @cor3ntin

in collectPlacementArgs (SemaCoroutine.cpp) we single out lambdas, which is probably incorrect.

It turns out that it introduced in 9860622, which is pretty early.

However, after I removed that limitation, I fall into the diagnostic at line 1434, which shows the type of this is Null

ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
QualType ThisTy = getCurrentThisType();
if (ThisTy.isNull()) {
DeclContext *DC = getFunctionLevelDeclContext();
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
Method && Method->isExplicitObjectMemberFunction()) {
return Diag(Loc, diag::err_invalid_this_use) << 1;
}
if (isLambdaCallWithExplicitObjectParameter(CurContext))
return Diag(Loc, diag::err_invalid_this_use) << 1;
return Diag(Loc, diag::err_invalid_this_use) << 0;
}
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}

I don't have an idea what happened.

We probably need a CXXThisScopeRAII so that this refers` to the lambda

@ChuanqiXu9
Copy link
Member

The *this of the lambda itself is supposed to be passed as the first argument, but apparently for lambdas we're treating the function as if it were not a non-static member function.

If it is the case, I feel like the root cause comes from how we treat lambdas.
CC: @cor3ntin

in collectPlacementArgs (SemaCoroutine.cpp) we single out lambdas, which is probably incorrect.

It turns out that it introduced in 9860622, which is pretty early.
However, after I removed that limitation, I fall into the diagnostic at line 1434, which shows the type of this is Null

ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
QualType ThisTy = getCurrentThisType();
if (ThisTy.isNull()) {
DeclContext *DC = getFunctionLevelDeclContext();
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
Method && Method->isExplicitObjectMemberFunction()) {
return Diag(Loc, diag::err_invalid_this_use) << 1;
}
if (isLambdaCallWithExplicitObjectParameter(CurContext))
return Diag(Loc, diag::err_invalid_this_use) << 1;
return Diag(Loc, diag::err_invalid_this_use) << 0;
}
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}

I don't have an idea what happened.

We probably need a CXXThisScopeRAII so that this refers` to the lambda

hmm it doesn't work if I put that in front of ActOnCXXThis in collectPlacementArgs or in front of collectPlacementArgs.

Maybe I need to investigate more. Or if you have time, I guess you can look into this directly.

@andreasfertig
Copy link
Contributor Author

Maybe I need to investigate more. Or if you have time, I guess you can look into this directly.

I don't know how to assign this issue to myself, but I'm on it. I have a version that works. I'm running the test suit on it to see whether it breaks other stuff.

Andreas

@cor3ntin cor3ntin assigned andreasfertig and unassigned ChuanqiXu9 Mar 6, 2024
@andreasfertig andreasfertig changed the title Clang error: coroutine member function with operator new in promise_type Clang error: lambda-coroutine with operator new in promise_type Mar 6, 2024
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 6, 2024
Fix llvm#84064

According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first
parameter for overload resolution of `operator new` is `size_t` followed
by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first argument
is the lvalue of `*this` if the coroutine is a member function.

Before this patch, Clang handled class types correctly but ignored lambdas.
This patch adds support for lambda coroutines with a `promise_type` that
implements a custom `operator new`.

The patch does consider C++23 `static operator()`, which already worked as
there is no `this` parameter.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 8, 2024
Fix llvm#84064

According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first
parameter for overload resolution of `operator new` is `size_t` followed
by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first argument
is the lvalue of `*this` if the coroutine is a member function.

Before this patch, Clang handled class types correctly but ignored lambdas.
This patch adds support for lambda coroutines with a `promise_type` that
implements a custom `operator new`.

The patch does consider C++23 `static operator()`, which already worked as
there is no `this` parameter.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 8, 2024
Fix llvm#84064

According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first
parameter for overload resolution of `operator new` is `size_t` followed
by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first argument
is the lvalue of `*this` if the coroutine is a member function.

Before this patch, Clang handled class types correctly but ignored lambdas.
This patch adds support for lambda coroutines with a `promise_type` that
implements a custom `operator new`.

The patch does consider C++23 `static operator()`, which already worked as
there is no `this` parameter.
erichkeane pushed a commit that referenced this issue Mar 8, 2024
#84193)

Fix #84064

According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first
parameter for overload resolution of `operator new` is `size_t` followed
by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first
argument is the lvalue of `*this` if the coroutine is a member function.

Before this patch, Clang handled class types correctly but ignored
lambdas. This patch adds support for lambda coroutines with a
`promise_type` that implements a custom `operator new`.

The patch does consider C++23 `static operator()`, which already worked
as there is no `this` parameter.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 8, 2024
This is a follow-up of llvm#84064. It turned out that a coroutine-lambda
with a `promise_type` and a user-defined constructor ignores the `this`
pointer. Per http://eel.is/c++draft/dcl.fct.def.coroutine#4, in such a
case, the first parameter to the constructor is an lvalue of `*this`.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 9, 2024
This is a follow-up of llvm#84064. It turned out that a coroutine-lambda
with a `promise_type` and a user-defined constructor ignores the `this`
pointer. Per http://eel.is/c++draft/dcl.fct.def.coroutine#4, in such a
case, the first parameter to the constructor is an lvalue of `*this`.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Mar 11, 2024
This is a follow-up of llvm#84064. It turned out that a coroutine-lambda
with a `promise_type` and a user-defined constructor ignores the `this`
pointer. Per http://eel.is/c++draft/dcl.fct.def.coroutine#4, in such a
case, the first parameter to the constructor is an lvalue of `*this`.
andreasfertig added a commit to andreasfertig/llvm-project that referenced this issue Apr 2, 2024
This is a follow-up of llvm#84064. It turned out that a coroutine-lambda
with a `promise_type` and a user-defined constructor ignores the `this`
pointer. Per http://eel.is/c++draft/dcl.fct.def.coroutine#4, in such a
case, the first parameter to the constructor is an lvalue of `*this`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" coroutines C++20 coroutines
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants