Skip to content

[BUG] t: @struct <T> type doesn't produce a templated class #695

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

Closed
leejy12 opened this issue Sep 23, 2023 · 11 comments
Closed

[BUG] t: @struct <T> type doesn't produce a templated class #695

leejy12 opened this issue Sep 23, 2023 · 11 comments
Labels
bug Something isn't working

Comments

@leejy12
Copy link

leejy12 commented Sep 23, 2023

Compiling the following Cpp2 code

MyStruct: @struct <T> type = {}

produces a non-templated class.

class MyStruct
{
};

Explicitly stating that T is a type parameter resolves this issue, but I wonder if this is the intended behavior.

MyStruct: @struct <T: type> type = {}

compiles to

template <typename T> class MyStruct
{
};

This behavior is in contrast to custom types without the @struct metafunction.

MyClass: <T> type = {}

compiles to

template <typename T> class MyClass
{
public:
    MyClass() = default;

public:
    MyClass(MyClass const&) = delete; /* No 'that' constructor, suppress copy */
public:
    auto operator=(MyClass const&) -> void = delete;
};
@leejy12 leejy12 added the bug Something isn't working label Sep 23, 2023
@JohelEGP
Copy link
Contributor

This used to error, saying "struct<T> is not a defined built-in metafunction"
before Cppfront started parsing the template arguments for @enum<T> support.

This is intended, as it's part of the grammar (as evidenced by @enum<u8> working).
See also #642 (reply in thread).

@msadeqhe
Copy link

Is there a reason that the syntax isn't like this?

MyStruct: <T> @struct type = {}

@JohelEGP
Copy link
Contributor

It might give the false sense that you could pass the template parameters to the metafunction.

From commit d8c1a50:

  • I want the syntax to not close the door on applying meta functions to declarations other than types. So putting the decoration up front right after : is important, because putting it at the end of the type would likely much harder to read for variables and especially functions.

@hsutter
Copy link
Owner

hsutter commented Sep 26, 2023

Thanks! Yes, this is max munch, but it should get a diagnostic.

With the next commit, @struct <T> type gets this error:

struct did not use its template arguments - did you mean to write 'struct<T: type>' ?

The following code already worked, but now the error guides you to it:

test: @struct <T:type> type = {
    x: int;
    t: T;
}

main: () = {
    x: test<std::string> = (1, "xyzzy");
    x.t = x.t.substr(3);
    std::cout << x.t;  // prints zy
}

Again, thanks.

@msadeqhe
Copy link

msadeqhe commented Sep 27, 2023

Thanks @JohelEGP. Also from commit 881f86f:

...
I did notice one grammar detail... currently the meta function name comes first, which means that a short-form <T> template parameter list can look syntactically like a template argument list. That is,

X: @mymetafunc <T> type = ...

parses as having the meta function name mymetafunc<T>, because of max munch. That's okay, if you wanted the <T> to be a template parameter list, you can just write it out as <T: type> and it's fine. But this might be a reason to put the meta function name first, like

@mymetafunc X: <T> type = ...

which those used to Python style (or who just prefer a newline) will write as

@mymetafunc
X: <T> type = ...

... aaaaand we'll forever be accused of slavishly copying Python. 😁 (IIUC, Python had different reasons to put the decorator before the name.)

For now I'll leave the decorator meta function name where it is, because I kind of like the "adjective type" naming style (ever since I did interface class for C++/CLI), but this is something that's easy to tweak based on experience and feedback...

This example is similar (but not exactly) to your suggestion "Down with typename!" #628 in which @hsutter wrote:

Can you elaborate on what the proposal is though? Cpp2 currently doesn't use typename, and if I discover any places where it would be needed my first thought would be to fix the grammar so it's unambiguous.

I think X: @mymetafunc <T> type = ... is one of those places where it needs :type to resolve the ambiguity in which it adds complexity to the grammar. On the other hand, :type could be unnecessary if Cpp2 had a different grammar as @hsutter described in his commit.

@msadeqhe
Copy link

What if a meta function has a template argument and I suddenly forget about max munch?

T: type = { ... }
X: @meta <T> type = { ... }

