Skip to content

Commit 8be99ff

Browse files
committed
Fix crash in member selection from type cast
1 parent 4e20675 commit 8be99ff

File tree

3 files changed

+90
-53
lines changed

3 files changed

+90
-53
lines changed

compiler/src/dotty/tools/dotc/transform/init/Errors.scala

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,55 +18,57 @@ object Errors:
1818

1919
def pos(using Context): SourcePosition = trace.last.sourcePos
2020

21+
def stacktrace(preamble: String = " Calling trace:\n")(using Context): String = buildStacktrace(trace, preamble)
22+
2123
def issue(using Context): Unit =
2224
report.warning(show, this.pos)
25+
end Error
26+
27+
def buildStacktrace(trace: Seq[Tree], preamble: String)(using Context): String = if trace.isEmpty then "" else preamble + {
28+
var lastLineNum = -1
29+
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
30+
trace.foreach { tree =>
31+
val pos = tree.sourcePos
32+
val prefix = "-> "
33+
val line =
34+
if pos.source.exists then
35+
val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
36+
val code = SyntaxHighlighting.highlight(pos.lineContent.trim.nn)
37+
i"$code\t$loc"
38+
else
39+
tree.show
40+
val positionMarkerLine =
41+
if pos.exists && pos.source.exists then
42+
positionMarker(pos)
43+
else ""
2344

24-
def stacktrace(preamble: String = " Calling trace:\n")(using Context): String = if trace.isEmpty then "" else preamble + {
25-
var lastLineNum = -1
26-
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
27-
trace.foreach { tree =>
28-
val pos = tree.sourcePos
29-
val prefix = "-> "
30-
val line =
31-
if pos.source.exists then
32-
val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
33-
val code = SyntaxHighlighting.highlight(pos.lineContent.trim.nn)
34-
i"$code\t$loc"
35-
else
36-
tree.show
37-
val positionMarkerLine =
38-
if pos.exists && pos.source.exists then
39-
positionMarker(pos)
40-
else ""
41-
42-
// always use the more precise trace location
43-
if lastLineNum == pos.line then
44-
lines.dropRightInPlace(1)
45-
46-
lines += (prefix + line + "\n" + positionMarkerLine)
47-
48-
lastLineNum = pos.line
49-
}
50-
val sb = new StringBuilder
51-
for line <- lines do sb.append(line)
52-
sb.toString
45+
// always use the more precise trace location
46+
if lastLineNum == pos.line then
47+
lines.dropRightInPlace(1)
48+
49+
lines += (prefix + line + "\n" + positionMarkerLine)
50+
51+
lastLineNum = pos.line
5352
}
53+
val sb = new StringBuilder
54+
for line <- lines do sb.append(line)
55+
sb.toString
56+
}
5457

55-
/** Used to underline source positions in the stack trace
56-
* pos.source must exist
57-
*/
58-
private def positionMarker(pos: SourcePosition): String =
59-
val trimmed = pos.lineContent.takeWhile(c => c.isWhitespace).length
60-
val padding = pos.startColumnPadding.substring(trimmed).nn + " "
61-
val carets =
62-
if (pos.startLine == pos.endLine)
63-
"^" * math.max(1, pos.endColumn - pos.startColumn)
64-
else "^"
58+
/** Used to underline source positions in the stack trace
59+
* pos.source must exist
60+
*/
61+
private def positionMarker(pos: SourcePosition): String =
62+
val trimmed = pos.lineContent.takeWhile(c => c.isWhitespace).length
63+
val padding = pos.startColumnPadding.substring(trimmed).nn + " "
64+
val carets =
65+
if (pos.startLine == pos.endLine)
66+
"^" * math.max(1, pos.endColumn - pos.startColumn)
67+
else "^"
6568

66-
s"$padding$carets\n"
69+
s"$padding$carets\n"
6770

68-
override def toString() = this.getClass.getName.nn
69-
end Error
71+
override def toString() = this.getClass.getName.nn
7072

7173
/** Access non-initialized field */
7274
case class AccessNonInit(field: Symbol, trace: Seq[Tree]) extends Error:

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ object Semantic:
652652
value.promote(msg)
653653
value
654654

655-
def select(field: Symbol, needResolve: Boolean = true): Contextual[Value] = log("select " + field.show + ", this = " + value, printer, (_: Value).show) {
655+
def select(field: Symbol, receiver: Type, needResolve: Boolean = true): Contextual[Value] = log("select " + field.show + ", this = " + value, printer, (_: Value).show) {
656656
if promoted.isCurrentObjectPromoted then Hot
657657
else value match {
658658
case Hot =>
@@ -668,13 +668,11 @@ object Semantic:
668668
if target.is(Flags.Lazy) then
669669
val rhs = target.defTree.asInstanceOf[ValDef].rhs
670670
eval(rhs, ref, target.owner.asClass, cacheResult = true)
671-
else
671+
else if target.exists then
672672
val obj = ref.objekt
673673
if obj.hasField(target) then
674674
obj.field(target)
675675
else if ref.isInstanceOf[Warm] then
676-
if !target.exists then
677-
println("obj.klass = " + obj.klass.show + ", field = " + field.show)
678676
assert(obj.klass.isSubClass(target.owner))
679677
if target.is(Flags.ParamAccessor) then
680678
// possible for trait parameters
@@ -693,13 +691,21 @@ object Semantic:
693691
val error = AccessNonInit(target, trace.toVector)
694692
reporter.report(error)
695693
Hot
694+
else
695+
if ref.klass.isSubClass(receiver.widenSingleton.classSymbol) then
696+
report.error("Unexpected resolution failure: ref.klass = " + ref.klass.show + ", field = " + field.show + buildStacktrace(trace.toVector, "\n"))
697+
Hot
698+
else
699+
// This is possible due to incorrect type cast.
700+
// See tests/init/pos/Type.scala
701+
Hot
696702

697703
case fun: Fun =>
698704
report.error("[Internal error] unexpected tree in selecting a function, fun = " + fun.expr.show, fun.expr)
699705
Hot
700706

701707
case RefSet(refs) =>
702-
refs.map(_.select(field)).join
708+
refs.map(_.select(field, receiver)).join
703709
}
704710
}
705711

@@ -811,13 +817,21 @@ object Semantic:
811817
val error = CallUnknown(target, trace.toVector)
812818
reporter.report(error)
813819
Hot
814-
else
820+
else if target.exists then
815821
// method call resolves to a field
816822
val obj = ref.objekt
817823
if obj.hasField(target) then
818824
obj.field(target)
819825
else
820-
value.select(target, needResolve = false)
826+
value.select(target, receiver, needResolve = false)
827+
else
828+
if ref.klass.isSubClass(receiver.widenSingleton.classSymbol) then
829+
report.error("Unexpected resolution failure: ref.klass = " + ref.klass.show + ", meth = " + meth.show + buildStacktrace(trace.toVector, "\n"))
830+
Hot
831+
else
832+
// This is possible due to incorrect type cast.
833+
// See tests/init/pos/Type.scala
834+
Hot
821835

822836
case Fun(body, thisV, klass) =>
823837
// meth == NoSymbol for poly functions
@@ -1126,12 +1140,12 @@ object Semantic:
11261140
if member.is(Flags.Method, butNot = Flags.Accessor) then
11271141
withTrace(Trace.empty) {
11281142
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty))
1129-
val res = warm.call(member, args, receiver = NoType, superType = NoType)
1143+
val res = warm.call(member, args, receiver = warm.klass.typeRef, superType = NoType)
11301144
res.promote("Cannot prove that the return value of " + member.show + " is hot. Found = " + res.show + ". ")
11311145
}
11321146
else
11331147
withTrace(Trace.empty) {
1134-
val res = warm.select(member)
1148+
val res = warm.select(member, receiver = warm.klass.typeRef)
11351149
res.promote("Cannot prove that the field " + member.show + " is hot. Found = " + res.show + ". ")
11361150
}
11371151
end for
@@ -1346,7 +1360,7 @@ object Semantic:
13461360
resolveThis(target, qual, current.asClass)
13471361
}
13481362
case _ =>
1349-
withTrace(trace2) { qual.select(expr.symbol) }
1363+
withTrace(trace2) { qual.select(expr.symbol, receiver = qualifier.tpe) }
13501364

