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] @value @struct type doesn't compile #447

Closed
realgdman opened this issue May 9, 2023 · 19 comments
Closed

[BUG] @value @struct type doesn't compile #447

realgdman opened this issue May 9, 2023 · 19 comments
Labels
bug Something isn't working

Comments

@realgdman
Copy link

realgdman commented May 9, 2023

According to grammar and other notes, type can have several metaclasses
meta-functions-list: meta-functions-list '@' id-expression
But compilation depends on order of those declaration.

S : @struct @value type = { i: int = 0; }
Compiles

S : @value @struct type = { i: int = 0; }
error: while applying @struct - a struct may not have a user-defined operator=

Another example
S : @interface @polymorphic_base type = { }
Compiles

S : @polymorphic_base @interface type = { }
error: while applying @interface - interface functions must not have a function body; remove the '=' initializer

https://cpp2.godbolt.org/z/4v47aGcGn

Version
latest (38aec57)

Command lines
cppfront/cppfront $1.cpp2 -p
clang++-15 -Icppfront/include $1.cpp -std=c++20 -o $1

Expected result
Consistent compilation result, not depending on order

Additional notes
In this particular combination, @struct check there are no user operator=, and then emits its own as basic_value.
But as @value already emitted it's own, @struct doesn't distinguish that those aren't user defined.
For @interface that's probably add_virtual_destructor from @polymorphic base.

Optional Enhancement Suggestion

Other combinations can give errors both cases, but different depending on order.
For example,
S: @interface @value type //error: while applying @value - a value type may not have a protected or virtual function
S: @value @interface type //error: while applying @interface - interfaces may not copy or move; consider a virtual clone() instead
Suggestion - applying of @meta can track and signal what @Metas are contradicting.

@realgdman realgdman added the bug Something isn't working label May 9, 2023
@JohelEGP
Copy link
Contributor

I think the current behavior is by design.

The meta functions list is a pipe of transformations with the type as input.

You're suggesting to make the process impossible to be pure by introducing dependent state.
I'm having a hard time thinking that will scale.

@realgdman
Copy link
Author

My first though was, since constructors emitted from reflection API, they could be marked somehow to distinguish cppfront-generated from user-defined. Probably thats just extra flag in node. I haven't looked at code, but suppose type formed from text code, then meta applied, so it possibly can be tracked.

Last suggestion can be scratched off.

Regarding pipe, could it be then some @meta1 writes something, then @meta2 overwrites it, breaking @meta1 assumption? Currently I haven't found such cases, but could it be in future?

@JohelEGP
Copy link
Contributor

It doesn't break @meta1's assumption.
It has already done its job and returned.

Your suggestion can also be done as a library.
t: @protected<value, interface> type = { };,
where @protected keeps track of the values as they change,
and complains however you want it because it's a library.

@realgdman
Copy link
Author

realgdman commented May 10, 2023

Ok you motivated me to find better (or worse) example :)

S : @copyable @interface type = { }
Doesn't compile, thats right, interfaces cannot have nonvirtual function
error: while applying @interface - interfaces may not copy or move;

S : @interface @copyable type = { }

Compiles, having nonvirtual function
class S {
public: virtual ~S();
public: S(S const& that); }

While I understand what you mean, by metaclasses are executed compile time programs, this feels like broken expectation for programmer, since I declaratively asked it to be interface, and it's contradicting requirements aren't checked.

@JohelEGP
Copy link
Contributor

That's a fair case.
The expectations of that programmer doesn't match the actual semantics of the meta functions list.
Either they want @protected<copyable, interface> described at #447 (comment),
or Cpp2 should make the grammar meta-function and not meta-functions-list,
and require either the above, or @sequential<copyable, interface>.
So the actual semantics of a meta functions list are opt-in (via a templated metafunction).

@realgdman
Copy link
Author

One more finding,
T: @partially_ordered @interface type = { }
emits operator<=>(...) = default = 0;

@realgdman
Copy link
Author

realgdman commented May 10, 2023

One idea, if combining requirements isn't possible, have syntax like T: @meta1 +@meta2 type thus allowing by default only 1 meta and have user explicit accept consequences of overwriting with 2nd.

Another wild idea, run meta application twice, allowing earlier metas to recheck if they still hold. I haven't explored in depth, will it work in any cases; but possibly this could work with imposed limitations, that metas only either check for some condition, or check+emit, in second pass emit could be skipped.
I.e. on second pass if mf.is_default_access() { mf.make_public(); } not making, put checking for public.

