diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 1dc99bb8550f..e992a53a4d1b 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -243,6 +243,8 @@ object Denotations { */ def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation + override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation + /** If this is a SingleDenotation, return it, otherwise throw a TypeError */ def checkUnique(implicit ctx: Context): SingleDenotation = suchThat(alwaysTrue) @@ -1253,6 +1255,8 @@ object Denotations { else sd1 else sd2 } + override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation = + derivedUnionDenotation(denot1.filterWithPredicate(p), denot2.filterWithPredicate(p)) def hasAltWith(p: SingleDenotation => Boolean): Boolean = denot1.hasAltWith(p) || denot2.hasAltWith(p) def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = { diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index cceebe50b219..9223e06d4724 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -592,6 +592,8 @@ object Flags { final val ImplicitOrImpliedOrGiven = Implicit | Implied | Given final val ImplicitOrGiven = Implicit | Given + final val ImpliedOrGiven = Implied | Given + final val ImplicitOrImpliedOrGivenTerm = ImplicitOrImpliedOrGiven.toTermFlags /** Flags retained in export forwarders */ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 3bb382967280..cf3352ac6c4c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -63,6 +63,7 @@ Standard-Section: "ASTs" TopLevelStat* Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)? Selector = IMPORTED name_NameRef -- name RENAMED to_NameRef -- => name + BOUNDED type_Term? -- for type TypeParam = TYPEPARAM Length NameRef type_Term Modifier* -- modifiers name bounds Params = PARAMS Length Param* @@ -80,7 +81,7 @@ Standard-Section: "ASTs" TopLevelStat* APPLY Length fn_Term arg_Term* -- fn(args) TYPEAPPLY Length fn_Term arg_Type* -- fn[args] SUPER Length this_Term mixinTypeIdent_Tree? -- super[mixin] - TYPED Length expr_Term ascriptionType_Tern -- expr: ascription + TYPED Length expr_Term ascriptionType_Term -- expr: ascription ASSIGN Length lhs_Term rhs_Term -- lhs = rhs BLOCK Length expr_Term Stat* -- { stats; expr } INLINED Length expr_Term call_Term? ValOrDefDef* -- Inlined code from call, with given body `expr` and given bindings @@ -369,6 +370,7 @@ object TastyFormat { final val RECtype = 91 final val TYPEALIAS = 92 final val SINGLETONtpt = 93 + final val BOUNDED = 94 // Cat. 4: tag Nat AST @@ -461,7 +463,7 @@ object TastyFormat { def isLegalTag(tag: Int): Boolean = firstSimpleTreeTag <= tag && tag <= EXPORTED || firstNatTreeTag <= tag && tag <= SYMBOLconst || - firstASTTreeTag <= tag && tag <= SINGLETONtpt || + firstASTTreeTag <= tag && tag <= BOUNDED || firstNatASTTreeTag <= tag && tag <= NAMEDARG || firstLengthTreeTag <= tag && tag <= MATCHtpt || tag == HOLE @@ -601,6 +603,7 @@ object TastyFormat { case PARAM => "PARAM" case IMPORTED => "IMPORTED" case RENAMED => "RENAMED" + case BOUNDED => "BOUNDED" case APPLY => "APPLY" case TYPEAPPLY => "TYPEAPPLY" case NEW => "NEW" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a0413437eea8..dbb24b42076c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -606,6 +606,10 @@ class TreePickler(pickler: TastyPickler) { pickleSelector(RENAMED, to) case id @ Ident(_) => pickleSelector(IMPORTED, id) + case bounded @ TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(tpt)) => + registerTreeAddr(bounded) + writeByte(BOUNDED) + pickleTree(tpt) } def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index aa3993ff9b01..1a0b052682d2 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -973,6 +973,11 @@ class TreeUnpickler(reader: TastyReader, case _ => from :: readSelectors() } + case BOUNDED => + val start = currentAddr + readByte() + val bounded = setSpan(start, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(readTpt()))) + bounded :: readSelectors() case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ddd9e2a6cb20..a7603acd963b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -627,9 +627,6 @@ object Parsers { def wildcardIdent(): Ident = atSpan(accept(USCORE)) { Ident(nme.WILDCARD) } - def termIdentOrWildcard(): Ident = - if (in.token == USCORE) wildcardIdent() else termIdent() - /** Accept identifier acting as a selector on given tree `t`. */ def selector(t: Tree): Tree = atSpan(startOffset(t), in.offset) { Select(t, ident()) } @@ -2310,8 +2307,57 @@ object Parsers { */ def importExpr(importImplied: Boolean, mkTree: ImportConstr): () => Tree = { + /** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’ + * FinalSelector ::= ImportSelector + * | ‘_’ + * | ‘for’ InfixType {‘,’ InfixType} + */ + def importSelectors(): List[Tree] = in.token match { + case USCORE => + wildcardIdent() :: Nil + case FOR => + if (!importImplied) + syntaxError(em"`for` qualifier only allowed in `import implied`") + atSpan(in.skipToken()) { + var t = infixType() + while (in.token == COMMA) { + val op = atSpan(in.skipToken()) { Ident(tpnme.raw.BAR) } + t = InfixOp(t, op, infixType()) + } + TypeBoundsTree(EmptyTree, t) + } :: Nil + case _ => + importSelector() :: { + if (in.token == COMMA) { + in.nextToken() + importSelectors() + } + else Nil + } + } + + /** ImportSelector ::= id [`=>' id | `=>' `_'] + */ + def importSelector(): Tree = { + val from = termIdent() + if (in.token == ARROW) + atSpan(startOffset(from), in.skipToken()) { + val start = in.offset + val to = if (in.token == USCORE) wildcardIdent() else termIdent() + val toWithPos = + if (to.name == nme.ERROR) + // error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span. + // Some testcases would then fail in Positioned.checkPos. Set a span anyway! + atSpan(start, start, in.lastOffset)(to) + else + to + Thicket(from, toWithPos) + } + else from + } + val handleImport: Tree => Tree = { tree: Tree => - if (in.token == USCORE) mkTree(importImplied, tree, importSelector() :: Nil) + if (in.token == USCORE) mkTree(importImplied, tree, wildcardIdent() :: Nil) else if (in.token == LBRACE) mkTree(importImplied, tree, inBraces(importSelectors())) else tree } @@ -2333,41 +2379,6 @@ object Parsers { } } - /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' - */ - def importSelectors(): List[Tree] = { - val sel = importSelector() - if (in.token == RBRACE) sel :: Nil - else { - sel :: { - if (!isWildcardArg(sel) && in.token == COMMA) { - in.nextToken() - importSelectors() - } - else Nil - } - } - } - - /** ImportSelector ::= id [`=>' id | `=>' `_'] - */ - def importSelector(): Tree = { - val from = termIdentOrWildcard() - if (from.name != nme.WILDCARD && in.token == ARROW) - atSpan(startOffset(from), in.skipToken()) { - val start = in.offset - val to = termIdentOrWildcard() - val toWithPos = - if (to.name == nme.ERROR) - // error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span. - // Some testcases would then fail in Positioned.checkPos. Set a span anyway! - atSpan(start, start, in.lastOffset)(to) - else - to - Thicket(from, toWithPos) - } - else from - } def posMods(start: Int, mods: Modifiers): Modifiers = { in.nextToken() diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 21612a1e32bb..5f614501edeb 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -501,7 +501,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case Import(importImplied, expr, selectors) => def selectorText(sel: Tree): Text = sel match { case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) - case _ => toTextGlobal(sel) + case _: Ident => toTextGlobal(sel) + case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt) } val selectorsText: Text = selectors match { case id :: Nil => toText(id) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index ee6938b78248..2f34a1005031 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -25,6 +25,11 @@ object TypeUtils { case _ => if (ctx.erasedTypes) MethodType(Nil, self) else ExprType(self) } + def widenToParents(implicit ctx: Context): Type = self.parents match { + case Nil => self + case ps => ps.reduceLeft(AndType(_, _)) + } + /** The arity of this tuple type, which can be made up of Unit, TupleX and `*:` pairs, * or -1 if this is not a tuple type. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index cc9500d395ab..c9b930b37bbb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -21,6 +21,7 @@ import StdNames._ import NameKinds.DefaultGetterName import ProtoTypes._ import Inferencing._ +import transform.TypeUtils._ import collection.mutable import config.Printers.{overload, typr, unapp} @@ -1332,6 +1333,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * iff * * T => R <:s U => R + * + * Also: If a compared type refers to an implied object or its module class, use + * the intersection of its parent classes instead. */ def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) = if (ctx.mode.is(Mode.OldOverloadingResolution)) @@ -1347,7 +1351,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case _ => mapOver(t) } } - (flip(tp1) relaxed_<:< flip(tp2)) || viewExists(tp1, tp2) + def prepare(tp: Type) = tp.stripTypeVar match { + case tp: NamedType if tp.symbol.is(Module) && tp.symbol.sourceModule.is(Implied) => + flip(tp.widen.widenToParents) + case _ => flip(tp) + } + (prepare(tp1) relaxed_<:< prepare(tp2)) || viewExists(tp1, tp2) } /** Widen the result type of synthetic implied methods from the implementation class to the @@ -1375,11 +1384,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case pt: PolyType => pt.derivedLambdaType(pt.paramNames, pt.paramInfos, widenImplied(pt.resultType, alt)) case _ => - if (alt.symbol.is(SyntheticImpliedMethod)) - tp.parents match { - case Nil => tp - case ps => ps.reduceLeft(AndType(_, _)) - } + if (alt.symbol.is(SyntheticImpliedMethod)) tp.widenToParents else tp } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 7518c49e234e..678f4f8c0791 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -10,6 +10,8 @@ import util.SimpleIdentityMap import Symbols._, Names._, Types._, Contexts._, StdNames._, Flags._ import Implicits.RenamedImplicitRef import printing.Texts.Text +import ProtoTypes.NoViewsAllowed.normalizedCompatible +import Decorators._ object ImportInfo { /** The import info for a root import from given symbol `sym` */ @@ -55,39 +57,41 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], /** The names that are excluded from any wildcard import */ def excluded: Set[TermName] = { ensureInitialized(); myExcluded } - /** A mapping from renamed to original names */ - def reverseMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myMapped } + /** A mapping from original to renamed names */ + def forwardMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myForwardMapping } - /** The original names imported by-name before renaming */ - def originals: Set[TermName] = { ensureInitialized(); myOriginals } + /** A mapping from renamed to original names */ + def reverseMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myReverseMapping } /** Does the import clause end with wildcard? */ def isWildcardImport: Boolean = { ensureInitialized(); myWildcardImport } private[this] var myExcluded: Set[TermName] = null - private[this] var myMapped: SimpleIdentityMap[TermName, TermName] = null - private[this] var myOriginals: Set[TermName] = null + private[this] var myForwardMapping: SimpleIdentityMap[TermName, TermName] = null + private[this] var myReverseMapping: SimpleIdentityMap[TermName, TermName] = null private[this] var myWildcardImport: Boolean = false /** Compute info relating to the selector list */ private def ensureInitialized(): Unit = if (myExcluded == null) { myExcluded = Set() - myMapped = SimpleIdentityMap.Empty - myOriginals = Set() + myForwardMapping = SimpleIdentityMap.Empty + myReverseMapping = SimpleIdentityMap.Empty def recur(sels: List[untpd.Tree]): Unit = sels match { case sel :: sels1 => sel match { case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) => myExcluded += name case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) => - myMapped = myMapped.updated(to, from) + myForwardMapping = myForwardMapping.updated(from, to) + myReverseMapping = myReverseMapping.updated(to, from) myExcluded += from - myOriginals += from case Ident(nme.WILDCARD) => myWildcardImport = true case Ident(name: TermName) => - myMapped = myMapped.updated(name, name) - myOriginals += name + myForwardMapping = myForwardMapping.updated(name, name) + myReverseMapping = myReverseMapping.updated(name, name) + case TypeBoundsTree(_, tpt) => + myWildcardImport = true // details are handled separately in impliedBounds } recur(sels1) case nil => @@ -95,6 +99,20 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], recur(selectors) } + private[this] var myImpliedBound: Type = null + + def impliedBound(implicit ctx: Context): Type = { + if (myImpliedBound == null) + myImpliedBound = selectors.lastOption match { + case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe + case Some(TypeBoundsTree(_, tpt)) => + myImpliedBound = NoType + ctx.typer.typedAheadType(tpt).tpe + case _ => NoType + } + myImpliedBound + } + private def implicitFlag(implicit ctx: Context) = if (importImplied || ctx.mode.is(Mode.FindHiddenImplicits)) ImplicitOrImpliedOrGiven else Implicit @@ -102,15 +120,24 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], /** The implicit references imported by this import clause */ def importedImplicits(implicit ctx: Context): List[ImplicitRef] = { val pre = site - if (isWildcardImport) { - val refs = pre.implicitMembers(implicitFlag) - if (excluded.isEmpty) refs - else refs filterNot (ref => excluded contains ref.name.toTermName) - } else - for { + if (isWildcardImport) + pre.implicitMembers(implicitFlag).flatMap { ref => + val name = ref.name.toTermName + if (excluded.contains(name)) Nil + else { + val renamed = forwardMapping(ref.name) + if (renamed == ref.name) ref :: Nil + else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil + else if (!impliedBound.exists || + normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil + else Nil + } + } + else + for renamed <- reverseMapping.keys denot <- pre.member(reverseMapping(renamed)).altsWith(_ is implicitFlag) - } yield { + yield { val original = reverseMapping(renamed) val ref = TermRef(pre, original, denot) if (renamed == original) ref @@ -149,7 +176,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], def featureImported(feature: TermName, owner: Symbol)(implicit ctx: Context): Boolean = { def compute = { val isImportOwner = site.widen.typeSymbol.eq(owner) - if (isImportOwner && originals.contains(feature)) true + if (isImportOwner && forwardMapping.contains(feature)) true else if (isImportOwner && excluded.contains(feature)) false else { var c = ctx.outer diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 985dd512fa15..feec8de3ad4f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -177,7 +177,7 @@ class Typer extends Namer previous } - def selection(imp: ImportInfo, name: Name) = + def selection(imp: ImportInfo, name: Name, checkBounds: Boolean) = if (imp.sym.isCompleting) { ctx.warning(i"cyclic ${imp.sym}, ignored", posd.sourcePos) NoType @@ -188,7 +188,11 @@ class Typer extends Namer var reqd = required var excl = EmptyFlags if (imp.importImplied) reqd |= Implied else excl |= Implied - val denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx) + var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx) + if (checkBounds && imp.impliedBound.exists) + denot = denot.filterWithPredicate(mbr => + NoViewsAllowed.normalizedCompatible(mbr.info, imp.impliedBound, keepConstraint = false)) + // 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 @@ -209,11 +213,10 @@ class Typer extends Namer } def unambiguousSelection(name: Name) = - checkUnambiguous(selection(imp, name)) + checkUnambiguous(selection(imp, name, checkBounds = false)) selector match { - case Thicket(fromId :: Ident(Name) :: _) => - val Ident(from) = fromId + case Thicket(Ident(from) :: Ident(Name) :: _) => unambiguousSelection(if (name.isTypeName) from.toTypeName else from) case Ident(Name) => unambiguousSelection(name) @@ -231,7 +234,7 @@ class Typer extends Namer */ def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR) - selection(imp, name) + selection(imp, name, checkBounds = imp.importImplied) else NoType /** Is (some alternative of) the given predenotation `denot` @@ -1785,7 +1788,12 @@ class Typer extends Namer def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkLegalImportPath(expr1) - assignType(cpy.Import(imp)(imp.importImplied, expr1, imp.selectors), sym) + val selectors1: List[untpd.Tree] = imp.selectors map { + case sel @ TypeBoundsTree(_, tpt) => + untpd.cpy.TypeBoundsTree(sel)(sel.lo, untpd.TypedSplice(typedType(tpt))) + case sel => sel + } + assignType(cpy.Import(imp)(imp.importImplied, expr1, selectors1), sym) } def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index a8a02c5fe03e..278dea2d4d41 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -335,8 +335,11 @@ Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Import ::= ‘import’ [‘implied’] ImportExpr {‘,’ ImportExpr} ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels) -ImportSelectors ::= ‘{’ {ImportSelector ‘,’} (ImportSelector | ‘_’) ‘}’ -ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] Ident(name), Pair(id, id) +ImportSelectors ::= ‘{’ {ImportSelector ‘,’} FinalSelector ‘}’ +FinalSelector ::= ImportSelector Ident(name) + | ‘_’ Pair(id, id) + | ‘for’ InfixType {‘,’ InfixType} TypeBoundsTree(EmptyTree, tpt) +ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] Export ::= ‘export’ [‘implied’] ImportExpr {‘,’ ImportExpr} ``` diff --git a/docs/docs/reference/contextual/import-implied.md b/docs/docs/reference/contextual/import-implied.md index edf7f77dbcbd..137384baf7a7 100644 --- a/docs/docs/reference/contextual/import-implied.md +++ b/docs/docs/reference/contextual/import-implied.md @@ -28,6 +28,35 @@ There are two main benefits arising from these rules: instances can be anonymous, so the usual recourse of using named imports is not practical. +### Importing By Type + +Since implied instances can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example: + +```scala +import implied A.{for TC} +``` +This imports any implied instance in `A` that has a type which conforms tp `TC`. There can be several bounding types following a `for` and bounding types can contain wildcards. +For instance, assuming the object +```scala +object Instances { + implied intOrd for Ordering[Int] + implied [T: Ordering] listOrd for Ordering[List[T]] + implied ec for ExecutionContext = ... + implied im for Monoid[Int] +} +``` +the import +``` +import implied Instances.{for Ordering[_], ExecutionContext} +``` +would import the `intOrd`, `listOrd`, and `ec` instances but leave out the `im` instance, since it fits none of the specified bounds. + +By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, the import clause +``` +import implied Instances.{im, for Ordering[_]} +``` +would import `im`, `intOrd`, and `listOrd` but leave out `ec`. By-type imports cannot be mixed with a wildcard import in the same import clause. + ### Migration The rules for `import implied` above have the consequence that a library diff --git a/tests/neg/implied-for.scala b/tests/neg/implied-for.scala new file mode 100644 index 000000000000..a5aa18bb59ef --- /dev/null +++ b/tests/neg/implied-for.scala @@ -0,0 +1,19 @@ +trait T +class B extends T +class C extends T + +object A { + implied b for B + implied c for C +} + +object Test extends App { + import A._ + import implied A.{for B} + + val x: B = b // OK + println(c) // error: not found + + the[C] // error + +} \ No newline at end of file diff --git a/tests/run/implied-for.scala b/tests/run/implied-for.scala new file mode 100644 index 000000000000..9b4486ec33a7 --- /dev/null +++ b/tests/run/implied-for.scala @@ -0,0 +1,57 @@ +trait T + +object A { + + class B extends T + class C extends T + class D[T] + + implied b for B + implied c for C + implied t for T + implied d for D[Int] +} + +object Test extends App { + import A._ + import implied A.{t, for B, D[_]} + + val x1: B = b + val x2: T = t + val x3: D[Int] = d + + assert(the[T].isInstanceOf[B]) + assert(the[D[Int]].isInstanceOf[D[_]]) +} + +class Ordering[T] +class ExecutionContext +class Monoid[T] + +object Instances { + implied intOrd for Ordering[Int] + implied listOrd[T] for Ordering[List[T]] given Ordering[T] + implied ec for ExecutionContext + implied im for Monoid[Int] +} + +object Test2 { + import implied Instances.{for Ordering[_], ExecutionContext} + val x = intOrd + val y = listOrd[Int] + val z = ec + the[Ordering[Int]] + the[Ordering[List[Int]]] + the[ExecutionContext] +} + +object Test3 { + import implied Instances.{im, for Ordering[_]} + val x = intOrd + val y = listOrd[Int] + val z = im + the[Ordering[Int]] + the[Ordering[List[Int]]] + the[Monoid[Int]] +} +