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] errors compiling the hello world exmaple #1334

Open
hros opened this issue Nov 7, 2024 · 14 comments
Open

[BUG] errors compiling the hello world exmaple #1334

hros opened this issue Nov 7, 2024 · 14 comments
Labels
bug Something isn't working

Comments

@hros
Copy link

hros commented Nov 7, 2024

Describe the bug
I get a couple of compiler errors compiling hello.cpp which is generated from the hello.cpp2 example

To Reproduce
Steps to reproduce the behavior:

  1. Running cppfront on hello.cpp2 from the documentation results in
hello.cpp #define CPP2_IMPORT_STD Yes

//=== Cpp2 type declarations ====================================================
#include "cpp2util.h"

#line 1 "hello.cpp2"

//=== Cpp2 type definitions and function declarations ===========================

#line 1 "hello.cpp2"
auto main() -> int;

#line 7 "hello.cpp2"
[[nodiscard]] auto hello(cpp2::impl::instd::string_view msg) -> decltype(auto);

//=== Cpp2 function definitions =================================================

#line 1 "hello.cpp2"
auto main() -> int{
#line 2 "hello.cpp2"
std::vector words {"Alice", "Bob"};
hello(CPP2_ASSERT_IN_BOUNDS_LITERAL(words, 0));
hello(CPP2_ASSERT_IN_BOUNDS_LITERAL(cpp2::move(words), 1));
}

#line 7 "hello.cpp2"
[[nodiscard]] auto hello(cpp2::impl::instd::string_view msg) -> decltype(auto) {
return std::cout << "Hello, " + cpp2::to_string(msg) + "!\n"; }

  1. Command lines including which C++ compiler you are using:
    g++ version 14.2.0 on Ubuntu 24.4
    g++ -std=c++20 hello.cpp -o hello -I$HOME/.local/cppfront/include

  2. Expected result - compile successfully

  3. Actual result/error:

hello.cpp2: In function ‘int main()’:
hello.cpp2:3:10: error: use of ‘decltype(auto) hello(cpp2::impl::in<std::basic_string_view<char> >)’ before deduction of ‘auto’
    3 |     hello( words[0] );
      |     ~~~~~^~~~~~~~~~~~~
hello.cpp2:4:10: error: use of ‘decltype(auto) hello(cpp2::impl::in<std::basic_string_view<char> >)’ before deduction of ‘auto’
    4 |     hello( words[1] );
      |     ~~~~~^~~~~~~~~~~~~

Additional context
Add any other context about the problem here.

@hros hros added the bug Something isn't working label Nov 7, 2024
@gregmarr
Copy link
Contributor

gregmarr commented Nov 7, 2024

The clang version:

cpp2:

main: () = hello("Alice");
hello: (msg: std::string_view) = std::cout << "Hello, (msg)$!\n";

cpp1:

auto hello(cpp2::impl::in<std::string_view> msg) -> decltype(auto);

auto main() -> int { hello("Alice");  }
auto hello(cpp2::impl::in<std::string_view> msg) -> decltype(auto) { return std::cout << "Hello, " + cpp2::to_string(msg) + "!\n";  }

clang error:

error: function 'hello' with deduced return type cannot be used before it is defined
   11 |     hello("Alice");
      |     ^

@DyXel
Copy link
Contributor

DyXel commented Nov 7, 2024

This became order-dependent after the tersest lambda change. Adding -> void to hello should fix it.

@gregmarr
Copy link
Contributor

gregmarr commented Nov 7, 2024

If it's order dependent, should cppfront still be writing out the declaration before the definition?

@DyXel
Copy link
Contributor

DyXel commented Nov 8, 2024

Good point, not sure. I think the current error is better than simply getting a "undeclared identifier". At least it gives you a hint about what the problem might be.

@gregmarr
Copy link
Contributor

gregmarr commented Nov 8, 2024

The Clang error is good, MSVC is also good:

<source>(5): error C3779: 'hello': a function that returns 'decltype(auto)' cannot be used before it is defined
<source>(3): note: see declaration of 'hello'

The GCC error could use some clarification. Once you know what the error means, you can squint and see the meaning there, but it's not immediately obvious.

use of ‘decltype(auto) hello(cpp2::impl::in<std::basic_string_view<char> >)’ before deduction of ‘auto’

Without the forward declaration, the errors are:

GCC: error: 'hello' was not declared in this scope
Clang: error: use of undeclared identifier 'hello'
MSVC: error C3861: 'hello': identifier not found

So it's a tossup whether it's better or not. I'm guessing most C++ developers are familiar with order dependence, and know what these errors mean.

Maybe cppfront can detect the order-dependent calls and produce an error? I suppose that would be difficult, since there could be another version that was #included from somewhere that it doesn't know about.

@ivo-doko
Copy link

ivo-doko commented Nov 20, 2024

This became order-dependent after the tersest lambda change.

This seems like an issue in itself.

Order independence is one of the design stakes of Cpp2/cppfront - "No forward declarations or ordering gotchas."

