Skip to content

typedSelect refactorings #11037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -645,8 +645,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
case TypSplice(tree) =>
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
case tree: Applications.ExtMethodApply =>
toText(tree.app) ~ Str("(ext method apply)").provided(printDebug)
case Thicket(trees) =>
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
case MacroTree(call) =>
Expand Down
17 changes: 0 additions & 17 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -196,23 +196,6 @@ object Applications {
def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(using Context): Tree =
if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree

abstract class AppProxy(implicit @constructorOnly src: SourceFile) extends ProxyTree {
def app: Tree
override def span = app.span

def forwardTo = app
def canEqual(that: Any): Boolean = app.canEqual(that)
def productArity: Int = app.productArity
def productElement(n: Int): Any = app.productElement(n)
}

/** A wrapper indicating that its argument is an application of an extension method.
*/
class ExtMethodApply(val app: Tree)(implicit @constructorOnly src: SourceFile) extends AppProxy:
overwriteType(app.tpe)
// ExtMethodApply always has wildcard type in order not to prompt any further adaptations
// such as eta expansion before the method is fully applied.

/** Find reference to default parameter getter for parameter #n in current
* parameter list, or NoType if none was found
*/
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,11 @@ object Checking {
else "Cannot override non-inline parameter with an inline parameter",
p1.srcPos)

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

trait Checking {
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Dynamic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,21 @@ import ErrorReporting._
import reporting._

object Dynamic {
def isDynamicMethod(name: Name): Boolean =
private def isDynamicMethod(name: Name): Boolean =
name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed

/** Is `tree` a reference over `Dynamic` that should be expanded to a
* dyanmic `applyDynamic`, `selectDynamic`, `updateDynamic`, or `applyDynamicNamed` call?
*/
def isDynamicExpansion(tree: untpd.RefTree)(using Context): Boolean =
isDynamicMethod(tree.name)
|| tree.match
case Select(Apply(fun: untpd.RefTree, _), nme.apply)
if defn.isContextFunctionClass(fun.symbol.owner) =>
isDynamicExpansion(fun)
case Select(qual, nme.apply) =>
isDynamicMethod(qual.symbol.name) && tree.span.isSynthetic
case _ => false
}

object DynamicUnapply {
Expand Down
82 changes: 42 additions & 40 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,13 @@ object Implicits:
}

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

/** A failed search */
Expand Down Expand Up @@ -491,12 +492,14 @@ object Implicits:
class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: Tree) extends SearchFailureType {
def explanation(using Context): String =
em"both ${err.refStr(alt1.ref)} and ${err.refStr(alt2.ref)} $qualify"
override def whyNoConversion(using Context): String = {
val what = if (expectedType.isInstanceOf[SelectionProto]) "extension methods" else "conversions"
i"""
|Note that implicit $what cannot be applied because they are ambiguous;
|$explanation"""
}
override def whyNoConversion(using Context): String =
if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then
""
else
val what = if (expectedType.isInstanceOf[SelectionProto]) "extension methods" else "conversions"
i"""
|Note that implicit $what cannot be applied because they are ambiguous;
|$explanation"""
}

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

inferred match {
case SearchSuccess(tree, ref, _)
if isOldStyleFunctionConversion(ref.underlying) && !tree.isInstanceOf[Applications.ExtMethodApply] =>
report.migrationWarning(
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.",
from
)
case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) =>
report.migrationWarning(
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.",
from
)
case _ =>
}

Expand All @@ -829,7 +831,7 @@ trait Implicits:
*/
def inferImplicitArg(formal: Type, span: Span)(using Context): Tree =
inferImplicit(formal, EmptyTree, span) match
case SearchSuccess(arg, _, _) => arg
case SearchSuccess(arg, _, _, _) => arg
case fail @ SearchFailure(failed) =>
if fail.isAmbiguous then failed
else
Expand Down Expand Up @@ -937,7 +939,6 @@ trait Implicits:
case Select(qual, _) => apply(x, qual)
case Apply(fn, _) => apply(x, fn)
case TypeApply(fn, _) => apply(x, fn)
case tree: Applications.AppProxy => apply(x, tree.app)
case _: This => false
case _ => foldOver(x, tree)
}
Expand Down Expand Up @@ -1026,13 +1027,23 @@ trait Implicits:
pt, locked)
}
pt match
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) if cand.isExtension =>
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>

