cppfront
cppfront copied to clipboard
[BUG] _braced-init-list_ argument in Cpp2
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 remembercheck((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})?
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.
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 atand then move-assign toa
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 = ().
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!
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) { \
| ~~~~~~
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.
Related: https://github.com/hsutter/cppfront/issues/568#issuecomment-1723403330, #678, #853.