From bdd9cfd54440b2f2b50973025b76b629d59a867c Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 9 Nov 2025 01:04:07 -0800 Subject: [PATCH 1/2] Fix typos and tweak booleans --- .../src/dotty/tools/dotc/core/Contexts.scala | 4 +-- .../src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- .../tools/dotc/typer/ImportSuggestions.scala | 2 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 17 ++++++------ tests/pos/i24192.scala | 27 +++++++++++++++++++ 7 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 tests/pos/i24192.scala diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index c33d276cbec0..9896cfaa6a1d 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -810,11 +810,11 @@ object Contexts { final def addMode(mode: Mode): c.type = c.setMode(c.mode | mode) final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) - /** Run `op` with a pool-allocated context that has an ExporeTyperState. */ + /** Run `op` with a pool-allocated context that has an ExploreTyperState. */ inline def explore[T](inline op: Context ?=> T)(using Context): T = exploreInFreshCtx(op) - /** Run `op` with a pool-allocated FreshContext that has an ExporeTyperState. */ + /** Run `op` with a pool-allocated FreshContext that has an ExploreTyperState. */ inline def exploreInFreshCtx[T](inline op: FreshContext ?=> T)(using Context): T = val pool = ctx.base.exploreContextPool val nestedCtx = pool.next() diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6bf19c5a27a1..e3fe4987b3d7 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2307,7 +2307,7 @@ object Types extends TypeUtils { /** A trait for proto-types, used as expected types in typer */ trait ProtoType extends Type { - def isMatchedBy(tp: Type, keepConstraint: Boolean = false)(using Context): Boolean + def isMatchedBy(tp: Type, keepConstraint: Boolean)(using Context): Boolean def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T def map(tm: TypeMap)(using Context): ProtoType diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index d3e3a0d06bd8..b31fc9afda6b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1221,7 +1221,7 @@ trait Implicits: def tryConversionForSelection(using Context) = val converted = tryConversion - if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) then + if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe, keepConstraint = false) then // this check is needed since adapting relative to a `SelectionProto` can succeed // even if the term is not a subtype of the `SelectionProto` err.typeMismatch(converted, selProto) diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 8c5a206a413a..f0d8b2e3b135 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -185,7 +185,7 @@ trait ImportSuggestions: // To regain precision, test both sides separately. test(ViewProto(argType, rt1)) || test(ViewProto(argType, rt2)) case pt: ViewProto => - pt.isMatchedBy(ref) + pt.isMatchedBy(ref, keepConstraint = false) case _ => normalize(ref, pt) <:< pt test(pt) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 4fc092d16007..ab2ca38520ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -133,7 +133,7 @@ object ProtoTypes { constFoldException(pt) || { if Inlines.isInlineable(meth) then - // Stricter behavisour in 3.4+: do not apply `wildApprox` to non-transparent inlines + // Stricter behaviour in 3.4+: do not apply `wildApprox` to non-transparent inlines // unless their return type is a MatchType. In this case there's no reason // not to constrain type variables in the expected type. For transparent inlines // we do not want to constrain type variables in the expected type since the diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7432a0900ac5..081659fe36f4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4148,7 +4148,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer Some(adapt(tree, pt, locked)) else val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false, tree.nameSpan) - if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then + if selProto.isMatchedBy(qual.tpe, keepConstraint = false) || tree.hasAttachment(InsertedImplicitOnQualifier) + then None else tryEither { @@ -4465,7 +4466,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer arg.tpe match case failed: SearchFailureType if canProfitFromMoreConstraints => val pt1 = pt.deepenProtoTrans - if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then + if (pt1 ne pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then return implicitArgs(formals, argIndex, pt1) case _ => @@ -4579,13 +4580,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Reset context in case it was set to a supercall context before. // otherwise the invariant for taking another this or super call context is not met. // Test case is i20483.scala - tree match - case tree: Block => - readaptSimplified(tpd.Block(tree.stats, tpd.Apply(tree.expr, args))) - case tree: NamedArg => - readaptSimplified(tpd.NamedArg(tree.name, tpd.Apply(tree.arg, args))) - case _ => - readaptSimplified(tpd.Apply(tree, args)) + val cpy = tree match + case tree: Block => tpd.Block(tree.stats, tpd.Apply(tree.expr, args)) + case tree: NamedArg => tpd.NamedArg(tree.name, tpd.Apply(tree.arg, args)) + case _ => tpd.Apply(tree, args) + readaptSimplified(cpy) end addImplicitArgs pt.revealIgnored match { diff --git a/tests/pos/i24192.scala b/tests/pos/i24192.scala new file mode 100644 index 000000000000..4210a2f62dfc --- /dev/null +++ b/tests/pos/i24192.scala @@ -0,0 +1,27 @@ +import scala.language.implicitConversions + +// https://github.com/scala/scala-collection-contrib/blob/main/src/main/scala/scala/collection/decorators/package.scala +object decorators { + trait IsMap[A] + implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): Map[C, map.type] = ??? +} +import decorators.mapDecorator // unused, required to reproduce + +trait Eq[T] +trait Applicative[F[_]] +given Applicative[Option] = ??? + +trait Traverse[F[_]]: + // context bound required to reproduce + def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] = ??? + +object Traverse: + def apply[F[_]]: Traverse[F] = ??? + +trait Segment[Element: Eq] + +case class MergeResult[Element: Eq] private (segments: Seq[Segment[Element]]): + def thisFailsToCompile(): Option[MergeResult[Element]] = + Traverse[Seq] + .sequence(Seq.empty[Option[Segment[Element]]]) + .map(MergeResult.apply) // no error From 02b1caa1db04e4c4800e49bb40243ea8c7b1cb65 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 9 Nov 2025 08:07:46 -0800 Subject: [PATCH 2/2] Avoid more constraints in result if trying views --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 081659fe36f4..b06836222f23 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4447,12 +4447,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def argHasDefault = hasDefaultParams && !defaultArg.isEmpty def canProfitFromMoreConstraints = + !ctx.mode.is(Mode.ImplicitExploration) + && { arg.tpe.isInstanceOf[AmbiguousImplicits] // Ambiguity could be decided by more constraints || !isFullyDefined(formal, ForceDegree.none) && !argHasDefault // More context might constrain type variables which could make implicit scope larger. // But in this case we should search with additional arguments typed only if there // is no default argument. + } // Try to constrain the result using `pt1`, but back out if a BadTyperStateAssertion // is thrown. TODO Find out why the bad typer state arises and prevent it. The try-catch @@ -4560,9 +4563,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else val app = cpy.Apply(tree)(untpd.TypedSplice(tree), namedArgs) // old-style implicit needs to be marked using so that implicits are searched + /* val needsUsing = wtp.isImplicitMethod || wtp.match case MethodType(ContextBoundParamName(_) :: _) => sourceVersion.isAtLeast(`3.4`) case _ => false + */ + val needsUsing = true //wtp.isImplicitMethod is asserted at beginning of method if needsUsing then app.setApplyKind(ApplyKind.Using) typr.println(i"try with default implicit args $app") // If the retyped tree still has an error type and is an `Apply`