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

Fixes and tweaks to implicit priority change scheme #21328

Closed
wants to merge 5 commits into from

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Aug 5, 2024

We now use a left-biased scheme, as follows.

From 3.7 on:

  • A given x: X is better than a given or implicit y: Y if y can be instantiated/widened to X.
  • An implicit x: X is better than a given or implicit y: Y if y can be instantiated to a supertype of X.
  • Use owner score for givens as a tie breaker if after all other tests we still have an ambiguity.

This is not transitive, but the PR implements a scheme to work around that.

Other change:

  • Drop special handling of NotGiven in prioritization. The previous logic pretended to do so, but was ineffective.

@odersky
Copy link
Contributor Author

odersky commented Aug 5, 2024

@WojciechMazur Can we get OpenCB results also for the two commits in this PR. Ideally first ffff15c and then 37dd612. My theory is that they would solve the uplicke problem and hopefully some other problems as well.

@WojciechMazur
Copy link
Contributor

Sure, I'll start the OpenCB

@WojciechMazur
Copy link
Contributor

WojciechMazur commented Aug 5, 2024

Another comparison based on 3.5.0-RC6 + this PR

New regressions:

Project Version Build URL Notes
neandertech/langoustine 0.0.22 Open CB logs missing implicit
softwaremill/tapir 1.11.0 Open CB logs crash
tpolecat/doobie 1.0.0-RC5 Open CB logs missing implicit
virtuslab/scala-cli 1.4.1 Open CB logs crash

Givens changes warnings:

Given alternatives choice: Some(the second alternative) - 503
Given alternatives choice: Some(none - it's ambiguous) - 753
Given alternatives choice: Some(the first alternative) - 397
Projects with changed alternatives warnings - 26

When compared with other alternatives this solution seems to keep the most ambitious implicits and overall seems to give the most similar when comparing stats with 3.5.0-RC6

@odersky
Copy link
Contributor Author

odersky commented Aug 5, 2024

The crash in Tapir seems to be unrelated to implicits, it's probably just because some different code is generated now.

Do you have the results from 3.5.0-RC6 for comparison?

@odersky
Copy link
Contributor Author

odersky commented Aug 5, 2024

When compared with #21325 (comment) this solution seems to keep the most ambitious implicits and overall seems to give the most similar when comparing stats with 3.5.0-RC6

Is upickle compiling now or still failing?

Most ambiguous is explainable since we now don't pick a winner in a given/implicit pair. These are treated as ambiguous, unless there is another overriding factor.

@odersky
Copy link
Contributor Author

odersky commented Aug 6, 2024

@Kordyjan This is the PR we will go with for 3.5. It implements the delay of the switchover that we discussed.

@odersky odersky added the backport:nominated If we agree to backport this PR, replace this tag with "backport:accepted", otherwise delete it. label Aug 6, 2024
@odersky odersky changed the title A left-biased variant for implicit/given pairs Fixes and tweaks to implicit priority change scheme Aug 6, 2024
// Intermediate rules: better means specialize, but map all type arguments downwards
// These are enabled for 3.0-3.5, and for all comparisons between old-style implicits,
// and in 3.5 and 3.6-migration when we compare with previous rules.
if scheme == CompareScheme.Intermediate || alt1IsImplicit then
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
if scheme == CompareScheme.Intermediate || alt1IsImplicit then
if scheme == CompareScheme.Intermediate then

Is it intentional that we still do the CompareScheme.Intermediate when specifically alt1 is an implicit ?

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! The idea is that for implicit/givens pairs we do the rules according to the left operand. Say an implicit I is more specialized than a given G. Then I wins by type against G but G also wins by type against I. So it's a draw in the end.
But in more complex situations it could be (for instance) that G doe not win against I because there are some variable's to instantiate on I but not on G. So I wins. (or G, vice versa). The PR comment mentions this.

compiler/src/dotty/tools/dotc/typer/Implicits.scala Outdated Show resolved Hide resolved
compiler/src/dotty/tools/dotc/typer/Implicits.scala Outdated Show resolved Hide resolved
@odersky
Copy link
Contributor Author

odersky commented Aug 6, 2024 via email

@EugeneFlesselle
Copy link
Contributor

I am testing a fix to disambiguate as we discussed. Let's try that and if that fails the CI we can fallback to this PR.

@odersky I initially implemented what we had proposed for disambiguate, i.e. if the alt1 is a failure, only return alt2 if it is strictly better than alt1 w.r.t compareAlternatives(disambiguate = true). This logic however should only apply if we are trying to recover from an ambiguous result, i.e. coming from healAmbigous (otherwise we do still want to keep alt2).
We can either thread another boolean parameter indicating whether or we are in that case, or move this logic into healAmbigous. I opted for the latter approach in #21339.

We now use a left-biased scheme, as follows.

From 3.6 on:

 - A given x: X is better than a given or implicit y: Y if y can be instantiated/widened to X.
 - An implicit x: X is better than a given or implicit y: Y if y can be instantiated to a supertype of X.
 - Use owner score for givens as a tie breaker if after all other tests we still have an ambiguity.

This is not transitive, so we need a separate scheme to work around that.

Other change:

 - Drop special handling of NotGiven in prioritization. The previous logic pretended to do so,
   but was ineffective.
We only have transitivity between givens or between implicits. To cope with that

 - We tank first all implicits, giving a best implicit search result.
 - Then we rank all givens startign with the implicit result. If there is
   a given that is better than the best implicit, the best given will be chosen.
   Otherwise we will stick with the best implicit.
Warnings from 3.6, change in 3.7. This is one version later than
originally planned.
Make the wording of a priority change warning message stable under different orders of eligibles.
We now always report the previously chosen alternative first and the new one second.

Note: We can still get ambiguities by fallging different pairs of alternatives depending on initial order.
case fail: SearchFailure =>
fail.reason match
case ambi: AmbiguousImplicits =>
if compareAlternatives(ambi.alt1, alt2, disambiguate = true) < 0
Copy link
Contributor

@EugeneFlesselle EugeneFlesselle Aug 6, 2024

Choose a reason for hiding this comment

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

I think this would be the expected behavior for disambiguate

  • when coming from healAmbiguous from a standard ambiguity case, that is, for the current search;
  • but not when coming from healAmbiguous from an ambiguity which happened in a nested search.

In the latter case, we would then be comparing alt2 to alternatives for an unrelated expected type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about we make use disambiguate = true for compareAlternatives when healing nested implicits?

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell, that would mean potentially faulty as in false positive (but never missing) warnings for a change in resolution in the nested search

Copy link
Contributor

@EugeneFlesselle EugeneFlesselle Aug 6, 2024

Choose a reason for hiding this comment

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

Perhaps we could do the check twice only in the 3.7-migration version, and keep the single check in 3.6 for better performance

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. In light of this, I think we should go with #49. Can you rebase it on the current state?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you rebase it on the current state?

Yes, will do that right now + plus first checking if we are in a migration version before the second comparison

Copy link
Contributor

Choose a reason for hiding this comment

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

I've pushed the changes (on #49) after the rebase, the optimization for versions staring from 3.7, and some documentation.

@odersky
Copy link
Contributor Author

odersky commented Aug 7, 2024

Superseded by #21339.

EugeneFlesselle added a commit that referenced this pull request Aug 7, 2024
Based on #21328: We now use a left-biased scheme, as follows.

From 3.7 on:

A given x: X is better than a given or implicit y: Y if y can be instantiated/widened to X.
An implicit x: X is better than a given or implicit y: Y if y can be instantiated to a supertype of X.
Use owner score for givens as a tie breaker if after all other tests we still have an ambiguity.
This is not transitive, but the PR implements a scheme to work around that.

Other changes:
- Drop special handling of NotGiven in prioritization. The previous logic pretended to do so, but was ineffective.
- Fix healAmbiguous to compareAlternatives with disambiguate = true, to account for changes made in #21045
@WojciechMazur WojciechMazur removed the backport:nominated If we agree to backport this PR, replace this tag with "backport:accepted", otherwise delete it. label Nov 8, 2024
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.

3 participants