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

[BUG] _braced-init-list_ argument in Cpp2 #542

Open
JohelEGP opened this issue Jul 13, 2023 · 6 comments · May be fixed by #927
Open

[BUG] _braced-init-list_ argument in Cpp2 #542

JohelEGP opened this issue Jul 13, 2023 · 6 comments · May be fixed by #927
Labels
bug Something isn't working

Comments

@JohelEGP
Copy link
Contributor

JohelEGP commented Jul 13, 2023

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).
ab29f19 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.

-- #449 (comment) (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
Copy link
Contributor Author

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
Copy link
Contributor Author

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 #386 (comment),
but v is a variable, and not a type-id like in :v = ().

@JohelEGP
Copy link
Contributor Author

Another point that's been bugging me
with regards to generalized braced-init-list and
the idea that x: quantity = 0; is "explicit" (#468 (comment)).

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
Copy link
Contributor Author

JohelEGP commented Aug 2, 2023

I tried this hack: #568 (comment).
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
Copy link
Contributor Author

JohelEGP commented Sep 13, 2023

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
Copy link
Contributor Author

JohelEGP commented Sep 14, 2023

Related: #568 (comment), #678, #853.

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.

1 participant