If it has turned out that this is not doable, then cppfront should accept and embrace the order-dependent nature it is forced to have, rather than pretend-having order independence, since that just ends up making issues caused by order dependence easier to stumble into and harder to figure out.

If it must be order dependent, then it needs to be designed in a way that makes that clear to the programmer and as easy to deal with as possible. Currently, that is clearly not the case.

@gregmarr
Copy link
Contributor

If it has turned out that this is not doable,

It is not available right now for free functions with deduced return types. It's too early to say that it's not doable at all for free functions with deduced return types. Even if that were to be true, that just means that free functions with deduced return types are not order-independent. Functions with fixed return types still are. It also shouldn't affect member functions. Whether that is a large percentage of the code or not still remains to be seen.

@ivo-doko
Copy link

Even if that were to be true, that just means that free functions with deduced return types are not order-independent.

That is an additional complication and corner-case that one needs to be aware of and keep in mind. As far as I'm aware, one of the goals of Cpp2 was precisely to eliminate as many complications and corner-cases as is possible from Cpp1.

Yes, one of those complications is order dependence itself. But if in endeavouring to eliminate it, Cpp2 merely produces a new complication ("Cpp2 is order-independent, except if/when..."), then that only does more harm than good, especially when it comes to people who are proficient in Cpp1, who are the main "target audience" anyway - it changes from being a new thing with eliminated complications w.r.t. Cpp1, to being a new thing with replaced complications w.r.t. Cpp1. And, to me, this particular complication seems worse than the one that was attempted to be resolved in the first place (order dependence).

I must admit it also feels a bit like burying the head in the sand to pretend that we have yet to tell how impactful this new complication is, when the provided hello world example suffers from its consequences. If you can get caught up in a complication when writing hello world, that's plainly and simply a problem that needs to be resolved.

@LT2H
Copy link

LT2H commented Nov 22, 2024

Adding { } to the hello function also fixes this.

main: () = {
    words: std::vector = ( "Alice", "Bob" );
    hello( words[0] );
    hello( words[1] );
}

hello: (msg: std::string_view) = {
    std::cout << "Hello, (msg)$!\n";
}

hello.cpp

auto main() -> int;

auto hello(cpp2::impl::in<std::string_view> msg) -> void;
auto main() -> int{
    std::vector words {"Alice", "Bob"}; 
    hello(CPP2_ASSERT_IN_BOUNDS_LITERAL(words, 0));
    hello(CPP2_ASSERT_IN_BOUNDS_LITERAL(cpp2::move(words), 1));
}

auto hello(cpp2::impl::in<std::string_view> msg) -> void{
    std::cout << "Hello, " + cpp2::to_string(msg) + "!\n";
}

@ivo-doko
Copy link

ivo-doko commented Nov 22, 2024

Adding { } to the hello function also fixes this.

That seems odd.

That makes me think that this should be resolvable with an appropriate parsing/translation improvement - omitting { } from function definitions ought to be merely a syntactic-sugar-level convenience, so I don't see a reason for that to have this sort of effect.

@zaucy
Copy link

zaucy commented Nov 22, 2024

omitting { } from function definitions ought to be merely a syntactic-sugar-level convenience

It is, but maybe not what you expected. Omitting { } translates to returning the expression.

// these are equivalent
f: (a, b) = a + b;
f: (a, b) -> _ = { return a + b; }

I for one expect and prefer that the shorter syntax returns my expression. It makes writing simple callbacks and predicates a breeze!

So adding { } in the hello world example makes it return void which doesn't have any order-dependent issues.

@gregmarr
Copy link
Contributor

There is discussion of this exact thing in the notes on the last release:
https://github.com/hsutter/cppfront/releases/tag/v0.8.0

an error that a deduced return type makes the function order-dependent, when the function is called from earlier in the source file... this is because a deduced return types creates a dependency on the body, and it is inherent (not an artifact of either Cpp1 or Cpp2)

@gregmarr
Copy link
Contributor

omitting { } from function definitions ought to be merely a syntactic-sugar-level convenience, so I don't see a reason for that to have this sort of effect.

As @zaucy described, you're not just omitting the {} from the function.

If you have this function (things inside [] are optional):

[name]: ([parameters]) -> return_type = { return something(); }

then you can remove the body prefix { return and the body suffix ; } so you get this.

[name]: ([parameters]) -> return_type = something()

If the type of something() is the same as return_type, then you can leave off the -> return_type.

If that type is void, then you might or might not not have the return there, but you can still do the same transformation.

Depending on where this definition is, you may need to add a ; at the end to terminate the statement, but that's not part of the function body. For example, if this is an If this is an inline lambda in a function call, then you don't:

    v : std::vector = (1,2,3,4,5,6,7,8);
    // Remove all even non-primes
    std::erase_if(v, :(e) = e != 2 || e % 2 == 0);

@ivo-doko
Copy link

Omitting { } translates to returning the expression.

you're not just omitting the {} from the function.

D'oh, apologies, I knew that, but just forgot.

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

No branches or pull requests

6 participants