diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
19 files changed, 889 insertions, 868 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 309b80f9ba..2b6a4c763a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -212,8 +212,8 @@ trait Checkable { ) /** Are all children of these symbols pairwise irreconcilable? */ def allChildrenAreIrreconcilable(sym1: Symbol, sym2: Symbol) = ( - sym1.children.toList forall (c1 => - sym2.children.toList forall (c2 => + sym1.sealedChildren.toList forall (c1 => + sym2.sealedChildren.toList forall (c2 => areIrreconcilableAsParents(c1, c2) ) ) diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 56ed0ee16c..2cd4785fbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -40,9 +40,10 @@ abstract class ConstantFolder { if ((x ne null) && x.tag != UnitTag) tree setType ConstantType(x) else tree } catch { - case _: ArithmeticException => tree // the code will crash at runtime, - // but that is better than the - // compiler itself crashing + case e: ArithmeticException => + if (settings.warnConstant) + warning(tree.pos, s"Evaluation of a constant expression results in an arithmetic error: ${e.getMessage}") + tree } private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match { @@ -75,7 +76,7 @@ abstract class ConstantFolder { case nme.AND => Constant(x.booleanValue & y.booleanValue) case nme.EQ => Constant(x.booleanValue == y.booleanValue) case nme.NE => Constant(x.booleanValue != y.booleanValue) - case _ => null + case _ => null } private def foldSubrangeOp(op: Name, x: Constant, y: Constant): Constant = op match { case nme.OR => Constant(x.intValue | y.intValue) @@ -95,14 +96,20 @@ abstract class ConstantFolder { case nme.MUL => Constant(x.intValue * y.intValue) case nme.DIV => Constant(x.intValue / y.intValue) case nme.MOD => Constant(x.intValue % y.intValue) - case _ => null + case _ => null } private def foldLongOp(op: Name, x: Constant, y: Constant): Constant = op match { case nme.OR => Constant(x.longValue | y.longValue) case nme.XOR => Constant(x.longValue ^ y.longValue) case nme.AND => Constant(x.longValue & y.longValue) - case nme.LSL => Constant(x.longValue << y.longValue) + case nme.LSL if x.tag <= IntTag + => Constant(x.intValue << y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) + case nme.LSL => Constant(x.longValue << y.longValue) + case nme.LSR if x.tag <= IntTag + => Constant(x.intValue >>> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) case nme.LSR => Constant(x.longValue >>> y.longValue) + case nme.ASR if x.tag <= IntTag + => Constant(x.intValue >> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) case nme.ASR => Constant(x.longValue >> y.longValue) case nme.EQ => Constant(x.longValue == y.longValue) case nme.NE => Constant(x.longValue != y.longValue) @@ -115,7 +122,7 @@ abstract class ConstantFolder { case nme.MUL => Constant(x.longValue * y.longValue) case nme.DIV => Constant(x.longValue / y.longValue) case nme.MOD => Constant(x.longValue % y.longValue) - case _ => null + case _ => null } private def foldFloatOp(op: Name, x: Constant, y: Constant): Constant = op match { case nme.EQ => Constant(x.floatValue == y.floatValue) @@ -129,7 +136,7 @@ abstract class ConstantFolder { case nme.MUL => Constant(x.floatValue * y.floatValue) case nme.DIV => Constant(x.floatValue / y.floatValue) case nme.MOD => Constant(x.floatValue % y.floatValue) - case _ => null + case _ => null } private def foldDoubleOp(op: Name, x: Constant, y: Constant): Constant = op match { case nme.EQ => Constant(x.doubleValue == y.doubleValue) @@ -143,7 +150,7 @@ abstract class ConstantFolder { case nme.MUL => Constant(x.doubleValue * y.doubleValue) case nme.DIV => Constant(x.doubleValue / y.doubleValue) case nme.MOD => Constant(x.doubleValue % y.doubleValue) - case _ => null + case _ => null } private def foldBinop(op: Name, x: Constant, y: Constant): Constant = { @@ -152,7 +159,7 @@ abstract class ConstantFolder { else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag) else NoTag - try optag match { + optag match { case BooleanTag => foldBooleanOp(op, x, y) case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y) case LongTag => foldLongOp(op, x, y) @@ -161,8 +168,5 @@ abstract class ConstantFolder { case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue) case _ => null } - catch { - case ex: ArithmeticException => null - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 727f09290a..ccdff5c9a1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package typechecker import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString } -import symtab.Flags.IS_ERROR import scala.compat.Platform.EOL import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException @@ -1105,7 +1104,7 @@ trait ContextErrors { def GetterDefinedTwiceError(getter: Symbol) = issueSymbolTypeError(getter, getter+" is defined twice") - def ValOrValWithSetterSuffixError(tree: Tree) = + def ValOrVarWithSetterSuffixError(tree: Tree) = issueNormalTypeError(tree, "Names of vals or vars may not end in `_='") def PrivateThisCaseClassParameterError(tree: Tree) = @@ -1212,7 +1211,8 @@ trait ContextErrors { import definitions._ - def AmbiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo, + def AmbiguousImplicitError(info1: ImplicitInfo, tree1: Tree, + info2: ImplicitInfo, tree2: Tree, pre1: String, pre2: String, trailer: String) (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { @@ -1248,10 +1248,21 @@ trait ContextErrors { if (explanation == "") "" else "\n" + explanation ) } + + def treeTypeArgs(annotatedTree: Tree): List[String] = annotatedTree match { + case TypeApply(_, args) => args.map(_.toString) + case Block(_, Function(_, treeInfo.Applied(_, targs, _))) => targs.map(_.toString) // eta expansion, see neg/t9527b.scala + case _ => Nil + } + context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, - if (isView) viewMsg - else s"ambiguous implicit values:\n${coreMsg}match expected type $pt") - ) + (info1.sym, info2.sym) match { + case (ImplicitAmbiguousMsg(msg), _) => msg.format(treeTypeArgs(tree1)) + case (_, ImplicitAmbiguousMsg(msg)) => msg.format(treeTypeArgs(tree2)) + case (_, _) if isView => viewMsg + case (_, _) => s"ambiguous implicit values:\n${coreMsg}match expected type $pt" + } + )) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 5ec16e84bb..bcc1ed3e64 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -24,7 +24,8 @@ trait Contexts { self: Analyzer => object NoContext extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit, - null) { // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer` + // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer` + null) { enclClass = this enclMethod = this @@ -48,12 +49,11 @@ trait Contexts { self: Analyzer => def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - private lazy val startContext = { - NoContext.make( + private lazy val startContext = NoContext.make( Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, - rootMirror.RootClass.info.decls) - } + rootMirror.RootClass.info.decls + ) private lazy val allUsedSelectors = mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set() @@ -387,8 +387,10 @@ trait Contexts { self: Analyzer => @inline final def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) @inline final def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op) @inline final def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op) + @inline final def withImplicits[T](enabled: Boolean)(op: => T): T = if (enabled) withImplicitsEnabled(op) else withImplicitsDisabled(op) @inline final def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) @inline final def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) + @inline final def withMacros[T](enabled: Boolean)(op: => T): T = if (enabled) withMacrosEnabled(op) else withMacrosDisabled(op) @inline final def withinStarPatterns[T](op: => T): T = withMode(enabled = StarPatterns)(op) @inline final def withinSuperInit[T](op: => T): T = withMode(enabled = SuperInit)(op) @inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op) @@ -723,7 +725,6 @@ trait Contexts { self: Analyzer => ( (ab.isTerm || ab == rootMirror.RootClass) || (accessWithin(ab) || accessWithinLinked(ab)) && ( !sym.isLocalToThis - || sym.owner.isImplClass // allow private local accesses to impl classes || sym.isProtected && isSubThisType(pre, sym.owner) || pre =:= sym.owner.thisType ) @@ -806,11 +807,13 @@ trait Contexts { self: Analyzer => (e ne null) && (e.owner == scope) && (!settings.isScala212 || e.sym.exists) }) - private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) = - for { - sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives - if isQualifyingImplicit(name, sym, pre, imported = true) - } f(sym) + /** Do something with the symbols with name `name` imported via the import in `imp`, + * if any such symbol is accessible from this context and is a qualifying implicit. + */ + private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) = for { + sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives + if isQualifyingImplicit(name, sym, pre, imported = true) + } f(sym) private def collectImplicits(syms: Scope, pre: Type, imported: Boolean = false): List[ImplicitInfo] = for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported)) yield @@ -819,12 +822,8 @@ trait Contexts { self: Analyzer => private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = { val qual = imp.qual - val pre = - if (qual.tpe.typeSymbol.isPackageClass) - // SI-6225 important if the imported symbol is inherited by the package object. - singleType(qual.tpe, qual.tpe member nme.PACKAGE) - else - qual.tpe + val qualSym = qual.tpe.typeSymbol + val pre = qual.tpe def collect(sels: List[ImportSelector]): List[ImplicitInfo] = sels match { case List() => List() @@ -900,7 +899,8 @@ trait Contexts { self: Analyzer => Some(collectImplicitImports(imports.head)) } else if (owner.isPackageClass) { // the corresponding package object may contain implicit members. - Some(collectImplicits(owner.tpe.implicitMembers, owner.tpe)) + val pre = owner.packageObject.typeOfThis + Some(collectImplicits(pre.implicitMembers, pre)) } else Some(Nil) } @@ -967,51 +967,19 @@ trait Contexts { self: Analyzer => private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean, record: Boolean): Symbol = imp.importedSymbol(name, requireExplicit, record) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false)) - /** Is `sym` defined in package object of package `pkg`? - * Since sym may be defined in some parent of the package object, - * we cannot inspect its owner only; we have to go through the - * info of the package object. However to avoid cycles we'll check - * what other ways we can before pushing that way. + private def requiresQualifier(s: Symbol): Boolean = ( + s.owner.isClass + && !s.owner.isPackageClass + && !s.isTypeParameterOrSkolem + && !s.isExistentiallyBound + ) + + /** Must `sym` defined in package object of package `pkg`, if + * it selected from a prefix with `pkg` as its type symbol? */ def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { - def uninitialized(what: String) = { - log(s"Cannot look for $sym in package object of $pkg; $what is not initialized.") - false - } - def pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg - def matchesInfo = ( - // need to be careful here to not get a cyclic reference during bootstrap - if (pkg.isInitialized) { - val module = pkg.info member nme.PACKAGEkw - if (module.isInitialized) - module.info.member(sym.name).alternatives contains sym - else - uninitialized("" + module) - } - else uninitialized("" + pkg) - ) - def inPackageObject(sym: Symbol) = ( - // To be in the package object, one of these must be true: - // 1) sym.owner is a package object class, and sym.owner.owner is the package class for `pkg` - // 2) sym.owner is inherited by the correct package object class - // We try to establish 1) by inspecting the owners directly, and then we try - // to rule out 2), and only if both those fail do we resort to looking in the info. - !sym.hasPackageFlag && sym.owner.exists && ( - if (sym.owner.isPackageObjectClass) - sym.owner.owner == pkgClass - else - !sym.owner.isPackageClass && matchesInfo - ) - ) - - // An overloaded symbol might not have the expected owner! - // The alternatives must be inspected directly. - pkgClass.isPackageClass && ( - if (sym.isOverloaded) - sym.alternatives forall (isInPackageObject(_, pkg)) - else - inPackageObject(sym) - ) + if (sym.isOverloaded) sym.alternatives.exists(alt => isInPackageObject(alt, pkg)) + else pkg.hasPackageFlag && sym.owner != pkg && requiresQualifier(sym) } def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess @@ -1048,11 +1016,6 @@ trait Contexts { self: Analyzer => || unit.exists && s.sourceFile != unit.source.file ) ) - def requiresQualifier(s: Symbol) = ( - s.owner.isClass - && !s.owner.isPackageClass - && !s.isTypeParameterOrSkolem - ) def lookupInPrefix(name: Name) = pre member name filter qualifies def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false) @@ -1407,7 +1370,6 @@ trait Contexts { self: Analyzer => protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) } - class ImportInfo(val tree: Import, val depth: Int) { def pos = tree.pos def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos @@ -1423,14 +1385,15 @@ trait Contexts { self: Analyzer => def isExplicitImport(name: Name): Boolean = tree.selectors exists (_.rename == name.toTermName) - /** The symbol with name `name` imported from import clause `tree`. - */ + /** The symbol with name `name` imported from import clause `tree`. */ def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false, record = true) - private def recordUsage(sel: ImportSelector, result: Symbol) { - def posstr = pos.source.file.name + ":" + posOf(sel).line - def resstr = if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})" - debuglog(s"In $this at $posstr, selector '${selectorString(sel)}' resolved to $resstr") + private def recordUsage(sel: ImportSelector, result: Symbol): Unit = { + debuglog(s"In $this at ${ pos.source.file.name }:${ posOf(sel).line }, selector '${ selectorString(sel) + }' resolved to ${ + if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" + else s"(expr=${tree.expr}, ${result.fullLocationString})" + }") allUsedSelectors(this) += sel } diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 69ae6ec0c8..0c10242950 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package typechecker import scala.tools.nsc.symtab.Flags -import scala.collection.{ mutable, immutable } +import scala.collection.mutable /** Duplicate trees and re-type check them, taking care to replace * and create fresh symbols for new local definitions. diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 7092f00bff..97de2b6c85 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -15,39 +15,29 @@ import symtab.Flags._ * @version 1.0 */ trait EtaExpansion { self: Analyzer => - import global._ - object etaExpansion { - private def isMatch(vparam: ValDef, arg: Tree) = arg match { - case Ident(name) => vparam.name == name - case _ => false - } - - def unapply(tree: Tree): Option[(List[ValDef], Tree, List[Tree])] = tree match { - case Function(vparams, Apply(fn, args)) if (vparams corresponds args)(isMatch) => - Some((vparams, fn, args)) - case _ => - None - } - } - - /** <p> - * Expand partial function applications of type `type`. - * </p><pre> - * p.f(es_1)...(es_n) - * ==> { - * <b>private synthetic val</b> eta$f = p.f // if p is not stable - * ... - * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable - * ... - * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) - * }</pre> - * <p> - * tree is already attributed - * </p> - */ - def etaExpand(unit : CompilationUnit, tree: Tree, typer: Typer): Tree = { + /** Expand partial method application `p.f(es_1)...(es_n)`. + * + * We expand this to the following block, which evaluates + * the target of the application and its supplied arguments if needed (they are not stable), + * and then wraps a Function that abstracts over the missing arguments. + * + * ``` + * { + * private synthetic val eta$f = p.f // if p is not stable + * ... + * private synthetic val eta$e_i = e_i // if e_i is not stable + * ... + * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) + * } + * ``` + * + * This is called from instantiateToMethodType after type checking `tree`, + * and we realize we have a method type, where a function type (builtin or SAM) is expected. + * + **/ + def etaExpand(unit: CompilationUnit, tree: Tree, typer: Typer): Tree = { val tpe = tree.tpe var cnt = 0 // for NoPosition def freshName() = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 509ce59104..bee2ae8e99 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -13,7 +13,7 @@ package tools.nsc package typechecker import scala.annotation.tailrec -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import mutable.{ LinkedHashMap, ListBuffer } import scala.util.matching.Regex import symtab.Flags._ @@ -34,14 +34,33 @@ trait Implicits { import typingStack.{ printTyping } import typeDebug._ + // standard usage + def inferImplicitFor(pt: Type, tree: Tree, context: Context, reportAmbiguous: Boolean = true): SearchResult = + inferImplicit(tree, pt, reportAmbiguous, isView = false, context, saveAmbiguousDivergent = true, tree.pos) + + // used by typer to find an implicit coercion + def inferImplicitView(from: Type, to: Type, tree: Tree, context: Context, reportAmbiguous: Boolean, saveAmbiguousDivergent: Boolean) = + inferImplicit(tree, Function1(from, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent, tree.pos) + + // used for manifests, typetags, checking language features, scaladoc + def inferImplicitByType(pt: Type, context: Context, pos: Position = NoPosition): SearchResult = + inferImplicit(EmptyTree, pt, reportAmbiguous = true, isView = false, context, saveAmbiguousDivergent = true, pos) + + def inferImplicitByTypeSilent(pt: Type, context: Context, pos: Position = NoPosition): SearchResult = + inferImplicit(EmptyTree, pt, reportAmbiguous = false, isView = false, context, saveAmbiguousDivergent = false, pos) + + @deprecated("Unused in scalac", "2.12.0-M4") def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent = true, tree.pos) + @deprecated("Unused in scalac", "2.12.0-M4") def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, tree.pos) - /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` - * for more info how the search is conducted. + /** Search for an implicit value. Consider using one of the convenience methods above. This one has many boolean levers. + * + * See the comment on `result` at the end of class `ImplicitSearch` for more info how the search is conducted. + * * @param tree The tree for which the implicit needs to be inserted. * (the inference might instantiate some of the undetermined * type parameters of that tree. @@ -92,9 +111,10 @@ trait Implicits { /** A friendly wrapper over inferImplicit to be used in macro contexts and toolboxes. */ def inferImplicit(tree: Tree, pt: Type, isView: Boolean, context: Context, silent: Boolean, withMacrosDisabled: Boolean, pos: Position, onError: (Position, String) => Unit): Tree = { - val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) - def wrapper(inference: => SearchResult) = wrapper1(inference) - val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) + val result = context.withMacros(enabled = !withMacrosDisabled) { + inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context, saveAmbiguousDivergent = !silent, pos) + } + if (result.isFailure && !silent) { val err = context.reporter.firstError val errPos = err.map(_.errPos).getOrElse(pos) @@ -304,6 +324,10 @@ trait Implicits { */ object Function1 { val Sym = FunctionClass(1) + val Pre = Sym.typeConstructor.prefix + + def apply(from: Type, to: Type) = TypeRef(Pre, Sym, List(from, to)) + // It is tempting to think that this should be inspecting "tp baseType Sym" // rather than tp. See test case run/t8280 and the commit message which // accompanies it for explanation why that isn't done. @@ -887,7 +911,7 @@ trait Implicits { * - find the most likely one * - if it matches, forget about all others it improves upon */ - @tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match { + @tailrec private def rankImplicits(pending: Infos, acc: List[(SearchResult, ImplicitInfo)]): List[(SearchResult, ImplicitInfo)] = pending match { case Nil => acc case firstPending :: otherPending => def firstPendingImproves(alt: ImplicitInfo) = @@ -914,7 +938,7 @@ trait Implicits { val pendingImprovingBest = undoLog undo { otherPending filterNot firstPendingImproves } - rankImplicits(pendingImprovingBest, firstPending :: acc) + rankImplicits(pendingImprovingBest, (newBest, firstPending) :: acc) } } @@ -930,14 +954,14 @@ trait Implicits { // So if there is any element not improved upon by the first it is an error. rankImplicits(eligible, Nil) match { case Nil => () - case chosen :: rest => - rest find (alt => !improves(chosen, alt)) match { - case Some(competing) => - AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context) + case (chosenResult, chosenInfo) :: rest => + rest find { case (_, alt) => !improves(chosenInfo, alt) } match { + case Some((competingResult, competingInfo)) => + AmbiguousImplicitError(chosenInfo, chosenResult.tree, competingInfo, competingResult.tree, "both", "and", "")(isView, pt, tree)(context) return AmbiguousSearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala case _ => - if (isView) chosen.useCountView += 1 - else chosen.useCountArg += 1 + if (isView) chosenInfo.useCountView += 1 + else chosenInfo.useCountArg += 1 } } @@ -1014,15 +1038,12 @@ trait Implicits { } case None => if (pre.isStable && !pre.typeSymbol.isExistentiallyBound) { - val companion = companionSymbolOf(sym, context) - companion.moduleClass match { - case mc: ModuleClassSymbol => - val infos = - for (im <- mc.implicitMembers.toList) yield new ImplicitInfo(im.name, singleType(pre, companion), im) - if (infos.nonEmpty) - infoMap += (sym -> infos) - case _ => - } + val pre1 = + if (sym.isPackageClass) sym.packageObject.typeOfThis + else singleType(pre, companionSymbolOf(sym, context)) + val infos = pre1.implicitMembers.iterator.map(mem => new ImplicitInfo(mem.name, pre1, mem)).toList + if (infos.nonEmpty) + infoMap += (sym -> infos) } val bts = tp.baseTypeSeq var i = 1 @@ -1210,7 +1231,7 @@ trait Implicits { /* Re-wraps a type in a manifest before calling inferImplicit on the result */ def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) = - inferImplicit(tree, appliedType(manifestClass, tp), reportAmbiguous = true, isView = false, context).tree + inferImplicitFor(appliedType(manifestClass, tp), tree, context).tree def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass) def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = { @@ -1413,7 +1434,7 @@ trait Implicits { } if (result.isFailure && settings.debug) // debuglog is not inlined for some reason - log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) + log(s"no implicits found for ${pt} ${pt.typeSymbol.info.baseClasses} ${implicitsOfExpectedType}") result } @@ -1450,9 +1471,9 @@ trait Implicits { } } - object ImplicitNotFoundMsg { - def unapply(sym: Symbol): Option[(Message)] = sym.implicitNotFoundMsg match { - case Some(m) => Some(new Message(sym, m)) + class ImplicitAnnotationMsg(f: Symbol => Option[String], clazz: Symbol, annotationName: String) { + def unapply(sym: Symbol): Option[(Message)] = f(sym) match { + case Some(m) => Some(new Message(sym, m, annotationName)) case None if sym.isAliasType => // perform exactly one step of dealiasing // this is necessary because ClassManifests are now aliased to ClassTags @@ -1464,41 +1485,45 @@ trait Implicits { // check the message's syntax: should be a string literal that may contain occurrences of the string "${X}", // where `X` refers to a type parameter of `sym` def check(sym: Symbol): Option[String] = - sym.getAnnotation(ImplicitNotFoundClass).flatMap(_.stringArg(0) match { - case Some(m) => new Message(sym, m).validate - case None => Some("Missing argument `msg` on implicitNotFound annotation.") + sym.getAnnotation(clazz).flatMap(_.stringArg(0) match { + case Some(m) => new Message(sym, m, annotationName).validate + case None => Some(s"Missing argument `msg` on $annotationName annotation.") }) + } + object ImplicitNotFoundMsg extends ImplicitAnnotationMsg(_.implicitNotFoundMsg, ImplicitNotFoundClass, "implicitNotFound") + + object ImplicitAmbiguousMsg extends ImplicitAnnotationMsg(_.implicitAmbiguousMsg, ImplicitAmbiguousClass, "implicitAmbiguous") + + class Message(sym: Symbol, msg: String, annotationName: String) { // http://dcsobral.blogspot.com/2010/01/string-interpolation-in-scala-with.html private val Intersobralator = """\$\{\s*([^}\s]+)\s*\}""".r - class Message(sym: Symbol, msg: String) { - private def interpolate(text: String, vars: Map[String, String]) = - Intersobralator.replaceAllIn(text, (_: Regex.Match) match { - case Regex.Groups(v) => Regex quoteReplacement vars.getOrElse(v, "") + private def interpolate(text: String, vars: Map[String, String]) = + Intersobralator.replaceAllIn(text, (_: Regex.Match) match { + case Regex.Groups(v) => Regex quoteReplacement vars.getOrElse(v, "") // #3915: need to quote replacement string since it may include $'s (such as the interpreter's $iw) - }) + }) - private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName) - private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs + private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName) + private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs - def format(paramName: Name, paramTp: Type): String = format(typeArgsAtSym(paramTp) map (_.toString)) + def format(paramName: Name, paramTp: Type): String = format(typeArgsAtSym(paramTp) map (_.toString)) - def format(typeArgs: List[String]): String = - interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc? + def format(typeArgs: List[String]): String = + interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc? - def validate: Option[String] = { - val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSet - val decls = typeParamNames.toSet + def validate: Option[String] = { + val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSet + val decls = typeParamNames.toSet - (refs &~ decls) match { - case s if s.isEmpty => None - case unboundNames => - val singular = unboundNames.size == 1 - val ess = if (singular) "" else "s" - val bee = if (singular) "is" else "are" - Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @implicitNotFound annotation $bee not defined by $sym.") - } + (refs &~ decls) match { + case s if s.isEmpty => None + case unboundNames => + val singular = unboundNames.size == 1 + val ess = if (singular) "" else "s" + val bee = if (singular) "is" else "are" + Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @$annotationName annotation $bee not defined by $sym.") } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 9f7bdf7aff..dc91d23011 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -164,7 +164,9 @@ trait Infer extends Checkable { | was: $restpe | now""")(normalize(restpe)) case mt @ MethodType(_, restpe) if mt.isImplicit => normalize(restpe) - case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => functionType(mt.paramTypes, normalize(restpe)) + case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => + if (phase.erasedTypes) FunctionClass(mt.params.length).tpe + else functionType(mt.paramTypes, normalize(restpe)) case NullaryMethodType(restpe) => normalize(restpe) case ExistentialType(tparams, qtpe) => newExistentialType(tparams, normalize(qtpe)) case _ => tp // @MAT aliases already handled by subtyping @@ -295,7 +297,7 @@ trait Infer extends Checkable { && !isByNameParamType(tp) && isCompatible(tp, dropByName(pt)) ) - def isCompatibleSam(tp: Type, pt: Type): Boolean = { + def isCompatibleSam(tp: Type, pt: Type): Boolean = (definitions.isFunctionType(tp) || tp.isInstanceOf[MethodType] || tp.isInstanceOf[PolyType]) && { val samFun = typer.samToFunctionType(pt) (samFun ne NoType) && isCompatible(tp, samFun) } @@ -1207,6 +1209,7 @@ trait Infer extends Checkable { } } tvars foreach instantiateTypeVar + invalidateTreeTpeCaches(tree0, tvars.map(_.origin.typeSymbol)) } /* If the scrutinee has free type parameters but the pattern does not, * we have to flip the arguments so the expected type is treated as more @@ -1217,7 +1220,7 @@ trait Infer extends Checkable { } def inferModulePattern(pat: Tree, pt: Type) = - if (!(pat.tpe <:< pt)) { + if ((pat.symbol ne null) && pat.symbol.isModule && !(pat.tpe <:< pt)) { val ptparams = freeTypeParamsOfTerms(pt) debuglog("free type params (2) = " + ptparams) val ptvars = ptparams map freshVar @@ -1446,7 +1449,7 @@ trait Infer extends Checkable { log(s"Attaching AntiPolyType-carrying overloaded type to $sym") // Multiple alternatives which are within bounds; spin up an // overloaded type which carries an "AntiPolyType" as a prefix. - val tparams = newAsSeenFromMap(pre, hd.owner) mapOver hd.typeParams + val tparams = new AsSeenFromMap(pre, hd.owner) mapOver hd.typeParams val bounds = tparams map (_.tpeHK) // see e.g., #1236 val tpe = PolyType(tparams, OverloadedType(AntiPolyType(pre, bounds), alts)) finish(sym setInfo tpe, tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 3ed128cbc5..d7c53ed3c4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -5,17 +5,13 @@ import java.lang.Math.min import symtab.Flags._ import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.runtime.ReflectionUtils -import scala.collection.mutable.ListBuffer -import scala.reflect.ClassTag import scala.reflect.internal.util.Statistics import scala.reflect.macros.util._ import scala.util.control.ControlThrowable import scala.reflect.internal.util.ListOfNil import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes} -import scala.reflect.runtime.{universe => ru} import scala.reflect.macros.compiler.DefaultMacroCompiler import scala.tools.reflect.FastTrack -import scala.runtime.ScalaRunTime import Fingerprint._ /** @@ -242,7 +238,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { if (!payload.contains(field)) failField("is supposed to be there") val raw: Any = payload(field) if (raw == null) failField(s"is not supposed to be null") - val expected = ScalaRunTime.box(clazz) + val expected = box(clazz) val actual = raw.getClass if (!expected.isAssignableFrom(actual)) failField(s"has wrong type: expected $expected, actual $actual") raw.asInstanceOf[T] @@ -259,6 +255,19 @@ trait Macros extends MacroRuntimes with Traces with Helpers { val signature = unpickle("signature", classOf[List[List[Fingerprint]]]) MacroImplBinding(isBundle, isBlackbox, className, methodName, signature, targs) } + + private def box[T](clazz: Class[T]): Class[_] = clazz match { + case java.lang.Byte.TYPE => classOf[java.lang.Byte] + case java.lang.Short.TYPE => classOf[java.lang.Short] + case java.lang.Character.TYPE => classOf[java.lang.Character] + case java.lang.Integer.TYPE => classOf[java.lang.Integer] + case java.lang.Long.TYPE => classOf[java.lang.Long] + case java.lang.Float.TYPE => classOf[java.lang.Float] + case java.lang.Double.TYPE => classOf[java.lang.Double] + case java.lang.Void.TYPE => classOf[scala.runtime.BoxedUnit] + case java.lang.Boolean.TYPE => classOf[java.lang.Boolean] + case _ => clazz + } } def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index f3856db552..c03094bc6a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -6,11 +6,8 @@ package scala.tools.nsc package typechecker import symtab.Flags._ -import scala.reflect.internal.util.StringOps.{ ojoin } -import scala.reflect.ClassTag +import scala.reflect.internal.util.StringOps.ojoin import scala.reflect.internal.util.ListOfNil -import scala.reflect.runtime.{ universe => ru } -import scala.language.higherKinds /** Logic related to method synthesis which involves cooperation between * Namer and Typer. @@ -22,17 +19,6 @@ trait MethodSynthesis { import definitions._ import CODE._ - /** The annotations amongst those found on the original symbol which - * should be propagated to this kind of accessor. - */ - def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { - def annotationFilter(ann: AnnotationInfo) = ann.metaAnnotations match { - case Nil if ann.defaultTargets.isEmpty => keepClean // no meta-annotations or default targets - case Nil => ann.defaultTargets contains category // default targets exist for ann - case metas => metas exists (_ matches category) // meta-annotations attached to ann - } - initial filter annotationFilter - } class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { def mkThis = This(clazz) setPos clazz.pos.focus @@ -134,39 +120,41 @@ trait MethodSynthesis { ImplicitClassWrapper(tree).createAndEnterSymbol() } - def enterGetterSetter(tree: ValDef) { - val ValDef(mods, name, _, _) = tree - if (nme.isSetterName(name)) - ValOrValWithSetterSuffixError(tree) - - tree.symbol = ( - if (mods.isLazy) { + // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories + def enterGetterSetter(tree: ValDef): Unit = { + tree.symbol = + if (tree.mods.isLazy) { val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol() enterLazyVal(tree, lazyValGetter) } else { - if (mods.isPrivateLocal) - PrivateThisCaseClassParameterError(tree) - val getter = Getter(tree).createAndEnterSymbol() + val getter = Getter(tree) + val getterSym = getter.createAndEnterSymbol() + // Create the setter if necessary. - if (mods.isMutable) + if (getter.needsSetter) Setter(tree).createAndEnterSymbol() - // If abstract, the tree gets the getter's symbol. Otherwise, create a field. - if (mods.isDeferred) getter setPos tree.pos + // If the getter's abstract the tree gets the getter's symbol, + // otherwise, create a field (assume the getter requires storage). + // NOTE: we cannot look at symbol info, since we're in the process of deriving them + // (luckily, they only matter for lazy vals, which we've ruled out in this else branch, + // and `doNotDeriveField` will skip them if `!mods.isLazy`) + if (Field.noFieldFor(tree)) getterSym setPos tree.pos else enterStrictVal(tree) } - ) enterBeans(tree) } + import AnnotationInfo.{mkFilter => annotationFilter} + /** This is called for those ValDefs which addDerivedTrees ignores, but * which might have a warnable annotation situation. */ private def warnForDroppedAnnotations(tree: Tree) { val annotations = tree.symbol.initialize.annotations val targetClass = defaultAnnotationTarget(tree) - val retained = deriveAnnotations(annotations, targetClass, keepClean = true) + val retained = annotations filter annotationFilter(targetClass, defaultRetention = true) annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass)) } @@ -177,11 +165,11 @@ trait MethodSynthesis { } def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { - case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) => + case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations val trees = ( - allValDefDerived(vd) + (field(vd) ::: standardAccessors(vd) ::: beanAccessors(vd)) map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) @@ -208,8 +196,8 @@ trait MethodSynthesis { context.unit.synthetics get meth match { case Some(mdef) => context.unit.synthetics -= meth - meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false) - cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true) + meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false)) + cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true)) List(cd, mdef) case _ => // Shouldn't happen, but let's give ourselves a reasonable error when it does @@ -221,11 +209,14 @@ trait MethodSynthesis { stat :: Nil } - def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( - if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd)) - else if (vd.mods.isLazy) List(LazyValGetter(vd)) - else List(Getter(vd)) - ) + def standardAccessors(vd: ValDef): List[DerivedFromValDef] = + if (vd.mods.isLazy) List(LazyValGetter(vd)) + else { + val getter = Getter(vd) + if (getter.needsSetter) List(getter, Setter(vd)) + else List(getter) + } + def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil if (vd.symbol hasAnnotation BeanPropertyAttr) @@ -234,15 +225,8 @@ trait MethodSynthesis { BooleanBeanGetter(vd) :: setter else Nil } - def allValDefDerived(vd: ValDef) = { - val field = if (vd.mods.isDeferred || (vd.mods.isLazy && hasUnitType(vd.symbol))) Nil - else List(Field(vd)) - field ::: standardAccessors(vd) ::: beanAccessors(vd) - } - // Take into account annotations so that we keep annotated unit lazy val - // to get better error message already from the cps plugin itself - def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty + def field(vd: ValDef): List[Field] = if (Field.noFieldFor(vd)) Nil else List(Field(vd)) /** This trait assembles what's needed for synthesizing derived methods. * Important: Typically, instances of this trait are created TWICE for each derived @@ -260,7 +244,6 @@ trait MethodSynthesis { def name: TermName /** The flags that are retained from the original symbol */ - def flagsMask: Long /** The flags that the derived symbol has in addition to those retained from @@ -284,8 +267,9 @@ trait MethodSynthesis { def enclClass: Symbol // Final methods to make the rest easier to reason about. - final def mods = tree.mods - final def basisSym = tree.symbol + final def mods = tree.mods + final def basisSym = tree.symbol + final def derivedMods = mods & flagsMask | flagsExtra } sealed trait DerivedFromClassDef extends DerivedFromMemberDef { @@ -297,25 +281,19 @@ trait MethodSynthesis { def tree: ValDef final def enclClass = basisSym.enclClass - /** Which meta-annotation is associated with this kind of entity. - * Presently one of: field, getter, setter, beanGetter, beanSetter, param. - */ - def category: Symbol /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) final def fieldSelection = Select(This(enclClass), basisSym) - final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil) def derivedSym: Symbol = tree.symbol def derivedTree: Tree = EmptyTree def isSetter = false def isDeferred = mods.isDeferred - def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept. def validate() { } - def createAndEnterSymbol(): Symbol = { - val sym = owner.newMethod(name, tree.pos.focus, (tree.mods.flags & flagsMask) | flagsExtra) + def createAndEnterSymbol(): MethodSymbol = { + val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags) setPrivateWithin(tree, sym) enterInScope(sym) sym setInfo completer(sym) @@ -328,12 +306,35 @@ trait MethodSynthesis { } final def derive(initial: List[AnnotationInfo]): Tree = { validate() - derivedSym setAnnotations deriveAnnotations(initial, category, keepClean) + + // see scala.annotation.meta's package class for more info + // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. + // The defaults are: + // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity. + // - val/var member annotations solely end up on the underlying field. + // + // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? + // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) + val annotFilter: AnnotationInfo => Boolean = this match { + case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true) + // By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR). + case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor) + case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false) + case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false) + case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false) + case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false) + } + + // The annotations amongst those found on the original symbol which + // should be propagated to this kind of accessor. + derivedSym setAnnotations (initial filter annotFilter) + logDerived(derivedTree) } } sealed trait DerivedGetter extends DerivedFromValDef { - // TODO + // A getter must be accompanied by a setter if the ValDef is mutable. + def needsSetter = mods.isMutable } sealed trait DerivedSetter extends DerivedFromValDef { override def isSetter = true @@ -341,10 +342,13 @@ trait MethodSynthesis { case (p :: Nil) :: _ => p case _ => NoSymbol } - private def setterRhs = ( - if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree + + private def setterRhs = { + assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for $basisSym in $enclClass") + if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree else Assign(fieldSelection, Ident(setterParam)) - ) + } + private def setterDef = DefDef(derivedSym, setterRhs) override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef } @@ -363,8 +367,7 @@ trait MethodSynthesis { context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $name in $enclClass / ${enclClass.info.decls}") result } - def derivedTree: DefDef = - factoryMeth(mods & flagsMask | flagsExtra, name, tree) + def derivedTree: DefDef = factoryMeth(derivedMods, name, tree) def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC def flagsMask: Long = AccessFlags def name: TermName = tree.name.toTermName @@ -372,7 +375,6 @@ trait MethodSynthesis { sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter { def name = tree.name - def category = GetterTargetClass def flagsMask = GetterFlags def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE ) @@ -385,8 +387,9 @@ trait MethodSynthesis { } } case class Getter(tree: ValDef) extends BaseGetter(tree) { - override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getterIn(enclClass) - private def derivedRhs = if (mods.isDeferred) EmptyTree else fieldSelection + override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass) + private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection + private def derivedTpt = { // For existentials, don't specify a type for the getter, even one derived // from the symbol! This leads to incompatible existentials for the field and @@ -396,23 +399,26 @@ trait MethodSynthesis { // starts compiling (instead of failing like it's supposed to) because the typer // expects to be able to identify escaping locals in typedDefDef, and fails to // spot that brand of them. In other words it's an artifact of the implementation. - val tpt = derivedSym.tpe_*.finalResultType.widen match { + val getterTp = derivedSym.tpe_*.finalResultType + val tpt = getterTp.widen match { // Range position errors ensue if we don't duplicate this in some // circumstances (at least: concrete vals with existential types.) - case ExistentialType(_, _) => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) - case _ if mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field - case tp => TypeTree(tp) + case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) + case _ if isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field + case _ => TypeTree(getterTp) } tpt setPos tree.tpt.pos.focus } override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt) } + /** Implements lazy value accessors: - * - for lazy values of type Unit and all lazy fields inside traits, - * the rhs is the initializer itself - * - for all other lazy values z the accessor is a block of this form: - * { z = <rhs>; z } where z can be an identifier or a field. - */ + * - for lazy values of type Unit and all lazy fields inside traits, + * the rhs is the initializer itself, because we'll just "compute" the result on every access + * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap) + * - for all other lazy values z the accessor is a block of this form: + * { z = <rhs>; z } where z can be an identifier or a field. + */ case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) { class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol) extends ChangeOwnerTraverser(oldowner, newowner) { @@ -432,10 +438,10 @@ trait MethodSynthesis { override def derivedTree: DefDef = { val ValDef(_, _, tpt0, rhs0) = tree val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0) - val body = ( - if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1 + val body = + if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor else gen.mkAssignAndReturn(basisSym, rhs1) - ) + derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition val ddefRes = DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body)) // ValDef will have its position focused whereas DefDef will have original correct rangepos @@ -448,32 +454,45 @@ trait MethodSynthesis { } case class Setter(tree: ValDef) extends DerivedSetter { def name = tree.setterName - def category = SetterTargetClass def flagsMask = SetterFlags def flagsExtra = ACCESSOR override def derivedSym = basisSym.setterIn(enclClass) } + + object Field { + // No field for these vals (either never emitted or eliminated later on): + // - abstract vals have no value we could store (until they become concrete, potentially) + // - lazy vals of type Unit + // - [Emitted, later removed during AddInterfaces/Mixins] concrete vals in traits can't have a field + // - [Emitted, later removed during Constructors] a concrete val with a statically known value (Unit / ConstantType) + // performs its side effect according to lazy/strict semantics, but doesn't need to store its value + // each access will "evaluate" the RHS (a literal) again + // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer. + // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does. + // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`), + // as the symbol info is in the process of being created then. + // TODO: harmonize tree & symbol creation + // TODO: the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite) + def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info)) + } + case class Field(tree: ValDef) extends DerivedFromValDef { def name = tree.localName - def category = FieldTargetClass def flagsMask = FieldFlags def flagsExtra = PrivateLocal - // By default annotations go to the field, except if the field is - // generated for a class parameter (PARAMACCESSOR). - override def keepClean = !mods.isParamAccessor - override def derivedTree = ( - if (mods.isDeferred) EmptyTree - else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus) + + // handle lazy val first for now (we emit a Field even though we probably shouldn't...) + override def derivedTree = + if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus) + else if (Field.noFieldFor(tree)) EmptyTree else copyValDef(tree)(mods = mods | flagsExtra, name = this.name) - ) + } case class Param(tree: ValDef) extends DerivedFromValDef { def name = tree.name - def category = ParamTargetClass def flagsMask = -1L def flagsExtra = 0L - override def keepClean = true override def derivedTree = EmptyTree } def validateParam(tree: ValDef) { @@ -487,7 +506,6 @@ trait MethodSynthesis { override def derivedSym = enclClass.info decl name } sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter { - def category = BeanGetterTargetClass override def validate() { if (derivedSym == NoSymbol) { // the namer decides whether to generate these symbols or not. at that point, we don't @@ -501,18 +519,16 @@ trait MethodSynthesis { // Derives a tree without attempting to use the original tree's symbol. override def derivedTree = { atPos(tree.pos.focus) { - DefDef(derivedMods, name, Nil, ListOfNil, tree.tpt.duplicate, + DefDef(derivedMods mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate, if (isDeferred) EmptyTree else Select(This(owner), tree.name) ) } } - override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree) + override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol] } case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { } case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { } - case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter { - def category = BeanSetterTargetClass - } + case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter // No Symbols available. private def beanAccessorsFromNames(tree: ValDef) = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4ad81b60ae..8943ec810d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package typechecker import scala.collection.mutable -import scala.annotation.tailrec import symtab.Flags._ import scala.language.postfixOps import scala.reflect.internal.util.ListOfNil @@ -115,21 +114,14 @@ trait Namers extends MethodSynthesis { TypeSigError(tree, ex) alt } - // PRIVATE | LOCAL are fields generated for primary constructor arguments - // @PP: ...or fields declared as private[this]. PARAMACCESSOR marks constructor arguments. - // Neither gets accessors so the code is as far as I know still correct. - def noEnterGetterSetter(vd: ValDef) = !vd.mods.isLazy && ( - !owner.isClass - || (vd.mods.isPrivateLocal && !vd.mods.isCaseAccessor) - || (vd.name startsWith nme.OUTER) - || (context.unit.isJava) - || isEnumConstant(vd) - ) - def noFinishGetterSetter(vd: ValDef) = ( - (vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this] - || vd.symbol.isModuleVar - || isEnumConstant(vd)) + // All lazy vals need accessors, including those owned by terms (e.g., in method) or private[this] in a class + def deriveAccessors(vd: ValDef) = vd.mods.isLazy || (owner.isClass && deriveAccessorsInClass(vd)) + + private def deriveAccessorsInClass(vd: ValDef) = + !vd.mods.isPrivateLocal && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors + !(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter + !isEnumConstant(vd) // enums can only occur in classes, so only check here /** Determines whether this field holds an enum constant. * To qualify, the following conditions must be met: @@ -170,13 +162,9 @@ trait Namers extends MethodSynthesis { def updatePosFlags(sym: Symbol, pos: Position, flags: Long): Symbol = { debuglog("[overwrite] " + sym) val newFlags = (sym.flags & LOCKED) | flags - sym.rawInfo match { - case tr: TypeRef => - // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef - // over this mutated symbol, and we witness a stale cache for `parents`. - tr.invalidateCaches() - case _ => - } + // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef + // over this mutated symbol, and we witness a stale cache for `parents`. + invalidateCaches(sym.rawInfo, sym :: sym.moduleClass :: Nil) sym reset NoType setFlag newFlags setPos pos sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags))) @@ -655,14 +643,20 @@ trait Namers extends MethodSynthesis { } } - def enterValDef(tree: ValDef) { - if (noEnterGetterSetter(tree)) - assignAndEnterFinishedSymbol(tree) - else - enterGetterSetter(tree) + def enterValDef(tree: ValDef): Unit = { + val isScala = !context.unit.isJava + if (isScala) { + if (nme.isSetterName(tree.name)) ValOrVarWithSetterSuffixError(tree) + if (tree.mods.isPrivateLocal && tree.mods.isCaseAccessor) PrivateThisCaseClassParameterError(tree) + } - if (isEnumConstant(tree)) + if (isScala && deriveAccessors(tree)) enterGetterSetter(tree) + else assignAndEnterFinishedSymbol(tree) + + if (isEnumConstant(tree)) { tree.symbol setInfo ConstantType(Constant(tree.symbol)) + tree.symbol.owner.linkedClassOfClass addChild tree.symbol + } } def enterLazyVal(tree: ValDef, lazyAccessor: Symbol): TermSymbol = { @@ -1600,11 +1594,7 @@ trait Namers extends MethodSynthesis { import SymValidateErrors._ def fail(kind: SymValidateErrors.Value) = SymbolValidationError(sym, kind) - def checkWithDeferred(flag: Int) { - if (sym hasFlag flag) - AbstractMemberWithModiferError(sym, flag) - } - def checkNoConflict(flag1: Int, flag2: Int) { + def checkNoConflict(flag1: Int, flag2: Int) = { if (sym hasAllFlags flag1.toLong | flag2) IllegalModifierCombination(sym, flag1, flag2) } @@ -1643,6 +1633,10 @@ trait Namers extends MethodSynthesis { checkNoConflict(ABSTRACT, FINAL) if (sym.isDeferred) { + def checkWithDeferred(flag: Int) = { + if (sym hasFlag flag) + AbstractMemberWithModiferError(sym, flag) + } // Is this symbol type always allowed the deferred flag? def symbolAllowsDeferred = ( sym.isValueParameter @@ -1658,14 +1652,16 @@ trait Namers extends MethodSynthesis { ) if (sym hasAnnotation NativeAttr) sym resetFlag DEFERRED - else if (!symbolAllowsDeferred && ownerRequiresConcrete) - fail(AbstractVar) + else { + if (!symbolAllowsDeferred && ownerRequiresConcrete) fail(AbstractVar) - checkWithDeferred(PRIVATE) - checkWithDeferred(FINAL) + checkWithDeferred(PRIVATE) + checkWithDeferred(FINAL) + } } - checkNoConflict(FINAL, SEALED) + if (!sym.isJavaEnum) + checkNoConflict(FINAL, SEALED) checkNoConflict(PRIVATE, PROTECTED) // checkNoConflict(PRIVATE, OVERRIDE) // this one leads to bad error messages like #4174, so catch in refchecks // checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 39cd610b1c..5062289ed1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -9,6 +9,7 @@ package typechecker import symtab.Flags._ import scala.collection.mutable import scala.reflect.ClassTag +import PartialFunction.{ cond => when } /** * @author Lukas Rytz @@ -468,8 +469,7 @@ trait NamesDefaults { self: Analyzer => else { // isClass also works for methods in objects, owner is the ModuleClassSymbol if (param.owner.owner.isClass) { - // .toInterface: otherwise we get the method symbol of the impl class - param.owner.owner.toInterface.info.member(defGetterName) + param.owner.owner.info.member(defGetterName) } else { // the owner of the method is another method. find the default // getter in the context. @@ -551,64 +551,73 @@ trait NamesDefaults { self: Analyzer => } } - /** - * Removes name assignments from args. Additionally, returns an array mapping - * argument indices from call-site-order to definition-site-order. + /** Removes name assignments from args. Additionally, returns an array mapping + * argument indices from call-site-order to definition-site-order. * - * Verifies that names are not specified twice, positional args don't appear - * after named ones. + * Verifies that names are not specified twice, and positional args don't appear after named ones. */ def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = { implicit val context0 = typer.context - // maps indices from (order written by user) to (order of definition) - val argPos = Array.fill(args.length)(-1) - var positionalAllowed = true - val namelessArgs = mapWithIndex(args) { (arg, argIndex) => - arg match { - case arg @ AssignOrNamedArg(Ident(name), rhs) => - def matchesName(param: Symbol) = !param.isSynthetic && ( - (param.name == name) || (param.deprecatedParamName match { - case Some(`name`) => - context0.deprecationWarning(arg.pos, param, - s"the parameter name $name has been deprecated. Use ${param.name} instead.") - true - case _ => false - }) - ) - val paramPos = params indexWhere matchesName - if (paramPos == -1) { - if (positionalAllowed) { - argPos(argIndex) = argIndex - // prevent isNamed from being true when calling doTypedApply recursively, - // treat the arg as an assignment of type Unit - Assign(arg.lhs, rhs) setPos arg.pos - } - else UnknownParameterNameNamesDefaultError(arg, name) - } - else if (argPos contains paramPos) { + def matchesName(param: Symbol, name: Name, argIndex: Int) = { + def warn(w: String) = context0.deprecationWarning(args(argIndex).pos, param, w) + def checkDeprecation(anonOK: Boolean) = + when (param.deprecatedParamName) { + case Some(`name`) => true + case Some(nme.NO_NAME) => anonOK + } + def checkName = { + val res = param.name == name + if (res && checkDeprecation(true)) warn(s"naming parameter $name has been deprecated.") + res + } + def checkAltName = { + val res = checkDeprecation(false) + if (res) warn(s"the parameter name $name has been deprecated. Use ${param.name} instead.") + res + } + !param.isSynthetic && (checkName || checkAltName) + } + // argPos maps indices from (order written by user) to (order of definition) + val argPos = Array.fill(args.length)(-1) + val namelessArgs = { + var positionalAllowed = true + def stripNamedArg(arg: AssignOrNamedArg, argIndex: Int): Tree = { + val AssignOrNamedArg(Ident(name), rhs) = arg + params indexWhere (p => matchesName(p, name, argIndex)) match { + case -1 if positionalAllowed => + // prevent isNamed from being true when calling doTypedApply recursively, + // treat the arg as an assignment of type Unit + Assign(arg.lhs, rhs) setPos arg.pos + case -1 => + UnknownParameterNameNamesDefaultError(arg, name) + case paramPos if argPos contains paramPos => val existingArgIndex = argPos.indexWhere(_ == paramPos) - val otherName = args(paramPos) match { - case AssignOrNamedArg(Ident(oName), rhs) if oName != name => Some(oName) - case _ => None + val otherName = Some(args(paramPos)) collect { + case AssignOrNamedArg(Ident(oName), _) if oName != name => oName } DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName) - } else if (isAmbiguousAssignment(typer, params(paramPos), arg)) + case paramPos if isAmbiguousAssignment(typer, params(paramPos), arg) => AmbiguousReferenceInNamesDefaultError(arg, name) - else { - // if the named argument is on the original parameter - // position, positional after named is allowed. - if (argIndex != paramPos) - positionalAllowed = false - argPos(argIndex) = paramPos + case paramPos if paramPos != argIndex => + positionalAllowed = false // named arg is not in original parameter order: require names after this + argPos(argIndex) = paramPos // fix up the arg position rhs - } - case _ => - argPos(argIndex) = argIndex - if (positionalAllowed) arg - else PositionalAfterNamedNamesDefaultError(arg) + case _ => rhs + } + } + mapWithIndex(args) { + case (arg: AssignOrNamedArg, argIndex) => + val t = stripNamedArg(arg, argIndex) + if (!t.isErroneous && argPos(argIndex) < 0) argPos(argIndex) = argIndex + t + case (arg, argIndex) => + if (positionalAllowed) { + argPos(argIndex) = argIndex + arg + } else + PositionalAfterNamedNamesDefaultError(arg) } } - (namelessArgs, argPos) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 3b2e07bdbd..da269168ec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -6,15 +6,17 @@ package scala.tools.nsc package typechecker -import symtab.Flags._ -import scala.collection.{ mutable, immutable } -import transform.InfoTransform -import scala.collection.mutable.ListBuffer import scala.language.postfixOps + +import scala.collection.mutable +import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.ScalaVersion -import scala.tools.nsc.settings.AnyScalaVersion import scala.tools.nsc.settings.NoScalaVersion +import symtab.Flags._ +import transform.InfoTransform + + /** <p> * Post-attribution checking and transformation. * </p> @@ -48,7 +50,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans import global._ import definitions._ - import typer.{typed, typedOperator, atOwner} + import typer.typed /** the following two members override abstract members in Transform */ val phaseName: String = "refchecks" @@ -86,17 +88,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (sym.hasAccessBoundary) "" + sym.privateWithin.name else "" ) - def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { + def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type, isModuleOverride: Boolean): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => rtp1 <:< rtp2 case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => rtp1 <:< rtp2 case (TypeRef(_, sym, _), _) if sym.isModuleClass => - overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix) + overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix, isModuleOverride) case _ => def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner) - - (tp1 <:< tp2) || ( // object override check + (tp1 <:< tp2) || isModuleOverride && ( + // Object override check. This requires that both the overridden and the overriding member are object + // definitions. The overriding module type is allowed to replace the original one with the same name + // as long as it conform to the original non-singleton type. tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && { val cb1 = classBoundAsSeen(tp1) val cb2 = classBoundAsSeen(tp2) @@ -518,7 +522,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def checkOverrideTerm() { other.cookJavaRawInfo() // #2454 - if (!overridesTypeInPrefix(lowType, highType, rootType)) { // 8 + if (!overridesTypeInPrefix(lowType, highType, rootType, low.isModuleOrModuleClass && high.isModuleOrModuleClass)) { // 8 overrideTypeError() explainTypes(lowType, highType) } @@ -1150,11 +1154,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def toConstructor(pos: Position, tpe: Type): Tree = { val rtpe = tpe.finalResultType assert(rtpe.typeSymbol hasFlag CASE, tpe) - localTyper.typedOperator { + val tree = localTyper.typedOperator { atPos(pos) { Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor) } } + checkUndesiredProperties(rtpe.typeSymbol, tree.pos) + tree } override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { @@ -1182,59 +1188,54 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks { val ModuleDef(_, _, impl) = moduleDef val module = moduleDef.symbol - val moduleClass = module.moduleClass val site = module.owner val moduleName = module.name.toTermName // The typer doesn't take kindly to seeing this ClassDef; we have to // set NoType so it will be ignored. - val cdef = ClassDef(moduleClass, impl) setType NoType - - // This code is related to the fix of SI-9375, which stops adding `readResolve` methods to - // non-static (nested) modules. Before the fix, the method would cause the module accessor - // to become notPrivate. To prevent binary changes in the 2.11.x branch, we mimic that behavior. - // There is a bit of code duplication between here and SyntheticMethods. We cannot call - // makeNotPrivate already in SyntheticMethod: that is during type checking, and not all references - // are resolved yet, so we cannot rename a definition. This code doesn't exist in the 2.12.x branch. - def hasConcreteImpl(name: Name) = moduleClass.info.member(name).alternatives exists (m => !m.isDeferred) - val hadReadResolveBeforeSI9375 = moduleClass.isSerializable && !hasConcreteImpl(nme.readResolve) - if (hadReadResolveBeforeSI9375) - moduleClass.sourceModule.makeNotPrivate(moduleClass.sourceModule.owner) - - // Create the module var unless the immediate owner is a class and - // the module var already exists there. See SI-5012, SI-6712. - def findOrCreateModuleVar() = { - val vsym = ( - if (site.isTerm) NoSymbol - else site.info decl nme.moduleVarName(moduleName) - ) - vsym orElse (site newModuleVarSymbol module) - } - def newInnerObject() = { - // Create the module var unless it is already in the module owner's scope. - // The lookup is on module.enclClass and not module.owner lest there be a - // nullary method between us and the class; see SI-5012. - val moduleVar = findOrCreateModuleVar() - val rhs = gen.newModule(module, moduleVar.tpe) - val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs) - val accessor = DefDef(module, body.changeOwner(moduleVar -> module)) - - ValDef(moduleVar) :: accessor :: Nil - } + val cdef = ClassDef(module.moduleClass, impl) setType NoType + def matchingInnerObject() = { val newFlags = (module.flags | STABLE) & ~MODULE - val newInfo = NullaryMethodType(moduleClass.tpe) + val newInfo = NullaryMethodType(module.moduleClass.tpe) val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo DefDef(accessor, Select(This(site), module)) :: Nil } val newTrees = cdef :: ( if (module.isStatic) + // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O. if (module.isOverridingSymbol) matchingInnerObject() else Nil else - newInnerObject() + newInnerObject(site, module) ) transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) } + def newInnerObject(site: Symbol, module: Symbol): List[Tree] = { + if (site.isTrait) + DefDef(module, EmptyTree) :: Nil + else { + val moduleVar = site newModuleVarSymbol module + // used for the mixin case: need a new symbol owned by the subclass for the accessor, rather than repurposing the module symbol + def mkAccessorSymbol = + site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | MIXEDIN) + .setInfo(moduleVar.tpe) + .andAlso(self => if (module.isPrivate) self.expandName(module.owner)) + + val accessor = if (module.owner == site) module else mkAccessorSymbol + val accessorDef = DefDef(accessor, gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.tpe)).changeOwner(moduleVar -> accessor)) + + ValDef(moduleVar) :: accessorDef :: Nil + } + } + + def mixinModuleDefs(clazz: Symbol): List[Tree] = { + val res = for { + mixinClass <- clazz.mixinClasses.iterator + module <- mixinClass.info.decls.iterator.filter(_.isModule) + newMember <- newInnerObject(clazz, module) + } yield transform(localTyper.typedPos(clazz.pos)(newMember)) + res.toList + } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => @@ -1480,10 +1481,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case m: MemberDef => val sym = m.symbol applyChecks(sym.annotations) - // validate implicitNotFoundMessage - analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => - reporter.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") - } + + def messageWarning(name: String)(warn: String) = + reporter.warning(tree.pos, f"Invalid $name message for ${sym}%s${sym.locationString}%s:%n$warn") + + // validate implicitNotFoundMessage and implicitAmbiguousMessage + analyzer.ImplicitNotFoundMsg.check(sym) foreach messageWarning("implicitNotFound") + analyzer.ImplicitAmbiguousMsg.check(sym) foreach messageWarning("implicitAmbiguous") case tpt@TypeTree() => if(tpt.original != null) { @@ -1526,11 +1530,21 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } private def transformCaseApply(tree: Tree) = { + def loop(t: Tree): Unit = t match { + case Ident(_) => + checkUndesiredProperties(t.symbol, t.pos) + case Select(qual, _) => + checkUndesiredProperties(t.symbol, t.pos) + loop(qual) + case _ => + } + tree foreach { case i@Ident(_) => enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` case _ => } + loop(tree) toConstructor(tree.pos, tree.tpe) } @@ -1669,11 +1683,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // SI-7870 default getters for constructors live in the companion module checkOverloadedRestrictions(currentOwner, currentOwner.companionModule) val bridges = addVarargBridges(currentOwner) + val moduleDesugared = if (currentOwner.isTrait) Nil else mixinModuleDefs(currentOwner) checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) if (currentOwner.isDerivedValueClass) currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler! - if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree + if (bridges.nonEmpty || moduleDesugared.nonEmpty) deriveTemplate(tree)(_ ::: bridges ::: moduleDesugared) else tree case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") case tpt@TypeTree() => diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index e0d96df062..a1bec13999 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -287,7 +287,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val result = (localTyper.typedPos(tree.pos) { Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) }).asInstanceOf[Select] - debuglog("alias replacement: " + tree + " ==> " + result); //debug + debuglog(s"alias replacement: $sym --> ${sym.alias} / $tree ==> $result"); //debug localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, beforeRefChecks = true)) } else { /* @@ -387,7 +387,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val savedValid = validCurrentOwner if (owner.isClass) validCurrentOwner = true val savedLocalTyper = localTyper - localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) + localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner) typers = typers updated (owner, localTyper) val result = super.atOwner(tree, owner)(trans) localTyper = savedLocalTyper diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 1b3f066fc1..6d883aee3d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -6,11 +6,13 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } -import symtab.Flags._ -import scala.collection.mutable.ListBuffer import scala.language.postfixOps +import scala.collection.mutable +import scala.collection.mutable.ListBuffer + +import symtab.Flags._ + /** Synthetic method implementations for case classes and case objects. * * Added to all case classes/objects: @@ -87,33 +89,18 @@ trait SyntheticMethods extends ast.TreeDSL { def accessors = clazz.caseFieldAccessors val arity = accessors.size - // If this is ProductN[T1, T2, ...], accessorLub is the lub of T1, T2, ..., . - // !!! Hidden behind -Xexperimental due to bummer type inference bugs. - // Refining from Iterator[Any] leads to types like - // - // Option[Int] { def productIterator: Iterator[String] } - // - // appearing legitimately, but this breaks invariant places - // like Tags and Arrays which are not robust and infer things - // which they shouldn't. - val accessorLub = ( - if (settings.Xexperimental) { - global.lub(accessors map (_.tpe.finalResultType)) match { - case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) - case tp => tp - } - } - else AnyTpe - ) def forwardToRuntime(method: Symbol): Tree = forwardMethod(method, getMember(ScalaRunTimeModule, (method.name prepend "_")))(mkThis :: _) - def callStaticsMethod(name: String)(args: Tree*): Tree = { - val method = termMember(RuntimeStaticsModule, name) + def callStaticsMethodName(name: TermName)(args: Tree*): Tree = { + val method = RuntimeStaticsModule.info.member(name) Apply(gen.mkAttributedRef(method), args.toList) } + def callStaticsMethod(name: String)(args: Tree*): Tree = + callStaticsMethodName(newTermName(name))(args: _*) + // Any concrete member, including private def hasConcreteImpl(name: Name) = clazz.info.member(name).alternatives exists (m => !m.isDeferred) @@ -125,8 +112,8 @@ trait SyntheticMethods extends ast.TreeDSL { } } def productIteratorMethod = { - createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ => - gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis)) + createMethod(nme.productIterator, iteratorOfType(AnyTpe))(_ => + gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(AnyTpe), List(mkThis)) ) } @@ -246,7 +233,7 @@ trait SyntheticMethods extends ast.TreeDSL { List( Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)), Product_productArity -> (() => constantNullary(nme.productArity, arity)), - Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(mkThisSelect)), + Product_productElement -> (() => perElementMethod(nme.productElement, AnyTpe)(mkThisSelect)), Product_iterator -> (() => productIteratorMethod), Product_canEqual -> (() => canEqualMethod) // This is disabled pending a reimplementation which doesn't add any @@ -261,10 +248,10 @@ trait SyntheticMethods extends ast.TreeDSL { case BooleanClass => If(Ident(sym), Literal(Constant(1231)), Literal(Constant(1237))) case IntClass => Ident(sym) case ShortClass | ByteClass | CharClass => Select(Ident(sym), nme.toInt) - case LongClass => callStaticsMethod("longHash")(Ident(sym)) - case DoubleClass => callStaticsMethod("doubleHash")(Ident(sym)) - case FloatClass => callStaticsMethod("floatHash")(Ident(sym)) - case _ => callStaticsMethod("anyHash")(Ident(sym)) + case LongClass => callStaticsMethodName(nme.longHash)(Ident(sym)) + case DoubleClass => callStaticsMethodName(nme.doubleHash)(Ident(sym)) + case FloatClass => callStaticsMethodName(nme.floatHash)(Ident(sym)) + case _ => callStaticsMethodName(nme.anyHash)(Ident(sym)) } } @@ -354,16 +341,18 @@ trait SyntheticMethods extends ast.TreeDSL { } for ((m, impl) <- methods ; if shouldGenerate(m)) yield impl() } - def extras = ( + def extras = { if (needsReadResolve) { // Aha, I finally decoded the original comment. // This method should be generated as private, but apparently if it is, then // it is name mangled afterward. (Wonder why that is.) So it's only protected. // For sure special methods like "readResolve" should not be mangled. - List(createMethod(nme.readResolve, Nil, ObjectTpe)(m => { m setFlag PRIVATE ; REF(clazz.sourceModule) })) + List(createMethod(nme.readResolve, Nil, ObjectTpe)(m => { + m setFlag PRIVATE; REF(clazz.sourceModule) + })) } else Nil - ) + } try impls ++ extras catch { case _: TypeError if reporter.hasErrors => Nil } @@ -381,7 +370,14 @@ trait SyntheticMethods extends ast.TreeDSL { for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body ; if isRewrite(ddef.symbol)) { val original = ddef.symbol - val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc => + val i = original.owner.caseFieldAccessors.indexOf(original) + def freshAccessorName = { + devWarning(s"Unable to find $original among case accessors of ${original.owner}: ${original.owner.caseFieldAccessors}") + context.unit.freshTermName(original.name + "$") + } + def nameSuffixedByParamIndex = original.name.append(nme.CASE_ACCESSOR + "$" + i).toTermName + val newName = if (i < 0) freshAccessorName else nameSuffixedByParamIndex + val newAcc = deriveMethod(ddef.symbol, name => newName) { newAcc => newAcc.makePublic newAcc resetFlag (ACCESSOR | PARAMACCESSOR | OVERRIDE) ddef.rhs.duplicate diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala index 56127f4026..e29451f379 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -13,16 +13,7 @@ trait Tags { private val runDefinitions = currentRun.runDefinitions private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper { - def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) - wrapper(inferImplicit( - EmptyTree, - taggedTp, - reportAmbiguous = true, - isView = false, - context, - saveAmbiguousDivergent = true, - pos - ).tree) + context.withMacros(enabled = allowMaterialization) { inferImplicitByType(taggedTp, context, pos).tree } } /** Finds in scope or materializes a ClassTag. diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index e8db8309f1..990edcd86d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -7,8 +7,6 @@ package scala.tools.nsc package typechecker import scala.collection.mutable -import mutable.ListBuffer -import util.returning import scala.reflect.internal.util.shortClassOfInstance import scala.reflect.internal.util.StringOps._ @@ -94,7 +92,7 @@ abstract class TreeCheckers extends Analyzer { def latest = maps.head._2 val defSyms = symbolTreeMap[DefTree]() val newSyms = mutable.HashSet[Symbol]() - val movedMsgs = new ListBuffer[String] + val movedMsgs = mutable.ListBuffer[String]() def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString) def record(tree: Tree) { @@ -113,8 +111,6 @@ abstract class TreeCheckers extends Analyzer { newSyms += sym else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) () - else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) - errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.") else { val s1 = (prevTrees map wholetreestr).sorted.distinct val s2 = wholetreestr(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6b73a538df..8f5c4b9f6d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -14,7 +14,7 @@ package tools.nsc package typechecker import scala.collection.{mutable, immutable} -import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance, ListOfNil } +import scala.reflect.internal.util.{ Statistics, ListOfNil } import mutable.ListBuffer import symtab.Flags._ import Mode._ @@ -105,12 +105,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // that are turned private by typedBlock private final val SYNTHETIC_PRIVATE = TRANS_FLAG - private final val InterpolatorCodeRegex = """\$\{.*?\}""".r + private final val InterpolatorCodeRegex = """\$\{\s*(.*?)\s*\}""".r private final val InterpolatorIdentRegex = """\$[$\w]+""".r // note that \w doesn't include $ abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors { import context0.unit - import typeDebug.{ ptTree, ptBlock, ptLine, inGreen, inRed } + import typeDebug.ptTree import TyperErrorGen._ val runDefinitions = currentRun.runDefinitions import runDefinitions._ @@ -128,6 +128,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def canTranslateEmptyListToNil = true def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree + // when type checking during erasure, generate erased types in spots that aren't transformed by erasure + // (it erases in TypeTrees, but not in, e.g., the type a Function node) + def phasedAppliedType(sym: Symbol, args: List[Type]) = { + val tp = appliedType(sym, args) + if (phase.erasedTypes) erasure.specialScalaErasure(tp) else tp + } + def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = typed(docDef.definition, mode, pt) @@ -151,7 +158,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper for(ar <- argResultsBuff) paramTp = paramTp.subst(ar.subst.from, ar.subst.to) - val res = if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, isView = false, context) + val res = + if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure + else inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors) argResultsBuff += res if (res.isSuccess) { @@ -187,14 +196,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper !from.isError && !to.isError && context.implicitsEnabled - && (inferView(context.tree, from, to, reportAmbiguous = false, saveErrors = true) != EmptyTree) + && (inferView(context.tree, from, to, reportAmbiguous = false) != EmptyTree) // SI-8230 / SI-8463 We'd like to change this to `saveErrors = false`, but can't. // For now, we can at least pass in `context.tree` rather then `EmptyTree` so as // to avoid unpositioned type errors. ) - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = - inferView(tree, from, to, reportAmbiguous, saveErrors = true) /** Infer an implicit conversion (`view`) between two types. * @param tree The tree which needs to be converted. @@ -207,25 +214,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * during the inference of a view be put into the original buffer. * False iff we don't care about them. */ - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { - debuglog("infer view from "+from+" to "+to)//debug - if (isPastTyper) EmptyTree - else from match { - case MethodType(_, _) => EmptyTree - case OverloadedType(_, _) => EmptyTree - case PolyType(_, _) => EmptyTree - case _ => - def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(from.withoutAnnotations :: Nil, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent = saveErrors) - if (result.subst != EmptyTreeTypeSubstituter) { - result.subst traverse tree - notifyUndetparamsInferred(result.subst.from, result.subst.to) - } - result.tree - } - wrapImplicit(from) orElse wrapImplicit(byNameType(from)) + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = + if (isPastTyper || from.isInstanceOf[MethodType] || from.isInstanceOf[OverloadedType] || from.isInstanceOf[PolyType]) EmptyTree + else { + debuglog(s"Inferring view from $from to $to for $tree (reportAmbiguous= $reportAmbiguous, saveErrors=$saveErrors)") + + val fromNoAnnot = from.withoutAnnotations + val result = inferImplicitView(fromNoAnnot, to, tree, context, reportAmbiguous, saveErrors) match { + case fail if fail.isFailure => inferImplicitView(byNameType(fromNoAnnot), to, tree, context, reportAmbiguous, saveErrors) + case ok => ok + } + + if (result.subst != EmptyTreeTypeSubstituter) { + result.subst traverse tree + notifyUndetparamsInferred(result.subst.from, result.subst.to) + } + result.tree } - } import infer._ @@ -239,6 +244,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper var context = context0 def context1 = context + // for use with silent type checking to when we can't have results with undetermined type params + // note that this captures the context var + val isMonoContext = (_: Any) => context.undetparams.isEmpty + def dropExistential(tp: Type): Type = tp match { case ExistentialType(tparams, tpe) => new SubstWildcardMap(tparams).apply(tp) @@ -542,7 +551,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } val qual = typedQualifier { atPos(tree.pos.makeTransparent) { tree match { - case Ident(_) => Ident(rootMirror.getPackageObjectWithMember(pre, sym)) + case Ident(_) => + val packageObject = + if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed + else pre.typeSymbol.packageObject + Ident(packageObject) case Select(qual, _) => Select(qual, nme.PACKAGEkw) case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw) } @@ -721,7 +734,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name def action(): Boolean = { - def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, reportAmbiguous = true, isView = false, context).isSuccess + def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess def hasOption = settings.language contains featureName val OK = hasImport || hasOption if (!OK) { @@ -798,7 +811,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * (11) Widen numeric literals to their expected type, if necessary * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit. * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated. - * (14) When in mode EXPRmode, apply a view + * (14) When in mode EXPRmode, do SAM conversion + * (15) When in mode EXPRmode, apply a view * If all this fails, error */ protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = { @@ -858,7 +872,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Block(_, tree1) => tree1.symbol case _ => tree.symbol } - if (!meth.isConstructor && (isFunctionType(pt) || samOf(pt).exists)) { // (4.2) + def shouldEtaExpandToSam: Boolean = { + // SI-9536 don't adapt parameterless method types to a to SAM's, fall through to empty application + // instead for backwards compatiblity with 2.11. See comments of that ticket and SI-7187 + // for analogous trouble with non-SAM eta expansion. Suggestions there are: a) deprecate eta expansion to Function0, + // or b) switch the order of eta-expansion and empty application in this adaptation. + !mt.params.isEmpty && samOf(pt).exists + } + if (!meth.isConstructor && (isFunctionType(pt) || shouldEtaExpandToSam)) { // (4.2) debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt") checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree, this) @@ -928,24 +949,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def insertApply(): Tree = { assert(!context.inTypeConstructorAllowed, mode) //@M val adapted = adaptToName(tree, nme.apply) - def stabilize0(pre: Type): Tree = stabilize(adapted, pre, MonoQualifierModes, WildcardType) - - // TODO reconcile the overlap between Typers#stablize and TreeGen.stabilize - val qual = adapted match { - case This(_) => - gen.stabilize(adapted) - case Ident(_) => - val owner = adapted.symbol.owner - val pre = - if (owner.isPackageClass) owner.thisType - else if (owner.isClass) context.enclosingSubClassContext(owner).prefix - else NoPrefix - stabilize0(pre) - case Select(qualqual, _) => - stabilize0(qualqual.tpe) - case other => - other - } + val qual = gen.stabilize(adapted) typedPos(tree.pos, mode, pt) { Select(qual setPos tree.pos.makeTransparent, nme.apply) } @@ -1020,72 +1024,70 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - def fallbackAfterVanillaAdapt(): Tree = { - def isPopulatedPattern = { - if ((tree.symbol ne null) && tree.symbol.isModule) - inferModulePattern(tree, pt) - - isPopulated(tree.tpe, approximateAbstracts(pt)) + def adaptExprNotFunMode(): Tree = { + def lastTry(err: AbsTypeError = null): Tree = { + debuglog("error tree = " + tree) + if (settings.debug && settings.explaintypes) explainTypes(tree.tpe, pt) + if (err ne null) context.issue(err) + if (tree.tpe.isErroneous || pt.isErroneous) setError(tree) + else adaptMismatchedSkolems() } - if (mode.inPatternMode && isPopulatedPattern) - return tree - val tree1 = constfold(tree, pt) // (10) (11) - if (tree1.tpe <:< pt) - return adapt(tree1, mode, pt, original) + // TODO: should we even get to fallbackAfterVanillaAdapt for an ill-typed tree? + if (mode.typingExprNotFun && !tree.tpe.isErroneous) { + @inline def tpdPos(transformed: Tree) = typedPos(tree.pos, mode, pt)(transformed) + @inline def tpd(transformed: Tree) = typed(transformed, mode, pt) - if (mode.typingExprNotFun) { - // The <: Any requirement inhibits attempts to adapt continuation types - // to non-continuation types. - if (tree.tpe <:< AnyTpe) pt.dealias match { - case TypeRef(_, UnitClass, _) => // (12) - if (!isPastTyper && settings.warnValueDiscard) - context.warning(tree.pos, "discarded non-Unit value") - return typedPos(tree.pos, mode, pt)(Block(List(tree), Literal(Constant(())))) - case TypeRef(_, sym, _) if isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt) => - if (!isPastTyper && settings.warnNumericWiden) - context.warning(tree.pos, "implicit numeric widening") - return typedPos(tree.pos, mode, pt)(Select(tree, "to" + sym.name)) - case _ => - } - if (pt.dealias.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt)) // (13) - return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) + @inline def warnValueDiscard(): Unit = + if (!isPastTyper && settings.warnValueDiscard) context.warning(tree.pos, "discarded non-Unit value") + @inline def warnNumericWiden(): Unit = + if (!isPastTyper && settings.warnNumericWiden) context.warning(tree.pos, "implicit numeric widening") - if (hasUndets) - return instantiate(tree, mode, pt) - - if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { - // (14); the condition prevents chains of views - debuglog("inferring view from " + tree.tpe + " to " + pt) - inferView(tree, tree.tpe, pt, reportAmbiguous = true) match { - case EmptyTree => - case coercion => - def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv) - context.echo(tree.pos, msg) - - debuglog(msg) - val silentContext = context.makeImplicit(context.ambiguousErrors) - val res = newTyper(silentContext).typed( - new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - silentContext.reporter.firstError match { - case Some(err) => context.issue(err) - case None => return res + // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types. + val anyTyped = tree.tpe <:< AnyTpe + + pt.dealias match { + case TypeRef(_, UnitClass, _) if anyTyped => // (12) + warnValueDiscard() ; tpdPos(gen.mkUnitBlock(tree)) + case TypeRef(_, numValueCls, _) if anyTyped && isNumericValueClass(numValueCls) && isNumericSubType(tree.tpe, pt) => // (10) (11) + warnNumericWiden() ; tpdPos(Select(tree, s"to${numValueCls.name}")) + case dealiased if dealiased.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt) => // (13) + tpd(adaptAnnotations(tree, this, mode, pt)) + case _ => + if (hasUndets) instantiate(tree, mode, pt) + else { + // (14) sam conversion + // TODO: figure out how to avoid partially duplicating typedFunction (samMatchingFunction) + // Could we infer the SAM type, assign it to the tree and add the attachment, + // all in one fell swoop at the end of typedFunction? + val samAttach = inferSamType(tree, pt, mode) + + if (samAttach.samTp ne NoType) tree.setType(samAttach.samTp).updateAttachment(samAttach) + else { // (15) implicit view application + val coercion = + if (context.implicitsEnabled) inferView(tree, tree.tpe, pt) + else EmptyTree + if (coercion ne EmptyTree) { + def msg = s"inferred view from ${tree.tpe} to $pt via $coercion: ${coercion.tpe}" + if (settings.logImplicitConv) context.echo(tree.pos, msg) + else debuglog(msg) + + val viewApplied = new ApplyImplicitView(coercion, List(tree)) setPos tree.pos + val silentContext = context.makeImplicit(context.ambiguousErrors) + val typedView = newTyper(silentContext).typed(viewApplied, mode, pt) + + silentContext.reporter.firstError match { + case None => typedView + case Some(err) => lastTry(err) + } + } else lastTry() } - } + } } - } - - debuglog("error tree = " + tree) - if (settings.debug && settings.explaintypes) - explainTypes(tree.tpe, pt) - - if (tree.tpe.isErroneous || pt.isErroneous) - setError(tree) - else - adaptMismatchedSkolems() + } else lastTry() } + def vanillaAdapt(tree: Tree) = { def applyPossible = { def applyMeth = member(adaptToName(tree, nme.apply), nme.apply) @@ -1119,8 +1121,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else if (tree.tpe <:< pt) tree - else - fallbackAfterVanillaAdapt() + else if (mode.inPatternMode && { inferModulePattern(tree, pt); isPopulated(tree.tpe, approximateAbstracts(pt)) }) + tree + else { + val constFolded = constfold(tree, pt) + if (constFolded.tpe <:< pt) adapt(constFolded, mode, pt, original) // set stage for (0) + else adaptExprNotFunMode() // (10) -- (15) + } } // begin adapt @@ -1185,7 +1192,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val savedUndetparams = context.undetparams silent(_.instantiate(tree, mode, UnitTpe)) orElse { _ => context.undetparams = savedUndetparams - val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant(())))) + val valueDiscard = atPos(tree.pos)(gen.mkUnitBlock(instantiate(tree, mode, WildcardType))) typed(valueDiscard, mode, UnitTpe) } } @@ -1246,7 +1253,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * If no conversion is found, return `qual` unchanged. * */ - def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) @@ -1262,7 +1269,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * a method `name`. If that's ambiguous try taking arguments into * account using `adaptToArguments`. */ - def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => ( silent (_.typedArgs(args.map(_.duplicate), mode)) @@ -1707,6 +1714,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper psym addChild context.owner else pending += ParentSealedInheritanceError(parent, psym) + if (psym.isLocalToBlock && !phase.erasedTypes) + psym addChild context.owner val parentTypeOfThis = parent.tpe.dealias.typeOfThis if (!(selfType <:< parentTypeOfThis) && @@ -1742,17 +1751,21 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefTpe))), classinfo.decls, clazz) - clazz.setInfo { - clazz.info match { - case PolyType(tparams, _) => PolyType(tparams, newinfo) - case _ => newinfo - } - } + updatePolyClassInfo(clazz, newinfo) FinitaryError(tparam) } } } + private def updatePolyClassInfo(clazz: Symbol, newinfo: ClassInfoType): clazz.type = { + clazz.setInfo { + clazz.info match { + case PolyType(tparams, _) => PolyType(tparams, newinfo) + case _ => newinfo + } + } + } + def typedClassDef(cdef: ClassDef): Tree = { val clazz = cdef.symbol val typedMods = typedModifiers(cdef.mods) @@ -1861,10 +1874,30 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // please FIXME: uncommenting this line breaks everything // val templ = treeCopy.Template(templ0, templ0.body, templ0.self, templ0.parents) val clazz = context.owner + + val parentTypes = parents1.map(_.tpe) + + // The parents may have been normalized by typedParentTypes. + // We must update the info as well, or we won't find the super constructor for our now-first parent class + // Consider `class C ; trait T extends C ; trait U extends T` + // `U`'s info will start with parent `T`, but `typedParentTypes` will return `List(C, T)` (`== parents1`) + // now, the super call in the primary ctor will fail to find `C`'s ctor, since it bases its search on + // `U`'s info, not the trees. + // + // For correctness and performance, we restrict this rewrite to anonymous classes, + // as others have their parents in order already (it seems!), and we certainly + // don't want to accidentally rewire superclasses for e.g. the primitive value classes. + // + // TODO: Find an example of a named class needing this rewrite, I tried but couldn't find one. + if (clazz.isAnonymousClass && clazz.info.parents != parentTypes) { +// println(s"updating parents of $clazz from ${clazz.info.parents} to $parentTypes") + updatePolyClassInfo(clazz, ClassInfoType(parentTypes, clazz.info.decls, clazz)) + } + clazz.annotations.map(_.completeInfo()) if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos) - val self1 = templ.self match { + val self1 = (templ.self: @unchecked) match { case vd @ ValDef(_, _, tpt, EmptyTree) => val tpt1 = checkNoEscaping.privates( clazz.thisSym, @@ -2228,7 +2261,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val allParams = meth.paramss.flatten for (p <- allParams) { for (n <- p.deprecatedParamName) { - if (allParams.exists(p1 => p1.name == n || (p != p1 && p1.deprecatedParamName.exists(_ == n)))) + if (allParams.exists(p1 => p != p1 && (p1.name == n || p1.deprecatedParamName.exists(_ == n)))) DeprecatedParamNameError(p, n) } } @@ -2581,8 +2614,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // the default uses applyOrElse's first parameter since the scrut's type has been widened val match_ = { - val defaultCase = methodBodyTyper.typedCase( - mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)), argTp, B1.tpe) + val cdef = mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)) + val List(defaultCase) = methodBodyTyper.typedCases(List(cdef), argTp, B1.tpe) treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase) } match_ setType B1.tpe @@ -2710,187 +2743,99 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - /** Synthesize and type check the implementation of a type with a Single Abstract Method - * - * `{ (p1: T1, ..., pN: TN) => body } : S` - * - * expands to (where `S` is the expected type that defines a single abstract method named `apply`) - * - * `{ - * def apply$body(p1: T1, ..., pN: TN): T = body - * new S { - * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN) - * } - * }` - * - * If 'T' is not fully defined, it is inferred by type checking - * `apply$body` without a result type before type checking the block. - * The method's inferred result type is used instead of `T`. [See test/files/pos/sammy_poly.scala] - * - * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`, - * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not... - * If it is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters. - * - * The types T1' ... TN' and T' are derived from the method signature of the sam method, - * as seen from the fully defined `samClassTpFullyDefined`. - * - * The function's body is put in a method outside of the class definition to enforce scoping. - * S's members should not be in scope in `body`. - * - * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list), - * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple. - * - * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined. - * However T must be fully defined before we type the instantiation, as it'll end up as a parent type, - * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code, - * and have the instantiation of the first occurrence propagate to the rest of the block. - * - * TODO: by-name params - * scala> trait LazySink { def accept(a: => Any): Unit } - * defined trait LazySink - * - * scala> val f: LazySink = (a) => (a, a) - * f: LazySink = $anonfun$1@1fb26910 - * - * scala> f(println("!")) - * <console>:10: error: LazySink does not take parameters - * f(println("!")) - * ^ - * - * scala> f.accept(println("!")) - * ! - * ! - */ - def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = { - // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info - val sampos = fun.pos - - // if the expected sam type is fully defined, use it for the method's result type - // otherwise, NoType, so that type inference will determine the method's result type - // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former - // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class - val samDefTp = if (isFullyDefined(resPt)) resPt else NoType - val bodyName = newTermName(sam.name + "$body") - - // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body` - val samBodyDef = - DefDef(NoMods, - bodyName, - Nil, - List(fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef` - TypeTree(samDefTp) setPos sampos.focus, - fun.body) - - // If we need to enter the sym for the body def before type checking the block, - // we'll create a nested context, as explained below. - var nestedTyper = this - - // Type check body def before classdef to fully determine samClassTp (if necessary). - // As `samClassTp` determines a parent type for the class, - // we can't type check `block` in one go unless `samClassTp` is fully defined. - val samClassTpFullyDefined = - if (isFullyDefined(samClassTp)) samClassTp + /** Synthesize and type check the implementation of a type with a Single Abstract Method. + * + * Based on a type checked Function node `{ (p1: T1, ..., pN: TN) => body } : S` + * where `S` is the expected type that defines a single abstract method (call it `apply` for the example), + * that has signature `(p1: T1', ..., pN: TN'): T'`, synthesize the instantiation of the following anonymous class + * + * ``` + * new S { + * def apply$body(p1: T1, ..., pN: TN): T = body + * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN) + * } + * ``` + * + * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `pt`, + * If `pt` is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters. + * + * The types T1' ... TN' and T' are derived from the method signature of the sam method, + * as seen from the fully defined `samClassTpFullyDefined`. + * + * The function's body is put in a (static) method in the class definition to enforce scoping. + * S's members should not be in scope in `body`. (Putting it in the block outside the class runs into implementation problems described below) + * + * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list), + * is to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple. + * + * Impl notes: + * - `fun` has a FunctionType, but the expected type `pt` is some SAM type -- let's remedy that + * - `fun` is fully attributed, so we'll have to wrangle some symbols into shape (owner change, vparam syms) + * - after experimentation, it works best to type check function literals fully first and then adapt to a sam type, + * as opposed to a sam-specific code paths earlier on in type checking (in typedFunction). + * For one, we want to emit the same bytecode regardless of whether the expected + * function type is a built-in FunctionN or some SAM type + * + */ + def inferSamType(fun: Tree, pt: Type, mode: Mode): SAMFunction = { + val sam = + if (fun.isInstanceOf[Function] && !isFunctionType(pt)) { + // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? + // seems like overloading complicates things? + val sam = samOf(pt) + if (samMatchesFunctionBasedOnArity(sam, fun.asInstanceOf[Function].vparams)) sam + else NoSymbol + } else NoSymbol + + def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { + val samMethType = pt memberInfo sam + fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) + } + + SAMFunction( + if (!sam.exists) NoType + else if (fullyDefinedMeetsExpectedFunTp(pt)) pt else try { - // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below. - // The symbol is entered in the same scope used for the block below, and won't thus be reentered later. - // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala] - // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams] - val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef) - nestedTyper = newTyper(nestedCtx) - - // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above - val actualSamType = samBodyDef.symbol.info + val samClassSym = pt.typeSymbol // we're trying to fully define the type arguments for this type constructor - val samTyCon = samClassTp.typeSymbol.typeConstructor + val samTyCon = samClassSym.typeConstructor // the unknowns - val tparams = samClassTp.typeSymbol.typeParams + val tparams = samClassSym.typeParams // ... as typevars - val tvars = tparams map freshVar - - // 1. Recover partial information: - // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined - // - constrain typevars to be equal to type args that are fully defined - val samClassTpMoreDefined = appliedType(samTyCon, - (samClassTp.typeArgs, tparams, tvars).zipped map { - case (a, _, tv) if isFullyDefined(a) => tv =:= a; a - case (_, p, _) => p.typeConstructor - }) + val tvars = tparams map freshVar - // the method type we're expecting the synthesized sam to have, based on the expected sam type, - // where fully defined type args to samClassTp have been preserved, - // with the unknown args replaced by their corresponding type param - val expectedSamType = samClassTpMoreDefined.memberInfo(sam) + val ptVars = appliedType(samTyCon, tvars) - // 2. make sure the body def's actual type (formals and result) conforms to - // sam's expected type (in terms of the typevars that represent the sam's class's type params) - actualSamType <:< expectedSamType.substituteTypes(tparams, tvars) + // carry over info from pt + ptVars <:< pt - // solve constraints tracked by tvars - val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil)) + val samInfoWithTVars = ptVars.memberInfo(sam) - debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams") + // use function type subtyping, not method type subtyping (the latter is invariant in argument types) + fun.tpe <:< functionType(samInfoWithTVars.paramTypes, samInfoWithTVars.finalResultType) - // a fully defined samClassTp - appliedType(samTyCon, targs) - } catch { - case _: NoInstance | _: TypeError => - devWarning(sampos, s"Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam)") - samClassTp - } - - // what's the signature of the method that we should actually be overriding? - val samMethTp = samClassTpFullyDefined memberInfo sam - // Before the mutation, `tp <:< vpar.tpt.tpe` should hold. - // TODO: error message when this is not the case, as the expansion won't type check - // - Ti' <:< Ti and T <: T' must hold for the samDef body to type check - val funArgTps = foreach2(samMethTp.paramTypes, fun.vparams)((tp, vpar) => vpar.tpt setType tp) - - // `final override def ${sam.name}($p1: $T1', ..., $pN: $TN'): ${samMethTp.finalResultType} = ${sam.name}\$body'($p1, ..., $pN)` - val samDef = - DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC), - sam.name.toTermName, - Nil, - List(fun.vparams), - TypeTree(samMethTp.finalResultType) setPos sampos.focus, - Apply(Ident(bodyName), fun.vparams map gen.paramToArg) - ) + val variances = tparams map varianceInType(sam.info) - val serializableParentAddendum = - if (typeIsSubTypeOfSerializable(samClassTp)) Nil - else List(TypeTree(SerializableTpe)) - - val classDef = - ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil, - gen.mkTemplate( - parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum, - self = noSelfType, - constrMods = NoMods, - vparamss = ListOfNil, - body = List(samDef), - superPos = sampos.focus - ) - ) - - // type checking the whole block, so that everything is packaged together nicely - // and we don't have to create any symbols by hand - val block = - nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) { - Block( - samBodyDef, - classDef, - Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), Nil) - ) - } + // solve constraints tracked by tvars + val targs = solvedTypes(tvars, tparams, variances, upper = false, lubDepth(sam.info :: Nil)) - // TODO: improve error reporting -- when we're in silent mode (from `silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError`) - // the errors in the function don't get out... - if (block exists (_.isErroneous)) - context.error(fun.pos, s"Could not derive subclass of $samClassTp\n (with SAM `def $sam$samMethTp`)\n based on: $fun.") + debuglog(s"sam infer: $pt --> ${appliedType(samTyCon, targs)} by ${fun.tpe} <:< $samInfoWithTVars --> $targs for $tparams") - classDef.symbol addAnnotation SerialVersionUIDAnnotation - block + val ptFullyDefined = appliedType(samTyCon, targs) + if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { + debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") + ptFullyDefined + } else { + debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") + NoType + } + } catch { + case e@(_: NoInstance | _: TypeError) => + debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") + NoType + }, sam) } /** Type check a function literal. @@ -2900,16 +2845,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * - a type with a Single Abstract Method (under -Xexperimental for now). */ private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { - val numVparams = fun.vparams.length + val vparams = fun.vparams + val numVparams = vparams.length val FunctionSymbol = if (numVparams > definitions.MaxFunctionArity) NoSymbol else FunctionClass(numVparams) + val ptSym = pt.typeSymbol + /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity, * as `(a => a): Int => Int` should not (yet) get the sam treatment. */ val sam = - if (pt.typeSymbol == FunctionSymbol) NoSymbol + if (ptSym == NoSymbol || ptSym == FunctionSymbol || ptSym == PartialFunctionClass) NoSymbol else samOf(pt) /* The SAM case comes first so that this works: @@ -2917,79 +2865,101 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * (a => a): MyFun * * Note that the arity of the sam must correspond to the arity of the function. + * TODO: handle vararg sams? */ - val samViable = sam.exists && sameLength(sam.info.params, fun.vparams) - val ptNorm = if (samViable) samToFunctionType(pt, sam) else pt + val ptNorm = + if (samMatchesFunctionBasedOnArity(sam, vparams)) samToFunctionType(pt, sam) + else pt val (argpts, respt) = ptNorm baseType FunctionSymbol match { case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) + case _ => (vparams map (if (pt == ErrorType) (_ => ErrorType) else (_ => NoType)), WildcardType) } - if (!FunctionSymbol.exists) - MaxFunctionArityError(fun) - else if (argpts.lengthCompare(numVparams) != 0) - WrongNumberOfParametersError(fun, argpts) + if (!FunctionSymbol.exists) MaxFunctionArityError(fun) + else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { - var issuedMissingParameterTypeError = false - foreach2(fun.vparams, argpts) { (vparam, argpt) => + val paramsMissingType = mutable.ArrayBuffer.empty[ValDef] //.sizeHint(numVparams) probably useless, since initial size is 16 and max fun arity is 22 + // first, try to define param types from expected function's arg types if needed + foreach2(vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { - val vparamType = - if (isFullyDefined(argpt)) argpt - else { - fun match { - case etaExpansion(vparams, fn, args) => - silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => - // if context.undetparams is not empty, the function was polymorphic, - // so we need the missing arguments to infer its type. See #871 - //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) - val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) - if (isFunctionType(ftpe) && isFullyDefined(ftpe)) - return typedFunction(fun, mode, ftpe) - } - case _ => - } - MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError) - issuedMissingParameterTypeError = true - ErrorType - } - vparam.tpt.setType(vparamType) + if (isFullyDefined(argpt)) vparam.tpt setType argpt + else paramsMissingType += vparam + if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus } } - fun.body match { - // translate `x => x match { <cases> }` : PartialFunction to - // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` - case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) => - // go to outer context -- must discard the context that was created for the Function since we're discarding the function - // thus, its symbol, which serves as the current context.owner, is not the right owner - // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) - val outerTyper = newTyper(context.outer) - val p = fun.vparams.head - if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe + // If we're typing `(a1: T1, ..., aN: TN) => m(a1,..., aN)`, where some Ti are not fully defined, + // type `m` directly (undoing eta-expansion of method m) to determine the argument types. + // This tree is the result from one of: + // - manual eta-expansion with named arguments (x => f(x)); + // - wildcard-style eta expansion (`m(_, _,)`); + // - instantiateToMethodType adapting a tree of method type to a function type using etaExpand. + // + // Note that method values are a separate thing (`m _`): they have the idiosyncratic shape + // of `Typed(expr, Function(Nil, EmptyTree))` + val ptUnrollingEtaExpansion = + if (paramsMissingType.nonEmpty && pt != ErrorType) fun.body match { + // we can compare arguments and parameters by name because there cannot be a binder between + // the function's valdefs and the Apply's arguments + case Apply(meth, args) if (vparams corresponds args) { case (p, Ident(name)) => p.name == name case _ => false } => + // We're looking for a method (as indicated by FUNmode in the silent typed below), + // so let's make sure our expected type is a MethodType + val methArgs = NoSymbol.newSyntheticValueParams(argpts map { case NoType => WildcardType case tp => tp }) + silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt))) filter (isMonoContext) map { methTyped => + // if context.undetparams is not empty, the method was polymorphic, + // so we need the missing arguments to infer its type. See #871 + val funPt = normalize(methTyped.tpe) baseType FunctionClass(numVparams) + // println(s"typeUnEtaExpanded $meth : ${methTyped.tpe} --> normalized: $funPt") + + // If we are sure this function type provides all the necesarry info, so that we won't have + // any undetermined argument types, go ahead an recurse below (`typedFunction(fun, mode, ptUnrollingEtaExpansion)`) + // and rest assured we won't end up right back here (and keep recursing) + if (isFunctionType(funPt) && funPt.typeArgs.iterator.take(numVparams).forall(isFullyDefined)) funPt + else null + } orElse { _ => null } + case _ => null + } else null + + + if (ptUnrollingEtaExpansion ne null) typedFunction(fun, mode, ptUnrollingEtaExpansion) + else { + // we ran out of things to try, missing parameter types are an irrevocable error + var issuedMissingParameterTypeError = false + paramsMissingType.foreach { vparam => + vparam.tpt setType ErrorType + MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError) + issuedMissingParameterTypeError = true + } - outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt) + fun.body match { + // translate `x => x match { <cases> }` : PartialFunction to + // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` + case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) => + // go to outer context -- must discard the context that was created for the Function since we're discarding the function + // thus, its symbol, which serves as the current context.owner, is not the right owner + // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) + val outerTyper = newTyper(context.outer) + val p = vparams.head + if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe - // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body` - // to an instance of the corresponding anonymous subclass of `pt`. - case _ if samViable => - newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode) + outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt) - // regular Function - case _ => - val vparamSyms = fun.vparams map { vparam => - enterSym(context, vparam) - if (context.retyping) context.scope enter vparam.symbol - vparam.symbol - } - val vparams = fun.vparams mapConserve typedValDef - val formals = vparamSyms map (_.tpe) - val body1 = typed(fun.body, respt) - val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = appliedType(FunctionSymbol, formals :+ restpe: _*) + case _ => + val vparamSyms = vparams map { vparam => + enterSym(context, vparam) + if (context.retyping) context.scope enter vparam.symbol + vparam.symbol + } + val vparamsTyped = vparams mapConserve typedValDef + val formals = vparamSyms map (_.tpe) + val body1 = typed(fun.body, respt) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = phasedAppliedType(FunctionSymbol, formals :+ restpe) - treeCopy.Function(fun, vparams, body1) setType funtpe + treeCopy.Function(fun, vparamsTyped, body1) setType funtpe + } } } } @@ -3152,7 +3122,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (phase.erasedTypes) stats1 else { // As packages are open, it doesn't make sense to check double definitions here. Furthermore, - // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope` + // it is expensive if the package is large. Instead, such double definitions are checked in `Namers.enterInScope` if (!context.owner.isPackageClass) checkNoDoubleDefs addSynthetics(stats1) @@ -3219,6 +3189,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // less expensive than including them in inferMethodAlternative (see below). def shapeType(arg: Tree): Type = arg match { case Function(vparams, body) => + // No need for phasedAppliedType, as we don't get here during erasure -- + // overloading resolution happens during type checking. + // During erasure, the condition above (fun.symbol.isOverloaded) is false. functionType(vparams map (_ => AnyTpe), shapeType(body)) case AssignOrNamedArg(Ident(name), rhs) => NamedType(name, shapeType(rhs)) @@ -3664,7 +3637,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val annType = annTpt.tpe finish( - if (typedFun.isErroneous) + if (typedFun.isErroneous || annType == null) ErroneousAnnotation else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) { // annotation to be saved as java classfile annotation @@ -4141,6 +4114,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ann setType arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe + // For `f(): @inline/noinline` callsites, add the InlineAnnotatedAttachment. TypeApplys + // are eliminated by erasure, so add it to the underlying function in this case. + def setInlineAttachment(t: Tree, att: InlineAnnotatedAttachment): Unit = t match { + case TypeApply(fun, _) => setInlineAttachment(fun, att) + case _ => t.updateAttachment(att) + } + if (atype.hasAnnotation(definitions.ScalaNoInlineClass)) setInlineAttachment(arg1, NoInlineCallsiteAttachment) + else if (atype.hasAnnotation(definitions.ScalaInlineClass)) setInlineAttachment(arg1, InlineCallsiteAttachment) Typed(arg1, resultingTypeTree(atype)) setPos tree.pos setType atype } } @@ -4292,7 +4273,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (pt.typeSymbol == PartialFunctionClass) synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt) else { - val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1 + val arity = functionArityFromType(pt) match { case -1 => 1 case arity => arity } // SI-8429: consider sam and function type equally in determining function arity + val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { ValDef(Modifiers(PARAM | SYNTHETIC), @@ -4392,31 +4374,43 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper treeCopy.New(tree, tpt1).setType(tp) } - def functionTypeWildcard(tree: Tree, arity: Int): Type = { - val tp = functionType(List.fill(arity)(WildcardType), WildcardType) - if (tp == NoType) MaxFunctionArityError(tree) - tp - } - - def typedEta(expr1: Tree): Tree = expr1.tpe match { - case TypeRef(_, ByNameParamClass, _) => - val expr2 = Function(List(), expr1) setPos expr1.pos - new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) - typed1(expr2, mode, pt) - case NullaryMethodType(restpe) => - val expr2 = Function(List(), expr1) setPos expr1.pos - new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) - typed1(expr2, mode, pt) - case PolyType(_, MethodType(formals, _)) => - if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) - case MethodType(formals, _) => - if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) + def functionTypeWildcard(arity: Int): Type = + functionType(List.fill(arity)(WildcardType), WildcardType) + + def checkArity(tree: Tree)(tp: Type): tp.type = tp match { + case NoType => MaxFunctionArityError(tree); tp + case _ => tp + } + + + /** Eta expand an expression like `m _`, where `m` denotes a method or a by-name argument + * + * The spec says: + * The expression `$e$ _` is well-formed if $e$ is of method type or if $e$ is a call-by-name parameter. + * (1) If $e$ is a method with parameters, `$e$ _` represents $e$ converted to a function type + * by [eta expansion](#eta-expansion). + * (2) If $e$ is a parameterless method or call-by-name parameter of type `=>$T$`, `$e$ _` represents + * the function of type `() => $T$`, which evaluates $e$ when it is applied to the empty parameterlist `()`. + */ + def typedEta(methodValue: Tree): Tree = methodValue.tpe match { + case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1) + val formals = tp.params + if (isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)) methodValue + else adapt(methodValue, mode, checkArity(methodValue)(functionTypeWildcard(formals.length))) + + case TypeRef(_, ByNameParamClass, _) | NullaryMethodType(_) => // (2) + val pos = methodValue.pos + // must create it here to change owner (normally done by typed's typedFunction) + val funSym = context.owner.newAnonymousFunctionValue(pos) + new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue + + typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt) + case ErrorType => - expr1 + methodValue + case _ => - UnderscoreEtaError(expr1) + UnderscoreEtaError(methodValue) } def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = { @@ -4460,7 +4454,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Annotated(_, r) => treesInResult(r) case If(_, t, e) => treesInResult(t) ++ treesInResult(e) case Try(b, catches, _) => treesInResult(b) ++ catches - case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) + case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) // a method value case Select(qual, name) => treesInResult(qual) case Apply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult) case TypeApply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult) @@ -4479,7 +4473,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tryTypedArgs(args, forArgMode(fun, mode)) match { case Some(args1) if !args1.exists(arg => arg.exists(_.isErroneous)) => val qual1 = - if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true) + if (!pt.isError) adaptToArguments(qual, name, args1, pt) else qual if (qual1 ne qual) { val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos @@ -4640,10 +4634,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def findMixinSuper(site: Type): Type = { var ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) - ps = site.parents filter (_.typeSymbol.toInterface.name == mix) + ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) { debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name)) - if (phase.erasedTypes && context.enclClass.owner.isImplClass) { + if (phase.erasedTypes && context.enclClass.owner.isTrait) { // the reference to super class got lost during erasure restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class") ErrorType @@ -4706,7 +4700,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) if (name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) { - val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = true, saveErrors = true) + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) } @@ -5100,11 +5094,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // because `expr` might contain nested macro calls (see SI-6673) // // Note: apparently `Function(Nil, EmptyTree)` is the secret parser marker - // which means trailing underscore. + // which means trailing underscore -- denoting a method value. See makeMethodValue in TreeBuilder. case Typed(expr, Function(Nil, EmptyTree)) => typed1(suppressMacroExpansion(expr), mode, pt) match { case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef) - case exprTyped => typedEta(checkDead(exprTyped)) + case methodValue => typedEta(checkDead(methodValue)) } case Typed(expr, tpt) => val tpt1 = typedType(tpt, mode) // type the ascribed type first @@ -5195,17 +5189,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case MethodType(p :: _, _) => p.isImplicit // implicit method requires no args case _ => true // catches all others including NullaryMethodType } - def isPlausible(m: Symbol) = m.alternatives exists (m => requiresNoArgs(m.info)) + def isPlausible(m: Symbol) = !m.isPackage && m.alternatives.exists(x => requiresNoArgs(x.info)) def maybeWarn(s: String): Unit = { def warn(message: String) = context.warning(lit.pos, s"possible missing interpolator: $message") def suspiciousSym(name: TermName) = context.lookupSymbol(name, _ => true).symbol - def suspiciousExpr = InterpolatorCodeRegex findFirstIn s + val suspiciousExprs = InterpolatorCodeRegex findAllMatchIn s def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(TermName(s drop 1))) - - if (suspiciousExpr.nonEmpty) - warn("detected an interpolated expression") // "${...}" - else + def isCheapIdent(expr: String) = (Character.isJavaIdentifierStart(expr.charAt(0)) && + expr.tail.forall(Character.isJavaIdentifierPart)) + def warnableExpr(expr: String) = !expr.isEmpty && (!isCheapIdent(expr) || isPlausible(suspiciousSym(TermName(expr)))) + + if (suspiciousExprs.nonEmpty) { + val exprs = (suspiciousExprs map (_ group 1)).toList + // short-circuit on leading ${} + if (!exprs.head.isEmpty && exprs.exists(warnableExpr)) + warn("detected an interpolated expression") // "${...}" + } else suspiciousIdents find isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id" } lit match { @@ -5229,7 +5229,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (refTyped.isErrorTyped) { setError(tree) } else { - tree setType refTyped.tpe.resultType + tree setType refTyped.tpe.resultType.deconst if (refTyped.isErrorTyped || treeInfo.admitsTypeSelection(refTyped)) tree else UnstableTreeError(tree) } @@ -5417,6 +5417,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) + if (mode.inPatternMode && !mode.inPolyMode && result.isType) + PatternMustBeValue(result, pt) + result } @@ -5506,10 +5509,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich, // but arbitrary conversions (in adapt) are disabled // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this? - typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) match { - case tpt if tpt.isType => PatternMustBeValue(tpt, pt); tpt - case pat => pat - } + typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) } /** Types a (fully parameterized) type tree */ diff --git a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala index 37fbb73b85..f2911fb98b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala @@ -6,9 +6,6 @@ package scala.tools.nsc package typechecker -import scala.collection.mutable -import scala.reflect.internal.util.{ BatchSourceFile, Statistics } -import mutable.ListBuffer import Mode._ trait TypersTracking { |