From f41ccda10b54ab9c1a02b8a85505b639b0bd00b1 Mon Sep 17 00:00:00 2001 From: Burak Emir Date: Tue, 16 Jan 2007 12:03:38 +0000 Subject: some fixes for unapply: * deals with unapply arguments that are *not* subtypes of unapply * method, handles polymorphic applies getProductArgs instead of * isProduct in PatternMatcher code generation --- src/compiler/scala/tools/nsc/Settings.scala | 1 + .../scala/tools/nsc/matching/PatternMatchers.scala | 60 ++++++++++++---------- .../scala/tools/nsc/symtab/Definitions.scala | 8 +-- src/compiler/scala/tools/nsc/symtab/StdNames.scala | 1 + .../scala/tools/nsc/typechecker/Infer.scala | 3 +- .../scala/tools/nsc/typechecker/Typers.scala | 34 +++++++++--- 6 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index 90978e58b6..4ee1ec82d0 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -131,6 +131,7 @@ class Settings(error: String => unit) { val Xunapply = BooleanSetting("-Xunapply", "enable unapply pattern matching") val Xplugtypes = BooleanSetting("-Xplugtypes", "parse but ignore annotations in more locations") val Xkilloption = BooleanSetting("-Xkilloption", "optimizes option types") + val XprintOuterMatches = BooleanSetting("-XprintOuterMatches", "prints outer-checks caused by pattern matching ") /** A list of all settings */ def allSettings: List[Setting] = allsettings.reverse diff --git a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala index cda0e309f2..b4ec46bf14 100644 --- a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala +++ b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala @@ -541,9 +541,7 @@ trait PatternMatchers requires (transform.ExplicitOuter with PatternNodes) { } private def newHeader(pos: PositionType, casted: Symbol, index: Int): Header = { - //Console.println("newHeader(pos,"+casted+","+index+")"); - //Console.println(" casted.tpe"+casted.tpe); - //Console.println(" casted.pos "+casted.pos+" equals firstpos?"+(casted.pos == Position.FIRSTPOS)); + //Console.println("newHeader(pos,"+casted+" (has CASE flag? "+casted.tpe.symbol.hasFlag(Flags.CASE)+") of type "+casted.tpe+" with pos "+casted.pos+"(equals FIRSTPOS? "+(casted.pos == Position.FIRSTPOS)+"),"+index+")"); val ident = typed(Ident(casted)) if (casted.pos == Position.FIRSTPOS) { // load the result of casted(i) //Console.println("FIRSTPOS"); @@ -562,11 +560,13 @@ trait PatternMatchers requires (transform.ExplicitOuter with PatternNodes) { if(!casted.tpe.symbol.hasFlag(Flags.CASE)) { //Console.println("NOT CASE"); - - if (defs.isProductType(casted.tpe)) { - val acc = defs.productProj(casted.tpe.typeArgs.length, index+1) - val accTree = typed(Apply(Select(ident, acc), List())) // nsc ! - return pHeader(pos, accTree.tpe, accTree) + //Console.println("getProductArgs? "+defs.getProductArgs(casted.tpe)); + defs.getProductArgs(casted.tpe) match { + case Some(targs) => + val accSym = defs.productProj(casted.tpe.symbol, index+1) + val accTree = typed(Apply(Select(ident, accSym), List())) // nsc ! + return pHeader(pos, accTree.tpe, accTree) + case None => } /* @@ -1264,6 +1264,8 @@ print() case Pair(TypeRef(lprefix, _,_), TypeRef(rprefix,_,_)) if lprefix =:= rprefix => true case _ => + if(settings.XprintOuterMatches.value) + cunit.warning(node.pos, "can't be sure statically that these outers are equal:"+{left,right}.toString) false } @@ -1271,7 +1273,7 @@ print() var cond: Tree = null // if type 2 test is same as static type, then just null test - if(isSubType(selector.tpe,ntpe) && isSubType(ntpe, definitions.AnyRefClass.tpe)) { + if(isSubType(selector.tpe, ntpe) && isSubType(ntpe, definitions.AnyRefClass.tpe)) { cond = NotNull(selector.duplicate) nstatic = nstatic + 1 } else if(ignoreSelectorType) { @@ -1279,27 +1281,33 @@ print() } else { cond = typed { gen.mkIsInstanceOf(selector.duplicate, ntpe) } } - // compare outer instance for patterns like foo1.Bar foo2.Bar if not statically known to match - if(!outerAlwaysEqual(casted.tpe, selector.tpe)) { - casted.tpe match { - case TypeRef(prefix,_,_) if (prefix.symbol.isTerm && !prefix.symbol.isPackage) => - var theRef = gen.mkAttributedRef(prefix.prefix, prefix.symbol) + // compare outer instance for patterns like foo1.Bar foo2.Bar if not statically known to match + casted.tpe match { + case TypeRef(prefix,_,_) if + (prefix.symbol.isTerm && !prefix.symbol.isPackage) + &&(!outerAlwaysEqual(casted.tpe, selector.tpe)) => + var theRef = gen.mkAttributedRef(prefix.prefix, prefix.symbol) - // needs explicitouter treatment - theRef = handleOuter(theRef) + // needs explicitouter treatment + theRef = handleOuter(theRef) - val outerAcc = outerAccessor(casted.tpe.symbol) + val outerAcc = outerAccessor(casted.tpe.symbol) + + //if(settings.XprintOuterMatches.value) + // cunit.warning(node.pos, "outer match "+outerAcc) + + if(outerAcc != NoSymbol) { // some guys don't have outers + cond = And(cond, + Eq(Apply(Select( + typed(gen.mkAsInstanceOf(selector.duplicate, ntpe, true)), outerAcc),List()), theRef)) + } else { + cunit.warning(node.pos, " no outer accessor for "+casted.tpe.symbol+" of type "+casted.tpe) + } + case _ => + //ignore ; + } - if(outerAcc != NoSymbol) { // some guys don't have outers - cond = And(cond, - Eq(Apply(Select( - typed(gen.mkAsInstanceOf(selector.duplicate, ntpe, true)), outerAcc),List()), theRef)) - } - case _ => - //ignore ; - } - } val succ = squeezedBlock(List(ValDef(casted, if(isSubType(selector.tpe,ntpe)) selector.duplicate else typed(gen.mkAsInstanceOf(selector.duplicate, ntpe, true)))), toTree(node.and)) diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 3203637130..b4ea587f04 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -116,8 +116,10 @@ trait Definitions requires SymbolTable { val MaxProductArity = 22 /* */ val ProductClass: Array[Symbol] = new Array(MaxProductArity + 1) - def productProj(n: Int, j: Int) = getMember(ProductClass(n), "_" + j) - def isProductType(tp: Type): Boolean = tp match { + def productProj(z:Symbol, j: Int): Symbol = getMember(z, nme.Product_(j)) + def productProj(n: Int, j: Int): Symbol = productProj(ProductClass(n), j) + /** returns true if this type is exactly ProductN[T1,...,Tn], not some subclass */ + def isExactProductType(tp: Type): Boolean = tp match { case TypeRef(_, sym, elems) => elems.length <= MaxProductArity && sym == ProductClass(elems.length); case _ => @@ -133,7 +135,7 @@ trait Definitions requires SymbolTable { /** if tpe <: ProductN[T1,...,TN], returns Some(T1,...,TN) else None */ def getProductArgs(tpe: Type): Option[List[Type]] = - tpe.baseClasses.find { x => definitions.isProductType(x.tpe) } match { + tpe.baseClasses.find { x => definitions.isExactProductType(x.tpe) } match { case Some(p) => Some(tpe.baseType(p).typeArgs) case _ => None } diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index be740910ef..9757d417ee 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -207,6 +207,7 @@ trait StdNames requires SymbolTable { val PartialFunction = newTermName("PartialFunction") val Predef = newTermName("Predef") val Product = newTermName("Product") + def Product_(i:Int) = newTermName("_" + i) val ScalaObject = newTermName("ScalaObject") val ScalaRunTime = newTermName("ScalaRunTime") val Seq = newTermName("Seq") diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 94dc40b230..35954ca8a7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -467,7 +467,8 @@ trait Infer requires Analyzer { * @return ... * @throws NoInstance */ - private def methTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, + // bq: was private, but need it for unapply checking + def methTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, argtpes: List[Type], pt: Type, uninstantiated: ListBuffer[Symbol]): List[Type] = { val tvars = tparams map freshVar diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2e98c001c7..7d361a1800 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1419,12 +1419,8 @@ trait Typers requires Analyzer { assert(unapp.exists, tree) // this is no longer needed! - val unappArg:Type = unapp.tpe match { - case PolyType(_,MethodType(List(res), _)) => res - case MethodType(List(res), _) => res - case _ => error(fun.pos, "unapply takes too many arguments to be used as pattern"); NoType - } - val argDummyType = pt // was unappArg + + val argDummyType = pt // was unappArg val argDummy = context.owner.newValue(fun.pos, nme.SELECTOR_DUMMY) .setFlag(SYNTHETIC) .setInfo(argDummyType) @@ -1450,8 +1446,27 @@ trait Typers requires Analyzer { } val fun0 = Ident(fun.symbol) setPos fun.pos setType otpe // would this change when patterns are terms??? val fun1untyped = atPos(fun.pos) { - Apply(Select(gen.mkAttributedRef(fun.tpe.prefix,fun.symbol), unapp), List(arg)) - } + Apply(Select(gen.mkAttributedRef(fun.tpe.prefix,fun.symbol), unapp), List(arg)) + } + + // bq: find out if argument requires instanceOf check, if yes then lie about the type + val oldArgType = arg.tpe + unapp.tpe match { + case MethodType(formals, restpe) => + if(!isSubType(arg.tpe, formals(0))) { + //Console.println(" -- apply mono hack") + arg.tpe = AllClass.tpe // deceive typechecker, we'll insert an instanceOf check later + } + case PolyType(tparams,MethodType(fmls, res)) => + try { + methTypeArgs(tparams, fmls, res, List(arg.tpe.deconst), WildcardType, new ListBuffer[Symbol]()) + } catch { + case e => //Console.println(e.getMessage()) + //Console.println(" -- apply poly hack") + arg.tpe = AllClass.tpe // deceive typechecker, we'll insert an instanceOf check later + } + } + //Console.println("UNAPPLY2 "+fun+"/"+fun.tpe+" "+fun1untyped+", funPt = "+funPt) val fun1 = typed(fun1untyped, EXPRmode, funPt) if (fun1.tpe.isErroneous) setError(tree) @@ -1460,8 +1475,11 @@ trait Typers requires Analyzer { val formals1 = formalTypes(formals0, args.length) val args1 = typedArgs(args, mode, formals0, formals1) if (!isFullyDefined(pt)) assert(false, tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) + // restore old type (this will never work, but just pass typechecking) + arg.tpe = oldArgType UnApply(fun1, args1) setPos tree.pos setType pt } + /* --- end unapply --- */ case _ => errorTree(tree, ""+fun+" does not take parameters") -- cgit v1.2.3