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

Fix Variant to support structs with "length" field of inappropriate type #2062

Merged
merged 1 commit into from Jul 17, 2014
Merged

Conversation

yglukhov
Copy link
Contributor

@yglukhov yglukhov commented Apr 1, 2014

Fixed handling of a struct with no "length" field defined but with opDispatch of inappropriate type.

@yglukhov yglukhov changed the title Update variant.d Fix Variant to support structs with "length" field of inappropriate type Apr 3, 2014
// Make sure Variant can handle types with opDispatch but no length field.
struct S
{
void opDispatch(string s)() { }
Copy link
Contributor

Choose a reason for hiding this comment

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

This is intended to support opDispatch in place of a length field, so shouldn't the opDispatch method in question return whatever type Variant expects for a length? Shouldn't this struct still be rejected as invalid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, if opDispatch!("length") returns some T : size_t, it will be used.
But if not, the sample still should compile and throw VariantException in runtime, if length is being accessed. At least this is how i understand this.

Shouldn't this struct still be rejected as invalid?
I think that it should be compiled as valid, because Variant does not require types to declare "length" property. Moreover, "length" property may not be semantically applicable to any type, that implements opDispatch.

Maybe i should extend unittest with the case where length is handled correctly in opDispatch?

Copy link
Contributor

Choose a reason for hiding this comment

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

Extending the unittest would be a good idea.

@Kapps
Copy link
Contributor

Kapps commented Apr 4, 2014

Adding an assertThrown to check that a VariantException is thrown upon accessing Variant(S).length would be good to have. I'm not sure what Phobos convention is for the assert(__traits(compiles)) in the unittest, whether it's better to have it as a static assert or not (or if it even matters; seems to be done either way in different places).

Besides that, LGTM.

@Orvid
Copy link
Contributor

Orvid commented Apr 4, 2014

Provided that it's OK to have a type with only a void opDispatch accepted as a length property (in my view it shouldn't be), and that the unittest is extended to test both the void and proper return type, I'll concur with Kapps, it LGTM.

@Kapps
Copy link
Contributor

Kapps commented Apr 4, 2014

To clarify, the point of the pull is not to make opDispatch accepted for length. The problem was that previously if "T.length" compiled, it would use it and enable Variant.length with "return T.length". But with this void opDispatch, T.length would always compile and thus allow Variant.length, however return T.length would not as void can not be returned as ptrdiff_t. This pull makes it so it checks if T.length is both valid and returns something implicitly convertible to ptrdiff_t, and if it does not not, throws a runtime exception as expected. Previously using Variant(Foo) where Foo is a type that contains opDispatch or a length property not implicitly convertible to ptrdiff_t would result in compiler errors.

This is not a change in behaviour, it simply fixes a bug that caused compiler errors when using Variant with certain types.

@yglukhov
Copy link
Contributor Author

yglukhov commented Apr 4, 2014

Ok, the test is there. Kapps, thanx for interpreting =).

@monarchdodra
Copy link
Collaborator

Needs rebase.

@yglukhov
Copy link
Contributor Author

Done.

@monarchdodra
Copy link
Collaborator

According to documentation:

/** If the $(D_PARAM VariantN) contains an (associative) array,
  * returns the length of that array. Otherwise, throws an
  * exception.
  */

Wouldn't the correct fix "simply" be to always throw when A isn't an (associative) array? It seems that's how it's done for opIndex, for example.

Given the type SWithLength, i'd expect v.length to just throw. The correct call should be v.get!SWithLength.length.

That's how I understand the documentation anyways. Somebody else more savvy with variants?

@yglukhov
Copy link
Contributor Author

Ahh, that's a good point. Should we fix variant to support array-like structs/classes, changing opIndex, opIndexAssign, documentation, etc? Or should we just throw exception in case of not-an-array value in length(), as you suggest? Actually, such change may break the code, depending on this behavior, but in both cases the behavior would be more "right" than current.

@yglukhov
Copy link
Contributor Author

