Skip to content

[SUGGESTION] Add inspect alternatives for literals #79

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

Conversation

filipsajdak
Copy link
Contributor

@filipsajdak filipsajdak commented Oct 17, 2022

The current implementation of alternatives for inspect handles only types. It makes it impossible to replace the cpp1 switch with it. This change brings support for literals in alternatives which makes it possible to use as in given example:

std::cout << inspect v -> std::string {
  is lexeme::hash  = "lexeme hash enum";
  is lexeme2::hash = "lexeme2 hash enum";
  is "hash"        = "'hash' string";
  is 42            = "42 int";
  is 1.23          = "1.23 double";
  is 'a'           = "'a' character";
  is _             = "unmatched value";
} << std::endl;

The implementation of specializations of is() functions is complicated by the fact that the support for Non-Type Template Parameters (NTTP) is not fully (uniformly) implemented in GCC, clang, and MSVC. I have made a lot of tests to find a solution with workarounds that will not require preprocessor ifs for the specific compiler but one issue in clang makes me use it (probably a bug in clang). A working prototype of the solution is available here: https://godbolt.org/z/j1fqWKa9G (if you have any idea how to improve the implementation please let me know).

One of the supported NTTPs is double but there is an issue in clang and MSVC with it which was solved by introducing an intermediate type double_wrapper that makes it work for clang and MSVC. Unfortunately, it additionally needs a special version of is() function that makes the magic work for clang.

During my experiments, I have been studying https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2392r0.pdf to understand the feature.

This PR makes #74 obsolete - enums are also covered by this implementation. Closes #73

@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch 4 times, most recently from a88bbb3 to c49ceb1 Compare October 18, 2022 08:59
@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from c49ceb1 to f4866cf Compare October 20, 2022 21:21
@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from f4866cf to 04e7701 Compare October 30, 2022 19:58
@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from 04e7701 to a0e99b0 Compare November 29, 2022 21:15
@filipsajdak
Copy link
Contributor Author

Rebase to main. Make is() function constexpr

@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from a0e99b0 to 50b7932 Compare December 6, 2022 21:55
@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch 2 times, most recently from 599f007 to 921ed9f Compare December 15, 2022 22:46
@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from 921ed9f to 2c21174 Compare December 20, 2022 23:14
@hsutter
Copy link
Owner

hsutter commented Dec 21, 2022

Thanks! This also has some variant changes that I want to avoid now so as not to break variants with repeated types, but I don't think anything in the rest of the PR depends on those and I can just look at the literal-related changes -- correct?

@hsutter hsutter self-assigned this Dec 21, 2022
@filipsajdak
Copy link
Contributor Author

Check the literals part. I will check the commits. Give me a minute.

@filipsajdak filipsajdak force-pushed the fsajdak-inspect-alternatives-for-literals branch from 2c21174 to b7668e9 Compare December 21, 2022 19:28
@filipsajdak
Copy link
Contributor Author

Ahh... I was rebasing yesterday to resolve this dependency and forgot to push. Now fixed.

@hsutter
Copy link
Owner

hsutter commented Dec 21, 2022

OK, it's mostly clean, but GCC 10.3 is having trouble with the double_wrapper. I tried extending your #if defined(_clang_) with || defined(__GNUC__) but that didn't solve it. That's the main version of GCC I use as a baseline and everything else currently works with it, so I'd like not to have to increase the lowest level of GCC compatibility unless I have to.

Also, I'm trying out variations of your code to support also non-literals.

More soon...

@hsutter
Copy link
Owner

hsutter commented Dec 21, 2022

OK, I think I have it... thanks for prodding me about inspecting to match values.

I've taken inspiration from this PR and followed your basic parsing implementation (looks exactly right, thanks!) and I'll apply it in a separate commit because I reworked the basic implementation in cpp2util.h to just this:

inline constexpr auto is( auto const& x, auto const& value ) -> bool
    requires requires{ x == value; }
{
    return x == value;
}

inline constexpr auto is( auto const& x, auto const& value ) -> bool
    requires (!requires{ x == value; })
{
    return false;
}

This is the direction I had in mind for the "is value" case in P2392 which has "use ==" as one of the rule cascade that covers value comparison. The above has a couple of benefits (besides being shorter):

  • It isn't limited to literals, anything equality-comparable will do.
  • And so it also avoids doesn't require advanced compiler support for passing literals in non-type template arguments, so it's more easily portable.

So whereas for the "is type" case we still emit is<typeid>(__expr), for the "is value" case we emit is(__expr, value).

Granted, this doesn't force literals to be processed at compile time, but if that's an issue we can optimize later... and even now the literal is visible in the generated Cpp1 source, and it's just a single one-line inlined function away from the call site, so I'm optimistic the Cpp1 compiler will pick it up after inlining. (The eventual most aggressive optimization would be to optimize a series of is integer-literal alternatives as in P2392 section 4.2, by emitting that subset of consecutive alternatives as a C switch or a table lookup. But that's for much later.)

Here is my test case:

main: ()->int = {
    test(42);
    test(3.14);
    test(0);
    test(-42);
    test("xyzzy" as std::string);
}

test: (x:_) = {
    forty_two := 42;
    std::cout << inspect x -> std::string {
        is 0           = "zero";
        is (forty_two) = "the answer";
        is int         = "integer " + cpp2::to_string(x);
        is std::string = x;
        is _           = "(no match)";
    } << "\n";
}

This (now) prints:

the answer
(no match)
zero
integer -42
xyzzy

The one inelegant thing right now is the parens required for the (forty-two) case to turn it into a postfix-expression. I can clean that up later, this should be enough to unblock value matching.

Thanks for prompting this! I harvested your basic parser implementation and it was fine, I just changed the names a little, most of the change was the above generalization in cpp2util.h.

@hsutter hsutter closed this in c77de70 Dec 21, 2022
@filipsajdak
Copy link
Contributor Author

Excellent! I like the simplicity of this solution. I was afraid that my solution was too complicated.

@filipsajdak
Copy link
Contributor Author

That approach will probably simplify also use of the functions/unnamed functions in that context: #90

@filipsajdak filipsajdak deleted the fsajdak-inspect-alternatives-for-literals branch December 21, 2022 21:28
@hsutter
Copy link
Owner

hsutter commented Dec 22, 2022

Now that I've done the support requested in #90: Yes it did simplify it, but only after a lot of fiddling with if constexpr and requires { ... }, and getting their test order right, and remembering to bool { ... } in all the right places. :) Checking in now...

Azmah-Bad pushed a commit to Azmah-Bad/cppfront that referenced this pull request Feb 24, 2023
Also cleaned up _alternative_ to use _type-id_ rather than _id-expression_.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Unable to use enums in inspect expressions - no matching function for call to 'is'
2 participants