Skip to content

Commit 9eb843d

Browse files
authored
Avoid more constraints in result if trying views (#24369)
In `canProfitFromMoreConstraints`, balk if `mode.is(ImplicitExploration)`. The failing case was due to the `constrainResult` "up the stack" in `adaptNoArgs`, which drove type comparisons and completions, including a conversion. The `FunProto` context had a console reporter that emitted the errors encountered, not an exploring reporter. The justification is that "more constraints" is motivated by arg inference, so if the arg is already involved in views, further information in the result is not needed, especially at the cost of spurious exploratory errors using a context that doesn't reflect the current mode. Fixes #24192
2 parents 3adf51e + 1001922 commit 9eb843d

File tree

11 files changed

+80
-20
lines changed

11 files changed

+80
-20
lines changed

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,11 +810,11 @@ object Contexts {
810810
final def addMode(mode: Mode): c.type = c.setMode(c.mode | mode)
811811
final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode)
812812

813-
/** Run `op` with a pool-allocated context that has an ExporeTyperState. */
813+
/** Run `op` with a pool-allocated context that has an ExploreTyperState. */
814814
inline def explore[T](inline op: Context ?=> T)(using Context): T =
815815
exploreInFreshCtx(op)
816816

817-
/** Run `op` with a pool-allocated FreshContext that has an ExporeTyperState. */
817+
/** Run `op` with a pool-allocated FreshContext that has an ExploreTyperState. */
818818
inline def exploreInFreshCtx[T](inline op: FreshContext ?=> T)(using Context): T =
819819
val pool = ctx.base.exploreContextPool
820820
val nestedCtx = pool.next()

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2312,7 +2312,7 @@ object Types extends TypeUtils {
23122312

23132313
/** A trait for proto-types, used as expected types in typer */
23142314
trait ProtoType extends Type {
2315-
def isMatchedBy(tp: Type, keepConstraint: Boolean = false)(using Context): Boolean
2315+
def isMatchedBy(tp: Type, keepConstraint: Boolean)(using Context): Boolean
23162316
def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T
23172317
def map(tm: TypeMap)(using Context): ProtoType
23182318

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,7 @@ trait Implicits:
12071207

12081208
def tryConversionForSelection(using Context) =
12091209
val converted = tryConversion
1210-
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) then
1210+
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe, keepConstraint = false) then
12111211
// this check is needed since adapting relative to a `SelectionProto` can succeed
12121212
// even if the term is not a subtype of the `SelectionProto`
12131213
err.typeMismatch(converted, selProto)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ trait ImportSuggestions:
185185
// To regain precision, test both sides separately.
186186
test(ViewProto(argType, rt1)) || test(ViewProto(argType, rt2))
187187
case pt: ViewProto =>
188-
pt.isMatchedBy(ref)
188+
pt.isMatchedBy(ref, keepConstraint = false)
189189
case _ =>
190190
normalize(ref, pt) <:< pt
191191
test(pt)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ object ProtoTypes {
133133

134134
constFoldException(pt) || {
135135
if Inlines.isInlineable(meth) then
136-
// Stricter behavisour in 3.4+: do not apply `wildApprox` to non-transparent inlines
136+
// Stricter behaviour in 3.4+: do not apply `wildApprox` to non-transparent inlines
137137
// unless their return type is a MatchType. In this case there's no reason
138138
// not to constrain type variables in the expected type. For transparent inlines
139139
// we do not want to constrain type variables in the expected type since the

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4174,7 +4174,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
41744174
Some(adapt(tree, pt, locked))
41754175
else
41764176
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false, tree.nameSpan)
4177-
if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then
4177+
if selProto.isMatchedBy(qual.tpe, keepConstraint = false) || tree.hasAttachment(InsertedImplicitOnQualifier)
4178+
then
41784179
None
41794180
else
41804181
tryEither {
@@ -4472,12 +4473,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
44724473
def argHasDefault = hasDefaultParams && !defaultArg.isEmpty
44734474

44744475
def canProfitFromMoreConstraints =
4476+
!ctx.mode.is(Mode.ImplicitExploration)
4477+
&& {
44754478
arg.tpe.isInstanceOf[AmbiguousImplicits]
44764479
// Ambiguity could be decided by more constraints
44774480
|| !isFullyDefined(formal, ForceDegree.none) && !argHasDefault
44784481
// More context might constrain type variables which could make implicit scope larger.
44794482
// But in this case we should search with additional arguments typed only if there
44804483
// is no default argument.
4484+
}
44814485

44824486
// Try to constrain the result using `pt1`, but back out if a BadTyperStateAssertion
44834487
// is thrown. TODO Find out why the bad typer state arises and prevent it. The try-catch
@@ -4491,7 +4495,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
44914495
arg.tpe match
44924496
case failed: SearchFailureType if canProfitFromMoreConstraints =>
44934497
val pt1 = pt.deepenProtoTrans
4494-
if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then
4498+
if (pt1 ne pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then
44954499
return implicitArgs(formals, argIndex, pt1)
44964500
case _ =>
44974501

@@ -4584,11 +4588,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
45844588
issueErrors(tree, args, failureType)
45854589
else
45864590
val app = cpy.Apply(tree)(untpd.TypedSplice(tree), namedArgs)
4587-
// old-style implicit needs to be marked using so that implicits are searched
4588-
val needsUsing = wtp.isImplicitMethod || wtp.match
4589-
case MethodType(ContextBoundParamName(_) :: _) => sourceVersion.isAtLeast(`3.4`)
4590-
case _ => false
4591-
if needsUsing then app.setApplyKind(ApplyKind.Using)
4591+
// avoid warning if method is old-style implicit that context bounds will be contextual
4592+
// also required for correctly reporting implicit not found
4593+
app.setApplyKind(ApplyKind.Using)
45924594
typr.println(i"try with default implicit args $app")
45934595
// If the retyped tree still has an error type and is an `Apply`
45944596
// node, we can report the errors for each argument nicely.
@@ -4605,13 +4607,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
46054607
// Reset context in case it was set to a supercall context before.
46064608
// otherwise the invariant for taking another this or super call context is not met.
46074609
// Test case is i20483.scala
4608-
tree match
4609-
case tree: Block =>
4610-
readaptSimplified(tpd.Block(tree.stats, tpd.Apply(tree.expr, args)))
4611-
case tree: NamedArg =>
4612-
readaptSimplified(tpd.NamedArg(tree.name, tpd.Apply(tree.arg, args)))
4613-
case _ =>
4614-
readaptSimplified(tpd.Apply(tree, args))
4610+
val cpy = tree match
4611+
case tree: Block => tpd.Block(tree.stats, tpd.Apply(tree.expr, args))
4612+
case tree: NamedArg => tpd.NamedArg(tree.name, tpd.Apply(tree.arg, args))
4613+
case _ => tpd.Apply(tree, args)
4614+
readaptSimplified(cpy)
46154615
end addImplicitArgs
46164616

46174617
pt.revealIgnored match {

tests/neg/i19594.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@
1818
26 | h(true, 1) // error
1919
| ^^^^^^^^^^
2020
| No given instance of type String was found
21+
-- [E172] Type Error: tests/neg/i19594.scala:31:11 ---------------------------------------------------------------------
22+
31 | competing(true, 42) // error when the default is in the same param list as the missing implicit
23+
| ^^^^^^^^^^^^^^^^^^^
24+
| Can you see me?!

tests/neg/i19594.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ object updated:
2424

2525
def h[A, B](a: A, b: B)(using c: Compare[A, B] = Compare.any, s: String) = ()
2626
h(true, 1) // error
27+
28+
class X
29+
val defx = X()
30+
def competing[A, B](a: A, b: B)(implicit x: X = defx, cmp: Compare[A, B]) = ()
31+
competing(true, 42) // error when the default is in the same param list as the missing implicit

tests/pos/i24192.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import scala.language.implicitConversions
2+
3+
// https://github.com/scala/scala-collection-contrib/blob/main/src/main/scala/scala/collection/decorators/package.scala
4+
object decorators {
5+
trait IsMap[A]
6+
implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): Map[C, map.type] = ???
7+
}
8+
import decorators.mapDecorator // unused, required to reproduce
9+
10+
trait Eq[T]
11+
trait Applicative[F[_]]
12+
given Applicative[Option] = ???
13+
14+
trait Traverse[F[_]]:
15+
// context bound required to reproduce
16+
def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] = ???
17+
18+
object Traverse:
19+
def apply[F[_]]: Traverse[F] = ???
20+
21+
trait Segment[Element: Eq]
22+
23+
case class MergeResult[Element: Eq] private (segments: Seq[Segment[Element]]):
24+
def thisFailsToCompile(): Option[MergeResult[Element]] =
25+
Traverse[Seq]
26+
.sequence(Seq.empty[Option[Segment[Element]]])
27+
.map(MergeResult.apply) // no error

tests/warn/i19506.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//> using options -source 3.4
2+
3+
trait Reader[T]
4+
def read[T: Reader](s: String, trace: Boolean = false)(implicit i: Int = 42): T = ???
5+
6+
// if apply kind is not using when retrying to insert the default arg, it will warn:
7+
// Context bounds will map to context parameters.
8+
// A `using` clause is needed to pass explicit arguments to them.
9+
// The method to invoke (read) might have been compiled under 3.3,
10+
// or (as above) the contextual parameter may have been merged with an existing implicit param list.
11+
def Test =
12+
implicit def reader: Reader[Object] = new Reader[Object]{}
13+
read[Object]("") // no warn

0 commit comments

Comments
 (0)