Skip to content

Commit 2364270

Browse files
authored
Merge pull request #6041 from dotty-staging/fix-import-implied
Refine `import implied` rules to smooth migration
2 parents 2dbc77c + d077feb commit 2364270

28 files changed

+242
-112
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ object desugar {
396396
if (enumCases.isEmpty)
397397
ctx.error("Enumerations must constain at least one case", namePos)
398398
val enumCompanionRef = new TermRefTree()
399-
val enumImport = Import(impliedOnly = false, enumCompanionRef, enumCases.flatMap(caseIds))
399+
val enumImport = Import(importImplied = false, enumCompanionRef, enumCases.flatMap(caseIds))
400400
(enumImport :: enumStats, enumCases, enumCompanionRef)
401401
}
402402
else (stats, Nil, EmptyTree)

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ object Trees {
751751
* where a selector is either an untyped `Ident`, `name` or
752752
* an untyped thicket consisting of `name` and `rename`.
753753
*/
754-
case class Import[-T >: Untyped] private[ast] (impliedOnly: Boolean, expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile)
754+
case class Import[-T >: Untyped] private[ast] (importImplied: Boolean, expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile)
755755
extends DenotingTree[T] {
756756
type ThisTree[-T >: Untyped] = Import[T]
757757
}
@@ -1140,9 +1140,9 @@ object Trees {
11401140
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
11411141
case tree => finalize(tree, untpd.Template(constr, parents, derived, self, body)(sourceFile(tree)))
11421142
}
1143-
def Import(tree: Tree)(impliedOnly: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match {
1144-
case tree: Import if (impliedOnly == tree.impliedOnly) && (expr eq tree.expr) && (selectors eq tree.selectors) => tree
1145-
case _ => finalize(tree, untpd.Import(impliedOnly, expr, selectors)(sourceFile(tree)))
1143+
def Import(tree: Tree)(importImplied: Boolean, expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match {
1144+
case tree: Import if (importImplied == tree.importImplied) && (expr eq tree.expr) && (selectors eq tree.selectors) => tree
1145+
case _ => finalize(tree, untpd.Import(importImplied, expr, selectors)(sourceFile(tree)))
11461146
}
11471147
def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = tree match {
11481148
case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree
@@ -1283,8 +1283,8 @@ object Trees {
12831283
cpy.TypeDef(tree)(name, transform(rhs))
12841284
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
12851285
cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body))
1286-
case Import(impliedOnly, expr, selectors) =>
1287-
cpy.Import(tree)(impliedOnly, transform(expr), selectors)
1286+
case Import(importImplied, expr, selectors) =>
1287+
cpy.Import(tree)(importImplied, transform(expr), selectors)
12881288
case PackageDef(pid, stats) =>
12891289
cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx))
12901290
case Annotated(arg, annot) =>
@@ -1403,7 +1403,7 @@ object Trees {
14031403
this(x, rhs)
14041404
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
14051405
this(this(this(this(x, constr), parents), self), tree.body)
1406-
case Import(impliedOnly, expr, selectors) =>
1406+
case Import(importImplied, expr, selectors) =>
14071407
this(x, expr)
14081408
case PackageDef(pid, stats) =>
14091409
this(this(x, pid), stats)(localCtx)

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
326326
Block(cdef :: Nil, New(cls.typeRef, Nil))
327327
}
328328

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

332332
def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef =
333333
ta.assignType(untpd.PackageDef(pid, stats), pid)

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
323323
def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template =
324324
if (derived.isEmpty) new Template(constr, parents, self, body)
325325
else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length)
326-
def Import(impliedOnly: Boolean, expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(impliedOnly, expr, selectors)
326+
def Import(importImplied: Boolean, expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(importImplied, expr, selectors)
327327
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
328328
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)
329329

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ trait ConstraintHandling[AbstractContext] {
8585

8686
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
8787
val equalBounds = isUpper && (lo eq bound) || !isUpper && (bound eq hi)
88-
if (equalBounds && !bound.existsPart(_.isInstanceOf[WildcardType])) {
88+
if (equalBounds &&
89+
!bound.existsPart(bp => bp.isInstanceOf[WildcardType] || (bp eq param))) {
8990
// The narrowed bounds are equal and do not contain wildcards,
9091
// so we can remove `param` from the constraint.
9192
// (Handling wildcards requires choosing a bound, but we don't know which

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,11 @@ object Contexts {
151151
/** The current type comparer. This ones updates itself automatically for
152152
* each new context.
153153
*/
154-
private[this] var _typeComparer: TypeComparer = null
155-
protected def typeComparer_=(typeComparer: TypeComparer): Unit = {
156-
assert(typeComparer.ctx eq this)
157-
_typeComparer = typeComparer
158-
}
159-
final def typeComparer: TypeComparer = {
160-
if (_typeComparer == null) _typeComparer = outer.typeComparer.copyIn(this)
154+
private[this] var _typeComparer: TypeComparer = _
155+
protected def typeComparer_=(typeComparer: TypeComparer): Unit = _typeComparer = typeComparer
156+
def typeComparer: TypeComparer = {
157+
if (_typeComparer.ctx ne this)
158+
_typeComparer = _typeComparer.copyIn(this)
161159
_typeComparer
162160
}
163161

@@ -406,7 +404,7 @@ object Contexts {
406404
case _ => None
407405
}
408406
ctx.fresh.setImportInfo(
409-
new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt, imp.impliedOnly))
407+
new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt, imp.importImplied))
410408
}
411409

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

425-
protected def init(outer: Context): this.type = {
423+
protected def init(outer: Context, origin: Context): this.type = {
424+
util.Stats.record("Context.fresh")
426425
_outer = outer
427-
_period = outer.period
428-
_mode = outer.mode
429-
_owner = outer.owner
430-
_tree = outer.tree
431-
_scope = outer.scope
432-
_typerState = outer.typerState
433-
_typeAssigner = outer.typeAssigner
434-
_importInfo = outer.importInfo
435-
_gadt = outer.gadt
436-
_searchHistory = outer.searchHistory
437-
_source = outer.source
438-
_moreProperties = outer.moreProperties
439-
_store = outer.store
426+
_period = origin.period
427+
_mode = origin.mode
428+
_owner = origin.owner
429+
_tree = origin.tree
430+
_scope = origin.scope
431+
_typerState = origin.typerState
432+
_typeAssigner = origin.typeAssigner
433+
_importInfo = origin.importInfo
434+
_gadt = origin.gadt
435+
_searchHistory = origin.searchHistory
436+
_typeComparer = origin.typeComparer
437+
_source = origin.source
438+
_moreProperties = origin.moreProperties
439+
_store = origin.store
440440
this
441441
}
442442

443-
/** A fresh clone of this context. */
444-
def fresh: FreshContext = {
445-
util.Stats.record("Context.fresh")
446-
new FreshContext(base).init(this)
447-
}
443+
/** A fresh clone of this context embedded in this context. */
444+
def fresh: FreshContext = freshOver(this)
445+
446+
/** A fresh clone of this context embedded in the specified `outer` context. */
447+
def freshOver(outer: Context): FreshContext = new FreshContext(base).init(outer, this)
448448

449449
final def withOwner(owner: Symbol): Context =
450450
if (owner ne this.owner) fresh.setOwner(owner) else this

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,7 @@ object Mode {
101101

102102
/** We are synthesizing the receiver of an extension method */
103103
val SynthesizeExtMethodReceiver: Mode = newMode(23, "SynthesizeExtMethodReceiver")
104+
105+
/** Are we trying to find a hidden implicit? */
106+
val FindHiddenImplicits: Mode = newMode(24, "FindHiddenImplicits")
104107
}

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,10 +532,10 @@ class TreePickler(pickler: TastyPickler) {
532532
}
533533
pickleStats(tree.constr :: rest)
534534
}
535-
case Import(impliedOnly, expr, selectors) =>
535+
case Import(importImplied, expr, selectors) =>
536536
writeByte(IMPORT)
537537
withLength {
538-
if (impliedOnly) writeByte(IMPLIED)
538+
if (importImplied) writeByte(IMPLIED)
539539
pickleTree(expr)
540540
pickleSelectors(selectors)
541541
}

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -954,10 +954,10 @@ class TreeUnpickler(reader: TastyReader,
954954
assert(sourcePathAt(start).isEmpty)
955955
readByte()
956956
readEnd()
957-
val impliedOnly = nextByte == IMPLIED
958-
if (impliedOnly) readByte()
957+
val importImplied = nextByte == IMPLIED
958+
if (importImplied) readByte()
959959
val expr = readTerm()
960-
setSpan(start, Import(impliedOnly, expr, readSelectors()))
960+
setSpan(start, Import(importImplied, expr, readSelectors()))
961961
}
962962

963963
def readSelectors()(implicit ctx: Context): List[untpd.Tree] = nextByte match {

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
10491049
val to = untpd.Ident(toName)
10501050
if (toName.isEmpty) from else untpd.Thicket(from, untpd.Ident(toName))
10511051
})
1052-
Import(impliedOnly = false, expr, selectors)
1052+
Import(importImplied = false, expr, selectors)
10531053

10541054
case TEMPLATEtree =>
10551055
setSym()

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ object JavaParsers {
600600
}
601601

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

605605
// Importing the companion object members cannot be done uncritically: see
606606
// ticket #2377 wherein a class contains two static inner classes, each of which
@@ -662,7 +662,7 @@ object JavaParsers {
662662
// case nme.WILDCARD => Pair(ident, Ident(null) withPos Span(-1))
663663
// case _ => Pair(ident, ident)
664664
// }
665-
val imp = atSpan(start) { Import(impliedOnly = false, qual, List(ident)) }
665+
val imp = atSpan(start) { Import(importImplied = false, qual, List(ident)) }
666666
imp :: Nil
667667
}
668668
}

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,9 +2224,9 @@ object Parsers {
22242224
*/
22252225
def importClause(): List[Tree] = {
22262226
val offset = accept(IMPORT)
2227-
val impliedOnly = in.token == IMPLIED
2228-
if (impliedOnly) in.nextToken()
2229-
commaSeparated(importExpr(impliedOnly)) match {
2227+
val importImplied = in.token == IMPLIED
2228+
if (importImplied) in.nextToken()
2229+
commaSeparated(importExpr(importImplied)) match {
22302230
case t :: rest =>
22312231
// The first import should start at the start offset of the keyword.
22322232
val firstPos =
@@ -2239,11 +2239,11 @@ object Parsers {
22392239

22402240
/** ImportExpr ::= StableId `.' (id | `_' | ImportSelectors)
22412241
*/
2242-
def importExpr(impliedOnly: Boolean): () => Import = {
2242+
def importExpr(importImplied: Boolean): () => Import = {
22432243

22442244
val handleImport: Tree => Tree = { tree: Tree =>
2245-
if (in.token == USCORE) Import(impliedOnly, tree, importSelector() :: Nil)
2246-
else if (in.token == LBRACE) Import(impliedOnly, tree, inBraces(importSelectors()))
2245+
if (in.token == USCORE) Import(importImplied, tree, importSelector() :: Nil)
2246+
else if (in.token == LBRACE) Import(importImplied, tree, inBraces(importSelectors()))
22472247
else tree
22482248
}
22492249

@@ -2252,10 +2252,10 @@ object Parsers {
22522252
imp
22532253
case sel @ Select(qual, name) =>
22542254
val selector = atSpan(pointOffset(sel)) { Ident(name) }
2255-
cpy.Import(sel)(impliedOnly, qual, selector :: Nil)
2255+
cpy.Import(sel)(importImplied, qual, selector :: Nil)
22562256
case t =>
22572257
accept(DOT)
2258-
Import(impliedOnly, t, Ident(nme.WILDCARD) :: Nil)
2258+
Import(importImplied, t, Ident(nme.WILDCARD) :: Nil)
22592259
}
22602260
}
22612261

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
484484
typeDefText(tparamsTxt, optText(rhs)(" = " ~ _))
485485
}
486486
recur(rhs, "")
487-
case Import(impliedOnly, expr, selectors) =>
487+
case Import(importImplied, expr, selectors) =>
488488
def selectorText(sel: Tree): Text = sel match {
489489
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
490490
case _ => toTextGlobal(sel)
@@ -493,7 +493,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
493493
case id :: Nil => toText(id)
494494
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
495495
}
496-
keywordText("import ") ~ (keywordText("implied ") provided impliedOnly) ~
496+
keywordText("import ") ~ (keywordText("implied ") provided importImplied) ~
497497
toTextLocal(expr) ~ "." ~ selectorsText
498498
case packageDef: PackageDef =>
499499
packageDefText(packageDef)

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,10 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
339339
tree match {
340340
case Match(selector, _) =>
341341
addPatMatDependency(selector.tpe)
342-
case Import(impliedOnly, expr, selectors) =>
342+
case Import(importImplied, expr, selectors) =>
343343
def lookupImported(name: Name) = {
344344
val sym = expr.tpe.member(name).symbol
345-
if (sym.is(Implied) == impliedOnly) sym else NoSymbol
345+
if (sym.is(Implied) == importImplied) sym else NoSymbol
346346
}
347347
def addImported(name: Name) = {
348348
// importing a name means importing both a term and a type (if they exist)

compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,15 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
115115
case _ => None
116116
}
117117

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

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

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

128128
type Definition = tpd.Tree
129129

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ trait Checking {
10121012
val cases =
10131013
for (stat <- impl.body if isCase(stat))
10141014
yield untpd.Ident(stat.symbol.name.toTermName)
1015-
val caseImport: Import = Import(impliedOnly = false, ref(cdef.symbol), cases)
1015+
val caseImport: Import = Import(importImplied = false, ref(cdef.symbol), cases)
10161016
val caseCtx = enumCtx.importContext(caseImport, caseImport.symbol)
10171017
for (stat <- impl.body) checkCaseOrDefault(stat, caseCtx)
10181018
case _ =>

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ object Implicits {
364364

365365
abstract class SearchFailureType extends ErrorType {
366366
def expectedType: Type
367-
protected def argument: Tree
367+
def argument: Tree
368368

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

830830
def missingArgMsg(arg: Tree, pt: Type, where: String)(implicit ctx: Context): String = {
831+
831832
def msg(shortForm: String)(headline: String = shortForm) = arg match {
832833
case arg: Trees.SearchFailureIdent[_] =>
833834
shortForm
@@ -842,6 +843,7 @@ trait Implicits { self: Typer =>
842843
|But ${tpe.explanation}."""
843844
}
844845
}
846+
845847
def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where"
846848

847849
/** Extract a user defined error message from a symbol `sym`
@@ -904,7 +906,30 @@ trait Implicits { self: Typer =>
904906
raw,
905907
pt.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
906908
pt.argInfos))
907-
msg(userDefined.getOrElse(em"no implicit argument of type $pt was found${location("for")}"))()
909+
910+
def hiddenImplicitsAddendum: String = arg.tpe match {
911+
case fail: SearchFailureType =>
912+
913+
def hiddenImplicitNote(s: SearchSuccess) =
914+
em"\n\nNote: implied instance ${s.ref.symbol.showLocated} was not considered because it was not imported with an `import implied`."
915+
916+
def FindHiddenImplicitsCtx(ctx: Context): Context =
917+
if (ctx == NoContext) ctx
918+
else ctx.freshOver(FindHiddenImplicitsCtx(ctx.outer)).addMode(Mode.FindHiddenImplicits)
919+
920+
inferImplicit(fail.expectedType, fail.argument, arg.span)(
921+
FindHiddenImplicitsCtx(ctx)) match {
922+
case s: SearchSuccess => hiddenImplicitNote(s)
923+
case f: SearchFailure =>
924+
f.reason match {
925+
case ambi: AmbiguousImplicits => hiddenImplicitNote(ambi.alt1)
926+
case r => ""
927+
}
928+
}
929+
}
930+
msg(userDefined.getOrElse(
931+
em"no implicit argument of type $pt was found${location("for")}"))() ++
932+
hiddenImplicitsAddendum
908933
}
909934
}
910935

0 commit comments

Comments
 (0)