From 9d330e3c839a52ddcb508ffb3c2660ce244fb92b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 6 Aug 2012 18:33:44 +0200 Subject: SI-6145 lax typing of args to synthetic case-labels Use wildcard for the expected type of the arguments of a jump to a label synthesized by the pattern matcher... except during erasure: we must take the expected type into account as it drives the insertion of casts! It's ok since we're typing the translation of well-typed code. The only "type errors" we catch are skolem mismatches. (after erasure the existential types that before caused problems have disappeared.) It's necessary to balance GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness. I've exhausted all other semi-clean approaches I could think of: - the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, - casting breaks SI-6145 (and it's an unnecessary cast at run time), - not casting breaks GADT typing as it requires sneaking ill-typed trees past typer --- .../tools/nsc/typechecker/PatternMatching.scala | 22 +++++++++++----------- .../scala/tools/nsc/typechecker/Typers.scala | 14 +++++++++++++- test/files/pos/t6145.scala | 11 +++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 test/files/pos/t6145.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index a8286c9f19..64260b4a17 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -230,17 +230,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL removeCPSAdaptAnnotations(origPt) else origPt - // we've packed the type for each case in typedMatch so that if all cases have the same existential case, we get a clean lub - // here, we should open up the existential again // relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala - // TODO: fix skolemizeExistential (it should preserve annotations, right?) - val pt = repeatedToSeq(ptUnCPS.skolemizeExistential(context.owner, context.tree) withAnnotations ptUnCPS.annotations) + // pt is the skolemized version + val pt = repeatedToSeq(ptUnCPS) + + // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) // the alternative to attaching the default case override would be to simply // append the default to the list of cases and suppress the unreachable case error that may arise (once we detect that...) val matchFailGenOverride = match_.attachments.get[DefaultOverrideMatchAttachment].map{case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)} - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS + val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental val combined = combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) @@ -1326,7 +1326,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree + def _asInstanceOf(t: Tree, tp: Type): Tree def _equals(checker: Tree, binder: Symbol): Tree def _isInstanceOf(b: Symbol, tp: Type): Tree def and(a: Tree, b: Tree): Tree @@ -1384,7 +1384,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL 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, force: Boolean = false): Tree = if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp) + 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 _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 } @@ -3481,7 +3481,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = newSynthCaseLabel("matchEnd") - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations // + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) @@ -3492,7 +3492,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val nextCase = newCaseSym _currCase = nextCase - LabelDef(currCase, Nil, mkCase(new OptimizedCasegen(matchEnd, nextCase, restpe))) + LabelDef(currCase, Nil, mkCase(new OptimizedCasegen(matchEnd, nextCase))) } // must compute catchAll after caseLabels (side-effects nextCase) @@ -3517,14 +3517,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL ) } - class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol, restpe: Type) extends CommonCodegen with Casegen { + class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) // only used to wrap the RHS of a body // res: T // returns MatchMonad[T] - def one(res: Tree): Tree = matchEnd APPLY (_asInstanceOf(res, restpe)) // need cast for GADT magic + def one(res: Tree): Tree = matchEnd APPLY (res) // a jump to a case label is special-cased in typedApply protected def zero: Tree = nextCase APPLY () // prev: MatchMonad[T] diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d785988738..c76095e0bf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3041,7 +3041,19 @@ trait Typers extends Modes with Adaptations with Tags { // target of a call. Since this information is no longer available from // typedArg, it is recorded here. checkDead.updateExpr(fun) - val args1 = typedArgs(args, forArgMode(fun, mode), paramTypes, formals) + + val args1 = + // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code) + // ... except during erasure: we must take the expected type into account as it drives the insertion of casts! + // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness + // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, + // casting breaks SI-6145, + // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer) + if (!phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol)) + typedArgs(args, forArgMode(fun, mode)) + else + typedArgs(args, forArgMode(fun, mode), paramTypes, formals) + // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) // precise(foo) : foo.type => foo.type diff --git a/test/files/pos/t6145.scala b/test/files/pos/t6145.scala new file mode 100644 index 0000000000..28334d4420 --- /dev/null +++ b/test/files/pos/t6145.scala @@ -0,0 +1,11 @@ +object Test { + // the existential causes a cast and the cast makes searchClass not be in tail position + // can we get rid of the useless cast? + @annotation.tailrec + final def searchClass: Class[_] = { + "packageName" match { + case _ => + searchClass + } + } +} \ No newline at end of file -- cgit v1.2.3