Skip to content

Commit d43d6bd

Browse files
authored
Fix stale symbol crashes in some path depended types in macro contexts (#18077)
Closes #17152 Closes #17294 In general the issues stemmed from the fact that after suspending runs due to the found macros, when typing Ident of the called method, symbols from the previous run are found there. Some of them either are able to have their validity updated while returning the denotation (which those path dependent types are unable to do, since their owners now have updated decls, which do not include the stale symbol), or do not need denotation as part of a code path they rely on. The fixes simply check if the to-be-used symbol has a valid runID, and if not then recomputes it. The first commit fixes the minimizations from above GitHub issues. Both minimizations by design have to result in cyclic macro errors (which they now do), so they were placed in `neg-macros` tests. By some experimentation, I ended up with another, slightly different stale symbol crash, with the stale symbol trying to create a denotation in different place (in `withPrefix`), requiring an additional fix there, included in the second commit. This minimization, unlike the previous ones, does compile successfully, without cyclic macro errors, which shows the issue was not exclusive to those.
2 parents 1a21f80 + 576d448 commit d43d6bd

File tree

7 files changed

+143
-7
lines changed

7 files changed

+143
-7
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,7 +2677,10 @@ object Types {
26772677
else {
26782678
if (isType) {
26792679
val res =
2680-
if (currentSymbol.isAllOf(ClassTypeParam)) argForParam(prefix)
2680+
val sym =
2681+
if (currentSymbol.isValidInCurrentRun) currentSymbol
2682+
else computeSymbol
2683+
if (sym.isAllOf(ClassTypeParam)) argForParam(prefix)
26812684
else prefix.lookupRefined(name)
26822685
if (res.exists) return res
26832686
if (Config.splitProjections)
@@ -2751,14 +2754,16 @@ object Types {
27512754
/** A reference like this one, but with the given prefix. */
27522755
final def withPrefix(prefix: Type)(using Context): Type = {
27532756
def reload(): NamedType = {
2754-
val lastSym = lastSymbol.nn
2755-
val allowPrivate = !lastSym.exists || lastSym.is(Private)
2757+
val sym =
2758+
if lastSymbol.nn.isValidInCurrentRun then lastSymbol.nn
2759+
else computeSymbol
2760+
val allowPrivate = !sym.exists || sym.is(Private)
27562761
var d = memberDenot(prefix, name, allowPrivate)
2757-
if (d.isOverloaded && lastSym.exists)
2762+
if (d.isOverloaded && sym.exists)
27582763
d = disambiguate(d,
2759-
if (lastSym.signature == Signature.NotAMethod) Signature.NotAMethod
2760-
else lastSym.asSeenFrom(prefix).signature,
2761-
lastSym.targetName)
2764+
if (sym.signature == Signature.NotAMethod) Signature.NotAMethod
2765+
else sym.asSeenFrom(prefix).signature,
2766+
sym.targetName)
27622767
NamedType(prefix, name, d)
27632768
}
27642769
if (prefix eq this.prefix) this

tests/neg-macros/i17152/DFBits.scala

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// nopos-error
2+
package crash
3+
4+
import scala.quoted.*
5+
6+
class IRDFType
7+
class IRDFBoolOrBit extends IRDFType
8+
class IRDFDecimal extends IRDFType
9+
class IRDFBits extends IRDFType
10+
11+
final class DFType[+T <: IRDFType, +A]
12+
type DFTypeAny = DFType[IRDFType, Any]
13+
14+
trait Baz
15+
16+
trait Width[T]:
17+
type Out <: Int
18+
object Width:
19+
given fromDFBoolOrBit[T <: DFBoolOrBit]: Width[T] with
20+
type Out = 1
21+
transparent inline given [T]: Width[T] = ${ getWidthMacro[T] }
22+
def getWidthMacro[T](using Quotes, Type[T]): Expr[Width[T]] =
23+
'{
24+
new Width[T]:
25+
type Out = 1
26+
}
27+
end Width
28+
29+
extension [T](t: T)(using baz: Baz) def width: 1 = ???
30+
31+
trait Check[T1 <: Int, T2 <: Int]
32+
33+
type DFBits[W <: Int] = DFType[IRDFBits, Tuple1[W]]
34+
35+
private object CompanionsDFBits:
36+
object Val:
37+
trait Candidate[R]:
38+
type OutW <: Int
39+
def apply(value: R): DFValOf[DFBits[OutW]]
40+
object Candidate:
41+
given fromDFUInt[W <: Int, R <: DFValOf[DFDecimal]]: Candidate[R] with
42+
type OutW = W
43+
def apply(value: R): DFValOf[DFBits[W]] =
44+
import DFVal.Ops.bits
45+
value.bits
46+
???
47+
end Candidate
48+
49+
object TC:
50+
import DFVal.TC
51+
given DFBitsFromCandidate[
52+
LW <: Int,
53+
V
54+
](using candidate: Candidate[V])(using
55+
check: Check[LW, candidate.OutW]
56+
): TC[DFBits[LW], V] with
57+
def conv(dfType: DFBits[LW], value: V): DFValOf[DFBits[LW]] =
58+
val dfVal = candidate(value)
59+
???
60+
end TC
61+
end Val
62+
63+
end CompanionsDFBits
64+
65+
type DFBoolOrBit = DFType[IRDFBoolOrBit, Any]
66+
type DFDecimal = DFType[IRDFDecimal, Any]
67+
object DFDecimal:
68+
def foo(arg1: Int, arg2: Int): Unit = ???
69+
70+
object Val:
71+
object TC:
72+
import DFVal.TC
73+
given [R]: TC[DFDecimal, R] = ???
74+
def apply(
75+
dfType: DFDecimal,
76+
dfVal: DFValOf[DFDecimal]
77+
): DFValOf[DFDecimal] =
78+
foo(dfType.width, dfVal.width)
79+
dfVal
80+
end TC
81+
end Val
82+
end DFDecimal

tests/neg-macros/i17152/DFVal.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package crash
2+
3+
trait TCConv[T <: DFTypeAny, V, O]:
4+
type Out <: O
5+
def conv(dfType: T, value: V): Out
6+
7+
class DFVal[+T <: DFTypeAny]
8+
type DFValAny = DFVal[DFTypeAny]
9+
type DFValOf[+T <: DFTypeAny] = DFVal[T]
10+
11+
object DFVal:
12+
trait TC[T <: DFTypeAny, R] extends TCConv[T, R, DFValAny]:
13+
type Out = DFValOf[T]
14+
final def apply(dfType: T, value: R): Out = ???
15+
16+
object TC:
17+
export CompanionsDFBits.Val.TC.given
18+
end TC
19+
20+
object Ops:
21+
extension [T <: DFTypeAny, A, C, I](dfVal: DFVal[T])
22+
def bits(using w: Width[T]): DFValOf[DFBits[w.Out]] = ???
23+
end extension
24+
end Ops
25+
end DFVal

tests/neg-macros/i17294/DFVal.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package crash
2+
3+
def bits[T](t: T)(using w: Width[T]): w.Out = ???

tests/neg-macros/i17294/Width.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// nopos-error
2+
package crash
3+
import scala.quoted.*
4+
5+
trait Width[T]:
6+
type Out
7+
object Width:
8+
transparent inline given [T]: Width[T] = ${ getWidthMacro[T] }
9+
def getWidthMacro[T](using Quotes, Type[T]): Expr[Width[T]] = '{ new Width[T] {} }
10+
end Width
11+
12+
val x = bits(1)

tests/pos-macros/i17294/Bar.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.quoted.*
2+
3+
class Bar[T]
4+
object Bar:
5+
transparent inline def bar[T](a: Foo, b: a.Out): Bar[T] = ${ getBarMacro[T] }
6+
def getBarMacro[T](using Quotes, Type[T]): Expr[Bar[T]] = '{ new Bar[T] }

tests/pos-macros/i17294/Foo.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo:
2+
type Out = Int
3+
val a = Bar.bar(new Foo(), 0)

0 commit comments

Comments
 (0)