summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala115
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala92
3 files changed, 137 insertions, 72 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
index 93fa9a60f6..f702f44338 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
@@ -397,7 +397,7 @@ trait MarkupParsers {
/** xScalaPatterns ::= patterns
*/
- def xScalaPatterns: List[Tree] = escapeToScala(parser.seqPatterns(), "pattern")
+ def xScalaPatterns: List[Tree] = escapeToScala(parser.xmlSeqPatterns(), "pattern")
def reportSyntaxError(pos: Int, str: String) = parser.syntaxError(pos, str)
def reportSyntaxError(str: String) {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index bca1cc4596..337ca7671c 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1705,11 +1705,11 @@ self =>
* was threaded through methods as boolean seqOK.
*/
trait SeqContextSensitive extends PatternContextSensitive {
- /** Returns Some(tree) if it finds a star and prematurely ends parsing.
- * This is an artifact of old implementation which has proven difficult
- * to cleanly extract.
- */
- def interceptStarPattern(top: Tree): Option[Tree]
+ // is a sequence pattern _* allowed?
+ def isSequenceOK: Boolean
+
+ // are we in an XML pattern?
+ def isXML: Boolean = false
def functionArgType(): Tree = argType()
def argType(): Tree = {
@@ -1796,45 +1796,98 @@ self =>
/** {{{
* Pattern3 ::= SimplePattern
* | SimplePattern {Id [nl] SimplePattern}
- * SeqPattern3 ::= SeqSimplePattern [ `*' | `?' | `+' ]
- * | SeqSimplePattern {Id [nl] SeqSimplePattern}
* }}}
*/
def pattern3(): Tree = {
+ var top = simplePattern(badPattern3)
+ // after peekahead
+ def acceptWildStar() = atPos(top.pos.startOrPoint, in.prev.offset)(Star(stripParens(top)))
+ def peekahead() = {
+ in.prev copyFrom in
+ in.nextToken()
+ }
+ def pushback() = {
+ in.next copyFrom in
+ in copyFrom in.prev
+ }
+ // See SI-3189, SI-4832 for motivation. Cf SI-3480 for counter-motivation.
+ // TODO: dredge out the remnants of regexp patterns.
+ // /{/ peek for _*) or _*} (for xml escape)
+ if (isSequenceOK) {
+ top match {
+ case Ident(nme.WILDCARD) if (isRawStar) =>
+ peekahead()
+ in.token match {
+ case RBRACE if (isXML) => return acceptWildStar()
+ case RPAREN if (!isXML) => return acceptWildStar()
+ case _ => pushback()
+ }
+ case _ =>
+ }
+ }
val base = opstack
- var top = simplePattern()
- interceptStarPattern(top) foreach { x => return x }
-
while (isIdent && in.name != raw.BAR) {
- top = reduceStack(
- false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name))
+ top = reduceStack(false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name))
val op = in.name
opstack = OpInfo(top, op, in.offset) :: opstack
ident()
- top = simplePattern()
+ top = simplePattern(badPattern3)
}
stripParens(reduceStack(false, base, top, 0, true))
}
+ def badPattern3(): Tree = {
+ def isComma = in.token == COMMA
+ def isAnyBrace = in.token == RPAREN || in.token == RBRACE
+ val badStart = "illegal start of simple pattern"
+ // better recovery if don't skip delims of patterns
+ var skip = !(isComma || isAnyBrace)
+ val msg = if (!opstack.isEmpty && opstack.head.operator == nme.STAR) {
+ opstack.head.operand match {
+ case Ident(nme.WILDCARD) =>
+ if (isSequenceOK && isComma)
+ "bad use of _* (a sequence pattern must be the last pattern)"
+ else if (isSequenceOK && isAnyBrace) {
+ skip = true // do skip bad paren; scanner may skip bad brace already
+ "bad brace or paren after _*"
+ } else if (!isSequenceOK && isAnyBrace)
+ "bad use of _* (sequence pattern not allowed)"
+ else badStart
+ case _ =>
+ if (isSequenceOK && isAnyBrace)
+ "use _* to match a sequence"
+ else if (isComma || isAnyBrace)
+ "trailing * is not a valid pattern"
+ else badStart
+ }
+ } else {
+ badStart
+ }
+ syntaxErrorOrIncomplete(msg, skip)
+ errorPatternTree
+ }
/** {{{
* SimplePattern ::= varid
* | `_'
* | literal
* | XmlPattern
- * | StableId [TypeArgs] [`(' [SeqPatterns] `)']
+ * | StableId /[TypeArgs]/ [`(' [Patterns] `)']
+ * | StableId [`(' [Patterns] `)']
+ * | StableId [`(' [Patterns] `,' [varid `@'] `_' `*' `)']
* | `(' [Patterns] `)'
- * SimpleSeqPattern ::= varid
- * | `_'
- * | literal
- * | XmlPattern
- * | `<' xLiteralPattern
- * | StableId [TypeArgs] [`(' [SeqPatterns] `)']
- * | `(' [SeqPatterns] `)'
* }}}
*
* XXX: Hook for IDE
*/
def simplePattern(): Tree = {
+ // simple diagnostics for this entry point
+ def badStart(): Tree = {
+ syntaxErrorOrIncomplete("illegal start of simple pattern", true)
+ errorPatternTree
+ }
+ simplePattern(badStart)
+ }
+ def simplePattern(onError: () => Tree): Tree = {
val start = in.offset
in.token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS =>
@@ -1867,8 +1920,7 @@ self =>
case XMLSTART =>
xmlLiteralPattern()
case _ =>
- syntaxErrorOrIncomplete("illegal start of simple pattern", true)
- errorPatternTree
+ onError()
}
}
}
@@ -1879,16 +1931,16 @@ self =>
}
/** The implementation for parsing inside of patterns at points where sequences are allowed. */
object seqOK extends SeqContextSensitive {
- // See ticket #3189 for the motivation for the null check.
- // TODO: dredge out the remnants of regexp patterns.
- // ... and now this is back the way it was because it caused #3480.
- def interceptStarPattern(top: Tree): Option[Tree] =
- if (isRawStar) Some(atPos(top.pos.startOrPoint, in.skipToken())(Star(stripParens(top))))
- else None
+ val isSequenceOK = true
}
/** The implementation for parsing inside of patterns at points where sequences are disallowed. */
object noSeq extends SeqContextSensitive {
- def interceptStarPattern(top: Tree) = None
+ val isSequenceOK = false
+ }
+ /** For use from xml pattern, where sequence is allowed and encouraged. */
+ object xmlSeqOK extends SeqContextSensitive {
+ val isSequenceOK = true
+ override val isXML = true
}
/** These are default entry points into the pattern context sensitive methods:
* they are all initiated from non-pattern context.
@@ -1902,7 +1954,8 @@ self =>
/** Default entry points into some pattern contexts. */
def pattern(): Tree = noSeq.pattern()
def patterns(): List[Tree] = noSeq.patterns()
- def seqPatterns(): List[Tree] = seqOK.patterns() // Also called from xml parser
+ def seqPatterns(): List[Tree] = seqOK.patterns()
+ def xmlSeqPatterns(): List[Tree] = xmlSeqOK.patterns() // Called from xml parser
def argumentPatterns(): List[Tree] = inParens {
if (in.token == RPAREN) Nil
else seqPatterns()
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 934a7567d5..6234c05258 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2249,43 +2249,35 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
}
}
- def typedMatchAnonFun(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type, selOverride: Option[(List[ValDef], Tree)] = None) = {
- val pt = deskolemizeGADTSkolems(pt0)
- val targs = pt.normalize.typeArgs
- val arity = if (isFunctionType(pt)) targs.length - 1 else 1 // TODO pt should always be a (Partial)Function, right?
- val ptRes = if (targs.isEmpty) WildcardType else targs.last // may not be fully defined
+ // synthesize and type check a (Partial)Function implementation based on a match specified by `cases`
+ // Match(EmptyTree, cases) ==> new <Partial>Function { 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
+ class MatchFunTyper(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type) {
+ 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
- val isPartial = pt.typeSymbol == PartialFunctionClass
- val anonClass = context.owner.newAnonymousFunctionClass(tree.pos)
- val funThis = This(anonClass)
- val serialVersionUIDAnnotation = AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
+ private val isPartial = pt.typeSymbol == PartialFunctionClass
+ private val anonClass = context.owner.newAnonymousFunctionClass(tree.pos)
+ private val funThis = This(anonClass)
- anonClass addAnnotation serialVersionUIDAnnotation
+ anonClass addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
def deriveFormals =
- selOverride match {
- case None if targs.isEmpty => Nil
- case None => targs.init // is there anything we can do if targs.isEmpty??
- case Some((vparams, _)) =>
- vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe}
- }
+ if (targs.isEmpty) Nil
+ else targs.init
- def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = {
- selOverride match {
- case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree)
- case None =>
- val ps = methodSym newSyntheticValueParams formals // is there anything we can do if targs.isEmpty??
- val ids = ps map (p => Ident(p.name))
- val sel = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
- (ps, sel)
- case Some((vparams, sel)) =>
- val newParamSyms = (vparams, formals).zipped map {(p, tp) =>
- methodSym.newValueParameter(p.name, p.pos.focus, SYNTHETIC) setInfo tp
- }
+ def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) =
+ if (formals.isEmpty) { MissingParameterTypeAnonMatchError(tree, pt); Nil }
+ else methodSym newSyntheticValueParams formals
- (newParamSyms, sel.duplicate)
+ 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._
@@ -2298,7 +2290,8 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
// rig the show so we can get started typing the method body -- later we'll correct the infos...
anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass)
val methodSym = anonClass.newMethod(nme.apply, tree.pos, if(isPartial) (FINAL | OVERRIDE) else FINAL)
- val (paramSyms, selector) = mkParams(methodSym)
+ val paramSyms = mkParams(methodSym)
+ val selector = mkSel(paramSyms)
if (selector eq EmptyTree) EmptyTree
else {
@@ -2329,9 +2322,10 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
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 (List(x), selector) = mkParams(methodSym, List(A1.tpe))
+ 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)
if (selector eq EmptyTree) EmptyTree
else {
@@ -2362,7 +2356,9 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
def isDefinedAtMethod = {
val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL)
- val (paramSyms, selector) = mkParams(methodSym)
+ val paramSyms = mkParams(methodSym)
+ val selector = mkSel(paramSyms)
+
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)
@@ -2382,8 +2378,23 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
else List(applyOrElseMethodDef, isDefinedAtMethod)
} else List(applyMethod)
- if (members.head eq EmptyTree) setError(tree)
- else typed(Block(List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, tree.pos)), New(anonClass.tpe)), mode, pt)
+ def translated =
+ if (members.head eq EmptyTree) setError(tree)
+ else typed(Block(List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, tree.pos)), New(anonClass.tpe)), mode, pt)
+ }
+
+ // 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 }
+
+ // 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
+ }
+
+ override def mkSel(params: List[Symbol]) = sel.duplicate
}
/**
@@ -2439,11 +2450,12 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
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
- case Match(sel, cases) if doMatchTranslation =>
+ case Match(sel, cases) if (sel ne EmptyTree) && doMatchTranslation =>
// 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)
- newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, pt, Some((fun.vparams, sel)))
+ val outerTyper = newTyper(context.outer)
+ (new outerTyper.MatchFunTyperBetaReduced(fun, sel, cases, mode, pt)).translated
case _ =>
val vparamSyms = fun.vparams map { vparam =>
enterSym(context, vparam)
@@ -3835,7 +3847,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser
if (selector ne EmptyTree) {
val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt)
typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt)
- } else typedMatchAnonFun(tree, cases, mode, pt)
+ } else (new MatchFunTyper(tree, cases, mode, pt)).translated
} else if (selector == EmptyTree) {
if (opt.virtPatmat) debugwarn("virtpatmat should not encounter empty-selector matches "+ tree)
val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1