Skip to content

Commit 8c6269d

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 ae9cf23 commit 8c6269d

17 files changed

+315
-286
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
@@ -196,23 +196,6 @@ object Applications {
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
198198

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.
215-
216199
/** Find reference to default parameter getter for parameter #n in current
217200
* parameter list, or NoType if none was found
218201
*/

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

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

651+
def checkConversionsSpecific(to: Type, pos: SrcPos)(using Context): Unit =
652+
if to.isRef(defn.AnyValClass, skipRefined = false)
653+
|| to.isRef(defn.ObjectClass, skipRefined = false)
654+
then
655+
report.error(em"the result of an implicit conversion must be more specific than $to", pos)
651656
}
652657

653658
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
}
@@ -1026,13 +1025,23 @@ trait Implicits:
10261025
pt, locked)
10271026
}
10281027
pt match
1029-
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) if cand.isExtension =>
1028+
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
1029+
10301030
def tryExtension(using Context) =
10311031
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
1032-
if cand.isConversion then
1032+
1033+
def tryConversionForSelection(using Context) =
1034+
val converted = tryConversion
1035+
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) then
1036+
// this check is needed since adapting relative to a `SelectionProto` can succeed
1037+
// even if the term is not a subtype of the `SelectionProto`
1038+
err.typeMismatch(converted, selProto)
1039+
converted
1040+
1041+
if cand.isExtension && cand.isConversion then
10331042
val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState()
10341043
val extensionResult = tryExtension(using extensionCtx)
1035-
val conversionResult = tryConversion(using conversionCtx)
1044+
val conversionResult = tryConversionForSelection(using conversionCtx)
10361045
if !extensionCtx.reporter.hasErrors then
10371046
extensionCtx.typerState.commit()
10381047
if !conversionCtx.reporter.hasErrors then
@@ -1041,7 +1050,8 @@ trait Implicits:
10411050
else
10421051
conversionCtx.typerState.commit()
10431052
conversionResult
1044-
else tryExtension
1053+
else if cand.isExtension then tryExtension
1054+
else tryConversionForSelection
10451055
case _ =>
10461056
tryConversion
10471057
}
@@ -1060,10 +1070,7 @@ trait Implicits:
10601070
SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument)))
10611071
}
10621072
else
1063-
val returned =
1064-
if (cand.isExtension) Applications.ExtMethodApply(adapted)
1065-
else adapted
1066-
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
1073+
SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt)
10671074
}
10681075

10691076
/** An implicit search; parameters as in `inferImplicit` */
@@ -1126,20 +1133,13 @@ trait Implicits:
11261133
case alt1: SearchSuccess =>
11271134
var diff = compareAlternatives(alt1, alt2)
11281135
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
1129-
if diff == 0 then
1136+
if diff == 0 && alt1.isExtension && alt2.isExtension then
11301137
// Fall back: if both results are extension method applications,
11311138
// compare the extension methods instead of their wrappers.
1132-
object extMethodApply:
1133-
def unapply(t: Tree): Option[Type] = t match
1134-
case t: Applications.ExtMethodApply => Some(methPart(stripApply(t.app)).tpe)
1135-
case _ => None
1136-
end extMethodApply
1137-
1138-
(alt1.tree, alt2.tree) match
1139-
case (extMethodApply(ref1: TermRef), extMethodApply(ref2: TermRef)) =>
1140-
diff = compare(ref1, ref2)
1139+
def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe
1140+
(stripExtension(alt1), stripExtension(alt2)) match
1141+
case (ref1: TermRef, ref2: TermRef) => diff = compare(ref1, ref2)
11411142
case _ =>
1142-
11431143
if diff < 0 then alt2
11441144
else if diff > 0 then alt1
11451145
else SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument), span)
@@ -1565,7 +1565,7 @@ final class SearchRoot extends SearchHistory:
15651565
else
15661566
result match {
15671567
case failure: SearchFailure => failure
1568-
case success @ SearchSuccess(tree, _, _) =>
1568+
case success: SearchSuccess =>
15691569
import tpd._
15701570

15711571
// We might have accumulated dictionary entries for by name implicit arguments
@@ -1588,7 +1588,7 @@ final class SearchRoot extends SearchHistory:
15881588
else prune(in.map(_._2) ++ trees, out, in ++ acc)
15891589
}
15901590

1591-
val pruned = prune(List(tree), implicitDictionary.map(_._2).toList, Nil)
1591+
val pruned = prune(List(success.tree), implicitDictionary.map(_._2).toList, Nil)
15921592
myImplicitDictionary = null
15931593
if (pruned.isEmpty) result
15941594
else if (pruned.exists(_._2 == EmptyTree)) NoMatchingImplicitsFailure
@@ -1645,7 +1645,7 @@ final class SearchRoot extends SearchHistory:
16451645
case tree => tree
16461646
})
16471647

1648-
val res = resMap(tree)
1648+
val res = resMap(success.tree)
16491649

16501650
val blk = Block(classDef :: inst :: Nil, res).withSpan(span)
16511651

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
@@ -235,12 +235,11 @@ object ProtoTypes {
235235
/** Create a selection proto-type, but only one level deep;
236236
* treat constructors specially
237237
*/
238-
def selectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
238+
def shallowSelectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
239239
if (name.isConstructorName) WildcardType
240-
else tp match {
240+
else tp match
241241
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
242242
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true)
243-
}
244243

245244
/** A prototype for expressions [] that are in some unspecified selection operation
246245
*

0 commit comments

Comments
 (0)