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

[SUPERSEDED] Inherited members can induce ambiguity #10177

Closed
wants to merge 1 commit into from

Conversation

som-snytt
Copy link
Contributor

@som-snytt som-snytt commented Oct 4, 2022

The language specification is clarified so that an inherited class member cannot shadow a local definition.

The following reference m is ambiguous:

class C { def m = ??? }
def f(m: Int) = new C {
  def g = m   // use this.m
}

The previous behavior is available under -Ylegacy-binding.

Fixes scala/bug#11921

@scala-jenkins scala-jenkins added this to the 2.13.11 milestone Oct 4, 2022
@som-snytt som-snytt changed the title Inherited members can induce ambiguity [ci:last-only] Inherited members can induce ambiguity [ci: last-only] Oct 4, 2022
@som-snytt
Copy link
Contributor Author

I forgot whether ci: requires a space or no space.

@som-snytt som-snytt force-pushed the issue/11921 branch 2 times, most recently from 8e21785 to 9913f83 Compare October 4, 2022 18:02
@som-snytt som-snytt marked this pull request as ready for review October 4, 2022 19:19
@som-snytt
Copy link
Contributor Author

som-snytt commented Oct 8, 2022

We don't have a convention for the spec change, which applies to Scala 3 source.

It would still be nice to have a page listing what behaviors are controlled by -Xsource (or the implicit search flag); most are restrictions or minor syntax? and therefore not spec-worthy.

@som-snytt som-snytt added the release-notes worth highlighting in next release notes label Oct 19, 2022
@som-snytt
Copy link
Contributor Author

This is an edge case where -Xsource:3 is actually useful.

@som-snytt som-snytt requested a review from lrytz November 3, 2022 21:25
Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

LGTM, thank you! Just the spec change should be revised.

spec/02-identifiers-names-and-scopes.md Outdated Show resolved Hide resolved
@som-snytt
Copy link
Contributor Author

I pushed -Ylegacy-binding, so that users get the improved behavior for free, but can opt-out if their legacy code base is littered with problematic inheritance.

@som-snytt
Copy link
Contributor Author

som-snytt commented Nov 4, 2022

I was wondering if it would pass bootstrap on the first try. I didn't expect more sbt fragility:

/tmp/sbt_3faddf00/xsbt/GlobalHelpers.scala:19: error: reference to global is ambiguous;
it is both defined in trait GlobalHelpers and available as value global in class Compat
  import global._
         ^

This is no doubt because the sbt code is never seen, but slinks by in a dark alley, brokering its shady deals.

Actually, this is just an ordinary member, but it looks like it is fooled by the self type! I'll add a test.

@som-snytt
Copy link
Contributor Author

som-snytt commented Nov 5, 2022

The update approximates WWDD at scala/scala3#8622

Follow-up for bootstrapping to follow.

Some edits are not entirely boring:

[error] .../scala/src/compiler/scala/reflect/quasiquotes/Parsers.scala:95:31: reference to global is ambiguous;
[error] it is both defined in class Parser and available as value global in class ParserTreeBuilder
[error]           case other       => global.abort("unreachable")
[error]                               ^

where the local code odor is

import treeBuilder.{global => _, unit => _}

