Skip to content

[BUG] Captured functor requires parenthesis around it to be interpreted correctly. #1283

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

Open
feature-engineer opened this issue Sep 17, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@feature-engineer
Copy link

feature-engineer commented Sep 17, 2024

Describe the bug
When using a functor inside a lambda, e.g.

std::array<std::string, 10> arr;
f: MyFunctor = ("Some initial value");
std::ranges::generate(arr, :() f&$*(););

This fails with some incomprehensible error saying is not invocable.
But std::ranges::generate(arr, :() (f&$*)();); works fine.

To Reproduce

Here's the code that fails (this is a toy example, ignore the bugs):

Letters: type = {
    str: std::string;
    i: size_t;
    operator=: (out this, str_: std::string) = {
        str = str_;
        i = 0;
    }
    operator(): (inout this) -> char = str[i++];
}

main: () = {
    arr: std::array<char, 10> = ();
    letters: Letters = ("This text would get copied letter by letter");
    std::ranges::generate(arr, :() letters&$*());
}

Here's the code that works:

Letters: type = {
    str: std::string;
    i: size_t;
    operator=: (out this, str_: std::string) = {
        str = str_;
        i = 0;
    }
    operator(): (inout this) -> char = str[i++];
}

main: () = {
    arr: std::array<char, 10> = ();
    letters: Letters = ("This text would get copied letter by letter");
    std::ranges::generate(arr, :() (letters&$*)());
}

I would have expected the first version to work, but failing that, I would have expected a better error message.

Here's the actual error message:

...: In lambda function:
...: error: expected primary-expression before ‘{’ token
  278 |     std::ranges::generate(arr, :() letters&$*());
      |                                                                                              ^
...: error: expected ‘;’ before ‘{’ token
  278 |     std::ranges::generate(arr, :() letters&$*());
      |                                                                                             ^ 
      |                                                                                             ;
...: In function ‘void fill()’:
...: error: no match for call to ‘(const std::ranges::__generate_fn) (std::array<char, 10>, fill()::<lambda()>)’
  278 |     std::ranges::generate(arr, :() letters&$*());
      |     ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~                                                   
In file included from /usr/include/c++/14.2.1/algorithm:63,
                 from /usr/include/cpp2util.h:257,
                 from /home/feature/projects/cpp2/ex1/build/_cppfront/main.cpp:6:
/usr/include/c++/14.2.1/bits/ranges_algo.h:947:7: note: candidate: ‘template<class _Out, class _Sent, class _Fp>  requires (input_or_output_iterator<_Out>) && (sentinel_for<_Sent, _Out>) && (copy_constructible<_Fp>) && ((invocable<_Fp&>) && (indirectly_writable<_Out, typename std::invoke_result<_Fp&>::type>)) constexpr _Out std::ranges::__generate_fn::operator()(_Out, _Sent, _Fp) const’
  947 |       operator()(_Out __first, _Sent __last, _Fp __gen) const
      |       ^~~~~~~~
/usr/include/c++/14.2.1/bits/ranges_algo.h:947:7: note:   candidate expects 3 arguments, 2 provided
/usr/include/c++/14.2.1/bits/ranges_algo.h:957:7: note: candidate: ‘template<class _Range, class _Fp>  requires (copy_constructible<_Fp>) && ((invocable<_Fp&>) && (output_range<_Range, typename std::invoke_result<_Fp&>::type>)) constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__generate_fn::operator()(_Range&&, _Fp) const’
  957 |       operator()(_Range&& __r, _Fp __gen) const
      |       ^~~~~~~~
/usr/include/c++/14.2.1/bits/ranges_algo.h:957:7: note:   template argument deduction/substitution failed:
/usr/include/c++/14.2.1/bits/ranges_algo.h:957:7: note: constraints not satisfied
In file included from /usr/include/c++/14.2.1/compare:40,
                 from /usr/include/c++/14.2.1/bits/stl_pair.h:65,
                 from /usr/include/c++/14.2.1/bits/stl_algobase.h:64,
                 from /usr/include/c++/14.2.1/algorithm:60:
/usr/include/c++/14.2.1/concepts: In substitution of ‘template<class _Range, class _Fp>  requires (copy_constructible<_Fp>) && ((invocable<_Fp&>) && (output_range<_Range, typename std::invoke_result<_Fp&>::type>)) constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__generate_fn::operator()(_Range&&, _Fp) const [with _Range = std::array<char, 10>; _Fp = fill()::<lambda()>]’:
...:   required from here
  278 |     std::ranges::generate(arr, :() letters&$*());
      |     ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~                                                   
/usr/include/c++/14.2.1/concepts:360:13:   required for the satisfaction of ‘invocable<_Fp&>’ [with _Fp = fill::._anon_942]
/usr/include/c++/14.2.1/concepts:360:25: note: the expression ‘is_invocable_v<_Fn, _Args ...> [with _Fn = fill::._anon_942&; _Args = {}]’ evaluated to ‘false’
  360 |     concept invocable = is_invocable_v<_Fn, _Args...>;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~


@feature-engineer feature-engineer added the bug Something isn't working label Sep 17, 2024
@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 4, 2024

See also #748.

@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 4, 2024

The bug is result of commit 94bea67
(modified by commit a18d22e).
I'll see if reverting it in #927 fixes the issue (it should!).

@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 4, 2024

Done?
You still need the parentheses since commit 5663493,
otherwise it's parsed as a multiplication.

@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 4, 2024

Note that commit 94bea67
made the rhs of the * lower to {}.
Operators don't accept a braced-init-list argument.
But commit 5663493 happened before,
which changed the * from dereference to multiplication.

@mihaiboros
Copy link
Contributor

Should this letters&$*() be a valid call?

I've been looking at this issue and it seems there are some cases where it's impossible to know what is the intent without checking the context.

For example in this case: res := var * (a + b);
Is this a function call on a pointer to function/functor or a multiplication?

It is impossible to know unless we look at the type of var. If it is some sort of pointer we can treat it as a function call, otherwise multiplication, but do we want to have this feature? In a big codebase it will be very confusing to understand that line properly if it can have double meaning.

I believe it is best to clarify the call by adding the necessary parenthesis res := (var*)(a + b);. This is similar to C++ where a similar code is malformed auto res = *var(a + b); due to operator precedence, requiring the same parenthesis to compile.

Maybe the bug should be closed, otherwise it needs clarification on the next course of action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants