Skip to content

Refine import implied rules to smooth migration #6041

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 9 commits into from
Mar 27, 2019
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: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ object desugar {
if (enumCases.isEmpty)
ctx.error("Enumerations must constain at least one case", namePos)
val enumCompanionRef = new TermRefTree()
val enumImport = Import(impliedOnly = false, enumCompanionRef, enumCases.flatMap(caseIds))
val enumImport = Import(importImplied = false, enumCompanionRef, enumCases.flatMap(caseIds))
(enumImport :: enumStats, enumCases, enumCompanionRef)
}
else (stats, Nil, EmptyTree)
Expand Down
14 changes: 7 additions & 7 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ object Trees {
* where a selector is either an untyped `Ident`, `name` or
* an untyped thicket consisting of `name` and `rename`.
*/
case class Import[-T >: Untyped] private[ast] (impliedOnly: Boolean, expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile)
case class Import[-T >: Untyped] private[ast] (importImplied: Boolean, expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] {
type ThisTree[-T >: Untyped] = Import[T]
}
Expand Down Expand Up @@ -1140,9 +1140,9 @@ object Trees {
case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (derived eq tree.derived) && (self eq tree.self) && (body eq tree.unforcedBody) => tree
case tree => finalize(tree, untpd.Template(constr, parents, derived, self, body)(sourceFile(tree)))
}
def Import(tree: Tree)(impliedOnly: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match {
case tree: Import if (impliedOnly == tree.impliedOnly) && (expr eq tree.expr) && (selectors eq tree.selectors) => tree
case _ => finalize(tree, untpd.Import(impliedOnly, expr, selectors)(sourceFile(tree)))
def Import(tree: Tree)(importImplied: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match {
case tree: Import if (importImplied == tree.importImplied) && (expr eq tree.expr) && (selectors eq tree.selectors) => tree
case _ => finalize(tree, untpd.Import(importImplied, expr, selectors)(sourceFile(tree)))
}
def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = tree match {
case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree
Expand Down Expand Up @@ -1283,8 +1283,8 @@ object Trees {
cpy.TypeDef(tree)(name, transform(rhs))
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body))
case Import(impliedOnly, expr, selectors) =>
cpy.Import(tree)(impliedOnly, transform(expr), selectors)
case Import(importImplied, expr, selectors) =>
cpy.Import(tree)(importImplied, transform(expr), selectors)
case PackageDef(pid, stats) =>
cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx))
case Annotated(arg, annot) =>
Expand Down Expand Up @@ -1403,7 +1403,7 @@ object Trees {
this(x, rhs)
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
this(this(this(this(x, constr), parents), self), tree.body)
case Import(impliedOnly, expr, selectors) =>
case Import(importImplied, expr, selectors) =>
this(x, expr)
case PackageDef(pid, stats) =>
this(this(x, pid), stats)(localCtx)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Block(cdef :: Nil, New(cls.typeRef, Nil))
}

def Import(impliedOnly: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import =
ta.assignType(untpd.Import(impliedOnly, expr, selectors), ctx.newImportSymbol(ctx.owner, expr))
def Import(importImplied: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import =
ta.assignType(untpd.Import(importImplied, expr, selectors), ctx.newImportSymbol(ctx.owner, expr))

def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef =
ta.assignType(untpd.PackageDef(pid, stats), pid)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template =
if (derived.isEmpty) new Template(constr, parents, self, body)
else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length)
def Import(impliedOnly: Boolean, expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(impliedOnly, expr, selectors)
def Import(importImplied: Boolean, expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(importImplied, expr, selectors)
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ trait ConstraintHandling[AbstractContext] {

val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
val equalBounds = isUpper && (lo eq bound) || !isUpper && (bound eq hi)
if (equalBounds && !bound.existsPart(_.isInstanceOf[WildcardType])) {
if (equalBounds &&
!bound.existsPart(bp => bp.isInstanceOf[WildcardType] || (bp eq param))) {
// The narrowed bounds are equal and do not contain wildcards,
// so we can remove `param` from the constraint.
// (Handling wildcards requires choosing a bound, but we don't know which
Expand Down
54 changes: 27 additions & 27 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,11 @@ object Contexts {
/** The current type comparer. This ones updates itself automatically for
* each new context.
*/
private[this] var _typeComparer: TypeComparer = null
protected def typeComparer_=(typeComparer: TypeComparer): Unit = {
assert(typeComparer.ctx eq this)
_typeComparer = typeComparer
}
final def typeComparer: TypeComparer = {
if (_typeComparer == null) _typeComparer = outer.typeComparer.copyIn(this)
private[this] var _typeComparer: TypeComparer = _
protected def typeComparer_=(typeComparer: TypeComparer): Unit = _typeComparer = typeComparer
def typeComparer: TypeComparer = {
if (_typeComparer.ctx ne this)
_typeComparer = _typeComparer.copyIn(this)
_typeComparer
}

Expand Down Expand Up @@ -406,7 +404,7 @@ object Contexts {
case _ => None
}
ctx.fresh.setImportInfo(
new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt, imp.impliedOnly))
new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt, imp.importImplied))
}

/** Does current phase use an erased types interpretation? */
Expand All @@ -422,29 +420,31 @@ object Contexts {
def useColors: Boolean =
base.settings.color.value == "always"

protected def init(outer: Context): this.type = {
protected def init(outer: Context, origin: Context): this.type = {
util.Stats.record("Context.fresh")
_outer = outer
_period = outer.period
_mode = outer.mode
_owner = outer.owner
_tree = outer.tree
_scope = outer.scope
_typerState = outer.typerState
_typeAssigner = outer.typeAssigner
_importInfo = outer.importInfo
_gadt = outer.gadt
_searchHistory = outer.searchHistory
_source = outer.source
_moreProperties = outer.moreProperties
_store = outer.store
_period = origin.period
_mode = origin.mode
_owner = origin.owner
_tree = origin.tree
_scope = origin.scope
_typerState = origin.typerState
_typeAssigner = origin.typeAssigner
_importInfo = origin.importInfo
_gadt = origin.gadt
_searchHistory = origin.searchHistory
_typeComparer = origin.typeComparer
_source = origin.source
_moreProperties = origin.moreProperties
_store = origin.store
this
}

/** A fresh clone of this context. */
def fresh: FreshContext = {
util.Stats.record("Context.fresh")
new FreshContext(base).init(this)
}
/** A fresh clone of this context embedded in this context. */
def fresh: FreshContext = freshOver(this)

/** A fresh clone of this context embedded in the specified `outer` context. */
def freshOver(outer: Context): FreshContext = new FreshContext(base).init(outer, this)

final def withOwner(owner: Symbol): Context =
if (owner ne this.owner) fresh.setOwner(owner) else this
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,7 @@ object Mode {

/** We are synthesizing the receiver of an extension method */
val SynthesizeExtMethodReceiver: Mode = newMode(23, "SynthesizeExtMethodReceiver")

/** Are we trying to find a hidden implicit? */
val FindHiddenImplicits: Mode = newMode(24, "FindHiddenImplicits")
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,10 @@ class TreePickler(pickler: TastyPickler) {
}
pickleStats(tree.constr :: rest)
}
case Import(impliedOnly, expr, selectors) =>
case Import(importImplied, expr, selectors) =>
writeByte(IMPORT)
withLength {
if (impliedOnly) writeByte(IMPLIED)
if (importImplied) writeByte(IMPLIED)
pickleTree(expr)
pickleSelectors(selectors)
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -954,10 +954,10 @@ class TreeUnpickler(reader: TastyReader,
assert(sourcePathAt(start).isEmpty)
readByte()
readEnd()
val impliedOnly = nextByte == IMPLIED
if (impliedOnly) readByte()
val importImplied = nextByte == IMPLIED
if (importImplied) readByte()
val expr = readTerm()
setSpan(start, Import(impliedOnly, expr, readSelectors()))
setSpan(start, Import(importImplied, expr, readSelectors()))
}

def readSelectors()(implicit ctx: Context): List[untpd.Tree] = nextByte match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
val to = untpd.Ident(toName)
if (toName.isEmpty) from else untpd.Thicket(from, untpd.Ident(toName))
})
Import(impliedOnly = false, expr, selectors)
Import(importImplied = false, expr, selectors)

case TEMPLATEtree =>
setSym()
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ object JavaParsers {
}

def importCompanionObject(cdef: TypeDef): Tree =
Import(impliedOnly = false, Ident(cdef.name.toTermName).withSpan(NoSpan), Ident(nme.WILDCARD) :: Nil)
Import(importImplied = false, Ident(cdef.name.toTermName).withSpan(NoSpan), Ident(nme.WILDCARD) :: Nil)

// Importing the companion object members cannot be done uncritically: see
// ticket #2377 wherein a class contains two static inner classes, each of which
Expand Down Expand Up @@ -662,7 +662,7 @@ object JavaParsers {
// case nme.WILDCARD => Pair(ident, Ident(null) withPos Span(-1))
// case _ => Pair(ident, ident)
// }
val imp = atSpan(start) { Import(impliedOnly = false, qual, List(ident)) }
val imp = atSpan(start) { Import(importImplied = false, qual, List(ident)) }
imp :: Nil
}
}
Expand Down
16 changes: 8 additions & 8 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2222,9 +2222,9 @@ object Parsers {
*/
def importClause(): List[Tree] = {
val offset = accept(IMPORT)
val impliedOnly = in.token == IMPLIED
if (impliedOnly) in.nextToken()
commaSeparated(importExpr(impliedOnly)) match {
val importImplied = in.token == IMPLIED
if (importImplied) in.nextToken()
commaSeparated(importExpr(importImplied)) match {
case t :: rest =>
// The first import should start at the start offset of the keyword.
val firstPos =
Expand All @@ -2237,11 +2237,11 @@ object Parsers {

/** ImportExpr ::= StableId `.' (id | `_' | ImportSelectors)
*/
def importExpr(impliedOnly: Boolean): () => Import = {
def importExpr(importImplied: Boolean): () => Import = {

val handleImport: Tree => Tree = { tree: Tree =>
if (in.token == USCORE) Import(impliedOnly, tree, importSelector() :: Nil)
else if (in.token == LBRACE) Import(impliedOnly, tree, inBraces(importSelectors()))
if (in.token == USCORE) Import(importImplied, tree, importSelector() :: Nil)
else if (in.token == LBRACE) Import(importImplied, tree, inBraces(importSelectors()))
else tree
}

Expand All @@ -2250,10 +2250,10 @@ object Parsers {
imp
case sel @ Select(qual, name) =>
val selector = atSpan(pointOffset(sel)) { Ident(name) }
cpy.Import(sel)(impliedOnly, qual, selector :: Nil)
cpy.Import(sel)(importImplied, qual, selector :: Nil)
case t =>
accept(DOT)
Import(impliedOnly, t, Ident(nme.WILDCARD) :: Nil)
Import(importImplied, t, Ident(nme.WILDCARD) :: Nil)
}
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
typeDefText(tparamsTxt, optText(rhs)(" = " ~ _))
}
recur(rhs, "")
case Import(impliedOnly, expr, selectors) =>
case Import(importImplied, expr, selectors) =>
def selectorText(sel: Tree): Text = sel match {
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
case _ => toTextGlobal(sel)
Expand All @@ -496,7 +496,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case id :: Nil => toText(id)
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
}
keywordText("import ") ~ (keywordText("implied ") provided impliedOnly) ~
keywordText("import ") ~ (keywordText("implied ") provided importImplied) ~
toTextLocal(expr) ~ "." ~ selectorsText
case packageDef: PackageDef =>
packageDefText(packageDef)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,10 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
tree match {
case Match(selector, _) =>
addPatMatDependency(selector.tpe)
case Import(impliedOnly, expr, selectors) =>
case Import(importImplied, expr, selectors) =>
def lookupImported(name: Name) = {
val sym = expr.tpe.member(name).symbol
if (sym.is(Implied) == impliedOnly) sym else NoSymbol
if (sym.is(Implied) == importImplied) sym else NoSymbol
}
def addImported(name: Name) = {
// importing a name means importing both a term and a type (if they exist)
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
case _ => None
}

def Import_impliedOnly(self: Import): Boolean = self.impliedOnly
def Import_implied(self: Import): Boolean = self.importImplied
def Import_expr(self: Import)(implicit ctx: Context): Tree = self.expr
def Import_selectors(self: Import)(implicit ctx: Context): List[ImportSelector] = self.selectors

def Import_apply(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import =
withDefaultPos(ctx => tpd.Import(impliedOnly, expr, selectors)(ctx))
def Import_apply(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import =
withDefaultPos(ctx => tpd.Import(importImplied, expr, selectors)(ctx))

def Import_copy(original: Import)(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import =
tpd.cpy.Import(original)(impliedOnly, expr, selectors)
def Import_copy(original: Import)(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import =
tpd.cpy.Import(original)(importImplied, expr, selectors)

type Definition = tpd.Tree

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ trait Checking {
val cases =
for (stat <- impl.body if isCase(stat))
yield untpd.Ident(stat.symbol.name.toTermName)
val caseImport: Import = Import(impliedOnly = false, ref(cdef.symbol), cases)
val caseImport: Import = Import(importImplied = false, ref(cdef.symbol), cases)
val caseCtx = enumCtx.importContext(caseImport, caseImport.symbol)
for (stat <- impl.body) checkCaseOrDefault(stat, caseCtx)
case _ =>
Expand Down
29 changes: 27 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ object Implicits {

abstract class SearchFailureType extends ErrorType {
def expectedType: Type
protected def argument: Tree
def argument: Tree

/** A "massaging" function for displayed types to give better info in error diagnostics */
def clarify(tp: Type)(implicit ctx: Context): Type = tp
Expand Down Expand Up @@ -822,6 +822,7 @@ trait Implicits { self: Typer =>
}

def missingArgMsg(arg: Tree, pt: Type, where: String)(implicit ctx: Context): String = {

def msg(shortForm: String)(headline: String = shortForm) = arg match {
case arg: Trees.SearchFailureIdent[_] =>
shortForm
Expand All @@ -836,6 +837,7 @@ trait Implicits { self: Typer =>
|But ${tpe.explanation}."""
}
}

def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where"

/** Extract a user defined error message from a symbol `sym`
Expand Down Expand Up @@ -898,7 +900,30 @@ trait Implicits { self: Typer =>
raw,
pt.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
pt.argInfos))
msg(userDefined.getOrElse(em"no implicit argument of type $pt was found${location("for")}"))()

def hiddenImplicitsAddendum: String = arg.tpe match {
case fail: SearchFailureType =>

def hiddenImplicitNote(s: SearchSuccess) =
em"\n\nNote: implied instance ${s.ref.symbol.showLocated} was not considered because it was not imported with an `import implied`."

def FindHiddenImplicitsCtx(ctx: Context): Context =
if (ctx == NoContext) ctx
else ctx.freshOver(FindHiddenImplicitsCtx(ctx.outer)).addMode(Mode.FindHiddenImplicits)

inferImplicit(fail.expectedType, fail.argument, arg.span)(
FindHiddenImplicitsCtx(ctx)) match {
case s: SearchSuccess => hiddenImplicitNote(s)
case f: SearchFailure =>
f.reason match {
case ambi: AmbiguousImplicits => hiddenImplicitNote(ambi.alt1)
case r => ""
}
}
}
msg(userDefined.getOrElse(
em"no implicit argument of type $pt was found${location("for")}"))() ++
hiddenImplicitsAddendum
}
}

Expand Down
Loading