diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index dd15a25031d5..f921f15a157c 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -344,8 +344,6 @@ object desugar { val isObject = mods.is(Module) val isCaseClass = mods.is(Case) && !isObject val isCaseObject = mods.is(Case) && isObject - val isImplicit = mods.is(Implicit) - val isInstance = isImplicit && mods.mods.exists(_.isInstanceOf[Mod.Instance]) val isEnum = mods.isEnumClass && !mods.is(Module) def isEnumCase = mods.isEnumCase val isValueClass = parents.nonEmpty && isAnyVal(parents.head) @@ -394,7 +392,7 @@ object desugar { if (isEnum) { val (enumCases, enumStats) = stats.partition(DesugarEnums.isEnumCase) val enumCompanionRef = new TermRefTree() - val enumImport = Import(enumCompanionRef, enumCases.flatMap(caseIds)) + val enumImport = Import(impliedOnly = false, enumCompanionRef, enumCases.flatMap(caseIds)) (enumImport :: enumStats, enumCases, enumCompanionRef) } else (stats, Nil, EmptyTree) @@ -666,17 +664,21 @@ object desugar { // synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] = // new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) = val implicitWrappers = - if (!isImplicit) + if (!mods.is(ImplicitOrImplied)) Nil else if (ctx.owner is Package) { ctx.error(TopLevelImplicitClass(cdef), cdef.sourcePos) Nil } + else if (mods.is(Trait)) { + ctx.error(TypesAndTraitsCantBeImplicit(), cdef.sourcePos) + Nil + } else if (isCaseClass) { ctx.error(ImplicitCaseClass(cdef), cdef.sourcePos) Nil } - else if (arity != 1 && !isInstance) { + else if (arity != 1 && !mods.is(Implied)) { ctx.error(ImplicitClassPrimaryConstructorArity(), cdef.sourcePos) Nil } @@ -690,7 +692,7 @@ object desugar { // implicit wrapper is typechecked in same scope as constructor, so // we can reuse the constructor parameters; no derived params are needed. DefDef(className.toTermName, constrTparams, defParamss, classTypeRef, creatorExpr) - .withMods(companionMods | Synthetic | Implicit | Final) + .withMods(companionMods | mods.flags.toTermFlags & ImplicitOrImplied | Synthetic | Final) .withSpan(cdef.span) :: Nil } @@ -1043,7 +1045,7 @@ object desugar { def needsObject(stat: Tree) = stat match { case _: ValDef | _: PatDef | _: DefDef => true case stat: ModuleDef => - stat.mods.is(Implicit) || opaqueNames.contains(stat.name.stripModuleClassSuffix.toTypeName) + stat.mods.is(ImplicitOrImplied) || opaqueNames.contains(stat.name.stripModuleClassSuffix.toTypeName) case stat: TypeDef => !stat.isClassDef || stat.mods.is(Implicit) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 7bf033e93873..eadd5b92206f 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -358,7 +358,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | TypeDef(_, _) - | Import(_, _) + | Import(_, _, _) | DefDef(_, _, _, _, _) => Pure case vdef @ ValDef(_, _, _) => diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 6cc697bfb5d6..d5cac688b835 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -762,7 +762,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] (expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile) + case class Import[-T >: Untyped] private[ast] (impliedOnly: Boolean, expr: Tree[T], selectors: List[Tree[Untyped]])(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] { type ThisTree[-T >: Untyped] = Import[T] } @@ -1156,9 +1156,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)(tree.source)) } - def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match { - case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree - case _ => finalize(tree, untpd.Import(expr, selectors)(tree.source)) + 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)(tree.source)) } 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 @@ -1303,8 +1303,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(expr, selectors) => - cpy.Import(tree)(transform(expr), selectors) + case Import(impliedOnly, expr, selectors) => + cpy.Import(tree)(impliedOnly, transform(expr), selectors) case PackageDef(pid, stats) => cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx)) case Annotated(arg, annot) => @@ -1427,7 +1427,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(expr, selectors) => + case Import(impliedOnly, 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 bbfacc2fdfef..514377802e57 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -329,8 +329,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Block(cdef :: Nil, New(cls.typeRef, Nil)) } - def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = - ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr)) + 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 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 a074281674c2..8dee85adb6b7 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -130,9 +130,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Var()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Mutable) - case class Implicit()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.ImplicitCommon) + case class Implicit()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implicit) - case class Given()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.ImplicitCommon | Flags.Given) + case class Given()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implicit | Flags.Given) case class Erased()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Erased) @@ -152,7 +152,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Enum()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Enum) - case class Instance()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implicit) + case class Instance()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implied) } /** Modifiers and annotations for definitions @@ -326,7 +326,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(expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(expr, selectors) + def Import(impliedOnly: Boolean, expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(impliedOnly, 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/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 31c8269d0da9..e5424a4b9998 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -13,6 +13,7 @@ import Scopes._ import Uniques._ import ast.Trees._ import ast.untpd +import Flags.ImplicitOrImplied import util.{FreshNameCreator, NoSource, SimpleIdentityMap, SourceFile} import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, SearchRoot, TypeAssigner, Typer} import Implicits.ContextualImplicits @@ -214,7 +215,7 @@ object Contexts { implicitsCache = { val implicitRefs: List[ImplicitRef] = if (isClassDefContext) - try owner.thisType.implicitMembers + try owner.thisType.implicitMembers(ImplicitOrImplied) catch { case ex: CyclicReference => Nil } @@ -404,7 +405,8 @@ object Contexts { case ref: RefTree[_] => Some(ref.name.asTermName) case _ => None } - ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt)) + ctx.fresh.setImportInfo( + new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt, imp.impliedOnly)) } /** Does current phase use an erased types interpretation? */ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0d6e42b3318a..3d1152784fc5 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -130,6 +130,7 @@ object Flags { */ case class FlagConjunction(bits: Long) { override def toString: String = FlagSet(bits).toString + def | (fs: FlagSet): FlagConjunction = FlagConjunction((FlagSet(bits) | fs).bits) } def termFlagConjunction(x: Long) = FlagConjunction(TERMS | x) @@ -242,8 +243,8 @@ object Flags { final val TypeParam: FlagSet = Param.toTypeFlags /** Labeled with `implicit` modifier (implicit value) */ - final val ImplicitCommon: FlagSet = commonFlag(9, "implicit") - final val Implicit: FlagSet = ImplicitCommon.toTermFlags + final val Implicit: FlagSet = commonFlag(9, "implicit") + final val ImplicitTerm: FlagSet = Implicit.toTermFlags /** Labeled with `lazy` (a lazy val). */ final val Lazy: FlagSet = termFlag(10, "lazy") @@ -384,6 +385,8 @@ object Flags { /** Symbol is a Java default method */ final val DefaultMethod: FlagSet = termFlag(38, "") + final val Implied: FlagSet = commonFlag(39, "implied") + /** Symbol is an enum class or enum case (if used with case) */ final val Enum: FlagSet = commonFlag(40, "") @@ -460,7 +463,7 @@ object Flags { /** Flags representing source modifiers */ private val CommonSourceModifierFlags: FlagSet = - commonFlags(Private, Protected, Final, Case, Implicit, Override, JavaStatic) + commonFlags(Private, Protected, Final, Case, Implicit, Implied, Override, JavaStatic) final val TypeSourceModifierFlags: FlagSet = CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque @@ -485,7 +488,7 @@ object Flags { HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags | Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | - Extension.toCommonFlags | NonMember | ImplicitCommon | Permanent | Synthetic | + Extension.toCommonFlags | NonMember | Implicit | Implied | Permanent | Synthetic | SuperAccessorOrScala2x | Inline /** Flags that are not (re)set when completing the denotation, or, if symbol is @@ -541,7 +544,7 @@ object Flags { /** Flags that can apply to a module val */ final val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | - Override | Final | Method | Implicit | Lazy | + Override | Final | Method | Implicit | Implied | Lazy | Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased /** Flags that can apply to a module class */ @@ -586,6 +589,10 @@ object Flags { /** An inline method or inline argument proxy */ final val InlineOrProxy: FlagSet = Inline | InlineProxy + final val ImplicitOrImplied = Implicit | Implied + + final val ImplicitOrImpliedTerm = ImplicitOrImplied.toTermFlags + /** Assumed to be pure */ final val StableOrErased: FlagSet = StableRealizable | Erased @@ -601,9 +608,6 @@ object Flags { /** An inline method */ final val InlineMethod: FlagConjunction = allOf(Inline, Method) - /** An implicit inline method */ - final val ImplicitInlineMethod: FlagConjunction = allOf(Inline, Implicit, Method) - /** An inline parameter */ final val InlineParam: FlagConjunction = allOf(Inline, Param) diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index 4ed90ed7bfdd..c9af4067d37a 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -9,7 +9,7 @@ package core import Symbols._ import Types.{TermRef, NoPrefix} -import Flags.Implicit +import Flags._ import Names._ import Contexts._ import Denotations._ @@ -409,7 +409,7 @@ object Scopes { var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { - if (e.sym is Implicit) { + if (e.sym is ImplicitOrImplied) { val d = e.sym.denot irefs += TermRef(NoPrefix, d.symbol.asTerm).withDenot(d) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e938f2d90e40..14dfa73b1a1a 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1873,7 +1873,8 @@ object SymDenotations { val ownSyms = if (keepOnly eq implicitFilter) if (this is Package) Iterator.empty - else info.decls.iterator filter (_ is Implicit) + // implicits in package objects are added by the overriding `memberNames` in `PackageClassDenotation` + else info.decls.iterator filter (_ is ImplicitOrImplied) else info.decls.iterator for (sym <- ownSyms) maybeAdd(sym.name) names diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 3d10901120cc..3dc51f8fff53 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -18,7 +18,7 @@ import config.Printers.cyclicErrors class TypeError(msg: String) extends Exception(msg) { def this() = this("") def toMessage(implicit ctx: Context): Message = super.getMessage - override def getMessage: String = throw new Exception("Use toMessage instead for TypeError") + override def getMessage: String = super.getMessage } class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) extends TypeError { @@ -137,7 +137,7 @@ class CyclicReference private (val denot: SymDenotation) extends TypeError { } } // Give up and give generic errors. - else if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm) + else if (cycleSym.is(ImplicitOrImplied, butNot = Method) && cycleSym.owner.isTerm) CyclicReferenceInvolvingImplicit(cycleSym) else CyclicReferenceInvolving(denot) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b453533f5610..ebe0c0da6f3b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -787,10 +787,13 @@ object Types { (name, buf) => buf += member(name).asSingleDenotation) } - /** The set of implicit members of this type */ - final def implicitMembers(implicit ctx: Context): List[TermRef] = track("implicitMembers") { + /** The set of implicit term members of this type + * @param kind A subset of {Implicit, Implied} that sepcifies what kind of implicit should + * be returned + */ + final def implicitMembers(kind: FlagSet)(implicit ctx: Context): List[TermRef] = track("implicitMembers") { memberDenots(implicitFilter, - (name, buf) => buf ++= member(name).altsWith(_ is Implicit)) + (name, buf) => buf ++= member(name).altsWith(_.is(ImplicitOrImpliedTerm & kind))) .toList.map(d => TermRef(this, d.symbol.asTerm)) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index b794555fed5f..b8edb41b0c4c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -58,7 +58,7 @@ Standard-Section: "ASTs" TopLevelStat* ValOrDefDef TYPEDEF Length NameRef (type_Term | Template) Modifier* OBJECTDEF Length NameRef Template Modifier* - IMPORT Length qual_Term Selector* + IMPORT Length [IMPLIED] qual_Term Selector* ValOrDefDef = VALDEF Length NameRef type_Term rhs_Term? Modifier* DEFDEF Length NameRef TypeParam* Params* returnType_Term rhs_Term? Modifier* @@ -180,6 +180,7 @@ Standard-Section: "ASTs" TopLevelStat* SEALED CASE IMPLICIT + IMPLIED ERASED LAZY OVERRIDE @@ -324,7 +325,8 @@ object TastyFormat { final val OPAQUE = 35 final val EXTENSION = 36 final val GIVEN = 37 - final val PARAMsetter = 38 + final val IMPLIED = 38 + final val PARAMsetter = 39 // Cat. 2: tag Nat @@ -475,6 +477,7 @@ object TastyFormat { | SEALED | CASE | IMPLICIT + | IMPLIED | ERASED | LAZY | OVERRIDE @@ -535,6 +538,7 @@ object TastyFormat { case SEALED => "SEALED" case CASE => "CASE" case IMPLICIT => "IMPLICIT" + case IMPLIED => "IMPLIED" case ERASED => "ERASED" case LAZY => "LAZY" case OVERRIDE => "OVERRIDE" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 414646749b96..101a03b9eec5 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -532,9 +532,13 @@ class TreePickler(pickler: TastyPickler) { } pickleStats(tree.constr :: rest) } - case Import(expr, selectors) => + case Import(impliedOnly, expr, selectors) => writeByte(IMPORT) - withLength { pickleTree(expr); pickleSelectors(selectors) } + withLength { + if (impliedOnly) writeByte(IMPLIED) + pickleTree(expr) + pickleSelectors(selectors) + } case PackageDef(pid, stats) => writeByte(PACKAGE) withLength { pickleType(pid.tpe); pickleStats(stats) } @@ -646,6 +650,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is Scala2x) writeByte(SCALA2X) if (isTerm) { if (flags is Implicit) writeByte(IMPLICIT) + if (flags is Implied) writeByte(IMPLIED) if (flags is Erased) writeByte(ERASED) if (flags.is(Lazy, butNot = Module)) writeByte(LAZY) if (flags is AbsOverride) { writeByte(ABSTRACT); writeByte(OVERRIDE) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 47c7299ff839..dedbf65598fb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -609,6 +609,7 @@ class TreeUnpickler(reader: TastyReader, case SEALED => addFlag(Sealed) case CASE => addFlag(Case) case IMPLICIT => addFlag(Implicit) + case IMPLIED => addFlag(Implied) case ERASED => addFlag(Erased) case LAZY => addFlag(Lazy) case OVERRIDE => addFlag(Override) @@ -955,8 +956,10 @@ class TreeUnpickler(reader: TastyReader, assert(sourcePathAt(start).isEmpty) readByte() readEnd() + val impliedOnly = nextByte == IMPLIED + if (impliedOnly) readByte() val expr = readTerm() - setSpan(start, Import(expr, readSelectors())) + setSpan(start, Import(impliedOnly, 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 ed0c57340cac..560a78459474 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -1049,8 +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(expr, selectors) + Import(impliedOnly = false, expr, selectors) case TEMPLATEtree => setSym() diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 57ecb3133722..79e78443378f 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -67,7 +67,7 @@ object Completion { if (name.span.contains(pos.span)) Mode.Import else Mode.None // Can't help completing the renaming - case Import(_, _) :: _ => + case Import(_, _, _) :: _ => Mode.Import case _ => @@ -84,7 +84,7 @@ object Completion { case Thicket(name :: _ :: Nil) :: (_: Import) :: _ => completionPrefix(name :: Nil, pos) - case Import(expr, selectors) :: _ => + case Import(_, expr, selectors) :: _ => selectors.find(_.span.contains(pos.span)).map { selector => completionPrefix(selector.asInstanceOf[Tree] :: Nil, pos) }.getOrElse("") @@ -120,10 +120,10 @@ object Completion { if (buffer.mode != Mode.None) { path match { - case Select(qual, _) :: _ => buffer.addMemberCompletions(qual) - case Import(expr, _) :: _ => buffer.addMemberCompletions(expr) - case (_: Thicket) :: Import(expr, _) :: _ => buffer.addMemberCompletions(expr) - case _ => buffer.addScopeCompletions + case Select(qual, _) :: _ => buffer.addMemberCompletions(qual) + case Import(_, expr, _) :: _ => buffer.addMemberCompletions(expr) // TODO: distinguish implied from non-implied + case (_: Thicket) :: Import(_, expr, _) :: _ => buffer.addMemberCompletions(expr) + case _ => buffer.addScopeCompletions } } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index d2f6464e814c..3803559d4d10 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -603,7 +603,7 @@ object JavaParsers { } def importCompanionObject(cdef: TypeDef): Tree = - Import(Ident(cdef.name.toTermName).withSpan(NoSpan), Ident(nme.WILDCARD) :: Nil) + Import(impliedOnly = 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 @@ -665,7 +665,7 @@ object JavaParsers { // case nme.WILDCARD => Pair(ident, Ident(null) withPos Span(-1)) // case _ => Pair(ident, ident) // } - val imp = atSpan(start) { Import(qual, List(ident)) } + val imp = atSpan(start) { Import(impliedOnly = 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 f9fb91029c32..46cf3da79a6a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -783,7 +783,7 @@ object Parsers { def functionRest(params: List[Tree]): Tree = atSpan(start, accept(ARROW)) { val t = typ() - if (imods.is(Implicit | Given | Erased)) new FunctionWithMods(params, t, imods) + if (imods.is(Given | Erased)) new FunctionWithMods(params, t, imods) else Function(params, t) } def funArgTypesRest(first: Tree, following: () => Tree) = { @@ -817,7 +817,7 @@ object Parsers { } openParens.change(LPAREN, -1) accept(RPAREN) - if (imods.is(Implicit) || isValParamList || in.token == ARROW) + if (isValParamList || in.token == ARROW) functionRest(ts) else { val ts1 = @@ -2157,11 +2157,13 @@ object Parsers { def finalizeDef(md: MemberDef, mods: Modifiers, start: Int): md.ThisTree[Untyped] = md.withMods(mods).setComment(in.getDocComment(start)) - /** Import ::= import ImportExpr {`,' ImportExpr} + /** Import ::= import [implied] [ImportExpr {`,' ImportExpr} */ def importClause(): List[Tree] = { val offset = accept(IMPORT) - commaSeparated(importExpr) match { + val impliedOnly = in.token == IMPLIED + if (impliedOnly) in.nextToken() + commaSeparated(importExpr(impliedOnly)) match { case t :: rest => // The first import should start at the start offset of the keyword. val firstPos = @@ -2174,21 +2176,24 @@ object Parsers { /** ImportExpr ::= StableId `.' (id | `_' | ImportSelectors) */ - val importExpr: () => Import = () => path(thisOK = false, handleImport) match { - case imp: Import => - imp - case sel @ Select(qual, name) => - val selector = atSpan(pointOffset(sel)) { Ident(name) } - cpy.Import(sel)(qual, selector :: Nil) - case t => - accept(DOT) - Import(t, Ident(nme.WILDCARD) :: Nil) - } + def importExpr(impliedOnly: Boolean): () => Import = { - val handleImport: Tree => Tree = { tree: Tree => - if (in.token == USCORE) Import(tree, importSelector() :: Nil) - else if (in.token == LBRACE) Import(tree, inBraces(importSelectors())) - else tree + 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())) + else tree + } + + () => path(thisOK = false, handleImport) match { + case imp: Import => + imp + case sel @ Select(qual, name) => + val selector = atSpan(pointOffset(sel)) { Ident(name) } + cpy.Import(sel)(impliedOnly, qual, selector :: Nil) + case t => + accept(DOT) + Import(impliedOnly, t, Ident(nme.WILDCARD) :: Nil) + } } /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e763ed2b9813..dac051cbd8af 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -479,7 +479,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { typeDefText(tparamsTxt, optText(rhs)(" = " ~ _)) } recur(rhs, "") - case Import(expr, selectors) => + case Import(impliedOnly, expr, selectors) => def selectorText(sel: Tree): Text = sel match { case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) case _ => toTextGlobal(sel) @@ -488,7 +488,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case id :: Nil => toText(id) case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" } - keywordStr("import ") ~ toTextLocal(expr) ~ "." ~ selectorsText + keywordText("import ") ~ (keywordText("implied ") provided impliedOnly) ~ + toTextLocal(expr) ~ "." ~ selectorsText case packageDef: PackageDef => packageDefText(packageDef) case tree: Template => @@ -771,7 +772,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (ctx.settings.YdebugFlags.value) AnyFlags else if (suppressKw) PrintableFlags(isType) &~ Private else PrintableFlags(isType) - if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= Implicit // drop implicit from classes + if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= ImplicitOrImplied // drop implicit/implied from classes val flags = (if (sym.exists) sym.flags else (mods.flags)) & flagMask val flagsText = if (flags.isEmpty) "" else keywordStr(flags.toString) val annotations = diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index f685096af5d5..1b103bc73ee2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1507,8 +1507,7 @@ object messages { val explanation: String = "" } - case class TypesAndTraitsCantBeImplicit(sym: Symbol)( - implicit ctx: Context) + case class TypesAndTraitsCantBeImplicit()(implicit ctx: Context) extends Message(TypesAndTraitsCantBeImplicitID) { val msg: String = hl"""${"implicit"} modifier cannot be used for types or traits""" val kind: String = "Syntax" diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 728e3eaf82a9..d66b61cd8c91 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -589,7 +589,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val abs = sym.is(Abstract) || sym.is(Deferred) || absOver val over = sym.is(Override) || absOver new api.Modifiers(abs, over, sym.is(Final), sym.is(Sealed), - sym.is(Implicit), sym.is(Lazy), sym.is(Macro), sym.isSuperAccessor) + sym.is(ImplicitOrImplied), sym.is(Lazy), sym.is(Macro), sym.isSuperAccessor) } def apiAnnotations(s: Symbol): List[api.Annotation] = { diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 3c6f0a0914ad..9d45383d6827 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -339,8 +339,11 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT tree match { case Match(selector, _) => addPatMatDependency(selector.tpe) - case Import(expr, selectors) => - def lookupImported(name: Name) = expr.tpe.member(name).symbol + case Import(impliedOnly, expr, selectors) => + def lookupImported(name: Name) = { + val sym = expr.tpe.member(name).symbol + if (sym.is(Implied) == impliedOnly) sym else NoSymbol + } def addImported(name: Name) = { // importing a name means importing both a term and a type (if they exist) addMemberRefDependency(lookupImported(name.toTermName)) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala index cb65129d3d25..ce422cd4a376 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/FlagsOpsImpl.scala @@ -21,6 +21,7 @@ trait FlagsOpsImpl extends scala.tasty.reflect.FlagsOps with CoreImpl { def Sealed: Flags = core.Flags.Sealed def Case: Flags = core.Flags.Case def Implicit: Flags = core.Flags.Implicit + def Implied = core.Flags.Implied def Erased: Flags = core.Flags.Erased def Lazy: Flags = core.Flags.Lazy def Override: Flags = core.Flags.Override diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala index 4e400c84f3fa..fe62efc28e17 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala @@ -20,6 +20,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers } def ImportDeco(imp: Import): ImportAPI = new ImportAPI { + def impliedOnly: Boolean = imp.impliedOnly def expr(implicit ctx: Context): Tree = imp.expr def selectors(implicit ctx: Context): List[ImportSelector] = imp.selectors } @@ -208,14 +209,14 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers } object Import extends ImportModule { - def apply(expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = - tpd.Import(expr, selectors) + def apply(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = + tpd.Import(impliedOnly, expr, selectors) - def copy(original: Import)(expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = - tpd.cpy.Import(original)(expr, selectors) + def copy(original: Import)(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import = + tpd.cpy.Import(original)(impliedOnly, expr, selectors) - def unapply(x: Tree)(implicit ctx: Context): Option[(Term, List[ImportSelector])] = x match { - case x: tpd.Import => Some((x.expr, x.selectors)) + def unapply(x: Tree)(implicit ctx: Context): Option[(Boolean, Term, List[ImportSelector])] = x match { + case x: tpd.Import => Some((x.impliedOnly, x.expr, x.selectors)) case _ => None } } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index a06273a6d452..aa8b003795e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -300,7 +300,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: LambdaTypeTree => VarianceChecker.checkLambda(tree) super.transform(tree) - case Import(expr, selectors) => + case Import(_, expr, selectors) => val exprTpe = expr.tpe val seen = mutable.Set.empty[Name] def checkIdent(ident: untpd.Ident): Unit = { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5a4fb8d50141..814459a672d6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -376,11 +376,11 @@ object Checking { )) fail(ParamsNoInline(sym.owner)) - if (sym.is(ImplicitCommon)) { + if (sym.is(ImplicitOrImplied)) { if (sym.owner.is(Package)) fail(TopLevelCantBeImplicit(sym)) if (sym.isType) - fail(TypesAndTraitsCantBeImplicit(sym)) + fail(TypesAndTraitsCantBeImplicit()) } if (!sym.isClass && sym.is(Abstract)) fail(OnlyClassesCanBeAbstract(sym)) @@ -634,17 +634,23 @@ trait Checking { * - it is defined in Predef * - it is the scala.reflect.Selectable.reflectiveSelectable conversion */ - def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(implicit ctx: Context): Unit = { - val conversionOK = - !sym.exists || - sym.is(Synthetic) || - sym.info.finalResultType.classSymbols.exists(_.owner.isLinkedWith(sym.owner)) || - defn.isPredefClass(sym.owner) || - sym.name == nme.reflectiveSelectable && sym.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass - if (!conversionOK) - checkFeature(defn.LanguageModuleClass, nme.implicitConversions, - i"Use of implicit conversion ${sym.showLocated}", NoSymbol, posd.sourcePos) - } + def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(implicit ctx: Context): Unit = + if (sym.exists) { + val conv = + if (sym.is(Implicit)) sym + else { + assert(sym.name == nme.apply) + sym.owner + } + val conversionOK = + conv.is(Synthetic) || + sym.info.finalResultType.classSymbols.exists(_.isLinkedWith(conv.owner)) || + defn.isPredefClass(conv.owner) || + conv.name == nme.reflectiveSelectable && conv.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass + if (!conversionOK) + checkFeature(defn.LanguageModuleClass, nme.implicitConversions, + i"Use of implicit conversion ${conv.showLocated}", NoSymbol, posd.sourcePos) + } /** Issue a feature warning if feature is not enabled */ def checkFeature(base: ClassSymbol, @@ -972,7 +978,7 @@ trait Checking { val cases = for (stat <- impl.body if isCase(stat)) yield untpd.Ident(stat.symbol.name.toTermName) - val caseImport: Import = Import(ref(cdef.symbol), cases) + val caseImport: Import = Import(impliedOnly = 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/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index add005ae2ba0..b6d5c839bebf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -145,7 +145,7 @@ trait Deriving { this: Typer => if (ctx.denotNamed(instanceName).exists) { if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName", pos) } - else add(newMethod(instanceName, info, pos.span, Implicit)) + else add(newMethod(instanceName, info, pos.span, Implied)) } /** Check derived type tree `derived` for the following well-formedness conditions: diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index 6ce0e18ae89d..119336950e0d 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -66,7 +66,7 @@ class FrontEnd extends Phase { private def firstTopLevelDef(trees: List[tpd.Tree])(implicit ctx: Context): Symbol = trees match { case PackageDef(_, defs) :: _ => firstTopLevelDef(defs) - case Import(_, _) :: defs => firstTopLevelDef(defs) + case Import(_, _, _) :: defs => firstTopLevelDef(defs) case (tree @ TypeDef(_, _)) :: _ => tree.symbol case _ => NoSymbol } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index ffd36af6feeb..6ec67f4b7188 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -230,7 +230,7 @@ object Implicits { assert(initctx.typer != null) lazy val refs: List[ImplicitRef] = { val buf = new mutable.ListBuffer[TermRef] - for (companion <- companionRefs) buf ++= companion.implicitMembers + for (companion <- companionRefs) buf ++= companion.implicitMembers(ImplicitOrImplied) buf.toList } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 5506b78aea22..488d68ce5449 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -16,8 +16,8 @@ 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(expr, selectors) - new ImportInfo(implicit ctx => imp.symbol, selectors, None, isRootImport = true) + def imp(implicit ctx: Context) = tpd.Import(impliedOnly = false, expr, selectors) + new ImportInfo(implicit ctx => imp.symbol, selectors, None, impliedOnly = false, isRootImport = true) } } @@ -26,11 +26,14 @@ object ImportInfo { * @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. */ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], - symNameOpt: Option[TermName], val isRootImport: Boolean = false) extends Showable { + symNameOpt: Option[TermName], + val impliedOnly: Boolean, + val isRootImport: Boolean = false) extends Showable { // Dotty deviation: we cannot use a lazy val here for the same reason // that we cannot use one for `DottyPredefModuleRef`. @@ -92,17 +95,19 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], recur(selectors) } + private def implicitFlag = if (impliedOnly) Implied else Implicit + /** The implicit references imported by this import clause */ def importedImplicits(implicit ctx: Context): List[ImplicitRef] = { val pre = site if (isWildcardImport) { - val refs = pre.implicitMembers + val refs = pre.implicitMembers(implicitFlag) if (excluded.isEmpty) refs else refs filterNot (ref => excluded contains ref.name.toTermName) } else for { renamed <- reverseMapping.keys - denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit) + denot <- pre.member(reverseMapping(renamed)).altsWith(_ is implicitFlag) } yield { val original = reverseMapping(renamed) val ref = TermRef(pre, original, denot) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 7c7f41f41582..93954b75b28f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1049,7 +1049,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case Some(x) => x > 1 || x == 1 && !boundSym.is(Method) case none => true } - } && !boundSym.is(ImplicitInlineMethod) + } && !(boundSym.is(InlineMethod) && boundSym.is(ImplicitOrImplied)) val inlineBindings = new TreeMap { override def transform(t: Tree)(implicit ctx: Context) = t match { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index fd71bff9a696..00793da1cfd2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -342,7 +342,7 @@ class Namer { typer: Typer => tree match { case tree: TypeDef if tree.isClassDef => val name = checkNoConflict(tree.name).asTypeName - val flags = checkFlags(tree.mods.flags &~ Implicit) + val flags = checkFlags(tree.mods.flags &~ ImplicitOrImplied) val cls = createOrRefine[ClassSymbol](tree, name, flags, cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), @@ -954,7 +954,7 @@ class Namer { typer: Typer => val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes if (ptype.typeParams.isEmpty) ptype else { - if (denot.is(ModuleClass) && denot.sourceModule.is(Implicit)) + if (denot.is(ModuleClass) && denot.sourceModule.is(ImplicitOrImplied)) missingType(denot.symbol, "parent ")(creationContext) fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.span) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 68bc87709823..327c9756bc44 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -182,7 +182,10 @@ class Typer extends Namer NoType else { val pre = imp.site - val denot = pre.memberBasedOnFlags(name, required, EmptyFlags).accessibleFrom(pre)(refctx) + var reqd = required + var excl = EmptyFlags + if (imp.impliedOnly) 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 if (reallyExists(denot)) pre.select(name, denot) else NoType @@ -1477,7 +1480,7 @@ class Typer extends Namer def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedValDef") { val ValDef(name, tpt, _) = vdef completeAnnotations(vdef, sym) - if (sym is Implicit) checkImplicitConversionDefOK(sym) + if (sym is ImplicitOrImplied) checkImplicitConversionDefOK(sym) val tpt1 = checkSimpleKinded(typedType(tpt)) val rhs1 = vdef.rhs match { case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe @@ -1532,7 +1535,7 @@ class Typer extends Namer val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) vparamss1.foreach(checkNoForwardDependencies) - if (sym is Implicit) checkImplicitConversionDefOK(sym) + if (sym is ImplicitOrImplied) checkImplicitConversionDefOK(sym) val tpt1 = checkSimpleKinded(typedType(tpt)) var rhsCtx = ctx @@ -1779,7 +1782,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)(expr1, imp.selectors), sym) + assignType(cpy.Import(imp)(imp.impliedOnly, expr1, imp.selectors), sym) } def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 962642e0292c..86bfdb686d7d 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -149,6 +149,8 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("tests/neg-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")) + compileFilesInDir("tests/neg-custom-args/allow-double-bindings", allowDoubleBindings) + compileDir("tests/neg-custom-args/impl-conv", defaultOptions.and("-Xfatal-warnings", "-feature")) + + compileFile("tests/neg-custom-args/implicit-conversions.scala", defaultOptions.and("-Xfatal-warnings", "-feature")) + + compileFile("tests/neg-custom-args/implicit-conversions-old.scala", defaultOptions.and("-Xfatal-warnings", "-feature")) + compileFile("tests/neg-custom-args/i3246.scala", scala2Mode) + compileFile("tests/neg-custom-args/overrideClass.scala", scala2Mode) + compileFile("tests/neg-custom-args/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")) + diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index f9e415173ee1..8d5f692aa65d 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -715,8 +715,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx assertMessageCount(1, messages) - val TypesAndTraitsCantBeImplicit(symbol) :: Nil = messages - assertEquals("trait S", symbol.show) + val TypesAndTraitsCantBeImplicit() :: Nil = messages } @Test def onlyClassesCanBeAbstract = diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index fd630475e10c..bbdbb5bd5842 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -327,7 +327,7 @@ AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args) -Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +Import ::= ‘import’ [‘implied’] ImportExpr {‘,’ ImportExpr} ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels) ImportSelectors ::= ‘{’ {ImportSelector ‘,’} (ImportSelector | ‘_’) ‘}’ ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] Ident(name), Pair(id, id) diff --git a/docs/docs/reference/contextual/conversions.md b/docs/docs/reference/contextual/conversions.md index e838964a5144..63da00aa2dc7 100644 --- a/docs/docs/reference/contextual/conversions.md +++ b/docs/docs/reference/contextual/conversions.md @@ -54,15 +54,15 @@ object Completions { // these always come with CompletionArg // They can be invoked explicitly, e.g. // - // CompletionArg.from(statusCode) + // CompletionArg.fromStatusCode(statusCode) - implied from for Conversion[String, CompletionArg] { + implied fromString for Conversion[String, CompletionArg] { def apply(s: String) = CompletionArg.Error(s) } - implied from for Conversion[Future[HttpResponse], CompletionArg] { + implied fromFuture for Conversion[Future[HttpResponse], CompletionArg] { def apply(f: Future[HttpResponse]) = CompletionArg.Response(f) } - implied from for Conversion[Future[StatusCode], CompletionArg] { + implied fromStatusCode for Conversion[Future[StatusCode], CompletionArg] { def apply(code: Future[StatusCode]) = CompletionArg.Status(code) } } diff --git a/docs/docs/reference/contextual/import-implied.md b/docs/docs/reference/contextual/import-implied.md new file mode 100644 index 000000000000..1024b59b6cdd --- /dev/null +++ b/docs/docs/reference/contextual/import-implied.md @@ -0,0 +1,29 @@ +--- +layout: doc-page +title: "Implied Imports" +--- + +A special form of import is used to import implied instances. Example: +```scala +object A { + class TC + implied tc for TC + def f given TC = ??? +} +object B { + import A._ + import implied A._ +} +``` +In the code above, the `import A._` clause of object `B` will import all members +of `A` _except_ the implied instance `tc`. Conversely, the second import `import implied A._` will import _only_ that implied instance. + +Generally, a normal import clause brings all definitions except implied instances into scope whereas an `import implied` clause beings only implied instances into scope. + +There are two main benefits arising from these rules: + + - It is made clearer where implied instances in scope are coming from. In particular, it is not possible to hide imported implied instances in a long list of regular imports. + - It enables importing all implied instances + 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. diff --git a/docs/docs/reference/contextual/query-types.md b/docs/docs/reference/contextual/query-types.md index 68adcb79b4d5..e903fdeedbc9 100644 --- a/docs/docs/reference/contextual/query-types.md +++ b/docs/docs/reference/contextual/query-types.md @@ -133,7 +133,7 @@ object PostConditions { } object Test { - import PostConditions.ensuring + import PostConditions.{ensuring, result} val s = List(1, 2, 3).sum.ensuring(result == 6) } ``` diff --git a/docs/sidebar.yml b/docs/sidebar.yml index b73d695be4af..be5d637b018b 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -47,6 +47,8 @@ sidebar: url: docs/reference/contextual/inferable-params.html - title: Context Bounds url: docs/reference/contextual/context-bounds.html + - title: Implied Imports + url: docs/reference/contextual/import-implied.html - title: Extension Methods url: docs/reference/contextual/extension-methods.html - title: Implementing Typeclasses diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 0432c4d83056..e24b5c0acc5a 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -200,8 +200,8 @@ trait Printers this += ", " += self += ", " ++= body += ")" case PackageDef(name, owner) => this += "PackageDef(\"" += name += "\", " += owner += ")" - case Import(expr, selectors) => - this += "Import(" += expr += ", " ++= selectors += ")" + case Import(impliedOnly, expr, selectors) => + this += "Import(" += impliedOnly += ", " += expr += ", " ++= selectors += ")" case PackageClause(pid, stats) => this += "PackageClause(" += pid += ", " ++= stats += ")" } @@ -566,7 +566,7 @@ trait Printers val stats1 = stats.collect { case IsPackageClause(stat) => stat case IsDefinition(stat) if !(stat.symbol.flags.is(Flags.Object) && stat.symbol.flags.is(Flags.Lazy)) => stat - case stat @ Import(_, _) => stat + case stat @ Import(_, _, _) => stat } name match { case Term.Ident("") => @@ -577,8 +577,9 @@ trait Printers inBlock(printTrees(stats1, lineBreak())) } - case Import(expr, selectors) => + case Import(impliedOnly, expr, selectors) => this += "import " + if (impliedOnly) this += "implied " printTree(expr) this += "." printImportSelectors(selectors) @@ -671,7 +672,7 @@ trait Printers } val stats1 = stats.collect { case IsDefinition(stat) if keepDefinition(stat) => stat - case stat @ Import(_, _) => stat + case stat @ Import(_, _, _) => stat case IsTerm(stat) => stat } diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index 5708aa545ed5..2b7930902dc7 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -72,12 +72,13 @@ trait TreeOps extends Core { val Import: ImportModule abstract class ImportModule { - def apply(expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import - def copy(original: Import)(expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import - def unapply(imp: Tree)(implicit ctx: Context): Option[(Term, List[ImportSelector])] + def apply(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import + def copy(original: Import)(impliedOnly: Boolean, expr: Term, selectors: List[ImportSelector])(implicit ctx: Context): Import + def unapply(imp: Tree)(implicit ctx: Context): Option[(Boolean, Term, List[ImportSelector])] } trait ImportAPI { + def impliedOnly: Boolean def expr(implicit ctx: Context): Term def selectors(implicit ctx: Context): List[ImportSelector] } diff --git a/library/src/scala/tasty/reflect/TreeUtils.scala b/library/src/scala/tasty/reflect/TreeUtils.scala index 9b821f24b202..36c0b5e2ca60 100644 --- a/library/src/scala/tasty/reflect/TreeUtils.scala +++ b/library/src/scala/tasty/reflect/TreeUtils.scala @@ -80,7 +80,7 @@ trait TreeUtils case IsDefinition(cdef @ ClassDef(_, constr, parents, derived, self, body)) => implicit val ctx = localCtx(cdef) foldTrees(foldTrees(foldTypeTrees(foldParents(foldTree(x, constr), parents), derived), self), body) - case Import(expr, selectors) => + case Import(_, expr, _) => foldTree(x, expr) case IsPackageClause(clause @ PackageClause(pid, stats)) => foldTrees(foldTree(x, pid), stats)(clause.symbol.localContext) @@ -160,7 +160,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)(transformTerm(tree.expr), tree.selectors) + Import.copy(tree)(tree.impliedOnly, transformTerm(tree.expr), tree.selectors) case IsStatement(tree) => transformStatement(tree) } @@ -185,7 +185,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)(transformTerm(tree.expr), tree.selectors) + Import.copy(tree)(tree.impliedOnly, transformTerm(tree.expr), tree.selectors) } } diff --git a/library/src/scalaShadowing/language.scala b/library/src/scalaShadowing/language.scala index 50d2bf069c04..a64c985e5a10 100644 --- a/library/src/scalaShadowing/language.scala +++ b/library/src/scalaShadowing/language.scala @@ -92,8 +92,10 @@ object language { */ @volatile implicit lazy val reflectiveCalls: reflectiveCalls = languageFeature.reflectiveCalls - /** Only where enabled, definitions of implicit conversions are allowed. An - * implicit conversion is an implicit value of unary function type `A => B`, + /** Only where enabled, definitions of legacy implicit conversions and certain uses + * of implicit conversions are allowed. + * + * A legacy implicit conversion is an implicit value of unary function type `A => B`, * or an implicit method that has in its first parameter section a single, * non-implicit parameter. Examples: * @@ -103,17 +105,36 @@ object language { * implicit def listToX(xs: List[T])(implicit f: T => X): X = ... * }}} * - * implicit values of other types are not affected, and neither are implicit - * classes. + * Implicit values of other types are not affected, and neither are implicit + * classes. In particular, implied instances of the scala.Conversion class can be + * defined without having to import the language feature. + * + * The language import is also required to enable _uses_ of implicit conversions + * unless the conversion in question is co-defined with the type to which it maps. + * Co-defined means: defined in the companion object of the class of the result type. + * Examples: + * + * {{{ + * class A + * class B + * object B { + * implied a2b for Conversion[A, B] { ... } + * } + * object C { + * implied b2a for Conversion[B, A] { ... } + * } + * import implied B._ + * import implied C._ + * val x: A = new B // language import required + * val x: B = new A // no import necessary since a2b is co-defined with B + * }}} * * '''Why keep the feature?''' Implicit conversions are central to many aspects * of Scala’s core libraries. * * '''Why control it?''' Implicit conversions are known to cause many pitfalls - * if over-used. And there is a tendency to over-use them because they look - * very powerful and their effects seem to be easy to understand. Also, in - * most situations using implicit parameters leads to a better design than - * implicit conversions. + * if over-used. This holds in particular for implicit conversions defined after + * the fact between unrelated types. * * @group production */ diff --git a/tests/neg-custom-args/implicit-conversions-old.scala b/tests/neg-custom-args/implicit-conversions-old.scala new file mode 100644 index 000000000000..6abc2a33dfca --- /dev/null +++ b/tests/neg-custom-args/implicit-conversions-old.scala @@ -0,0 +1,23 @@ +class A +class B + +object A { + + implicit def a2b(x: A): B = ??? // error under -Xfatal-warnings -feature + + implicit def b2a(x: B): A = ??? // error under -Xfatal-warnings -feature +} + +class C + +object D { + implicit def a2c(x: A): C = ??? // error under -Xfatal-warnings -feature +} + +object Test { + import D._ + + val x1: A = new B + val x2: B = new A // error under -Xfatal-warnings -feature + val x3: C = new A // error under -Xfatal-warnings -feature +} \ No newline at end of file diff --git a/tests/neg-custom-args/implicit-conversions.scala b/tests/neg-custom-args/implicit-conversions.scala new file mode 100644 index 000000000000..a878b1112573 --- /dev/null +++ b/tests/neg-custom-args/implicit-conversions.scala @@ -0,0 +1,29 @@ +class A +class B + +object A { + + implied for Conversion[A, B] { + def apply(x: A): B = ??? + } + + implied for Conversion[B, A] { + def apply(x: B): A = ??? + } +} + +class C + +object D { + implied for Conversion[A, C] { + def apply(x: A): C = ??? + } +} + +object Test { + import implied D._ + + val x1: A = new B + val x2: B = new A // error under -Xfatal-warnings -feature + val x3: C = new A // error under -Xfatal-warnings -feature +} \ No newline at end of file diff --git a/tests/neg/import-implied.scala b/tests/neg/import-implied.scala new file mode 100644 index 000000000000..2dbbd6763625 --- /dev/null +++ b/tests/neg/import-implied.scala @@ -0,0 +1,29 @@ +class TC +object A { + implied tc for TC + def foo given TC = () +} +object B { + import A._ + foo // error: no implicit argument was found + foo given tc // error: not found: tc + foo given A.tc // ok +} +object C { + import A._ + import implied A.tc + foo // ok + foo given tc // ok +} +object D { + import A.foo + import implied A._ + foo // ok + foo given tc // ok +} +object E { + import A._ + import implied A._ + foo // ok + foo given tc // ok +} diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index f5358aee3d4a..b6e598892f4c 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(expr: Term, selector: List[ImportSelector]) extends Statement + case class Import(impliedOnly: Boolean, expr: Term, selector: List[ImportSelector]) extends Statement enum ImportSelector { case SimpleSelector(id: Id) diff --git a/tests/pos/postconditions.scala b/tests/pos/postconditions.scala new file mode 100644 index 000000000000..0ee5970f0d5a --- /dev/null +++ b/tests/pos/postconditions.scala @@ -0,0 +1,21 @@ +object PostConditions { + opaque type WrappedResult[T] = T + + private object WrappedResult { + def wrap[T](x: T): WrappedResult[T] = x + def unwrap[T](x: WrappedResult[T]): T = x + } + + def result[T] given (r: WrappedResult[T]): T = WrappedResult.unwrap(r) + + def (x: T) ensuring [T](condition: given WrappedResult[T] => Boolean): T = { + implied for WrappedResult[T] = WrappedResult.wrap(x) + assert(condition) + x + } +} + +object Test { + import PostConditions.{ensuring, result} + val s = List(1, 2, 3).sum.ensuring(result == 6) +} \ No newline at end of file diff --git a/tests/pos/reference/instances.scala b/tests/pos/reference/instances.scala index 925fec9ca6ae..d5a838845af9 100644 --- a/tests/pos/reference/instances.scala +++ b/tests/pos/reference/instances.scala @@ -140,8 +140,10 @@ object Instances extends Common { class Token(str: String) - implied StringToToken for Conversion[String, Token] { - def apply(str: String): Token = new Token(str) + object Token { + implied StringToToken for Conversion[String, Token] { + def apply(str: String): Token = new Token(str) + } } val x: Token = "if" @@ -246,7 +248,8 @@ object Implicits extends Common { object Test extends App { Instances.test() - import PostConditions._ + import PostConditions.result + import implied PostConditions._ val s = List(1, 2, 3).sum s.ensuring(result == 6) } @@ -263,6 +266,24 @@ object Completions { case Response(f: Future[HttpResponse]) case Status(code: Future[StatusCode]) } + object CompletionArg { + + // conversions defining the possible arguments to pass to `complete` + // these always come with CompletionArg + // They can be invoked explicitly, e.g. + // + // CompletionArg.from(statusCode) + + implied fromString for Conversion[String, CompletionArg] { + def apply(s: String) = CompletionArg.Error(s) + } + implied fromFuture for Conversion[Future[HttpResponse], CompletionArg] { + def apply(f: Future[HttpResponse]) = CompletionArg.Response(f) + } + implied fromStatusCode for Conversion[Future[StatusCode], CompletionArg] { + def apply(code: Future[StatusCode]) = CompletionArg.Status(code) + } + } import CompletionArg._ def complete[T](arg: CompletionArg) = arg match { @@ -270,15 +291,4 @@ object Completions { case Response(f) => ??? case Status(code) => ??? } - - // conversions defining the possible arguments to pass to `complete` - implied stringArg for Conversion[String, CompletionArg] { - def apply(s: String) = CompletionArg.Error(s) - } - implied responseArg for Conversion[Future[HttpResponse], CompletionArg] { - def apply(f: Future[HttpResponse]) = CompletionArg.Response(f) - } - implied statusArg for Conversion[Future[StatusCode], CompletionArg] { - def apply(code: Future[StatusCode]) = CompletionArg.Status(code) - } } \ No newline at end of file diff --git a/tests/run/derive-multi.scala b/tests/run/derive-multi.scala index 14cb73926119..b78e48aa3843 100644 --- a/tests/run/derive-multi.scala +++ b/tests/run/derive-multi.scala @@ -30,6 +30,7 @@ case class Triple[S, T, U] derives A, B object Test1 { import Lst._ + import implied Lst._ implicitly[A] }