summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala5
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala9
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala324
6 files changed, 190 insertions, 153 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 5cb43575b8..96146b7343 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -253,6 +253,11 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
}
}
+ // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any
+ // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree
+ def mkCastPreservingAnnotations(tree: Tree, pt: Type) =
+ Typed(mkCast(tree, pt.withoutAnnotations.dealias), TypeTree(pt))
+
/** Generate a cast for tree Tree representing Array with
* elem type elemtp to expected type pt.
*/
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
index ff73745972..1323a06c01 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
@@ -339,7 +339,7 @@ function configureTextFilter() {
printAlphabet();
var input = $("#textfilter input");
resizeFilterBlock();
- input.bind("keydown", function(event) {
+ input.bind('keyup', function(event) {
if (event.keyCode == 27) { // escape
input.attr("value", "");
}
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index cfc7f14210..517b91dca8 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -174,6 +174,7 @@ trait ScalaSettings extends AbsScalaSettings
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "")
val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
+ val companionsInPkgObjs = BooleanSetting("-Ycompanions-in-pkg-objs", "Allow companion objects and case classes in package objects. See issue SI-5954.")
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 10003723fd..ec3a0a0ef7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -956,7 +956,7 @@ trait Implicits {
infoMap get sym match {
case Some(infos1) =>
if (infos1.nonEmpty && !(pre =:= infos1.head.pre.prefix)) {
- println("amb prefix: "+pre+"#"+sym+" "+infos1.head.pre.prefix+"#"+sym)
+ log(s"Ignoring implicit members of $pre#$sym as it is also visible via another prefix: ${infos1.head.pre.prefix}")
infoMap(sym) = List() // ambiguous prefix - ignore implicit members
}
case None =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
index fa8aff5cdd..d73fb680f2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -1478,14 +1478,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya
def and(a: Tree, b: Tree): Tree = a AND b
- // drop annotations generated by CPS plugin etc, since its annotationchecker rejects T @cps[U] <: Any
- // let's assume for now annotations don't affect casts, drop them there, and bring them back using the outer Typed tree
- private def mkCast(t: Tree, tp: Type) =
- Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp)
-
// the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly)
- def _asInstanceOf(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp)
- def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp)
+ def _asInstanceOf(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else gen.mkCastPreservingAnnotations(t, tp)
+ def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else gen.mkCastPreservingAnnotations(REF(b), tp)
def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false)
// if (typesConform(b.info, tpX)) { patmatDebug("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 0ca1e01114..2720f8a0ef 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -996,6 +996,10 @@ trait Typers extends Modes with Adaptations with Tags {
object variantToSkolem extends VariantTypeMap {
def apply(tp: Type) = mapOver(tp) match {
case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm =>
+ // must initialize or tpSym.tpe might see random type params!!
+ // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala
+ // TODO: why is that??
+ tpSym.initialize
val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe)
// origin must be the type param so we can deskolemize
val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds)
@@ -1018,7 +1022,7 @@ trait Typers extends Modes with Adaptations with Tags {
newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe)
// simplify types without losing safety,
- // so that error messages don't unnecessarily refer to skolems
+ // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems
val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type)
val extrapolated = tree1.tpe match {
case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node
@@ -1891,7 +1895,33 @@ trait Typers extends Modes with Adaptations with Tags {
})
}
val impl2 = finishMethodSynthesis(impl1, clazz, context)
+
+ // SI-5954. On second compile of a companion class contained in a package object we end up
+ // with some confusion of names which leads to having two symbols with the same name in the
+ // same owner. Until that can be straightened out we can't allow companion objects in package
+ // objects. But this code also tries to be friendly by distinguishing between case classes and
+ // user written companion pairs
+ def restrictPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) {
+ // ignore synthetic objects, because the "companion" object to a case class is synthetic and
+ // we only want one error per case class
+ if (!m.isSynthetic) {
+ // can't handle case classes in package objects
+ if (m.isCaseClass) pkgObjectRestriction(m, mdef, "case")
+ // can't handle companion class/object pairs in package objects
+ else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) ||
+ (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic))
+ pkgObjectRestriction(m, mdef, "companion")
+ }
+
+ def pkgObjectRestriction(m : Symbol, mdef : ModuleDef, restricted : String) = {
+ val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString
+ context.error(if (m.pos.isDefined) m.pos else mdef.pos, s"implementation restriction: package object ${pkgName} cannot contain ${restricted} ${m}. Instead, ${m} should be placed directly in package ${pkgName}.")
+ }
+ }
+ if (!settings.companionsInPkgObjs.value && mdef.symbol.isPackageObject)
+ restrictPackageObjectMembers(mdef)
+
treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
}
/** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
@@ -2526,11 +2556,7 @@ trait Typers extends Modes with Adaptations with Tags {
}
// body1 = checkNoEscaping.locals(context.scope, pt, body1)
- val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
-
- new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(treeWithSkolems)
-
- treeWithSkolems // now without skolems, actually
+ treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
}
// undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher
@@ -2565,7 +2591,10 @@ trait Typers extends Modes with Adaptations with Tags {
val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp))
- treeCopy.Match(tree, selector1, casesAdapted) setType resTp
+ val matchTyped = treeCopy.Match(tree, selector1, casesAdapted) setType resTp
+ if (!newPatternMatching) // TODO: remove this in 2.11 -- only needed for old pattern matcher
+ new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(matchTyped)
+ matchTyped
}
// match has been typed -- virtualize it if we're feeling experimental
@@ -2588,174 +2617,177 @@ trait Typers extends Modes with Adaptations with Tags {
match_ // will be translated in phase `patmat`
}
- // synthesize and type check a PartialFunction implementation based on a match specified by `cases`
- // Match(EmptyTree, cases) ==> new PartialFunction { def apply<OrElse>(params) = `translateMatch('`(param1,...,paramN)` match { cases }')` }
- // for fresh params, the selector of the match we'll translated simply gathers those in a tuple
- // NOTE: restricted to PartialFunction -- leave Function trees if the expected type does not demand a partial function
- class MatchFunTyper(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type) {
- // TODO: remove FunctionN support -- this is currently designed so that it can emit FunctionN and PartialFunction subclasses
- // however, we should leave Function nodes until Uncurry so phases after typer can still detect normal Function trees
- // we need to synthesize PartialFunction impls, though, to avoid nastiness in Uncurry in transforming&duplicating generated pattern matcher trees
- // TODO: remove PartialFunction support from UnCurry
- private val pt = deskolemizeGADTSkolems(pt0)
- private val targs = pt.normalize.typeArgs
- private val arity = if (isFunctionType(pt)) targs.length - 1 else 1 // TODO pt should always be a (Partial)Function, right?
- private val ptRes = if (targs.isEmpty) WildcardType else targs.last // may not be fully defined
-
- private val isPartial = pt.typeSymbol == PartialFunctionClass
- assert(isPartial)
-
- private val anonClass = context.owner.newAnonymousFunctionClass(tree.pos)
- private val funThis = This(anonClass)
-
- anonClass addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
-
- def deriveFormals =
- if (targs.isEmpty) Nil
- else targs.init
-
- def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) =
- if (formals.isEmpty || !formals.forall(isFullyDefined)) { MissingParameterTypeAnonMatchError(tree, pt); Nil }
- else methodSym newSyntheticValueParams formals
-
- def mkSel(params: List[Symbol]) =
- if (params.isEmpty) EmptyTree
- else {
- val ids = params map (p => Ident(p.name))
- atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
- }
-
- import CODE._
+ /** synthesize and type check a PartialFunction implementation based on the match in `tree`
+ *
+ * `param => sel match { cases }` becomes:
+ *
+ * new AbstractPartialFunction[$argTp, $matchResTp] {
+ * def applyOrElse[A1 <: $argTp, B1 >: $matchResTp]($param: A1, default: A1 => B1): B1 =
+ * $selector match { $cases }
+ * def isDefinedAt(x: $argTp): Boolean =
+ * $selector match { $casesTrue }
+ * }
+ *
+ * TODO: it would be nicer to generate the tree specified above at once and type it as a whole,
+ * there are two gotchas:
+ * - matchResTp may not be known until we've typed the match (can only use resTp when it's fully defined),
+ * - if we typed the match in isolation first, you'd know its result type, but would have to re-jig the owner structure
+ * - could we use a type variable for matchResTp and backpatch it?
+ * - occurrences of `this` in `cases` or `sel` must resolve to the this of the class originally enclosing the match,
+ * not of the anonymous partial function subclass
+ *
+ * an alternative TODO: add partial function AST node or equivalent and get rid of this synthesis --> do everything in uncurry (or later)
+ * however, note that pattern matching codegen is designed to run *before* uncurry
+ */
+ def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Int, pt0: Type): Tree = {
+ assert(pt0.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt0.")
- // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up
- val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE_typed)).duplicate.asInstanceOf[CaseDef]) else Nil
- // println("casesTrue "+ casesTrue)
- def parentsPartial(targs: List[Type]) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, targs))
+ val pt = deskolemizeGADTSkolems(pt0)
+ val targs = pt.normalize.typeArgs
- def applyMethod = {
- // rig the show so we can get started typing the method body -- later we'll correct the infos...
- anonClass setInfo ClassInfoType(addSerializable(ObjectClass.tpe, pt), newScope, anonClass)
- val methodSym = anonClass.newMethod(nme.apply, tree.pos, if(isPartial) (FINAL | OVERRIDE) else FINAL)
- val paramSyms = mkParams(methodSym)
- val selector = mkSel(paramSyms)
+ // if targs.head isn't fully defined, we can translate --> error
+ targs match {
+ case argTp :: _ if isFullyDefined(argTp) => // ok
+ case _ => // uh-oh
+ MissingParameterTypeAnonMatchError(tree, pt)
+ return setError(tree)
+ }
- if (selector eq EmptyTree) EmptyTree
- else {
- methodSym setInfoAndEnter MethodType(paramSyms, AnyClass.tpe)
+ // NOTE: resTp still might not be fully defined
+ val argTp :: resTp :: Nil = targs
- val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
- paramSyms foreach (methodBodyTyper.context.scope enter _)
+ // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs)
+ val targsValidParams = targs forall (_ <:< AnyClass.tpe)
- val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes)
- val resTp = match_.tpe
+ val anonClass = (context.owner
+ newAnonymousFunctionClass tree.pos
+ addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()))
- val methFormals = paramSyms map (_.tpe)
- val parents = (
- if (isPartial) parentsPartial(List(methFormals.head, resTp))
- else addSerializable(abstractFunctionType(methFormals, resTp))
- )
- anonClass setInfo ClassInfoType(parents, newScope, anonClass)
- methodSym setInfoAndEnter MethodType(paramSyms, resTp)
+ import CODE._
- DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, resTp))
- }
- }
+ val Match(sel, cases) = tree
- // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 =
+ // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up
+ val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE_typed)).duplicate.asInstanceOf[CaseDef])
+
+ // must generate a new tree every time
+ def selector: Tree = gen.mkUnchecked(
+ if (sel != EmptyTree) sel.duplicate
+ else atPos(tree.pos.focusStart)(
+ // SI-6925: subsume type of the selector to `argTp`
+ // we don't want/need the match to see the `A1` type that we must use for variance reasons in the method signature
+ //
+ // this failed: replace `selector` by `Typed(selector, TypeTree(argTp))` -- as it's an upcast, this should never fail,
+ // `(x: A1): A` doesn't always type check, even though `A1 <: A`, due to singleton types (test/files/pos/t4269.scala)
+ // hence the cast, which will be erased in posterasure
+ // (the cast originally caused extremely weird types to show up
+ // in test/scaladoc/run/SI-5933.scala because `variantToSkolem` was missing `tpSym.initialize`)
+ gen.mkCastPreservingAnnotations(Ident(paramName), argTp)
+ ))
+
+ def mkParam(methodSym: Symbol, tp: Type = argTp) =
+ methodSym.newValueParameter(paramName, paramPos.focus, SYNTHETIC) setInfo tp
+
+ // `def applyOrElse[A1 <: $argTp, B1 >: $matchResTp](x: A1, default: A1 => B1): B1 =
+ // ${`$selector match { $cases }` updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))}`
def applyOrElseMethodDef = {
- // rig the show so we can get started typing the method body -- later we'll correct the infos...
- // targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well
- anonClass setInfo ClassInfoType(parentsPartial(targs), newScope, anonClass)
val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE)
// create the parameter that corresponds to the function's parameter
- val List(argTp) = deriveFormals
- val A1 = methodSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argTp)
- val paramSyms@List(x) = mkParams(methodSym, List(A1.tpe))
- val selector = mkSel(paramSyms)
+ val A1 = methodSym newTypeParameter (newTypeName("A1")) setInfo TypeBounds.upper(argTp)
+ val x = mkParam(methodSym, A1.tpe)
- if (selector eq EmptyTree) EmptyTree
- else {
- // applyOrElse's default parameter:
- val B1 = methodSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp)
- val default = methodSym newValueParameter(newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe)
+ // applyOrElse's default parameter:
+ val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp)
+ val default = methodSym newValueParameter (newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe)
- val paramSyms = List(x, default)
- methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe))
+ val paramSyms = List(x, default)
+ methodSym setInfo polyType(List(A1, B1), MethodType(paramSyms, B1.tpe))
- val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
- paramSyms foreach (methodBodyTyper.context.scope enter _)
+ val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym))
+ // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
+ paramSyms foreach (methodBodyTyper.context.scope enter _)
- val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), cases, mode, ptRes)
- val resTp = match_.tpe
+ val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp)
- anonClass setInfo ClassInfoType(parentsPartial(List(argTp, resTp)), newScope, anonClass)
- B1 setInfo TypeBounds.lower(resTp)
- anonClass.info.decls enter methodSym // methodSym's info need not change (B1's bound has been updated instead)
+ val matchResTp = match_.tpe
+ B1 setInfo TypeBounds.lower(matchResTp) // patch info
- match_ setType B1.tpe
+ match_ setType B1.tpe
- // the default uses applyOrElse's first parameter since the scrut's type has been widened
- val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))), mode, B1.tpe)
-
- DefDef(methodSym, body)
- }
+ // the default uses applyOrElse's first parameter since the scrut's type has been widened
+ val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))
+ (DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, B1.tpe)), matchResTp)
}
+ // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue ` updateAttachment DefaultOverrideMatchAttachment(FALSE_typed)}`
def isDefinedAtMethod = {
val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL)
- val paramSyms = mkParams(methodSym)
- val selector = mkSel(paramSyms)
+ val paramSym = mkParam(methodSym)
- if (selector eq EmptyTree) EmptyTree
- else {
- val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
- paramSyms foreach (methodBodyTyper.context.scope enter _)
- methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe)
+ val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
+ methodBodyTyper.context.scope enter paramSym
+ methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe)
- val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), casesTrue, mode, BooleanClass.tpe)
- val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed), mode, BooleanClass.tpe)
+ val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe)
- DefDef(methodSym, body)
- }
+ val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed)
+ DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, BooleanClass.tpe))
}
- lazy val members = if (isPartial) {
- // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's
- // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?)
- if (targs forall (_ <:< AnyClass.tpe)) List(applyOrElseMethodDef, isDefinedAtMethod)
- else List(applyMethod, isDefinedAtMethod)
- } else List(applyMethod)
+ // only used for @cps annotated partial functions
+ // `def apply(x: $argTp): $matchResTp = $selector match { $cases }`
+ def applyMethod = {
+ val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL | OVERRIDE)
+ val paramSym = mkParam(methodSym)
- def translated =
- if (members.head eq EmptyTree) setError(tree)
- else {
- val typedBlock = typedPos(tree.pos, mode, pt) {
- Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe)))
- }
- // Don't leak implementation details into the type, see SI-6575
- if (isPartial && !typedBlock.isErrorTyped)
- typedPos(tree.pos, mode, pt) {
- Typed(typedBlock, TypeTree(typedBlock.tpe baseType PartialFunctionClass))
- }
- else typedBlock
- }
- }
+ methodSym setInfo MethodType(List(paramSym), AnyClass.tpe)
- // Function(params, Match(sel, cases)) ==> new <Partial>Function { def apply<OrElse>(params) = `translateMatch('sel match { cases }')` }
- class MatchFunTyperBetaReduced(fun: Function, sel: Tree, cases: List[CaseDef], mode: Int, pt: Type) extends MatchFunTyper(fun, cases, mode, pt) {
- override def deriveFormals =
- fun.vparams map { p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe }
+ val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym))
+ // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
+ methodBodyTyper.context.scope enter paramSym
- // the only difference from the super class is that we must preserve the names of the parameters
- override def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) =
- (fun.vparams, formals).zipped map { (p, tp) =>
- methodSym.newValueParameter(p.name, p.pos.focus, SYNTHETIC) setInfo tp
+ val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp)
+
+ val matchResTp = match_.tpe
+ methodSym setInfo MethodType(List(paramSym), matchResTp) // patch info
+
+ (DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, matchResTp)), matchResTp)
+ }
+
+ def parents(resTp: Type) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, List(argTp, resTp)))
+
+ val members = {
+ val (applyMeth, matchResTp) = {
+ // rig the show so we can get started typing the method body -- later we'll correct the infos...
+ // targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well
+ anonClass setInfo ClassInfoType(parents(resTp), newScope, anonClass)
+
+ // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's
+ // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?)
+ if (targsValidParams) applyOrElseMethodDef
+ else applyMethod
}
- override def mkSel(params: List[Symbol]) = sel.duplicate
+ // patch info to the class's definitive info
+ anonClass setInfo ClassInfoType(parents(matchResTp), newScope, anonClass)
+ List(applyMeth, isDefinedAtMethod)
+ }
+
+ members foreach (m => anonClass.info.decls enter m.symbol)
+
+ val typedBlock = typedPos(tree.pos, mode, pt) {
+ Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe)))
+ }
+
+ if (typedBlock.isErrorTyped) typedBlock
+ else // Don't leak implementation details into the type, see SI-6575
+ typedPos(tree.pos, mode, pt) {
+ Typed(typedBlock, TypeTree(typedBlock.tpe baseType PartialFunctionClass))
+ }
}
+
/**
* @param fun ...
* @param mode ...
@@ -2807,14 +2839,17 @@ trait Typers extends Modes with Adaptations with Tags {
}
fun.body match {
- // later phase indicates scaladoc is calling (where shit is messed up, I tell you)
- // -- so fall back to old patmat, which is more forgiving
+ // 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) && newPatternMatching && (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)
- (new outerTyper.MatchFunTyperBetaReduced(fun, sel, cases, mode, pt)).translated
+ val p = fun.vparams.head
+ if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe
+
+ outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt)
case _ =>
val vparamSyms = fun.vparams map { vparam =>
enterSym(context, vparam)
@@ -4350,7 +4385,8 @@ trait Typers extends Modes with Adaptations with Tags {
val selector = tree.selector
val cases = tree.cases
if (selector == EmptyTree) {
- if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) (new MatchFunTyper(tree, cases, mode, pt)).translated
+ if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass))
+ synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, tree, mode, pt)
else {
val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1
val params = for (i <- List.range(0, arity)) yield