hsutter / cppfront

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

[BUG] Disable UFCS if the function needs to be captured #863

Open JohelEGP opened 8 months ago

JohelEGP commented 8 months ago

See thread starting at https://github.com/hsutter/cppfront/issues/550#issuecomment-1834594548:

There is another case that #506 doesn't yet cover (https://cpp2.godbolt.org/z/hzYxv5rn6):

type_declaration: @struct type = {
  require: (this) = { }
}
require_fn: type == std::function<void(bool, std::string_view)>;
array2: (inout t: type_declaration) = {
  require: require_fn = :(args...) t&$*.require(args...);
  _ = require;
}
main: () = { }
main.cpp2:6:91: error: variable 'require' cannot be implicitly captured in a lambda with no capture-default specified
    6 |   require_fn require {[_0 = (&t)](auto const& ...args) mutable -> auto { return CPP2_UFCS(require, (*cpp2::assert_not_null(_0)), args...);  }};
      |                                                                                           ^
main.cpp2:6:14: note: 'require' declared here
    6 |   require_fn require {[_0 = (&t)](auto const& ...args) mutable -> auto { return CPP2_UFCS(require, (*cpp2::assert_not_null(_0)), args...);  }};
      |              ^

require has type require_fn, so it isn't that its type is deduced. But it still tries to use require without capturing it. Name lookup finds an object, so it doesn't try a free function, and the object must be captured. If we're in a context that would need to capture the name, we should not use the UFCS macro. In the context of a member function, this would happen to work due to #281.

require: require_fn = :(args...) t&$*.require(args...);

For this kind of code, in GitHub, there are precisely 7 uses with the identifier size that would break if they were rewritten to Cpp2 and compiled with cppfront: https://github.com/search?q=lang%3AC%2B%2B+content%3A%2F+size+%3D+%5C%5B.*%5C.size%5C%28%5C%29%3B%2F+NOT+is%3Afork&type=code.