def tryExtension(using Context) =
extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType)
if cand.isConversion then

def tryConversionForSelection(using Context) =
val converted = tryConversion
if !ctx.reporter.hasErrors && !selProto.isMatchedBy(converted.tpe) 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)
converted

if cand.isExtension && cand.isConversion then
val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState()
val extensionResult = tryExtension(using extensionCtx)
val conversionResult = tryConversion(using conversionCtx)
val conversionResult = tryConversionForSelection(using conversionCtx)
if !extensionCtx.reporter.hasErrors then
extensionCtx.typerState.commit()
if !conversionCtx.reporter.hasErrors then
Expand All @@ -1041,7 +1052,8 @@ trait Implicits:
else
conversionCtx.typerState.commit()
conversionResult
else tryExtension
else if cand.isExtension then tryExtension
else tryConversionForSelection
case _ =>
tryConversion
}
Expand All @@ -1060,10 +1072,7 @@ trait Implicits:
SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument)))
}
else
val returned =
if (cand.isExtension) Applications.ExtMethodApply(adapted)
else adapted
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt)
}

/** An implicit search; parameters as in `inferImplicit` */
Expand Down Expand Up @@ -1126,20 +1135,13 @@ trait Implicits:
case alt1: SearchSuccess =>
var diff = compareAlternatives(alt1, alt2)
assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank`
if diff == 0 then
if diff == 0 && alt1.isExtension && alt2.isExtension then
// Fall back: if both results are extension method applications,
// compare the extension methods instead of their wrappers.
object extMethodApply:
def unapply(t: Tree): Option[Type] = t match
case t: Applications.ExtMethodApply => Some(methPart(stripApply(t.app)).tpe)
case _ => None
end extMethodApply

(alt1.tree, alt2.tree) match
case (extMethodApply(ref1: TermRef), extMethodApply(ref2: TermRef)) =>
diff = compare(ref1, ref2)
def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe
(stripExtension(alt1), stripExtension(alt2)) match
case (ref1: TermRef, ref2: TermRef) => diff = compare(ref1, ref2)
case _ =>

if diff < 0 then alt2
else if diff > 0 then alt1
else SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument), span)
Expand Down Expand Up @@ -1565,7 +1567,7 @@ final class SearchRoot extends SearchHistory:
else
result match {
case failure: SearchFailure => failure
case success @ SearchSuccess(tree, _, _) =>
case success: SearchSuccess =>
import tpd._

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

val pruned = prune(List(tree), implicitDictionary.map(_._2).toList, Nil)
val pruned = prune(List(success.tree), implicitDictionary.map(_._2).toList, Nil)
myImplicitDictionary = null
if (pruned.isEmpty) result
else if (pruned.exists(_._2 == EmptyTree)) NoMatchingImplicitsFailure
Expand Down Expand Up @@ -1645,7 +1647,7 @@ final class SearchRoot extends SearchHistory:
case tree => tree
})

val res = resMap(tree)
val res = resMap(success.tree)

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

Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ object Inferencing {
result
}

/** Try to fully define `tp`. Return whether constraint has changed.
* Any changed constraint is kept.
*/
def canDefineFurther(tp: Type)(using Context): Boolean =
val prevConstraint = ctx.typerState.constraint
isFullyDefined(tp, force = ForceDegree.all)
&& (ctx.typerState.constraint ne prevConstraint)

/** The fully defined type, where all type variables are forced.
* Throws an error if type contains wildcards.
*/
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import transform.SymUtils._
import Contexts._
import Names.{Name, TermName}
import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName, BodyRetainerName}
import ProtoTypes.selectionProto
import ProtoTypes.shallowSelectionProto
import Annotations.Annotation
import SymDenotations.SymDenotation
import Inferencing.isFullyDefined
Expand Down Expand Up @@ -1240,7 +1240,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {

override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
assert(tree.hasType, tree)
val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this))
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this))
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
val resMaybeReduced = constToLiteral(reducer.reduceProjection(resNoReduce))
if (resNoReduce ne resMaybeReduced)
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,11 @@ object ProtoTypes {
/** Create a selection proto-type, but only one level deep;
* treat constructors specially
*/
def selectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
def shallowSelectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType =
if (name.isConstructorName) WildcardType
else tp match {
else tp match
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true)
}

/** A prototype for expressions [] that are in some unspecified selection operation
*
Expand Down
Loading