Ok, i've changed length() to support only (associative) arrays. This should be ok as a bug fix, while support for array-like structs/classes should probably be done as an extra feature.

@Kapps
Copy link
Contributor

Kapps commented Apr 17, 2014

I hadn't noticed that the documentation states that length (and similar operators) are not allowed if it's not an array. It is worth noting that the previous implementation did allow this, so this could technically break code, but since indexing and opApply were not allowed, I don't imagine much code relied on length being available (particularly with the documentation saying it's not allowed).

Regardless, the following works in the latest release and would not after this pull with the fix:

import std.stdio, std.variant;

struct FooContainer {
    @property size_t length() {
        return 3;
    }
}

void main() {
    Variant v = FooContainer();
    assert(v.length == 3);
}

I do find it a bit odd that opIndex/opApply/length/etc are defined to only work for arrays as opposed to any container that defines them though, but this isn't really the place for that.

@yglukhov
Copy link
Contributor Author

So.. anything else we could do here?

@Kapps
Copy link
Contributor

Kapps commented May 21, 2014

Any updates on this? The implementation looks fine, the only concern would be breakage, but all this does is makes the implementation conform to the documentation. The actual breakage should be negligible, if any, because it allowed length yet not opIndex/other operators, and the documentation says it's not allowed anyways.

While I believe that Variant should allow these operations on user-defined types as opposed to only built-in arrays, that's outside the scope of this pull. This pull makes things more consistent and in line with the documentation, along with the intended compile-time bug fix.

@yglukhov
Copy link
Contributor Author

@Kapps, are you at the DConf? Lets meet! =)

@Kapps
Copy link
Contributor

Kapps commented May 21, 2014

@yglukhov Unfortunately I wasn't able to make it out to DConf. I'm just watching the live-stream.

@yglukhov
Copy link
Contributor Author

Ah, thats pity.

@JakobOvrum
Copy link
Member

As std.variant is probably going to be replaced with a newer implementation at some point, I don't think we need to update all operations to handle UDTs, but this breakage seems gratituous.

I think a more reasonable fix would be:

static if (is(typeof(zis.length) : long))

@Kapps
Copy link
Contributor

Kapps commented May 25, 2014

Do you have any details about this replaced with a new implementation? I've never heard anything about this, and I'm not sure if I really see the benefit.

@yglukhov
Copy link
Contributor Author

yglukhov commented Jun 4, 2014

I've got no details, but i'd like to introduce std.variant replacement, after my optional monitors accepted. My replacement will be done in a more OOP way, ctfe friendly, and zero-size variants will occupy less space, than current ones. Because of monitors my current variant impl occupies more space than it should. It will take some time though to complete it, so i still suggest pulling this PR =).

@yglukhov
Copy link
Contributor Author

Juuust a little bump. I agree with @JakobOvrum that the first commit was better, checking length to be typeof(return). This would be a bit more forward compatible.

@Kapps
Copy link
Contributor

Kapps commented Jul 15, 2014

I prefer the first commit as well, but unless Variant is expanded to allow user-defined types for all array functions, it may as well be consistent and actually conform with the documentation. Regardless, either implementation is perfectly fine and I would be quite surprised if any code breaks with the second commit.

Regardless, it just needs a decision and to be pulled, it's rather silly for this pull to be open for 3 months when neither solution is controversial.

@Kapps
Copy link
Contributor

Kapps commented Jul 15, 2014

Seems to have merge conflicts.

@yglukhov
Copy link
Contributor Author

Rebased and squashed.

@mihails-strasuns
Copy link

I like the version that matches the documentation ;)

@mihails-strasuns
Copy link

Auto-merge toggled on

mihails-strasuns pushed a commit that referenced this pull request Jul 17, 2014
Fix Variant to support structs with "length" field of inappropriate type
@mihails-strasuns mihails-strasuns merged commit 7123819 into dlang:master Jul 17, 2014
@yglukhov yglukhov deleted the patch-1 branch July 20, 2014 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants