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] in parameter's type is non-deducible #367

Closed
JohelEGP opened this issue Apr 13, 2023 · 3 comments
Closed

[BUG] in parameter's type is non-deducible #367

JohelEGP opened this issue Apr 13, 2023 · 3 comments
Labels
bug Something isn't working

Comments

@JohelEGP
Copy link
Contributor

Discovered at #343 (comment).
A parameter type that uses a template parameter,
when lowered to C++1, is wrapped in cpp2::in,
making it non-deducible.

Minimal reproducer (https://godbolt.org/z/GoMWoT3WM):

f: <T> (_: std::vector<T>) = { }

main: () = {
    v: std::vector<int> = ();
    f(v);
}

Commands:

cppfront x.cpp2
clang++17 -std=c++2b -stdlib=libc++ -I $CPPFRONT_INCLUDE_DIR x.cpp

Expected result:
template<typename T> auto f(std::vector<T> _) -> void{}
to be generated (no use of cpp2::in).

Actual result and error:

Generated C++1.
//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


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

#line 1 "/app/main.cpp2"
template<typename T> auto f(cpp2::in<std::vector<T>> _) -> void;

auto main() -> int;

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

#line 1 "/app/main.cpp2"
template<typename T> auto f(cpp2::in<std::vector<T>> _) -> void{}

auto main() -> int{
    std::vector<int> v {}; 
    f(std::move(v));
}
main.cpp2:5:5: error: no matching function for call to 'f'
    f(std::move(v));
    ^
main.cpp2:1:27: note: candidate template ignored: couldn't infer template argument 'T'
template<typename T> auto f(cpp2::in<std::vector<T>> _) -> void{}
                          ^
1 error generated.
@JohelEGP
Copy link
Contributor Author

Minimal reproducer (https://godbolt.org/z/3v8b36s3q):

f: <T: std::regular> (_: T) = { }
main: () = f(0);

Commands:

cppfront x.cpp2
clang++17 -std=c++2b -stdlib=libc++ -I $CPPFRONT_INCLUDE_DIR x.cpp

Expected result:

template<std::regular T> auto f(T _) -> void{}

Actual result and error:

template<std::regular T> auto f(cpp2::in<T> _) -> void{}
Full generated Cpp1.
//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"



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

#line 1 "/app/main.cpp2"
template<std::regular T> auto f(cpp2::in<T> _) -> void;
auto main() -> int;


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

#line 1 "/app/main.cpp2"
template<std::regular T> auto f(cpp2::in<T> _) -> void{}
auto main() -> int { f(0);  }
main.cpp2:2:22: error: no matching function for call to 'f'
auto main() -> int { f(0);  }
                     ^
main.cpp2:1:31: note: candidate template ignored: couldn't infer template argument 'T'
template<std::regular T> auto f(cpp2::in<T> _) -> void{}
                              ^
1 error generated.

@hsutter
Copy link
Owner

hsutter commented Apr 23, 2023

Thanks! There are two examples here, taking them one by one...

f: <T> (_: std::vector<T>) = { }

You're right, I should include that in the "hey this is using a template parameter name" deduced-type case.

I've done that now in the above commit -- please check?

f: <T: std::regular> (_: T) = { }

This is using the value parameter syntax (see NOTE), so in this position Cpp2 expects std::regular is a type (not a concept). I realize that Cpp1 allows a concept name in the analogous position, but Cpp2 aims to have a syntax that does not require name lookup to decide what something is (either actual name lookup in the compiler, or even just visual name lookup for the programmer to figure it out), so my stake in the ground is that anything of this form should mean "value."

Today you can express this using requires, like this

f: <T> (_: T) requires std::regular<T> = { }

which lowers to this

template<typename T> auto f(T const& _) -> void
requires (std::regular<T>)
{}

I've thought of supporting a syntactic sugar for this case along the lines of

//  Speculative possibility (not implemented)
f: <T is std::regular> (_: T) = { }

and make it lower to the same thing. But for now the answer is requires... as a user who encountered and reported this, does that seem like a reasonable answer to you? (Be frank!)


(NOTE) In general in parameters of templated type T are emitted as const T& for this reason, so this

//  A type parameter says 'type' (possibly implicitly)
f: <T: type> (_: T) = { }

or its short form

f: <T> (_: T) = { }    // same, omitting default optional syntax

lowers to this

template<typename T> auto f(T const& _) -> void{}

Note that the syntax for non-type (value) parameters is the same as for objects, so this

//  A value parameter does not say 'type'
f: <I: int> (_: I) = { }

lowers to this

template<int I> auto f(cpp2::in<I> _) -> void{}

@JohelEGP
Copy link
Contributor Author

I've done that now in the above commit -- please check?

The generated code looks good now.

as a user who encountered and reported this, does that seem like a reasonable answer to you? (Be frank!)

Yes! I felt something was odd, but couldn't quite grasp it.

zaucy pushed a commit to zaucy/cppfront that referenced this issue Dec 5, 2023
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

2 participants