Skip to content

Commit 55ddaec

Browse files
authored
Fixes for cleanup retains scheme (#21350)
- Cleanup all inferred types, not just result types of ValDefs and DefDefs. - To compensate, map overriding ValDefs and DefDefs to have declared result types. - Make type trees generated for varargs inferred. Based on #21309.
2 parents 6a1db57 + a8cc133 commit 55ddaec

File tree

10 files changed

+56
-34
lines changed

10 files changed

+56
-34
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

+2
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@ object Trees {
778778
override def isEmpty: Boolean = !hasType
779779
override def toString: String =
780780
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
781+
def isInferred = false
781782
}
782783

783784
/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
@@ -800,6 +801,7 @@ object Trees {
800801
*/
801802
class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]:
802803
type ThisTree[+T <: Untyped] <: InferredTypeTree[T]
804+
override def isInferred = true
803805

804806
/** ref.type */
805807
case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)

compiler/src/dotty/tools/dotc/cc/Setup.scala

+4-6
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
330330
end transformExplicitType
331331

332332
/** Transform type of type tree, and remember the transformed type as the type the tree */
333-
private def transformTT(tree: TypeTree, boxed: Boolean, exact: Boolean)(using Context): Unit =
333+
private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit =
334334
if !tree.hasRememberedType then
335335
val transformed =
336-
if tree.isInstanceOf[InferredTypeTree] && !exact
336+
if tree.isInferred
337337
then transformInferredType(tree.tpe)
338338
else transformExplicitType(tree.tpe, tptToCheck = Some(tree))
339339
tree.rememberType(if boxed then box(transformed) else transformed)
@@ -398,8 +398,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
398398
&& !ccConfig.useSealed
399399
&& !sym.hasAnnotation(defn.UncheckedCapturesAnnot),
400400
// types of mutable variables are boxed in pre 3.3 code
401-
exact = sym.allOverriddenSymbols.hasNext,
402-
// types of symbols that override a parent don't get a capture set TODO drop
403401
)
404402
catch case ex: IllegalCaptureRef =>
405403
capt.println(i"fail while transforming result type $tpt of $sym")
@@ -442,7 +440,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
442440
traverse(fn)
443441
if !defn.isTypeTestOrCast(fn.symbol) then
444442
for case arg: TypeTree <- args do
445-
transformTT(arg, boxed = true, exact = false) // type arguments in type applications are boxed
443+
transformTT(arg, boxed = true) // type arguments in type applications are boxed
446444

447445
case tree: TypeDef if tree.symbol.isClass =>
448446
val sym = tree.symbol
@@ -465,7 +463,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
465463

466464
def postProcess(tree: Tree)(using Context): Unit = tree match
467465
case tree: TypeTree =>
468-
transformTT(tree, boxed = false, exact = false)
466+
transformTT(tree, boxed = false)
469467
case tree: ValOrDefDef =>
470468
val sym = tree.symbol
471469

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -806,10 +806,10 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
806806
report.error(ex.toMessage, tree.srcPos.focus)
807807
pickleErrorType()
808808
case ex: AssertionError =>
809-
println(i"error when pickling tree $tree")
809+
println(i"error when pickling tree $tree of class ${tree.getClass}")
810810
throw ex
811811
case ex: MatchError =>
812-
println(i"error when pickling tree $tree")
812+
println(i"error when pickling tree $tree of class ${tree.getClass}")
813813
throw ex
814814
}
815815
}

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
560560
else keywordText("{{") ~ keywordText("/* inlined from ") ~ toText(call) ~ keywordText(" */") ~ bodyText ~ keywordText("}}")
561561
case tpt: untpd.DerivedTypeTree =>
562562
"<derived typetree watching " ~ tpt.watched.showSummary() ~ ">"
563-
case TypeTree() =>
563+
case tree: TypeTree =>
564564
typeText(toText(tree.typeOpt))
565-
~ Str("(inf)").provided(tree.isInstanceOf[InferredTypeTree] && printDebug)
565+
~ Str("(inf)").provided(tree.isInferred && printDebug)
566566
case SingletonTypeTree(ref) =>
567567
toTextLocal(ref) ~ "." ~ keywordStr("type")
568568
case RefinedTypeTree(tpt, refines) =>

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

+19-20
Original file line numberDiff line numberDiff line change
@@ -303,20 +303,19 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
303303
if !tree.symbol.is(Package) then tree
304304
else errorTree(tree, em"${tree.symbol} cannot be used as a type")
305305

306-
// Cleans up retains annotations in inferred type trees. This is needed because
307-
// during the typer, it is infeasible to correctly infer the capture sets in most
308-
// cases, resulting ill-formed capture sets that could crash the pickler later on.
309-
// See #20035.
310-
private def cleanupRetainsAnnot(symbol: Symbol, tpt: Tree)(using Context): Tree =
306+
/** Make result types of ValDefs and DefDefs that override some other definitions
307+
* declared types rather than InferredTypes. This is necessary since we otherwise
308+
* clean retains annotations from such types. But for an overriding symbol the
309+
* retains annotations come from the explicitly declared parent types, so should
310+
* be kept.
311+
*/
312+
private def makeOverrideTypeDeclared(symbol: Symbol, tpt: Tree)(using Context): Tree =
311313
tpt match
312314
case tpt: InferredTypeTree
313-
if !symbol.allOverriddenSymbols.hasNext =>
314-
// if there are overridden symbols, the annotation comes from an explicit type of the overridden symbol
315-
// and should be retained.
316-
val tm = new CleanupRetains
317-
val tpe1 = tm(tpt.tpe)
318-
tpt.withType(tpe1)
319-
case _ => tpt
315+
if symbol.allOverriddenSymbols.hasNext =>
316+
TypeTree(tpt.tpe, inferred = false).withSpan(tpt.span).withAttachmentsFrom(tpt)
317+
case _ =>
318+
tpt
320319