Another good one, which tree is intended? There is no guarantee that the tree in the completer is the tree we passed in. It would be nice if there were syntax for that, new C(tree' = tree).

    protected def finishSymbolMaybeExpandee(tree: Tree, annZippers: List[treeInfo.AnnotationZipper]): Unit = {
      val sym = tree.symbol
      unmarkWeak(sym)
      markMaybeExpandee(sym)
      sym.setInfo(new MaybeExpandeeCompleter(tree) {
        override def kind = s"maybeExpandeeCompleter for ${sym.accurateKindString} ${sym.rawname}#${sym.id}"
        override def maybeExpand(): Unit = {
          val companion = if (tree.isInstanceOf[ClassDef]) patchedCompanionSymbolOf(sym, context) else NoSymbol

An actual bug in a test:

  class OnceOnly extends IterableOnce[Int] {
    override def knownSize: Int = 1

    var iterated:Boolean = false

    override def iterator: Iterator[Int] = {
      new Iterator[Int] {
        assertFalse("Attempt to re-iterate!", iterated)
        iterated = true
        private var v = Option(42)
        override def hasNext: Boolean = v.nonEmpty && knownSize > 0
        override def next(): Int = {
          val res = v.get
          v = None
          res
        }
      }
    }
  }

  @Test
  def addAllTest2(): Unit = {
    val hs = HashSet.empty[Int]
    hs.addAll(new OnceOnly)
  }

That's my fault for not reviewing the contribution (which I suggested). It intends to enforce that the collection is not iterated twice. It's not even iterated once, because knownSize is the inherited -1, not the positive value in scope. The test should check that something was added.

@som-snytt
Copy link
Contributor Author

You don't often get to edit pos/t0165.scala. Probably any edit nullifies the early test of a crasher. The charming package name is test3.

    object result extends LinkedHashMap[String,String] with ArrayResult {
      def current = result
    }

@som-snytt
Copy link
Contributor Author

@lrytz This is why I was looking at this code recently. Could you comment on the idea that this was a spec bug and that fixing it finds a few code bugs without breaking legitimate code? Probably some Community Build experience will say more.

-Ylegacy-binding, while technically a forking option, follows in the path forged by other transition options such as -Yeta-expand-keeps-star and -Yinfer-by-name.

Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

This is not going to be a trivial change to push through a minor release, but because it's a clear improvement and it is likely to flag actual bugs, I think we should give it a try.

People will get compilation errors when upgrading Scala on code that previously compiled, so let's add a very specific message to the error. Something like this:

Since 2.13.11, symbols inherited by a superclass no longer take precedence over symbols defined in an outer scope.
Typically this is fixed by writing `this.coll`. Use `-Ylegacy-binding` to revert to the previous behavior.

src/compiler/scala/tools/nsc/typechecker/Contexts.scala Outdated Show resolved Hide resolved
@som-snytt
Copy link
Contributor Author

That's a good idea, to mitigate the migration effort with an explanatory message.

I also would not be opposed to releasing 2.14 next 14.2 with default -Xsource:3 (!).

@lrytz
Copy link
Member

lrytz commented Nov 14, 2022

Testing this change on a codebase that might or might not have similarities to https://github.com/morganstanley/optimus-cirrus. It would really help if the error could be skipped in case the compiler knows the two symbols are aliases.

Type example:

class C[T] { type TT = T }
object O { type TT = String
  class D extends C[TT] {
    def n(x: TT) = x  // now ambiguous
    def m(x: O.TT): this.TT = x  // but they are aliases
  }
}

Term example:

trait Context
class A[C <: Context](val c: C)
class B(val c: Context) { b =>
  val a = new A[c.type](c) {
    def m = c  // now ambiguous
    def t: this.c.type = b.c // but they are aliases
  }
}

What do you think?

@som-snytt
Copy link
Contributor Author

That is a design goal, which I assume Dotty achieves.

@lrytz
Copy link
Member

lrytz commented Nov 14, 2022

Scala 3.2.1 issues an ambiguity error for both of these examples. But if it's actually correct it would be helpful, and could be forward ported. The disambiguation could be extended to other cases, not only the new one added here.

@som-snytt
Copy link
Contributor Author

Maybe take the last thing I said and assume the opposite. I remember seeing the dealiasing in dotty and made a mental note but did not follow up. I'll give it more thought or ponderance (made-up word).

@som-snytt
Copy link
Contributor Author

som-snytt commented Nov 14, 2022

Oh actually, just back from cooking lunch, I did test this or similar case, and was relieved that dotty didn't require me to notice the aliasing.

Edit: or I'm thinking of pos/t11921c.scala where package-level is an escape hatch.

@som-snytt som-snytt changed the title Inherited members can induce ambiguity [ci: last-only] Inherited members can induce ambiguity Nov 14, 2022
@som-snytt som-snytt force-pushed the issue/11921 branch 2 times, most recently from 13ae402 to f0be08a Compare November 15, 2022 00:04
@@ -267,6 +267,8 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")

val legacyBinding = BooleanSetting("-Ylegacy-binding", "Observe legacy name binding preference for inherited member competing with local definition.")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

"local"!

Spec binding precedence of inherited definition.
Use -Ylegacy-binding for old precedence.
@som-snytt
Copy link
Contributor Author

Tweaked the code comment and rebased.

@lrytz merge for parity with dotty? Then your de-aliasing improvement with de-this follows hard upon.

@lrytz
Copy link
Member

lrytz commented Dec 12, 2022

@lrytz merge for parity with dotty? Then your de-aliasing improvement with de-this follows hard upon.

Let's wait and see, instead of merging a new error and later narrowing it down. I'm also conidering whether it should maybe be a warning (on by default), an error only under -Xsource:3.

@som-snytt
Copy link
Contributor Author

subsumed and superseded by #10220

@som-snytt som-snytt closed this Dec 16, 2022
@SethTisue SethTisue removed this from the 2.13.11 milestone Dec 16, 2022
@som-snytt som-snytt removed the release-notes worth highlighting in next release notes label Feb 11, 2023
@som-snytt som-snytt changed the title Inherited members can induce ambiguity [SUPERSEDED] Inherited members can induce ambiguity Feb 11, 2023
@som-snytt som-snytt deleted the issue/11921 branch March 29, 2023 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants