fix(cpp1): emit concept in Phase 1 "Cpp2 type declarations"#578
fix(cpp1): emit concept in Phase 1 "Cpp2 type declarations"#578JohelEGP wants to merge 1 commit intohsutter:mainfrom
concept in Phase 1 "Cpp2 type declarations"#578Conversation
|
Aaah... |
|
It's currently more convenient for me to have |
|
Emitting What I have found is that a library that defines concepts and uses them In contrast, if emitted in Phase 1, |
|
The same would be true for a concept that needs to use something emitted in Phase 2 "Cpp2 type definitions and function declarations" |
… type function This commit includes "just enough" to make this first meta function work, which can be used like this... ``` Human: @interface type = { speak: (this); } ``` ... where the implementation of `interface` is just about line-for-line from my paper P0707, and now (just barely!) compiles and runs in cppfront (and I did test the `.require` failure cases and it's quite lovely to see them merge with the compiler's own built-in diagnostics): ``` //----------------------------------------------------------------------- // interface: an abstract base class having only pure virtual functions auto interface( meta::type_declaration& t ) -> void { bool has_dtor = false; for (auto m : t.get_members()) { m.require( !m.is_object(), "interfaces may not contain data objects"); if (m.is_function()) { auto mf = m.as_function(); mf.require( !mf.is_copy_or_move(), "interfaces may not copy or move; consider a virtual clone() instead"); mf.require( !mf.has_initializer(), "interface functions must not have a function body; remove the '=' initializer"); mf.require( mf.make_public(), "interface functions must be public"); mf.make_function_virtual(); has_dtor |= mf.is_destructor(); } } if (!has_dtor) { t.require( t.add_member( "operator=: (virtual move this) = { }"), "could not add pure virtual destructor"); } } ``` That's the only example that works so far. To make this example work, so far I've added: - The beginnings of a reflection API. - The beginnings of generation from source code: The above `t.add_member` call now takes the source code fragment string, lexes it, parses it, and adds it to the `meta::type_declaration` object `t`. - The first compile-time meta function that participates in interpreting the meaning of a type definition immediately after the type grammar is initially parsed (we'll never modify a type after it's defined, that would be ODR-bad). I have NOT yet added the following, and won't get to them in the short term (thanks in advance for understanding): - There is not yet a general reflection operator/expression. - There is not yet a general Cpp2 interpreter that runs inside the cppfront compiler and lets users write meta functions like `interface` as external code outside the compiler. For now I've added `interface`, and I plan to add a few more from P0707, as meta functions provided within the compiler. But with this commit, `interface` is legitimately doing everything except being run through an interpreter -- it's using the `meta::` API and exercising it so I can learn how that API should expand and become richer, it's spinning up a new lexer and parser to handle code generation to add a member, it's stitching the generated result into the parse tree as if it had been written by the user explicitly... it's doing everything I envisioned for it in P0707 except for being run through an interpreter. This commit is just one step. That said, it is a pretty big step, and I'm quite pleased to finally have reached this point. --- This example is now part of the updated `pure2-types-inheritance.cpp2` test case: // Before this commit it was this Human: type = { speak: (virtual this); } // Now it's this... and this fixed a subtle bug (can you spot it?) Human: @interface type = { speak: (this); } That's a small change, but it actually also silently fixed a bug that I had written in the original code but hadn't noticed: Before this commit, the `Human` interface did not have a virtual destructor (oops). But now it does, because part of `interface`'s implementation is to generate a virtual destructor if the user didn't write one, and so by letting the user (today, that was me) express their intent, we get to do more on their behalf. I didn't even notice the omission until I saw the diff for the test case's generated `.cpp` had added a `virtual ~Human()`... sweet. Granted, if `Human` were a class I was writing for real use, I would have later discovered that I forgot to write a virtual destructor when I did more testing or tried to do a polymorphic destruction, or maybe a lint/checker tool might have told me. But by declaratively expressing my intent, I got to not only catch the problem earlier, but even prevent it. I think it's a promising data point that my own first attempt to use a metaclass in such a simple way already fixed a latent simple bug in my own code that I hadn't noticed. Cool beans. --- Re syntax: I considered several options to request a meta function `m` be applied to the type being defined, including variations of `is(m)` and `as(m)` and `type(m)` and `$m`. I'm going with `@m` for now, and not because of Python envy... there are two main reasons: - I think "generation of new code is happening here" is such a fundamental and important new concept that it should be very visible, and actually warrants taking a precious new symbol. The idea of "generation" is likely to be more widely used, so being able to have a symbol reserved for that meaning everywhere is useful. The list of unused symbols is quite short (Cpp2 already took `$` for capture), and the `@` swirl maybe even visually connotes generation (like the swirl in a stirred pot -- we're stirring/cooking something up here -- or maybe it's just me). - 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.
This is generally more useful.
Mentioned at #406 (comment).
Testing summary:
Acknowledgements: