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

Change rules for given prioritization #19300

Merged
merged 8 commits into from
May 6, 2024

Commits on May 1, 2024

  1. Fix shapeless 3 deriving test.

    Typo: mkInstances instead of mkProductInstances, previously got healed
    by accident because if most specific rule.
    Change rules for given prioritization
    
    Consider the following program:
    
    ```scala
    class A
    class B extends A
    class C extends A
    
    given A = A()
    given B = B()
    given C = C()
    
    def f(using a: A, b: B, c: C) =
      println(a.getClass)
      println(b.getClass)
      println(c.getClass)
    
    @main def Test = f
    ```
    With the current rules, this would fail with an ambiguity error between B and C when
    trying to synthesize the A parameter. This is a problem without an easy remedy.
    
    We can fix this problem by flipping the priority for implicit arguments. Instead of
    requiring an argument to be most _specific_, we now require it to be most _general_
    while still conforming to the formal parameter.
    
    There are three justifications for this change, which at first glance seems quite drastic:
    
     - It gives us a natural way to deal with inheritance triangles like the one in the code above.
       Such triangles are quite common.
     - Intuitively, we want to get the closest possible match between required formal parameter type and
       synthetisized argument. The "most general" rule provides that.
     - We already do a crucial part of this. Namely, with current rules we interpolate all
       type variables in an implicit argument downwards, no matter what their variance is.
       This makes no sense in theory, but solves hairy problems with contravariant typeclasses
       like `Comparable`. Instead of this hack, we now do something more principled, by
       flipping the direction everywhere, preferring general over specific, instead of just
       flipping contravariant type parameters.
    
    Don't flip contravariant type arguments for overloading resolution
    
    Flipping contravariant type arguments was needed for implicit search
    where it will be replaced by a more general scheme. But it makes no
    sense for overloading resolution. For overloading resolution, we want
    to pick the most specific alternative, analogous to us picking the
    most specific instantiation when we force a fully defined type.
    Disable implicit search everywhere for disambiaguation
    
    Previously, one disambiguation step missed that, whereas implicits were
    turned off everywhere else.
    odersky committed May 1, 2024
    Configuration menu
    Copy the full SHA
    d4de2cb View commit details
    Browse the repository at this point in the history
  2. Change rules for given prioritization

    Consider the following program:
    
    ```scala
    class A
    class B extends A
    class C extends A
    
    given A = A()
    given B = B()
    given C = C()
    
    def f(using a: A, b: B, c: C) =
      println(a.getClass)
      println(b.getClass)
      println(c.getClass)
    
    @main def Test = f
    ```
    With the current rules, this would fail with an ambiguity error between B and C when
    trying to synthesize the A parameter. This is a problem without an easy remedy.
    
    We can fix this problem by flipping the priority for implicit arguments. Instead of
    requiring an argument to be most _specific_, we now require it to be most _general_
    while still conforming to the formal parameter.
    
    There are three justifications for this change, which at first glance seems quite drastic:
    
     - It gives us a natural way to deal with inheritance triangles like the one in the code above.
       Such triangles are quite common.
     - Intuitively, we want to get the closest possible match between required formal parameter type and
       synthetisized argument. The "most general" rule provides that.
     - We already do a crucial part of this. Namely, with current rules we interpolate all
       type variables in an implicit argument downwards, no matter what their variance is.
       This makes no sense in theory, but solves hairy problems with contravariant typeclasses
       like `Comparable`. Instead of this hack, we now do something more principled, by
       flipping the direction everywhere, preferring general over specific, instead of just
       flipping contravariant type parameters.
    
    The behavior is dependent on the Scala version
    
     - Old behavior: up to 3.4
     - New behavior: from 3.5, 3.5-migration warns on behavior change
    
    The CB builds under the new rules. One fix was needed for a shapeless 3 deriving test.
    There was a typo: mkInstances instead of mkProductInstances, which previously got healed
    by accident because of the most specific rule.
    
    Also: Don't flip contravariant type arguments for overloading resolution
    
    Flipping contravariant type arguments was needed for implicit search
    where it will be replaced by a more general scheme. But it makes no
    sense for overloading resolution. For overloading resolution, we want
    to pick the most specific alternative, analogous to us picking the
    most specific instantiation when we force a fully defined type.
    
    Also: Disable implicit search everywhere for disambiaguation
    
    Previously, one disambiguation step missed that, whereas implicits were
    turned off everywhere else.
    odersky committed May 1, 2024
    Configuration menu
    Copy the full SHA
    1b9a7e0 View commit details
    Browse the repository at this point in the history
  3. Fix rebase breakage

    odersky committed May 1, 2024
    Configuration menu
    Copy the full SHA
    7721833 View commit details
    Browse the repository at this point in the history
  4. Switch to new rules only if both sides are givens

    (rather than implicits).
    odersky committed May 1, 2024
    Configuration menu
    Copy the full SHA
    c54dbbf View commit details
    Browse the repository at this point in the history

Commits on May 5, 2024

  1. Refine prioritization rules between givens and implicits

    In the new system, givens always beat implicits when comparing value types. This
    is necessary to maintain two invariants in the new system:
    
     - When comparing old-style implicits nothing changes, we still use the old rules.
     - The isAsGood comparison is transitive.
    
    Exception: NotGiven has to be treated at lower priority.
    odersky committed May 5, 2024
    Configuration menu
    Copy the full SHA
    b644d30 View commit details
    Browse the repository at this point in the history
  2. Delay roll-out of new prioritization scheme:

    Now:
    
      3.5: old scheme but warn if there are changes in the future
      3.6-migration: new scheme, warn if prioritization has changed
      3.6: new scheme, no warning
    odersky committed May 5, 2024
    Configuration menu
    Copy the full SHA
    bc26c51 View commit details
    Browse the repository at this point in the history
  3. Test extract from reactive-mongo

    This will now be ambiguous.
    odersky committed May 5, 2024
    Configuration menu
    Copy the full SHA
    33f801b View commit details
    Browse the repository at this point in the history

Commits on May 6, 2024

  1. Avoid ambiguity errors arising from double references in implicits

    If two implicit candidate references happen to be the same pick one of them instead
    of reporting an ambiguity.
    odersky committed May 6, 2024
    Configuration menu
    Copy the full SHA
    8a3854f View commit details
    Browse the repository at this point in the history