diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 807a532f02d2..199b7a338cd6 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 8226c6b19133..b7f361b33097 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -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] } @@ -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 @@ -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) => @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 9d786d21c44c..86d4b6b037e5 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index d31b368873d5..4c51d26fce69 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index e8e7b534dc3e..bbfbbb7f7503 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index eeabe135b573..4c1ac824297b 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -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 } @@ -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? */ @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 57fd67956205..1aa2c0f6f1ba 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -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") } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 1cbe5d3a9581..fa818934839b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -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) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ef22e72bc82d..4f07c5a9597a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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 { diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index d7d934d2805d..7f1e79aa3b8a 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -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() diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 005505ba6fb2..6601d74f2c81 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -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 @@ -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 } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ad5d39291536..b89a31dd787a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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 = @@ -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 } @@ -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) } } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index ac312b9d5e11..1bfd6926fcaa 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -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) @@ -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) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 9d45383d6827..2390f7943a1d 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index eda23f2948d7..25ddb3c96624 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index d7f58baa94ee..30d1166fc7d8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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 _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 02b2a9d0f279..1ce31ea9467f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -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 @@ -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 @@ -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` @@ -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 } } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 488d68ce5449..19b4268ab703 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -16,23 +16,23 @@ object ImportInfo { def rootImport(refFn: () => TermRef)(implicit ctx: Context): ImportInfo = { val selectors = untpd.Ident(nme.WILDCARD) :: Nil def expr(implicit ctx: Context) = tpd.Ident(refFn()) - def imp(implicit ctx: Context) = tpd.Import(impliedOnly = false, expr, selectors) - new ImportInfo(implicit ctx => imp.symbol, selectors, None, impliedOnly = false, isRootImport = true) + def imp(implicit ctx: Context) = tpd.Import(importImplied = false, expr, selectors) + new ImportInfo(implicit ctx => imp.symbol, selectors, None, importImplied = false, isRootImport = true) } } /** Info relating to an import clause - * @param sym The import symbol defined by the clause - * @param selectors The selector clauses - * @param symNameOpt Optionally, the name of the import symbol. None for root imports. - * Defined for all explicit imports from ident or select nodes. - * @param impliedOnly true if this is an implied import - * @param isRootImport true if this is one of the implicit imports of scala, java.lang, - * scala.Predef or dotty.DottyPredef in the start context, false otherwise. + * @param sym The import symbol defined by the clause + * @param selectors The selector clauses + * @param symNameOpt Optionally, the name of the import symbol. None for root imports. + * Defined for all explicit imports from ident or select nodes. + * @param importImplied true if this is an implied import + * @param isRootImport true if this is one of the implicit imports of scala, java.lang, + * scala.Predef or dotty.DottyPredef in the start context, false otherwise. */ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], symNameOpt: Option[TermName], - val impliedOnly: Boolean, + val importImplied: Boolean, val isRootImport: Boolean = false) extends Showable { // Dotty deviation: we cannot use a lazy val here for the same reason @@ -95,7 +95,9 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], recur(selectors) } - private def implicitFlag = if (impliedOnly) Implied else Implicit + private def implicitFlag(implicit ctx: Context) = + if (importImplied || ctx.mode.is(Mode.FindHiddenImplicits)) ImplicitOrImplied + else Implicit /** The implicit references imported by this import clause */ def importedImplicits(implicit ctx: Context): List[ImplicitRef] = { diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 389fe82c2732..e59af85c247f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -45,7 +45,8 @@ object Inferencing { /** Instantiate selected type variables `tvars` in type `tp` */ def instantiateSelected(tp: Type, tvars: List[Type])(implicit ctx: Context): Unit = - new IsFullyDefinedAccumulator(new ForceDegree.Value(tvars.contains, minimizeAll = true)).process(tp) + if (tvars.nonEmpty) + new IsFullyDefinedAccumulator(new ForceDegree.Value(tvars.contains, minimizeAll = true)).process(tp) /** Instantiate any type variables in `tp` whose bounds contain a reference to * one of the parameters in `tparams` or `vparamss`. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 40e4cc82f4e5..da6834600182 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -187,7 +187,7 @@ class Typer extends Namer val pre = imp.site var reqd = required var excl = EmptyFlags - if (imp.impliedOnly) reqd |= Implied else excl |= Implied + if (imp.importImplied) reqd |= Implied else excl |= Implied val denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx) // Pass refctx so that any errors are reported in the context of the // reference instead of the @@ -1785,7 +1785,7 @@ class Typer extends Namer val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.sourcePos) if (!ctx.isAfterTyper) checkRealizable(expr1.tpe, imp.expr.posd) - assignType(cpy.Import(imp)(imp.impliedOnly, expr1, imp.selectors), sym) + assignType(cpy.Import(imp)(imp.importImplied, expr1, imp.selectors), sym) } def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { diff --git a/docs/docs/reference/contextual/import-implied.md b/docs/docs/reference/contextual/import-implied.md index 7e4c2dda9041..dc10f3585d79 100644 --- a/docs/docs/reference/contextual/import-implied.md +++ b/docs/docs/reference/contextual/import-implied.md @@ -27,3 +27,25 @@ There are two main benefits arising from these rules: without importing anything else. This is particularly important since implied instances can be anonymous, so the usual recourse of using named imports is not practical. + +### Relationship with Old-Style Implicits + +The rules of implied imports above have the consequence that a library +would have to migrate in lockstep with all its users from old style implicits and +normal imports to implied instances and imports. + +The following modifications avoid this hurdle to migration. + + 1. An implied import also brings old style implicits into scope. So, in Scala 3.0 + an old-style implicit definition can be brought into scope either by a normal or + by an implied import. + + 2. In Scala 3.1, an old-style implicits accessed implicitly through a normal import + will give a deprecation warning. + + 3. In some version after 3.1, an old-style implicits accessed implicitly through a normal import + will give a compiler error. + +These rules mean that library users can use `import implied` to access old-style implicits in Scala 3.0, +and will be gently nudged and then forced to do so in later versions. Libraries can then switch to +implied instances once their user base has migrated. diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 0163f510ed8f..358a93ca2e52 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -185,13 +185,13 @@ trait Kernel { def matchImport(tree: Tree)(implicit ctx: Context): Option[Import] - def Import_impliedOnly(self: Import): Boolean + def Import_implied(self: Import): Boolean def Import_expr(self: Import)(implicit ctx: Context): Term def Import_selectors(self: Import)(implicit ctx: Context): List[ImportSelector] - def Import_apply(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import + def Import_apply(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import - def Import_copy(original: Import)(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import + def Import_copy(original: Import)(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import /** Tree representing a definition in the source code. It can be `PackageDef`, `ClassDef`, `TypeDef`, `DefDef` or `ValDef` */ type Definition <: Statement diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 570a83fb800e..40e4634503a5 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -203,8 +203,8 @@ trait Printers this += ", " += self += ", " ++= body += ")" case PackageDef(name, owner) => this += "PackageDef(\"" += name += "\", " += owner += ")" - case Import(impliedOnly, expr, selectors) => - this += "Import(" += impliedOnly += ", " += expr += ", " ++= selectors += ")" + case Import(importImplied, expr, selectors) => + this += "Import(" += importImplied += ", " += expr += ", " ++= selectors += ")" case PackageClause(pid, stats) => this += "PackageClause(" += pid += ", " ++= stats += ")" case TypeTree.Inferred() => @@ -539,9 +539,9 @@ trait Printers inBlock(printTrees(stats1, lineBreak())) } - case Import(impliedOnly, expr, selectors) => + case Import(importImplied, expr, selectors) => this += "import " - if (impliedOnly) this += "implied " + if (importImplied) this += "implied " printTree(expr) this += "." printImportSelectors(selectors) diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index 2c456949d777..285c0b2fb99e 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -37,16 +37,16 @@ trait TreeOps extends Core { } object Import { - def apply(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = - kernel.Import_apply(impliedOnly, expr, selectors) - def copy(original: Import)(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = - kernel.Import_copy(original)(impliedOnly, expr, selectors) + def apply(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = + kernel.Import_apply(importImplied, expr, selectors) + def copy(original: Import)(importImplied: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = + kernel.Import_copy(original)(importImplied, expr, selectors) def unapply(tree: Tree)(implicit ctx: Context): Option[(Boolean, Term, List[ImportSelector])] = - kernel.matchImport(tree).map(x => (x.impliedOnly, x.expr, x.selectors)) + kernel.matchImport(tree).map(x => (x.importImplied, x.expr, x.selectors)) } implicit class ImportAPI(self: Import) { - def impliedOnly: Boolean = kernel.Import_impliedOnly(self) + def importImplied: Boolean = kernel.Import_implied(self) def expr(implicit ctx: Context): Term = kernel.Import_expr(self) def selectors(implicit ctx: Context): List[ImportSelector] = kernel.Import_selectors(self) diff --git a/library/src/scala/tasty/reflect/TreeUtils.scala b/library/src/scala/tasty/reflect/TreeUtils.scala index 4fec048ac8f0..3d0a772b6c57 100644 --- a/library/src/scala/tasty/reflect/TreeUtils.scala +++ b/library/src/scala/tasty/reflect/TreeUtils.scala @@ -126,7 +126,7 @@ trait TreeUtils case IsPackageClause(tree) => PackageClause.copy(tree)(transformTerm(tree.pid).asInstanceOf[Term.Ref], transformTrees(tree.stats)(tree.symbol.localContext)) case IsImport(tree) => - Import.copy(tree)(tree.impliedOnly, transformTerm(tree.expr), tree.selectors) + Import.copy(tree)(tree.importImplied, transformTerm(tree.expr), tree.selectors) case IsStatement(tree) => transformStatement(tree) case IsTypeTree(tree) => transformTypeTree(tree) @@ -158,7 +158,7 @@ trait TreeUtils case IsClassDef(tree) => ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.derived, tree.self, tree.body) case IsImport(tree) => - Import.copy(tree)(tree.impliedOnly, transformTerm(tree.expr), tree.selectors) + Import.copy(tree)(tree.importImplied, transformTerm(tree.expr), tree.selectors) } } diff --git a/tests/neg/i5978.scala b/tests/neg/i5978.scala new file mode 100644 index 000000000000..33fceb941082 --- /dev/null +++ b/tests/neg/i5978.scala @@ -0,0 +1,29 @@ +import scala.language.implicitConversions + +opaque type Position[Buffer] = Int + +trait TokenParser[Token, R] + +object TextParser { + implied TP for TokenParser[Char, Position[CharSequence]] {} + + implied FromCharToken + given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ??? +} + + +object Testcase { + def main(args: Array[String]): Unit = { + import TextParser._ + + val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP + val tp_i = the[TokenParser[Char, Position[CharSequence]]] // error + val co_i = the[Conversion[Char, Position[CharSequence]]] // error + val co_x : Position[CharSequence] = 'x' // error + + { + implied XXX for Conversion[Char, Position[CharSequence]] = co_i + val co_y : Position[CharSequence] = 'x' + } + } +} \ No newline at end of file diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index b6e598892f4c..07e822cd851b 100644 --- a/tests/pos-with-compiler/tasty/definitions.scala +++ b/tests/pos-with-compiler/tasty/definitions.scala @@ -12,7 +12,7 @@ object definitions { case class PackageClause(pkg: Term, body: List[Tree]) extends Tree - case class Import(impliedOnly: Boolean, expr: Term, selector: List[ImportSelector]) extends Statement + case class Import(importImplied: Boolean, expr: Term, selector: List[ImportSelector]) extends Statement enum ImportSelector { case SimpleSelector(id: Id) diff --git a/tests/pos/i5978.scala b/tests/pos/i5978.scala index ee800e8ba902..a17da36bcf37 100644 --- a/tests/pos/i5978.scala +++ b/tests/pos/i5978.scala @@ -4,27 +4,74 @@ opaque type Position[Buffer] = Int trait TokenParser[Token, R] -object TextParser { - implied TP for TokenParser[Char, Position[CharSequence]] {} +package p1 { - implied FromCharToken - given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ??? + object TextParser { + implied TP for TokenParser[Char, Position[CharSequence]] {} + + implied FromCharToken + given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ??? + } + + + object Testcase { + def main(args: Array[String]): Unit = { + import implied TextParser._ + import TextParser._ + + val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP + val tp_i = the[TokenParser[Char, Position[CharSequence]]] + val co_i = the[Conversion[Char, Position[CharSequence]]] + val co_x : Position[CharSequence] = 'x' + + { + implied XXX for Conversion[Char, Position[CharSequence]] = co_i + val co_y : Position[CharSequence] = 'x' + } + } + } +} +package p2 { + + object TextParser { + implicit object TP extends TokenParser[Char, Position[CharSequence]] {} + + implicit def FromCharToken(c: Char)(implicit T: TokenParser[Char, Position[CharSequence]]): Position[CharSequence] = ??? + } + + object Testcase { + def main(args: Array[String]): Unit = { + import TextParser._ + import implied TextParser._ + + val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP + val tp_i = the[TokenParser[Char, Position[CharSequence]]] + val co_x : Position[CharSequence] = 'x' + } + } } +package p3 { + object TextParser { + implicit object TP extends TokenParser[Char, Position[CharSequence]] {} + + implicit def FromCharToken(implicit T: TokenParser[Char, Position[CharSequence]]): Conversion[Char, Position[CharSequence]] = ??? + } -object Testcase { - def main(args: Array[String]): Unit = { - import TextParser._ - import implied TextParser._ + object Testcase { + def main(args: Array[String]): Unit = { + import implied TextParser._ + import TextParser._ - val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP - val tp_i = the[TokenParser[Char, Position[CharSequence]]] - val co_i = the[Conversion[Char, Position[CharSequence]]] - val co_x : Position[CharSequence] = 'x' + val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP + val tp_i = the[TokenParser[Char, Position[CharSequence]]] + implicit val co_i: Conversion[Char, Position[CharSequence]] = the[Conversion[Char, Position[CharSequence]]] + val co_x : Position[CharSequence] = 'x' - { - implied XXX for Conversion[Char, Position[CharSequence]] = co_i - val co_y : Position[CharSequence] = 'x' + { + implied XXX for Conversion[Char, Position[CharSequence]] = co_i + val co_y : Position[CharSequence] = 'x' + } } } } \ No newline at end of file