hsutter / cppfront

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

[BUG] Error returning `ostream` expression from a type's streaming operator `operator<<` #1072

Closed bluetarpmedia closed 1 month ago

bluetarpmedia commented 1 month ago

Describe the bug Cppfront reports this error: a 'forward' return type cannot return a temporary variable when returning an expression containing a call to ostream::operator<< from a type's streaming operator.

To Reproduce Run cppfront on this code:

P: @struct type = {
    first: int = 0;
    second: char = '\0';
    operator<<: (inout os: std::ostream, p: P) -> forward std::ostream = {
        return os << '{' << p.first << ",'" << p.second << "'}";
    }
}

cppfront reports this error: a 'forward' return type cannot return a temporary variable

[Aside: Is forward std::ostream the correct way to generate the desired std::ostream& return type?]

Changing the operator<< implementation to the following succeeds:

operator<<: (inout os: std::ostream, p: P) -> forward std::ostream = {
    os << '{' << p.first << ",'" << p.second << "'}";
    return os;
}

Repro on Godbolt

Additional context I'm translating the ranges::for_each_n example code from cppreference:

struct P
{
    int first;
    char second;
    friend std::ostream& operator<<(std::ostream& os, const P& p)
    {
        return os << '{' << p.first << ",'" << p.second << "'}";
    }
};
gregmarr commented 1 month ago
    operator<<: (inout os: std::ostream, p: P) -> forward std::ostream = {
        return os << '{' << p.first << ",'" << p.second << "'}";
    }
    operator<<: (inout os: std::ostream, p: P) -> forward std::ostream = {
        os << '{' << p.first << ",'" << p.second << "'}";
        return os;
    }

I think the latter (which doesn't produce the error) is better for compiler diagnostics since the compiler can tell that the thing being returned is the same as the input parameter. With the chaining return, it can't tell that it's the same without examination of every one of the operator<< calls invoked to be sure that the return value is the same as the input value. If the bodies of those calls aren't available to the compiler, then it's impossible to tell for sure.

It would be good to cover this case in the docs/design notes, since I suspect that people are going to run into this quite a bit when writing their own operators.

hsutter commented 1 month ago

Thanks! Yes, the difficulty is that cppfront currently doesn't know whether the expression is a reference to a non-local.

For now I'll try to improve the error message to indicate the above way to rewrite the code.