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

lambda with this auto that captures this in a member function is not an integral constant expression #80997

Closed
hewillk opened this issue Feb 7, 2024 · 10 comments · Fixed by #81102
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" lambda C++11 lambda expressions

Comments

@hewillk
Copy link

hewillk commented Feb 7, 2024

https://godbolt.org/z/azqxac8xE

struct S { 
  int i = 42;
  constexpr auto f() { 
    return [this](this auto) { 
      return this->i;
    }(); 
  }; 
};

static_assert(S().f() == 42);

Clang rejects the above code with:

<source>:10:15: error: static assertion expression is not an integral constant expression
   10 | static_assert(S().f() == 42);
      |               ^~~~~~~~~~~~~
<source>:5:14: note: use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
    5 |       return this->i;
      |              ^

If we remove this auto then compile fine, the two should be equivalent right?
Not really sure what the standards say about this.

@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" lambda C++11 lambda expressions and removed new issue labels Feb 7, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 7, 2024

@llvm/issue-subscribers-clang-frontend

Author: Hewill Kang (hewillk)

https://godbolt.org/z/azqxac8xE ```cpp struct S { int i = 42; constexpr auto f() { return [this](this auto) { return this->i; }(); }; };

static_assert(S().f() == 42);

Clang rejects the above code with:

<source>:10:15: error: static assertion expression is not an integral constant expression
10 | static_assert(S().f() == 42);
| ^~~~~~~~~~~~~
<source>:5:14: note: use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
5 | return this->i;
| ^

If we remove `this auto` then [compile fine](https://godbolt.org/z/Gb8qE7Whh), the two should be equivalent right?
Not really sure what the standards say about this.

</details>

@Sirraide
Copy link
Contributor

Sirraide commented Feb 7, 2024

[expr.prim.this] has this to say about this (emphasis mine)

It shall not appear within the declaration of either a static member function or an explicit object member function of the current class (although its type and value category are defined within such member functions as they are within an implicit object member function)

In other words, you can’t use this in a member function that has an explicit object parameter, and the body of a lambda is a member function, so it looks like the diagnostic is correct here.

@Sirraide
Copy link
Contributor

Sirraide commented Feb 7, 2024

Admittedly, the diagnostic is not ideal here:

use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function

The problem is not so much that it isn’t constexpr (because it is), but even if it weren’t, the problem is that it’s an explicit object member function.

@hewillk
Copy link
Author

hewillk commented Feb 8, 2024

In other words, you can’t use this in a member function that has an explicit object parameter, and the body of a lambda is a member function.

I don't think so. The this here is captured by lambda, it is not lambda's. Clang accepts the following:

struct S { 
  int i = 42;
  constexpr auto f() { 
    return [this](this auto) { 
      return this->i;
    }(); 
  }; 
};
int main() {
  return S().f();
}

https://godbolt.org/z/vq4n4WzMb

@Sirraide
Copy link
Contributor

Sirraide commented Feb 8, 2024

Right, the this ought to refer to the this of the surrounding context because it’s a lambda, that’s true. Interestingly, GCC doesn’t like this either, but MSVC seems to accept it.

@hewillk
Copy link
Author

hewillk commented Feb 8, 2024

There is already a GCC Bug report about this. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113563

@Sirraide
Copy link
Contributor

Sirraide commented Feb 8, 2024

Accessing captures of lambdas w/ explicit object parameters seems to have some problems currently. This also straight-up asserts:

struct S {
  int i = 42;
  constexpr auto f() { 
    return [i = this->i] (this auto) { 
      return i;
    }(); 
  }; 
};

static_assert(S().f() == 42);

https://godbolt.org/z/b3oerGvaq

Sirraide added a commit that referenced this issue Mar 13, 2024
… explicit object parameter in constant evaluator (#81102)

There were some bugs wrt explicit object parameters in lambdas in the
constant evaluator:
- The code evaluating a `CXXThisExpr` wasn’t checking for explicit
object parameters at all and thus assumed that there was no `this` in
the current context because the lambda didn’t have one, even though we
were in a member function and had captured its `this`.
- The code retrieving captures as lvalues *did* account for explicit
object parameters, but it did not handle the case of the explicit object
parameter being passed by value rather than by reference.

This fixes #80997.

---------

Co-authored-by: cor3ntin <corentinjabot@gmail.com>
Co-authored-by: Aaron Ballman <aaron@aaronballman.com>
@zygoloid
Copy link
Collaborator

[expr.prim.this] has this to say about this (emphasis mine)

It shall not appear within the declaration of either a static member function or an explicit object member function of the current class (although its type and value category are defined within such member functions as they are within an implicit object member function)

In other words, you can’t use this in a member function that has an explicit object parameter, and the body of a lambda is a member function, so it looks like the diagnostic is correct here.

Note that a lambda-expression is an expression, not a declaration of an explicit object member function -- in context, it's clear that this rule is talking about syntactic function-definitions, member-declarators, and declarators, which a lambda-expression does not have -- so this rule does not apply here. But since Clang and GCC both seem to get this wrong, I've filed cplusplus/draft#6855 asking for a note to clarify.

@Sirraide
Copy link
Contributor

But since Clang and GCC both seem to get this wrong

Hmm, I’m not sure I’m interpreting this correctly, but for what it’s worth, the reason why we were rejecting this code is because the constant evaluator ostensibly wasn’t handling lambdas with explicit object parameters properly (e.g. it was outright crashing on captures if the explicit object parameter was passed by value), and we were already accepting this code outside of constant evaluation (unless you mean that fixing this and allowing this is us getting it wrong).

I've filed cplusplus/draft#6855 asking for a note to clarify.

Yeah, conceptually, I see nothing wrong w/ using this in an explicit object lambda since it’s not the lambda’s this anyway, so it would surprise me if the intention of the standard was to disallow this in such a context (note: in my original statement, I misread the explicit object parameter as being part of the member function, not the lambda, which is why I initially thought we were right to reject this code).

@zygoloid
Copy link
Collaborator

Hmm, I’m not sure I’m interpreting this correctly, but for what it’s worth, the reason why we were rejecting this code is because the constant evaluator ostensibly wasn’t handling lambdas with explicit object parameters properly (e.g. it was outright crashing on captures if the explicit object parameter was passed by value), and we were already accepting this code outside of constant evaluation (unless you mean that fixing this and allowing this is us getting it wrong).

I just meant that Clang was (prior to the fix here) not completely handling the case of this being used in an explicit object parameter lambda correctly, specifically in constant evaluation.

ilovepi pushed a commit to ilovepi/llvm-project that referenced this issue Mar 15, 2024
… explicit object parameter in constant evaluator (llvm#81102)

There were some bugs wrt explicit object parameters in lambdas in the
constant evaluator:
- The code evaluating a `CXXThisExpr` wasn’t checking for explicit
object parameters at all and thus assumed that there was no `this` in
the current context because the lambda didn’t have one, even though we
were in a member function and had captured its `this`.
- The code retrieving captures as lvalues *did* account for explicit
object parameters, but it did not handle the case of the explicit object
parameter being passed by value rather than by reference.

This fixes llvm#80997.

---------

Co-authored-by: cor3ntin <corentinjabot@gmail.com>
Co-authored-by: Aaron Ballman <aaron@aaronballman.com>
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" lambda C++11 lambda expressions
Projects
None yet
5 participants