hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler
Other
5.55k stars 248 forks source link

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

Open feature-engineer opened 2 months ago

feature-engineer commented 2 months ago

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...>;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
JohelEGP commented 1 month ago

See also #748.

JohelEGP commented 1 month ago

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

JohelEGP commented 1 month ago

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

JohelEGP commented 1 month ago

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