Skip to content

Handle super accessors in initialization checker #15703

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

Merged
merged 3 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 6 additions & 17 deletions compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Symbols.*
import Types.*
import StdNames.*
import NameKinds.OuterSelectName
import NameKinds.SuperAccessorName

import ast.tpd.*
import config.Printers.init as printer
Expand Down Expand Up @@ -788,7 +789,9 @@ object Semantic:
if !needResolve then
meth
else if superType.exists then
resolveSuper(ref.klass, superType, meth)
meth
else if meth.name.is(SuperAccessorName) then
ResolveSuper.rebindSuper(ref.klass, meth)
else
resolve(ref.klass, meth)

Expand Down Expand Up @@ -829,7 +832,7 @@ object Semantic:
value.select(target, receiver, needResolve = false)
else
if ref.klass.isSubClass(receiver.widenSingleton.classSymbol) then
report.error("Unexpected resolution failure: ref.klass = " + ref.klass.show + ", meth = " + meth.show + Trace.show, Trace.position)
report.error("[Internal error] Unexpected resolution failure: ref.klass = " + ref.klass.show + ", meth = " + meth.show + Trace.show, Trace.position)
Hot
else
// This is possible due to incorrect type cast.
Expand Down Expand Up @@ -1175,9 +1178,8 @@ object Semantic:
// Note that a parameterized trait may only get parameters from the class that extends the trait.
// A trait may not supply constructor arguments to another trait.
if !klass.is(Flags.Trait) then
for parent <- klass.parentSyms if parent.hasSource do doPromote(parent.asClass, klass, isHotSegment)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it OK to remove this? We don't need to promote super-trait segments? Is it because it is duplicated by the promotion of mixins below?

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, with the changes we avoid some duplicate check (with cache they are not expensive though):

trait A

class B extends A

class C extends B with A

// We still need to handle indirectly extended traits via traits, which are not in the parent list.
val superCls = klass.superClass
if superCls.hasSource then doPromote(superCls.asClass, klass, isHotSegment)
val mixins = klass.baseClasses.tail.takeWhile(_ != superCls)
for mixin <- mixins if mixin.hasSource do doPromote(mixin.asClass, klass, isHotSegment)
end doPromote
Expand Down Expand Up @@ -1768,16 +1770,3 @@ object Semantic:
if (sym.isEffectivelyFinal || sym.isConstructor) sym
else sym.matchingMember(cls.appliedRef)
}

def resolveSuper(cls: ClassSymbol, superType: Type, sym: Symbol)(using Context): Symbol =
import annotation.tailrec
@tailrec def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
case bc :: bcs1 =>
val cand = sym.matchingDecl(bcs.head, cls.thisType)
.suchThat(alt => !alt.is(Flags.Deferred)).symbol
if (cand.exists) cand else loop(bcs.tail)
case _ =>
NoSymbol
}
loop(cls.info.baseClasses.dropWhile(sym.owner != _))

36 changes: 36 additions & 0 deletions tests/init/neg/super-resolution.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- Error: tests/init/neg/super-resolution.scala:21:6 -------------------------------------------------------------------
21 | val m = 30 // error
| ^
| Access non-initialized value m. Calling trace:
| -> class C extends A with M with N: [ super-resolution.scala:17 ]
| ^
| -> foo() [ super-resolution.scala:18 ]
| ^^^^^
| -> override def foo(): Int = b * super.foo() [ super-resolution.scala:15 ]
| ^^^^^^^^^^^
| -> override def foo(): Int = a + super.foo() [ super-resolution.scala:11 ]
| ^^^^^^^^^^^
| -> def foo(): Int = m [ super-resolution.scala:7 ]
| ^
-- Error: tests/init/neg/super-resolution.scala:19:6 -------------------------------------------------------------------
19 | val a = 10 // error
| ^
| Access non-initialized value a. Calling trace:
| -> class C extends A with M with N: [ super-resolution.scala:17 ]
| ^
| -> foo() [ super-resolution.scala:18 ]
| ^^^^^
| -> override def foo(): Int = b * super.foo() [ super-resolution.scala:15 ]
| ^^^^^^^^^^^
| -> override def foo(): Int = a + super.foo() [ super-resolution.scala:11 ]
| ^
-- Error: tests/init/neg/super-resolution.scala:20:6 -------------------------------------------------------------------
20 | val b = 20 // error
| ^
| Access non-initialized value b. Calling trace:
| -> class C extends A with M with N: [ super-resolution.scala:17 ]
| ^
| -> foo() [ super-resolution.scala:18 ]
| ^^^^^
| -> override def foo(): Int = b * super.foo() [ super-resolution.scala:15 ]
| ^
23 changes: 23 additions & 0 deletions tests/init/neg/super-resolution.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
abstract class A:
val n: Int
def foo(): Int = n

trait B:
val m: Int
def foo(): Int = m

trait M extends A with B:
val a: Int
override def foo(): Int = a + super.foo()

trait N extends A with B:
val b: Int
override def foo(): Int = b * super.foo()

class C extends A with M with N:
foo()
val a = 10 // error
val b = 20 // error
val m = 30 // error
val n = 40

28 changes: 28 additions & 0 deletions tests/init/neg/super-resolution2.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Error: tests/init/neg/super-resolution2.scala:19:6 ------------------------------------------------------------------
19 | val n = 40 // error
| ^
| Access non-initialized value n. Calling trace:
| -> class N extends A with B: [ super-resolution2.scala:9 ]
| ^
| -> new Inner [ super-resolution2.scala:16 ]
| ^^^^^^^^^
| -> class Inner: [ super-resolution2.scala:12 ]
| ^
| -> N.super[A].foo() [ super-resolution2.scala:13 ]
| ^^^^^^^^^^^^^^^^
| -> def foo(): Int = n [ super-resolution2.scala:3 ]
| ^
-- Error: tests/init/neg/super-resolution2.scala:18:6 ------------------------------------------------------------------
18 | val m = 30 // error
| ^
| Access non-initialized value m. Calling trace:
| -> class N extends A with B: [ super-resolution2.scala:9 ]
| ^
| -> new Inner [ super-resolution2.scala:16 ]
| ^^^^^^^^^
| -> class Inner: [ super-resolution2.scala:12 ]
| ^
| -> N.super.foo() [ super-resolution2.scala:14 ]
| ^^^^^^^^^^^^^
| -> def foo(): Int = m [ super-resolution2.scala:7 ]
| ^
20 changes: 20 additions & 0 deletions tests/init/neg/super-resolution2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
abstract class A:
val n: Int
def foo(): Int = n

trait B:
val m: Int
def foo(): Int = m

class N extends A with B:
override def foo(): Int = a * super.foo()

class Inner:
N.super[A].foo()
N.super.foo()

new Inner

val m = 30 // error
val n = 40 // error
val a = 50