From 2e664079445549288789ad24a95ce7d583ae205c Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 31 Jan 2012 11:30:41 +0100 Subject: Introduce getAnnotations that triggers symbol completion Default getter for annotations doesn't perform initialization, hence we've faced the following bug: https://issues.scala-lang.org/browse/SI-5423. One of the approaches to fixing it would be to auto-complete on getter, but according to Martin we'd better not do that because of cycles. That's why I'm just introducing a new, eager, variation of `annotations' and redirecting public API to it. Review by @odersky. --- src/compiler/scala/reflect/internal/Symbols.scala | 10 ++++++++++ src/library/scala/reflect/api/Symbols.scala | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 94d764067f..e777491300 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1272,6 +1272,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => * the annotations attached to member a definition (class, method, type, field). */ def annotations: List[AnnotationInfo] = _annotations + + /** This getter is necessary for reflection, see https://issues.scala-lang.org/browse/SI-5423 + * We could auto-inject completion into `annotations' and `setAnnotations', but I'm not sure about that + * @odersky writes: I fear we can't do the forcing for all compiler symbols as that could introduce cycles + */ + def getAnnotations: List[AnnotationInfo] = { + initialize + _annotations + } + def setAnnotations(annots: List[AnnotationInfo]): this.type = { _annotations = annots this diff --git a/src/library/scala/reflect/api/Symbols.scala b/src/library/scala/reflect/api/Symbols.scala index 01c1a0f2ae..17d9b06324 100755 --- a/src/library/scala/reflect/api/Symbols.scala +++ b/src/library/scala/reflect/api/Symbols.scala @@ -79,7 +79,7 @@ trait Symbols { self: Universe => /** A list of annotations attached to this Symbol. */ - def annotations: List[self.AnnotationInfo] + def getAnnotations: List[self.AnnotationInfo] /** For a class: the module or case class factory with the same name in the same package. * For all others: NoSymbol -- cgit v1.2.3 From 18d6a9f5054bf7f1c76e48c3eb8da0fb96ccdccb Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 1 Feb 2012 15:16:14 +0100 Subject: Fixed handling of empty keys in emitSWITCH. The problem of emitSWITCH not handling empty keys popped up when I tried to implement unfolding of pattern alternatives in genicode instead of in typers/explicitouter. This change makes perfect sense in isolation as bytecode allows LOOKUPSWITCHes that have only default case. I actually verified that this kind of bytecode is generated by javac when one has switch statement with only default case defined. Review by @paulp or @dragos. --- lib/fjbg.jar.desired.sha1 | 2 +- src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/lib/fjbg.jar.desired.sha1 b/lib/fjbg.jar.desired.sha1 index 1b1068b0d3..d24a5d01fc 100644 --- a/lib/fjbg.jar.desired.sha1 +++ b/lib/fjbg.jar.desired.sha1 @@ -1 +1 @@ -9aa9c99b8032e454f1f85d27de31a88b3dec1045 ?fjbg.jar +c3f9b576c91cb9761932ad936ccc4a71f33d2ef2 ?fjbg.jar diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java index 8b0338ed29..d4c5417260 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java @@ -596,6 +596,16 @@ public class JExtendedCode extends JCode { double minDensity) { assert keys.length == branches.length; + //The special case for empty keys. It makes sense to allow + //empty keys and generate LOOKUPSWITCH with defaultBranch + //only. This is exactly what javac does for switch statement + //that has only a default case. + if (keys.length == 0) { + emitLOOKUPSWITCH(keys, branches, defaultBranch); + return; + } + //the rest of the code assumes that keys.length > 0 + // sorting the tables // FIXME use quicksort for (int i = 1; i < keys.length; i++) { -- cgit v1.2.3 From e311585e26449a921bc8a40b87b2552f1d363086 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:38:38 +0100 Subject: Unfold pattern alternatives in genicode. Implemented unfolding of pattern alternatives that can be translated into switch table in genicode. This way pattern matcher can keep simple patterns as-is and let backend handle translation of them instead of generating bunch of LabelDefs and jumps. Review by @dragos or @magarciaEPFL as both seem to know genicode very well. --- src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 3d650ef753..3baff7da9e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1072,6 +1072,15 @@ abstract class GenICode extends SubComponent { targets = tmpCtx.bb :: targets case Ident(nme.WILDCARD) => default = tmpCtx.bb + case Alternative(alts) => + alts foreach { + case Literal(value) => + tags = value.intValue :: tags + targets = tmpCtx.bb :: targets + case _ => + abort("Invalid case in alternative in switch-like pattern match: " + + tree + " at: " + tree.pos) + } case _ => abort("Invalid case statement in switch-like pattern match: " + tree + " at: " + (tree.pos)) -- cgit v1.2.3 From 97912733f9e7e2c2528ebbab6b70ef35b8dd0fbc Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:42:24 +0100 Subject: Get rid of unused import. --- src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 44579400ff..0b35f1b1d0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1138,7 +1138,7 @@ defined class Foo */ case (btm@BodyTreeMaker(body, _)) :: Nil => Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) // constant - case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => import CODE._ + case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => Some(CaseDef(const, EmptyTree, btm.substitution(body))) // alternatives case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) -- cgit v1.2.3 From aa7759651d25ab8c315a2d36e3f28cf3caaa041f Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:42:52 +0100 Subject: Generate default case for switches. --- .../scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 0b35f1b1d0..cf5985eeee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1160,9 +1160,20 @@ defined class Foo */ sequence(caseDefs) map { caseDefs => import CODE._ + val caseDefsWithDefault = { + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false + } + val hasDefault = caseDefs exists isDefault + if (hasDefault) caseDefs else { + val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } + caseDefs :+ default + } + } val matcher = BLOCK( VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't - Match(REF(scrutSym), caseDefs) // match on scrutSym, not scrut to avoid duplicating scrut + Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut ) // matcher filter (tree => tree.tpe == null) foreach println -- cgit v1.2.3 From 39457f6c85fc9764d714d52317edcd4300fd82b8 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 1 Feb 2012 17:26:11 +0100 Subject: Convert values to Int in switchable patterns. Further improvements to how -Yvirtpatmat handles switch-like patterns that can be translated to switch tables. First of all, we added a check whether a type of an expression we pattern match on is in the set of allowed types for switch patterns. If yes, we translate a pattern to switch one by converting both an expression we pattern match on and literals in a pattern to an Int. I borrowed an idea of converting to Ints from both old pattern matcher implementation and from how javac handles it. --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 62 ++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index cf5985eeee..b1e02cb062 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1128,10 +1128,22 @@ defined class Foo */ // } // } + private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (!optimizingCodeGen) None else { def sequence[T](xs: List[Option[T]]): Option[List[T]] = if (xs exists (_.isEmpty)) None else Some(xs.flatten) + def isSwitchableTpe(tpe: Type): Boolean = + switchableTpes contains tpe + def switchableConstToInt(x: Tree): Tree = { + val Literal(const) = x + const.tag match { + case IntTag => x + case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue)) + } + } + val caseDefs = cases map { makers => removeSubstOnly(makers) match { // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches) @@ -1139,12 +1151,12 @@ defined class Foo */ Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) // constant case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(const, EmptyTree, btm.substitution(body))) + Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body))) // alternatives case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) val caseConstants = altss map { case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil => - Some(const) + Some(switchableConstToInt(const)) case _ => None } @@ -1158,27 +1170,35 @@ defined class Foo */ } } - sequence(caseDefs) map { caseDefs => - import CODE._ - val caseDefsWithDefault = { - def isDefault(x: CaseDef): Boolean = x match { - case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true - case _ => false - } - val hasDefault = caseDefs exists isDefault - if (hasDefault) caseDefs else { - val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } - caseDefs :+ default + if (!isSwitchableTpe(scrut.tpe)) + None + else { + sequence(caseDefs) map { caseDefs => + import CODE._ + val caseDefsWithDefault = { + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false + } + val hasDefault = caseDefs exists isDefault + if (hasDefault) caseDefs else { + val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } + caseDefs :+ default + } } + val matcher = BLOCK( + if (scrut.tpe != IntClass.tpe) { + scrutSym setInfo IntClass.tpe + VAL(scrutSym) === (scrut DOT nme.toInt) + } else { + VAL(scrutSym) === scrut + }, + Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut + ) + // matcher filter (tree => tree.tpe == null) foreach println + // treeBrowser browse matcher + matcher // set type to avoid recursion in typedMatch } - val matcher = BLOCK( - VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't - Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut - ) - - // matcher filter (tree => tree.tpe == null) foreach println - // treeBrowser browse matcher - matcher // set type to avoid recursion in typedMatch } } -- cgit v1.2.3