diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c155f42c52e6..41077637d92e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1646,7 +1646,9 @@ object Trees { } def foldMoreCases(x: X, tree: Tree)(using Context): X = { - assert(ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), tree) + assert(ctx.reporter.hasUnreportedErrors + || ctx.reporter.errorsReported + || ctx.mode.is(Mode.Interactive), tree) // In interactive mode, errors might come from previous runs. // In case of errors it may be that typed trees point to untyped ones. // The IDE can still traverse inside such trees, either in the run where errors diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 275f04283146..7889f6fc0ad1 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -362,7 +362,7 @@ object ProtoTypes { * - t2 is a ascription (t22: T) and t1 is at the outside of t22 * - t2 is a closure (...) => t22 and t1 is at the outside of t22 */ - def hasInnerErrors(t: Tree): Boolean = t match + def hasInnerErrors(t: Tree)(using Context): Boolean = t match case Typed(expr, tpe) => hasInnerErrors(expr) case closureDef(mdef) => hasInnerErrors(mdef.rhs) case _ => diff --git a/tests/neg/i15301.scala b/tests/neg/i15301.scala new file mode 100644 index 000000000000..cb8ac3660438 --- /dev/null +++ b/tests/neg/i15301.scala @@ -0,0 +1,63 @@ +package theproblem + +import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.ExecutionContext.Implicits.global + +object Items: + opaque type TheId = Int + extension (TheId: TheId) def raw: Int = TheId + + object TheId: + def apply(id: Int): TheId = id + + +import Items.TheId + +case class AnError(id: TheId) + +type ErrAcc[A] = Either[Seq[AnError], A] + +case class Res[A](future: Future[ErrAcc[A]]): + + def map[B](f: A => B)(using ExecutionContext): Res[B] = + Res(this.future.map(_.map(f))) + + def flatMap[B](f: A => Res[B])(using ExecutionContext): Res[B] = + Res(this.future.flatMap { + case Right(x) => f(x).future + case Left(es) => Future.successful(Left[Seq[AnError], B](es)) + }) + + def zip[B](that: Res[B])(using ExecutionContext): Res[(A, B)] = + def zipacc(a: ErrAcc[A], b: ErrAcc[B]): ErrAcc[(A, B)] = (a, b) match + case (Right(x), Right(y)) => Right((x, y)) + case (Right(_), Left(e) ) => Left(e) + case (Left(e), Right(_)) => Left(e) + case (Left(ex), Left(ey)) => Left(ex ++ ey) + + Res(this.future.flatMap { a => that.future.map { b => zipacc(a, b) } }) + +object Res: + def successful[A](x: A): Res[A] = Res(Future.successful(Right[Seq[AnError], A](x))) + + def traverse[A, B](as: Seq[A])(f: A => Res[B])(using ExecutionContext): Res[Seq[B]] = + as.foldLeft[Res[Seq[B]]](successful(Seq.empty)) { (acc, x) => acc.zip(f(x)).map(_ :+ _) } + +trait M: + def getID(name: String)(using ExecutionContext): Res[TheId] + def bfs(sid: TheId, tid: TheId): Res[Boolean] + +class theproblem(m: M): + + def thebug(names: List[String]): Res[Seq[(String, String, Boolean)]] = + Res.traverse(names)(m.getID).flatMap { ids => + val id_names = ids.zip(names) + val ps = for { + (sid, sname) <- id_names + (tid, tname) <- id_names + if sid != tid + } yield (sid -> sname, tid -> tname) + Res.traverse(ps){ (s, t) => + m.bfs(s(0), t(0)).map((s(1), t(2), _)) // error t(2) should be t(1) + } + } \ No newline at end of file