321320
override def transform(tree: Tree)(using Context): Tree =
322321
try tree match {
@@ -432,7 +431,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
432431
registerIfHasMacroAnnotations(tree)
433432
checkErasedDef(tree)
434433
Checking.checkPolyFunctionType(tree.tpt)
435-
val tree1 = cpy.ValDef(tree)(tpt = cleanupRetainsAnnot(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
434+
val tree1 = cpy.ValDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
436435
if tree1.removeAttachment(desugar.UntupledParam).isDefined then
437436
checkStableSelection(tree.rhs)
438437
processValOrDefDef(super.transform(tree1))
@@ -441,7 +440,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
441440
checkErasedDef(tree)
442441
Checking.checkPolyFunctionType(tree.tpt)
443442
annotateContextResults(tree)
444-
val tree1 = cpy.DefDef(tree)(tpt = cleanupRetainsAnnot(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
443+
val tree1 = cpy.DefDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
445444
processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef]))
446445
case tree: TypeDef =>
447446
registerIfHasMacroAnnotations(tree)
@@ -524,12 +523,12 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
524523
report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos)
525524
super.transform(tree)
526525
case tree: TypeTree =>
527-
tree.withType(
528-
tree.tpe match {
529-
case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot))
530-
case tpe => tpe
531-
}
532-
)
526+
val tpe = if tree.isInferred then CleanupRetains()(tree.tpe) else tree.tpe
527+
tree.withType:
528+
tpe match
529+
case AnnotatedType(parent, annot) =>
530+
AnnotatedType(parent, transformAnnot(annot)) // TODO: Also map annotations embedded in type?
531+
case _ => tpe
533532
case Typed(Ident(nme.WILDCARD), _) =>
534533
withMode(Mode.Pattern)(super.transform(tree))
535534
// The added mode signals that bounds in a pattern need not

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ trait Applications extends Compatibility {
883883
def makeVarArg(n: Int, elemFormal: Type): Unit = {
884884
val args = typedArgBuf.takeRight(n).toList
885885
typedArgBuf.dropRightInPlace(n)
886-
val elemtpt = TypeTree(elemFormal)
886+
val elemtpt = TypeTree(elemFormal, inferred = true)
887887
typedArgBuf += seqToRepeated(SeqLiteral(args, elemtpt))
888888
}
889889

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ trait TypeAssigner {
5151
else sym.info
5252

5353
private def toRepeated(tree: Tree, from: ClassSymbol)(using Context): Tree =
54-
Typed(tree, TypeTree(tree.tpe.widen.translateToRepeated(from)))
54+
Typed(tree, TypeTree(tree.tpe.widen.translateToRepeated(from), inferred = true))
5555

5656
def seqToRepeated(tree: Tree)(using Context): Tree = toRepeated(tree, defn.SeqClass)
5757

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1457,7 +1457,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
14571457
cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant
14581458
case _ =>
14591459
val target = pt.simplified
1460-
val targetTpt = InferredTypeTree().withType(target)
1460+
val targetTpt = TypeTree(target, inferred = true)
14611461
if tree.tpe <:< target then Typed(tree, targetTpt)
14621462
else
14631463
// This case should not normally arise. It currently does arise in test cases
@@ -2092,7 +2092,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20922092
// TODO: move the check above to patternMatcher phase
20932093
val uncheckedTpe = AnnotatedType(sel.tpe.widen, Annotation(defn.UncheckedAnnot, tree.selector.span))
20942094
tpd.cpy.Match(result)(
2095-
selector = tpd.Typed(sel, new tpd.InferredTypeTree().withType(uncheckedTpe)),
2095+
selector = tpd.Typed(sel, tpd.TypeTree(uncheckedTpe, inferred = true)),
20962096
cases = result.cases
20972097
)
20982098
case _ =>

library/src/scala/caps.scala

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import annotation.{experimental, compileTimeOnly, retainsCap}
2929
*/
3030
given containsImpl[C <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]()
3131

32+
/** A wrapper indicating a type variable in a capture argument list of a
33+
* @retains annotation. E.g. `^{x, Y^}` is represented as `@retains(x, capsOf[Y])`.
34+
*/
3235
@compileTimeOnly("Should be be used only internally by the Scala compiler")
3336
def capsOf[CS]: Any = ???
3437

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
trait Cancellable
2+
3+
abstract class Source[+T, Cap^]
4+
5+
extension[T, Cap^](src: Source[T, Cap]^)
6+
def transformValuesWith[U](f: (T -> U)^{Cap^}): Source[U, Cap]^{src, f} = ???
7+
8+
def race[T, Cap^](sources: Source[T, Cap]^{Cap^}*): Source[T, Cap]^{Cap^} = ???
9+
10+
def either[T1, T2, Cap^](src1: Source[T1, Cap]^{Cap^}, src2: Source[T2, Cap]^{Cap^}): Source[Either[T1, T2], Cap]^{Cap^} =
11+
val left = src1.transformValuesWith(Left(_))
12+
val right = src2.transformValuesWith(Right(_))
13+
race(left, right)
14+
15+
16+
17+
18+
19+
20+

0 commit comments

Comments
 (0)