@AbhinavK00
Copy link

Should this even be supported? There are metaclasses that contradict each other as shown in comments above. So should we enable this feature just to support a small set of combinations? Probably not
I think the appropriate solution would just be for programmer to write his own metaclass by combining the required two (once it's supported, or is it already?)

@realgdman
Copy link
Author

Probably, I have reported for that particular issue, but probably it can be superseded with bigger issue, if multiple metaclasses should be allowed at all.
From 0707 proposal it's not clear it they are supposed to be combined by user. Internally they are kinda "derived" or reused, i.e. "struct is-a basic_value" so that could mean user need to write his own.

On the other hand, it could be reasonable to have something @interface @canMeow @canEat

Also interaction with inheritance should be explored, i.e

B: @value type = { i: int = 0; }
T: @polymorphic_base type = { this: B; }

(polymorphic_base is planned to change, but currently it is not allowing data)
Should this be checked for error by compiler?
From presentation It was perceived as more strongly typed, but after trying I see it closer to C marcos, than to C++ templates.

@JohelEGP
Copy link
Contributor

Something that concerns me is the use of is and metafunctions.
The standard's proposal intends for is to be able to ask if a metafunction was applied to a type's declaration.
The information that gives is only useful in very constrained cases.
Consider the extreme t: @value @whatever @empty_type type = { },
where @empty_type makes the final type equivalent to t: type = { }.
t is value is useless.

@JohelEGP
Copy link
Contributor

Another wild idea, run meta application twice, allowing earlier metas to recheck if they still hold. I haven't explored in depth, will it work in any cases; but possibly this could work with imposed limitations, that metas only either check for some condition, or check+emit, in second pass emit could be skipped.

You can see a use-case by cppfront itself that your suggestion would break:
f0802be#diff-9124d88032e1de131eaa643c46e63344e15dd90e07eea20b2c0fcc5e3b9c84d5R149.

@realgdman
Copy link
Author

realgdman commented May 15, 2023

I'm not sure about that, because comment above that line says it is uncopyable wrapper. This could be as intended, but could be illustration of #447 (comment) issue, where compiler didn't warn about contradiction.

Edit: author answered that is intent.

@JohelEGP
Copy link
Contributor

Yeah, you did comment on that at 38aec57#r113218852.

@hsutter
Copy link
Owner

hsutter commented May 15, 2023

Ah, that "noncopyable" is a stray comment from an earlier design I was trying out. Thanks for the catch, comment fixed.

@hsutter
Copy link
Owner

hsutter commented May 15, 2023

While I'm here: Yes, the design is to pipeline these. And I'm still considering the is metafunction but it was always expected to work only for certain kinds of metafunctions, and when a single metafunction is being used.

@realgdman
Copy link
Author

realgdman commented May 15, 2023

Then this can be closed, I think? Since it's probably not worth effort and should be programmer responsibility.

@hsutter
Copy link
Owner

hsutter commented May 15, 2023

OK, and thanks again.

@hsutter hsutter closed this as completed May 15, 2023
@JohelEGP
Copy link
Contributor

JohelEGP commented Sep 4, 2023

Something that concerns me is the use of is and metafunctions.
The standard's proposal intends for is to be able to ask if a metafunction was applied to a type's declaration.
The information that gives is only useful in very constrained cases.
Consider the extreme t: @value @whatever @empty_type type = { },
where @empty_type makes the final type equivalent to t: type = { }.
t is value is useless.

While I'm here: Yes, the design is to pipeline these. And I'm still considering the is metafunction but it was always expected to work only for certain kinds of metafunctions, and when a single metafunction is being used.

That sounds like a terribly limited feature.

Opting into a nominal interface is much more generally useful
(like Carbon's checked generics).

@AbhinavK00
Copy link

Opting into a nominal interface is much more generally useful

Maybe I don't fully understand your point, but is it similar to deriving feature in haskell. We have interfaces in cpp2, but they are limited as the user has to implement the methods himself or he could rely on default implementation which is not always enough (default implementation can't access members). Using metaprogramming, we could make it so that implementations could be generated for a type.
Think about today's

auto operator<=> (const Type&) const = default

currently this is baked into the language but this could be user-written with reflection. I'm talking about somethings like mixins from this cppnow talk

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