Skip to content

Commit 49ae535

Browse files
authored
Merge pull request #11037 from dotty-staging/fix-#8182
typedSelect refactorings
2 parents 3e85fca + 2dcbb69 commit 49ae535

18 files changed

+327
-284
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: 42 additions & 40 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 */
@@ -491,12 +492,14 @@ object Implicits:
491492
class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: Tree) extends SearchFailureType {
492493
def explanation(using Context): String =
493494
em"both ${err.refStr(alt1.ref)} and ${err.refStr(alt2.ref)} $qualify"
494-
override def whyNoConversion(using Context): String = {
495-
val what = if (expectedType.isInstanceOf[SelectionProto]) "extension methods" else "conversions"
496-
i"""
497-
|Note that implicit $what cannot be applied because they are ambiguous;
498-
|$explanation"""
499-
}
495+
override def whyNoConversion(using Context): String =
496+
if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then
497+
""
498+
else
499+
val what = if (expectedType.isInstanceOf[SelectionProto]) "extension methods" else "conversions"
500+
i"""
501+
|Note that implicit $what cannot be applied because they are ambiguous;
502+
|$explanation"""
500503
}
501504

502505
class MismatchedImplicit(ref: TermRef,
@@ -802,12 +805,11 @@ trait Implicits:
802805
val inferred = inferImplicit(adjust(to), from, from.span)
803806

804807
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-
)
808+
case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) =>
809+
report.migrationWarning(
810+
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.",
811+
from
812+
)
811813
case _ =>
812814
}
813815

@@ -829,7 +831,7 @@ trait Implicits:
829831
*/
830832
def inferImplicitArg(formal: Type, span: Span)(using Context): Tree =
831833
inferImplicit(formal, EmptyTree, span) match
832-
case SearchSuccess(arg, _, _) => arg
834+
case SearchSuccess(arg, _, _, _) => arg
833835
case fail @ SearchFailure(failed) =>
834836
if fail.isAmbiguous then failed
835837
else
@@ -937,7 +939,6 @@ trait Implicits:
937939
case Select(qual, _) => apply(x, qual)
938940
case Apply(fn, _) => apply(x, fn)
939941
case TypeApply(fn, _) => apply(x, fn)
940-
case tree: Applications.AppProxy => apply(x, tree.app)
941942
case _: This => false
942943
case _ => foldOver(x, tree)
943944
}
@@ -1026,13 +1027,23 @@ trait Implicits:
10261027
pt, locked)
10271028
}
10281029
pt match
1029-
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) if cand.isExtension =>
1030+
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
1031+
10301032
def tryExtension(using Context) =
10311033
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
1032-
if cand.isConversion then
1034+
1035+
def tryConversionForSelection(using Context) =
1036+
val converted = tryConversion
1037+
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) then
1038+
// this check is needed since adapting relative to a `SelectionProto` can succeed
1039+
// even if the term is not a subtype of the `SelectionProto`
1040+
err.typeMismatch(converted, selProto)
1041+
converted
1042+
1043+
if cand.isExtension && cand.isConversion then
10331044
val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState()
10341045
val extensionResult = tryExtension(using extensionCtx)
1035-
val conversionResult = tryConversion(using conversionCtx)
1046+
val conversionResult = tryConversionForSelection(using conversionCtx)
10361047
if !extensionCtx.reporter.hasErrors then
10371048
extensionCtx.typerState.commit()
10381049
if !conversionCtx.reporter.hasErrors then
@@ -1041,7 +1052,8 @@ trait Implicits:
10411052
else
10421053
conversionCtx.typerState.commit()
10431054
conversionResult
1044-
else tryExtension
1055+
else if cand.isExtension then tryExtension
1056+
else tryConversionForSelection
10451057
case _ =>
10461058
tryConversion
10471059
}
@@ -1060,10 +1072,7 @@ trait Implicits:
10601072
SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument)))
10611073
}
10621074
else
1063-
val returned =
1064-
if (cand.isExtension) Applications.ExtMethodApply(adapted)
1065-
else adapted
1066-
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
1075+
SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt)
10671076
}
10681077

10691078
/** An implicit search; parameters as in `inferImplicit` */
@@ -1126,20 +1135,13 @@ trait Implicits:
11261135
case alt1: SearchSuccess =>
11271136
var diff = compareAlternatives(alt1, alt2)
11281137
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
1129-
if diff == 0 then
1138+
if diff == 0 && alt1.isExtension && alt2.isExtension then
11301139
// Fall back: if both results are extension method applications,
11311140
// 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)
1141+
def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe
1142+
(stripExtension(alt1), stripExtension(alt2)) match
1143+
case (ref1: TermRef, ref2: TermRef) => diff = compare(ref1, ref2)
11411144
case _ =>
1142-
11431145
if diff < 0 then alt2
11441146
else if diff > 0 then alt1
11451147
else SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument), span)
@@ -1565,7 +1567,7 @@ final class SearchRoot extends SearchHistory:
15651567
else
15661568
result match {
15671569
case failure: SearchFailure => failure
1568-
case success @ SearchSuccess(tree, _, _) =>
1570+
case success: SearchSuccess =>
15691571
import tpd._
15701572

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

1591-
val pruned = prune(List(tree), implicitDictionary.map(_._2).toList, Nil)
1593+
val pruned = prune(List(success.tree), implicitDictionary.map(_._2).toList, Nil)
15921594
myImplicitDictionary = null
15931595
if (pruned.isEmpty) result
15941596
else if (pruned.exists(_._2 == EmptyTree)) NoMatchingImplicitsFailure
@@ -1645,7 +1647,7 @@ final class SearchRoot extends SearchHistory:
16451647
case tree => tree
16461648
})
16471649

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

16501652
val blk = Block(classDef :: inst :: Nil, res).withSpan(span)
16511653

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

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

41+
/** Try to fully define `tp`. Return whether constraint has changed.
42+
* Any changed constraint is kept.
43+
*/
44+
def canDefineFurther(tp: Type)(using Context): Boolean =
45+
val prevConstraint = ctx.typerState.constraint
46+
isFullyDefined(tp, force = ForceDegree.all)
47+
&& (ctx.typerState.constraint ne prevConstraint)
48+
4149
/** The fully defined type, where all type variables are forced.
4250
* Throws an error if type contains wildcards.
4351
*/

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)