Skip to content

Commit 65c942d

Browse files
authored
Backport "Simplify avoidance of local types of Hole" to LTS (#18939)
Backports #17571 to the LTS branch. PR submitted by the release tooling.
2 parents dd88619 + 8c53f4d commit 65c942d

File tree

18 files changed

+110
-73
lines changed

18 files changed

+110
-73
lines changed

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,16 +1513,29 @@ object SymDenotations {
15131513
* See tests/pos/i10769.scala
15141514
*/
15151515
def reachableTypeRef(using Context) =
1516-
TypeRef(owner.reachableThisType, symbol)
1516+
TypeRef(owner.reachablePrefix, symbol)
15171517

1518-
/** Like termRef, but objects in the prefix are represented by their singleton type,
1518+
/** The reachable typeRef with wildcard arguments for each type parameter */
1519+
def reachableRawTypeRef(using Context) =
1520+
reachableTypeRef.appliedTo(typeParams.map(_ => TypeBounds.emptyPolyKind))
1521+
1522+
/** Like termRef, if it is addressable from the current context,
1523+
* but objects in the prefix are represented by their singleton type,
15191524
* this means we output `pre.O.member` rather than `pre.O$.this.member`.
15201525
*
15211526
* This is required to avoid owner crash in ExplicitOuter.
15221527
* See tests/pos/i10769.scala
1528+
*
1529+
* If the reference is to an object that is not accessible from the
1530+
* current context since the object is nested in a class that is not an outer
1531+
* class of the current context, fall back to a TypeRef to the module class.
1532+
* Test case is tests/pos/i17556.scala.
1533+
* If the reference is to some other inaccessible object, throw an AssertionError.
15231534
*/
1524-
def reachableTermRef(using Context) =
1525-
TermRef(owner.reachableThisType, symbol)
1535+
def reachableTermRef(using Context): Type = owner.reachablePrefix match
1536+
case pre: SingletonType => TermRef(pre, symbol)
1537+
case pre if symbol.is(ModuleVal) => TypeRef(pre, symbol.moduleClass)
1538+
case _ => throw AssertionError(i"cannot compute path to TermRef $this from ${ctx.owner}")
15261539

15271540
/** Like thisType, but objects in the type are represented by their singleton type,
15281541
* this means we output `pre.O.member` rather than `pre.O$.this.member`.
@@ -1537,6 +1550,18 @@ object SymDenotations {
15371550
else
15381551
ThisType.raw(TypeRef(owner.reachableThisType, symbol.asType))
15391552

1553+
/** Like `reachableThisType`, except if that would refer to a class where
1554+
* the `this` cannot be accessed. In that case, fall back to the
1555+
* rawTypeRef of the class. E.g. instead of `A.this.X` where `A.this`
1556+
* is inaccessible, use `A#X`.
1557+
*/
1558+
def reachablePrefix(using Context): Type = reachableThisType match
1559+
case pre: ThisType
1560+
if !pre.cls.isStaticOwner && !ctx.owner.isContainedIn(pre.cls) =>
1561+
pre.cls.reachableRawTypeRef
1562+
case pre =>
1563+
pre
1564+
15401565
/** The variance of this type parameter or type member as a subset of
15411566
* {Covariant, Contravariant}
15421567
*/

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3037,7 +3037,8 @@ object Types {
30373037
abstract case class SuperType(thistpe: Type, supertpe: Type) extends CachedProxyType with SingletonType {
30383038
override def underlying(using Context): Type = supertpe
30393039
override def superType(using Context): Type =
3040-
thistpe.baseType(supertpe.typeSymbol)
3040+
if supertpe.typeSymbol.exists then thistpe.baseType(supertpe.typeSymbol)
3041+
else super.superType
30413042
def derivedSuperType(thistpe: Type, supertpe: Type)(using Context): Type =
30423043
if ((thistpe eq this.thistpe) && (supertpe eq this.supertpe)) this
30433044
else SuperType(thistpe, supertpe)

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,9 @@ class ClassfileParser(
991991
return unpickleTASTY(tastyBytes)
992992
}
993993
}
994-
else return unpickleTASTY(bytes)
994+
else
995+
// Before 3.0.0 we had a mode where we could embed the TASTY bytes in the classfile. This has not been supported in any stable release.
996+
report.error(s"Found a TASTY attribute with a length different from 16 in $classfile. This is likely a bug in the compiler. Please report.", NoSourcePosition)
995997
}
996998

997999
if scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,8 +1468,8 @@ object Parsers {
14681468
* PolyFunType ::= HKTypeParamClause '=>' Type
14691469
* | HKTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions
14701470
* FunTypeArgs ::= InfixType
1471-
* | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
1472-
* | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
1471+
* | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
1472+
* | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
14731473
*/
14741474
def typ(): Tree =
14751475
val start = in.offset

compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ import dotty.tools.dotc.ast.tpd
1717
import dotty.tools.dotc.ast.untpd
1818
import dotty.tools.dotc.config.ScalaRelease.*
1919

20-
import scala.collection.mutable
2120
import dotty.tools.dotc.core.Annotations._
2221
import dotty.tools.dotc.core.StdNames._
2322
import dotty.tools.dotc.quoted._
2423
import dotty.tools.dotc.inlines.Inlines
2524

2625
import scala.annotation.constructorOnly
26+
import scala.collection.mutable
2727

2828
/** Translates quoted terms and types to `unpickleExprV2` or `unpickleType` method calls.
2929
*
@@ -106,16 +106,19 @@ class PickleQuotes extends MacroTransform {
106106
private def extractHolesContents(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) =
107107
class HoleContentExtractor extends Transformer:
108108
private val holeContents = List.newBuilder[Tree]
109+
private val stagedClasses = mutable.HashSet.empty[Symbol]
109110
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
110111
tree match
111112
case tree @ Hole(isTerm, _, _, content) =>
112113
assert(isTerm)
113114
assert(!content.isEmpty)
114115
holeContents += content
115-
val holeType = getTermHoleType(tree.tpe)
116+
val holeType = getPicklableHoleType(tree.tpe, stagedClasses)
116117
val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType)
117118
cpy.Inlined(tree)(EmptyTree, Nil, hole)
118119
case tree: DefTree =>
120+
if tree.symbol.isClass then
121+
stagedClasses += tree.symbol
119122
val newAnnotations = tree.symbol.annotations.mapconserve { annot =>
120123
annot.derivedAnnotation(transform(annot.tree)(using ctx.withOwner(tree.symbol)))
121124
}
@@ -134,19 +137,6 @@ class PickleQuotes extends MacroTransform {
134137
}
135138
}
136139

137-
/** Remove references to local types that will not be defined in this quote */
138-
private def getTermHoleType(using Context) = new TypeMap() {
139-
override def apply(tp: Type): Type = tp match
140-
case tp @ TypeRef(NoPrefix, _) =>
141-
// reference to term with a type defined in outer quote
142-
getTypeHoleType(tp)
143-
case tp @ TermRef(NoPrefix, _) =>
144-
// widen term refs to terms defined in outer quote
145-
apply(tp.widenTermRefExpr)
146-
case tp =>
147-
mapOver(tp)
148-
}
149-
150140
/** Get the holeContents of the transformed tree */
151141
def getContents() =
152142
val res = holeContents.result
@@ -196,11 +186,11 @@ class PickleQuotes extends MacroTransform {
196186
cpy.Quote(quote)(Block(tdefs, body1), quote.tags)
197187

198188
private def mkTagSymbolAndAssignType(typeArg: Tree, idx: Int)(using Context): TypeDef = {
199-
val holeType = getTypeHoleType(typeArg.tpe.select(tpnme.Underlying))
189+
val holeType = getPicklableHoleType(typeArg.tpe.select(tpnme.Underlying), _ => false)
200190
val hole = untpd.cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree).withType(holeType)
201191
val local = newSymbol(
202192
owner = ctx.owner,
203-
name = UniqueName.fresh(hole.tpe.dealias.typeSymbol.name.toTypeName),
193+
name = UniqueName.fresh(typeArg.symbol.name.toTypeName),
204194
flags = Synthetic,
205195
info = TypeAlias(typeArg.tpe.select(tpnme.Underlying)),
206196
coord = typeArg.span
@@ -209,25 +199,11 @@ class PickleQuotes extends MacroTransform {
209199
ctx.typeAssigner.assignType(untpd.TypeDef(local.name, hole), local).withSpan(typeArg.span)
210200
}
211201

212-
/** Remove references to local types that will not be defined in this quote */
213-
private def getTypeHoleType(using Context) = new TypeMap() {
214-
override def apply(tp: Type): Type = tp match
215-
case tp: TypeRef if tp.typeSymbol.isTypeSplice =>
216-
apply(tp.dealias)
217-
case tp @ TypeRef(pre, _) if isLocalPath(pre) =>
218-
val hiBound = tp.typeSymbol.info match
219-
case info: ClassInfo => info.parents.reduce(_ & _)
220-
case info => info.hiBound
221-
apply(hiBound)
222-
case tp =>
223-
mapOver(tp)
224-
225-
private def isLocalPath(tp: Type): Boolean = tp match
226-
case NoPrefix => true
227-
case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix)
228-
case tp => false
229-
}
230-
202+
/** Avoid all non-static types except those defined in the quote. */
203+
private def getPicklableHoleType(tpe: Type, isStagedClasses: Symbol => Boolean)(using Context) =
204+
new TypeOps.AvoidMap {
205+
def toAvoid(tp: NamedType) = !isStagedClasses(tp.typeSymbol) && !isStaticPrefix(tp)
206+
}.apply(tpe)
231207
}
232208

233209
object PickleQuotes {

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,6 @@ object SymUtils:
333333
else owner.isLocal
334334
}
335335

336-
/** The reachable typeRef with wildcard arguments for each type parameter */
337-
def reachableRawTypeRef(using Context) =
338-
self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind))
339-
340336
/** Is symbol a type splice operation? */
341337
def isTypeSplice(using Context): Boolean =
342338
self == defn.QuotedType_splice

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
552552
.map((pre, child) => rawRef(child).asSeenFrom(pre, child.owner))
553553
case _ =>
554554
cls.children.map(rawRef)
555-
end computeChildTypes
555+
556556
val childTypes = computeChildTypes
557557
val cases =
558558
for (patType, idx) <- childTypes.zipWithIndex yield

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,10 @@ class Namer { typer: Typer =>
11221122
No("is already an extension method, cannot be exported into another one")
11231123
else if targets.contains(alias) then
11241124
No(i"clashes with another export in the same export clause")
1125-
else if sym.is(Override) then
1125+
else if sym.is(Override) || sym.is(JavaDefined) then
1126+
// The tests above are used to avoid futile searches of `allOverriddenSymbols`.
1127+
// Scala defined symbols can override concrete symbols only if declared override.
1128+
// For Java defined symbols, this does not hold, so we have to search anyway.
11261129
sym.allOverriddenSymbols.find(
11271130
other => cls.derivesFrom(other.owner) && !other.is(Deferred)
11281131
) match

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ i15922.scala
2222
t5031_2.scala
2323
i16997.scala
2424
i7414.scala
25+
i17588.scala
2526

2627
# Tree is huge and blows stack for printing Text
2728
i7034.scala

docs/_docs/reference/changed-features/type-checking.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/_docs/reference/dropped-features/nonlocal-returns.md

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,17 @@ Returning from nested anonymous functions has been deprecated, and will produce
99

1010
Nonlocal returns are implemented by throwing and catching `scala.runtime.NonLocalReturnException`-s. This is rarely what is intended by the programmer. It can be problematic because of the hidden performance cost of throwing and catching exceptions. Furthermore, it is a leaky implementation: a catch-all exception handler can intercept a `NonLocalReturnException`.
1111

12-
A drop-in library replacement is provided in [`scala.util.control.NonLocalReturns`](https://scala-lang.org/api/3.x/scala/util/control/NonLocalReturns$.html). Example:
12+
A better alternative to nonlocal returns and also the `scala.util.control.Breaks` API is provided by [`scala.util.boundary` and `boundary.break`](http://dotty.epfl.ch/api/scala/util/boundary$.html).
1313

14-
```scala
15-
import scala.util.control.NonLocalReturns.*
16-
17-
extension [T](xs: List[T])
18-
def has(elem: T): Boolean = returning {
19-
for x <- xs do
20-
if x == elem then throwReturn(true)
21-
false
22-
}
14+
Example:
2315

24-
@main def test(): Unit =
25-
val xs = List(1, 2, 3, 4, 5)
26-
assert(xs.has(2) == xs.contains(2))
16+
```scala
17+
import scala.util.boundary, boundary.break
18+
def firstIndex[T](xs: List[T], elem: T): Int =
19+
boundary:
20+
for (x, i) <- xs.zipWithIndex do
21+
if x == elem then break(i)
22+
-1
2723
```
2824

2925
Note: compiler produces deprecation error on nonlocal returns only with `-source:future` option.

docs/sidebar.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ subsection:
9292
- page: reference/changed-features/operators.md
9393
- page: reference/changed-features/wildcards.md
9494
- page: reference/changed-features/imports.md
95-
- page: reference/changed-features/type-checking.md
9695
- page: reference/changed-features/type-inference.md
9796
- page: reference/changed-features/implicit-resolution.md
9897
- page: reference/changed-features/implicit-conversions.md

project/resources/referenceReplacements/sidebar.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ subsection:
8888
- page: reference/changed-features/operators.md
8989
- page: reference/changed-features/wildcards.md
9090
- page: reference/changed-features/imports.md
91-
- page: reference/changed-features/type-checking.md
9291
- page: reference/changed-features/type-inference.md
9392
- page: reference/changed-features/implicit-resolution.md
9493
- page: reference/changed-features/implicit-conversions.md

project/scripts/expected-links/reference-expected-links.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
./changed-features/pattern-matching.html
1919
./changed-features/structural-types-spec.html
2020
./changed-features/structural-types.html
21-
./changed-features/type-checking.html
2221
./changed-features/type-inference.html
2322
./changed-features/vararg-splices.html
2423
./changed-features/wildcards.html

tests/pos/i17556.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
sealed trait A {
2+
// must be `object` or `case class`
3+
object X extends A
4+
case class Y() extends A
5+
}
6+
7+
// companion object must exist
8+
object A

tests/pos/i17588.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class StringBox(inner: String):
2+
export inner.*

tests/run/i15913.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// https://github.com/lampepfl/dotty/issues/15913
2+
3+
class injector[F]
4+
5+
object injectorFactory {
6+
def apply[F](overrides: String*): injector[F] = new injector[F]
7+
8+
def apply[F](
9+
bootstrapActivation: Int = ???,
10+
overrides: Seq[String] = Seq.empty,
11+
): injector[F] = new injector[F]
12+
}
13+
14+
object Test extends App {
15+
println(
16+
injectorFactory[String](
17+
bootstrapActivation = 0
18+
)
19+
)
20+
}

tests/run/i17555.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Root {
2+
override def toString() = "Root"
3+
}
4+
trait A extends Root with B { }
5+
trait B {
6+
override def toString() = "B"
7+
}
8+
case class C() extends A {
9+
override def toString() = super.toString()
10+
}
11+
class D() extends A, Serializable {
12+
override def toString() = super.toString()
13+
}
14+
15+
@main def Test =
16+
assert(C().toString == "B")
17+
assert(D().toString == "B")

0 commit comments

Comments
 (0)