hsutter / cppfront

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

[BUG] Cannot value initialise a function argument with equivalent of C++ `{}` #1090

Closed bluetarpmedia closed 2 weeks ago

bluetarpmedia commented 1 month ago

Describe the bug I can't find a Cpp2 syntax to value initialise an argument passed to a function using its default constructor.

To Reproduce Run cppfront on this code:

log: (message: std::string_view,
      context: std::string,
      event_id: int) =
{
    std::println("{} {} {}", message, context, event_id);
}

main: () = {
    log("event", (), 123);  // (1) Error in C++ compiler
    log("event", _, 123);   // (2) Error in C++ compiler
    log("event", {}, 123);  // (3) Error in cppfront
}

(1) produces this error:

main.cpp2:10:19: error: expected expression
   10 |     log("event", (), 123);
      |                   ^

(2) produces this error:

main.cpp2:10:18: error: use of undeclared identifier '_'
   10 |     log("event", _, 123);
      |                  ^

(3) produces a cppfront syntax error.

Repro on Godbolt

Additional context I was translating the ranges::find_end example from cppreference and have used explicit arguments like this:

found4:= std::ranges::find_end(
    secret, "SWORD"sv,
    std::ranges::equal_to(),
    std::identity(),
    :(c: char) std::tolower(c));

instead of the original:

const auto found4 = std::ranges::find_end(secret, "SWORD"sv,
        {},
        {},
        [](char c) { return std::tolower(c); });
sookach commented 1 month ago

Hi Neil, The solution I think might be simplest for this is to use '{}' for default initialized arguments just like c++, and adding it would be pretty straight forward. One approach is to allow brace expressions here:

https://github.com/hsutter/cppfront/blob/130f14952e9400b95dbc055a8d9d97fa66ed5b1e/source/parse.h#L5715-L5718

However that approach might not be best one given the comment right above it. I can probably add logic to the parser to grab '{}' only in this specific instance, but I'd like to hear from our BDFL, @hsutter, before going down any route.

Thanks.

hsutter commented 1 month ago

Thanks! In Cpp2 so far, { } are used only for scopes.

Hot take: () does already mean default construction (e.g., x : int = ();) so an empty list argument () could be lowered to {}.

sookach commented 1 month ago

I like that idea, I'll try it out.

DerpMcDerp commented 1 month ago

if () is changed to lower to {} then wouldn't that imply that explicit constructors should be included in the overload set? i.e.

The following doesn't work in C++ (but should)

struct B {
    explicit B(int);
};

void foo(B);

B asdf() {
    B _ = auto{1}; // should mean B _ = B{1};
    foo({1});      // should mean foo(B{1}); (some might disagree)
    foo(auto{1});  // should mean foo(B{1});

    if (rand())
        return {1};     // should mean return B{1}; (some might disagree)
    else
        return auto{1}; // should mean return B{1};
}

The following should work in Cpp2:

B: type = {
    operator=: (out this, _: i32) = {} // explicit ctor
}

foo: (_: B) = {}

B asdf() {
    _: B = (1);
    foo((1)); // some might disagree that this should work
    foo(:_ = (1));
    foo(_(1));

    if (rand())
        return (1); // some might disagree that this should work
    else if (rand())
        return :_ = (1);
    else
        return _(1);
}