diff options
Diffstat (limited to 'src/compiler')
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 |