Skip to content

Commit 175123a

Browse files
committed
Refactor typedSelect
The aim of the refactoring is to be clearer and to be able to drop ExtMethodApply. Two main strategies: 1. Move some code out of adapt and assignType into typedSelect. 2. Concentrate extension method expansion and conversions for selections in a method `tryExtensionOrConversion. The method is called from `typedSelect` and `tryInsertImplicitOnQualifier`. The aim of the refactoring is to move all select-dependent adaptations and re-tries into typedSelect. Right now some are in adapt and some are in assignType. This is awkward since it means that - we do too much in TypeAssigner. Sine it aims to be minimal TypeAssigner should have no business doing adaptations and retries. - we do some adaptations too early in adpt. This means we need the kludge of wrapping trees in ExtMethodApply which causes #8182, among others. Fixes #8182
1 parent 5672999 commit 175123a

18 files changed

+319
-287
lines changed

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
645645
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
646646
case TypSplice(tree) =>
647647
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
648-
case tree: Applications.ExtMethodApply =>
649-
toText(tree.app) ~ Str("(ext method apply)").provided(printDebug)
650648
case Thicket(trees) =>
651649
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
652650
case MacroTree(call) =>

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

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -195,23 +195,6 @@ object Applications {
195195

196196
def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(using Context): Tree =
197197
if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree
198-
199-
abstract class AppProxy(implicit @constructorOnly src: SourceFile) extends ProxyTree {
200-
def app: Tree
201-
override def span = app.span
202-
203-
def forwardTo = app
204-
def canEqual(that: Any): Boolean = app.canEqual(that)
205-
def productArity: Int = app.productArity
206-
def productElement(n: Int): Any = app.productElement(n)
207-
}
208-
209-
/** A wrapper indicating that its argument is an application of an extension method.
210-
*/
211-
class ExtMethodApply(val app: Tree)(implicit @constructorOnly src: SourceFile) extends AppProxy:
212-
overwriteType(app.tpe)
213-
// ExtMethodApply always has wildcard type in order not to prompt any further adaptations
214-
// such as eta expansion before the method is fully applied.
215198
}
216199

217200
trait Applications extends Compatibility {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,14 @@ object Checking {
648648
else "Cannot override non-inline parameter with an inline parameter",
649649
p1.srcPos)
650650

651+
def checkConversionsSpecific(from: Type, to: Type, pos: SrcPos)(using Context): Unit =
652+
def fail(part: String, tp: Type) =
653+
report.error(em"the $part of an implicit conversion must be more specific than $tp", pos)
654+
if to.isRef(defn.AnyValClass, skipRefined = false)
655+
|| to.isRef(defn.ObjectClass, skipRefined = false)
656+
then fail("result", to)
657+
if from.isBottomTypeAfterErasure
658+
then fail("argument", from)
651659
}
652660

653661
trait Checking {

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,21 @@ import ErrorReporting._
1919
import reporting._
2020

2121
object Dynamic {
22-
def isDynamicMethod(name: Name): Boolean =
22+
private def isDynamicMethod(name: Name): Boolean =
2323
name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
24+
25+
/** Is `tree` a reference over `Dynamic` that should be expanded to a
26+
* dyanmic `applyDynamic`, `selectDynamic`, `updateDynamic`, or `applyDynamicNamed` call?
27+
*/
28+
def isDynamicExpansion(tree: untpd.RefTree)(using Context): Boolean =
29+
isDynamicMethod(tree.name)
30+
|| tree.match
31+
case Select(Apply(fun: untpd.RefTree, _), nme.apply)
32+
if defn.isContextFunctionClass(fun.symbol.owner) =>
33+
isDynamicExpansion(fun)
34+
case Select(qual, nme.apply) =>
35+
isDynamicMethod(qual.symbol.name) && tree.span.isSynthetic
36+
case _ => false
2437
}
2538

2639
object DynamicUnapply {

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

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -396,12 +396,13 @@ object Implicits:
396396
}
397397

398398
/** A successful search
399-
* @param tree The typed tree that needs to be inserted
400-
* @param ref The implicit reference that succeeded
401-
* @param level The level where the reference was found
399+
* @param tree The typed tree that needs to be inserted
400+
* @param ref The implicit reference that succeeded
401+
* @param level The level where the reference was found
402+
* @param isExtension Whether the result is an extension method application
402403
* @param tstate The typer state to be committed if this alternative is chosen
403404
*/
404-
case class SearchSuccess(tree: Tree, ref: TermRef, level: Int)(val tstate: TyperState, val gstate: GadtConstraint)
405+
case class SearchSuccess(tree: Tree, ref: TermRef, level: Int, isExtension: Boolean = false)(val tstate: TyperState, val gstate: GadtConstraint)
405406
extends SearchResult with RefAndLevel with Showable
406407

407408
/** A failed search */
@@ -802,12 +803,11 @@ trait Implicits:
802803
val inferred = inferImplicit(adjust(to), from, from.span)
803804

804805
inferred match {
805-
case SearchSuccess(tree, ref, _)
806-
if isOldStyleFunctionConversion(ref.underlying) && !tree.isInstanceOf[Applications.ExtMethodApply] =>
807-
report.migrationWarning(
808-
i"The conversion ${ref} will not be applied implicitly here in Scala 3 because only implicit methods and instances of Conversion class will continue to work as implicit views.",
809-
from
810-
)
806+
case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) =>
807+
report.migrationWarning(
808+
i"The conversion ${ref} will not be applied implicitly here in Scala 3 because only implicit methods and instances of Conversion class will continue to work as implicit views.",
809+
from
810+
)
811811
case _ =>
812812
}
813813

@@ -829,7 +829,7 @@ trait Implicits:
829829
*/
830830
def inferImplicitArg(formal: Type, span: Span)(using Context): Tree =
831831
inferImplicit(formal, EmptyTree, span) match
832-
case SearchSuccess(arg, _, _) => arg
832+
case SearchSuccess(arg, _, _, _) => arg
833833
case fail @ SearchFailure(failed) =>
834834
if fail.isAmbiguous then failed
835835
else
@@ -937,7 +937,6 @@ trait Implicits:
937937
case Select(qual, _) => apply(x, qual)
938938
case Apply(fn, _) => apply(x, fn)
939939
case TypeApply(fn, _) => apply(x, fn)
940-
case tree: Applications.AppProxy => apply(x, tree.app)
941940
case _: This => false
942941
case _ => foldOver(x, tree)
943942
}
@@ -1021,13 +1020,23 @@ trait Implicits:
10211020
pt, locked)
10221021
}
10231022
pt match
1024-
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) if cand.isExtension =>
1023+
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
1024+
10251025
def tryExtension(using Context) =
10261026
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
1027-
if cand.isConversion then
1027+
1028+
def tryConversionForSelection(using Context) =
1029+
val converted = tryConversion
1030+
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) then
1031+
// this check is needed since adapting relative to a `SelectionProto` can succeed
1032+
// even if the term is not a subtype of the `SelectionProto`
1033+
err.typeMismatch(converted, selProto)
1034+
converted
1035+
1036+
if cand.isExtension && cand.isConversion then
10281037
val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState()
10291038
val extensionResult = tryExtension(using extensionCtx)
1030-
val conversionResult = tryConversion(using conversionCtx)
1039+
val conversionResult = tryConversionForSelection(using conversionCtx)
10311040
if !extensionCtx.reporter.hasErrors then
10321041
extensionCtx.typerState.commit()
10331042
if !conversionCtx.reporter.hasErrors then
@@ -1036,7 +1045,8 @@ trait Implicits:
10361045
else
10371046
conversionCtx.typerState.commit()
10381047
conversionResult
1039-
else tryExtension
1048+
else if cand.isExtension then tryExtension
1049+
else tryConversionForSelection
10401050
case _ =>
10411051
tryConversion
10421052
}
@@ -1055,10 +1065,7 @@ trait Implicits:
10551065
SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument)))
10561066
}
10571067
else
1058-
val returned =
1059-
if (cand.isExtension) Applications.ExtMethodApply(adapted)
1060-
else adapted
1061-
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
1068+
SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt)
10621069
}
10631070

10641071
/** An implicit search; parameters as in `inferImplicit` */
@@ -1121,20 +1128,13 @@ trait Implicits:
11211128
case alt1: SearchSuccess =>
11221129
var diff = compareAlternatives(alt1, alt2)
11231130
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
1124-
if diff == 0 then
1131+
if diff == 0 && alt1.isExtension && alt2.isExtension then
11251132
// Fall back: if both results are extension method applications,
11261133
// compare the extension methods instead of their wrappers.
1127-
object extMethodApply:
1128-
def unapply(t: Tree): Option[Type] = t match
1129-
case t: Applications.ExtMethodApply => Some(methPart(stripApply(t.app)).tpe)
1130-
case _ => None
1131-
end extMethodApply
1132-
1133-
(alt1.tree, alt2.tree) match
1134-
case (extMethodApply(ref1: TermRef), extMethodApply(ref2: TermRef)) =>
1135-
diff = compare(ref1, ref2)
1134+
def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe
1135+
(stripExtension(alt1), stripExtension(alt2)) match
1136+
case (ref1: TermRef, ref2: TermRef) => diff = compare(ref1, ref2)
11361137
case _ =>
1137-
11381138
if diff < 0 then alt2
11391139
else if diff > 0 then alt1
11401140
else SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument), span)
@@ -1560,7 +1560,7 @@ final class SearchRoot extends SearchHistory:
15601560
else
15611561
result match {
15621562
case failure: SearchFailure => failure
1563-
case success @ SearchSuccess(tree, _, _) =>
1563+
case success: SearchSuccess =>
15641564
import tpd._
15651565

15661566
// We might have accumulated dictionary entries for by name implicit arguments
@@ -1583,7 +1583,7 @@ final class SearchRoot extends SearchHistory:
15831583
else prune(in.map(_._2) ++ trees, out, in ++ acc)
15841584
}
15851585

1586-
val pruned = prune(List(tree), implicitDictionary.map(_._2).toList, Nil)
1586+
val pruned = prune(List(success.tree), implicitDictionary.map(_._2).toList, Nil)
15871587
myImplicitDictionary = null
15881588
if (pruned.isEmpty) result
15891589
else if (pruned.exists(_._2 == EmptyTree)) NoMatchingImplicitsFailure
@@ -1640,7 +1640,7 @@ final class SearchRoot extends SearchHistory:
16401640
case tree => tree
16411641
})
16421642

1643-
val res = resMap(tree)
1643+
val res = resMap(success.tree)
16441644

16451645
val blk = Block(classDef :: inst :: Nil, res).withSpan(span)
16461646

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ object Inferencing {
3838
result
3939
}
4040

41+
def canDefineFurther(tp: Type)(using Context): Boolean =
42+
val prevConstraint = ctx.typerState.constraint
43+
isFullyDefined(tp, force = ForceDegree.all)
44+
&& (ctx.typerState.constraint ne prevConstraint)
45+
4146
/** The fully defined type, where all type variables are forced.
4247
* Throws an error if type contains wildcards.
4348
*/

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import transform.SymUtils._
1616
import Contexts._
1717
import Names.{Name, TermName}
1818
import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName, BodyRetainerName}
19-
import ProtoTypes.selectionProto
19+
import ProtoTypes.shallowSelectionProto
2020
import Annotations.Annotation
2121
import SymDenotations.SymDenotation
2222
import Inferencing.isFullyDefined
@@ -1240,7 +1240,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
12401240

12411241
override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
12421242
assert(tree.hasType, tree)
1243-
val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this))
1243+
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
12441244
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
12451245
val resMaybeReduced = constToLiteral(reducer.reduceProjection(resNoReduce))
12461246
if (resNoReduce ne resMaybeReduced)

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,11 @@ object ProtoTypes {
229229
/** Create a selection proto-type, but only one level deep;
230230
* treat constructors specially
231231
*/
232-
def selectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
232+
def shallowSelectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
233233
if (name.isConstructorName) WildcardType
234-
else tp match {
234+
else tp match
235235
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
236236
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true)
237-
}
238237

239238
/** A prototype for expressions [] that are in some unspecified selection operation
240239
*

0 commit comments

Comments
 (0)