diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 942fd6c9b0c7..4c7ca396117e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -778,6 +778,7 @@ object Trees { override def isEmpty: Boolean = !hasType override def toString: String = s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" + def isInferred = false } /** Tree that replaces a level 1 splices in pickled (level 0) quotes. @@ -800,6 +801,7 @@ object Trees { */ class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]: type ThisTree[+T <: Untyped] <: InferredTypeTree[T] + override def isInferred = true /** ref.type */ case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 25d50052f107..91671d7d7776 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -330,10 +330,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: end transformExplicitType /** Transform type of type tree, and remember the transformed type as the type the tree */ - private def transformTT(tree: TypeTree, boxed: Boolean, exact: Boolean)(using Context): Unit = + private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit = if !tree.hasRememberedType then val transformed = - if tree.isInstanceOf[InferredTypeTree] && !exact + if tree.isInferred then transformInferredType(tree.tpe) else transformExplicitType(tree.tpe, tptToCheck = Some(tree)) tree.rememberType(if boxed then box(transformed) else transformed) @@ -398,8 +398,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: && !ccConfig.useSealed && !sym.hasAnnotation(defn.UncheckedCapturesAnnot), // types of mutable variables are boxed in pre 3.3 code - exact = sym.allOverriddenSymbols.hasNext, - // types of symbols that override a parent don't get a capture set TODO drop ) catch case ex: IllegalCaptureRef => capt.println(i"fail while transforming result type $tpt of $sym") @@ -442,7 +440,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: traverse(fn) if !defn.isTypeTestOrCast(fn.symbol) then for case arg: TypeTree <- args do - transformTT(arg, boxed = true, exact = false) // type arguments in type applications are boxed + transformTT(arg, boxed = true) // type arguments in type applications are boxed case tree: TypeDef if tree.symbol.isClass => val sym = tree.symbol @@ -465,7 +463,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def postProcess(tree: Tree)(using Context): Unit = tree match case tree: TypeTree => - transformTT(tree, boxed = false, exact = false) + transformTT(tree, boxed = false) case tree: ValOrDefDef => val sym = tree.symbol diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index eeeaaaf72bf1..6659348fb5de 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -806,10 +806,10 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { report.error(ex.toMessage, tree.srcPos.focus) pickleErrorType() case ex: AssertionError => - println(i"error when pickling tree $tree") + println(i"error when pickling tree $tree of class ${tree.getClass}") throw ex case ex: MatchError => - println(i"error when pickling tree $tree") + println(i"error when pickling tree $tree of class ${tree.getClass}") throw ex } } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7f83a16d39a9..ea729e9549d5 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -560,9 +560,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else keywordText("{{") ~ keywordText("/* inlined from ") ~ toText(call) ~ keywordText(" */") ~ bodyText ~ keywordText("}}") case tpt: untpd.DerivedTypeTree => "" - case TypeTree() => + case tree: TypeTree => typeText(toText(tree.typeOpt)) - ~ Str("(inf)").provided(tree.isInstanceOf[InferredTypeTree] && printDebug) + ~ Str("(inf)").provided(tree.isInferred && printDebug) case SingletonTypeTree(ref) => toTextLocal(ref) ~ "." ~ keywordStr("type") case RefinedTypeTree(tpt, refines) => diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index c6ad1bb860e8..0feee53ca50f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -303,20 +303,19 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => if !tree.symbol.is(Package) then tree else errorTree(tree, em"${tree.symbol} cannot be used as a type") - // Cleans up retains annotations in inferred type trees. This is needed because - // during the typer, it is infeasible to correctly infer the capture sets in most - // cases, resulting ill-formed capture sets that could crash the pickler later on. - // See #20035. - private def cleanupRetainsAnnot(symbol: Symbol, tpt: Tree)(using Context): Tree = + /** Make result types of ValDefs and DefDefs that override some other definitions + * declared types rather than InferredTypes. This is necessary since we otherwise + * clean retains annotations from such types. But for an overriding symbol the + * retains annotations come from the explicitly declared parent types, so should + * be kept. + */ + private def makeOverrideTypeDeclared(symbol: Symbol, tpt: Tree)(using Context): Tree = tpt match case tpt: InferredTypeTree - if !symbol.allOverriddenSymbols.hasNext => - // if there are overridden symbols, the annotation comes from an explicit type of the overridden symbol - // and should be retained. - val tm = new CleanupRetains - val tpe1 = tm(tpt.tpe) - tpt.withType(tpe1) - case _ => tpt + if symbol.allOverriddenSymbols.hasNext => + TypeTree(tpt.tpe, inferred = false).withSpan(tpt.span).withAttachmentsFrom(tpt) + case _ => + tpt override def transform(tree: Tree)(using Context): Tree = try tree match { @@ -432,7 +431,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => registerIfHasMacroAnnotations(tree) checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) - val tree1 = cpy.ValDef(tree)(tpt = cleanupRetainsAnnot(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) + val tree1 = cpy.ValDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) if tree1.removeAttachment(desugar.UntupledParam).isDefined then checkStableSelection(tree.rhs) processValOrDefDef(super.transform(tree1)) @@ -441,7 +440,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) annotateContextResults(tree) - val tree1 = cpy.DefDef(tree)(tpt = cleanupRetainsAnnot(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) + val tree1 = cpy.DefDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt), rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef])) case tree: TypeDef => registerIfHasMacroAnnotations(tree) @@ -524,12 +523,12 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos) super.transform(tree) case tree: TypeTree => - tree.withType( - tree.tpe match { - case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot)) - case tpe => tpe - } - ) + val tpe = if tree.isInferred then CleanupRetains()(tree.tpe) else tree.tpe + tree.withType: + tpe match + case AnnotatedType(parent, annot) => + AnnotatedType(parent, transformAnnot(annot)) // TODO: Also map annotations embedded in type? + case _ => tpe case Typed(Ident(nme.WILDCARD), _) => withMode(Mode.Pattern)(super.transform(tree)) // The added mode signals that bounds in a pattern need not diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 7cb0dbcdc833..33ab9f210634 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -883,7 +883,7 @@ trait Applications extends Compatibility { def makeVarArg(n: Int, elemFormal: Type): Unit = { val args = typedArgBuf.takeRight(n).toList typedArgBuf.dropRightInPlace(n) - val elemtpt = TypeTree(elemFormal) + val elemtpt = TypeTree(elemFormal, inferred = true) typedArgBuf += seqToRepeated(SeqLiteral(args, elemtpt)) } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2be81a4222cd..fd16f0de5f3a 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -51,7 +51,7 @@ trait TypeAssigner { else sym.info private def toRepeated(tree: Tree, from: ClassSymbol)(using Context): Tree = - Typed(tree, TypeTree(tree.tpe.widen.translateToRepeated(from))) + Typed(tree, TypeTree(tree.tpe.widen.translateToRepeated(from), inferred = true)) def seqToRepeated(tree: Tree)(using Context): Tree = toRepeated(tree, defn.SeqClass) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ace8439553fd..c9d562a0531a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1457,7 +1457,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant case _ => val target = pt.simplified - val targetTpt = InferredTypeTree().withType(target) + val targetTpt = TypeTree(target, inferred = true) if tree.tpe <:< target then Typed(tree, targetTpt) else // 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 // TODO: move the check above to patternMatcher phase val uncheckedTpe = AnnotatedType(sel.tpe.widen, Annotation(defn.UncheckedAnnot, tree.selector.span)) tpd.cpy.Match(result)( - selector = tpd.Typed(sel, new tpd.InferredTypeTree().withType(uncheckedTpe)), + selector = tpd.Typed(sel, tpd.TypeTree(uncheckedTpe, inferred = true)), cases = result.cases ) case _ => diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 9700ed62738d..9911ef920116 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -29,6 +29,9 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ given containsImpl[C <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]() + /** A wrapper indicating a type variable in a capture argument list of a + * @retains annotation. E.g. `^{x, Y^}` is represented as `@retains(x, capsOf[Y])`. + */ @compileTimeOnly("Should be be used only internally by the Scala compiler") def capsOf[CS]: Any = ??? diff --git a/tests/pos-custom-args/captures/cc-poly-varargs.scala b/tests/pos-custom-args/captures/cc-poly-varargs.scala new file mode 100644 index 000000000000..ac76c47d6dd5 --- /dev/null +++ b/tests/pos-custom-args/captures/cc-poly-varargs.scala @@ -0,0 +1,20 @@ +trait Cancellable + +abstract class Source[+T, Cap^] + +extension[T, Cap^](src: Source[T, Cap]^) + def transformValuesWith[U](f: (T -> U)^{Cap^}): Source[U, Cap]^{src, f} = ??? + +def race[T, Cap^](sources: Source[T, Cap]^{Cap^}*): Source[T, Cap]^{Cap^} = ??? + +def either[T1, T2, Cap^](src1: Source[T1, Cap]^{Cap^}, src2: Source[T2, Cap]^{Cap^}): Source[Either[T1, T2], Cap]^{Cap^} = + val left = src1.transformValuesWith(Left(_)) + val right = src2.transformValuesWith(Right(_)) + race(left, right) + + + + + + +