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

[abstracts] fix unify_with_variance for abstracts #9723

Merged

Conversation

vonagam
Copy link
Member

@vonagam vonagam commented Jul 17, 2020

Closes #9721.


and unify_type_params uctx a b tl1 tl2 =
let uctx = get_nested_context uctx in
Copy link
Member

Choose a reason for hiding this comment

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

Technically, this uctx should not go into type_eq, but I don't think it makes a difference in the current implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Question is why type_eq needs a unification context? I mean there is no need for it right now, but is there some idea for future property in the unification context that will be used both by unify and type_eq?

Copy link
Member

Choose a reason for hiding this comment

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

I was contemplating merging type_eq and unify, but I've moved away from that idea. It's hard to tell if a unification context could be useful for it, so I wouldn't mind removing it again for now.

List.iter2 unify_param tl1 tl2
| TAbstract(a1,tl1),TAbstract(a2,tl2) ->
let uctx = get_after_abstract_context() in
compare_underlying EqStrict (get_underlying_type t1) (get_underlying_type t2);
Copy link
Member

Choose a reason for hiding this comment

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

Looking at this I'm wondering why we do something with the underlying type here. I realize that the previous implementation already does this as well, but I can't immediately tell why.

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess there might be two reasons:

  1. For static targets physical difference matters and so if you have Array<Int> you can't simply cast it to Array<Float>, even though Int defines to Float. Though allowed cast from/to dynamic in abstract-t/t-abstract contradicts this (if i understand it right).

  2. If you allow an abstract over a child with defined cast to a parent to unify with a parent in a type param, this will permit unchecked covariance.

Copy link
Member

Choose a reason for hiding this comment

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

But Array<Int> to Array<Float> has nothing to do with underlying types. Array is a normal class.

Shouldn't all this be covered by the to and from declarations? I don't see why the relationship of underlying types is significant here.

Copy link
Member Author

Choose a reason for hiding this comment

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

About <Int> to <Float> - issue: #2844, commit: 6c22897. If underlying type checks are removed, only tests related to this fail.

But if we go only by tests, then underlying type checks can be skipped for t-abstract and abstract-t.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added commit with removal of underlying checks for abstract/non-abstract.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed that commit. Instead added assertions showcasing second reason (so first reason covers abstract-abstract, second one - abstract-t). Example here:

class Parent {}
class Child extends Parent {}
abstract MyChild(Child) to Parent {}

class Main {
  public static function main() {
    ((null: Array<Child>): Array<Parent>); // fails as expected
    ((null: Array<MyChild>): Array<Parent>); // works if no underlying type checks are made
  }
}

Copy link
Member

Choose a reason for hiding this comment

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

I think the correct behavior here is to require bidirectional unification for type-parameters. This will then work when the abstract has to Parent from Parent, in which case there's no variance issue.

And then if we ever allow variance annotations on type parameters, we would only have to check one of the directions instead of both.

Copy link
Member Author

Choose a reason for hiding this comment

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

What about <Array> to <ReadOnlyArray>?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure, that looks like it shouldn't work without to Array<T>, but at the same time I don't see what harm it could do.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think those breaking changes will require more discussion and input...
In the meantime, i would prefer to fix bugs in current behaviour (and express it in tests).
Are there some considerations that prevent this from being merged?
Performance or complexity of code?

@vonagam vonagam force-pushed the fix-with-variance-for-abstracts branch from aa90a10 to 69ac183 Compare July 17, 2020 16:32
@Simn Simn merged commit 847e808 into HaxeFoundation:development Jul 17, 2020
@skial skial mentioned this pull request Jul 21, 2020
1 task
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.

[abstracts] unification of nested abstracts in type parameters and structure fields
2 participants