-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Simplify polyfunction logic #18200
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
Simplify polyfunction logic #18200
Conversation
c224a3b
to
4b3d61b
Compare
return isSubInfo(tp1.refinedInfo, tp2.refinedInfo) | ||
case _ => | ||
tp1w.widenDealias match | ||
case tp1: RefinedType => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the refinement is for another member than apply
? I don't really understand either the previous or current logic, it seems we should be checking for apply
in both tp1
and tp2
, but then why do we even need special logic for functions instead of the normal logic for refinements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was added in https://github.com/lampepfl/dotty/pull/15877/files#diff-c163f9660fb06bb487b0120921d06353fc52154ae1b3066f58da1c73ecd00914R613-R641
It has the following comment
// A relaxed version of subtyping for dependent functions where method types
// are treated as contravariant.
// TODO: Merge with isSubInfo in hasMatchingMember. Currently, we can't since
// the isSubinfo of hasMatchingMember has problems dealing with PolyTypes
// (---> orphan params during pickling)
Removing this completely makes some cc tests fail (such as tests/neg-custom-args/captures/heal-tparam-cs.scala).
@odersky do you have more insight on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a orphan param thing can be fixed by using comparingTypeLambdas
when recursing in the PolyTypes, I'll update the PR.
I've pushed c304f1a which merges the two
/cc @Linyxus |
5efada6
to
ea842be
Compare
The isSubInfo for capture-checking introduced in scala#15877 had the following TODO: // A relaxed version of subtyping for dependent functions where method types // are treated as contravariant. // TODO: Merge with isSubInfo in hasMatchingMember. Currently, we can't since // the isSubinfo of hasMatchingMember has problems dealing with PolyTypes // (---> orphan params during pickling) The orphan params error was due to the recursion on the result of the PolyTypes being done without first calling `compareTypeLambda`. After fixing this we can safely merge the two versions while keeping the new behavior for dependent and polymorphic function types hidden under the `captureChecking` feature since they're language changes. I'll open a separate PR to create a `relaxedSubtyping` feature for these improvements since their usefulness is independent of capture checking. The isSubInfo for capture-checking got two additional cases involving CapturingTypes in 3e690a8, I don't know what they're supposed to do, but moving those to the regular isSubInfo breaks various capture-checking tests: -- [E007] Type Mismatch Error: tests/pos-custom-args/captures/classes.scala:11:32 -------------------------------------- 11 | val c1: C{val n: B^{x}}^{x} = c0 | ^^ | Found: (c0 : C{val n: B^}^{x}) | Required: C{val n: B^{x}}^{x} So this commit breaks capture-checking and I don't know enough about what this code is supposed to do to fix it.
- Keep the shortcut for function types in compareRefined - Temper isSubInfo logic
ea842be
to
dd21be8
Compare
I just pushed a commit, which tweaks the merged |
else isSubType(info1, info2) | ||
val isCCPhase = ctx.phase == Phases.checkCapturesPhase | ||
|
||
(info1, info2) match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous version used two pattern matches on info1 and info2 instead of a pattern match on a tuple for performance, I suggest keeping the two pattern matches since this code path could be hot.
&& isSubInfo(parent1, info2, symInfo1) | ||
case _ => fallback | ||
def fallback = (info1: Type, info2: Type) => | ||
def tp1IsSingleton: Boolean = tp1.isInstanceOf[SingletonType] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a def here instead of a val doesn't seem useful since it's evaluated in the next line
instead of matching a tuple. Suggested by @smarter.
- Document the shortcut in compareRefined for function types under CC phase - Change a `def` to a `val`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this closely, I doubt this is an improvement.
Positive: We have merged to subInfo definitions into one.
Negative: The new subInfo definition is a lot more complicated and needs to be parameterized with two auxiliary functions.
I think at least for me the net result is a loss in legibility. And despite the attempt to remove duplication, the code is now longer than before.
Is there a strong reason we need to do this refactoring?
The problem is that the current capture checking logic is relying on hacks, the refactoring at least makes this clearer. To recap:
I think the longer we keep the two isSubInfo, the more problems like this we're likely to introduce, and the harder compiler maintenance becomes in general (in particular, I was trying to experiment with changes to isSubInfo, but having to maintain and tests two versions in parallel makes that a lot more tedious that it needs to be). I agree that as-is the PR isn't great because of the lingering FIXME, but I don't know enough about the capture checking logic to fix that myself right now. |
It's actually not a hack. CC checking converts all function types to dependent function types but then intentionally uses only the refinement in a function type for comparisons and ignores the parent. In a sense it moves already what towards what we think a principled handling of function types should do. Given this, it seems at present counter-productive to merge the two cases. It's better to isolate the capture checking case in its own corner. |
OK, could the documentation of the cc isSubInfo be updated to remove the TODO mentioning merging them then? |
OK, I'll update the comment. |
I'll roll the doc change into another PR. |
Follow up of #18193 (comment)