summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Typers.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala1366
1 files changed, 753 insertions, 613 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index ac0a653626..69bf5fdef7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -13,11 +13,12 @@ package scala
package tools.nsc
package typechecker
-import scala.collection.{mutable, immutable}
-import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance, ListOfNil }
+import scala.collection.{immutable, mutable}
+import scala.reflect.internal.util.{ListOfNil, Statistics}
import mutable.ListBuffer
import symtab.Flags._
import Mode._
+import scala.reflect.macros.whitebox
// Suggestion check whether we can do without priming scopes with symbols of outer scopes,
// like the IDE does.
@@ -105,12 +106,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 +129,22 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def canTranslateEmptyListToNil = true
def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree
+ // used to exempt synthetic accessors (i.e. those that are synthesized by the compiler to access a field)
+ // from skolemization because there's a weird bug that causes spurious type mismatches
+ // (it seems to have something to do with existential abstraction over values
+ // https://github.com/scala/scala-dev/issues/165
+ // when we're past typer, lazy accessors are synthetic, but before they are user-defined
+ // to make this hack less hacky, we could rework our flag assignment to allow for
+ // requiring both the ACCESSOR and the SYNTHETIC bits to trigger the exemption
+ private def isSyntheticAccessor(sym: Symbol) = sym.isAccessor && (!sym.isLazy || isPastTyper)
+
+ // 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 +168,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 +206,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 +224,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 +254,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 +561,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 +744,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) {
@@ -760,7 +783,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else FunctionClass(numVparams)
}
- if (samSym.exists && samSym.owner != correspondingFunctionSymbol) // don't treat Functions as SAMs
+ if (samSym.exists && tp.typeSymbol != correspondingFunctionSymbol) // don't treat Functions as SAMs
wildcardExtrapolation(normalize(tp memberInfo samSym))
else NoType
}
@@ -798,7 +821,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 = {
@@ -840,11 +864,24 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ =>
}
debuglog(s"fallback on implicits: ${tree}/$resetTree")
- val tree1 = typed(resetTree, mode)
- // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that
- // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin.
- tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode, pt)
- if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree)
+ // SO-10066 Need to patch the enclosing tree in the context to make translation of Dynamic
+ // work during fallback typechecking below.
+ val resetContext: Context = {
+ object substResetForOriginal extends Transformer {
+ override def transform(tree: Tree): Tree = {
+ if (tree eq original) resetTree
+ else super.transform(tree)
+ }
+ }
+ context.make(substResetForOriginal.transform(context.tree))
+ }
+ typerWithLocalContext(resetContext) { typer1 =>
+ val tree1 = typer1.typed(resetTree, mode)
+ // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that
+ // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin.
+ tree1 setType pluginsTyped(tree1.tpe, typer1, tree1, mode, pt)
+ if (tree1.isEmpty) tree1 else typer1.adapt(tree1, mode, pt, EmptyTree)
+ }
}
)
else
@@ -858,9 +895,32 @@ 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 cantAdapt =
+ if (context.implicitsEnabled) MissingArgsForMethodTpeError(tree, meth)
+ else setError(tree)
+
+ // constructors do not eta-expand
+ if (meth.isConstructor) cantAdapt
+ // (4.2) eta-expand method value when function or sam type is expected
+ else if (isFunctionType(pt) || (!mt.params.isEmpty && samOf(pt).exists)) {
+ // SI-9536 `!mt.params.isEmpty &&`: for backwards compatibility with 2.11,
+ // we don't adapt a zero-arg method value to a SAM
+ // In 2.13, we won't do any eta-expansion for zero-arg method values, but we should deprecate first
+
debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt")
checkParamsConvertible(tree, tree.tpe)
+
+ // SI-7187 eta-expansion of zero-arg method value is deprecated, switch order of (4.3) and (4.2) in 2.13
+ def isExplicitEtaExpansion = original match {
+ case Typed(_, Function(Nil, EmptyTree)) => true // tree shape for `f _`
+ case _ => false
+ }
+ if (mt.params.isEmpty && !isExplicitEtaExpansion) {
+ currentRun.reporting.deprecationWarning(tree.pos, NoSymbol,
+ s"Eta-expansion of zero-argument method values is deprecated. Did you intend to write ${Apply(tree, Nil)}?", "2.12.0")
+ }
+
val tree0 = etaExpand(context.unit, tree, this)
// #2624: need to infer type arguments for eta expansion of a polymorphic method
@@ -874,12 +934,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else
typed(tree0, mode, pt)
}
- else if (!meth.isConstructor && mt.params.isEmpty) // (4.3)
- adapt(typed(Apply(tree, Nil) setPos tree.pos), mode, pt, original)
- else if (context.implicitsEnabled)
- MissingArgsForMethodTpeError(tree, meth)
- else
- setError(tree)
+ // (4.3) apply to empty argument list -- TODO 2.13: move this one case up to avoid eta-expanding at arity 0
+ else if (mt.params.isEmpty) adapt(typed(Apply(tree, Nil) setPos tree.pos), mode, pt, original)
+ else cantAdapt
}
def adaptType(): Tree = {
@@ -928,24 +985,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 +1060,75 @@ 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 _ =>
+ @inline def warnValueDiscard(): Unit = if (!isPastTyper && settings.warnValueDiscard) {
+ def isThisTypeResult = (tree, tree.tpe) match {
+ case (Apply(Select(receiver, _), _), SingleType(_, sym)) => sym == receiver.symbol
+ case _ => false
+ }
+ if (!isThisTypeResult) context.warning(tree.pos, "discarded non-Unit value")
}
- if (pt.dealias.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt)) // (13)
- return typed(adaptAnnotations(tree, this, mode, pt), mode, pt)
-
- 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
+ @inline def warnNumericWiden(): Unit =
+ if (!isPastTyper && settings.warnNumericWiden) context.warning(tree.pos, "implicit numeric widening")
+
+ // 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 +1162,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
@@ -1139,7 +1187,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
adapt(tree setType arg, mode, pt, original)
- case tp if mode.typingExprNotLhs && isExistentialType(tp) =>
+ case tp if mode.typingExprNotLhs && isExistentialType(tp) && !isSyntheticAccessor(context.owner) =>
adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original)
case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (3)
// assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
@@ -1185,7 +1233,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 +1294,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 +1310,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))
@@ -1697,9 +1745,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation &&
!sameSourceFile && !context.owner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) {
- val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse ""
- val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix"
- context.deprecationWarning(parent.pos, psym, msg)
+ val version = psym.deprecatedInheritanceVersion.getOrElse("")
+ val since = if (version.isEmpty) version else s" (since $version)"
+ val message = psym.deprecatedInheritanceMessage.map(msg => s": $msg").getOrElse("")
+ val report = s"inheritance from ${psym.fullLocationString} is deprecated$since$message"
+ context.deprecationWarning(parent.pos, psym, report, version)
}
val parentTypeOfThis = parent.tpe.dealias.typeOfThis
@@ -1737,17 +1787,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)
@@ -1856,10 +1910,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,
@@ -1897,11 +1971,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
- val body2 = {
- val body2 =
- if (isPastTyper || reporter.hasErrors) body1
- else body1 flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
- val primaryCtor = treeInfo.firstConstructor(body2)
+ val bodyWithPrimaryCtor = {
+ val primaryCtor = treeInfo.firstConstructor(body1)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
val argss = superArgs(parents1.head) getOrElse Nil
@@ -1910,10 +1981,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
case _ => primaryCtor
}
- body2 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
+ body1 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
}
- val body3 = typedStats(body2, templ.symbol)
+ val body3 = typedStats(bodyWithPrimaryCtor, templ.symbol)
if (clazz.info.firstParent.typeSymbol == AnyValClass)
validateDerivedValueClass(clazz, body3)
@@ -1956,13 +2027,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// use typedValDef instead. this version is called after creating a new context for the ValDef
private def typedValDefImpl(vdef: ValDef) = {
val sym = vdef.symbol.initialize
- val typedMods = typedModifiers(vdef.mods)
+ val typedMods = if (nme.isLocalName(sym.name) && sym.isPrivateThis && !vdef.mods.isPrivateLocal) {
+ // SI-10009 This tree has been given a field symbol by `enterGetterSetter`, patch up the
+ // modifiers accordingly so that we can survive resetAttrs and retypechecking.
+ // Similarly, we use `sym.name` rather than `vdef.name` below to use the local name.
+ typedModifiers(vdef.mods.copy(flags = sym.flags, privateWithin = tpnme.EMPTY))
+ } else typedModifiers(vdef.mods)
sym.annotations.map(_.completeInfo())
val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt))
checkNonCyclic(vdef, tpt1)
- if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable)
+ // allow trait accessors: it's the only vehicle we have to hang on to annotations that must be passed down to
+ // the field that's mixed into a subclass
+ if (sym.hasAnnotation(definitions.VolatileAttr) && !((sym hasFlag MUTABLE | LAZY) || (sym hasFlag ACCESSOR) && sym.owner.isTrait))
VolatileValueError(vdef)
val rhs1 =
@@ -1979,7 +2057,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
override def matches(sym: Symbol, sym1: Symbol) =
if (sym.isSkolem) matches(sym.deSkolemize, sym1)
else if (sym1.isSkolem) matches(sym, sym1.deSkolemize)
- else super[SubstTypeMap].matches(sym, sym1)
+ else super.matches(sym, sym1)
}
// allow defaults on by-name parameters
if (sym hasFlag BYNAMEPARAM)
@@ -1989,7 +2067,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
} else tpt1.tpe
transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2)
}
- treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType
+ treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType
}
/** Enter all aliases of local parameter accessors.
@@ -2028,35 +2106,39 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ =>
(call, Nil)
}
- val (superConstr, superArgs) = decompose(rhs)
- assert(superConstr.symbol ne null, superConstr)//debug
- def superClazz = superConstr.symbol.owner
- def superParamAccessors = superClazz.constrParamAccessors
// associate superclass paramaccessors with their aliases
- if (superConstr.symbol.isPrimaryConstructor && !superClazz.isJavaDefined && sameLength(superParamAccessors, superArgs)) {
- for ((superAcc, superArg @ Ident(name)) <- superParamAccessors zip superArgs) {
- if (mexists(vparamss)(_.symbol == superArg.symbol)) {
- val alias = (
- superAcc.initialize.alias
- orElse (superAcc getterIn superAcc.owner)
- filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias)
- )
- if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) {
- val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match {
- case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed
- case acc => acc
- }
- ownAcc match {
- case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
- debuglog(s"$acc has alias ${alias.fullLocationString}")
- acc setAlias alias
- case _ =>
+ val (superConstr, superArgs) = decompose(rhs)
+ if (superConstr.symbol.isPrimaryConstructor) {
+ val superClazz = superConstr.symbol.owner
+ if (!superClazz.isJavaDefined) {
+ val superParamAccessors = superClazz.constrParamAccessors
+ if (sameLength(superParamAccessors, superArgs)) {
+ for ((superAcc, superArg@Ident(name)) <- superParamAccessors zip superArgs) {
+ if (mexists(vparamss)(_.symbol == superArg.symbol)) {
+ val alias = (
+ superAcc.initialize.alias
+ orElse (superAcc getterIn superAcc.owner)
+ filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias)
+ )
+ if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) {
+ val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match {
+ case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed
+ case acc => acc
+ }
+ ownAcc match {
+ case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
+ debuglog(s"$acc has alias ${alias.fullLocationString}")
+ acc setAlias alias
+ case _ =>
+ }
+ }
}
}
}
}
}
+
pending.foreach(ErrorUtils.issueTypeError)
}
@@ -2158,6 +2240,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
def typedDefDef(ddef: DefDef): DefDef = {
+ // an accessor's type completer may mutate a type inside `ddef` (`== context.unit.synthetics(ddef.symbol)`)
+ // concretely: it sets the setter's parameter type or the getter's return type (when derived from a valdef with empty tpt)
val meth = ddef.symbol.initialize
reenterTypeParams(ddef.tparams)
@@ -2167,7 +2251,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!isPastTyper && meth.isPrimaryConstructor) {
for (vparams <- ddef.vparamss; vd <- vparams) {
if (vd.mods.isParamAccessor) {
- namer.validateParam(vd)
+ vd.symbol setAnnotations (vd.symbol.annotations filter AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = true))
}
}
}
@@ -2203,9 +2287,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
}
- if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass)) {
- // At this point in AnyVal there is no supercall, which will blow up
- // in computeParamAliases; there's nothing to be computed for Anyval anyway.
+ if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass) && !meth.isJava) {
+ // There are no supercalls for AnyVal or constructors from Java sources, which
+ // would blow up in computeParamAliases; there's nothing to be computed for them
+ // anyway.
if (meth.isPrimaryConstructor)
computeParamAliases(meth.owner, vparamss1, rhs1)
else
@@ -2223,7 +2308,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)
}
}
@@ -2319,7 +2404,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
for (stat <- block.stats) enterLabelDef(stat)
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
- // This is very tricky stuff, because we are navigating the Skylla and Charybdis of
+ // This is very tricky stuff, because we are navigating the Scylla and Charybdis of
// anonymous classes and what to return from them here. On the one hand, we cannot admit
// every non-private member of an anonymous class as a part of the structural type of the
// enclosing block. This runs afoul of the restriction that a structural type may not
@@ -2351,31 +2436,49 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
// The block is an anonymous class definitions/instantiation pair
// -> members that are hidden by the type of the block are made private
- val toHide = (
- classDecls filter (member =>
- member.isTerm
- && member.isPossibleInRefinement
- && member.isPublic
- && !matchesVisibleMember(member)
- ) map (member => member
- resetFlag (PROTECTED | LOCAL)
- setFlag (PRIVATE | SYNTHETIC_PRIVATE)
- setPrivateWithin NoSymbol
- )
- )
- syntheticPrivates ++= toHide
+ classDecls foreach { toHide =>
+ if (toHide.isTerm
+ && toHide.isPossibleInRefinement
+ && toHide.isPublic
+ && !matchesVisibleMember(toHide)) {
+ (toHide
+ resetFlag (PROTECTED | LOCAL)
+ setFlag (PRIVATE | SYNTHETIC_PRIVATE)
+ setPrivateWithin NoSymbol)
+
+ syntheticPrivates += toHide
+ }
+ }
+
case _ =>
}
}
- val stats1 = if (isPastTyper) block.stats else
- block.stats.flatMap(stat => stat match {
- case vd@ValDef(_, _, _, _) if vd.symbol.isLazy =>
- namer.addDerivedTrees(Typer.this, vd)
- case _ => stat::Nil
- })
- val stats2 = typedStats(stats1, context.owner)
+ val statsTyped = typedStats(block.stats, context.owner, warnPure = false)
val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt)
- treeCopy.Block(block, stats2, expr1)
+
+ // sanity check block for unintended expr placement
+ if (!isPastTyper) {
+ val (count, result0, adapted) =
+ expr1 match {
+ case Block(expr :: Nil, Literal(Constant(()))) => (1, expr, true)
+ case Literal(Constant(())) => (0, EmptyTree, false)
+ case _ => (1, EmptyTree, false)
+ }
+ def checkPure(t: Tree, supple: Boolean): Unit =
+ if (treeInfo.isPureExprForWarningPurposes(t)) {
+ val msg = "a pure expression does nothing in statement position"
+ val parens = if (statsTyped.length + count > 1) "multiline expressions might require enclosing parentheses" else ""
+ val discard = if (adapted) "; a value can be silently discarded when Unit is expected" else ""
+ val text =
+ if (supple) s"${parens}${discard}"
+ else if (!parens.isEmpty) s"${msg}; ${parens}" else msg
+ context.warning(t.pos, text)
+ }
+ statsTyped.foreach(checkPure(_, supple = false))
+ if (result0.nonEmpty) checkPure(result0, supple = true)
+ }
+
+ treeCopy.Block(block, statsTyped, expr1)
.setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst)
} finally {
// enable escaping privates checking from the outside and recycle
@@ -2464,7 +2567,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// TODO: add fallback __match sentinel to predef
val matchStrategy: Tree =
- if (!(settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen
+ if (!(settings.Yvirtpatmat && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen
else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match)), reportAmbiguousErrors = false) orElse (_ => null)
if (matchStrategy ne null) // virtualize
@@ -2576,8 +2679,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
@@ -2705,187 +2808,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
- })
-
- // 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 tvars = tparams map freshVar
- // 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)
+ val ptVars = appliedType(samTyCon, tvars)
- // solve constraints tracked by tvars
- val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil))
+ // carry over info from pt
+ ptVars <:< pt
- debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams")
+ val samInfoWithTVars = ptVars.memberInfo(sam)
- // 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)
- )
+ // use function type subtyping, not method type subtyping (the latter is invariant in argument types)
+ fun.tpe <:< functionType(samInfoWithTVars.paramTypes, samInfoWithTVars.finalResultType)
- 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
- )
- )
+ val variances = tparams map varianceInType(sam.info)
- // 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.
@@ -2895,16 +2910,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:
@@ -2912,79 +2930,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 necessary 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
+ }
}
}
}
@@ -3012,99 +3052,124 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ => log("unhandled import: "+imp+" in "+unit); imp
}
- def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
+ def typedStats(stats: List[Tree], exprOwner: Symbol, warnPure: Boolean = true): List[Tree] = {
val inBlock = exprOwner == context.owner
def includesTargetPos(tree: Tree) =
tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos)
val localTarget = stats exists includesTargetPos
- def typedStat(stat: Tree): Tree = {
- if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat))
- OnlyDeclarationsError(stat)
- else
- stat match {
- case imp @ Import(_, _) =>
- imp.symbol.initialize
- if (!imp.symbol.isError) {
- context = context.make(imp)
- typedImport(imp)
- } else EmptyTree
- case _ =>
- if (localTarget && !includesTargetPos(stat)) {
- // skip typechecking of statements in a sequence where some other statement includes
- // the targetposition
- stat
- } else {
- val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) {
- this
- } else newTyper(context.make(stat, exprOwner))
- // XXX this creates a spurious dead code warning if an exception is thrown
- // in a constructor, even if it is the only thing in the constructor.
- val result = checkDead(localTyper.typedByValueExpr(stat))
-
- if (treeInfo.isSelfOrSuperConstrCall(result)) {
- context.inConstructorSuffix = true
- if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0))
- ConstructorsOrderError(stat)
- }
-
- if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
- "a pure expression does nothing in statement position; " +
- "you may be omitting necessary parentheses"
- )
- result
- }
+ def typedStat(stat: Tree): Tree = stat match {
+ case s if context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(s) => OnlyDeclarationsError(s)
+ case imp @ Import(_, _) =>
+ imp.symbol.initialize
+ if (!imp.symbol.isError) {
+ context = context.make(imp)
+ typedImport(imp)
+ } else EmptyTree
+ // skip typechecking of statements in a sequence where some other statement includes the targetposition
+ case s if localTarget && !includesTargetPos(s) => s
+ case _ =>
+ val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this
+ else newTyper(context.make(stat, exprOwner))
+ // XXX this creates a spurious dead code warning if an exception is thrown
+ // in a constructor, even if it is the only thing in the constructor.
+ val result = checkDead(localTyper.typedByValueExpr(stat))
+
+ if (treeInfo.isSelfOrSuperConstrCall(result)) {
+ context.inConstructorSuffix = true
+ if (treeInfo.isSelfConstrCall(result)) {
+ if (result.symbol == exprOwner.enclMethod)
+ ConstructorRecursesError(stat)
+ else if (result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0))
+ ConstructorsOrderError(stat)
+ }
+ }
+ if (warnPure && !isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) {
+ val msg = "a pure expression does nothing in statement position"
+ val clause = if (stats.lengthCompare(1) > 0) "; multiline expressions may require enclosing parentheses" else ""
+ context.warning(stat.pos, s"${msg}${clause}")
}
+ result
}
- /* 'accessor' and 'accessed' are so similar it becomes very difficult to
- * follow the logic, so I renamed one to something distinct.
- */
+ // TODO: adapt to new trait field encoding, figure out why this exemption is made
+ // 'accessor' and 'accessed' are so similar it becomes very difficult to
+ //follow the logic, so I renamed one to something distinct.
def accesses(looker: Symbol, accessed: Symbol) = accessed.isLocalToThis && (
- (accessed.isParamAccessor)
- || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
- )
+ (accessed.isParamAccessor)
+ || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
+ )
- def checkNoDoubleDefs: Unit = {
- val scope = if (inBlock) context.scope else context.owner.info.decls
+ def checkNoDoubleDefs(scope: Scope): Unit = {
var e = scope.elems
while ((e ne null) && e.owner == scope) {
var e1 = scope.lookupNextEntry(e)
while ((e1 ne null) && e1.owner == scope) {
- if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) &&
- (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe)))
- // default getters are defined twice when multiple overloads have defaults. an
- // error for this is issued in RefChecks.checkDefaultsInOverloaded
- if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefault &&
- !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) {
- log("Double definition detected:\n " +
- ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " +
- ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain)))
-
- DefDefinedTwiceError(e.sym, e1.sym)
- scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
- }
- e1 = scope.lookupNextEntry(e1)
+ val sym = e.sym
+ val sym1 = e1.sym
+
+ /** From the spec (refchecks checks other conditions regarding erasing to the same type and default arguments):
+ *
+ * A block expression [... its] statement sequence may not contain two definitions or
+ * declarations that bind the same name --> `inBlock`
+ *
+ * It is an error if a template directly defines two matching members.
+ *
+ * A member definition $M$ _matches_ a member definition $M'$, if $M$ and $M'$ bind the same name,
+ * and one of following holds:
+ * 1. Neither $M$ nor $M'$ is a method definition.
+ * 2. $M$ and $M'$ define both monomorphic methods with equivalent argument types.
+ * 3. $M$ defines a parameterless method and $M'$ defines a method with an empty parameter list `()` or _vice versa_.
+ * 4. $M$ and $M'$ define both polymorphic methods with equal number of argument types $\overline T$, $\overline T'$
+ * and equal numbers of type parameters $\overline t$, $\overline t'$, say,
+ * and $\overline T' = [\overline t'/\overline t]\overline T$.
+ */
+ if (!(accesses(sym, sym1) || accesses(sym1, sym)) // TODO: does this purely defer errors until later?
+ && (inBlock || !(sym.isMethod || sym1.isMethod) || (sym.tpe matches sym1.tpe))
+ // default getters are defined twice when multiple overloads have defaults.
+ // The error for this is deferred until RefChecks.checkDefaultsInOverloaded
+ && (!sym.isErroneous && !sym1.isErroneous && !sym.hasDefault &&
+ !sym.hasAnnotation(BridgeClass) && !sym1.hasAnnotation(BridgeClass))) {
+ log("Double definition detected:\n " +
+ ((sym.getClass, sym.info, sym.ownerChain)) + "\n " +
+ ((sym1.getClass, sym1.info, sym1.ownerChain)))
+
+ DefDefinedTwiceError(sym, sym1)
+ scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
+ }
+ e1 = scope.lookupNextEntry(e1)
}
e = e.next
}
}
- def addSynthetics(stats: List[Tree]): List[Tree] = {
- val scope = if (inBlock) context.scope else context.owner.info.decls
+ def addSynthetics(stats: List[Tree], scope: Scope): List[Tree] = {
var newStats = new ListBuffer[Tree]
var moreToAdd = true
- val retractErroneousSynthetics = settings.isScala212
-
while (moreToAdd) {
val initElems = scope.elems
// SI-5877 The decls of a package include decls of the package object. But we don't want to add
// the corresponding synthetics to the package class, only to the package object class.
- def shouldAdd(sym: Symbol) =
- inBlock || !context.isInPackageObject(sym, context.owner)
+ // SI-6734 Locality test below is meaningless if we're not even in the correct tree.
+ // For modules that are synthetic case companions, check that case class is defined here.
+ def shouldAdd(sym: Symbol): Boolean = {
+ def shouldAddAsModule: Boolean =
+ sym.moduleClass.attachments.get[ClassForCaseCompanionAttachment] match {
+ case Some(att) =>
+ val cdef = att.caseClass
+ stats.exists {
+ case t @ ClassDef(_, _, _, _) => t.symbol == cdef.symbol // cdef ne t
+ case _ => false
+ }
+ case _ => true
+ }
+
+ (!sym.isModule || shouldAddAsModule) && (inBlock || !context.isInPackageObject(sym, context.owner))
+ }
for (sym <- scope)
- for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
+ // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
+ for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) {
// if the completer set the IS_ERROR flag, retract the stat (currently only used by applyUnapplyMethodCompleter)
- if (!(retractErroneousSynthetics && sym.initialize.hasFlag(IS_ERROR)))
+ if (!sym.initialize.hasFlag(IS_ERROR))
newStats += typedStat(tree) // might add even more synthetics to the scope
context.unit.synthetics -= sym
}
@@ -3132,6 +3197,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case (ClassDef(cmods, cname, _, _), DefDef(dmods, dname, _, _, _, _)) =>
cmods.isImplicit && dmods.isImplicit && cname.toTermName == dname
+ // ValDef and Accessor
+ case (ValDef(_, cname, _, _), DefDef(_, dname, _, _, _, _)) =>
+ cname.getterName == dname.getterName
+
case _ => false
}
@@ -3150,11 +3219,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val stats1 = stats mapConserve typedStat
if (phase.erasedTypes) stats1
else {
+ val scope = if (inBlock) context.scope else context.owner.info.decls
+
// 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)
+ checkNoDoubleDefs(scope)
+
+ addSynthetics(stats1, scope)
}
}
@@ -3218,6 +3290,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))
@@ -3254,40 +3329,74 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
val fun = preSelectOverloaded(fun0)
+ val argslen = args.length
fun.tpe match {
case OverloadedType(pre, alts) =>
def handleOverloaded = {
val undetparams = context.undetparams
+
+ def funArgTypes(tpAlts: List[(Type, Symbol)]) = tpAlts.map { case (tp, alt) =>
+ val relTp = tp.asSeenFrom(pre, alt.owner)
+ val argTps = functionOrSamArgTypes(relTp)
+ //println(s"funArgTypes $argTps from $relTp")
+ argTps.map(approximateAbstracts)
+ }
+
+ def functionProto(argTpWithAlt: List[(Type, Symbol)]): Type =
+ try functionType(funArgTypes(argTpWithAlt).transpose.map(lub), WildcardType)
+ catch { case _: IllegalArgumentException => WildcardType }
+
+ // To propagate as much information as possible to typedFunction, which uses the expected type to
+ // infer missing parameter types for Function trees that we're typing as arguments here,
+ // we expand the parameter types for all alternatives to the expected argument length,
+ // then transpose to get a list of alternative argument types (push down the overloading to the arguments).
+ // Thus, for each `arg` in `args`, the corresponding `argPts` in `altArgPts` is a list of expected types
+ // for `arg`. Depending on which overload is picked, only one of those expected types must be met, but
+ // we're in the process of figuring that out, so we'll approximate below by normalizing them to function types
+ // and lubbing the argument types (we treat SAM and FunctionN types equally, but non-function arguments
+ // do not receive special treatment: they are typed under WildcardType.)
+ val altArgPts =
+ if (settings.isScala212 && args.exists(treeInfo.isFunctionMissingParamType))
+ try alts.map(alt => formalTypes(alt.info.paramTypes, argslen).map(ft => (ft, alt))).transpose // do least amount of work up front
+ catch { case _: IllegalArgumentException => args.map(_ => Nil) } // fail safe in case formalTypes fails to align to argslen
+ else args.map(_ => Nil) // will type under argPt == WildcardType
+
val (args1, argTpes) = context.savingUndeterminedTypeParams() {
val amode = forArgMode(fun, mode)
- def typedArg0(tree: Tree) = typedArg(tree, amode, BYVALmode, WildcardType)
- args.map {
- case arg @ AssignOrNamedArg(Ident(name), rhs) =>
- // named args: only type the righthand sides ("unknown identifier" errors otherwise)
- // the assign is untyped; that's ok because we call doTypedApply
- val typedRhs = typedArg0(rhs)
- val argWithTypedRhs = treeCopy.AssignOrNamedArg(arg, arg.lhs, typedRhs)
-
- // TODO: SI-8197/SI-4592: check whether this named argument could be interpreted as an assign
+
+ map2(args, altArgPts) { (arg, argPtAlts) =>
+ def typedArg0(tree: Tree) = {
+ // if we have an overloaded HOF such as `(f: Int => Int)Int <and> (f: Char => Char)Char`,
+ // and we're typing a function like `x => x` for the argument, try to collapse
+ // the overloaded type into a single function type from which `typedFunction`
+ // can derive the argument type for `x` in the function literal above
+ val argPt =
+ if (argPtAlts.nonEmpty && treeInfo.isFunctionMissingParamType(tree)) functionProto(argPtAlts)
+ else WildcardType
+
+ val argTyped = typedArg(tree, amode, BYVALmode, argPt)
+ (argTyped, argTyped.tpe.deconst)
+ }
+
+ arg match {
+ // SI-8197/SI-4592 call for checking whether this named argument could be interpreted as an assign
// infer.checkNames must not use UnitType: it may not be a valid assignment, or the setter may return another type from Unit
- //
- // var typedAsAssign = true
- // val argTyped = silent(_.typedArg(argWithTypedRhs, amode, BYVALmode, WildcardType)) orElse { errors =>
- // typedAsAssign = false
- // argWithTypedRhs
- // }
- //
- // TODO: add an assignmentType field to NamedType, equal to:
- // assignmentType = if (typedAsAssign) argTyped.tpe else NoType
-
- (argWithTypedRhs, NamedType(name, typedRhs.tpe.deconst))
- case arg @ treeInfo.WildcardStarArg(repeated) =>
- val arg1 = typedArg0(arg)
- (arg1, RepeatedType(arg1.tpe.deconst))
- case arg =>
- val arg1 = typedArg0(arg)
- (arg1, arg1.tpe.deconst)
+ // TODO: just make it an error to refer to a non-existent named arg, as it's far more likely to be
+ // a typo than an assignment passed as an argument
+ case AssignOrNamedArg(lhs@Ident(name), rhs) =>
+ // named args: only type the righthand sides ("unknown identifier" errors otherwise)
+ // the assign is untyped; that's ok because we call doTypedApply
+ typedArg0(rhs) match {
+ case (rhsTyped, tp) => (treeCopy.AssignOrNamedArg(arg, lhs, rhsTyped), NamedType(name, tp))
+ }
+ case treeInfo.WildcardStarArg(_) =>
+ typedArg0(arg) match {
+ case (argTyped, tp) => (argTyped, RepeatedType(tp))
+ }
+ case _ =>
+ typedArg0(arg)
+ }
}.unzip
}
if (context.reporter.hasErrors)
@@ -3308,7 +3417,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// governed by a) the argument types and b) the expected type
val args1 = typedArgs(args, forArgMode(fun, mode))
val pts = args1.map(_.tpe.deconst)
- val clone = fun.symbol.cloneSymbol
+ val clone = fun.symbol.cloneSymbol.withoutAnnotations
val cloneParams = pts map (pt => clone.newValueParameter(currentUnit.freshTermName()).setInfo(pt))
val resultType = if (isFullyDefined(pt)) pt else ObjectTpe
clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType))
@@ -3318,32 +3427,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case mt @ MethodType(params, _) =>
val paramTypes = mt.paramTypes
// repeat vararg as often as needed, remove by-name
- val argslen = args.length
val formals = formalTypes(paramTypes, argslen)
- /* Try packing all arguments into a Tuple and apply `fun`
- * to that. This is the last thing which is tried (after
- * default arguments)
+ /* Try packing all arguments into a Tuple and apply `fun` to that.
+ * This is the last thing which is tried (after default arguments).
*/
- def tryTupleApply: Tree = {
- if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) {
+ def tryTupleApply: Tree =
+ if (phase.erasedTypes || !eligibleForTupleConversion(paramTypes, argslen)) EmptyTree
+ else {
val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args)))
// expected one argument, but got 0 or >1 ==> try applying to tuple
// the inner "doTypedApply" does "extractUndetparams" => restore when it fails
val savedUndetparams = context.undetparams
- silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t =>
- // Depending on user options, may warn or error here if
- // a Unit or tuple was inserted.
- val keepTree = (
- !mode.typingExprNotFun // why? introduced in 4e488a60, doc welcome
- || t.symbol == null // ditto
- || checkValidAdaptation(t, args)
- )
- if (keepTree) t else EmptyTree
- } orElse { _ => context.undetparams = savedUndetparams ; EmptyTree }
+ // May warn or error if a Unit or tuple was inserted.
+ def validate(t: Tree): Tree = {
+ // regardless of typer's mode
+ val invalidAdaptation = t.symbol != null && !checkValidAdaptation(t, args)
+ // only bail if we're typing an expression (and not inside another application)
+ if (invalidAdaptation && mode.typingExprNotFun) EmptyTree else t
+ }
+ def reset(errors: Seq[AbsTypeError]): Tree = {
+ context.undetparams = savedUndetparams
+ EmptyTree
+ }
+ silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)).map(validate).orElse(reset)
}
- else EmptyTree
- }
/* Treats an application which uses named or default arguments.
* Also works if names + a vararg used: when names are used, the vararg
@@ -3363,7 +3471,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// #2064
duplErrorTree(WrongNumberOfArgsError(tree, fun))
} else if (lencmp > 0) {
- tryTupleApply orElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun))
+ tryTupleApply orElse duplErrorTree {
+ val (namelessArgs, argPos) = removeNames(Typer.this)(args, params)
+ TooManyArgsNamesDefaultsError(tree, fun, formals, args, namelessArgs, argPos)
+ }
} else if (lencmp == 0) {
// we don't need defaults. names were used, so this application is transformed
// into a block (@see transformNamedApplication in NamesDefaults)
@@ -3427,7 +3538,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val lencmp2 = compareLengths(allArgs, formals)
if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) {
- duplErrorTree(ModuleUsingCompanionClassDefaultArgsErrror(tree))
+ duplErrorTree(ModuleUsingCompanionClassDefaultArgsError(tree))
} else if (lencmp2 > 0) {
removeNames(Typer.this)(allArgs, params) // #3818
duplErrTree
@@ -3663,7 +3774,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
@@ -3672,10 +3783,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
reportAnnotationError(MultipleArgumentListForAnnotationError(ann))
}
else {
- val annScope = annType.decls
- .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
+ val annScopeJava =
+ if (isJava) annType.decls.filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
+ else EmptyScope // annScopeJava is only used if isJava
+
val names = mutable.Set[Symbol]()
- names ++= (if (isJava) annScope.iterator
+ names ++= (if (isJava) annScopeJava.iterator
else typedFun.tpe.params.iterator)
def hasValue = names exists (_.name == nme.value)
@@ -3686,7 +3799,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val nvPairs = args map {
case arg @ AssignOrNamedArg(Ident(name), rhs) =>
- val sym = if (isJava) annScope.lookup(name)
+ val sym = if (isJava) annScopeJava.lookup(name)
else findSymbol(typedFun.tpe.params)(_.name == name)
if (sym == NoSymbol) {
reportAnnotationError(UnknownAnnotationNameError(arg, name))
@@ -3742,7 +3855,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2)
- context.deprecationWarning(ann.pos, DeprecatedAttr, "@deprecated now takes two arguments; see the scaladoc.")
+ context.deprecationWarning(ann.pos, DeprecatedAttr, "@deprecated now takes two arguments; see the scaladoc.", "2.11.0")
if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation
else annInfo(typedAnn)
@@ -4140,6 +4253,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
}
}
@@ -4148,7 +4269,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val name = tree.name
val body = tree.body
name match {
- case name: TypeName => assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass)
+ case name: TypeName =>
+ assert(body == EmptyTree, s"${context.unit} typedBind: ${name.debugString} ${body} ${body.getClass}")
val sym =
if (tree.symbol != NoSymbol) tree.symbol
else {
@@ -4231,7 +4353,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// if (varsym.isVariable ||
// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?!
// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) {
- if (varsym.isVariable || varsym.isValue && phase.erasedTypes) {
+ if (varsym.isVariable || varsym.isValue && phase.assignsFields) {
val rhs1 = typedByValueExpr(rhs, lhs1.tpe)
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe
}
@@ -4291,7 +4413,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),
@@ -4366,8 +4489,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def narrowRhs(tp: Type) = { val sym = context.tree.symbol
context.tree match {
case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol =>
- val sym1 = if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner)
- else sym.lazyAccessorOrSelf
+ val sym1 =
+ if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner)
+ else sym
val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix
intersectionType(List(tp, singleType(pre, sym1)))
case _ => tp
@@ -4391,31 +4515,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, original: 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)), original)
+
+ 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]] = {
@@ -4459,7 +4595,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)
@@ -4478,7 +4614,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
@@ -4534,19 +4670,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val qual1 = typedQualifier(qual)
if (treeInfo.isVariableOrGetter(qual1)) {
if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart)
- val erred = qual1.isErroneous || args.exists(_.isErroneous)
+ val erred = qual1.exists(_.isErroneous) || args.exists(_.isErroneous)
if (erred) reportError(error) else {
val convo = convertToAssignment(fun, qual1, name, args)
silent(op = _.typed1(convo, mode, pt)) match {
case SilentResultValue(t) => t
- case err: SilentTypeError => reportError(SilentTypeError(advice1(convo, error.errors, err), error.warnings))
+ case err: SilentTypeError => reportError(
+ SilentTypeError(advice1(convo, error.errors, err), error.warnings)
+ )
}
}
- }
- else {
+ } else {
if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart)
val Apply(Select(qual2, _), args2) = tree
- val erred = qual2.isErroneous || args2.exists(_.isErroneous)
+ val erred = qual2.exists(_.isErroneous) || args2.exists(_.isErroneous)
reportError {
if (erred) error else SilentTypeError(advice2(error.errors), error.warnings)
}
@@ -4667,10 +4804,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
@@ -4702,6 +4839,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree
}
+
+ // For Java, instance and static members are in the same scope, but we put the static ones in the companion object
+ // so, when we can't find a member in the class scope, check the companion
+ def inCompanionForJavaStatic(pre: Type, cls: Symbol, name: Name): Symbol =
+ if (!(context.unit.isJava && cls.isClass && !cls.isModuleClass)) NoSymbol else {
+ val companion = companionSymbolOf(cls, context)
+ if (!companion.exists) NoSymbol
+ else member(gen.mkAttributedRef(pre, companion), name) // assert(res.isStatic, s"inCompanionForJavaStatic($pre, $cls, $name) = $res ${res.debugFlagString}")
+ }
+
/* Attribute a selection where `tree` is `qual.name`.
* `qual` is already attributed.
*/
@@ -4728,12 +4875,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
dyna.wrapErrors(t, (_.typed1(t, mode, pt)))
}
- val sym = tree.symbol orElse member(qual, name) orElse {
+ val sym = tree.symbol orElse member(qual, name) orElse inCompanionForJavaStatic(qual.tpe.prefix, qual.symbol, name) orElse {
// symbol not found? --> try to convert implicitly to a type that does have the required
// 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)
}
@@ -4820,16 +4967,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- // temporarily use `filter` as an alternative for `withFilter`
- def tryWithFilterAndFilter(tree: Select, qual: Tree): Tree = {
- def warn(sym: Symbol) = context.deprecationWarning(tree.pos, sym, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead")
- silent(_ => typedSelect(tree, qual, nme.withFilter)) orElse { _ =>
- silent(_ => typed1(Select(qual, nme.filter) setPos tree.pos, mode, pt)) match {
- case SilentResultValue(res) => warn(res.symbol) ; res
- case SilentTypeError(err) => WithFilterError(tree, err)
- }
- }
- }
def typedSelectOrSuperCall(tree: Select) = tree match {
case Select(qual @ Super(_, _), nme.CONSTRUCTOR) =>
// the qualifier type of a supercall constructor is its first parent class
@@ -4843,10 +4980,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else
UnstableTreeError(qualTyped)
)
- val tree1 = name match {
- case nme.withFilter if !settings.future => tryWithFilterAndFilter(tree, qualStableOrError)
- case _ => typedSelect(tree, qualStableOrError, name)
- }
+ val tree1 = typedSelect(tree, qualStableOrError, name)
def sym = tree1.symbol
if (tree.isInstanceOf[PostfixSelect])
checkFeature(tree.pos, PostfixOpsFeature, name.decode)
@@ -5127,11 +5261,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), tree)
}
case Typed(expr, tpt) =>
val tpt1 = typedType(tpt, mode) // type the ascribed type first
@@ -5222,17 +5356,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 {
@@ -5256,7 +5396,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)
}
@@ -5444,6 +5584,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
}
@@ -5535,10 +5678,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 */
@@ -5613,7 +5753,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
def reportWarning(inferredType: Type) = {
val explanation = s"inference of $inferredType from macro impl's c.Expr[$inferredType] is deprecated and is going to stop working in 2.12"
- context.deprecationWarning(ddef.pos, ddef.symbol, s"$commonMessage ($explanation)")
+ context.deprecationWarning(ddef.pos, ddef.symbol, s"$commonMessage ($explanation)", "2.12.0")
}
computeMacroDefTypeFromMacroImplRef(ddef, rhs1) match {
case ErrorType => ErrorType