It compiles fine, but X is not a generic type where a programmer may mistakenly think it's equal to this one (because this is the default behavior in everywhere else):

T: type = { ... }
X: @meta <T: type> type = { ... }

Now, if the programmer write this code, they would be surprised why it doesn't work:

let x: X<i32> = ...;

Novice programmers (like me) wouldn't like to fight with the complexity of the grammar.

@msadeqhe
Copy link

msadeqhe commented Sep 27, 2023

What if I want to pass generic parameter to a meta function?

Color: @enum<T> <T: type> type = {
    r; b; g;
}

let c: Color<i32> = ...;

Is it possible with the current grammar? If it's like Cpp1, the answer is probably no, because the generic parameter is used before the generic argument declaration.

Now compare with this syntax, it would be readable and possible:

// Color:    <T> @enum<T> type = {
Color: <T: type> @enum<T> type = {
    r; b; g;
}

let c: Color<i32> = ...;

Maybe it's an out of scope feature, and meta functions won't ever be able to get their generic parameter from a generic argument of a declaration.

@msadeqhe
Copy link

msadeqhe commented Sep 27, 2023

@hsutter, would Cpp2 possibly allow function declarations with meta functions in the future? If the answer is yes, what if the meta function is placed before =:

X: <T, U> (a: T, b: U) @meta<decltype(a[b])> = {...}
A: <T> type @meta<T> = {...}

It allows the programmer to work with function arguments and pass them to the meta function.

@JohelEGP
Copy link
Contributor

It might give the false sense that you could pass the template parameters to the metafunction.

I was wrong.
At least Cppfront accepts it.
It's up to the Cpp1 compiler to error.
Probably because metafunction application happens before all template stuff.

You can't test it with @enum due to #704 (comment).
But the generated code looks reasonable (https://cpp2.godbolt.org/z/fEYG1WjME):

machine: @enum<T> <T> type = {
  off;
  on;
}
main: () = {
  _ = machine<int>::off;
  _ = machine<long>::off;
}
template<typename T> class machine {
private: T _value; private: constexpr machine(cpp2::in<cpp2::i64> val);

private: constexpr auto operator=(cpp2::in<cpp2::i64> val) -> machine& ;
public: [[nodiscard]] constexpr auto get_raw_value() const& -> T;
public: constexpr machine(machine const& that);
public: constexpr auto operator=(machine const& that) -> machine& ;
public: constexpr machine(machine&& that) noexcept;
public: constexpr auto operator=(machine&& that) noexcept -> machine& ;
public: [[nodiscard]] auto operator<=>(machine const& that) const& -> std::strong_ordering = default;
public: static const machine off;
public: static const machine on;
public: [[nodiscard]] auto to_string() const& -> std::string;
public: friend auto operator<<(std::ostream& o, cpp2::in<machine> val) -> std::ostream&;

};

@hsutter
Copy link
Owner

hsutter commented Sep 28, 2023

Thanks for the feedback, I'll think again about the grammar position of applied metatypes.

But for the original question, I was actually thinking of a different fix... just like for postfix-expressions any postfix operators must not have preceding whitespace between the identifier and the operator, I think for id-expressions any postfix qualifiers (i.e., the template argument list) must also not have preceding whitespace. I think that's actually clear for programmers, and most of the time they wouldn't even notice the rule because you just naturally write x: @struct <T> type (has space) for a templated type (as @leejy12 did!) and y: @enum<i64> type (no space) for a template argument list, so this solution Just Does the Right Thing. And I like the consistency with postfix-expressions.

hsutter added a commit that referenced this issue Sep 28, 2023
This is a better solution for #695

Now `S: @struct <T> type` just works and does the expected thing
@hsutter
Copy link
Owner

hsutter commented Sep 28, 2023

OK, I've pushed the commit: fa9ba7f

Now the original code just works and does the right and expected thing:

MyStruct: @struct <T> type = {}

Thanks again!

zaucy pushed a commit to zaucy/cppfront that referenced this issue Dec 5, 2023
zaucy pushed a commit to zaucy/cppfront that referenced this issue Dec 5, 2023
This is a better solution for hsutter#695

Now `S: @struct <T> type` just works and does the expected thing
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

4 participants