diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 45dba97a79f7..558463618d81 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -7,6 +7,7 @@ import ast.tpd, tpd.* import util.Spans.Span import printing.{Showable, Printer} import printing.Texts.Text +import cc.isRetainsLike import scala.annotation.internal.sharable @@ -63,7 +64,11 @@ object Annotations { else val tp1 = tm(tree.tpe) foldOver(if tp1 frozen_=:= tree.tpe then x else tp1, tree) - val diff = findDiff(NoType, args) + val diff = if symbol.isRetainsLike then defn.AnyType else findDiff(NoType, args) + // If this is a @retains annotation, the diff check seems to be unreliable. + // We work around this issue by always mapping @retains annotations. See #20035. + // + // FIXME: investigate why the diff check is incorrect for @retains if tm.isRange(diff) then EmptyAnnotation else if diff.exists then derivedAnnotation(tm.mapOver(tree)) else this @@ -73,9 +78,16 @@ object Annotations { val args = arguments if args.isEmpty then false else tree.existsSubTree: - case id: (Ident | This) => id.tpe.stripped match + case id: Ident => id.tpe.stripped match case TermParamRef(tl1, _) => tl eq tl1 case _ => false + case ref: This => ref.tpe match + case TermParamRef(tl1, _) => tl eq tl1 + case AnnotatedType(tp1, annot1) => + // The type of captured elements in @retains annotation could themself + // refer to term parameters. See #20035. + annot1.refersToParamOf(tl) + case _ => false case _ => false /** A string representation of the annotation. Overridden in BodyAnnotation. diff --git a/tests/pos-custom-args/captures/tablediff.scala b/tests/pos-custom-args/captures/tablediff.scala new file mode 100644 index 000000000000..244ee1a46a23 --- /dev/null +++ b/tests/pos-custom-args/captures/tablediff.scala @@ -0,0 +1,11 @@ +import language.experimental.captureChecking + +trait Seq[+A]: + def zipAll[A1 >: A, B](that: Seq[B]^, thisElem: A1, thatElem: B): Seq[(A1, B)]^{this, that} + def map[B](f: A => B): Seq[B]^{this, f} + +def zipAllOption[X](left: Seq[X], right: Seq[X]) = + left.map(Option(_)).zipAll(right.map(Option(_)), None, None) + +def fillRow[T](headRow: Seq[T], tailRow: Seq[T]) = + val paddedZip = zipAllOption(headRow, tailRow)