cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] _braced-init-list_ argument in Cpp2

Open JohelEGP opened this issue 2 years ago • 7 comments
trafficstars

Title: braced-init-list argument in Cpp2.

Description:

#449 should be fixed by #487, and here I extract a remaining issue:

point: type = {
    operator=: (out this, x: int, y: int) = {}
}
check: (p: point) = {}
main: () = {
    _ = (: point = (0, 0)) + (0, 0); // ERROR!
    check((0, 0)); // BUG!
}

AFAIK, you could always use parentheses to initialize a variable, and assign to one (modulo bugs). ab29f19d84985594569d27e11db8190bb54bf019 added support for return. I don't remember check((0, 0)); // BUG! ever being supported (or it was always bugged). So the bug is either that it lowers

  • to a comma operator, rather than being rejected, or
  • to a parenthesized expression list, rather than braces for initialization.

-- https://github.com/hsutter/cppfront/issues/449#issuecomment-1544025828 (extract)

I'm trying to convert the SFML's https://www.sfml-dev.org/documentation/2.6.0/#example:

    // Create the main window
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");

to Cpp2, which in its master branch (SFML 3) looks like:

    // Create the main window
    sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML window");

Fortunately, I can name the type, so I translated it to:

  // Create the main window
  window: sf::RenderWindow = (:sf::VideoMode = :sf::Vector2u = (800, 600), "SFML window");

But what if this Cpp1's API type wasn't possible to be named, and required such Cpp1 syntax to be used: sf::VideoMode({800, 600})?

JohelEGP avatar Jul 13 '23 22:07 JohelEGP

But what if this Cpp1's API type wasn't possible to be named, and required such Cpp1 syntax to be used: sf::VideoMode({800, 600})?

For example, I think it's preferable to replace Cppfront's Cpp1 source code

parameter_declaration_list(false, false, true)
parameter_declaration_list(false, true)

with

parameter_declaration_list({.parameterized_statement = true})
parameter_declaration_list({.whatever_else = true})

respectively.

JohelEGP avatar Jul 14 '23 00:07 JohelEGP

Something that's been bothering me in Cpp2 is the overloading of parentheses. Sometimes, it's just a primary-expression (using the comma operator). Sometimes, it's an argument list (it's rightful use). Sometimes, it lowers to a braced-init-list.

Instead of overloading that last meaning, what if we just made the type-id in unnamed-declaration optional? So we require := before the parentheses:

    check(:point = (0, 0)); // OK, explicit initialization syntax
    check(:=(0, 0));        // OK, chosen constructor is `implicit`

That could resolve #321, #449, and close #487.

-        tab = (3,2,1);
+        tab = :=(3,2,1);
-    check((0, 0)); // BUG!
+    check(:=(0, 0));
-  v                   = ();
+  v                   = :=();

#487 also mentions #408 and #451. #408 would also be resolved.

-f: (forward x) -> forward _ = (forward x); // error: lowers to `return { CPP2_FORWARD(x) };`
+f: (forward x) -> forward _ = (forward x); // OK
+f: (forward x) -> forward _ = :=(forward x); // error: lowers to `return { CPP2_FORWARD(x) };`
-f: (forward x) -> forward _ = forward x;   // error: missing parentheses
+f: (forward x) -> forward _ = forward x;   // OK

#451 is superficially resolved, but shouldn't be closed, because as quoted:

a = {x, y} would happen to work, but actually construct a t and then move-assign to a

It might seem tempting to continue permitting v = () by drawing parallels with https://github.com/hsutter/cppfront/issues/386#issuecomment-1518194517, but v is a variable, and not a type-id like in :v = ().

JohelEGP avatar Jul 14 '23 13:07 JohelEGP

Another point that's been bugging me with regards to generalized braced-init-list and the idea that x: quantity = 0; is "explicit" (https://github.com/hsutter/cppfront/issues/468#issuecomment-1627541323).

tiles: tiles = (1); defines a container of tiles. It has a non-implicit this operator= from i32. Unfortunately, the syntax tiles: tiles = 1; also works, but tiles are NaN!

JohelEGP avatar Jul 25 '23 15:07 JohelEGP

I tried this hack: https://github.com/hsutter/cppfront/issues/568#issuecomment-1662264109. And found out that a braced-init-list argument breaks UFCS (https://cpp2.godbolt.org/z/ohz4ajMvP):

#define INIT(...) {__VA_ARGS__}
f: (a: i32, b: i32) -> i32 = a + b;
void cpp1() { (void) f(0, {1}); } // OK
main: () = { _ = 0.f(INIT(1)); } // error
main.cpp2:4:27: error: no matching function for call to object of type '(lambda at /app/main.cpp2:4:27)'
    4 | auto main() -> int{(void) CPP2_UFCS(f, 0, INIT(1)); }// error
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:713:40: note: expanded from macro 'CPP2_UFCS'
  713 | #define CPP2_UFCS(FUNCNAME,PARAM1,...) \
      |                                        ^
  714 | [&] CPP2_LAMBDA_NO_DISCARD (auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  715 |     if constexpr (requires{ CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); }) { \
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  716 |         return CPP2_FORWARD(obj).FUNCNAME(CPP2_FORWARD(params)...); \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  717 |     } else { \
      |     ~~~~~~~~~~
  718 |         return FUNCNAME(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  719 |     } \
      |     ~~~
main.cpp2:4:27: note: candidate template ignored: substitution failure [with obj:auto = int]: deduced incomplete pack <(no value)> for template parameter 'params:auto'
    4 | auto main() -> int{(void) CPP2_UFCS(f, 0, INIT(1)); }// error
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:713:40: note: expanded from macro 'CPP2_UFCS'
  714 | #define CPP2_UFCS(FUNCNAME,PARAM1,...) \
      |                                        ^
  715 | [&] CPP2_LAMBDA_NO_DISCARD (auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
      |                                                   ~~~~~~

JohelEGP avatar Aug 02 '23 14:08 JohelEGP

I just missed being able to make an argument be non-deducible. I wanted x/v0, but had to do y/v1 (https://compiler-explorer.com/z/P1q6sb4dG):

template<class T> struct C {
  T a;
  T b;
};
auto x(auto a, auto b) { return C{a, {b}}; }
auto y(auto a, auto b) { return C<decltype(a)>{a, b}; }
C v0 = x(0LL, 0);
C v1 = y(0LL, 0);

In my actual code base, this is the difference:

  export point: (x, y) -> _ = :cartesian::point2d = (x, y);
  export rectangle_v: <O, N> (position: point_t<O, N>, size) -> rectangle<O, N> = (position, size);

rectangle_v should have looked just like point. But those are actually workarounds for Clang's lack of CTAD for aliases and a bug for CTAD of prvalue aggregates.

JohelEGP avatar Sep 13 '23 13:09 JohelEGP