Skip to content

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

Closed

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Jul 13, 2023

Follow up of #18193 (comment)

@nicolasstucki nicolasstucki force-pushed the simplify-polyfunction-logic branch from c224a3b to 4b3d61b Compare July 14, 2023 06:58
@nicolasstucki nicolasstucki requested a review from smarter July 14, 2023 09:08
@nicolasstucki nicolasstucki marked this pull request as ready for review July 14, 2023 09:08
return isSubInfo(tp1.refinedInfo, tp2.refinedInfo)
case _ =>
tp1w.widenDealias match
case tp1: RefinedType =>
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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.

@nicolasstucki nicolasstucki assigned odersky and unassigned smarter Jul 14, 2023
@smarter
Copy link
Member

smarter commented Jul 14, 2023

I've pushed c304f1a which merges the two isSubInfo but that breaks 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.

/cc @Linyxus

@nicolasstucki nicolasstucki assigned smarter and Linyxus and unassigned odersky Jul 17, 2023
@smarter smarter removed their assignment Jul 17, 2023
@Linyxus Linyxus force-pushed the simplify-polyfunction-logic branch 2 times, most recently from 5efada6 to ea842be Compare July 18, 2023 19:51
nicolasstucki and others added 3 commits July 19, 2023 03:52
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
@Linyxus Linyxus force-pushed the simplify-polyfunction-logic branch from ea842be to dd21be8 Compare July 18, 2023 19:52
@Linyxus
Copy link
Contributor

Linyxus commented Jul 18, 2023

I just pushed a commit, which tweaks the merged isSubInfo to work for capture checking. However, the shortcut for function types in compareRefined is kept. I tried to remove it and traced the problem down; it turns out to be related to both the setup of CC and member lookup (tp.member(name)), and seems to be a rabbit hole.

else isSubType(info1, info2)
val isCCPhase = ctx.phase == Phases.checkCapturesPhase

(info1, info2) match
Copy link
Member

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]
Copy link
Member

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

Linyxus added 2 commits July 19, 2023 09:40
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`
@Linyxus Linyxus requested a review from odersky July 19, 2023 07:24
@Linyxus Linyxus assigned odersky and unassigned Linyxus Jul 20, 2023
Copy link
Contributor

@odersky odersky left a 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?

@odersky odersky removed their assignment Jul 23, 2023
@smarter
Copy link
Member

smarter commented Jul 24, 2023

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:

  1. The duplication was introduced in https://github.com/dotty-staging/dotty/blob/943d84aadfb3f4a07dfaec92b9b5696196977b24/compiler/src/dotty/tools/dotc/core/TypeComparer.scala#L616-L621, with a TODO mentioning an error with orphan parameters when merging the logic
  2. Extra cases were added in 3e690a8
  3. It turns out that I know how to solve the original TODO (using comparingTypeLambda), but merging the two isSubInfo isn't possible anymore because the cases introduced in 2. are accidentally relying on being only in one of the two isSubInfo, and no one understands why.

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.

@odersky
Copy link
Contributor

odersky commented Jul 25, 2023

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.

@smarter
Copy link
Member

smarter commented Jul 25, 2023

Given this, it seems at present counter-productive to merge the two cases.

OK, could the documentation of the cc isSubInfo be updated to remove the TODO mentioning merging them then?
https://github.com/lampepfl/dotty/blob/3e00a0d801ec0528992708dc210a0c102dd1eb8f/compiler/src/dotty/tools/dotc/core/TypeComparer.scala#L648
More documentation explaining why this is treated differently than in regular typechecking would also be helpful because I'm pretty lost at this point.

@odersky
Copy link
Contributor

odersky commented Jul 25, 2023

OK, I'll update the comment.

@odersky
Copy link
Contributor

odersky commented Jul 27, 2023

I'll roll the doc change into another PR.

@odersky odersky closed this Jul 27, 2023
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.

4 participants