diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala | 319 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 277 |
2 files changed, 322 insertions, 274 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala new file mode 100644 index 0000000000..f09c142aef --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -0,0 +1,319 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package tools +package nsc +package typechecker + +import scala.collection.mutable +import symtab.Flags +import Mode._ + + /** + * + * A pattern match such as + * + * x match { case Foo(a, b) => ...} + * + * Might match an instance of any of the following definitions of Foo. + * Note the analogous treatment between case classes and unapplies. + * + * case class Foo(xs: Int*) + * case class Foo(a: Int, xs: Int*) + * case class Foo(a: Int, b: Int) + * case class Foo(a: Int, b: Int, xs: Int*) + * + * object Foo { def unapplySeq(x: Any): Option[Seq[Int]] } + * object Foo { def unapplySeq(x: Any): Option[(Int, Seq[Int])] } + * object Foo { def unapply(x: Any): Option[(Int, Int)] } + * object Foo { def unapplySeq(x: Any): Option[(Int, Int, Seq[Int])] } + */ + +trait PatternTypers { + self: Analyzer => + + import global._ + import definitions._ + + // when true: + // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) + // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction + // this is disabled by: interactive compilation (we run it for scaladoc due to SI-5933) + protected def newPatternMatching = true // presently overridden in the presentation compiler + + trait PatternTyper { + self: Typer => + + import TyperErrorGen._ + import infer._ + + private def unit = context.unit + + /** Type trees in `args0` against corresponding expected type in `adapted0`. + * + * The mode in which each argument is typed is derived from `mode` and + * whether the arg was originally by-name or var-arg (need `formals0` for that) + * the default is by-val, of course. + * + * (docs reverse-engineered -- AM) + */ + def typedArgs(args0: List[Tree], mode: Mode, formals0: List[Type], adapted0: List[Type]): List[Tree] = { + def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { + if (args.isEmpty || adapted.isEmpty) Nil + else { + // No formals left or * indicates varargs. + val isVarArgs = formals.isEmpty || formals.tail.isEmpty && isRepeatedParamType(formals.head) + val isByName = formals.nonEmpty && isByNameParamType(formals.head) + def typedMode = if (isByName) mode.onlySticky else mode.onlySticky | BYVALmode + def body = typedArg(args.head, mode, typedMode, adapted.head) + def arg1 = if (isVarArgs) context.withinStarPatterns(body) else body + + // formals may be empty, so don't call tail + arg1 :: loop(args.tail, formals drop 1, adapted.tail) + } + } + loop(args0, formals0, adapted0) + } + + /* + * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, + * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). + * + * Consider the following example: + * + * class AbsWrapperCov[+A] + * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] + * + * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { + * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it + * wrapped // : Wrapped[_ <: T] + * } + * + * this method should type check if and only if Wrapped is covariant in its type parameter + * + * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], + * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} + * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound + * + * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), + * we can simply replace skolems that represent method type parameters as seen from the method's body + * by other skolems that are (upper/lower)-bounded by that type-parameter skolem + * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) + * + * see test/files/../t5189*.scala + */ + def adaptConstrPattern(tree: Tree, pt: Type): Tree = { // (5) + def hasUnapplyMember(tp: Type) = reallyExists(unapplyMember(tp)) + val overloadedExtractorOfObject = tree.symbol filter (sym => hasUnapplyMember(sym.tpe)) + // if the tree's symbol's type does not define an extractor, maybe the tree's type does. + // this is the case when we encounter an arbitrary tree as the target of an unapply call + // (rather than something that looks like a constructor call.) (for now, this only happens + // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become + // more common place) + val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) + def convertToCaseConstructor(clazz: Symbol): TypeTree = { + // convert synthetic unapply of case class to case class constructor + val prefix = tree.tpe.prefix + val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) + .setOriginal(tree) + + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends TypeMap(trackVariance = true) { + def apply(tp: Type) = mapOver(tp) match { + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && 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.isPositive) 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) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // simplify types without losing safety, + // 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 + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated + } + + if (extractor != NoSymbol) { + // if we did some ad-hoc overloading resolution, update the tree's symbol + // do not update the symbol if the tree's symbol's type does not define an unapply member + // (e.g. since it's some method that returns an object with an unapply member) + if (overloadedExtractorOfObject != NoSymbol) + tree setSymbol overloadedExtractorOfObject + + tree.tpe match { + case OverloadedType(pre, alts) => tree setType overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) + case _ => + } + val unapply = unapplyMember(extractor.tpe) + val clazz = unapplyParameterType(unapply) + + if (unapply.isCase && clazz.isCase) { + convertToCaseConstructor(clazz) + } else { + tree + } + } else { + val clazz = tree.tpe.typeSymbol.linkedClassOfClass + if (clazz.isCase) + convertToCaseConstructor(clazz) + else + CaseClassConstructorError(tree) + } + } + + def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { + def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) + def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } + + val otpe = fun.tpe + + if (args.length > MaxTupleArity) + return duplErrorTree(TooManyArgsPatternError(fun)) + + // + def freshArgType(tp: Type): (List[Symbol], Type) = tp match { + case MethodType(param :: _, _) => + (Nil, param.tpe) + case PolyType(tparams, restpe) => + createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) + // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) + case OverloadedType(_, _) => + OverloadedUnapplyError(fun) + (Nil, ErrorType) + case _ => + UnapplyWithSingleArgError(fun) + (Nil, ErrorType) + } + + val unapp = unapplyMember(otpe) + val unappType = otpe.memberType(unapp) + val argDummy = context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, Flags.SYNTHETIC) setInfo pt + val arg = Ident(argDummy) setType pt + + val uncheckedTypeExtractor = + if (unappType.paramTypes.nonEmpty) + extractorForUncheckedType(tree.pos, unappType.paramTypes.head) + else None + + if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { + //Console.println(s"UNAPP: need to typetest, arg: ${arg.tpe} unappType: $unappType") + val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) + val unapplyContext = context.makeNewScope(context.tree, context.owner) + freeVars foreach unapplyContext.scope.enter + + val typer1 = newTyper(unapplyContext) + val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe, canRemedy = uncheckedTypeExtractor.nonEmpty) + + // turn any unresolved type variables in freevars into existential skolems + val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) + arg setType pattp.substSym(freeVars, skolems) + argDummy setInfo arg.tpe + } + + // clearing the type is necessary so that ref will be stabilized; see bug 881 + val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapp), List(arg))) + + if (fun1.tpe.isErroneous) duplErrTree + else { + val resTp = fun1.tpe.finalResultType.dealiasWiden + val nbSubPats = args.length + val (formals, formalsExpanded) = + extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol, treeInfo.effectivePatternArity(args)) + if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) + else { + val args1 = typedArgs(args, mode, formals, formalsExpanded) + val pt1 = ensureFullyDefined(pt) // SI-1048 + val itype = glb(List(pt1, arg.tpe)) + arg setType pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) + val unapply = UnApply(fun1, args1) setPos tree.pos setType itype + + // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor + // skip if the unapply's type is not a method type with (at least, but really it should be exactly) one argument + // also skip if we already wrapped a classtag extractor (so we don't keep doing that forever) + if (uncheckedTypeExtractor.isEmpty || fun1.symbol.owner.isNonBottomSubClass(ClassTagClass)) unapply + else wrapClassTagUnapply(unapply, uncheckedTypeExtractor.get, unappType.paramTypes.head) + } + } + } + + def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { + // TODO: disable when in unchecked match + // we don't create a new Context for a Match, so find the CaseDef, then go out one level and navigate back to the match that has this case + // val thisCase = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) + // val unchecked = thisCase.outer.tree.collect{case Match(selector, cases) if cases contains thisCase => selector} match { + // case List(Typed(_, tpt)) if tpt.tpe hasAnnotation UncheckedClass => true + // case t => println("outer tree: "+ (t, thisCase, thisCase.outer.tree)); false + // } + // println("wrapClassTagUnapply"+ (!isPastTyper && infer.containsUnchecked(pt), pt, uncheckedPattern)) + // println("wrapClassTagUnapply: "+ extractor) + // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) + + val args = List(uncheckedPattern) + val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) + // must call doTypedUnapply directly, as otherwise we get undesirable rewrites + // and re-typechecks of the target of the unapply call in PATTERNmode, + // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, + // but an arbitrary tree as is the case here + doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + } + + // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test + // return the corresponding extractor (an instance of ClassTag[`pt`]) + def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (isPastTyper) None else { + // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) + // but at least make a proper type before passing it elsewhere + val pt1 = pt.dealiasWiden match { + case tr @ TypeRef(pre, sym, args) if args.nonEmpty => copyTypeRef(tr, pre, sym, sym.typeParams map (_.tpeHK)) // replace actual type args with dummies + case pt1 => pt1 + } + pt1 match { + // if at least one of the types in an intersection is checkable, use the checkable ones + // this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike` + // Coll is an abstract type, but SeqLike of course is not + case RefinedType(ps, _) if ps.length > 1 && (ps exists infer.isCheckable) => + None + + case ptCheckable if infer isUncheckable ptCheckable => + val classTagExtractor = resolveClassTag(pos, ptCheckable) + + if (classTagExtractor != EmptyTree && unapplyMember(classTagExtractor.tpe) != NoSymbol) + Some(classTagExtractor) + else None + + case _ => None + } + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index dd92657de8..522ad97036 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -26,7 +26,7 @@ import Mode._ * @author Martin Odersky * @version 1.0 */ -trait Typers extends Adaptations with Tags with TypersTracking { +trait Typers extends Adaptations with Tags with TypersTracking with PatternTypers { self: Analyzer => import global._ @@ -90,13 +90,7 @@ trait Typers extends Adaptations with Tags with TypersTracking { private final val InterpolatorCodeRegex = """\$\{.*?\}""".r private final val InterpolatorIdentRegex = """\$\w+""".r - // when true: - // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) - // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction - // this is disabled by: interactive compilation (we run it for scaladoc due to SI-5933) - protected def newPatternMatching = true // presently overridden in the presentation compiler - - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors with PatternTyper { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine, inGreen, inRed } import TyperErrorGen._ @@ -911,122 +905,6 @@ trait Typers extends Adaptations with Tags with TypersTracking { case _ => TypeTree(tree.tpe) setOriginal tree } } - - /* - * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, - * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). - * - * Consider the following example: - * - * class AbsWrapperCov[+A] - * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] - * - * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { - * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it - * wrapped // : Wrapped[_ <: T] - * } - * - * this method should type check if and only if Wrapped is covariant in its type parameter - * - * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], - * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} - * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound - * - * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), - * we can simply replace skolems that represent method type parameters as seen from the method's body - * by other skolems that are (upper/lower)-bounded by that type-parameter skolem - * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) - * - * see test/files/../t5189*.scala - */ - def adaptConstrPattern(): Tree = { // (5) - def hasUnapplyMember(tp: Type) = reallyExists(unapplyMember(tp)) - val overloadedExtractorOfObject = tree.symbol filter (sym => hasUnapplyMember(sym.tpe)) - // if the tree's symbol's type does not define an extractor, maybe the tree's type does. - // this is the case when we encounter an arbitrary tree as the target of an unapply call - // (rather than something that looks like a constructor call.) (for now, this only happens - // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become - // more common place) - val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) - def convertToCaseConstructor(clazz: Symbol): TypeTree = { - // convert synthetic unapply of case class to case class constructor - val prefix = tree.tpe.prefix - val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) - .setOriginal(tree) - - val skolems = new mutable.ListBuffer[TypeSymbol] - object variantToSkolem extends TypeMap(trackVariance = true) { - def apply(tp: Type) = mapOver(tp) match { - // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 - case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && 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.isPositive) 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) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) - skolems += skolem - skolem.tpe - case tp1 => tp1 - } - } - - // have to open up the existential and put the skolems in scope - // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) - val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? - val freeVars = skolems.toList - - // use "tree" for the context, not context.tree: don't make another CaseDef context, - // as instantiateTypeVar's bounds would end up there - val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter - newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - - // simplify types without losing safety, - // 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 - ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type - copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) - case tp => tp - } - - // once the containing CaseDef has been type checked (see typedCase), - // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) - tree1 setType extrapolated - } - - if (extractor != NoSymbol) { - // if we did some ad-hoc overloading resolution, update the tree's symbol - // do not update the symbol if the tree's symbol's type does not define an unapply member - // (e.g. since it's some method that returns an object with an unapply member) - if (overloadedExtractorOfObject != NoSymbol) - tree setSymbol overloadedExtractorOfObject - - tree.tpe match { - case OverloadedType(pre, alts) => tree setType overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) - case _ => - } - val unapply = unapplyMember(extractor.tpe) - val clazz = unapplyParameterType(unapply) - - if (unapply.isCase && clazz.isCase) { - convertToCaseConstructor(clazz) - } else { - tree - } - } else { - val clazz = tree.tpe.typeSymbol.linkedClassOfClass - if (clazz.isCase) - convertToCaseConstructor(clazz) - else - CaseClassConstructorError(tree) - } - } - def insertApply(): Tree = { assert(!context.inTypeConstructorAllowed, mode) //@M val adapted = adaptToName(tree, nme.apply) @@ -1213,7 +1091,7 @@ trait Typers extends Adaptations with Tags with TypersTracking { else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else if (mode.typingConstructorPattern) - adaptConstrPattern() + adaptConstrPattern(tree, pt) else if (shouldInsertApply(tree)) insertApply() else if (hasUndetsInMonoMode) { // (9) @@ -3026,32 +2904,6 @@ trait Typers extends Adaptations with Tags with TypersTracking { def typedArgs(args: List[Tree], mode: Mode) = args mapConserve (arg => typedArg(arg, mode, NOmode, WildcardType)) - /** Type trees in `args0` against corresponding expected type in `adapted0`. - * - * The mode in which each argument is typed is derived from `mode` and - * whether the arg was originally by-name or var-arg (need `formals0` for that) - * the default is by-val, of course. - * - * (docs reverse-engineered -- AM) - */ - def typedArgs(args0: List[Tree], mode: Mode, formals0: List[Type], adapted0: List[Type]): List[Tree] = { - def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { - if (args.isEmpty || adapted.isEmpty) Nil - else { - // No formals left or * indicates varargs. - val isVarArgs = formals.isEmpty || formals.tail.isEmpty && isRepeatedParamType(formals.head) - val isByName = formals.nonEmpty && isByNameParamType(formals.head) - def typedMode = if (isByName) mode.onlySticky else mode.onlySticky | BYVALmode - def body = typedArg(args.head, mode, typedMode, adapted.head) - def arg1 = if (isVarArgs) context.withinStarPatterns(body) else body - - // formals may be empty, so don't call tail - arg1 :: loop(args.tail, formals drop 1, adapted.tail) - } - } - loop(args0, formals0, adapted0) - } - /** Does function need to be instantiated, because a missing parameter * in an argument closure overlaps with an uninstantiated formal? */ @@ -3387,129 +3239,6 @@ trait Typers extends Adaptations with Tags with TypersTracking { } } - def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { - def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } - - val otpe = fun.tpe - - if (args.length > MaxTupleArity) - return duplErrorTree(TooManyArgsPatternError(fun)) - - // - def freshArgType(tp: Type): (List[Symbol], Type) = tp match { - case MethodType(param :: _, _) => - (Nil, param.tpe) - case PolyType(tparams, restpe) => - createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) - // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) - case OverloadedType(_, _) => - OverloadedUnapplyError(fun) - (Nil, ErrorType) - case _ => - UnapplyWithSingleArgError(fun) - (Nil, ErrorType) - } - - val unapp = unapplyMember(otpe) - val unappType = otpe.memberType(unapp) - val argDummy = context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, SYNTHETIC) setInfo pt - val arg = Ident(argDummy) setType pt - - val uncheckedTypeExtractor = - if (unappType.paramTypes.nonEmpty) - extractorForUncheckedType(tree.pos, unappType.paramTypes.head) - else None - - if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { - //Console.println(s"UNAPP: need to typetest, arg: ${arg.tpe} unappType: $unappType") - val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) - val unapplyContext = context.makeNewScope(context.tree, context.owner) - freeVars foreach unapplyContext.scope.enter - - val typer1 = newTyper(unapplyContext) - val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe, canRemedy = uncheckedTypeExtractor.nonEmpty) - - // turn any unresolved type variables in freevars into existential skolems - val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) - arg setType pattp.substSym(freeVars, skolems) - argDummy setInfo arg.tpe - } - - // clearing the type is necessary so that ref will be stabilized; see bug 881 - val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapp), List(arg))) - - if (fun1.tpe.isErroneous) duplErrTree - else { - val resTp = fun1.tpe.finalResultType.dealiasWiden - val nbSubPats = args.length - val (formals, formalsExpanded) = - extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol, treeInfo.effectivePatternArity(args)) - if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) - else { - val args1 = typedArgs(args, mode, formals, formalsExpanded) - val pt1 = ensureFullyDefined(pt) // SI-1048 - val itype = glb(List(pt1, arg.tpe)) - arg setType pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) - val unapply = UnApply(fun1, args1) setPos tree.pos setType itype - - // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor - // skip if the unapply's type is not a method type with (at least, but really it should be exactly) one argument - // also skip if we already wrapped a classtag extractor (so we don't keep doing that forever) - if (uncheckedTypeExtractor.isEmpty || fun1.symbol.owner.isNonBottomSubClass(ClassTagClass)) unapply - else wrapClassTagUnapply(unapply, uncheckedTypeExtractor.get, unappType.paramTypes.head) - } - } - } - - def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { - // TODO: disable when in unchecked match - // we don't create a new Context for a Match, so find the CaseDef, then go out one level and navigate back to the match that has this case - // val thisCase = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) - // val unchecked = thisCase.outer.tree.collect{case Match(selector, cases) if cases contains thisCase => selector} match { - // case List(Typed(_, tpt)) if tpt.tpe hasAnnotation UncheckedClass => true - // case t => println("outer tree: "+ (t, thisCase, thisCase.outer.tree)); false - // } - // println("wrapClassTagUnapply"+ (!isPastTyper && infer.containsUnchecked(pt), pt, uncheckedPattern)) - // println("wrapClassTagUnapply: "+ extractor) - // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) - - val args = List(uncheckedPattern) - val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) - // must call doTypedUnapply directly, as otherwise we get undesirable rewrites - // and re-typechecks of the target of the unapply call in PATTERNmode, - // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, - // but an arbitrary tree as is the case here - doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) - } - - // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test - // return the corresponding extractor (an instance of ClassTag[`pt`]) - def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (isPastTyper) None else { - // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) - // but at least make a proper type before passing it elsewhere - val pt1 = pt.dealiasWiden match { - case tr @ TypeRef(pre, sym, args) if args.nonEmpty => copyTypeRef(tr, pre, sym, sym.typeParams map (_.tpeHK)) // replace actual type args with dummies - case pt1 => pt1 - } - pt1 match { - // if at least one of the types in an intersection is checkable, use the checkable ones - // this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike` - // Coll is an abstract type, but SeqLike of course is not - case RefinedType(ps, _) if ps.length > 1 && (ps exists infer.isCheckable) => - None - - case ptCheckable if infer isUncheckable ptCheckable => - val classTagExtractor = resolveClassTag(pos, ptCheckable) - - if (classTagExtractor != EmptyTree && unapplyMember(classTagExtractor.tpe) != NoSymbol) - Some(classTagExtractor) - else None - - case _ => None - } - } - /** * Convert an annotation constructor call into an AnnotationInfo. */ |