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

Replace @SubCommands SumType!(TYPES...) with SubCommand!(TYPES...) #143

Merged
merged 15 commits into from
Jan 17, 2024

Conversation

andrey-zherikov
Copy link
Owner

Closes #80

Copy link

codecov bot commented Dec 18, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (3d1e8b2) 99.22% compared to head (e5bb105) 99.23%.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #143   +/-   ##
=======================================
  Coverage   99.22%   99.23%           
=======================================
  Files          27       28    +1     
  Lines        1943     1966   +23     
=======================================
+ Hits         1928     1951   +23     
  Misses         15       15           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Co-authored-by: Nickolay Bukreyev <[email protected]>
SirNickolas and others added 4 commits December 20, 2023 17:55
* Fix compatibility with D <2.102

* Fix compatibility with D <2.101

* Avoid shortened-methods syntax

This allows compiling without `-preview=shortenedMethods` on D <2.104.
D <2.096 did not support them at all.

* Fix compatibility with D <2.100

* Add an older DMD to the CI
Comment on lines 68 to 75
package(argparse) enum bool isSubCommand(T) = is(T : SubCommand!Args, Args...);


public template match(handlers...)
{
auto ref match(Args...)(auto ref SubCommand!Args sc)
{
alias RETURN_TYPE = typeof(SumType!(SubCommand!Args.Types).init.sumtype_match!handlers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
package(argparse) enum bool isSubCommand(T) = is(T : SubCommand!Args, Args...);
public template match(handlers...)
{
auto ref match(Args...)(auto ref SubCommand!Args sc)
{
alias RETURN_TYPE = typeof(SumType!(SubCommand!Args.Types).init.sumtype_match!handlers);
package(argparse) enum bool isSubCommand(T) = is(T : const SubCommand!Args, Args...);
public template match(handlers...)
{
auto ref match(Sub : const SubCommand!Args, Args...)(auto ref Sub sc)
{
alias RETURN_TYPE = typeof(SumType!(Sub.Types).init.sumtype_match!handlers);
immutable sub = SubCommand!(A, B)(A(1));
static assert(isSubCommand!(typeof(sub)));
assert(sub.match!(_ => _.i) == 1);

(Sorry for focusing on such tiny bits. Just can’t see anything more substantial.)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed match but compiler didn't complain about isSubCommand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. A reduced example:

struct A(T)
{
    T* p;
}

static if(is(immutable A!int : A!Args, Args...)) // Surprisingly, this is true.
{
    // Let's see what it deduced:
    static assert(Args.length == 1);
    static assert(is(Args[0] == int));
    // Let's check it again, just in case:
    static assert(!is(immutable A!int : A!Args)); // Now it's false!
}
else
    static assert(false);

Congratulations, I suppose we’ve discovered a bug in the compiler. I’ll need to mock this a bit more; will then report to D’s Bugzilla.

A unit test that passes now but IMO should fail with constless isSubCommand:

struct A
{
    int[] a;
}

static assert(isSubCommand!(immutable SubCommand!A));

I think it is sensible to put const in isSubCommand to be safe against future fixes in the compiler in case it is really a bug.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before submitting issue to compiler, I'd suggest checking SumType/isSumType

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly modified examples from dlang reference:
Fails for short

onlineapp.d(2): Error: static assert: `is(const(short) == short)` is false

Works for SumType:

assert(isSumType!(const SumType!int));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With indirections:

pragma(msg, is(const(int)* : int*));                           // => false
pragma(msg, is(const SumType!(int*) : SumType!Args, Args...)); // => true

Copy link
Owner Author

@andrey-zherikov andrey-zherikov Dec 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But:

struct A(T) {}
writeln(is(const(A!int) : A!T1,T1));    // => true
writeln(is(const(A!int) == A!T2,T2));   // => true
writeln(is(const(A!int) : A!int));      // => true
writeln(is(const(A!int) == A!int));     // => false

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, your last snippet shows it clearly. Intuitively, const(A!int) cannot be == A!T2 for any T2, but DMD insists it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asked on the forum about this, too.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh I don't expect any answers from forum

@SirNickolas
Copy link
Contributor

struct A
{
    int[] arr;
}

immutable one = SubCommand!A(A([1]));
SubCommand!A mut;
mut = one;
mut.match!(_ => _.arr[0] = 2);
assert(one.match!(_ => _.arr[0]) == 2); // Successfully modified an immutable object!

Still have to investigate if it’s a SubCommand’s fault or SumType’s.

@andrey-zherikov
Copy link
Owner Author

Still have to investigate if it’s a SubCommand’s fault or SumType’s

It seems also related to int[] array - this fails as expected:

struct A
{
    int arr;
}

immutable one = SubCommand!A(A(1));

SubCommand!A mut = one;
mut = one;
mut.match!(_ => _.arr = 2);

assert(one.match!(_ => _.arr) == 2);

@andrey-zherikov
Copy link
Owner Author

struct None {}

struct S(T)
{
    SumType!(None,T) a;
}

struct A
{
    int[] arr;
}

immutable one = S!A.init;
immutable two = SumType!(None,A)(A([1]));
immutable three = SumType!(A,None)(A([1]));
immutable four = SumType!(A)(A([1]));

one and two do compile and three and four do not (cannot implicitly convert expression)

@andrey-zherikov
Copy link
Owner Author

Asked question on forum.

# Conflicts:
#	source/argparse/internal/parser.d
@andrey-zherikov
Copy link
Owner Author

May be adding None to SumType was not good solution so I'd like to re-think my proposal a bit:

  • There are two use cases: with default subcommand and without.
  • Public API for both cases should be the same although internal implementation might not.
  • I think the following public API is useful:
    • Check whether any subcommand is chosen. This case always be true for a case with default subcommand.
    • Check whether specific subcommand is chosen.
    • Ctor and assignment operator that take subcommand object as parameter (without Default!).
  • In case of default subcommand, init value should be set to that default subcommand's init.

@andrey-zherikov
Copy link
Owner Author

Pushed changes and confirmed that this does not compile:

    struct A
    {
        int[] i;
    }

    immutable sub = SubCommand!(A)(A([1]));

Error:

Error: cannot implicitly convert expression SubCommand(Nullable(DontCallDestructorT(SumType(Storage(A(null)), cast(ubyte)0u)), true)).this(A([1])) of type SubCommand!(A) to immutable(SubCommand!(A))

@andrey-zherikov
Copy link
Owner Author

@SirNickolas Is this now good from your point of view?

@andrey-zherikov andrey-zherikov merged commit 6a13cd0 into master Jan 17, 2024
20 checks passed
@andrey-zherikov andrey-zherikov deleted the subcommand branch January 17, 2024 20:29
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.

README.md does not properly describe subcommands
2 participants