13511365
case _: This =>
13521366
cases(expr.tpe, thisV, klass)
@@ -1468,7 +1482,7 @@ object Semantic:
14681482
thisV.accessLocal(tmref, klass)
14691483

14701484
case tmref: TermRef =>
1471-
cases(tmref.prefix, thisV, klass).select(tmref.symbol)
1485+
cases(tmref.prefix, thisV, klass).select(tmref.symbol, receiver = tmref.prefix)
14721486

14731487
case tp @ ThisType(tref) =>
14741488
val cls = tref.classSymbol.asClass

tests/init/pos/Type.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class Type:
2+
def underlyingType =
3+
val isProxy = this.isInstanceOf[TypeProxy]
4+
if (isProxy) this.asInstanceOf[TypeProxy].underlying
5+
else NoType
6+
7+
def underlyingName =
8+
val isProxy = this.isInstanceOf[TypeProxy]
9+
if (isProxy) this.asInstanceOf[TypeProxy].name
10+
else "<empty>"
11+
12+
abstract class TypeProxy extends Type:
13+
def underlying: Type = this
14+
val name: String
15+
16+
object NoType extends Type
17+
18+
object Implicits:
19+
object NoImplicits extends Type
20+
println(NoImplicits)
21+
val n = 10

0 commit comments

Comments
 (0)